webvtt-ruby 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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