webvtt-ruby 0.3.2 → 0.4.2

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: e0ecae0ceefe5452aa14107dcb45c892ff9d02a6
4
- data.tar.gz: 173216b97d99db10103bc59207916ac5474748d8
2
+ SHA256:
3
+ metadata.gz: 378a27bdabf62c13b0446de29e0aefc7267ac38f67401f1bab668f1ea673edab
4
+ data.tar.gz: 9b68d17e5435a1ef088bc06ec1c5346cc3526dc3af5b56578c14afca6c922656
5
5
  SHA512:
6
- metadata.gz: 252d353160e8055adf98277d1c581cec2d13d975be165a8ec7108b10574606173e71946f670242c71def21e7f5a8eab7487834c0bcd06612f4f4b1c95d1b8c77
7
- data.tar.gz: dcb306b34ac391d51bcdec9ad9677a7cef97c6aa699a3b4cbf620b699d213f05f1960f839bc811752d96fd25784960f7291633dbd02cdf0d43683834ee277b16
6
+ metadata.gz: 5b7e76f6306f2df4fcc2f45a194583eb288b4822f2f414723fb6b1675023adbc9ba088be5bbf6b0e6062ef13c3d649dcd0794ab5ff0bca4bbb87012f01c318e6
7
+ data.tar.gz: ea5a3ea7d52dc8f61bc1ee66fe495e1721f11c4430a1eda17a73b54fd279758eb861b716e2fda1ad5a2e1154400dde4694f83aefe8669995fee3cb0574c528d2
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)
data/lib/webvtt/parser.rb CHANGED
@@ -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?
@@ -81,6 +80,29 @@ 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
 
@@ -146,10 +168,12 @@ module WebVTT
146
168
  return
147
169
  end
148
170
 
149
- 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})(.*)/)
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})(.*)/)
150
172
  @start = Timestamp.new $1
151
- @end = Timestamp.new $2
152
- @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) }]
175
+ else
176
+ raise WebVTT::MalformedFile
153
177
  end
154
178
  @text = lines[1..-1].join("\n")
155
179
  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/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,7 +186,7 @@ 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
191
  cue = WebVTT::Cue.parse <<-CUE
160
192
  00:00:01.000 --> 00:00:25.432
@@ -210,4 +242,18 @@ The text should change)
210
242
  webvtt = WebVTT.convert_from_srt("tests/subtitles/invalid_cue.srt")
211
243
  assert_equal 1, webvtt.cues.size
212
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
+
254
+ def test_invalid_vtt_without_milliseconds
255
+ assert_raises WebVTT::MalformedFile do
256
+ vtt = WebVTT::File.new('tests/subtitles/no_milliseconds.vtt')
257
+ end
258
+ end
213
259
  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 @@
1
+ WEBVTT
@@ -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.2'
3
+ s.version = '0.4.2'
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.2
4
+ version: 0.4.2
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-11-09 00:00:00.000000000 Z
11
+ date: 2024-05-17 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).
@@ -36,8 +36,14 @@ files:
36
36
  - tests/subtitles/no_text.vtt
37
37
  - tests/subtitles/notvalid.vtt
38
38
  - tests/subtitles/test.vtt
39
+ - tests/subtitles/test_carriage_returns.vtt
39
40
  - tests/subtitles/test_from_srt.srt
40
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
41
47
  - tests/subtitles/withnote.vtt
42
48
  - webvtt-ruby.gemspec
43
49
  homepage: https://github.com/HeyWatch/webvtt-ruby
@@ -59,8 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
65
  - !ruby/object:Gem::Version
60
66
  version: '0'
61
67
  requirements: []
62
- rubyforge_project:
63
- rubygems_version: 2.4.3
68
+ rubygems_version: 3.1.4
64
69
  signing_key:
65
70
  specification_version: 4
66
71
  summary: WebVTT parser and segmenter in ruby