m3uzi 0.1.4 → 0.1.6
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/lib/m3uzi.rb +54 -18
- data/lib/m3uzi/file.rb +6 -2
- data/lib/m3uzi/version.rb +1 -1
- data/test/m3uzi_test.rb +78 -0
- metadata +5 -10
- data/test/m3uzzi_test.rb +0 -18
data/lib/m3uzi.rb
CHANGED
@@ -6,12 +6,13 @@ require 'm3uzi/version'
|
|
6
6
|
|
7
7
|
class M3Uzi
|
8
8
|
|
9
|
-
# Unsupported:
|
10
|
-
VALID_TAGS = %w{TARGETDURATION MEDIA-SEQUENCE ALLOW-CACHE ENDLIST
|
9
|
+
# Unsupported: PROGRAM-DATE-TIME DISCONTINUITY
|
10
|
+
VALID_TAGS = %w{TARGETDURATION MEDIA-SEQUENCE ALLOW-CACHE ENDLIST KEY}
|
11
11
|
|
12
12
|
attr_accessor :files, :streams
|
13
13
|
attr_accessor :tags, :comments
|
14
14
|
attr_accessor :final_media_file
|
15
|
+
attr_accessor :version
|
15
16
|
|
16
17
|
def initialize
|
17
18
|
@files = []
|
@@ -19,6 +20,7 @@ class M3Uzi
|
|
19
20
|
@tags = []
|
20
21
|
@comments = []
|
21
22
|
@final_media_file = true
|
23
|
+
@version = 1
|
22
24
|
end
|
23
25
|
|
24
26
|
|
@@ -65,31 +67,38 @@ class M3Uzi
|
|
65
67
|
m3u
|
66
68
|
end
|
67
69
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
70
|
+
def write_to_io(io_stream)
|
71
|
+
check_version_restrictions
|
72
|
+
io_stream << "#EXTM3U\n"
|
73
|
+
io_stream << "#EXT-X-VERSION:#{@version.to_i}" if @version > 1
|
71
74
|
comments.each do |comment|
|
72
|
-
|
75
|
+
io_stream << "##{comment}\n"
|
73
76
|
end
|
74
77
|
tags.each do |tag|
|
75
78
|
next if %w{M3U ENDLIST}.include?(tag.name.to_s.upcase)
|
76
79
|
if VALID_TAGS.include?(tag.name.to_s.upcase)
|
77
|
-
|
80
|
+
io_stream << "#EXT-X-#{tag.name.to_s.upcase}"
|
78
81
|
else
|
79
|
-
|
82
|
+
io_stream << "##{tag.name.to_s.upcase}"
|
80
83
|
end
|
81
|
-
tag.value &&
|
82
|
-
|
84
|
+
tag.value && io_stream << ":#{tag.value}"
|
85
|
+
io_stream << "\n"
|
83
86
|
end
|
84
87
|
files.each do |file|
|
85
|
-
|
86
|
-
|
88
|
+
io_stream << "#EXTINF:#{file.attribute_string}"
|
89
|
+
io_stream << "\n#{file.path}\n"
|
87
90
|
end
|
88
91
|
streams.each do |stream|
|
89
|
-
|
90
|
-
|
92
|
+
io_stream << "#EXT-X-STREAM-INF:#{stream.attribute_string}"
|
93
|
+
io_stream << "\n#{stream.path}\n"
|
91
94
|
end
|
92
|
-
|
95
|
+
io_stream << "#EXT-X-ENDLIST\n" if files.length > 0 && final_media_file
|
96
|
+
end
|
97
|
+
|
98
|
+
def write(path)
|
99
|
+
check_version_restrictions
|
100
|
+
f = ::File.open(path, "w")
|
101
|
+
write_to_io(f)
|
93
102
|
f.close()
|
94
103
|
end
|
95
104
|
|
@@ -105,7 +114,7 @@ class M3Uzi
|
|
105
114
|
end
|
106
115
|
|
107
116
|
def filenames
|
108
|
-
files.map{|file| file.path }
|
117
|
+
files.map { |file| file.path }
|
109
118
|
end
|
110
119
|
|
111
120
|
|
@@ -120,7 +129,7 @@ class M3Uzi
|
|
120
129
|
end
|
121
130
|
|
122
131
|
def stream_names
|
123
|
-
streams.map{|stream| stream.path }
|
132
|
+
streams.map { |stream| stream.path }
|
124
133
|
end
|
125
134
|
|
126
135
|
|
@@ -136,7 +145,7 @@ class M3Uzi
|
|
136
145
|
|
137
146
|
def [](key)
|
138
147
|
tag_name = key.to_s.upcase.gsub("_", "-")
|
139
|
-
obj = tags.detect{|tag| tag.name == tag_name }
|
148
|
+
obj = tags.detect { |tag| tag.name == tag_name }
|
140
149
|
obj && obj.value
|
141
150
|
end
|
142
151
|
|
@@ -160,6 +169,33 @@ class M3Uzi
|
|
160
169
|
add_comment(comment)
|
161
170
|
end
|
162
171
|
|
172
|
+
def check_version_restrictions
|
173
|
+
@version = 1
|
174
|
+
|
175
|
+
# Version 2 Features
|
176
|
+
if @tags.detect { |tag| tag.name == 'KEY' && tag.value.to_s =~ /,IV=/ }
|
177
|
+
@version = 2 if @version < 2
|
178
|
+
end
|
179
|
+
|
180
|
+
# Version 3 Features
|
181
|
+
if @files.detect { |file| file.duration.kind_of?(Float) }
|
182
|
+
@version = 3 if @version < 3
|
183
|
+
end
|
184
|
+
|
185
|
+
# Version 4 Features
|
186
|
+
if @files.detect { |file| file.byterange }
|
187
|
+
@version = 4 if @version < 4
|
188
|
+
end
|
189
|
+
if @tags.detect { |tag| ['MEDIA','I-FRAMES-ONLY'].include?(tag.name) }
|
190
|
+
@version = 4 if @version < 4
|
191
|
+
end
|
192
|
+
|
193
|
+
# NOTES
|
194
|
+
# EXT-X-I-FRAME-STREAM-INF is supposed to be ignored by older clients.
|
195
|
+
# AUDIO/VIDEO attributes of X-STREAM-INF are used in conjunction with MEDIA, so it should trigger v4.
|
196
|
+
|
197
|
+
@version
|
198
|
+
end
|
163
199
|
|
164
200
|
protected
|
165
201
|
|
data/lib/m3uzi/file.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
class M3Uzi
|
2
2
|
class File
|
3
3
|
|
4
|
-
attr_accessor :path, :duration, :description
|
4
|
+
attr_accessor :path, :duration, :description, :byterange
|
5
5
|
|
6
6
|
def attribute_string
|
7
|
-
|
7
|
+
if duration.kind_of?(Float)
|
8
|
+
"#{sprintf('%0.3f',duration)},#{description}"
|
9
|
+
else
|
10
|
+
"#{duration},#{description}"
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
end
|
data/lib/m3uzi/version.rb
CHANGED
data/test/m3uzi_test.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__),'../lib')
|
2
|
+
require 'm3uzi'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'shoulda'
|
6
|
+
|
7
|
+
class M3UziTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
context "basic checks" do
|
10
|
+
should "instantiate an M3Uzi object" do
|
11
|
+
m3u = M3Uzi.new
|
12
|
+
assert_equal M3Uzi, m3u.class
|
13
|
+
end
|
14
|
+
|
15
|
+
should "read in an index file" do
|
16
|
+
m3u = M3Uzi.read(File.join(File.dirname(__FILE__), "fixtures/index.m3u8"))
|
17
|
+
assert_equal M3Uzi, m3u.class
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with protocol versions" do
|
22
|
+
should "set version 2 for encryption IV" do
|
23
|
+
m3u = M3Uzi.new
|
24
|
+
m3u['KEY'] = "0x1234567890abcdef1234567890abcdef"
|
25
|
+
assert_equal 1, m3u.check_version_restrictions
|
26
|
+
m3u['KEY'] = "0x1234567890abcdef1234567890abcdef,IV=0x1234567890abcdef1234567890abcdef"
|
27
|
+
assert_equal 2, m3u.check_version_restrictions
|
28
|
+
|
29
|
+
output_stream = StringIO.new
|
30
|
+
m3u.write_to_io(output_stream)
|
31
|
+
assert output_stream.string =~ /,IV=/
|
32
|
+
end
|
33
|
+
|
34
|
+
should "set version 3 for floating point durations" do
|
35
|
+
m3u = M3Uzi.new
|
36
|
+
m3u.add_file do |f|
|
37
|
+
f.path = "stuff.ts"
|
38
|
+
f.duration = 10
|
39
|
+
end
|
40
|
+
assert_equal 1, m3u.check_version_restrictions
|
41
|
+
|
42
|
+
output_stream = StringIO.new
|
43
|
+
m3u.write_to_io(output_stream)
|
44
|
+
assert output_stream.string =~ /:10,/
|
45
|
+
assert output_stream.string !~ /:10\./
|
46
|
+
|
47
|
+
m3u = M3Uzi.new
|
48
|
+
m3u.add_file do |f|
|
49
|
+
f.path = "stuff.ts"
|
50
|
+
f.duration = 10.0
|
51
|
+
end
|
52
|
+
assert_equal 3, m3u.check_version_restrictions
|
53
|
+
|
54
|
+
output_stream = StringIO.new
|
55
|
+
m3u.write_to_io(output_stream)
|
56
|
+
assert output_stream.string =~ /:10\.000,/
|
57
|
+
assert output_stream.string !~ /:10,/
|
58
|
+
end
|
59
|
+
|
60
|
+
should "set version 4 for advanced features" do
|
61
|
+
m3u = M3Uzi.new
|
62
|
+
m3u.add_file do |f|
|
63
|
+
f.path = "stuff.ts"
|
64
|
+
f.duration = 10
|
65
|
+
end
|
66
|
+
assert_equal 1, m3u.check_version_restrictions
|
67
|
+
|
68
|
+
m3u = M3Uzi.new
|
69
|
+
m3u.add_file do |f|
|
70
|
+
f.path = "stuff.ts"
|
71
|
+
f.duration = 10.0
|
72
|
+
f.byterange = "1234@0"
|
73
|
+
end
|
74
|
+
assert_equal 4, m3u.check_version_restrictions
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: m3uzi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 19
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
7
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
8
|
+
- 6
|
9
|
+
version: 0.1.6
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Brandon Arbini
|
@@ -16,7 +15,7 @@ autorequire:
|
|
16
15
|
bindir: bin
|
17
16
|
cert_chain: []
|
18
17
|
|
19
|
-
date: 2011-
|
18
|
+
date: 2011-10-10 00:00:00 -05:00
|
20
19
|
default_executable:
|
21
20
|
dependencies: []
|
22
21
|
|
@@ -38,7 +37,7 @@ files:
|
|
38
37
|
- lib/m3uzi.rb
|
39
38
|
- test/fixtures/index.m3u8
|
40
39
|
- test/fixtures/stream.m3u8
|
41
|
-
- test/
|
40
|
+
- test/m3uzi_test.rb
|
42
41
|
- LICENSE
|
43
42
|
- Rakefile
|
44
43
|
- README.md
|
@@ -52,27 +51,23 @@ rdoc_options: []
|
|
52
51
|
require_paths:
|
53
52
|
- lib
|
54
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
54
|
requirements:
|
57
55
|
- - ">="
|
58
56
|
- !ruby/object:Gem::Version
|
59
|
-
hash: 3
|
60
57
|
segments:
|
61
58
|
- 0
|
62
59
|
version: "0"
|
63
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
61
|
requirements:
|
66
62
|
- - ">="
|
67
63
|
- !ruby/object:Gem::Version
|
68
|
-
hash: 3
|
69
64
|
segments:
|
70
65
|
- 0
|
71
66
|
version: "0"
|
72
67
|
requirements: []
|
73
68
|
|
74
69
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.3.
|
70
|
+
rubygems_version: 1.3.6
|
76
71
|
signing_key:
|
77
72
|
specification_version: 3
|
78
73
|
summary: Read and write M3U files with (relative) ease.
|
data/test/m3uzzi_test.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'm3uzzi'
|
3
|
-
require 'test/unit'
|
4
|
-
require 'shoulda'
|
5
|
-
|
6
|
-
class M3UziTest < Test::Unit::TestCase
|
7
|
-
|
8
|
-
should "instantiate an M3Uzi object" do
|
9
|
-
m3u = M3Uzi.new
|
10
|
-
assert_equal M3Uzi, m3u.class
|
11
|
-
end
|
12
|
-
|
13
|
-
should "read in an index file" do
|
14
|
-
m3u = M3Uzi.read_file("#{Rails.root}/test/fixtures/index.m3u8")
|
15
|
-
assert_equal M3Uzi, m3u.class
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|