srt 0.0.9 → 0.0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +16 -1
- data/lib/srt/file.rb +33 -13
- data/lib/srt/line.rb +6 -2
- data/lib/srt/version.rb +1 -1
- data/spec/invalid.srt +4 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/srt_spec.rb +111 -0
- data/srt.gemspec +1 -0
- metadata +24 -4
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SRT [](https://travis-ci.org/cpetersen/srt)
|
1
|
+
# SRT [](https://travis-ci.org/cpetersen/srt) [](https://codeclimate.com/github/cpetersen/srt)
|
2
2
|
|
3
3
|
SRT stands for SubRip text file format, which is a file for storing subtitles; This is a Ruby library for manipulating SRT files.
|
4
4
|
Current functionality includes **parsing**, **appending**, **splitting** and **timeshifting** (constant, progressive and framerate-based).
|
@@ -60,8 +60,22 @@ The method `split` splits your subtitles at one (or more) points and returns an
|
|
60
60
|
By default, the timecodes of the split parts are relatively shifted towards their beginnings (to line up with correspondingly split multi-part video);
|
61
61
|
By additionally passing `:timeshift => false` you can prevent that behaviour and retain the original timecodes for each split part.
|
62
62
|
|
63
|
+
Pass the option `:renumber => false` to prevent the line sequence number from being reset for a segment.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
parts = file.split( :at => "01:09:24,000", :renumber => false ) # Split the file in two at 01:09:24 but do not reset the sequence number on the second part
|
67
|
+
```
|
68
|
+
|
63
69
|
Example options for a multi-split: `{ :at => ["00:19:24,500", "01:32:09,120", ...] }`
|
64
70
|
|
71
|
+
|
72
|
+
Optionally, for multi-splitting, you can pass a ":every" option to split the subtitles at a fixed interval.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
parts = file.split( :every => "00:01:00,000" ) # Split the file every 1 minute
|
76
|
+
```
|
77
|
+
Note that the options :at and :every are mutually exclusive, and :at takes precedence.
|
78
|
+
|
65
79
|
#### Timeshifting
|
66
80
|
|
67
81
|
The method `timeshift` takes a hash and supports three different modes of timecode processing:
|
@@ -118,3 +132,4 @@ This is usually only useful if you have some background information about the de
|
|
118
132
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
119
133
|
4. Push to the branch (`git push origin my-new-feature`)
|
120
134
|
5. Create new Pull Request
|
135
|
+
|
data/lib/srt/file.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module SRT
|
2
2
|
class File
|
3
|
-
def self.parse(input)
|
3
|
+
def self.parse(input, options = {})
|
4
|
+
@debug = options.fetch(:debug, false)
|
4
5
|
if input.is_a?(String)
|
5
6
|
parse_string(input)
|
6
7
|
elsif input.is_a?(::File)
|
@@ -31,12 +32,12 @@ module SRT
|
|
31
32
|
|
32
33
|
if (line.start_time = SRT::File.parse_timecode(mres["start_timecode"])) == nil
|
33
34
|
line.error = "#{index}, Invalid formatting of start timecode, [#{mres["start_timecode"]}]"
|
34
|
-
$stderr.puts line.error
|
35
|
+
$stderr.puts line.error if @debug
|
35
36
|
end
|
36
37
|
|
37
38
|
if (line.end_time = SRT::File.parse_timecode(mres["end_timecode"])) == nil
|
38
39
|
line.error = "#{index}, Invalid formatting of end timecode, [#{mres["end_timecode"]}]"
|
39
|
-
$stderr.puts line.error
|
40
|
+
$stderr.puts line.error if @debug
|
40
41
|
end
|
41
42
|
|
42
43
|
if mres["display_coordinates"]
|
@@ -44,7 +45,7 @@ module SRT
|
|
44
45
|
end
|
45
46
|
else
|
46
47
|
line.error = "#{index}, Invalid Time Line formatting, [#{str}]"
|
47
|
-
$stderr.puts line.error
|
48
|
+
$stderr.puts line.error if @debug
|
48
49
|
end
|
49
50
|
else
|
50
51
|
line.text << str.strip
|
@@ -53,7 +54,7 @@ module SRT
|
|
53
54
|
end
|
54
55
|
rescue
|
55
56
|
line.error = "#{index}, General Error, [#{str}]"
|
56
|
-
$stderr.puts line.error
|
57
|
+
$stderr.puts line.error if @debug
|
57
58
|
end
|
58
59
|
end
|
59
60
|
result
|
@@ -91,9 +92,19 @@ module SRT
|
|
91
92
|
end
|
92
93
|
|
93
94
|
def split(options)
|
94
|
-
options = { :timeshift => true }.merge(options)
|
95
|
-
|
95
|
+
options = { :timeshift => true, :renumber => true }.merge(options)
|
96
|
+
|
97
|
+
split_points = []
|
98
|
+
|
99
|
+
if (options[:at])
|
96
100
|
split_points = [options[:at]].flatten.map{ |timecode| SRT::File.parse_timecode(timecode) }.sort
|
101
|
+
elsif (options[:every])
|
102
|
+
interval = SRT::File.parse_timecode(options[:every])
|
103
|
+
max = lines.last.end_time
|
104
|
+
(interval..max).step(interval){ |t| split_points << t }
|
105
|
+
end
|
106
|
+
|
107
|
+
if (split_points.count > 0)
|
97
108
|
split_offsprings = [SRT::File.new]
|
98
109
|
|
99
110
|
reshift = 0
|
@@ -102,7 +113,7 @@ module SRT
|
|
102
113
|
lines.each do |line|
|
103
114
|
if split_points.empty? || line.end_time <= split_points.first
|
104
115
|
cloned_line = line.clone
|
105
|
-
cloned_line.sequence -= renumber
|
116
|
+
cloned_line.sequence -= renumber if options[:renumber]
|
106
117
|
if options[:timeshift]
|
107
118
|
cloned_line.start_time -= reshift
|
108
119
|
cloned_line.end_time -= reshift
|
@@ -110,7 +121,7 @@ module SRT
|
|
110
121
|
split_offsprings.last.lines << cloned_line
|
111
122
|
elsif line.start_time < split_points.first
|
112
123
|
cloned_line = line.clone
|
113
|
-
cloned_line.sequence -= renumber
|
124
|
+
cloned_line.sequence -= renumber if options[:renumber]
|
114
125
|
if options[:timeshift]
|
115
126
|
cloned_line.start_time -= reshift
|
116
127
|
cloned_line.end_time = split_points.first - reshift
|
@@ -123,7 +134,7 @@ module SRT
|
|
123
134
|
|
124
135
|
split_offsprings << SRT::File.new
|
125
136
|
cloned_line = line.clone
|
126
|
-
cloned_line.sequence -= renumber
|
137
|
+
cloned_line.sequence -= renumber if options[:renumber]
|
127
138
|
if options[:timeshift]
|
128
139
|
cloned_line.start_time = 0
|
129
140
|
cloned_line.end_time -= reshift
|
@@ -136,7 +147,7 @@ module SRT
|
|
136
147
|
|
137
148
|
split_offsprings << SRT::File.new
|
138
149
|
cloned_line = line.clone
|
139
|
-
cloned_line.sequence -= renumber
|
150
|
+
cloned_line.sequence -= renumber if options[:renumber]
|
140
151
|
if options[:timeshift]
|
141
152
|
cloned_line.start_time -= reshift
|
142
153
|
cloned_line.end_time -= reshift
|
@@ -198,8 +209,17 @@ module SRT
|
|
198
209
|
end
|
199
210
|
end
|
200
211
|
|
201
|
-
def to_s
|
202
|
-
lines.map { |l| [l.sequence, (l.display_coordinates ? l.
|
212
|
+
def to_s(time_str_function=:time_str)
|
213
|
+
lines.map { |l| [l.sequence, (l.display_coordinates ? l.send(time_str_function) + l.display_coordinates : l.send(time_str_function)), l.text, ""] }.flatten.join("\n")
|
214
|
+
end
|
215
|
+
|
216
|
+
def to_webvtt
|
217
|
+
header = <<eos
|
218
|
+
WEBVTT
|
219
|
+
X-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000
|
220
|
+
|
221
|
+
eos
|
222
|
+
header + to_s(:webvtt_time_str)
|
203
223
|
end
|
204
224
|
|
205
225
|
attr_writer :lines
|
data/lib/srt/line.rb
CHANGED
@@ -32,8 +32,12 @@ module SRT
|
|
32
32
|
sequence.nil? && start_time.nil? && end_time.nil? && text.empty?
|
33
33
|
end
|
34
34
|
|
35
|
-
def time_str
|
36
|
-
[@start_time, @end_time].map { |t| sprintf("%02d:%02d:%02d
|
35
|
+
def time_str(subframe_separator=",")
|
36
|
+
[@start_time, @end_time].map { |t| sprintf("%02d:%02d:%02d#{subframe_separator}%s", t / 3600, (t % 3600) / 60, t % 60, sprintf("%.3f", t)[-3, 3]) }.join(" --> ")
|
37
|
+
end
|
38
|
+
|
39
|
+
def webvtt_time_str
|
40
|
+
time_str(".")
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
data/lib/srt/version.rb
CHANGED
data/spec/invalid.srt
ADDED
data/spec/spec_helper.rb
ADDED
data/spec/srt_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'srt'
|
2
|
+
require 'spec_helper'
|
2
3
|
|
3
4
|
describe SRT do
|
4
5
|
|
@@ -26,6 +27,20 @@ describe SRT do
|
|
26
27
|
end
|
27
28
|
|
28
29
|
describe SRT::File do
|
30
|
+
describe '#parse' do
|
31
|
+
context "parsing with debug true" do
|
32
|
+
it "should be verbose when failing" do
|
33
|
+
$stderr.should_receive(:puts).once
|
34
|
+
SRT::File.parse(File.open("./spec/invalid.srt"), debug: true).errors.should_not be_empty
|
35
|
+
end
|
36
|
+
end
|
37
|
+
context "parsing with debug false" do
|
38
|
+
it "should raise exception silently" do
|
39
|
+
$stderr.should_not_receive(:puts)
|
40
|
+
SRT::File.parse(File.open("./spec/invalid.srt")).errors.should_not be_empty
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
29
44
|
|
30
45
|
describe ".parse_id" do
|
31
46
|
it "should convert the id string (#[id]) to an int representing the sequence number" do
|
@@ -281,6 +296,71 @@ describe SRT do
|
|
281
296
|
result[2].lines.last.sequence.should eq(212)
|
282
297
|
end
|
283
298
|
end
|
299
|
+
|
300
|
+
context "when passing { :at => \"00:19:24,500\", :every => \"00:00:01,000\" }" do
|
301
|
+
let(:result) { file.split( :at => "00:19:24,500", :every => "00:00:01,000" ) }
|
302
|
+
|
303
|
+
it "should return an array containing two SRT::File instances, ignoring :every" do
|
304
|
+
result.length.should eq(2)
|
305
|
+
result[0].class.should eq(SRT::File)
|
306
|
+
result[1].class.should eq(SRT::File)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context "when passing { :every => \"00:05:00,000\" }" do
|
311
|
+
let(:result) { file.split( :every => "00:05:00,000" ) }
|
312
|
+
|
313
|
+
it "should return an array containing nine SRT::File instances" do
|
314
|
+
result.length.should eq(9)
|
315
|
+
(0...result.count).each do |n|
|
316
|
+
result[n].class.should eq(SRT::File)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context "when passing { :at => \"00:19:24,500\", :renumber => false }" do
|
322
|
+
let(:result) { file.split( :at => "00:19:24,500", :renumber => false ) }
|
323
|
+
|
324
|
+
it "sequence for the last line of first part should be the sequence for the first line of second part" do
|
325
|
+
result[0].lines.last.text.should == result[1].lines.first.text
|
326
|
+
result[0].lines.last.sequence.should == result[1].lines.first.sequence
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context "when passing { :at => \"00:19:24,500\", :renumber => true }" do
|
331
|
+
let(:result) { file.split( :at => "00:19:24,500", :renumber => true ) }
|
332
|
+
|
333
|
+
it "first line of second part's number should be one" do
|
334
|
+
result[1].lines.first.sequence.should == 1
|
335
|
+
end
|
336
|
+
|
337
|
+
it "sequence for the last line of first part should have different number than the sequence for the first line of second part" do
|
338
|
+
result[0].lines.last.text.should == result[1].lines.first.text
|
339
|
+
result[0].lines.last.sequence.should_not == result[1].lines.first.sequence
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do
|
344
|
+
let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) }
|
345
|
+
|
346
|
+
it "time for last line of first part should be the time for first line of second part" do
|
347
|
+
result[0].lines.last.text.should == result[1].lines.first.text
|
348
|
+
result[0].lines.last.time_str.should == result[1].lines.first.time_str
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
context "when passing { :at => \"00:19:24,500\", :timeshift => true }" do
|
353
|
+
let(:result) { file.split( :at => "00:19:24,500", :timeshift => true ) }
|
354
|
+
|
355
|
+
it "start_time of first line in second part should be 0" do
|
356
|
+
result[1].lines.first.start_time.should == 0
|
357
|
+
end
|
358
|
+
|
359
|
+
it "time for last line of first part should not be the time for first line of second part" do
|
360
|
+
result[0].lines.last.text.should == result[1].lines.first.text
|
361
|
+
result[0].lines.last.time_str.should_not == result[1].lines.first.time_str
|
362
|
+
end
|
363
|
+
end
|
284
364
|
end
|
285
365
|
end
|
286
366
|
|
@@ -383,6 +463,37 @@ END
|
|
383
463
|
end
|
384
464
|
end
|
385
465
|
end
|
466
|
+
|
467
|
+
describe "#to_webvtt" do
|
468
|
+
context "when calling it on a short SRT file" do
|
469
|
+
let(:file) { SRT::File.parse(File.open("./spec/bsg-s01e01.srt")) }
|
470
|
+
|
471
|
+
before { file.lines = file.lines[0..2] }
|
472
|
+
|
473
|
+
it "should produce the exactly correct output" do
|
474
|
+
OUTPUT_WEBVTT =<<END
|
475
|
+
WEBVTT
|
476
|
+
X-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000
|
477
|
+
|
478
|
+
1
|
479
|
+
00:00:02.110 --> 00:00:04.578
|
480
|
+
<i>(male narrator) Previously
|
481
|
+
on Battlestar Galactica.</i>
|
482
|
+
|
483
|
+
2
|
484
|
+
00:00:05.313 --> 00:00:06.871
|
485
|
+
Now you're telling me
|
486
|
+
you're a machine.
|
487
|
+
|
488
|
+
3
|
489
|
+
00:00:07.014 --> 00:00:08.003
|
490
|
+
The robot.
|
491
|
+
END
|
492
|
+
file.to_webvtt.should eq(OUTPUT_WEBVTT)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
386
497
|
end
|
387
498
|
end
|
388
499
|
end
|
data/srt.gemspec
CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |gem|
|
|
10
10
|
|
11
11
|
gem.add_development_dependency('rake')
|
12
12
|
gem.add_development_dependency('rspec')
|
13
|
+
gem.add_development_dependency('coveralls')
|
13
14
|
|
14
15
|
gem.files = `git ls-files`.split($\)
|
15
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: srt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.9
|
4
|
+
version: 0.0.9.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: coveralls
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
description: Ruby gem for parsing srt (subtitle) files. SRT stands for SubRip text
|
47
63
|
file format, which is a file for storing subtitles.
|
48
64
|
email:
|
@@ -65,6 +81,8 @@ files:
|
|
65
81
|
- spec/blackswan-part2.srt
|
66
82
|
- spec/bsg-s01e01.srt
|
67
83
|
- spec/coordinates-dummy.srt
|
84
|
+
- spec/invalid.srt
|
85
|
+
- spec/spec_helper.rb
|
68
86
|
- spec/srt_spec.rb
|
69
87
|
- spec/wotw-dubious.srt
|
70
88
|
- srt.gemspec
|
@@ -82,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
100
|
version: '0'
|
83
101
|
segments:
|
84
102
|
- 0
|
85
|
-
hash:
|
103
|
+
hash: 3664658785611759133
|
86
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
105
|
none: false
|
88
106
|
requirements:
|
@@ -91,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
109
|
version: '0'
|
92
110
|
segments:
|
93
111
|
- 0
|
94
|
-
hash:
|
112
|
+
hash: 3664658785611759133
|
95
113
|
requirements: []
|
96
114
|
rubyforge_project:
|
97
115
|
rubygems_version: 1.8.23
|
@@ -103,5 +121,7 @@ test_files:
|
|
103
121
|
- spec/blackswan-part2.srt
|
104
122
|
- spec/bsg-s01e01.srt
|
105
123
|
- spec/coordinates-dummy.srt
|
124
|
+
- spec/invalid.srt
|
125
|
+
- spec/spec_helper.rb
|
106
126
|
- spec/srt_spec.rb
|
107
127
|
- spec/wotw-dubious.srt
|