#!/usr/bin/env ruby | |
require 'optparse' | |
# create an empty hash to hold the options | |
options = {} | |
optparse = OptionParser.new do|opts| | |
opts.banner = "Usage: shift_subtitle [options] infile outfile" | |
opts.on('-h', '--help', 'Display this screen') do | |
puts opts | |
exit | |
end | |
opts.on('-o', '--operation [add|sub]', 'What type of operation to use') do|o| | |
if o == 'add' or o == 'sub' | |
options[:operation] = o | |
else | |
puts "operation must be 'add' or 'sub'." | |
exit | |
end | |
end | |
opts.on('-t', '--time [HH:[MM:[SS,[mmm]]]]', 'Amount of time to shift') do|t| | |
options[:time] = t | |
end | |
end | |
optparse.parse! | |
# make sure the files exist... | |
ARGV[0] ? options[:file1] = ARGV[0] : exit | |
ARGV[1] ? options[:file2] = ARGV[1] : exit | |
class SubtitleTime | |
attr_accessor :hours, :minutes, :seconds, :milliseconds | |
# reverse char by char block | |
# at first comma, set milli | |
# at first : set seconds, etc. | |
def initialize(time, modifier='add') | |
num = '' | |
set_sec,set_min,set_hour= false | |
time.reverse.each_char do|char| | |
if num =~ /\d\d\d/ | |
@milliseconds = num.to_i | |
num = '' | |
set_sec = true | |
elsif char == ':' and set_sec | |
@seconds = num.to_i | |
num = '' | |
set_sec = false | |
set_min = true | |
elsif char == ':' and set_min | |
@minutes = num.to_i | |
num = '' | |
set_min = false | |
set_hour = true | |
elsif char == ':' and set_hour | |
@hours = num.to_i | |
num = '' | |
else | |
num = char + num | |
end | |
end | |
if set_sec | |
@seconds = num.to_i | |
elsif set_min | |
@minutes = num.to_i | |
elsif set_hour | |
@hours = num.to_i | |
end | |
@milliseconds = @milliseconds || 0 | |
@seconds = @seconds || 0 | |
@minutes = @minutes || 0 | |
@hours = @hours || 0 | |
@modifier = modifier | |
end | |
def change_hour(hours) | |
if @modifier == 'add' | |
@hours += hours | |
else | |
@hours -= hours | |
if @hours < 0 | |
@hours = 0 | |
end | |
end | |
end | |
def change_min(minutes) | |
if @modifier == 'add' | |
@minutes += minutes | |
if @minutes >= 60 | |
hours = @minutes/60 | |
change_hour(hours) | |
@minutes %= 60 | |
end | |
else | |
@minutes -= minutes | |
if @minutes < 0 | |
change_hour(1) | |
@minutes += 60 | |
end | |
end | |
end | |
def change_sec(seconds) | |
if @modifier == 'add' | |
@seconds += seconds | |
if @seconds >= 60 | |
minutes = @seconds/60 | |
change_min(minutes) | |
@seconds %= 60 | |
end | |
else | |
@seconds -= seconds | |
if @seconds < 0 | |
change_min(1) | |
@seconds += 60 | |
end | |
end | |
end | |
def change_milli(milli) | |
if @modifier == 'add' | |
@milliseconds += milli | |
if @milliseconds >= 1000 | |
seconds = @milliseconds/1000 | |
change_sec(seconds) | |
@milliseconds %= 1000 | |
end | |
else | |
@milliseconds -= milli | |
if @milliseconds < 0 | |
change_sec(1) | |
@milliseconds += 1000 | |
end | |
end | |
end | |
def to_s | |
return sprintf("%02d:%02d:%02d,%03d", @hours, @minutes, @seconds, @milliseconds) | |
end | |
end | |
# turn the input time into a SubtitleTime object | |
time_mod = SubtitleTime.new(options[:time]) | |
out_file = File.new(options[:file2], "w") | |
# every line that has timestamps | |
# take each time stamp split into h/m/s/ms | |
# apply the operation | |
# how to deal with carry overs? | |
IO.foreach(options[:file1]) do|line| | |
if (line =~ /-->/) | |
first = SubtitleTime.new(line.split(/ --> /)[0], options[:operation]) | |
second = SubtitleTime.new(line.split(/ --> /)[1], options[:operation]) | |
first.change_hour(time_mod.hours) | |
first.change_min(time_mod.minutes) | |
first.change_sec(time_mod.seconds) | |
first.change_milli(time_mod.milliseconds) | |
second.change_hour(time_mod.hours) | |
second.change_min(time_mod.minutes) | |
second.change_sec(time_mod.seconds) | |
second.change_milli(time_mod.milliseconds) | |
out_file.syswrite("#{first.to_s} --> #{second.to_s}\n") | |
else | |
out_file.syswrite(line) | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment