m3u8 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/lib/m3u8.rb +5 -1
- data/lib/m3u8/media_item.rb +14 -0
- data/lib/m3u8/playlist_item.rb +25 -0
- data/lib/m3u8/reader.rb +51 -141
- data/lib/m3u8/version.rb +1 -1
- data/spec/m3u8_spec.rb +8 -1
- data/spec/media_item_spec.rb +18 -0
- data/spec/playlist_item_spec.rb +37 -0
- data/spec/reader_spec.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5c073951f7f46336970cc6f1e16c38e89f4705d
|
4
|
+
data.tar.gz: af7de6933beb9ae49f2a88f4aad6124aafb4c88f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5440e1466f6f24dcf44f39e03ef44034b7aca088a81e5363e3778472b5d1d3c901d8c6cc40a78efdc9ce8c5354a789843d998606d661d3cf9f478798fd639f2
|
7
|
+
data.tar.gz: c02a202da39fd3d5611dcb3a059e56d2f64f0b036622f364fc21d27cfbf55d673fc8fc4c7ab967f4363024de3267b34f1cb916822468ff1e662e78ae8d649237
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
#### 0.5.2 (2/18/2015) - Fix issue where PlaylistItem.average_bandwidth would default to 0 instead of nil when not present in m3u8 being parsed.
|
2
|
+
|
1
3
|
#### 0.5.1 (2/16/2015) - [takashisite](https://github.com/takashisite) added support for [EXT-X-DISCONTINUITY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.3). Added support for [EXT-X-KEY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.4) (keys for encrypted segments).
|
2
4
|
***
|
3
5
|
#### 0.5.0 (2/10/2015) - BREAKING: renamed PlaylistItem.playlist to PlaylistItem.uri, MediaItem.group to MediaItem.group_id, and PlaylistItem.bitrate to PlaylistItem.bandwidth so attributes more closely match the spec. Added support for EXT-X-I-FRAME-STREAM-INF, EXT-X-I-FRAMES-ONLY, EXT-X-BYTERANGE, and EXT-X-SESSION-DATA.
|
data/lib/m3u8.rb
CHANGED
@@ -14,7 +14,11 @@ require 'm3u8/error'
|
|
14
14
|
# M3u8 provides parsing, generation, and validation of m3u8 playlists
|
15
15
|
module M3u8
|
16
16
|
def parse_attributes(line)
|
17
|
-
array = line.scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
|
17
|
+
array = line.gsub("\n", '').scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
|
18
18
|
Hash[array.map { |key, value| [key, value.gsub('"', '')] }]
|
19
19
|
end
|
20
|
+
|
21
|
+
def parse_yes_no(value)
|
22
|
+
value == 'YES' ? true : false
|
23
|
+
end
|
20
24
|
end
|
data/lib/m3u8/media_item.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module M3u8
|
2
2
|
# MediaItem represents a set of EXT-X-MEDIA attributes
|
3
3
|
class MediaItem
|
4
|
+
include M3u8
|
4
5
|
attr_accessor :type, :group_id, :language, :assoc_language, :name,
|
5
6
|
:autoselect, :default, :uri, :forced
|
6
7
|
|
@@ -10,6 +11,19 @@ module M3u8
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
def parse(text)
|
15
|
+
attributes = parse_attributes text
|
16
|
+
options = { type: attributes['TYPE'], group_id: attributes['GROUP-ID'],
|
17
|
+
language: attributes['LANGUAGE'],
|
18
|
+
assoc_language: attributes['ASSOC-LANGUAGE'],
|
19
|
+
name: attributes['NAME'],
|
20
|
+
autoselect: parse_yes_no(attributes['AUTOSELECT']),
|
21
|
+
default: parse_yes_no(attributes['DEFAULT']),
|
22
|
+
forced: parse_yes_no(attributes['FORCED']),
|
23
|
+
uri: attributes['URI'] }
|
24
|
+
initialize options
|
25
|
+
end
|
26
|
+
|
13
27
|
def to_s
|
14
28
|
attributes = [type_format,
|
15
29
|
group_id_format,
|
data/lib/m3u8/playlist_item.rb
CHANGED
@@ -2,6 +2,7 @@ module M3u8
|
|
2
2
|
# PlaylistItem represents a set of EXT-X-STREAM-INF or
|
3
3
|
# EXT-X-I-FRAME-STREAM-INF attributes
|
4
4
|
class PlaylistItem
|
5
|
+
include M3u8
|
5
6
|
attr_accessor :program_id, :width, :height, :codecs, :bandwidth,
|
6
7
|
:audio_codec, :level, :profile, :video, :audio, :uri,
|
7
8
|
:average_bandwidth, :subtitles, :closed_captions, :iframe
|
@@ -14,6 +15,20 @@ module M3u8
|
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
def parse(text)
|
19
|
+
attributes = parse_attributes text
|
20
|
+
average = parse_average_bandwidth attributes['AVERAGE-BANDWIDTH']
|
21
|
+
options = { program_id: attributes['PROGRAM-ID'],
|
22
|
+
codecs: attributes['CODECS'],
|
23
|
+
bandwidth: attributes['BANDWIDTH'].to_i,
|
24
|
+
average_bandwidth: average,
|
25
|
+
video: attributes['VIDEO'], audio: attributes['AUDIO'],
|
26
|
+
uri: attributes['URI'], subtitles: attributes['SUBTITLES'],
|
27
|
+
closed_captions: attributes['CLOSED-CAPTIONS'] }
|
28
|
+
initialize options
|
29
|
+
parse_resolution attributes['RESOLUTION']
|
30
|
+
end
|
31
|
+
|
17
32
|
def resolution
|
18
33
|
return if width.nil?
|
19
34
|
"#{width}x#{height}"
|
@@ -43,6 +58,16 @@ module M3u8
|
|
43
58
|
|
44
59
|
private
|
45
60
|
|
61
|
+
def parse_average_bandwidth(value)
|
62
|
+
value.to_i unless value.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_resolution(resolution)
|
66
|
+
return if resolution.nil?
|
67
|
+
self.width = resolution.split('x')[0].to_i
|
68
|
+
self.height = resolution.split('x')[1].to_i
|
69
|
+
end
|
70
|
+
|
46
71
|
def validate
|
47
72
|
fail MissingCodecError, MISSING_CODEC_MESSAGE if codecs.nil?
|
48
73
|
end
|
data/lib/m3u8/reader.rb
CHANGED
@@ -1,28 +1,15 @@
|
|
1
1
|
module M3u8
|
2
2
|
# Reader provides parsing of m3u8 playlists
|
3
3
|
class Reader
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
STREAM_IFRAME_START = '#EXT-X-I-FRAME-STREAM-INF:'
|
14
|
-
MEDIA_START = '#EXT-X-MEDIA:'
|
15
|
-
SESSION_DATA_START = '#EXT-X-SESSION-DATA:'
|
16
|
-
KEY_START = '#EXT-X-KEY:'
|
17
|
-
SEGMENT_START = '#EXTINF:'
|
18
|
-
SEGMENT_DISCONTINUITY_TAG_START = '#EXT-X-DISCONTINUITY'
|
19
|
-
BYTERANGE_START = '#EXT-X-BYTERANGE:'
|
20
|
-
RESOLUTION = 'RESOLUTION'
|
21
|
-
BANDWIDTH = 'BANDWIDTH'
|
22
|
-
AVERAGE_BANDWIDTH = 'AVERAGE-BANDWIDTH'
|
23
|
-
AUTOSELECT = 'AUTOSELECT'
|
24
|
-
DEFAULT = 'DEFAULT'
|
25
|
-
FORCED = 'FORCED'
|
4
|
+
include M3u8
|
5
|
+
attr_accessor :playlist, :item, :open, :master, :tags
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
@tags = [basic_tags,
|
9
|
+
media_segment_tags,
|
10
|
+
media_playlist_tags,
|
11
|
+
master_playlist_tags].inject(:merge)
|
12
|
+
end
|
26
13
|
|
27
14
|
def read(input)
|
28
15
|
self.playlist = Playlist.new
|
@@ -34,79 +21,64 @@ module M3u8
|
|
34
21
|
|
35
22
|
private
|
36
23
|
|
37
|
-
def
|
38
|
-
|
39
|
-
return if parse_master_playlist_tags line
|
40
|
-
return if parse_segment_tags line
|
41
|
-
return if parse_header_tags line
|
42
|
-
parse_next_line line if !item.nil? && open
|
24
|
+
def basic_tags
|
25
|
+
{ '#EXT-X-VERSION' => proc { |line| parse_version line } }
|
43
26
|
end
|
44
27
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
parse_sequence line
|
52
|
-
elsif line.start_with? CACHE_START
|
53
|
-
parse_cache line
|
54
|
-
elsif line.start_with? TARGET_START
|
55
|
-
parse_target line
|
56
|
-
elsif line.start_with? IFRAME_START
|
57
|
-
playlist.iframes_only = true
|
58
|
-
else
|
59
|
-
return false
|
60
|
-
end
|
28
|
+
def media_segment_tags
|
29
|
+
{ '#EXTINF' => proc { |line| parse_segment line },
|
30
|
+
'#EXT-X-DISCONTINUITY' => proc { |line| parse_discontinuity line },
|
31
|
+
'#EXT-X-BYTERANGE' => proc { |line| parse_byterange line },
|
32
|
+
'#EXT-X-KEY' => proc { |line| parse_key line }
|
33
|
+
}
|
61
34
|
end
|
62
35
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
elsif line.start_with? SEGMENT_DISCONTINUITY_TAG_START
|
71
|
-
parse_segment_discontinuity_tag line
|
72
|
-
elsif line.start_with? SESSION_DATA_START
|
73
|
-
parse_session_data line
|
74
|
-
else
|
75
|
-
return false
|
76
|
-
end
|
36
|
+
def media_playlist_tags
|
37
|
+
{ '#EXT-X-MEDIA-SEQUENCE' => proc { |line| parse_sequence line },
|
38
|
+
'#EXT-X-ALLOW-CACHE' => proc { |line| parse_cache line },
|
39
|
+
'#EXT-X-TARGETDURATION' => proc { |line| parse_target line },
|
40
|
+
'#EXT-X-I-FRAMES-ONLY' => proc { playlist.iframes_only = true },
|
41
|
+
'#EXT-X-PLAYLIST-TYPE' => proc { |line| parse_playlist_type line }
|
42
|
+
}
|
77
43
|
end
|
78
44
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
45
|
+
def master_playlist_tags
|
46
|
+
{ '#EXT-X-MEDIA' => proc { |line| parse_media line },
|
47
|
+
'#EXT-X-SESSION-DATA' => proc { |line| parse_session_data line },
|
48
|
+
'#EXT-X-STREAM-INF' => proc { |line| parse_stream line },
|
49
|
+
'#EXT-X-I-FRAME-STREAM-INF' => proc { |line| parse_iframe_stream line }
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_line(line)
|
54
|
+
return if match_tag(line)
|
55
|
+
parse_next_line line if !item.nil? && open
|
56
|
+
end
|
57
|
+
|
58
|
+
def match_tag(line)
|
59
|
+
tag = @tags.select { |key| line.start_with? key }
|
60
|
+
tag.values.first.call line unless tag.empty?
|
89
61
|
end
|
90
62
|
|
91
63
|
def parse_playlist_type(line)
|
92
|
-
playlist.type = line.gsub(
|
64
|
+
playlist.type = line.gsub('#EXT-X-PLAYLIST-TYPE:', '').delete!("\n")
|
93
65
|
end
|
94
66
|
|
95
67
|
def parse_version(line)
|
96
|
-
playlist.version = line.gsub(
|
68
|
+
playlist.version = line.gsub('#EXT-X-VERSION:', '').to_i
|
97
69
|
end
|
98
70
|
|
99
71
|
def parse_sequence(line)
|
100
|
-
playlist.sequence = line.gsub(
|
72
|
+
playlist.sequence = line.gsub('#EXT-X-MEDIA-SEQUENCE:', '').to_i
|
101
73
|
end
|
102
74
|
|
103
75
|
def parse_cache(line)
|
104
|
-
line = line.gsub(
|
76
|
+
line = line.gsub('#EXT-X-ALLOW-CACHE:', '')
|
105
77
|
playlist.cache = parse_yes_no(line)
|
106
78
|
end
|
107
79
|
|
108
80
|
def parse_target(line)
|
109
|
-
playlist.target = line.gsub(
|
81
|
+
playlist.target = line.gsub('#EXT-X-TARGETDURATION:', '').to_i
|
110
82
|
end
|
111
83
|
|
112
84
|
def parse_stream(line)
|
@@ -114,9 +86,7 @@ module M3u8
|
|
114
86
|
self.open = true
|
115
87
|
|
116
88
|
self.item = M3u8::PlaylistItem.new
|
117
|
-
|
118
|
-
attributes = parse_attributes line
|
119
|
-
parse_stream_attributes attributes
|
89
|
+
item.parse line
|
120
90
|
end
|
121
91
|
|
122
92
|
def parse_iframe_stream(line)
|
@@ -124,31 +94,12 @@ module M3u8
|
|
124
94
|
self.open = false
|
125
95
|
|
126
96
|
self.item = M3u8::PlaylistItem.new
|
97
|
+
item.parse line
|
127
98
|
item.iframe = true
|
128
|
-
line = line.gsub STREAM_IFRAME_START, ''
|
129
|
-
attributes = parse_attributes line
|
130
|
-
parse_stream_attributes attributes
|
131
99
|
playlist.items.push item
|
132
100
|
end
|
133
101
|
|
134
|
-
def
|
135
|
-
attributes.each do |pair|
|
136
|
-
name = pair[0]
|
137
|
-
value = parse_value pair[1]
|
138
|
-
case name
|
139
|
-
when RESOLUTION
|
140
|
-
parse_resolution value
|
141
|
-
when BANDWIDTH
|
142
|
-
item.bandwidth = value.to_i
|
143
|
-
when AVERAGE_BANDWIDTH
|
144
|
-
item.average_bandwidth = value.to_i
|
145
|
-
else
|
146
|
-
set_value name, value
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def parse_segment_discontinuity_tag(*)
|
102
|
+
def parse_discontinuity(*)
|
152
103
|
self.master = false
|
153
104
|
self.open = false
|
154
105
|
|
@@ -156,11 +107,6 @@ module M3u8
|
|
156
107
|
playlist.items.push item
|
157
108
|
end
|
158
109
|
|
159
|
-
def parse_resolution(resolution)
|
160
|
-
item.width = resolution.split('x')[0].to_i
|
161
|
-
item.height = resolution.split('x')[1].to_i
|
162
|
-
end
|
163
|
-
|
164
110
|
def parse_key(line)
|
165
111
|
item = M3u8::KeyItem.parse line
|
166
112
|
playlist.items.push item
|
@@ -168,7 +114,7 @@ module M3u8
|
|
168
114
|
|
169
115
|
def parse_segment(line)
|
170
116
|
self.item = M3u8::SegmentItem.new
|
171
|
-
values = line.gsub(
|
117
|
+
values = line.gsub('#EXTINF:', '').gsub("\n", ',').split(',')
|
172
118
|
item.duration = values[0].to_f
|
173
119
|
item.comment = values[1] unless values[1].nil?
|
174
120
|
|
@@ -177,7 +123,7 @@ module M3u8
|
|
177
123
|
end
|
178
124
|
|
179
125
|
def parse_byterange(line)
|
180
|
-
values = line.gsub(
|
126
|
+
values = line.gsub('#EXT-X-BYTERANGE:', '').gsub("\n", ',').split '@'
|
181
127
|
item.byterange_length = values[0].to_i
|
182
128
|
item.byterange_start = values[1].to_i unless values[1].nil?
|
183
129
|
end
|
@@ -190,29 +136,10 @@ module M3u8
|
|
190
136
|
def parse_media(line)
|
191
137
|
self.open = false
|
192
138
|
self.item = M3u8::MediaItem.new
|
193
|
-
|
194
|
-
attributes = parse_attributes line
|
195
|
-
parse_media_attributes attributes
|
139
|
+
item.parse line
|
196
140
|
playlist.items.push item
|
197
141
|
end
|
198
142
|
|
199
|
-
def parse_media_attributes(attributes)
|
200
|
-
attributes.each do |pair|
|
201
|
-
name = pair[0]
|
202
|
-
value = parse_value pair[1]
|
203
|
-
case name
|
204
|
-
when AUTOSELECT
|
205
|
-
item.autoselect = parse_yes_no value
|
206
|
-
when DEFAULT
|
207
|
-
item.default = parse_yes_no value
|
208
|
-
when FORCED
|
209
|
-
item.forced = parse_yes_no value
|
210
|
-
else
|
211
|
-
set_value name, value
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
143
|
def parse_next_line(line)
|
217
144
|
value = line.gsub "\n", ''
|
218
145
|
if master
|
@@ -223,22 +150,5 @@ module M3u8
|
|
223
150
|
playlist.items.push item
|
224
151
|
self.open = false
|
225
152
|
end
|
226
|
-
|
227
|
-
def parse_yes_no(string)
|
228
|
-
string == 'YES' ? true : false
|
229
|
-
end
|
230
|
-
|
231
|
-
def parse_attributes(line)
|
232
|
-
line.scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
|
233
|
-
end
|
234
|
-
|
235
|
-
def parse_value(value)
|
236
|
-
value.gsub("\n", '').gsub('"', '')
|
237
|
-
end
|
238
|
-
|
239
|
-
def set_value(name, value)
|
240
|
-
name = name.downcase.gsub('-', '_')
|
241
|
-
item.instance_variable_set("@#{name}", value)
|
242
|
-
end
|
243
153
|
end
|
244
154
|
end
|
data/lib/m3u8/version.rb
CHANGED
data/spec/m3u8_spec.rb
CHANGED
@@ -4,10 +4,17 @@ describe do
|
|
4
4
|
let(:test_class) { Class.new { extend M3u8 } }
|
5
5
|
|
6
6
|
it 'should parse attributes to hash' do
|
7
|
-
line = %(TEST-ID="Help",URI="http://test",ID=33)
|
7
|
+
line = %(TEST-ID="Help",URI="http://test",ID=33\n)
|
8
8
|
hash = test_class.parse_attributes line
|
9
9
|
expect(hash['TEST-ID']).to eq 'Help'
|
10
10
|
expect(hash['URI']).to eq 'http://test'
|
11
11
|
expect(hash['ID']).to eq '33'
|
12
12
|
end
|
13
|
+
|
14
|
+
it 'should parse yes/no string' do
|
15
|
+
value = 'YES'
|
16
|
+
expect(test_class.parse_yes_no value).to be true
|
17
|
+
value = 'NO'
|
18
|
+
expect(test_class.parse_yes_no value).to be false
|
19
|
+
end
|
13
20
|
end
|
data/spec/media_item_spec.rb
CHANGED
@@ -22,4 +22,22 @@ describe M3u8::MediaItem do
|
|
22
22
|
'DEFAULT=NO,URI="frelo/prog_index.m3u8",FORCED=YES'
|
23
23
|
expect(output).to eq expected
|
24
24
|
end
|
25
|
+
|
26
|
+
it 'should parse m3u8 text into instance' do
|
27
|
+
format = '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",' \
|
28
|
+
'ASSOC-LANGUAGE="spoken",NAME="Francais",AUTOSELECT=YES,' +
|
29
|
+
%("DEFAULT=NO,URI="frelo/prog_index.m3u8",FORCED=YES\n")
|
30
|
+
item = M3u8::MediaItem.new
|
31
|
+
item.parse format
|
32
|
+
|
33
|
+
expect(item.type).to eq 'AUDIO'
|
34
|
+
expect(item.group_id).to eq 'audio-lo'
|
35
|
+
expect(item.language).to eq 'fre'
|
36
|
+
expect(item.assoc_language).to eq 'spoken'
|
37
|
+
expect(item.name).to eq 'Francais'
|
38
|
+
expect(item.autoselect).to be true
|
39
|
+
expect(item.default).to be false
|
40
|
+
expect(item.uri).to eq 'frelo/prog_index.m3u8'
|
41
|
+
expect(item.forced).to be true
|
42
|
+
end
|
25
43
|
end
|
data/spec/playlist_item_spec.rb
CHANGED
@@ -15,6 +15,43 @@ describe M3u8::PlaylistItem do
|
|
15
15
|
expect(item.iframe).to be false
|
16
16
|
end
|
17
17
|
|
18
|
+
it 'should parse m3u8 text into instance' do
|
19
|
+
format = %(#EXT-X-STREAM-INF:CODECS="avc",BANDWIDTH=540,) +
|
20
|
+
%(PROGRAM-ID=1,RESOLUTION=1920x1080,) +
|
21
|
+
%(AVERAGE-BANDWIDTH=550,AUDIO="test",VIDEO="test2",) +
|
22
|
+
%(SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url")
|
23
|
+
item = M3u8::PlaylistItem.new
|
24
|
+
item.parse(format)
|
25
|
+
expect(item.program_id).to eq '1'
|
26
|
+
expect(item.codecs).to eq 'avc'
|
27
|
+
expect(item.bandwidth).to eq 540
|
28
|
+
expect(item.average_bandwidth).to eq 550
|
29
|
+
expect(item.width).to eq 1920
|
30
|
+
expect(item.height).to eq 1080
|
31
|
+
expect(item.audio).to eq 'test'
|
32
|
+
expect(item.video).to eq 'test2'
|
33
|
+
expect(item.subtitles).to eq 'subs'
|
34
|
+
expect(item.closed_captions).to eq 'caps'
|
35
|
+
expect(item.uri).to eq 'test.url'
|
36
|
+
|
37
|
+
format = %(#EXT-X-STREAM-INF:CODECS="avc",BANDWIDTH=540,) +
|
38
|
+
%(PROGRAM-ID=1,AUDIO="test",VIDEO="test2",) +
|
39
|
+
%(SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url")
|
40
|
+
item = M3u8::PlaylistItem.new
|
41
|
+
item.parse(format)
|
42
|
+
expect(item.program_id).to eq '1'
|
43
|
+
expect(item.codecs).to eq 'avc'
|
44
|
+
expect(item.bandwidth).to eq 540
|
45
|
+
expect(item.average_bandwidth).to be_nil
|
46
|
+
expect(item.width).to be_nil
|
47
|
+
expect(item.height).to be_nil
|
48
|
+
expect(item.audio).to eq 'test'
|
49
|
+
expect(item.video).to eq 'test2'
|
50
|
+
expect(item.subtitles).to eq 'subs'
|
51
|
+
expect(item.closed_captions).to eq 'caps'
|
52
|
+
expect(item.uri).to eq 'test.url'
|
53
|
+
end
|
54
|
+
|
18
55
|
it 'should provide m3u8 format representation' do
|
19
56
|
hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
|
20
57
|
bandwidth: 540, uri: 'test.url' }
|
data/spec/reader_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: m3u8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Seth Deckard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|