srt 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Mzk5Mjk2ZGU5NjI2NTZjYWVmN2JkN2E1MTY4ZjJkMmEzZmRmNzA5ZA==
5
+ data.tar.gz: !binary |-
6
+ MTIxY2EyYTczM2Q2NTg1NjZlZGNlNDQ4ZGQ4MDRmZDBmOWE3OTA1Yg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MTQyM2M1Y2I4ZDY5MGJkNTk3MWVkNGJkNzk5ZWI5MTJkYjZkMjBiYzhmMDY0
10
+ YThlZDQyOWQ5YzRmYjNjODkwYjFmMGM4ZjQ1ZTgwN2QwMTJiOWM3Y2I0Y2M1
11
+ Njc4NTg5ZjI5ZTNiZTAxZjYzYzQzZjg2YmFjODY5NzUyOTVjOTE=
12
+ data.tar.gz: !binary |-
13
+ NzIyNzExYzBiMjYzZWMzOGVkZDMyZjhiN2EyNWNmNGZhNThhNzkwMDhmNGRk
14
+ NGE3ZTMxMjA5YmNlYjJmYmNmZmZmYTgwNDIzOWJkZjA2YzE2YzAyZThlOGY5
15
+ ZTNhYTBlN2ZiYmFlYmY2ZmM4NzI0ODc5NTg4MmFmNDY5YjE1NTE=
data/lib/srt.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require "srt/file"
2
2
  require "srt/line"
3
- require "srt/version"
3
+ require "srt/parser"
4
+ require "srt/version"
@@ -16,26 +16,26 @@ module SRT
16
16
  end
17
17
 
18
18
  def self.parse_string(srt_data)
19
- result = SRT::File.new
20
- line = SRT::Line.new
19
+ result = new
20
+ line = Line.new
21
21
 
22
22
  split_srt_data(srt_data).each_with_index do |str, index|
23
23
  begin
24
24
  if str.strip.empty?
25
25
  result.lines << line unless line.empty?
26
- line = SRT::Line.new
26
+ line = Line.new
27
27
  elsif !line.error
28
28
  if line.sequence.nil?
29
29
  line.sequence = str.to_i
30
30
  elsif line.start_time.nil?
31
31
  if mres = str.match(/(?<start_timecode>[^[[:space:]]]+) -+> (?<end_timecode>[^[[:space:]]]+) ?(?<display_coordinates>X1:\d+ X2:\d+ Y1:\d+ Y2:\d+)?/)
32
32
 
33
- if (line.start_time = SRT::File.parse_timecode(mres["start_timecode"])) == nil
33
+ if (line.start_time = Parser.timecode(mres["start_timecode"])) == nil
34
34
  line.error = "#{index}, Invalid formatting of start timecode, [#{mres["start_timecode"]}]"
35
35
  $stderr.puts line.error if @debug
36
36
  end
37
37
 
38
- if (line.end_time = SRT::File.parse_timecode(mres["end_timecode"])) == nil
38
+ if (line.end_time = Parser.timecode(mres["end_timecode"])) == nil
39
39
  line.error = "#{index}, Invalid formatting of end timecode, [#{mres["end_timecode"]}]"
40
40
  $stderr.puts line.error if @debug
41
41
  end
@@ -50,7 +50,6 @@ module SRT
50
50
  else
51
51
  line.text << str.strip
52
52
  end
53
-
54
53
  end
55
54
  rescue
56
55
  line.error = "#{index}, General Error, [#{str}]"
@@ -76,8 +75,8 @@ module SRT
76
75
  end
77
76
 
78
77
  def append(options)
79
- if options.length == 1 && options.values[0].class == SRT::File
80
- reshift = SRT::File.parse_timecode(options.keys[0]) || (lines.last.end_time + SRT::File.parse_timespan(options.keys[0]))
78
+ if options.length == 1 && options.values[0].class == self.class
79
+ reshift = Parser.timecode(options.keys[0]) || (lines.last.end_time + Parser.timespan(options.keys[0]))
81
80
  renumber = lines.last.sequence
82
81
 
83
82
  options.values[0].lines.each do |line|
@@ -97,15 +96,15 @@ module SRT
97
96
  split_points = []
98
97
 
99
98
  if (options[:at])
100
- split_points = [options[:at]].flatten.map{ |timecode| SRT::File.parse_timecode(timecode) }.sort
99
+ split_points = [options[:at]].flatten.map{ |timecode| Parser.timecode(timecode) }.sort
101
100
  elsif (options[:every])
102
- interval = SRT::File.parse_timecode(options[:every])
101
+ interval = Parser.timecode(options[:every])
103
102
  max = lines.last.end_time
104
103
  (interval..max).step(interval){ |t| split_points << t }
105
104
  end
106
105
 
107
106
  if (split_points.count > 0)
108
- split_offsprings = [SRT::File.new]
107
+ split_offsprings = [File.new]
109
108
 
110
109
  reshift = 0
111
110
  renumber = 0
@@ -132,7 +131,7 @@ module SRT
132
131
  reshift = split_points.first
133
132
  split_points.delete_at(0)
134
133
 
135
- split_offsprings << SRT::File.new
134
+ split_offsprings << File.new
136
135
  cloned_line = line.clone
137
136
  cloned_line.sequence -= renumber if options[:renumber]
138
137
  if options[:timeshift]
@@ -145,7 +144,7 @@ module SRT
145
144
  reshift = split_points.first
146
145
  split_points.delete_at(0)
147
146
 
148
- split_offsprings << SRT::File.new
147
+ split_offsprings << File.new
149
148
  cloned_line = line.clone
150
149
  cloned_line.sequence -= renumber if options[:renumber]
151
150
  if options[:timeshift]
@@ -162,12 +161,12 @@ module SRT
162
161
 
163
162
  def timeshift(options)
164
163
  if options.length == 1
165
- if options[:all] && (seconds = SRT::File.parse_timespan(options[:all]))
164
+ if options[:all] && (seconds = Parser.timespan(options[:all]))
166
165
  lines.each do |line|
167
166
  line.start_time += seconds
168
167
  line.end_time += seconds
169
168
  end
170
- elsif (original_framerate = SRT::File.parse_framerate(options.keys[0])) && (target_framerate = SRT::File.parse_framerate(options.values[0]))
169
+ elsif (original_framerate = Parser.framerate(options.keys[0])) && (target_framerate = Parser.framerate(options.values[0]))
171
170
  ratio = target_framerate / original_framerate
172
171
  lines.each do |line|
173
172
  line.start_time *= ratio
@@ -178,16 +177,16 @@ module SRT
178
177
  origins, targets = options.keys, options.values
179
178
 
180
179
  [0,1].each do |i|
181
- if origins[i].is_a?(String) && SRT::File.parse_id(origins[i])
182
- origins[i] = lines[SRT::File.parse_id(origins[i]) - 1].start_time
183
- elsif origins[i].is_a?(String) && SRT::File.parse_timecode(origins[i])
184
- origins[i] = SRT::File.parse_timecode(origins[i])
180
+ if origins[i].is_a?(String) && Parser.id(origins[i])
181
+ origins[i] = lines[Parser.id(origins[i]) - 1].start_time
182
+ elsif origins[i].is_a?(String) && Parser.timecode(origins[i])
183
+ origins[i] = Parser.timecode(origins[i])
185
184
  end
186
185
 
187
- if targets[i].is_a?(String) && SRT::File.parse_timecode(targets[i])
188
- targets[i] = SRT::File.parse_timecode(targets[i])
189
- elsif targets[i].is_a?(String) && SRT::File.parse_timespan(targets[i])
190
- targets[i] = origins[i] + SRT::File.parse_timespan(targets[i])
186
+ if targets[i].is_a?(String) && Parser.timecode(targets[i])
187
+ targets[i] = Parser.timecode(targets[i])
188
+ elsif targets[i].is_a?(String) && Parser.timespan(targets[i])
189
+ targets[i] = origins[i] + Parser.timespan(targets[i])
191
190
  end
192
191
  end
193
192
 
@@ -231,33 +230,5 @@ eos
231
230
  def errors
232
231
  lines.collect { |l| l.error if l.error }.compact
233
232
  end
234
-
235
- protected
236
-
237
- def self.parse_framerate(framerate_string)
238
- mres = framerate_string.match(/(?<fps>\d+((\.)?\d+))(fps)/)
239
- mres ? mres["fps"].to_f : nil
240
- end
241
-
242
- def self.parse_id(id_string)
243
- mres = id_string.match(/#(?<id>\d+)/)
244
- mres ? mres["id"].to_i : nil
245
- end
246
-
247
- def self.parse_timecode(timecode_string)
248
- mres = timecode_string.match(/(?<h>\d+):(?<m>\d+):(?<s>\d+),(?<ms>\d+)/)
249
- mres ? "#{mres["h"].to_i * 3600 + mres["m"].to_i * 60 + mres["s"].to_i}.#{mres["ms"]}".to_f : nil
250
- end
251
-
252
- def self.parse_timespan(timespan_string)
253
- factors = {
254
- "ms" => 0.001,
255
- "s" => 1,
256
- "m" => 60,
257
- "h" => 3600
258
- }
259
- mres = timespan_string.match(/(?<amount>(\+|-)?\d+((\.)?\d+)?)(?<unit>ms|s|m|h)/)
260
- mres ? mres["amount"].to_f * factors[mres["unit"]] : nil
261
- end
262
233
  end
263
234
  end
@@ -0,0 +1,31 @@
1
+ module SRT
2
+ class Parser
3
+ class << self
4
+ def framerate(framerate_string)
5
+ mres = framerate_string.match(/(?<fps>\d+((\.)?\d+))(fps)/)
6
+ mres ? mres["fps"].to_f : nil
7
+ end
8
+
9
+ def id(id_string)
10
+ mres = id_string.match(/#(?<id>\d+)/)
11
+ mres ? mres["id"].to_i : nil
12
+ end
13
+
14
+ def timecode(timecode_string)
15
+ mres = timecode_string.match(/(?<h>\d+):(?<m>\d+):(?<s>\d+),(?<ms>\d+)/)
16
+ mres ? "#{mres["h"].to_i * 3600 + mres["m"].to_i * 60 + mres["s"].to_i}.#{mres["ms"]}".to_f : nil
17
+ end
18
+
19
+ def timespan(timespan_string)
20
+ factors = {
21
+ "ms" => 0.001,
22
+ "s" => 1,
23
+ "m" => 60,
24
+ "h" => 3600
25
+ }
26
+ mres = timespan_string.match(/(?<amount>(\+|-)?\d+((\.)?\d+)?)(?<unit>ms|s|m|h)/)
27
+ mres ? mres["amount"].to_f * factors[mres["unit"]] : nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module SRT
2
- VERSION = "0.0.10"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,440 @@
1
+ require 'spec_helper'
2
+ require 'srt'
3
+
4
+ describe SRT::File do
5
+ describe '#parse' do
6
+ context "parsing with debug true" do
7
+ it "should be verbose when failing" do
8
+ $stderr.should_receive(:puts).once
9
+ SRT::File.parse(File.open("./spec/fixtures/invalid.srt"), debug: true).errors.should_not be_empty
10
+ end
11
+ end
12
+ context "parsing with debug false" do
13
+ it "should raise exception silently" do
14
+ $stderr.should_not_receive(:puts)
15
+ SRT::File.parse(File.open("./spec/fixtures/invalid.srt")).errors.should_not be_empty
16
+ end
17
+ end
18
+ end
19
+
20
+ shared_examples_for "an SRT file" do
21
+ context "when parsing a properly formatted BSG SRT file" do
22
+ it "should return an SRT::File" do
23
+ subject.class.should eq(SRT::File)
24
+ end
25
+
26
+ it "should have 600 lines" do
27
+ subject.lines.size.should eq(600)
28
+ end
29
+
30
+ it "should have no errors" do
31
+ subject.errors.should be_empty
32
+ end
33
+
34
+ it "should have the expected sequence number on the first subtitle" do
35
+ subject.lines.first.sequence.should eq(1)
36
+ end
37
+
38
+ it "should have the expected timecodes on the first subtitle" do
39
+ subject.lines.first.time_str.should eq("00:00:02,110 --> 00:00:04,578")
40
+ end
41
+
42
+ it "should have the expected text on the first subtitle" do
43
+ subject.lines.first.text.should eq(["<i>(male narrator) Previously", "on Battlestar Galactica.</i>"])
44
+ end
45
+
46
+ it "should have the expected sequence number on the last subtitle" do
47
+ subject.lines.last.sequence.should eq(600)
48
+ end
49
+
50
+ it "should have the expected timecodes on the last subtitle" do
51
+ subject.lines.last.time_str.should eq("00:43:26,808 --> 00:43:28,139")
52
+ end
53
+
54
+ it "should have the expected text on the last subtitle" do
55
+ subject.lines.last.text.should eq(["Thank you."])
56
+ end
57
+ end
58
+ end
59
+
60
+ describe ".parse with uncommon formats" do
61
+ context "when parsing a spanish language WOTW SRT file with unknown encoding" do
62
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/wotw-dubious.srt")) }
63
+
64
+ it "should parse" do
65
+ file.class.should eq(SRT::File)
66
+ end
67
+
68
+ it "should have 1123 lines" do
69
+ file.lines.size.should eq(1123)
70
+ end
71
+
72
+ it "should have no errors" do
73
+ file.errors.should be_empty
74
+ end
75
+ end
76
+
77
+ context "when parsing a dummy SRT file containing display coordinates" do
78
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/coordinates-dummy.srt")) }
79
+
80
+ it "should return an SRT::File" do
81
+ file.class.should eq(SRT::File)
82
+ end
83
+
84
+ it "should have 3 lines" do
85
+ file.lines.size.should eq(3)
86
+ end
87
+
88
+ it "should have no errors" do
89
+ file.errors.should be_empty
90
+ end
91
+
92
+ it "should have the expected display coordinates on the first subtitle" do
93
+ file.lines.first.display_coordinates.should eq("X1:100 X2:600 Y1:1 Y2:4")
94
+ end
95
+
96
+ it "should have the expected display coordinates on the last subtitle" do
97
+ file.lines.last.display_coordinates.should eq("X1:1 X2:333 Y1:50 Y2:29")
98
+ end
99
+ end
100
+ end
101
+
102
+ describe SRT::File, "when initialized with a valid BSG SRT string" do
103
+ subject { SRT::File.parse(File.read("./spec/fixtures/bsg-s01e01.srt")) }
104
+ it_should_behave_like "an SRT file"
105
+ end
106
+
107
+ describe SRT::File, "when initialized with a valid BSG SRT File" do
108
+ subject { SRT::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
109
+ it_should_behave_like "an SRT file"
110
+ end
111
+
112
+ describe "#append" do
113
+ context "when calling it on the first (part1) of two seperate SRT files for Black Swan" do
114
+ let(:part1) { SRT::File.parse(File.open("./spec/fixtures/blackswan-part1.srt")) }
115
+ let(:part2) { SRT::File.parse(File.open("./spec/fixtures/blackswan-part2.srt")) }
116
+
117
+ context "when passing { \"00:53:57,241\" => part2 }" do
118
+ before { part1.append({ "00:53:57,241" => part2 }) }
119
+
120
+ it "should have grown to 808 subtitles" do
121
+ part1.lines.length.should eq(808)
122
+ end
123
+
124
+ it "should have appended subtitles starting with sequence number 448" do
125
+ part1.lines[447].sequence.should eq(448)
126
+ end
127
+
128
+ it "should have appended subtitles ending with sequence number 808" do
129
+ part1.lines.last.sequence.should eq(808)
130
+ end
131
+
132
+ it "should have appended subtitles relatively from 00:53:57,241" do
133
+ part1.lines[447].time_str.should eq("00:54:02,152 --> 00:54:04,204")
134
+ end
135
+ end
136
+
137
+ context "when passing { \"+7.241s\" => part2 }" do
138
+ before { part1.append({ "+7.241s" => part2 }) }
139
+
140
+ it "should have appended subtitles relatively from +7.241s after the previously last subtitle" do
141
+ part1.lines[447].time_str.should eq("00:54:02,283 --> 00:54:04,335")
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ describe "#split" do
148
+ context "when calling it on a properly formatted BSG SRT file" do
149
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
150
+
151
+ context "when passing { :at => \"00:19:24,500\" }" do
152
+ let(:result) { file.split( :at => "00:19:24,500" ) }
153
+
154
+ it "should return an array containing two SRT::File instances" do
155
+ result.length.should eq(2)
156
+ result[0].class.should eq(SRT::File)
157
+ result[1].class.should eq(SRT::File)
158
+ end
159
+
160
+ it "should include a subtitle that overlaps a splitting point in the first file" do
161
+ result[0].lines.last.text.should eq(["I'll see you guys in combat."])
162
+ end
163
+
164
+ it "should make an overlapping subtitle end at the splitting point in the first file" do
165
+ result[0].lines.last.time_str.should eq("00:19:23,901 --> 00:19:24,500")
166
+ end
167
+
168
+ it "should include a subtitle that overlaps a splitting point in the second file as well" do
169
+ result[1].lines.first.text.should eq(["I'll see you guys in combat."])
170
+ end
171
+
172
+ it "should make an overlapping subtitle remain at the beginning in the second file" do
173
+ result[1].lines.first.time_str.should eq("00:00:00,000 --> 00:00:01,528")
174
+ end
175
+
176
+ it "should shift back all timecodes of the second file relative to the new file beginning" do
177
+ result[1].lines[1].time_str.should eq("00:00:01,737 --> 00:00:03,466")
178
+ end
179
+ end
180
+
181
+ context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do
182
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) }
183
+
184
+ it "should return an array containing two SRT::File instances" do
185
+ result.length.should eq(2)
186
+ result[0].class.should eq(SRT::File)
187
+ result[1].class.should eq(SRT::File)
188
+ end
189
+
190
+ it "should include a subtitle that overlaps a splitting point in the first file" do
191
+ result[0].lines.last.text.should eq(["I'll see you guys in combat."])
192
+ end
193
+
194
+ it "should not make an overlapping subtitle end at the splitting point in the first file" do
195
+ result[0].lines.last.time_str.should eq("00:19:23,901 --> 00:19:26,028")
196
+ end
197
+
198
+ it "should include a subtitle that overlaps a splitting point in the second file as well" do
199
+ result[1].lines.first.text.should eq(["I'll see you guys in combat."])
200
+ end
201
+
202
+ it "should not make an overlapping subtitle remain at the beginning in the second file" do
203
+ result[1].lines.first.time_str.should eq("00:19:23,901 --> 00:19:26,028")
204
+ end
205
+
206
+ it "should not shift back timecodes of the second file relative to the new file beginning" do
207
+ result[1].lines[1].time_str.should eq("00:19:26,237 --> 00:19:27,966")
208
+ end
209
+ end
210
+
211
+ context "when passing { :at => [\"00:15:00,000\", \"00:30:00,000\"] }" do
212
+ let(:result) { file.split( :at => ["00:15:00,000", "00:30:00,000"] ) }
213
+
214
+ it "should return an array containing three SRT::File instances" do
215
+ result.length.should eq(3)
216
+ result[0].class.should eq(SRT::File)
217
+ result[1].class.should eq(SRT::File)
218
+ result[2].class.should eq(SRT::File)
219
+ end
220
+
221
+ it "should let subtitles start at sequence number #1 in all three files" do
222
+ result[0].lines.first.sequence.should eq(1)
223
+ result[1].lines.first.sequence.should eq(1)
224
+ result[2].lines.first.sequence.should eq(1)
225
+ end
226
+
227
+ it "should put 176 subtitles in the first file" do
228
+ result[0].lines.length.should eq(176)
229
+ result[0].lines.last.sequence.should eq(176)
230
+ end
231
+
232
+ it "should put 213 subtitles in the second file" do
233
+ result[1].lines.length.should eq(213)
234
+ result[1].lines.last.sequence.should eq(213)
235
+ end
236
+
237
+ it "should put 212 subtitles in the third file" do
238
+ result[2].lines.length.should eq(212)
239
+ result[2].lines.last.sequence.should eq(212)
240
+ end
241
+ end
242
+
243
+ context "when passing { :at => \"00:19:24,500\", :every => \"00:00:01,000\" }" do
244
+ let(:result) { file.split( :at => "00:19:24,500", :every => "00:00:01,000" ) }
245
+
246
+ it "should return an array containing two SRT::File instances, ignoring :every" do
247
+ result.length.should eq(2)
248
+ result[0].class.should eq(SRT::File)
249
+ result[1].class.should eq(SRT::File)
250
+ end
251
+ end
252
+
253
+ context "when passing { :every => \"00:05:00,000\" }" do
254
+ let(:result) { file.split( :every => "00:05:00,000" ) }
255
+
256
+ it "should return an array containing nine SRT::File instances" do
257
+ result.length.should eq(9)
258
+ (0...result.count).each do |n|
259
+ result[n].class.should eq(SRT::File)
260
+ end
261
+ end
262
+ end
263
+
264
+ context "when passing { :at => \"00:19:24,500\", :renumber => false }" do
265
+ let(:result) { file.split( :at => "00:19:24,500", :renumber => false ) }
266
+
267
+ it "sequence for the last line of first part should be the sequence for the first line of second part" do
268
+ result[0].lines.last.text.should == result[1].lines.first.text
269
+ result[0].lines.last.sequence.should == result[1].lines.first.sequence
270
+ end
271
+ end
272
+
273
+ context "when passing { :at => \"00:19:24,500\", :renumber => true }" do
274
+ let(:result) { file.split( :at => "00:19:24,500", :renumber => true ) }
275
+
276
+ it "first line of second part's number should be one" do
277
+ result[1].lines.first.sequence.should == 1
278
+ end
279
+
280
+ it "sequence for the last line of first part should have different number than the sequence for the first line of second part" do
281
+ result[0].lines.last.text.should == result[1].lines.first.text
282
+ result[0].lines.last.sequence.should_not == result[1].lines.first.sequence
283
+ end
284
+ end
285
+
286
+ context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do
287
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) }
288
+
289
+ it "time for last line of first part should be the time for first line of second part" do
290
+ result[0].lines.last.text.should == result[1].lines.first.text
291
+ result[0].lines.last.time_str.should == result[1].lines.first.time_str
292
+ end
293
+ end
294
+
295
+ context "when passing { :at => \"00:19:24,500\", :timeshift => true }" do
296
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => true ) }
297
+
298
+ it "start_time of first line in second part should be 0" do
299
+ result[1].lines.first.start_time.should == 0
300
+ end
301
+
302
+ it "time for last line of first part should not be the time for first line of second part" do
303
+ result[0].lines.last.text.should == result[1].lines.first.text
304
+ result[0].lines.last.time_str.should_not == result[1].lines.first.time_str
305
+ end
306
+ end
307
+ end
308
+ end
309
+
310
+ describe "#timeshift" do
311
+ context "when calling it on a properly formatted BSG SRT file" do
312
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
313
+
314
+ context "when passing { :all => \"+2.5s\" }" do
315
+ before { file.timeshift({ :all => "+2.5s" }) }
316
+
317
+ it "should have timecodes shifted forward by 2.5s for subtitle #24" do
318
+ file.lines[23].time_str.should eq("00:01:59,291 --> 00:02:00,815")
319
+ end
320
+
321
+ it "should have timecodes shifted forward by 2.5s for subtitle #43" do
322
+ file.lines[42].time_str.should eq("00:03:46,164 --> 00:03:47,631")
323
+ end
324
+ end
325
+
326
+ context "when passing { \"25fps\" => \"23.976fps\" }" do
327
+ before { file.timeshift({ "25fps" => "23.976fps" }) }
328
+
329
+ it "should have correctly scaled timecodes for subtitle #24" do
330
+ file.lines[23].time_str.should eq("00:01:52,007 --> 00:01:53,469")
331
+ end
332
+
333
+ it "should have correctly scaled timecodes for subtitle #43" do
334
+ file.lines[42].time_str.should eq("00:03:34,503 --> 00:03:35,910")
335
+ end
336
+ end
337
+
338
+ context "when passing { \"#24\" => \"00:03:53,582\", \"#42\" => \"00:04:24,656\" }" do
339
+ before { file.timeshift({ "#24" => "00:03:53,582", "#42" => "00:04:24,656" }) }
340
+
341
+ it "should have shifted timecodes for subtitle #24" do
342
+ file.lines[23].time_str.should eq("00:03:53,582 --> 00:03:54,042")
343
+ end
344
+
345
+ it "should have differently shifted timecodes for subtitle #43" do
346
+ file.lines[41].time_str.should eq("00:04:24,656 --> 00:04:25,298")
347
+ end
348
+ end
349
+
350
+ context "when passing { 180 => \"+1s\", 264 => \"+1.5s\" }" do
351
+ before { file.timeshift({ 180 => "+1s", 264 => "+1.5s" }) }
352
+
353
+ it "should have shifted by +1s at 180 seconds" do
354
+ file.lines[23].time_str.should eq("00:01:57,415 --> 00:01:58,948")
355
+ end
356
+
357
+ it "should have shifted by +1.5s at 264 seconds" do
358
+ file.lines[41].time_str.should eq("00:03:40,997 --> 00:03:43,136")
359
+ end
360
+ end
361
+ end
362
+
363
+ context "when calling it on a spanish language WOTW SRT file with unknown encoding" do
364
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/wotw-dubious.srt")) }
365
+
366
+ context "when passing { :all => \"-2.7m\" }" do
367
+ before { file.timeshift({ :all => "-2.7m" }) }
368
+
369
+ it "should have dumped 16 lines with now negative timecodes, leaving 1107" do
370
+ file.lines.size.should eq(1107)
371
+ end
372
+ end
373
+
374
+ context "when passing { \"00:03:25,430\" => \"00:00:44,200\", \"01:49:29,980\" => \"01:46:35,600\" }" do
375
+ before { file.timeshift({ "00:03:25,430" => "00:00:44,200", "01:49:29,980" => "01:46:35,600" }) }
376
+
377
+ it "should have dumped 16 lines with now negative timecodes, leaving 1107" do
378
+ file.lines.size.should eq(1107)
379
+ end
380
+ end
381
+ end
382
+
383
+ describe "#to_s" do
384
+ context "when calling it on a short SRT file" do
385
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
386
+
387
+ before { file.lines = file.lines[0..2] }
388
+
389
+ it "should produce the exactly correct output" do
390
+ OUTPUT =<<END
391
+ 1
392
+ 00:00:02,110 --> 00:00:04,578
393
+ <i>(male narrator) Previously
394
+ on Battlestar Galactica.</i>
395
+
396
+ 2
397
+ 00:00:05,313 --> 00:00:06,871
398
+ Now you're telling me
399
+ you're a machine.
400
+
401
+ 3
402
+ 00:00:07,014 --> 00:00:08,003
403
+ The robot.
404
+ END
405
+ file.to_s.should eq(OUTPUT)
406
+ end
407
+ end
408
+ end
409
+
410
+ describe "#to_webvtt" do
411
+ context "when calling it on a short SRT file" do
412
+ let(:file) { SRT::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
413
+
414
+ before { file.lines = file.lines[0..2] }
415
+
416
+ it "should produce the exactly correct output" do
417
+ OUTPUT_WEBVTT =<<END
418
+ WEBVTT
419
+ X-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000
420
+
421
+ 1
422
+ 00:00:02.110 --> 00:00:04.578
423
+ <i>(male narrator) Previously
424
+ on Battlestar Galactica.</i>
425
+
426
+ 2
427
+ 00:00:05.313 --> 00:00:06.871
428
+ Now you're telling me
429
+ you're a machine.
430
+
431
+ 3
432
+ 00:00:07.014 --> 00:00:08.003
433
+ The robot.
434
+ END
435
+ file.to_webvtt.should eq(OUTPUT_WEBVTT)
436
+ end
437
+ end
438
+ end
439
+ end
440
+ end