secret
anonymous / CharlesFeduke
Last active

  • Download Gist
CharlesFeduke
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
# Charles Feduke
require 'optparse'
require 'ostruct'
require 'time'
 
class Time
def to_s
self.strftime('%H:%M:%S') + ',' + (self.usec / 1_000).to_s
end
end
 
class ShiftSubtitle
 
def initialize(kernel=Kernel,file=File)
@kernel = kernel
@file = file
@options = OpenStruct.new({
:operation => :add,
:time => "0,000",
:input_file => "",
:output_file => ""
})
end
 
def process_arguments(args)
opts = OptionParser.new do |opts|
opts.banner = "Usage: shiftsubtitle.rb --operation ADD|SUB --time [TIME=0,000] INPUT_FILE OUTPUT_FILE"
opts.separator ""
opts.on("-o", "--operation OPERATION", [:add, :sub], "Add (add) or subtract (sub) TIME") do |op|
@options.operation = op
end
 
opts.on("-t", "--time TIME", "Amount of time in SECONDS,MILLISECONDS to shift by") do |time|
@options.time = time || '0,000'
if not @options.time =~ /^\d+$|^\d+[,]+\d{1,3}$/
puts "TIME must be in #,### format."
@kernel.exit
end
@options.time = @options.time.gsub!(/,/, '.').to_f
end
 
opts.on_tail("-h", "--help", "Shows this message") do
puts opts
@kernel.exit
end
end
 
opts.parse!(args)
@options.input_file, @options.output_file = args
 
if @options.input_file.nil? or
@options.input_file.length == 0 or
@options.output_file.nil? or
@options.output_file.length == 0
puts "Both INPUT_FILE and OUTPUT_FILE must be specified."
@kernel.exit
end
 
if not @options.input_file.nil? and not @options.input_file.length == 0 and not @file.exist?(@options.input_file)
puts "INPUT_FILE must exist."
@kernel.exit
end
 
@options
end
 
def transform
count = 0
@file.open(@options.output_file, 'w') do |outfile|
@file.open(@options.input_file, 'r') do |infile|
while !infile.eof
entry = SubRipEntry.new
begin
entry.process infile
rescue EOFError
end
entry.adjust @options.operation, @options.time
entry.output outfile
outfile.puts
count += 1
end #while
end #infile
end #outfile
count
end
 
end
 
class SubRipEntry
attr_reader :identifier, :start, :end, :lines
 
def process(file)
@identifier = file.readline.chomp.to_i
times = file.readline.chomp
@start, @end = times.split(' --> ', 2).map { |time| Time.parse(time) }
@lines = Array.new
while (line = file.readline)
line.chomp!
break if line.length == 0
@lines.push(line)
end
end
 
def adjust(operation, amount)
operation = (operation == :add ? :+ : :-)
@start, @end = [@start, @end].map {|time| time.send(operation, amount)}
end
 
def output(file)
[@identifier, @start.to_s + ' --> ' + @end.to_s, @lines].each {|line| file.puts(line)}
end
end
 
def stopwatch
start = Time.now
yield
Time.now - start
end
 
# this prevents the block of code within from executing during unit tests
if __FILE__ == $0
count = 0
elapsed = stopwatch do
ss = ShiftSubtitle.new
ss.process_arguments ARGV
count = ss.transform
end
puts "Processed #{count} entries in #{elapsed} seconds."
end
shiftsubtitle_spec.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
# shiftsubtitle_spec.rb
require 'shiftsubtitle'
require 'construct'
 
describe ShiftSubtitle do
include Construct::Helpers
before :each do
@mock_kernel = mock(Kernel)
@mock_kernel.stub!(:exit)
@mock_file = mock(File)
@mock_file.stub!(:exist?).and_return(true)
end
 
it "should exit cleanly when -h is used" do
ss = ShiftSubtitle.new(@mock_kernel)
@mock_kernel.should_receive(:exit)
ss.process_arguments(["-h"])
end
 
it "should throw an exception when operation is not add/sub" do
ss = ShiftSubtitle.new
lambda {
ss.process_arguments(["-o", "blah"])
}.should raise_error
end
 
it "should only accept the appropriate #,### format for time" do
ss = ShiftSubtitle.new(@mock_kernel, @mock_file)
@mock_kernel.should_receive(:exit)
ss.process_arguments(["-t", "x"])
ss.process_arguments(["-t", "34,"])
ss.process_arguments(["-t", "3333."])
ss.process_arguments(["-t", "22,4444"])
end
 
it "should require that INPUT_FILE and OUTPUT_FILE be specified" do
ss = ShiftSubtitle.new(@mock_kernel)
@mock_kernel.should_receive(:exit)
ss.process_arguments([""])
ss.process_arguments(["input.txt"])
end
 
it "should verify the INPUT_FILE exists" do
ss = ShiftSubtitle.new(@mock_kernel)
@mock_kernel.should_receive(:exit)
ss.process_arguments(["input.txt"])
end
 
it "should parse correctly if expected parameters are passed" do
ss = ShiftSubtitle.new(@mock_kernel, @mock_file)
options = ss.process_arguments(["-o", "add", "-t", "3,14", "input", "output"])
options.operation.should == :add
options = ss.process_arguments(["-o", "sub", "-t", "4.344", "input", "output"])
options.operation.should == :sub
end
 
it "should correctly process an input file and produce an output file" do
within_construct do |construct|
construct.file('unit_test_in.srt') do
< 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,
 
EOS
end
construct.file('unit_test_out.srt')
ss = ShiftSubtitle.new
ss.process_arguments(['-o', 'add', '-t', '0,500', 'unit_test_in.srt', 'unit_test_out.srt'])
ss.transform
 
entries = Array.new
File.open('unit_test_out.srt', 'r') do |f|
begin
while !f.eof do
entry = SubRipEntry.new
entry.process f
entries.push entry
end
rescue EOFError
f.close
end
end
 
entries[0].identifier.should == 645
entries[0].start.to_s.should == "01:31:51,710"
entries[0].end.to_s.should == "01:31:55,393"
entries[0].lines.length.should == 1
entries[0].lines[0].should == "the government is implementing a new policy..."
 
entries[1].identifier.should == 646
entries[1].start.to_s.should == "01:31:55,428"
entries[1].end.to_s.should == "01:31:58,164"
entries[1].lines.length.should == 2
entries[1].lines[0].should == "In connection with a dramatic increase"
entries[1].lines[1].should == "in crime in certain neighbourhoods,"
end # within
end
 
end
subripentry_test.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
# subripentry_test.rb
require 'test/unit'
require 'construct'
require 'shiftsubtitle'
 
class Pathname
# windows has problems with temp files created by Ruby
# http://redmine.ruby-lang.org/issues/show/1494
def rmtree
nil
end
end
 
class SubRipEntryTest < Test::Unit::TestCase
include Construct::Helpers
 
def test_process
entry = SubRipEntry
within_construct do |construct|
construct.file('unit_test.srt') do
< 01:31:57,664
In connection with a dramatic increase
in crime in certain neighbourhoods,
EOS
end
 
entry = SubRipEntry.new
 
file = File.open('unit_test.srt')
begin
entry.process file
rescue EOFError
file.close
end
 
assert_equal 646, entry.identifier
assert_equal '01:31:54,928', entry.start.to_s
assert_equal '01:31:57,664', entry.end.to_s
assert_equal 2, entry.lines.length
assert_equal 'In connection with a dramatic increase', entry.lines[0]
assert_equal 'in crime in certain neighbourhoods,', entry.lines[1]
end
 
end
 
def test_adjust
entry = SubRipEntry
within_construct do |construct|
construct.file('unit_test.srt') do
< 01:31:57,664
In connection with a dramatic increase
in crime in certain neighbourhoods,
EOS
end
 
entry = SubRipEntry.new
 
file = File.open('unit_test.srt')
begin
entry.process file
rescue EOFError
file.close
end
 
assert_equal '01:31:54,928', entry.start.to_s
assert_equal '01:31:57,664', entry.end.to_s
 
entry.adjust(:add, 2.500)
 
assert_equal '01:31:57,428', entry.start.to_s
assert_equal '01:32:00,164', entry.end.to_s
 
entry.adjust(:sub, 2.500)
 
assert_equal '01:31:54,928', entry.start.to_s
assert_equal '01:31:57,664', entry.end.to_s
end
 
end
 
def test_output
src, dst = SubRipEntry
within_construct do |construct|
construct.file('unit_test_in.srt') do
< 01:31:57,664
In connection with a dramatic increase
in crime in certain neighbourhoods,
EOS
end
 
src = SubRipEntry.new
 
file = File.open('unit_test_in.srt')
begin
src.process file
rescue EOFError
file.close
end
 
construct.file('unit_test_out.srt')
File.open('unit_test_out.srt', 'w') do |f|
src.output(f)
end
dst = SubRipEntry.new
File.open('unit_test_out.srt', 'r') do |f|
begin
dst.process f
rescue EOFError
f.close
end
end
 
assert_equal 646, dst.identifier
assert_equal '01:31:54,928', dst.start.to_s
assert_equal '01:31:57,664', dst.end.to_s
assert_equal 2, dst.lines.length
assert_equal 'In connection with a dramatic increase', dst.lines[0]
assert_equal 'in crime in certain neighbourhoods,', dst.lines[1]
end
 
end
 
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.