srt_validator 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ab8648611901c99338f817ab6b4434f551060b6
4
+ data.tar.gz: 4add311f3dd3cfbe7242afd5a151e297c0df1b82
5
+ SHA512:
6
+ metadata.gz: 148b2353b1805061334d2d3f1dd871611fc3980fc2b8310244d6485c7a4f4e8357412fccd5d1de3d166cd505cf3e96c9403eee7cccfefcb41b61bde4a0d26c4e
7
+ data.tar.gz: 801385a018cea1d05cb8e700b9d227b6ae82505a6786d91781b4d48849be1c02bedbe30b77f8203aeb6e1a97217f9f3e6d801c208eee35ac58beb7dfdebfbe42
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .ruby-gemset
6
+ .ruby-version
7
+ .rvmrc
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/.rspec_status ADDED
@@ -0,0 +1,81 @@
1
+ example_id | status | run_time |
2
+ ------------------------------ | ------ | --------------- |
3
+ ./spec/file_spec.rb[1:1:1:1] | failed | 0.01425 seconds |
4
+ ./spec/file_spec.rb[1:1:2:1] | failed | 0.0005 seconds |
5
+ ./spec/file_spec.rb[1:2:1:1] | failed | 0.00011 seconds |
6
+ ./spec/file_spec.rb[1:2:1:2] | failed | 0.0001 seconds |
7
+ ./spec/file_spec.rb[1:2:1:3] | failed | 0.00009 seconds |
8
+ ./spec/file_spec.rb[1:2:1:4] | failed | 0.00009 seconds |
9
+ ./spec/file_spec.rb[1:2:2:1] | failed | 0.0001 seconds |
10
+ ./spec/file_spec.rb[1:2:2:2] | failed | 0.00009 seconds |
11
+ ./spec/file_spec.rb[1:2:2:3] | failed | 0.00009 seconds |
12
+ ./spec/file_spec.rb[1:2:2:4] | failed | 0.00009 seconds |
13
+ ./spec/file_spec.rb[1:2:2:5] | failed | 0.00009 seconds |
14
+ ./spec/file_spec.rb[1:3:1:1:1] | failed | 0.00033 seconds |
15
+ ./spec/file_spec.rb[1:3:1:1:2] | failed | 0.00009 seconds |
16
+ ./spec/file_spec.rb[1:3:1:1:3] | failed | 0.00009 seconds |
17
+ ./spec/file_spec.rb[1:3:1:1:4] | failed | 0.00009 seconds |
18
+ ./spec/file_spec.rb[1:3:1:1:5] | failed | 0.00032 seconds |
19
+ ./spec/file_spec.rb[1:3:1:1:6] | failed | 0.00009 seconds |
20
+ ./spec/file_spec.rb[1:3:1:1:7] | failed | 0.00009 seconds |
21
+ ./spec/file_spec.rb[1:3:1:1:8] | failed | 0.00009 seconds |
22
+ ./spec/file_spec.rb[1:3:1:1:9] | failed | 0.00009 seconds |
23
+ ./spec/file_spec.rb[1:4:1:1:1] | failed | 0.0001 seconds |
24
+ ./spec/file_spec.rb[1:4:1:1:2] | failed | 0.00009 seconds |
25
+ ./spec/file_spec.rb[1:4:1:1:3] | failed | 0.00009 seconds |
26
+ ./spec/file_spec.rb[1:4:1:1:4] | failed | 0.00009 seconds |
27
+ ./spec/file_spec.rb[1:4:1:1:5] | failed | 0.00009 seconds |
28
+ ./spec/file_spec.rb[1:4:1:1:6] | failed | 0.00009 seconds |
29
+ ./spec/file_spec.rb[1:4:1:1:7] | failed | 0.00009 seconds |
30
+ ./spec/file_spec.rb[1:4:1:1:8] | failed | 0.00009 seconds |
31
+ ./spec/file_spec.rb[1:4:1:1:9] | failed | 0.00009 seconds |
32
+ ./spec/file_spec.rb[1:5:1:1:1] | failed | 0.0001 seconds |
33
+ ./spec/file_spec.rb[1:5:1:1:2] | failed | 0.00012 seconds |
34
+ ./spec/file_spec.rb[1:5:1:1:3] | failed | 0.00011 seconds |
35
+ ./spec/file_spec.rb[1:5:1:1:4] | failed | 0.00011 seconds |
36
+ ./spec/file_spec.rb[1:5:1:2:1] | failed | 0.00011 seconds |
37
+ ./spec/file_spec.rb[1:6:1:1:1] | failed | 0.0001 seconds |
38
+ ./spec/file_spec.rb[1:6:1:1:2] | failed | 0.00015 seconds |
39
+ ./spec/file_spec.rb[1:6:1:1:3] | failed | 0.00012 seconds |
40
+ ./spec/file_spec.rb[1:6:1:1:4] | failed | 0.00011 seconds |
41
+ ./spec/file_spec.rb[1:6:1:1:5] | failed | 0.00013 seconds |
42
+ ./spec/file_spec.rb[1:6:1:1:6] | failed | 0.00014 seconds |
43
+ ./spec/file_spec.rb[1:6:1:2:1] | failed | 0.0001 seconds |
44
+ ./spec/file_spec.rb[1:6:1:2:2] | failed | 0.00009 seconds |
45
+ ./spec/file_spec.rb[1:6:1:2:3] | failed | 0.00012 seconds |
46
+ ./spec/file_spec.rb[1:6:1:2:4] | failed | 0.0001 seconds |
47
+ ./spec/file_spec.rb[1:6:1:2:5] | failed | 0.00011 seconds |
48
+ ./spec/file_spec.rb[1:6:1:2:6] | failed | 0.00009 seconds |
49
+ ./spec/file_spec.rb[1:6:1:3:1] | failed | 0.00009 seconds |
50
+ ./spec/file_spec.rb[1:6:1:3:2] | failed | 0.00009 seconds |
51
+ ./spec/file_spec.rb[1:6:1:3:3] | failed | 0.0001 seconds |
52
+ ./spec/file_spec.rb[1:6:1:3:4] | failed | 0.0001 seconds |
53
+ ./spec/file_spec.rb[1:6:1:3:5] | failed | 0.00011 seconds |
54
+ ./spec/file_spec.rb[1:6:1:4:1] | failed | 0.0001 seconds |
55
+ ./spec/file_spec.rb[1:6:1:5:1] | failed | 0.0001 seconds |
56
+ ./spec/file_spec.rb[1:6:1:6:1] | failed | 0.0001 seconds |
57
+ ./spec/file_spec.rb[1:6:1:7:1] | failed | 0.00011 seconds |
58
+ ./spec/file_spec.rb[1:6:1:7:2] | failed | 0.0001 seconds |
59
+ ./spec/file_spec.rb[1:6:1:8:1] | failed | 0.0001 seconds |
60
+ ./spec/file_spec.rb[1:6:1:9:1] | failed | 0.0001 seconds |
61
+ ./spec/file_spec.rb[1:6:1:9:2] | failed | 0.0001 seconds |
62
+ ./spec/file_spec.rb[1:7:1:1:1] | failed | 0.0001 seconds |
63
+ ./spec/file_spec.rb[1:7:1:1:2] | failed | 0.00011 seconds |
64
+ ./spec/file_spec.rb[1:7:1:2:1] | failed | 0.0001 seconds |
65
+ ./spec/file_spec.rb[1:7:1:2:2] | failed | 0.00011 seconds |
66
+ ./spec/file_spec.rb[1:7:1:3:1] | failed | 0.00009 seconds |
67
+ ./spec/file_spec.rb[1:7:1:3:2] | failed | 0.00009 seconds |
68
+ ./spec/file_spec.rb[1:7:1:4:1] | failed | 0.00009 seconds |
69
+ ./spec/file_spec.rb[1:7:1:4:2] | failed | 0.0001 seconds |
70
+ ./spec/file_spec.rb[1:7:2:1:1] | failed | 0.00011 seconds |
71
+ ./spec/file_spec.rb[1:7:2:2:1] | failed | 0.00009 seconds |
72
+ ./spec/file_spec.rb[1:7:3:1:1] | failed | 0.0001 seconds |
73
+ ./spec/file_spec.rb[1:7:4:1:1] | failed | 0.00011 seconds |
74
+ ./spec/line_spec.rb[1:1:1] | passed | 0.00141 seconds |
75
+ ./spec/line_spec.rb[1:2:1] | passed | 0.00052 seconds |
76
+ ./spec/parser_spec.rb[1:1:1] | passed | 0.00012 seconds |
77
+ ./spec/parser_spec.rb[1:2:1] | passed | 0.00012 seconds |
78
+ ./spec/parser_spec.rb[1:3:1] | passed | 0.00016 seconds |
79
+ ./spec/parser_spec.rb[1:3:2] | passed | 0.00011 seconds |
80
+ ./spec/parser_spec.rb[1:3:3] | passed | 0.00011 seconds |
81
+ ./spec/parser_spec.rb[1:4:1] | passed | 0.00013 seconds |
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.3.0
7
+ script:
8
+ - bundle exec rake spec
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at laerciosbezerra@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in srt_validator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # SrtValidator
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/srt_validator`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'srt_validator'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install srt_validator
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/srt_validator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => :spec
8
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "srt_validator"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,246 @@
1
+ module SrtValidator
2
+ class File
3
+ def self.parse(input, options = {})
4
+ @debug = options.fetch(:debug, false)
5
+ if input.is_a?(String)
6
+ parse_string(input)
7
+ elsif input.is_a?(::File)
8
+ parse_file(input)
9
+ else
10
+ raise "Invalid input. Expected a String or File, got #{input.class.name}."
11
+ end
12
+ end
13
+
14
+ def self.parse_file(srt_file)
15
+ parse_string ::File.open(srt_file, 'rb') { |f| srt_file.read }
16
+ end
17
+
18
+ def self.parse_string(srt_data)
19
+ result = new
20
+ line = Line.new
21
+ last_line = Line.new
22
+ count = 0
23
+
24
+ split_srt_data(srt_data).each_with_index do |str, index|
25
+ begin
26
+ if str.strip.empty?
27
+ result.lines << line unless line.empty?
28
+ line = Line.new
29
+ elsif !line.error
30
+ if line.sequence.nil?
31
+ count += 1
32
+ line.sequence = str.to_i
33
+ unless count == line.sequence
34
+ line.error = "#{index}, Invalid formatting of sequence index, [#{line.sequence}]"
35
+ $stderr.puts line.error if @debug
36
+ end
37
+ elsif line.start_time.nil?
38
+ if mres = str.match(/(?<start_timecode>[^[[:space:]]]+) -+> (?<end_timecode>[^[[:space:]]]+) ?(?<display_coordinates>X1:\d+ X2:\d+ Y1:\d+ Y2:\d+)?/)
39
+ line.start_time = Parser.timecode(mres["start_timecode"])
40
+ line.end_time = Parser.timecode(mres["end_timecode"])
41
+
42
+ if line.start_time.nil? || !last_line.empty? && last_line.end_time > line.start_time
43
+ line.error = "#{index}, Invalid formatting of start timecode, [#{mres["start_timecode"]}]"
44
+ $stderr.puts line.error if @debug
45
+ end
46
+
47
+ if line.end_time.nil? || line.start_time > line.end_time
48
+ line.error = "#{index}, Invalid formatting of end timecode, [#{mres["end_timecode"]}]"
49
+ $stderr.puts line.error if @debug
50
+ end
51
+
52
+ if mres["display_coordinates"]
53
+ line.display_coordinates = mres["display_coordinates"]
54
+ end
55
+
56
+ last_line = line
57
+ else
58
+ line.error = "#{index}, Invalid Time Line formatting, [#{str}]"
59
+ $stderr.puts line.error if @debug
60
+ end
61
+ else
62
+ line.text << str.strip
63
+ end
64
+ end
65
+ rescue
66
+ line.error = "#{index}, General Error, [#{str}]"
67
+ $stderr.puts line.error if @debug
68
+ end
69
+ end
70
+ result
71
+ end
72
+
73
+ # Ruby often gets the wrong encoding for a file and will throw
74
+ # errors on `split` for invalid byte sequences. This chain of
75
+ # fallback encodings lets us get something that works.
76
+ def self.split_srt_data(srt_data)
77
+ begin
78
+ srt_data.split(/\n/) + ["\n"]
79
+ rescue
80
+ begin
81
+ srt_data = srt_data.unpack("C*").pack("U*")
82
+ srt_data.force_encoding('utf-8').split(/\n/) + ["\n"]
83
+ rescue
84
+ srt_data.force_encoding('iso-8859-1').split(/\n/) + ["\n"]
85
+ end
86
+ end
87
+ end
88
+
89
+ def append(options)
90
+ if options.length == 1 && options.values[0].class == self.class
91
+ reshift = Parser.timecode(options.keys[0]) || (lines.last.end_time + Parser.timespan(options.keys[0]))
92
+ renumber = lines.last.sequence
93
+
94
+ options.values[0].lines.each do |line|
95
+ lines << line.clone
96
+ lines.last.sequence += renumber
97
+ lines.last.start_time += reshift
98
+ lines.last.end_time += reshift
99
+ end
100
+ end
101
+
102
+ self
103
+ end
104
+
105
+ def split(options)
106
+ options = { :timeshift => true, :renumber => true }.merge(options)
107
+
108
+ split_points = []
109
+
110
+ if (options[:at])
111
+ split_points = [options[:at]].flatten.map{ |timecode| Parser.timecode(timecode) }.sort
112
+ elsif (options[:every])
113
+ interval = Parser.timecode(options[:every])
114
+ max = lines.last.end_time
115
+ (interval..max).step(interval){ |t| split_points << t }
116
+ end
117
+
118
+ if (split_points.count > 0)
119
+ split_offsprings = [File.new]
120
+
121
+ reshift = 0
122
+ renumber = 0
123
+
124
+ lines.each do |line|
125
+ if split_points.empty? || line.end_time <= split_points.first
126
+ cloned_line = line.clone
127
+ cloned_line.sequence -= renumber if options[:renumber]
128
+ if options[:timeshift]
129
+ cloned_line.start_time -= reshift
130
+ cloned_line.end_time -= reshift
131
+ end
132
+ split_offsprings.last.lines << cloned_line
133
+ elsif line.start_time < split_points.first
134
+ cloned_line = line.clone
135
+ cloned_line.sequence -= renumber if options[:renumber]
136
+ if options[:timeshift]
137
+ cloned_line.start_time -= reshift
138
+ cloned_line.end_time = split_points.first - reshift
139
+ end
140
+ split_offsprings.last.lines << cloned_line
141
+
142
+ renumber = line.sequence - 1
143
+ reshift = split_points.first
144
+ split_points.delete_at(0)
145
+
146
+ split_offsprings << File.new
147
+ cloned_line = line.clone
148
+ cloned_line.sequence -= renumber if options[:renumber]
149
+ if options[:timeshift]
150
+ cloned_line.start_time = 0
151
+ cloned_line.end_time -= reshift
152
+ end
153
+ split_offsprings.last.lines << cloned_line
154
+ else
155
+ renumber = line.sequence - 1
156
+ reshift = split_points.first
157
+ split_points.delete_at(0)
158
+
159
+ split_offsprings << File.new
160
+ cloned_line = line.clone
161
+ cloned_line.sequence -= renumber if options[:renumber]
162
+ if options[:timeshift]
163
+ cloned_line.start_time -= reshift
164
+ cloned_line.end_time -= reshift
165
+ end
166
+ split_offsprings.last.lines << cloned_line
167
+ end
168
+ end
169
+ end
170
+
171
+ split_offsprings
172
+ end
173
+
174
+ def timeshift(options)
175
+ if options.length == 1
176
+ if options[:all] && (seconds = Parser.timespan(options[:all]))
177
+ lines.each do |line|
178
+ line.start_time += seconds
179
+ line.end_time += seconds
180
+ end
181
+ elsif (original_framerate = Parser.framerate(options.keys[0])) && (target_framerate = Parser.framerate(options.values[0]))
182
+ time_ratio = original_framerate / target_framerate
183
+ lines.each do |line|
184
+ line.start_time *= time_ratio
185
+ line.end_time *= time_ratio
186
+ end
187
+ end
188
+ elsif options.length == 2
189
+ origins, targets = options.keys, options.values
190
+
191
+ [0,1].each do |i|
192
+ if origins[i].is_a?(String) && Parser.id(origins[i])
193
+ origins[i] = lines[Parser.id(origins[i]) - 1].start_time
194
+ elsif origins[i].is_a?(String) && Parser.timecode(origins[i])
195
+ origins[i] = Parser.timecode(origins[i])
196
+ end
197
+
198
+ if targets[i].is_a?(String) && Parser.timecode(targets[i])
199
+ targets[i] = Parser.timecode(targets[i])
200
+ elsif targets[i].is_a?(String) && Parser.timespan(targets[i])
201
+ targets[i] = origins[i] + Parser.timespan(targets[i])
202
+ end
203
+ end
204
+
205
+ time_rescale_factor = (targets[1] - targets[0]) / (origins[1] - origins[0])
206
+ time_rebase_shift = targets[0] - origins[0] * time_rescale_factor
207
+
208
+ lines.each do |line|
209
+ line.start_time = line.start_time * time_rescale_factor + time_rebase_shift
210
+ line.end_time = line.end_time * time_rescale_factor + time_rebase_shift
211
+ end
212
+ end
213
+
214
+ if lines.reject! { |line| line.end_time < 0 }
215
+ lines.sort_by! { |line| line.sequence }
216
+ lines.each_with_index do |line, index|
217
+ line.sequence = index + 1
218
+ line.start_time = 0 if line.start_time < 0
219
+ end
220
+ end
221
+ end
222
+
223
+ def to_s(time_str_function=:time_str)
224
+ lines.map { |l| l.to_s(time_str_function) }.join("\n")
225
+ end
226
+
227
+ def to_webvtt
228
+ header = <<eos
229
+ WEBVTT
230
+ X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000
231
+
232
+ eos
233
+ header + to_s(:webvtt_time_str)
234
+ end
235
+
236
+ attr_writer :lines
237
+
238
+ def lines
239
+ @lines ||= []
240
+ end
241
+
242
+ def errors
243
+ lines.collect { |l| l.error if l.error }.compact
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,47 @@
1
+ module SrtValidator
2
+ class Line
3
+ attr_accessor :display_coordinates
4
+ attr_accessor :sequence
5
+ attr_accessor :start_time
6
+ attr_accessor :end_time
7
+ attr_accessor :error
8
+ attr_writer :text
9
+
10
+ def text
11
+ @text ||= []
12
+ end
13
+
14
+ def initialize(options={})
15
+ options.each do |k,v|
16
+ self.send("#{k}=",v)
17
+ end
18
+ end
19
+
20
+ def clone
21
+ clone = Line.new
22
+ clone.display_coordinates = display_coordinates
23
+ clone.sequence = sequence
24
+ clone.start_time = start_time
25
+ clone.end_time = end_time
26
+ clone.error = error
27
+ clone.text = text.clone
28
+ clone
29
+ end
30
+
31
+ def empty?
32
+ sequence.nil? && start_time.nil? && end_time.nil? && text.empty?
33
+ end
34
+
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(".")
41
+ end
42
+
43
+ def to_s(time_str_function=:time_str)
44
+ [sequence, (display_coordinates ? send(time_str_function) + display_coordinates : send(time_str_function)), text, ''].flatten.join("\n")
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ module SrtValidator
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
@@ -0,0 +1,3 @@
1
+ module SrtValidator
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "srt_validator/file"
2
+ require "srt_validator/line"
3
+ require "srt_validator/parser"
4
+ require "srt_validator/version"
data/spec/file_spec.rb ADDED
@@ -0,0 +1,445 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe SrtValidator::File do
5
+ describe '#parse' do
6
+ context "parsing with debug true" do
7
+ it "should be verbose when failing" do
8
+ expect($stderr).to receive(:puts).once
9
+ expect(SrtValidator::File.parse(File.open("./spec/fixtures/invalid.srt"), debug: true).errors).not_to be_empty
10
+ end
11
+ end
12
+ context "parsing with debug false" do
13
+ it "should raise exception silently" do
14
+ expect($stderr).not_to receive(:puts)
15
+ expect(SrtValidator::File.parse(File.open("./spec/fixtures/invalid.srt")).errors).not_to 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 SrtValidator::File" do
23
+ expect(subject.class).to eq(SrtValidator::File)
24
+ end
25
+
26
+ it "should have 600 lines" do
27
+ expect(subject.lines.size).to eq(600)
28
+ end
29
+
30
+ it "should have no errors" do
31
+ expect(subject.errors).to be_empty
32
+ end
33
+
34
+ it "should have the expected sequence number on the first subtitle" do
35
+ expect(subject.lines.first.sequence).to eq(1)
36
+ end
37
+
38
+ it "should have the expected timecodes on the first subtitle" do
39
+ expect(subject.lines.first.time_str).to 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
+ expect(subject.lines.first.text).to 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
+ expect(subject.lines.last.sequence).to eq(600)
48
+ end
49
+
50
+ it "should have the expected timecodes on the last subtitle" do
51
+ expect(subject.lines.last.time_str).to 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
+ expect(subject.lines.last.text).to 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) { SrtValidator::File.parse(File.open("./spec/fixtures/wotw-dubious.srt")) }
63
+
64
+ it "should parse" do
65
+ expect(file.class).to eq(SrtValidator::File)
66
+ end
67
+
68
+ it "should have 1123 lines" do
69
+ expect(file.lines.size).to eq(1123)
70
+ end
71
+
72
+ it "should have no errors" do
73
+ expect(file.errors).to be_empty
74
+ end
75
+
76
+ it "should convert some dubious UTF-8 encodings" do
77
+ first_line = file.lines.first.text.join(" ")
78
+ expect(first_line).to eq("<i>Nadie habría creído en los primeros años del siglo 21...</i>")
79
+ end
80
+ end
81
+
82
+ context "when parsing a dummy SRT file containing display coordinates" do
83
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/coordinates-dummy.srt")) }
84
+
85
+ it "should return an SrtValidator::File" do
86
+ expect(file.class).to eq(SrtValidator::File)
87
+ end
88
+
89
+ it "should have 3 lines" do
90
+ expect(file.lines.size).to eq(3)
91
+ end
92
+
93
+ it "should have no errors" do
94
+ expect(file.errors).to be_empty
95
+ end
96
+
97
+ it "should have the expected display coordinates on the first subtitle" do
98
+ expect(file.lines.first.display_coordinates).to eq("X1:100 X2:600 Y1:1 Y2:4")
99
+ end
100
+
101
+ it "should have the expected display coordinates on the last subtitle" do
102
+ expect(file.lines.last.display_coordinates).to eq("X1:1 X2:333 Y1:50 Y2:29")
103
+ end
104
+ end
105
+ end
106
+
107
+ describe SrtValidator::File, "when initialized with a valid BSG SRT string" do
108
+ subject { SrtValidator::File.parse(File.read("./spec/fixtures/bsg-s01e01.srt")) }
109
+ it_should_behave_like "an SRT file"
110
+ end
111
+
112
+ describe SrtValidator::File, "when initialized with a valid BSG SRT File" do
113
+ subject { SrtValidator::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
114
+ it_should_behave_like "an SRT file"
115
+ end
116
+
117
+ describe "#append" do
118
+ context "when calling it on the first (part1) of two seperate SRT files for Black Swan" do
119
+ let(:part1) { SrtValidator::File.parse(File.open("./spec/fixtures/blackswan-part1.srt")) }
120
+ let(:part2) { SrtValidator::File.parse(File.open("./spec/fixtures/blackswan-part2.srt")) }
121
+
122
+ context "when passing { \"00:53:57,241\" => part2 }" do
123
+ before { part1.append({ "00:53:57,241" => part2 }) }
124
+
125
+ it "should have grown to 808 subtitles" do
126
+ expect(part1.lines.length).to eq(808)
127
+ end
128
+
129
+ it "should have appended subtitles starting with sequence number 448" do
130
+ expect(part1.lines[447].sequence).to eq(448)
131
+ end
132
+
133
+ it "should have appended subtitles ending with sequence number 808" do
134
+ expect(part1.lines.last.sequence).to eq(808)
135
+ end
136
+
137
+ it "should have appended subtitles relatively from 00:53:57,241" do
138
+ expect(part1.lines[447].time_str).to eq("00:54:02,152 --> 00:54:04,204")
139
+ end
140
+ end
141
+
142
+ context "when passing { \"+7.241s\" => part2 }" do
143
+ before { part1.append({ "+7.241s" => part2 }) }
144
+
145
+ it "should have appended subtitles relatively from +7.241s after the previously last subtitle" do
146
+ expect(part1.lines[447].time_str).to eq("00:54:02,283 --> 00:54:04,335")
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "#split" do
153
+ context "when calling it on a properly formatted BSG SRT file" do
154
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
155
+
156
+ context "when passing { :at => \"00:19:24,500\" }" do
157
+ let(:result) { file.split( :at => "00:19:24,500" ) }
158
+
159
+ it "should return an array containing two SrtValidator::File instances" do
160
+ expect(result.length).to eq(2)
161
+ expect(result[0].class).to eq(SrtValidator::File)
162
+ expect(result[1].class).to eq(SrtValidator::File)
163
+ end
164
+
165
+ it "should include a subtitle that overlaps a splitting point in the first file" do
166
+ expect(result[0].lines.last.text).to eq(["I'll see you guys in combat."])
167
+ end
168
+
169
+ it "should make an overlapping subtitle end at the splitting point in the first file" do
170
+ expect(result[0].lines.last.time_str).to eq("00:19:23,901 --> 00:19:24,500")
171
+ end
172
+
173
+ it "should include a subtitle that overlaps a splitting point in the second file as well" do
174
+ expect(result[1].lines.first.text).to eq(["I'll see you guys in combat."])
175
+ end
176
+
177
+ it "should make an overlapping subtitle remain at the beginning in the second file" do
178
+ expect(result[1].lines.first.time_str).to eq("00:00:00,000 --> 00:00:01,528")
179
+ end
180
+
181
+ it "should shift back all timecodes of the second file relative to the new file beginning" do
182
+ expect(result[1].lines[1].time_str).to eq("00:00:01,737 --> 00:00:03,466")
183
+ end
184
+ end
185
+
186
+ context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do
187
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) }
188
+
189
+ it "should return an array containing two SrtValidator::File instances" do
190
+ expect(result.length).to eq(2)
191
+ expect(result[0].class).to eq(SrtValidator::File)
192
+ expect(result[1].class).to eq(SrtValidator::File)
193
+ end
194
+
195
+ it "should include a subtitle that overlaps a splitting point in the first file" do
196
+ expect(result[0].lines.last.text).to eq(["I'll see you guys in combat."])
197
+ end
198
+
199
+ it "should not make an overlapping subtitle end at the splitting point in the first file" do
200
+ expect(result[0].lines.last.time_str).to eq("00:19:23,901 --> 00:19:26,028")
201
+ end
202
+
203
+ it "should include a subtitle that overlaps a splitting point in the second file as well" do
204
+ expect(result[1].lines.first.text).to eq(["I'll see you guys in combat."])
205
+ end
206
+
207
+ it "should not make an overlapping subtitle remain at the beginning in the second file" do
208
+ expect(result[1].lines.first.time_str).to eq("00:19:23,901 --> 00:19:26,028")
209
+ end
210
+
211
+ it "should not shift back timecodes of the second file relative to the new file beginning" do
212
+ expect(result[1].lines[1].time_str).to eq("00:19:26,237 --> 00:19:27,966")
213
+ end
214
+ end
215
+
216
+ context "when passing { :at => [\"00:15:00,000\", \"00:30:00,000\"] }" do
217
+ let(:result) { file.split( :at => ["00:15:00,000", "00:30:00,000"] ) }
218
+
219
+ it "should return an array containing three SrtValidator::File instances" do
220
+ expect(result.length).to eq(3)
221
+ expect(result[0].class).to eq(SrtValidator::File)
222
+ expect(result[1].class).to eq(SrtValidator::File)
223
+ expect(result[2].class).to eq(SrtValidator::File)
224
+ end
225
+
226
+ it "should let subtitles start at sequence number #1 in all three files" do
227
+ expect(result[0].lines.first.sequence).to eq(1)
228
+ expect(result[1].lines.first.sequence).to eq(1)
229
+ expect(result[2].lines.first.sequence).to eq(1)
230
+ end
231
+
232
+ it "should put 176 subtitles in the first file" do
233
+ expect(result[0].lines.length).to eq(176)
234
+ expect(result[0].lines.last.sequence).to eq(176)
235
+ end
236
+
237
+ it "should put 213 subtitles in the second file" do
238
+ expect(result[1].lines.length).to eq(213)
239
+ expect(result[1].lines.last.sequence).to eq(213)
240
+ end
241
+
242
+ it "should put 212 subtitles in the third file" do
243
+ expect(result[2].lines.length).to eq(212)
244
+ expect(result[2].lines.last.sequence).to eq(212)
245
+ end
246
+ end
247
+
248
+ context "when passing { :at => \"00:19:24,500\", :every => \"00:00:01,000\" }" do
249
+ let(:result) { file.split( :at => "00:19:24,500", :every => "00:00:01,000" ) }
250
+
251
+ it "should return an array containing two SrtValidator::File instances, ignoring :every" do
252
+ expect(result.length).to eq(2)
253
+ expect(result[0].class).to eq(SrtValidator::File)
254
+ expect(result[1].class).to eq(SrtValidator::File)
255
+ end
256
+ end
257
+
258
+ context "when passing { :every => \"00:05:00,000\" }" do
259
+ let(:result) { file.split( :every => "00:05:00,000" ) }
260
+
261
+ it "should return an array containing nine SrtValidator::File instances" do
262
+ expect(result.length).to eq(9)
263
+ (0...result.count).each do |n|
264
+ expect(result[n].class).to eq(SrtValidator::File)
265
+ end
266
+ end
267
+ end
268
+
269
+ context "when passing { :at => \"00:19:24,500\", :renumber => false }" do
270
+ let(:result) { file.split( :at => "00:19:24,500", :renumber => false ) }
271
+
272
+ it "sequence for the last line of first part should be the sequence for the first line of second part" do
273
+ expect(result[0].lines.last.text).to eq(result[1].lines.first.text)
274
+ expect(result[0].lines.last.sequence).to eq(result[1].lines.first.sequence)
275
+ end
276
+ end
277
+
278
+ context "when passing { :at => \"00:19:24,500\", :renumber => true }" do
279
+ let(:result) { file.split( :at => "00:19:24,500", :renumber => true ) }
280
+
281
+ it "first line of second part's number should be one" do
282
+ expect(result[1].lines.first.sequence).to eq(1)
283
+ end
284
+
285
+ it "sequence for the last line of first part should have different number than the sequence for the first line of second part" do
286
+ expect(result[0].lines.last.text).to eq(result[1].lines.first.text)
287
+ expect(result[0].lines.last.sequence).not_to eq(result[1].lines.first.sequence)
288
+ end
289
+ end
290
+
291
+ context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do
292
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) }
293
+
294
+ it "time for last line of first part should be the time for first line of second part" do
295
+ expect(result[0].lines.last.text).to eq(result[1].lines.first.text)
296
+ expect(result[0].lines.last.time_str).to eq(result[1].lines.first.time_str)
297
+ end
298
+ end
299
+
300
+ context "when passing { :at => \"00:19:24,500\", :timeshift => true }" do
301
+ let(:result) { file.split( :at => "00:19:24,500", :timeshift => true ) }
302
+
303
+ it "start_time of first line in second part should be 0" do
304
+ expect(result[1].lines.first.start_time).to eq(0)
305
+ end
306
+
307
+ it "time for last line of first part should not be the time for first line of second part" do
308
+ expect(result[0].lines.last.text).to eq(result[1].lines.first.text)
309
+ expect(result[0].lines.last.time_str).not_to eq(result[1].lines.first.time_str)
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ describe "#timeshift" do
316
+ context "when calling it on a properly formatted BSG SRT file" do
317
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
318
+
319
+ context "when passing { :all => \"+2.5s\" }" do
320
+ before { file.timeshift({ :all => "+2.5s" }) }
321
+
322
+ it "should have timecodes shifted forward by 2.5s for subtitle #24" do
323
+ expect(file.lines[23].time_str).to eq("00:01:59,291 --> 00:02:00,815")
324
+ end
325
+
326
+ it "should have timecodes shifted forward by 2.5s for subtitle #43" do
327
+ expect(file.lines[42].time_str).to eq("00:03:46,164 --> 00:03:47,631")
328
+ end
329
+ end
330
+
331
+ context "when passing { \"25fps\" => \"23.976fps\" }" do
332
+ before { file.timeshift({ "25fps" => "23.976fps" }) }
333
+
334
+ it "should have correctly scaled timecodes for subtitle #24" do
335
+ expect(file.lines[23].time_str).to eq("00:02:01,779 --> 00:02:03,368")
336
+ end
337
+
338
+ it "should have correctly scaled timecodes for subtitle #43" do
339
+ expect(file.lines[42].time_str).to eq("00:03:53,217 --> 00:03:54,746")
340
+ end
341
+ end
342
+
343
+ context "when passing { \"#24\" => \"00:03:53,582\", \"#42\" => \"00:04:24,656\" }" do
344
+ before { file.timeshift({ "#24" => "00:03:53,582", "#42" => "00:04:24,656" }) }
345
+
346
+ it "should have shifted timecodes for subtitle #24" do
347
+ expect(file.lines[23].time_str).to eq("00:03:53,582 --> 00:03:54,042")
348
+ end
349
+
350
+ it "should have differently shifted timecodes for subtitle #43" do
351
+ expect(file.lines[41].time_str).to eq("00:04:24,656 --> 00:04:25,298")
352
+ end
353
+ end
354
+
355
+ context "when passing { 180 => \"+1s\", 264 => \"+1.5s\" }" do
356
+ before { file.timeshift({ 180 => "+1s", 264 => "+1.5s" }) }
357
+
358
+ it "should have shifted by +1s at 180 seconds" do
359
+ expect(file.lines[23].time_str).to eq("00:01:57,415 --> 00:01:58,948")
360
+ end
361
+
362
+ it "should have shifted by +1.5s at 264 seconds" do
363
+ expect(file.lines[41].time_str).to eq("00:03:40,997 --> 00:03:43,136")
364
+ end
365
+ end
366
+ end
367
+
368
+ context "when calling it on a spanish language WOTW SRT file with unknown encoding" do
369
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/wotw-dubious.srt")) }
370
+
371
+ context "when passing { :all => \"-2.7m\" }" do
372
+ before { file.timeshift({ :all => "-2.7m" }) }
373
+
374
+ it "should have dumped 16 lines with now negative timecodes, leaving 1107" do
375
+ expect(file.lines.size).to eq(1107)
376
+ end
377
+ end
378
+
379
+ context "when passing { \"00:03:25,430\" => \"00:00:44,200\", \"01:49:29,980\" => \"01:46:35,600\" }" do
380
+ before { file.timeshift({ "00:03:25,430" => "00:00:44,200", "01:49:29,980" => "01:46:35,600" }) }
381
+
382
+ it "should have dumped 16 lines with now negative timecodes, leaving 1107" do
383
+ expect(file.lines.size).to eq(1107)
384
+ end
385
+ end
386
+ end
387
+
388
+ describe "#to_s" do
389
+ context "when calling it on a short SRT file" do
390
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
391
+
392
+ before { file.lines = file.lines[0..2] }
393
+
394
+ it "should produce the exactly correct output" do
395
+ OUTPUT =<<END
396
+ 1
397
+ 00:00:02,110 --> 00:00:04,578
398
+ <i>(male narrator) Previously
399
+ on Battlestar Galactica.</i>
400
+
401
+ 2
402
+ 00:00:05,313 --> 00:00:06,871
403
+ Now you're telling me
404
+ you're a machine.
405
+
406
+ 3
407
+ 00:00:07,014 --> 00:00:08,003
408
+ The robot.
409
+ END
410
+ expect(file.to_s).to eq(OUTPUT)
411
+ end
412
+ end
413
+ end
414
+
415
+ describe "#to_webvtt" do
416
+ context "when calling it on a short SRT file" do
417
+ let(:file) { SrtValidator::File.parse(File.open("./spec/fixtures/bsg-s01e01.srt")) }
418
+
419
+ before { file.lines = file.lines[0..2] }
420
+
421
+ it "should produce the exactly correct output" do
422
+ OUTPUT_WEBVTT =<<END
423
+ WEBVTT
424
+ X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000
425
+
426
+ 1
427
+ 00:00:02.110 --> 00:00:04.578
428
+ <i>(male narrator) Previously
429
+ on Battlestar Galactica.</i>
430
+
431
+ 2
432
+ 00:00:05.313 --> 00:00:06.871
433
+ Now you're telling me
434
+ you're a machine.
435
+
436
+ 3
437
+ 00:00:07.014 --> 00:00:08.003
438
+ The robot.
439
+ END
440
+ expect(file.to_webvtt).to eq(OUTPUT_WEBVTT)
441
+ end
442
+ end
443
+ end
444
+ end
445
+ end
data/spec/line_spec.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe SrtValidator::Line do
4
+ describe "#new" do
5
+ let(:line) { SrtValidator::Line.new }
6
+
7
+ it "should create an empty subtitle" do
8
+ expect(line).to be_empty
9
+ end
10
+ end
11
+
12
+ describe "#time_str" do
13
+ let(:line) { SrtValidator::Line.new }
14
+
15
+ before do
16
+ line.start_time = 224.2
17
+ line.end_time = 244.578
18
+ end
19
+
20
+ it "should produce timecodes that match the internal float values" do
21
+ expect(line.time_str).to eq("00:03:44,200 --> 00:04:04,578")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe SrtValidator::Parser do
4
+ subject { described_class }
5
+
6
+ describe ".id" do
7
+ it "should convert the id string (#[id]) to an int representing the sequence number" do
8
+ expect(subject.id("#317")).to eq(317)
9
+ end
10
+ end
11
+
12
+ describe ".timecode" do
13
+ it "should convert the SRT timecode format to a float representing seconds" do
14
+ expect(subject.timecode("01:03:44,200")).to eq(3824.2)
15
+ end
16
+ end
17
+
18
+ describe ".timespan" do
19
+ it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do
20
+ expect(subject.timespan("-3.5m")).to eq(-210)
21
+ end
22
+
23
+ it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do
24
+ expect(subject.timespan("-1s")).to eq(-1)
25
+ end
26
+
27
+ it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do
28
+ expect(subject.timespan("100ms")).to eq(0.1)
29
+ end
30
+ end
31
+
32
+ describe ".parse_framerate" do
33
+ it "should convert a framerate string ([number]fps) to a float representing seconds" do
34
+ expect(subject.framerate("23.976fps")).to eq(23.976)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ require "bundler/setup"
2
+ require "srt_validator"
3
+ require "support/factory_girl"
4
+
5
+ RSpec.configure do |config|
6
+ # Enable flags like --only-failures and --next-failure
7
+ config.example_status_persistence_file_path = ".rspec_status"
8
+
9
+ config.expect_with :rspec do |c|
10
+ c.syntax = :expect
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ require "factory_girl"
2
+
3
+ # spec/support/factory_girl.rb
4
+ RSpec.configure do |config|
5
+ config.include FactoryGirl::Syntax::Methods
6
+
7
+ config.before(:suite) do
8
+ FactoryGirl.find_definitions
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/srt_validator/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["laerciosb"]
6
+ gem.email = ["laerciosbezerra@gmail.com"]
7
+ gem.description = %q{Ruby gem for validates SubRip Text (SRT) files. SRT stands for SubRip text file format, which is a file for storing subtitles.}
8
+ gem.summary = %q{Ruby gem for validates SubRip Text (SRT) files.}
9
+ gem.homepage = "https://github.com/laerciosb/srt_validator"
10
+ gem.license = "MIT"
11
+
12
+ gem.add_development_dependency "bundler", "~> 1.14"
13
+ gem.add_development_dependency "rake", "~> 10.0"
14
+ gem.add_development_dependency "rspec", "~> 3.0"
15
+ gem.add_development_dependency "factory_girl", "~> 4.0"
16
+ gem.add_development_dependency "coveralls", "~> 0.8.15"
17
+
18
+ gem.files = `git ls-files`.split($\)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.name = "srt_validator"
22
+ gem.require_paths = ["lib"]
23
+ gem.version = SrtValidator::VERSION
24
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: srt_validator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - laerciosb
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_girl
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.8.15
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.8.15
83
+ description: Ruby gem for validates SubRip Text (SRT) files. SRT stands for SubRip
84
+ text file format, which is a file for storing subtitles.
85
+ email:
86
+ - laerciosbezerra@gmail.com
87
+ executables:
88
+ - console
89
+ - setup
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".gitignore"
94
+ - ".rspec_status"
95
+ - ".travis.yml"
96
+ - CODE_OF_CONDUCT.md
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/setup
103
+ - lib/srt_validator.rb
104
+ - lib/srt_validator/file.rb
105
+ - lib/srt_validator/line.rb
106
+ - lib/srt_validator/parser.rb
107
+ - lib/srt_validator/version.rb
108
+ - spec/file_spec.rb
109
+ - spec/line_spec.rb
110
+ - spec/parser_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/support/factory_girl.rb
113
+ - srt_validator.gemspec
114
+ homepage: https://github.com/laerciosb/srt_validator
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.6.8
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Ruby gem for validates SubRip Text (SRT) files.
138
+ test_files:
139
+ - spec/file_spec.rb
140
+ - spec/line_spec.rb
141
+ - spec/parser_spec.rb
142
+ - spec/spec_helper.rb
143
+ - spec/support/factory_girl.rb