m3u8 0.7.1 → 0.8.0

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.
@@ -3,177 +3,251 @@ require 'spec_helper'
3
3
 
4
4
  describe M3u8::Writer do
5
5
  describe '#write' do
6
- it 'should render master playlist' do
7
- options = { uri: 'playlist_url', bandwidth: 6400,
8
- audio_codec: 'mp3' }
9
- item = M3u8::PlaylistItem.new(options)
10
- playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
11
- playlist.items << item
12
-
13
- output = "#EXTM3U\n" \
14
- "#EXT-X-VERSION:6\n" \
15
- "#EXT-X-INDEPENDENT-SEGMENTS\n" +
16
- %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
17
- ",BANDWIDTH=6400\nplaylist_url\n"
18
-
19
- io = StringIO.open
20
- writer = M3u8::Writer.new(io)
21
- writer.write(playlist)
22
- expect(io.string).to eq(output)
6
+ context 'when playlist is a master playlist' do
7
+ it 'writes playlist to io' do
8
+ playlist = M3u8::Playlist.new
9
+ options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
10
+ audio_codec: 'mp3' }
11
+ item = M3u8::PlaylistItem.new(options)
12
+ playlist.items << item
13
+ options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
14
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
15
+ audio_codec: 'aac-lc' }
16
+ item = M3u8::PlaylistItem.new(options)
17
+ playlist.items << item
18
+ options = { data_id: 'com.test.movie.title', value: 'Test',
19
+ uri: 'http://test', language: 'en' }
20
+ item = M3u8::SessionDataItem.new(options)
21
+ playlist.items << item
22
+
23
+ expected = "#EXTM3U\n" +
24
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
25
+ ",BANDWIDTH=6400\nplaylist_url\n" \
26
+ '#EXT-X-STREAM-INF:PROGRAM-ID=2,' +
27
+ %(RESOLUTION=1920x1080,CODECS="avc1.640029,mp4a.40.2") +
28
+ ",BANDWIDTH=50000\nplaylist_url\n" +
29
+ %(#EXT-X-SESSION-DATA:DATA-ID="com.test.movie.title",) +
30
+ %(VALUE="Test",URI="http://test",LANGUAGE="en"\n)
31
+
32
+ io = StringIO.open
33
+ writer = described_class.new(io)
34
+ writer.write(playlist)
35
+ expect(io.string).to eq(expected)
36
+ end
37
+ end
23
38
 
24
- options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
25
- audio_codec: 'mp3' }
26
- item = M3u8::PlaylistItem.new(options)
27
- playlist = M3u8::Playlist.new
28
- playlist.items << item
39
+ context 'when playlist is a master playlist with single stream' do
40
+ it 'writes playlist to io' do
41
+ options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
42
+ audio_codec: 'mp3' }
43
+ item = M3u8::PlaylistItem.new(options)
44
+ playlist = M3u8::Playlist.new
45
+ playlist.items << item
46
+
47
+ expected = "#EXTM3U\n" +
48
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
49
+ ",BANDWIDTH=6400\nplaylist_url\n"
50
+
51
+ io = StringIO.open
52
+ writer = described_class.new(io)
53
+ writer.write(playlist)
54
+ expect(io.string).to eq(expected)
55
+ end
56
+ end
29
57
 
30
- output = "#EXTM3U\n" +
31
- %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
32
- ",BANDWIDTH=6400\nplaylist_url\n"
58
+ context 'when playlist is a master playlist with header options' do
59
+ it 'writes playlist to io' do
60
+ options = { uri: 'playlist_url', bandwidth: 6400,
61
+ audio_codec: 'mp3' }
62
+ item = M3u8::PlaylistItem.new(options)
63
+ playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
64
+ playlist.items << item
65
+
66
+ expected = "#EXTM3U\n" \
67
+ "#EXT-X-VERSION:6\n" \
68
+ "#EXT-X-INDEPENDENT-SEGMENTS\n" +
69
+ %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
70
+ ",BANDWIDTH=6400\nplaylist_url\n"
71
+
72
+ io = StringIO.open
73
+ writer = described_class.new(io)
74
+ writer.write(playlist)
75
+ expect(io.string).to eq(expected)
76
+ end
77
+ end
33
78
 
34
- io = StringIO.open
35
- writer = M3u8::Writer.new(io)
36
- writer.write(playlist)
37
- expect(io.string).to eq(output)
79
+ context 'when playlist is a new master playlist' do
80
+ it 'writes playlist to io' do
81
+ io = StringIO.open
82
+ writer = described_class.new(io)
83
+ playlist = M3u8::Playlist.new(master: true)
84
+ writer.write(playlist)
85
+ expect(io.string).to eq("#EXTM3U\n")
86
+ end
87
+ end
38
88
 
39
- options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
40
- width: 1920, height: 1080, profile: 'high', level: 4.1,
41
- audio_codec: 'aac-lc' }
42
- item = M3u8::PlaylistItem.new(options)
43
- playlist = M3u8::Playlist.new
44
- playlist.items << item
89
+ context 'when playlist is a new media playlist' do
90
+ it 'writes playlist to io' do
91
+ io = StringIO.open
92
+ writer = described_class.new(io)
93
+ writer.write(M3u8::Playlist.new)
94
+
95
+ expected = "#EXTM3U\n" \
96
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
97
+ "#EXT-X-TARGETDURATION:10\n" \
98
+ "#EXT-X-ENDLIST\n"
99
+ expect(io.string).to eq(expected)
100
+ end
101
+ end
45
102
 
46
- output = "#EXTM3U\n" \
47
- '#EXT-X-STREAM-INF:PROGRAM-ID=2,RESOLUTION=1920x1080,' +
48
- %(CODECS="avc1.640029,mp4a.40.2",BANDWIDTH=50000\n) +
49
- "playlist_url\n"
103
+ context 'when playlist is a media playlist' do
104
+ it 'writes playlist to io' do
105
+ options = { version: 4, cache: false, target: 6.2, sequence: 1,
106
+ discontinuity_sequence: 10, type: 'EVENT',
107
+ iframes_only: true }
108
+ playlist = M3u8::Playlist.new(options)
109
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
110
+ item = M3u8::SegmentItem.new(options)
111
+ playlist.items << item
112
+
113
+ expected = "#EXTM3U\n" \
114
+ "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
115
+ "#EXT-X-VERSION:4\n" \
116
+ "#EXT-X-I-FRAMES-ONLY\n" \
117
+ "#EXT-X-MEDIA-SEQUENCE:1\n" \
118
+ "#EXT-X-DISCONTINUITY-SEQUENCE:10\n" \
119
+ "#EXT-X-ALLOW-CACHE:NO\n" \
120
+ "#EXT-X-TARGETDURATION:6\n" \
121
+ "#EXTINF:11.344644,\n" \
122
+ "1080-7mbps00000.ts\n" \
123
+ "#EXT-X-ENDLIST\n"
124
+
125
+ io = StringIO.open
126
+ writer = described_class.new(io)
127
+ writer.write(playlist)
128
+ expect(io.string).to eq(expected)
129
+ end
130
+ end
50
131
 
51
- io = StringIO.open
52
- writer = M3u8::Writer.new(io)
53
- writer.write(playlist)
54
- expect(io.string).to eq(output)
132
+ context 'when playlist is media playlist with keys' do
133
+ it 'writes playlist to io' do
134
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
135
+ item = M3u8::SegmentItem.new(options)
136
+ playlist = M3u8::Playlist.new(version: 7)
137
+ playlist.items << item
138
+
139
+ options = { method: 'AES-128', uri: 'http://test.key',
140
+ iv: 'D512BBF', key_format: 'identity',
141
+ key_format_versions: '1/3' }
142
+ item = M3u8::KeyItem.new(options)
143
+ playlist.items << item
144
+
145
+ options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
146
+ item = M3u8::SegmentItem.new(options)
147
+ playlist.items << item
148
+
149
+ expected = "#EXTM3U\n" \
150
+ "#EXT-X-VERSION:7\n" \
151
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
152
+ "#EXT-X-TARGETDURATION:10\n" \
153
+ "#EXTINF:11.344644,\n" \
154
+ "1080-7mbps00000.ts\n" +
155
+ %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
156
+ %(IV=D512BBF,KEYFORMAT="identity",) +
157
+ %(KEYFORMATVERSIONS="1/3"\n) +
158
+ "#EXTINF:11.261233,\n" \
159
+ "1080-7mbps00001.ts\n" \
160
+ "#EXT-X-ENDLIST\n"
161
+
162
+ io = StringIO.open
163
+ writer = described_class.new(io)
164
+ writer.write(playlist)
165
+ expect(io.string).to eq(expected)
166
+ end
167
+ end
55
168
 
169
+ it 'raises error if item types are mixed' do
56
170
  playlist = M3u8::Playlist.new
57
- options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
58
- audio_codec: 'mp3' }
171
+ options = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
172
+ bandwidth: 540, playlist: 'test.url' }
59
173
  item = M3u8::PlaylistItem.new(options)
60
174
  playlist.items << item
61
- options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
62
- width: 1920, height: 1080, profile: 'high', level: 4.1,
63
- audio_codec: 'aac-lc' }
64
- item = M3u8::PlaylistItem.new(options)
65
- playlist.items << item
66
- options = { data_id: 'com.test.movie.title', value: 'Test',
67
- uri: 'http://test', language: 'en' }
68
- item = M3u8::SessionDataItem.new(options)
175
+
176
+ options = { duration: 10.991, segment: 'test.ts' }
177
+ item = M3u8::SegmentItem.new(options)
69
178
  playlist.items << item
70
179
 
71
- output = "#EXTM3U\n" +
72
- %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
73
- ",BANDWIDTH=6400\nplaylist_url\n#EXT-X-STREAM-INF:PROGRAM-ID=2," +
74
- %(RESOLUTION=1920x1080,CODECS="avc1.640029,mp4a.40.2") +
75
- ",BANDWIDTH=50000\nplaylist_url\n" +
76
- %(#EXT-X-SESSION-DATA:DATA-ID="com.test.movie.title",) +
77
- %(VALUE="Test",URI="http://test",LANGUAGE="en"\n)
78
-
79
- io = StringIO.open
80
- writer = M3u8::Writer.new(io)
81
- writer.write(playlist)
82
- expect(io.string).to eq(output)
180
+ message = 'Playlist is invalid.'
181
+ io = StringIO.new
182
+ writer = described_class.new(io)
183
+ expect { writer.write(playlist) }
184
+ .to raise_error(M3u8::PlaylistTypeError, message)
83
185
  end
186
+ end
84
187
 
85
- it 'should render playlist' do
86
- options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
87
- item = M3u8::SegmentItem.new(options)
88
- playlist = M3u8::Playlist.new(version: 7)
89
- playlist.items << item
188
+ describe '#write_footer' do
189
+ context 'when playlist is a master playlist' do
190
+ it 'does nothing' do
191
+ io = StringIO.open
192
+ writer = described_class.new(io)
193
+ playlist = M3u8::Playlist.new(master: true)
194
+ writer.write_footer(playlist)
90
195
 
91
- output = "#EXTM3U\n" \
92
- "#EXT-X-VERSION:7\n" \
93
- "#EXT-X-MEDIA-SEQUENCE:0\n" \
94
- "#EXT-X-TARGETDURATION:10\n" \
95
- "#EXTINF:11.344644,\n" \
96
- "1080-7mbps00000.ts\n" \
97
- "#EXT-X-ENDLIST\n"
98
- io = StringIO.open
99
- writer = M3u8::Writer.new(io)
100
- writer.write(playlist)
101
- expect(io.string).to eq(output)
102
-
103
- options = { method: 'AES-128', uri: 'http://test.key',
104
- iv: 'D512BBF', key_format: 'identity',
105
- key_format_versions: '1/3' }
106
- item = M3u8::KeyItem.new(options)
107
- playlist.items << item
196
+ expect(io.string).to be_empty
197
+ end
198
+ end
108
199
 
109
- options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
110
- item = M3u8::SegmentItem.new(options)
111
- playlist.items << item
200
+ context 'when playlist is a media playlist' do
201
+ it 'writes end list tag' do
202
+ io = StringIO.open
203
+ writer = described_class.new(io)
204
+ playlist = M3u8::Playlist.new(master: false)
205
+ writer.write_footer(playlist)
112
206
 
113
- output = "#EXTM3U\n" \
114
- "#EXT-X-VERSION:7\n" \
115
- "#EXT-X-MEDIA-SEQUENCE:0\n" \
116
- "#EXT-X-TARGETDURATION:10\n" \
117
- "#EXTINF:11.344644,\n" \
118
- "1080-7mbps00000.ts\n" +
119
- %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
120
- %(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3"\n) +
121
- "#EXTINF:11.261233,\n" \
122
- "1080-7mbps00001.ts\n" \
123
- "#EXT-X-ENDLIST\n"
124
- io = StringIO.open
125
- writer = M3u8::Writer.new(io)
126
- writer.write(playlist)
127
- expect(io.string).to eq(output)
128
-
129
- options = { version: 4, cache: false, target: 12, sequence: 1,
130
- type: 'EVENT', iframes_only: true }
131
- playlist = M3u8::Playlist.new(options)
132
- options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
133
- item = M3u8::SegmentItem.new(options)
134
- playlist.items << item
135
-
136
- output = "#EXTM3U\n" \
137
- "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
138
- "#EXT-X-VERSION:4\n" \
139
- "#EXT-X-I-FRAMES-ONLY\n" \
140
- "#EXT-X-MEDIA-SEQUENCE:1\n" \
141
- "#EXT-X-ALLOW-CACHE:NO\n" \
142
- "#EXT-X-TARGETDURATION:12\n" \
143
- "#EXTINF:11.344644,\n" \
144
- "1080-7mbps00000.ts\n" \
145
- "#EXT-X-ENDLIST\n"
146
- io = StringIO.open
147
- writer = M3u8::Writer.new(io)
148
- writer.write(playlist)
149
- expect(io.string).to eq(output)
207
+ expect(io.string).to eq("#EXT-X-ENDLIST\n")
208
+ end
150
209
  end
210
+ end
151
211
 
152
- it 'should render the target duration as a decimal-integer' do
153
- playlist = M3u8::Playlist.new(target: 6.2)
154
- io = StringIO.open
155
- writer = M3u8::Writer.new(io)
156
- writer.write(playlist)
157
- expect(io.string).to include('#EXT-X-TARGETDURATION:6')
212
+ describe '#write_header' do
213
+ context 'when playlist is a master playlist' do
214
+ it 'writes header content only' do
215
+ playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
216
+ options = { uri: 'playlist_url', bandwidth: 6400,
217
+ audio_codec: 'mp3' }
218
+ item = M3u8::PlaylistItem.new(options)
219
+ playlist.items << item
220
+ expect(playlist.master?).to be true
221
+
222
+ expected = "#EXTM3U\n" \
223
+ "#EXT-X-VERSION:6\n" \
224
+ "#EXT-X-INDEPENDENT-SEGMENTS\n"
225
+
226
+ io = StringIO.open
227
+ writer = described_class.new(io)
228
+ writer.write_header(playlist)
229
+ expect(io.string).to eq(expected)
230
+ end
158
231
  end
159
232
 
160
- it 'should raise error on write if item types are mixed' do
161
- playlist = M3u8::Playlist.new
233
+ context 'when playlist is a media playlist' do
234
+ it 'writes header content only' do
235
+ playlist = M3u8::Playlist.new(version: 7)
236
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
237
+ item = M3u8::SegmentItem.new(options)
238
+ playlist.items << item
162
239
 
163
- hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
164
- bandwidth: 540, playlist: 'test.url' }
165
- item = M3u8::PlaylistItem.new(hash)
166
- playlist.items << item
240
+ io = StringIO.open
241
+ writer = described_class.new(io)
242
+ writer.write_header(playlist)
167
243
 
168
- hash = { duration: 10.991, segment: 'test.ts' }
169
- item = M3u8::SegmentItem.new(hash)
170
- playlist.items << item
244
+ expected = "#EXTM3U\n" \
245
+ "#EXT-X-VERSION:7\n" \
246
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
247
+ "#EXT-X-TARGETDURATION:10\n"
171
248
 
172
- message = 'Playlist is invalid.'
173
- io = StringIO.new
174
- writer = M3u8::Writer.new(io)
175
- expect { writer.write(playlist) }
176
- .to raise_error(M3u8::PlaylistTypeError, message)
249
+ expect(io.string).to eq(expected)
250
+ end
177
251
  end
178
252
  end
179
253
  end
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.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Deckard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
195
  version: '0'
196
196
  requirements: []
197
197
  rubyforge_project:
198
- rubygems_version: 2.5.2
198
+ rubygems_version: 2.6.11
199
199
  signing_key:
200
200
  specification_version: 4
201
201
  summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).