m3u8 0.5.1 → 0.5.2
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 +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
|