Skip to content

Instantly share code, notes, and snippets.

/RobisonWRSantos Secret

Created October 5, 2009 03:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/92e2a8426d44a3d465ab to your computer and use it in GitHub Desktop.
Save anonymous/92e2a8426d44a3d465ab to your computer and use it in GitHub Desktop.
#!/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
#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
#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