|
#! /usr/bin/env ruby |
|
|
|
# NAME : Sync Subs |
|
# DESCRIPTION : Synchronize .srt subtitles |
|
# AUTHOR : Madh93 (Miguel Hernandez) |
|
# VERSION : 0.0.1 |
|
# LICENSE : GNU General Public License v3 |
|
# USAGE : ruby syncsubs.rb |
|
|
|
require 'optparse' |
|
|
|
Options = Struct.new(:input, :output, :sync) |
|
|
|
# Extends String |
|
class String |
|
def to_milli |
|
a = [1, 1000, 60_000, 3_600_000] * 2 |
|
split(/[:\.]/).map { |time| time.to_i * a.pop }.inject(&:+) |
|
end |
|
end |
|
|
|
# Extends Fixnum |
|
class Fixnum |
|
def to_time |
|
Time.at(to_f / 1000).utc.strftime('%H:%M:%S.%L') |
|
end |
|
end |
|
|
|
module SyncSubs |
|
# Argument Parser Class |
|
class ArgParser |
|
def self.parse(options) |
|
args = Options.new |
|
|
|
opt_parser = OptionParser.new do |opts| |
|
opts.banner = 'Usage: ruby syncsubs.rb [options]' |
|
|
|
opts.on('-i', '--input NAME', 'SRT input file name') do |f| |
|
args.input = f |
|
end |
|
|
|
opts.on('-o', '--output NAME', 'SRT output file name') do |f| |
|
args.output = f |
|
end |
|
|
|
opts.on('-s', '--sync TIME', 'Sync time in milliseconds') do |t| |
|
args.sync = t.to_i |
|
end |
|
|
|
opts.on('-h', '--help', 'Prints this help') do |
|
puts opts |
|
puts 'Example: ruby syncsubs.rb -i input.srt -o output.srt -s -500' |
|
exit |
|
end |
|
end |
|
|
|
opt_parser.parse!(options) |
|
if args.count(&:nil?) != 0 |
|
puts 'Try: ruby syncsubs.rb --help' |
|
exit |
|
end |
|
args |
|
end |
|
end |
|
end |
|
|
|
module SyncSubs |
|
# Sync Subs class |
|
class SyncSubs |
|
attr_accessor :input, :output, :sync |
|
|
|
def initialize |
|
yield self if block_given? |
|
end |
|
|
|
def synchronize! |
|
# Read input subtitles |
|
begin |
|
data = File.readlines(@input) |
|
rescue |
|
puts "'#{@input}' can't be opened" |
|
exit |
|
end |
|
|
|
# Synchronize times |
|
data.map! { |line| line.include?('-->') ? sync_time(line) : line } |
|
|
|
# Export to output subtitles file |
|
File.open("#{@output}", 'w+') do |f| |
|
data.each { |line| f.write(line) } |
|
end |
|
end |
|
|
|
private |
|
|
|
def sync_time(str) |
|
# Split and remove commas |
|
str = str.tr(',', '.').split |
|
|
|
# Apply new sync time |
|
start = str[0].to_milli + @sync |
|
ending = str[2].to_milli + @sync |
|
|
|
if start < 0 || ending < 0 |
|
puts "New sync time below 0... exiting." |
|
exit |
|
end |
|
|
|
"#{start.to_time} #{str[1]} #{ending.to_time}\n" |
|
end |
|
end |
|
end |
|
|
|
# Main |
|
args = SyncSubs::ArgParser.parse(ARGV) |
|
|
|
SyncSubs::SyncSubs.new do |s| |
|
s.input = args.input |
|
s.output = args.output |
|
s.sync = args.sync |
|
end.synchronize! |