-
-
Save anonymous/92e2a8426d44a3d465ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby -w | |
#File: shift_subtitle.rb | |
=begin | |
Robison WR Santos (rwrsantos@gmail.com) | |
26/09/2009 | |
Difficulty: Ruby beginner. | |
Goals: Basic control over Ruby elements, specially command line scripting. | |
Description: There are several ways to subtitle a movie nowadays, and one of | |
the most well known format is the SubRip format | |
http://en.wikipedia.org/wiki/SubRip). It has entries like these: | |
-------------------------------------------------------------------------------- | |
645 | |
01:31:51,210 --> 01:31:54,893 | |
the government is implementing a new policy... | |
646 | |
01:31:54,928 --> 01:31:57,664 | |
In connection with a dramatic increase | |
in crime in certain neighbourhoods, | |
-------------------------------------------------------------------------------- | |
Each line has an increasing integer identification, then comes the time range | |
(start and end time) in the format “hours:minutes:seconds,milliseconds”. | |
The decimal separator used is the comma. Finally there are the subtitles | |
themselves and a line break marks the end of an entry. | |
Sometimes the timing is shifted for a small amount, 2 or 3 seconds. Then comes | |
the trouble when you need to shift everything a few seconds back or ahead. | |
The goal is to create a small command line script in Ruby that will read an SRT | |
file, and output another one with the new calculated times. | |
So, for example, if I want to shift everything 2,500 (2 seconds and 500 | |
milliseconds) ahead, I would start with this: | |
-------------------------------------------------------------------------------- | |
01:32:04,283 --> 01:32:07,769 | |
-------------------------------------------------------------------------------- | |
and end up with: | |
-------------------------------------------------------------------------------- | |
01:32:06,783 --> 01:32:10,269 | |
-------------------------------------------------------------------------------- | |
The command line should accept arguments such as: | |
shift_subtitle --operation add --time 02,110 input_file output_file | |
This means “--operation” can accept either ‘add’ or ’sub’ to add or subtract | |
times. The “--time” will accept the amount of time to shift in the format 11,222 | |
where “11? is the amount of seconds and “222? the amount of milliseconds. | |
Requirements: This has to be a pure Ruby script, using only the Ruby Standard | |
Libraries (meaning, no external Gems). | |
It has to implement “optparse” to parse the command line arguments. | |
=end | |
require 'time_shifter' | |
require 'optparse' | |
if __FILE__ == $0 | |
options = {} | |
OptionParser.new do |opt| | |
opt.banner = "Usage: shift_subtitle.rb [options] input_file output_file" | |
opt.on("--operation=add|sub","Operation to be performed, 'add' or 'sub'.") do |op| | |
options[:operation] = op | |
end | |
opt.on("--time=time","Time to be added or subtract in seconds", | |
"[and milliseconds, separeted by comma or period].") do |t| | |
options[:time] = t | |
end | |
opt.on_tail("-h", "--help", "Show this message.") do | |
puts opt | |
exit | |
end | |
end.parse! | |
if ARGV.size \d{2}:\d{2}:\d{2},\d{3}$/ | |
t = Time.now.to_f | |
begin | |
File.open(ARGV[1],'w') do |fout| #output_file | |
File.open(ARGV[0],'r') do |fin| #input_file | |
while line = fin.gets | |
line = line.chomp if line | |
if line =~ TIME_PATTERN | |
line = line.split(' --> ') | |
in_time = TimeShifter.shift(line[0],options[:time],options[:operation]) | |
out_time = TimeShifter.shift(line[1],options[:time],options[:operation]) | |
fout.puts "#{in_time} --> #{out_time}" | |
else | |
fout.puts line | |
end | |
end | |
end #closes input_file | |
end #closes output_file | |
rescue TimeShifter::InvalidOperation | |
puts "\n[ERROR]: A valid operation should be either 'add' or 'sub'." | |
exit! | |
rescue TimeShifter::IlegalTimeFormat | |
puts "\n[ERROR]: An IlegalTimeFormat error has occured. Please verify your" + | |
"time parameter or your file format." | |
exit! | |
rescue SystemCallError | |
puts "\n[ERROR]: An error has occured. Maybe your input file does not exist." | |
exit! | |
end | |
puts "\n[SUCCESS]: It took #{Time.now.to_f - t} seconds to finish..." | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#file: time_shifter.rb | |
=begin | |
Robison WR Santos (rwrsantos@gmail.com) | |
26/09/2009 | |
=end | |
class TimeShifter | |
# Declaring error classes | |
InvalidOperation = Class.new(StandardError) | |
IlegalTimeFormat = Class.new(StandardError) | |
# This method must receive a time to be shifted. | |
# The time_in parameter is a string in format hh:mm:ss,millisecond. | |
# The amount_to_shift parameter is a string representing the amount of time | |
# to be shifted. | |
# The operation parameter is a string representing whether it should add or | |
# subtract the amount to shift tp/from the time in. | |
def self.shift(time_in, amount_to_shift='0', operation='add') | |
#Validates entry | |
raise IlegalTimeFormat,"time_in should respect the format #{/^\d{2}:\d{2}:\d{2},\d{3}$/}" unless time_in =~ /\d{2}:\d{2}:\d{2},\d{3}$/ | |
raise IlegalTimeFormat, "amount_to_shift should respect the format #{/^\d+([,\.]\d+)?$/}" unless amount_to_shift =~ /^\d+([,\.]\d+)?$/ | |
#Transforms into values | |
time = real_time(time_in) | |
amount = amount_to_shift.gsub(',','.').to_f | |
time = case operation | |
when 'add' | |
time + amount | |
when 'sub' | |
time - amount | |
else | |
raise InvalidOperation | |
end | |
# Return the formated string | |
string_time(time.to_f) | |
end | |
# Transorms the string time in a real time value in seconds. | |
def self.real_time(time) | |
# get all values of time_in in parts | |
time = time.gsub(',','.').split(':').reverse | |
#transform all time in a time value (seconds,microsseconds) | |
(time.size-1).downto(1) do |i| | |
time[i-1] = time[i-1].to_f + time[i].to_f * 60 | |
end | |
time[0] | |
end | |
# Transorms a real time representation into a string representation. | |
def self.string_time(time) | |
# get the seconds and minutes | |
all_minutes = (time / 60).to_i | |
all_seconds = time % 60 | |
# get the hours and minutes | |
hours = all_minutes / 60 | |
minutes = all_minutes % 60 | |
# filter seconds | |
seconds = all_seconds.to_i | |
# filter milliseconds | |
milliseconds = ((all_seconds - seconds) * 1000).round | |
"#{hours.to_s.rjust(2,'0')}:#{minutes.to_s.rjust(2,'0')}:#{seconds.to_s.rjust(2,'0')},#{milliseconds.to_s.rjust(3,'0')}" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#File: time_shifter_rspec.tb | |
=begin | |
RSpec test file for TimeShifter class. | |
Robison WR Santos (rwrsantos@gmail.com) | |
26/09/2009 | |
=end | |
require 'time_shifter' | |
describe TimeShifter do | |
it 'shoud have a way t shift an string' do | |
TimeShifter.should respond_to(:shift) | |
end | |
it 'should return the same string if only time_in is passed as parameter of | |
shift method and its finished with one zero' do | |
time_in = "01:02:03,450" | |
TimeShifter.shift(time_in).should == time_in | |
end | |
it 'should return the same string if only time_in is passed as parameter of | |
shift method and its finished with two zero' do | |
time_in = "01:02:03,400" | |
TimeShifter.shift(time_in).should == time_in | |
end | |
it 'should return the same string if only time_in is passed as parameter of | |
shift method and its finished with three zeros' do | |
time_in = "01:02:03,000" | |
TimeShifter.shift(time_in).should == time_in | |
end | |
it 'should return the same string if only time_in is passed as parameter of | |
shift method' do | |
time_in = "01:02:03,456" | |
TimeShifter.shift(time_in).should == time_in | |
end | |
it 'should have a way to tranform the string time into a float value' do | |
TimeShifter.should respond_to(:real_time) | |
end | |
it 'should return a value of seconds and milliseconds of an string | |
representation of time' do | |
string_time = "01:02:03,456" | |
TimeShifter.real_time(string_time).should == 3723.456 | |
end | |
it 'should have a way to tranform a real time array into its the string time | |
representation' do | |
TimeShifter.should respond_to(:real_time) | |
end | |
it 'should return a string representation of a real time array value containing | |
[seconds,milliseconds]' do | |
real_time = 3723.456000 | |
TimeShifter.string_time(real_time).should == "01:02:03,456" | |
end | |
it 'should add a time in seconds and milliseconds when an operation is not | |
provided for shifting' do | |
time_in = "01:02:03,456" | |
amount_to_shift = "65,432" | |
TimeShifter.shift(time_in,amount_to_shift).should == "01:03:08,888" | |
end | |
it 'should add a time in seconds and milliseconds when an operation "add" is | |
provided for shifting' do | |
time_in = "01:02:03,456" | |
amount_to_shift = "65,432" | |
TimeShifter.shift(time_in,amount_to_shift,"add").should == "01:03:08,888" | |
end | |
it 'should subtract a time in seconds and milliseconds when an operation "sub" | |
is provided for shifting' do | |
time_in = "01:02:03,456" | |
amount_to_shift = "65,432" | |
TimeShifter.shift(time_in,amount_to_shift,"sub").should == "01:00:58,024" | |
end | |
it 'should raise "InvalidOperation" when an operation different from "add" or | |
"sub" is provided' do | |
lambda {TimeShifter.shift("01:02:03,456","65,432","any")}.should raise_error(TimeShifter::InvalidOperation) | |
end | |
it 'should raise "IlegalTimeFormat" when an ilegal time format is provided | |
as time_in' do | |
lambda {TimeShifter.shift("00:00","65,432")}.should raise_error(TimeShifter::IlegalTimeFormat) | |
end | |
it 'should raise "IlegalTimeFormat" when an ilegal time format is provided as | |
amount_to_shift' do | |
lambda {TimeShifter.shift("00:00:00,000","65..00")}.should raise_error(TimeShifter::IlegalTimeFormat) | |
end | |
it 'should not raise "IlegalTimeFormat" when amount_to_shift is provided with | |
period instead of comma' do | |
lambda {TimeShifter.shift("00:00:00,000","65.0")}.should_not raise_error(TimeShifter::IlegalTimeFormat) | |
end | |
it 'should not raise "IlegalTimeFormat" when amount_to_shift is provided as | |
an integer (string without comma or period' do | |
lambda {TimeShifter.shift("00:00:00,000","65")}.should_not raise_error(TimeShifter::IlegalTimeFormat) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment