webvtt-ruby 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 750601959beae93666af734645757e22a9f8fe2d
4
- data.tar.gz: ce85964a436d8459dc409503ff61e8ae12ba33b3
2
+ SHA256:
3
+ metadata.gz: f4334c8feffaab4ed2438a52554ce17ebd2bd0152622adbd8445c7de99e6e14d
4
+ data.tar.gz: 6de8052ac1d531bb77630c086425c3f2640295d510cd87546d357a2791d06695
5
5
  SHA512:
6
- metadata.gz: 9d4c6238c8c843bb83955db872f2b7ad51281e64074e8c1beecb30e9fb1ef930e4f0344d1db7e4243c63f5625b6c575d3d55947d8c8555887a2fcf19ea380081
7
- data.tar.gz: f250cac70bf7c1a534f518ca013fe04c8a7d422686d8cb7439ff3621b4410d35b2aab69f7ed3456c2ed7a8db25c8ca2c0e0dd4ce032297e2eedfeddde48680f4
6
+ metadata.gz: 7cba26291e3f92b889ff2d03246873d44f07c12890dec24201bd4e13e61b286a0a2af4c22e207c37ff976d3056cd38381bd470be9894bf3ebccd3043fdfac0ab
7
+ data.tar.gz: 7350a1b409e9178eea25526bb0fb2d7167b7348fb4fc19bc96a3edcf353f6aa4ae3c86f3ea31ac988925604da6def8ce621f636d73b66dd02656c0d945fb13a7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- webvtt-ruby (0.2.5)
4
+ webvtt-ruby (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -116,6 +116,6 @@ Usage: bin/webvtt-segmenter [--arg]
116
116
 
117
117
  **Bruno Celeste**
118
118
 
119
- * http://www.heywatchencoding.com
120
- * bruno@heywatch.com
121
- * [@sadikzzz](http://twitter.com/sadikzzz)
119
+ * http://coconut.co
120
+ * bruno@coconut.co
121
+ * [@brunoceleste](http://twitter.com/brunoceleste)
@@ -4,14 +4,20 @@ module WebVTT
4
4
  File.new(file)
5
5
  end
6
6
 
7
+ def self.from_blob(content)
8
+ Blob.new(content)
9
+ end
10
+
7
11
  def self.convert_from_srt(srt_file, output=nil)
8
- if !::File.exists?(srt_file)
12
+ if !::File.exist?(srt_file)
9
13
  raise InputError, "SRT file not found"
10
14
  end
11
15
 
12
16
  srt = ::File.read(srt_file)
13
17
  output ||= srt_file.gsub(".srt", ".vtt")
14
18
 
19
+ # normalize timestamps in srt
20
+ srt.gsub!(/(:|^)(\d)(,|:)/, '\10\2\3')
15
21
  # convert timestamps and save the file
16
22
  srt.gsub!(/([0-9]{2}:[0-9]{2}:[0-9]{2})([,])([0-9]{3})/, '\1.\3')
17
23
  # normalize new line character
@@ -23,19 +29,20 @@ module WebVTT
23
29
  return File.new(output)
24
30
  end
25
31
 
26
- class File
27
- attr_reader :header, :path, :filename
32
+ class Blob
33
+ attr_reader :header
28
34
  attr_accessor :cues
29
35
 
30
- def initialize(webvtt_file)
31
- if !::File.exists?(webvtt_file)
32
- raise InputError, "WebVTT file not found"
33
- end
36
+ def initialize(content = nil)
37
+ @cues = []
34
38
 
35
- @path = webvtt_file
36
- @filename = ::File.basename(@path)
37
- @content = ::File.read(webvtt_file).gsub("\r\n", "\n") # normalizing new line character
38
- parse
39
+ if content
40
+ parse(
41
+ content.gsub("\r\n", "\n").gsub("\r","\n") # normalizing new line character
42
+ )
43
+ else
44
+ @header = 'WEBVTT'
45
+ end
39
46
  end
40
47
 
41
48
  def to_webvtt
@@ -50,20 +57,12 @@ module WebVTT
50
57
  @cues.last.end_in_sec - @cues.first.start_in_sec
51
58
  end
52
59
 
53
- def save(output=nil)
54
- output ||= @path.gsub(".srt", ".vtt")
55
-
56
- ::File.open(output, "w") do |f|
57
- f.write(to_webvtt)
58
- end
59
- return output
60
- end
61
-
62
- def parse
60
+ def parse(content)
63
61
  # remove bom first
64
- @content.gsub!("\uFEFF", '')
62
+ content.gsub!("\uFEFF", '')
63
+
64
+ cues = content.split(/\n\n+/)
65
65
 
66
- cues = @content.split("\n\n")
67
66
  @header = cues.shift
68
67
  header_lines = @header.split("\n").map(&:strip)
69
68
  if (header_lines[0] =~ /^WEBVTT/).nil?
@@ -72,7 +71,7 @@ module WebVTT
72
71
 
73
72
  @cues = []
74
73
  cues.each do |cue|
75
- cue_parsed = Cue.new(cue.strip)
74
+ cue_parsed = Cue.parse(cue.strip)
76
75
  if !cue_parsed.text.nil?
77
76
  @cues << cue_parsed
78
77
  end
@@ -81,12 +80,41 @@ module WebVTT
81
80
  end
82
81
  end
83
82
 
83
+ class File < Blob
84
+ attr_reader :path, :filename
85
+
86
+ def initialize(webvtt_file)
87
+ if !::File.exist?(webvtt_file)
88
+ raise InputError, "WebVTT file not found"
89
+ end
90
+
91
+ @path = webvtt_file
92
+ @filename = ::File.basename(@path)
93
+ super(::File.read(webvtt_file))
94
+ end
95
+
96
+ def save(output=nil)
97
+ output ||= @path.gsub(".srt", ".vtt")
98
+
99
+ ::File.open(output, "w") do |f|
100
+ f.write(to_webvtt)
101
+ end
102
+ return output
103
+ end
104
+ end
105
+
84
106
  class Cue
85
107
  attr_accessor :identifier, :start, :end, :style, :text
86
108
 
87
- def initialize(cue)
109
+ def initialize(cue = nil)
88
110
  @content = cue
89
- parse
111
+ @style = {}
112
+ end
113
+
114
+ def self.parse(cue)
115
+ cue = Cue.new(cue)
116
+ cue.parse
117
+ return cue
90
118
  end
91
119
 
92
120
  def to_webvtt
@@ -136,10 +164,14 @@ module WebVTT
136
164
  lines.shift
137
165
  end
138
166
 
139
- if lines[0].match(/([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}) -+> ([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})(.*)/)
167
+ if lines.empty?
168
+ return
169
+ end
170
+
171
+ if lines[0].match(/(([0-9]{2}:)?[0-9]{2}:[0-9]{2}\.[0-9]{3}) -+> (([0-9]{2}:)?[0-9]{2}:[0-9]{2}\.[0-9]{3})(.*)/)
140
172
  @start = Timestamp.new $1
141
- @end = Timestamp.new $2
142
- @style = Hash[$3.strip.split(" ").map{|s| s.split(":").map(&:strip) }]
173
+ @end = Timestamp.new $3
174
+ @style = Hash[$5.strip.split(" ").map{|s| s.split(":").map(&:strip) }]
143
175
  end
144
176
  @text = lines[1..-1].join("\n")
145
177
  end
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module WebVTT
2
4
 
3
5
  def self.segment(input, options={})
@@ -25,6 +27,25 @@ module WebVTT
25
27
  @options[:length] ||= 10
26
28
  @options[:output] ||= "fileSequence-%05d.vtt"
27
29
  @options[:playlist] ||= "prog_index.m3u8"
30
+
31
+ # a dirty hack to check if output and playlist are ending with / (indicating a directory) and if so use the default filename and playlist name and create directory which is required.
32
+
33
+ if @options[:output].end_with?('/')
34
+ @options[:output] = "#{@options[:output]}fileSequence-%05d.vtt"
35
+ end
36
+ if @options[:playlist].end_with?('/')
37
+ @options[:playlist] = "#{@options[:playlist]}prog_index.m3u8"
38
+ end
39
+
40
+ output_dirname = ::File.dirname(@options[:output])
41
+ playlist_dirname = ::File.dirname(@options[:playlist])
42
+
43
+ unless ::File.directory?(output_dirname)
44
+ ::FileUtils.mkdir_p(output_dirname)
45
+ end
46
+ unless ::File.directory?(playlist_dirname)
47
+ ::FileUtils.mkdir_p(playlist_dirname)
48
+ end
28
49
  end
29
50
 
30
51
  def find_segment_files(cue)
@@ -123,4 +144,4 @@ module WebVTT
123
144
  return filenames.map{|f| File.new(f) }
124
145
  end
125
146
  end
126
- end
147
+ end
data/lib/webvtt.rb CHANGED
@@ -9,5 +9,5 @@ module WebVTT
9
9
  class InputError < RuntimeError; end
10
10
  end
11
11
 
12
- require "parser"
13
- require "segmenter"
12
+ require "webvtt/parser"
13
+ require "webvtt/segmenter"
data/tests/parser.rb CHANGED
@@ -20,6 +20,12 @@ class ParserTest < Minitest::Test
20
20
  }
21
21
  end
22
22
 
23
+ def test_can_create_empty_webvtt
24
+ webvtt = WebVTT::Blob.new
25
+ assert_equal 'WEBVTT', webvtt.header
26
+ assert_equal [], webvtt.cues
27
+ end
28
+
23
29
  def test_list_cues
24
30
  webvtt = WebVTT.read("tests/subtitles/test.vtt")
25
31
  assert_instance_of Array, webvtt.cues
@@ -43,6 +49,16 @@ class ParserTest < Minitest::Test
43
49
  assert_equal "English subtitle 15 -Forced- (00:00:27.000)\nline:75%", cue.text
44
50
  end
45
51
 
52
+ def test_cue_non_hours
53
+ webvtt = WebVTT.read("tests/subtitles/test_mmss_format.vtt")
54
+ cue = webvtt.cues[0]
55
+ assert_equal "00:00:29.000", cue.start.to_s
56
+ assert_equal "00:00:31.000", cue.end.to_s
57
+ assert_instance_of Hash, cue.style
58
+ assert_equal "75%", cue.style["line"]
59
+ assert_equal "English subtitle 15 -Forced- (00:00:27.000)\nline:75%", cue.text
60
+ end
61
+
46
62
  def test_cue_identifier
47
63
  webvtt = WebVTT.read("tests/subtitles/test.vtt")
48
64
  cue = webvtt.cues[1]
@@ -54,6 +70,11 @@ class ParserTest < Minitest::Test
54
70
  assert_equal "English subtitle 16 -Unforced- (00:00:31.000)\nalign:start line:0%", cue.text
55
71
  end
56
72
 
73
+ def test_multiple_line_separators
74
+ webvtt = WebVTT.read("tests/subtitles/test_multiple_line_separators.vtt")
75
+ assert_equal 2, webvtt.cues.length
76
+ end
77
+
57
78
  def test_ignore_if_note
58
79
  webvtt = WebVTT.read("tests/subtitles/withnote.vtt")
59
80
  assert_equal 3, webvtt.cues.size
@@ -129,6 +150,17 @@ The text should change)
129
150
  assert_equal 2, webvtt.cues.size
130
151
  end
131
152
 
153
+ def test_convert_weird_format_srt_to_webvtt
154
+ webvtt = WebVTT.convert_from_srt("tests/subtitles/weird_format.srt")
155
+ correct_webvtt = WebVTT.read("tests/subtitles/weird_format_corrected.vtt")
156
+
157
+ converted_cues = webvtt.cues.map { |cue| cue.start.to_s }
158
+ correct_cues = correct_webvtt.cues.map { |cue| cue.start.to_s }
159
+
160
+ assert_instance_of WebVTT::File, webvtt
161
+ assert_equal correct_cues, converted_cues
162
+ end
163
+
132
164
  def test_parse_big_file
133
165
  return
134
166
  webvtt = WebVTT.read("tests/subtitles/big_srt.vtt")
@@ -154,9 +186,9 @@ The text should change)
154
186
  assert_equal "00:09:02.373", webvtt.cues[1].end.to_s
155
187
  assert_equal "", webvtt.cues[1].text
156
188
  end
157
-
189
+
158
190
  def test_cue_offset_by
159
- cue = WebVTT::Cue.new <<-CUE
191
+ cue = WebVTT::Cue.parse <<-CUE
160
192
  00:00:01.000 --> 00:00:25.432
161
193
  Test Cue
162
194
  CUE
@@ -195,4 +227,28 @@ The text should change)
195
227
  assert_equal "03:39:34.008", ts3.to_s
196
228
  end
197
229
 
230
+ def test_build_cue
231
+ cue = WebVTT::Cue.new
232
+ cue.start = WebVTT::Timestamp.new 0
233
+ cue.end = WebVTT::Timestamp.new 12
234
+ cue.text = "Built from scratch"
235
+ output = ""
236
+ output << "00:00:00.000 --> 00:00:12.000\n"
237
+ output << "Built from scratch"
238
+ assert_equal output, cue.to_webvtt
239
+ end
240
+
241
+ def test_invalid_cue
242
+ webvtt = WebVTT.convert_from_srt("tests/subtitles/invalid_cue.srt")
243
+ assert_equal 1, webvtt.cues.size
244
+ end
245
+
246
+ def test_can_validate_webvtt_with_carriage_returns
247
+ webvtt = WebVTT::File.new("tests/subtitles/test_carriage_returns.vtt")
248
+ assert_instance_of Array, webvtt.cues
249
+ assert !webvtt.cues.empty?, "Cues should not be empty"
250
+ assert_instance_of WebVTT::Cue, webvtt.cues[0]
251
+ assert_equal 15, webvtt.cues.size
252
+ end
253
+
198
254
  end
data/tests/segmenter.rb CHANGED
@@ -43,7 +43,7 @@ class ParserTest < Minitest::Test
43
43
  subs = segmenter.split_to_files
44
44
  segmenter.generate_playlist(subs)
45
45
 
46
- assert File.exists?("test.m3u8")
46
+ assert File.exist?("test.m3u8")
47
47
  # clean up
48
48
  subs.each {|f| FileUtils.rm(f.filename)}
49
49
  FileUtils.rm("test.m3u8")
@@ -0,0 +1,5 @@
1
+ 1
2
+ 00:00:30,828 --> 00:00:34,894
3
+
4
+ Captain Phillips (2013)
5
+
@@ -0,0 +1,6 @@
1
+ WEBVTT
2
+
3
+ 1
4
+ 00:00:30.828 --> 00:00:34.894
5
+
6
+ Captain Phillips (2013)
@@ -0,0 +1 @@
1
+ WEBVTT
@@ -6,4 +6,4 @@ Elephant's Dream
6
6
 
7
7
  2
8
8
  00:00:15.000 --> 00:00:18.000
9
- At the left we can see...
9
+ At the left we can see...
@@ -0,0 +1,9 @@
1
+ WEBVTT
2
+
3
+ 00:29.000 --> 00:31.000 line:75%
4
+ English subtitle 15 -Forced- (00:00:27.000)
5
+ line:75%
6
+
7
+ 2
8
+ 00:31.000 --> 00:33.000 align:start line:0%
9
+ &nbsp;
@@ -0,0 +1,15 @@
1
+ WEBVTT X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000
2
+
3
+
4
+
5
+
6
+ 00:00:29.000 --> 00:00:31.000 line:75%
7
+ English subtitle 15 -Forced- (00:00:27.000)
8
+ line:75%
9
+
10
+
11
+
12
+
13
+ 00:00:31.000 --> 00:00:33.000 align:start line:0%
14
+ English subtitle 16 -Unforced- (00:00:31.000)
15
+ align:start line:0%
@@ -0,0 +1,15 @@
1
+ 1
2
+ 00:00:00,970 --> 00:00:03,000
3
+ Normal formatting
4
+
5
+ 2
6
+ 00:00:4,080 --> 00:00:06,080
7
+ Third digit
8
+
9
+ 3
10
+ 00:0:09,350 --> 00:00:13,350
11
+ Second digit
12
+
13
+ 4
14
+ 0:00:15,000 --> 00:00:16,000
15
+ First digit
@@ -0,0 +1,17 @@
1
+ WEBVTT
2
+
3
+ 1
4
+ 00:00:00.970 --> 00:00:03.000
5
+ Normal formatting
6
+
7
+ 2
8
+ 00:00:04.080 --> 00:00:06.080
9
+ Third digit
10
+
11
+ 3
12
+ 00:00:09.350 --> 00:00:13.350
13
+ Second digit
14
+
15
+ 4
16
+ 00:00:15.000 --> 00:00:16.000
17
+ First digit
@@ -0,0 +1,17 @@
1
+ WEBVTT
2
+
3
+ 1
4
+ 00:00:00.970 --> 00:00:03.000
5
+ Normal formatting
6
+
7
+ 2
8
+ 00:00:04.080 --> 00:00:06.080
9
+ Third digit
10
+
11
+ 3
12
+ 00:00:09.350 --> 00:00:13.350
13
+ Second digit
14
+
15
+ 4
16
+ 00:00:15.000 --> 00:00:16.000
17
+ First digit
data/webvtt-ruby.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'webvtt-ruby'
3
- s.version = '0.3.0'
3
+ s.version = '0.4.0'
4
4
  s.summary = "WebVTT parser and segmenter in ruby"
5
5
  s.description = "WebVTT parser and segmenter in ruby for HTML5 and HTTP Live Streaming (HLS)."
6
6
  s.authors = ["Bruno Celeste"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webvtt-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno Celeste
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-15 00:00:00.000000000 Z
11
+ date: 2023-04-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: WebVTT parser and segmenter in ruby for HTML5 and HTTP Live Streaming
14
14
  (HLS).
@@ -24,18 +24,26 @@ files:
24
24
  - LICENSE
25
25
  - README.md
26
26
  - bin/webvtt-segmenter
27
- - lib/parser.rb
28
- - lib/segmenter.rb
29
27
  - lib/webvtt.rb
28
+ - lib/webvtt/parser.rb
29
+ - lib/webvtt/segmenter.rb
30
30
  - tests/parser.rb
31
31
  - tests/segmenter.rb
32
32
  - tests/subtitles/big_srt.srt
33
33
  - tests/subtitles/big_srt.vtt
34
+ - tests/subtitles/invalid_cue.srt
35
+ - tests/subtitles/invalid_cue.vtt
34
36
  - tests/subtitles/no_text.vtt
35
37
  - tests/subtitles/notvalid.vtt
36
38
  - tests/subtitles/test.vtt
39
+ - tests/subtitles/test_carriage_returns.vtt
37
40
  - tests/subtitles/test_from_srt.srt
38
41
  - tests/subtitles/test_from_srt.vtt
42
+ - tests/subtitles/test_mmss_format.vtt
43
+ - tests/subtitles/test_multiple_line_separators.vtt
44
+ - tests/subtitles/weird_format.srt
45
+ - tests/subtitles/weird_format.vtt
46
+ - tests/subtitles/weird_format_corrected.vtt
39
47
  - tests/subtitles/withnote.vtt
40
48
  - webvtt-ruby.gemspec
41
49
  homepage: https://github.com/HeyWatch/webvtt-ruby
@@ -57,8 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
65
  - !ruby/object:Gem::Version
58
66
  version: '0'
59
67
  requirements: []
60
- rubyforge_project:
61
- rubygems_version: 2.4.3
68
+ rubygems_version: 3.1.4
62
69
  signing_key:
63
70
  specification_version: 4
64
71
  summary: WebVTT parser and segmenter in ruby