shift_subtitles 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby-debug'
4
+ require 'optparse'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/shift_subtitles'
7
+
8
+ options = {}
9
+
10
+ optparse = OptionParser.new do |opts|
11
+ options[:action] = nil
12
+ opts.on('--action OPERATION') { |action| options[:action] = action }
13
+
14
+ options[:time] = nil
15
+ opts.on('--time TIME') { |time| options[:time] = time }
16
+
17
+ options[:input] = nil
18
+ opts.on('--input INPUT') { |input| options[:input] = input }
19
+
20
+ options[:output] = nil
21
+ opts.on('--output OUTPUT') { |output| options[:output] = output }
22
+ end
23
+
24
+ optparse.parse!
25
+
26
+ begin
27
+ ShiftSubtitles::Process.shift_subtitles(options)
28
+ rescue => e
29
+ puts "OOPS! something went wrong......#{e.message}"
30
+ else
31
+ puts 'Shift Subtitle successfully run. Welcome to AV heaven'
32
+ end
@@ -0,0 +1,11 @@
1
+ class Numeric
2
+
3
+ def minutes_to_secs
4
+ self*60
5
+ end
6
+
7
+ def hours_to_secs
8
+ self*60*60
9
+ end
10
+
11
+ end
@@ -0,0 +1,16 @@
1
+ module ShiftSubtitles
2
+
3
+ module FileHelper
4
+
5
+ def self.operation_with_validation operation, file_name
6
+ raise("Please specify an #{operation} file") unless file_name
7
+ if operation == 'input'
8
+ raise("Please specify a valid #{operation} file") unless File.exists?(file_name)
9
+ end
10
+ yield
11
+ end
12
+
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,47 @@
1
+ module ShiftSubtitles
2
+
3
+ class Operation
4
+
5
+ attr_reader :action, :time
6
+
7
+ def initialize action, time
8
+ @action = action_with_validation(action)
9
+ @time = time_with_validation(time)
10
+ end
11
+
12
+ def seconds_difference
13
+ case action
14
+ when "add" then return time_difference
15
+ when "subtract" then return negative_time_difference
16
+ end
17
+ end
18
+
19
+ def action_with_validation action
20
+ valid_action?(action) ? action : raise("Please specify either 'add' or 'subtract' as the operation")
21
+ end
22
+
23
+ def time_with_validation time
24
+ raise("Please specify a time to shift") unless time
25
+ raise("Please specify a valid time to shift") unless valid_time?(time)
26
+ time
27
+ end
28
+
29
+ def valid_time? time
30
+ time =~ /\A[0-9]{2},[0-9]{3}\Z/
31
+ end
32
+
33
+ def valid_action? action
34
+ action =~ /\A(add|subtract)\Z/
35
+ end
36
+
37
+ def time_difference
38
+ time.sub(',','.').to_f
39
+ end
40
+
41
+ def negative_time_difference
42
+ -time_difference
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,20 @@
1
+ module ShiftSubtitles
2
+
3
+ module Process
4
+
5
+ def self.shift_subtitles options
6
+ operation = ShiftSubtitles::Operation.new(options[:action], options[:time])
7
+ subtitles = ShiftSubtitles::Subtitles.new(options[:input])
8
+ subtitles.update_subtitles(operation.seconds_difference)
9
+ create_and_populate_output_file(options[:output], subtitles)
10
+ end
11
+
12
+ def self.create_and_populate_output_file output_file_name, subtitles
13
+ ShiftSubtitles::FileHelper.operation_with_validation('output', output_file_name) do
14
+ File.open(output_file_name, 'w') { |file| file << subtitles.formatted_subtitles_for_file }
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,36 @@
1
+ require 'time'
2
+
3
+ module ShiftSubtitles
4
+
5
+ class Subtitle
6
+
7
+ attr_reader :index, :start_time, :end_time, :text
8
+
9
+ def initialize subtitle_string
10
+ subtitle_pattern =~ subtitle_string
11
+ @index, @start_time, @end_time, @text = $1, $2, $3, $4
12
+ end
13
+
14
+ def update_duration seconds_difference
15
+ @start_time = update_time(start_time, seconds_difference)
16
+ @end_time = update_time(end_time, seconds_difference)
17
+ end
18
+
19
+ def update_time time_string, seconds_difference
20
+ time = Time.parse(time_string)
21
+ updated_time = Time.parse(time_string) + seconds_difference
22
+ raise("Please specify a legal time to shift, this will push the subs into negative values") if updated_time.day < time.day
23
+ updated_time.strftime("%H:%M:%S,%L")
24
+ end
25
+
26
+ def format
27
+ "#{index}\n#{start_time} --> #{end_time}\n#{text}\n\n"
28
+ end
29
+
30
+ def subtitle_pattern
31
+ /([0-9]+)\n([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}) --> ([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3})\n(.*)/
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ module ShiftSubtitles
2
+
3
+ class Subtitles
4
+
5
+ include Enumerable
6
+
7
+ attr_accessor :subtitle_list
8
+
9
+ def initialize input_file_path
10
+ input_file_subtitles = input_file_contents(input_file_path).scan(input_file_pattern)
11
+ @subtitle_list = input_file_subtitles.collect { |subtitle| ShiftSubtitles::Subtitle.new(subtitle) }
12
+ end
13
+
14
+ def each
15
+ @subtitle_list.each {|subtitle| yield(subtitle)}
16
+ end
17
+
18
+ def update_subtitles seconds_difference
19
+ each { |subtitle| subtitle.update_duration(seconds_difference) }
20
+ end
21
+
22
+ def formatted_subtitles_for_file
23
+ collect { |subtitle| subtitle.format }.join
24
+ end
25
+
26
+ def input_file_contents input_file_path
27
+ ShiftSubtitles::FileHelper.operation_with_validation('input', input_file_path) { File.read(input_file_path) }
28
+ end
29
+
30
+ def input_file_pattern
31
+ /[0-9]+\n[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3} --> [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}\n.*/
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/shift_subtitles/process'
2
+ require File.dirname(__FILE__) + '/shift_subtitles/operation'
3
+ require File.dirname(__FILE__) + '/shift_subtitles/subtitles'
4
+ require File.dirname(__FILE__) + '/shift_subtitles/subtitle'
5
+ require File.dirname(__FILE__) + '/shift_subtitles/file_helper'
6
+ require File.dirname(__FILE__) + '/shift_subtitles/core_ext/numeric'
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Numeric do
4
+
5
+ it "converts a 1 minute into 60 seconds" do
6
+ 1.minutes_to_secs.should == 60
7
+ end
8
+
9
+ it "converts a 1 hour into 3600 seconds" do
10
+ 1.hours_to_secs.should == 3600
11
+ end
12
+
13
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe ShiftSubtitles::FileHelper do
4
+
5
+ before(:each) do
6
+ @block = lambda { 'yielded_value' }
7
+ end
8
+
9
+ context "output file" do
10
+
11
+ it "yields the block when a file_name & block are passed in" do
12
+ File.should_not_receive(:exists?)
13
+ yielded_value = ShiftSubtitles::FileHelper.operation_with_validation('output', 'filename.txt', &@block)
14
+ yielded_value.should == 'yielded_value'
15
+ end
16
+
17
+ it "raises a invalid file error when no file_name & block are passed in" do
18
+ File.should_not_receive(:exists?)
19
+ lambda { ShiftSubtitles::FileHelper.operation_with_validation('output', nil, &@block) }.should raise_error("Please specify an output file")
20
+ end
21
+
22
+ end
23
+
24
+ context "input file" do
25
+
26
+ it "yields the block when a file_name & block are passed in" do
27
+ File.should_receive(:exists?).with('filename.txt').and_return(true)
28
+ yielded_value = ShiftSubtitles::FileHelper.operation_with_validation('input', 'filename.txt', &@block)
29
+ yielded_value.should == 'yielded_value'
30
+ end
31
+
32
+ it "raises a invalid file error when no file_name is passed in" do
33
+ File.should_not_receive(:exists?)
34
+ lambda { ShiftSubtitles::FileHelper.operation_with_validation('input', nil, &@block) }.should raise_error("Please specify an input file")
35
+ end
36
+
37
+ it "raises a file doesn't exist error when an invalid file_name is passed in" do
38
+ File.should_receive(:exists?).with('invalid.txt').and_return(false)
39
+ lambda { ShiftSubtitles::FileHelper.operation_with_validation('input', 'invalid.txt', &@block) }.should raise_error("Please specify a valid input file")
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe ShiftSubtitles::Operation do
4
+
5
+ context "operation adds 2 seconds" do
6
+
7
+ before(:each) do
8
+ @operation = ShiftSubtitles::Operation.new('add', '02,000')
9
+ end
10
+
11
+ it "sets the action on initialization" do
12
+ @operation.action.should == 'add'
13
+ end
14
+
15
+ it "sets the time on initialization" do
16
+ @operation.time.should == '02,000'
17
+ end
18
+
19
+ it "converts the time and action to a float of the amount of seconds to change" do
20
+ @operation.seconds_difference.should == 2.0
21
+ end
22
+
23
+ end
24
+
25
+ context "operation subtracts 1.5 seconds" do
26
+
27
+ before(:each) do
28
+ @operation = ShiftSubtitles::Operation.new('subtract', '01,500')
29
+ end
30
+
31
+ it "converts the time and action to a float of the amount of seconds to change" do
32
+ @operation.seconds_difference.should == -1.5
33
+ end
34
+
35
+ end
36
+
37
+ context "invalid scenarios" do
38
+
39
+ it "no time is passed in on initialization raises an error" do
40
+ lambda { @operation = ShiftSubtitles::Operation.new('subtract', nil) }.should raise_error("Please specify a time to shift")
41
+ end
42
+
43
+ it "invalid time is passed in on initialization raises an error" do
44
+ lambda { @operation = ShiftSubtitles::Operation.new('subtract', 'rogue-time') }.should raise_error("Please specify a valid time to shift")
45
+ end
46
+
47
+ it "invalid time is passed in on initialization raises an error" do
48
+ lambda { @operation = ShiftSubtitles::Operation.new('subtract', '111,889') }.should raise_error("Please specify a valid time to shift")
49
+ end
50
+
51
+ it "invalid time is passed in on initialization raises an error" do
52
+ lambda { @operation = ShiftSubtitles::Operation.new('ssubtractt', '02,222') }.should raise_error("Please specify either 'add' or 'subtract' as the operation")
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ShiftSubtitles::Process do
4
+
5
+ context "valid input" do
6
+
7
+ it "reads in the output file name and creates the file" do
8
+ options = {:action => "add", :time => "2,000", :input => "input.srt", :output => "output.srt"}
9
+ mock_operation = mock(ShiftSubtitles::Operation, :seconds_difference => 2.0)
10
+ mock_subtitles = mock(ShiftSubtitles::Subtitles, :formatted_subtitles => 'formatted_subtitles')
11
+ ShiftSubtitles::Operation.should_receive(:new).with("add", "2,000").and_return(mock_operation)
12
+ ShiftSubtitles::Subtitles.should_receive(:new).with("input.srt").and_return(mock_subtitles)
13
+ file_block = lambda { File.open(options[:output], 'w') { |file| file << mock_subtitles.formatted_subtitles_for_file } }
14
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).with('output', options[:output], &file_block)
15
+ mock_subtitles.should_receive(:update_subtitles).with(2.0)
16
+ mock_operation.should_receive(:seconds_difference).with()
17
+ mock_subtitles.should_receive(:formatted_subtitles_for_file).with()
18
+ ShiftSubtitles::Process.shift_subtitles(options)
19
+ end
20
+
21
+ end
22
+
23
+ context "invalid input" do
24
+
25
+ it "fails when no output file is specified" do
26
+ options = {:action => "add", :time => "2,000", :input => "input.srt"}
27
+ mock_operation = mock(ShiftSubtitles::Operation, :seconds_difference => 2.0)
28
+ mock_subtitles = mock(ShiftSubtitles::Subtitles)
29
+ ShiftSubtitles::Operation.should_receive(:new).with("add", "2,000").and_return(mock_operation)
30
+ ShiftSubtitles::Subtitles.should_receive(:new).with("input.srt").and_return(mock_subtitles)
31
+ file_block = lambda { File.open(nil, 'w') { |file| file << mock_subtitles.formatted_subtitles_for_file } }
32
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).with('output', nil, &file_block)
33
+ mock_subtitles.should_receive(:update_subtitles).with(2.0)
34
+ mock_operation.should_receive(:seconds_difference).with()
35
+ mock_subtitles.should_not_receive(:formatted_subtitles_for_file)
36
+
37
+ lambda { ShiftSubtitles::Process.shift_subtitles(options) }.should raise_error
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe ShiftSubtitles::Subtitle do
4
+
5
+ context "valid subtitle" do
6
+
7
+ before(:each) do
8
+ @subtitle = ShiftSubtitles::Subtitle.new("1\n01:31:51,210 --> 01:31:54,893\ntext")
9
+ end
10
+
11
+ it "sets an index on initialization" do
12
+ @subtitle.index.should == '1'
13
+ end
14
+
15
+ it "sets the start_time on initialization" do
16
+ @subtitle.start_time.should == '01:31:51,210'
17
+ end
18
+
19
+ it "sets the end_time on initialization" do
20
+ @subtitle.end_time.should == '01:31:54,893'
21
+ end
22
+
23
+ it "sets the text on initialization" do
24
+ @subtitle.text.should == 'text'
25
+ end
26
+
27
+ it "adds 2 seconds to the start_time and end_time" do
28
+ seconds_difference = 2.0
29
+ @subtitle.update_duration(seconds_difference)
30
+ @subtitle.start_time.should == '01:31:53,210'
31
+ @subtitle.end_time.should == '01:31:56,893'
32
+ end
33
+
34
+ it "subtracts 1.5 seconds to the start_time and end_time" do
35
+ seconds_difference = -1.5
36
+ @subtitle.update_duration(seconds_difference)
37
+ @subtitle.start_time.should == '01:31:49,710'
38
+ @subtitle.end_time.should == '01:31:53,393'
39
+ end
40
+
41
+ it "formats index, start_time, end_time, text for output" do
42
+ formatted_subtitle = @subtitle.format
43
+ formatted_subtitle.should == "1\n01:31:51,210 --> 01:31:54,893\ntext\n\n"
44
+ end
45
+
46
+ end
47
+
48
+ context "valid subtitle with illegal seconds difference" do
49
+
50
+ it "raises an error for negative times" do
51
+ @subtitle = ShiftSubtitles::Subtitle.new("1\n00:00:01,210 --> 00:00:01,893\ntext")
52
+ seconds_difference = -3.0
53
+ lambda { @subtitle.update_duration(seconds_difference) }.should raise_error("Please specify a legal time to shift, this will push the subs into negative values")
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe ShiftSubtitles::Subtitles do
4
+
5
+ context "a file with 1 subtitle is input" do
6
+
7
+ before(:each) do
8
+ input_file_contents = "1\n01:31:51,210 --> 01:31:54,893\ntext"
9
+ @mock_subtitle = mock(ShiftSubtitles::Subtitle)
10
+ @seconds_difference = 2.0
11
+ File.should_receive(:read).with('input.srt').and_return(input_file_contents)
12
+ file_block = lambda { File.read('input.srt') }
13
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).with('input', 'input.srt', &file_block)
14
+ ShiftSubtitles::Subtitle.should_receive(:new).once.with("1\n01:31:51,210 --> 01:31:54,893\ntext").and_return(@mock_subtitle)
15
+ @subtitles = ShiftSubtitles::Subtitles.new('input.srt')
16
+ end
17
+
18
+ it "is initialized with a file containing 1 subtitle" do
19
+ @subtitles.subtitle_list.count.should == 1
20
+ end
21
+
22
+ it "includes the Enumerable each method to access the subtitle_list" do
23
+ @subtitles.count.should == 1
24
+ end
25
+
26
+ it "updates each subtitle using the Operation" do
27
+ @mock_subtitle.should_receive(:update_duration).with(@seconds_difference)
28
+ updated_subtitles = @subtitles.update_subtitles(@seconds_difference)
29
+ end
30
+
31
+ it "formats the subtitles for output" do
32
+ @mock_subtitle.should_receive(:format).once.and_return("1\n01:31:51,210 --> 01:31:54,893\ntext\n\n")
33
+ formatted_subtitles = @subtitles.formatted_subtitles_for_file
34
+ formatted_subtitles.should == "1\n01:31:51,210 --> 01:31:54,893\ntext\n\n"
35
+ end
36
+
37
+ end
38
+
39
+ context "a file with 2 subtitles is input" do
40
+
41
+ before(:each) do
42
+ input_file_contents = "1\n01:31:51,210 --> 01:31:54,893\ntext\n\n2\n01:31:54,210 --> 01:31:57,893\nmore text\n\n\n"
43
+ @mock_subtitle = mock(ShiftSubtitles::Subtitle)
44
+ @seconds_difference = 2.0
45
+ File.should_receive(:read).with('input.srt').and_return(input_file_contents)
46
+ file_block = lambda { File.read('input.srt') }
47
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).with('input', 'input.srt', &file_block)
48
+ ShiftSubtitles::Subtitle.should_receive(:new).once.with("1\n01:31:51,210 --> 01:31:54,893\ntext").and_return(@mock_subtitle)
49
+ ShiftSubtitles::Subtitle.should_receive(:new).once.with("2\n01:31:54,210 --> 01:31:57,893\nmore text").and_return(@mock_subtitle)
50
+ @subtitles = ShiftSubtitles::Subtitles.new('input.srt')
51
+ end
52
+
53
+ it "is initialized with a file containing 2 subtitles" do
54
+ @subtitles.subtitle_list.count.should == 2
55
+ end
56
+
57
+ end
58
+
59
+ context "invalid input file" do
60
+
61
+ it "is initialized with no file" do
62
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).and_raise
63
+ lambda { ShiftSubtitles::Subtitles.new(nil) }.should raise_error
64
+ end
65
+
66
+ it "is initialized with an invalid filename" do
67
+ ShiftSubtitles::FileHelper.should_receive(:operation_with_validation).and_raise
68
+ lambda { ShiftSubtitles::Subtitles.new('dud') }.should raise_error
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/../lib/shift_subtitles'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.color_enabled = true
6
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shift_subtitles
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - James Shipton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-10-31 00:00:00 +00:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "2.6"
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: cucumber
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 1.1.0
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: aruba
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 0.4.6
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: "Shift Subtitles - Ruby Learning Challenge #1"
50
+ email:
51
+ - ionysis@gmail.com
52
+ executables:
53
+ - shift_subtitles
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - lib/shift_subtitles/core_ext/numeric.rb
60
+ - lib/shift_subtitles/file_helper.rb
61
+ - lib/shift_subtitles/operation.rb
62
+ - lib/shift_subtitles/process.rb
63
+ - lib/shift_subtitles/subtitle.rb
64
+ - lib/shift_subtitles/subtitles.rb
65
+ - lib/shift_subtitles.rb
66
+ - spec/shift_subtitles/core_ext/numeric_spec.rb
67
+ - spec/shift_subtitles/file_helper_spec.rb
68
+ - spec/shift_subtitles/operation_spec.rb
69
+ - spec/shift_subtitles/process_spec.rb
70
+ - spec/shift_subtitles/subtitle_spec.rb
71
+ - spec/shift_subtitles/subtitles_spec.rb
72
+ - spec/spec_helper.rb
73
+ - bin/shift_subtitles
74
+ has_rdoc: true
75
+ homepage: https://github.com/jamesshipton/quizzes/shift_subtitles
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options: []
80
+
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "0"
95
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.6.2
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: "Shift Subtitles - Ruby Learning Challenge #1"
102
+ test_files:
103
+ - spec/shift_subtitles/core_ext/numeric_spec.rb
104
+ - spec/shift_subtitles/file_helper_spec.rb
105
+ - spec/shift_subtitles/operation_spec.rb
106
+ - spec/shift_subtitles/process_spec.rb
107
+ - spec/shift_subtitles/subtitle_spec.rb
108
+ - spec/shift_subtitles/subtitles_spec.rb
109
+ - spec/spec_helper.rb