m3u8 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -17
- data/README.md +8 -11
- data/lib/m3u8.rb +3 -1
- data/lib/m3u8/discontinuity_item.rb +14 -0
- data/lib/m3u8/key_item.rb +54 -0
- data/lib/m3u8/reader.rb +51 -19
- data/lib/m3u8/segment_item.rb +2 -0
- data/lib/m3u8/version.rb +2 -1
- data/lib/m3u8/writer.rb +1 -0
- data/spec/discontinuity_item_spec.rb +10 -0
- data/spec/fixtures/encrypted.m3u8 +18 -0
- data/spec/fixtures/playlist.m3u8 +1 -0
- data/spec/fixtures/playlist_with_comments.m3u8 +1 -0
- data/spec/key_item_spec.rb +46 -0
- data/spec/reader_spec.rb +18 -2
- data/spec/writer_spec.rb +17 -9
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54599784eb3cf885ed988991d50590eb1190b63d
|
4
|
+
data.tar.gz: ca8ce3a4faf778a961fd937057702ecba30a9905
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db42bd190ed77c6595b8ba4f42ccbdb28a8d42a4e0f0f840ab0d5b2b7fb480fb9e99b3415dbc3a172dd29126c08647ebf2e234c8d0d47d4b56afe0f9c6e34590
|
7
|
+
data.tar.gz: 5ac8f0fa3f99a83bf088435be2ff0625bc9d5db5f3159f88f575fe8c5d7a6e4561c671ec999c9359533a1c8c69685ce034c606ceae9c61acbc1a751388ff28b9
|
data/CHANGELOG.md
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
1
|
+
#### 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
|
+
***
|
3
|
+
#### 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.
|
4
|
+
***
|
5
|
+
#### 0.4.0 (1/20/2015) - BREAKING: Playlist.audio is now Playlist.audio_codec, audio now represents the newly supported AUDIO attribute, please update your code accordingly. Added support for all EXT-X-MEDIA attributes as well as the following EXT-X-STREAM-INF attributes: AVERAGE-BANDWIDTH, AUDIO, VIDEO, SUBTILES, and CLOSED-CAPTIONS. This means the library now supports alternate audio / camera angles as well as subtitles and closed captions. The EXT-X-PLAYLIST-TYPE attribute is now supported as Playlist.type. [elsurudo](https://github.com/elsurudo) added support for comments/titles in SegmentItems. A bug was also fixed in Reader that prevented reuse of the instance.
|
6
|
+
***
|
7
|
+
#### 0.3.2 (1/16/2015) - PROGRAM-ID was removed in protocol version 6, if not provided it will now be omitted with a fix implemented by [elsurudo](https://github.com/elsurudo).
|
8
|
+
***
|
9
|
+
#### 0.3.1 (1/15/2015) - [DaKaZ](https://github.com/DaKaZ) added duration method to Playlist to get the total length of segments contained within it.
|
10
|
+
***
|
11
|
+
#### 0.3.0 (11/26/2014) - DEPRECIATED add_playlist and add_segment on Playlist, manipulate the items array directly instead. Extracted writing of playlists to it's own Writer class (only use directly if you want more control over the process). Added read convience method to Playlist so Reader doesn't have to be used directly unless more control is desired. Simplified validation and other aspects during this refactoring.
|
12
|
+
***
|
13
|
+
#### 0.2.1 (11/26/2014) - Moved codec generation / validation to PlaylistItem, allowing for the flexibility to either specify :audio, :level, :profile when creating new instances (codecs value will be automatically generated) or just set the codecs attribute directly.
|
14
|
+
***
|
15
|
+
#### 0.2.0 (11/25/2014) - Added ability to parse m3u8 playlists with new Reader class. Introduced two new classes to represent the items that make up a Playlist: PlaylistItem and SegmentItem.
|
16
|
+
***
|
17
|
+
#### 0.1.3 (11/23/2014) - Fixed bug in codec string generation.
|
18
|
+
***
|
19
|
+
#### 0.1.0 (10/06/2014) - Initial release, provides ability to generate m3u8 playlists (master and segments).
|
data/README.md
CHANGED
@@ -34,9 +34,8 @@ playlist = M3u8::Playlist.new
|
|
34
34
|
|
35
35
|
Create a new playlist item with options:
|
36
36
|
```ruby
|
37
|
-
options = {
|
38
|
-
|
39
|
-
playlist: 'test.url' }
|
37
|
+
options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
|
38
|
+
audio_codec: 'aac-lc', bandwidth: 540, uri: 'test.url' }
|
40
39
|
item = M3u8::PlaylistItem.new options
|
41
40
|
playlist.items.push item
|
42
41
|
```
|
@@ -74,8 +73,8 @@ M3u8::Writer is the class that handles generating the playlist output.
|
|
74
73
|
|
75
74
|
Alternatively you can set codecs rather than having it generated automatically:
|
76
75
|
```ruby
|
77
|
-
options = {
|
78
|
-
|
76
|
+
options = { width: 1920, height: 1080, codecs: 'avc1.66.30,mp4a.40.2',
|
77
|
+
bandwidth: 540, uri: 'test.url' }
|
79
78
|
item = M3u8::PlaylistItem.new options
|
80
79
|
```
|
81
80
|
Just get the codec string for custom use:
|
@@ -109,9 +108,8 @@ playlist.items.first
|
|
109
108
|
```
|
110
109
|
Create a new playlist item with options:
|
111
110
|
```ruby
|
112
|
-
options = {
|
113
|
-
|
114
|
-
playlist: 'test.url' }
|
111
|
+
options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
|
112
|
+
audio_codec: 'aac-lc', bandwidth: 540, uri: 'test.url' }
|
115
113
|
item = M3u8::PlaylistItem.new options
|
116
114
|
#add it to the top of the playlist
|
117
115
|
playlist.items.insert 0, item
|
@@ -126,19 +124,18 @@ M3u8::Reader is the class handles parsing if you want more control over the proc
|
|
126
124
|
* Supports I-Frames (Intra frames) and Byte Ranges in Segments.
|
127
125
|
* Supports subtitles, closed captions, alternate audio and video, and comments.
|
128
126
|
# Supports Session Data in master playlists.
|
127
|
+
* Supports keys for encrypted media segments (EXT-X-KEY).
|
128
|
+
* Supports EXT-X-DISCONTINUITY in media segments.
|
129
129
|
* Can write playlist to an IO object (StringIO/File, etc) or access string via to_s.
|
130
130
|
* Can read playlists into a model (Playlist and Items) from an IO object.
|
131
131
|
* Any tag or attribute supported by the object model is supported both parsing and generation of m3u8 playlists.
|
132
132
|
|
133
133
|
## Missing (but planned) Features
|
134
|
-
* Support for encrypted segments.
|
135
134
|
* Validation of all attributes and their values to match the rules defined in the spec.
|
136
135
|
* Still missing support for a few tags and attributes.
|
137
136
|
|
138
137
|
## Contributing
|
139
138
|
|
140
|
-
(Be sure to check to dev branch to make sure I'm not already working on it)
|
141
|
-
|
142
139
|
1. Fork it ( https://github.com/sethdeckard/m3u8/fork )
|
143
140
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
144
141
|
3. Run the specs, make sure they pass and that new features are covered
|
data/lib/m3u8.rb
CHANGED
@@ -3,14 +3,16 @@ require 'm3u8/version'
|
|
3
3
|
require 'm3u8/playlist'
|
4
4
|
require 'm3u8/playlist_item'
|
5
5
|
require 'm3u8/segment_item'
|
6
|
+
require 'm3u8/discontinuity_item'
|
7
|
+
require 'm3u8/key_item'
|
6
8
|
require 'm3u8/media_item'
|
7
9
|
require 'm3u8/session_data_item'
|
8
10
|
require 'm3u8/reader'
|
9
11
|
require 'm3u8/writer'
|
10
12
|
require 'm3u8/error'
|
11
13
|
|
14
|
+
# M3u8 provides parsing, generation, and validation of m3u8 playlists
|
12
15
|
module M3u8
|
13
|
-
|
14
16
|
def parse_attributes(line)
|
15
17
|
array = line.scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
|
16
18
|
Hash[array.map { |key, value| [key, value.gsub('"', '')] }]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module M3u8
|
2
|
+
# DiscontinuityItem represents a EXT-X-DISCONTINUITY tag to indicate a
|
3
|
+
# discontinuity between the SegmentItems that proceed and follow it.
|
4
|
+
class DiscontinuityItem
|
5
|
+
attr_accessor :tag
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#EXT-X-DISCONTINUITY\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module M3u8
|
2
|
+
# KeyItem represents a set of EXT-X-KEY attributes
|
3
|
+
class KeyItem
|
4
|
+
extend M3u8
|
5
|
+
attr_accessor :method, :uri, :iv, :key_format, :key_format_versions
|
6
|
+
|
7
|
+
def initialize(params = {})
|
8
|
+
params.each do |key, value|
|
9
|
+
instance_variable_set("@#{key}", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse(text)
|
14
|
+
attributes = parse_attributes text
|
15
|
+
options = { method: attributes['METHOD'], uri: attributes['URI'],
|
16
|
+
iv: attributes['IV'], key_format: attributes['KEYFORMAT'],
|
17
|
+
key_format_versions: attributes['KEYFORMATVERSIONS'] }
|
18
|
+
KeyItem.new options
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
attributes = [method_format,
|
23
|
+
uri_format,
|
24
|
+
iv_format,
|
25
|
+
key_format_format,
|
26
|
+
key_format_versions_format].compact.join(',')
|
27
|
+
"#EXT-X-KEY:#{attributes}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def method_format
|
33
|
+
"METHOD=#{method}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def uri_format
|
37
|
+
%(URI="#{uri}") unless uri.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def iv_format
|
41
|
+
"IV=#{iv}" unless iv.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def key_format_format
|
45
|
+
%(KEYFORMAT="#{key_format}") unless key_format.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def key_format_versions_format
|
49
|
+
return if key_format_versions.nil?
|
50
|
+
|
51
|
+
%(KEYFORMATVERSIONS="#{key_format_versions}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/m3u8/reader.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module M3u8
|
2
|
+
# Reader provides parsing of m3u8 playlists
|
2
3
|
class Reader
|
3
4
|
attr_accessor :playlist, :item, :open, :master
|
4
5
|
PLAYLIST_START = '#EXTM3U'
|
@@ -12,7 +13,9 @@ module M3u8
|
|
12
13
|
STREAM_IFRAME_START = '#EXT-X-I-FRAME-STREAM-INF:'
|
13
14
|
MEDIA_START = '#EXT-X-MEDIA:'
|
14
15
|
SESSION_DATA_START = '#EXT-X-SESSION-DATA:'
|
16
|
+
KEY_START = '#EXT-X-KEY:'
|
15
17
|
SEGMENT_START = '#EXTINF:'
|
18
|
+
SEGMENT_DISCONTINUITY_TAG_START = '#EXT-X-DISCONTINUITY'
|
16
19
|
BYTERANGE_START = '#EXT-X-BYTERANGE:'
|
17
20
|
RESOLUTION = 'RESOLUTION'
|
18
21
|
BANDWIDTH = 'BANDWIDTH'
|
@@ -33,27 +36,13 @@ module M3u8
|
|
33
36
|
|
34
37
|
def parse_line(line)
|
35
38
|
return if line.start_with? PLAYLIST_START
|
36
|
-
|
37
|
-
if line
|
38
|
-
|
39
|
-
|
40
|
-
parse_iframe_stream line
|
41
|
-
elsif line.start_with? SEGMENT_START
|
42
|
-
parse_segment line
|
43
|
-
elsif line.start_with? MEDIA_START
|
44
|
-
parse_media line
|
45
|
-
elsif line.start_with? BYTERANGE_START
|
46
|
-
parse_byterange line
|
47
|
-
elsif line.start_with? SESSION_DATA_START
|
48
|
-
parse_session_data line
|
49
|
-
elsif !item.nil? && open
|
50
|
-
parse_next_line line
|
51
|
-
else
|
52
|
-
parse_header line
|
53
|
-
end
|
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
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
45
|
+
def parse_header_tags(line)
|
57
46
|
if line.start_with? PLAYLIST_TYPE_START
|
58
47
|
parse_playlist_type line
|
59
48
|
elsif line.start_with? VERSION_START
|
@@ -66,6 +55,36 @@ module M3u8
|
|
66
55
|
parse_target line
|
67
56
|
elsif line.start_with? IFRAME_START
|
68
57
|
playlist.iframes_only = true
|
58
|
+
else
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_master_playlist_tags(line)
|
64
|
+
if line.start_with? STREAM_START
|
65
|
+
parse_stream line
|
66
|
+
elsif line.start_with? STREAM_IFRAME_START
|
67
|
+
parse_iframe_stream line
|
68
|
+
elsif line.start_with? MEDIA_START
|
69
|
+
parse_media line
|
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
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_segment_tags(line)
|
80
|
+
if line.start_with? KEY_START
|
81
|
+
parse_key line
|
82
|
+
elsif line.start_with? SEGMENT_START
|
83
|
+
parse_segment line
|
84
|
+
elsif line.start_with? BYTERANGE_START
|
85
|
+
parse_byterange line
|
86
|
+
else
|
87
|
+
return false
|
69
88
|
end
|
70
89
|
end
|
71
90
|
|
@@ -129,11 +148,24 @@ module M3u8
|
|
129
148
|
end
|
130
149
|
end
|
131
150
|
|
151
|
+
def parse_segment_discontinuity_tag(*)
|
152
|
+
self.master = false
|
153
|
+
self.open = false
|
154
|
+
|
155
|
+
self.item = M3u8::DiscontinuityItem.new
|
156
|
+
playlist.items.push item
|
157
|
+
end
|
158
|
+
|
132
159
|
def parse_resolution(resolution)
|
133
160
|
item.width = resolution.split('x')[0].to_i
|
134
161
|
item.height = resolution.split('x')[1].to_i
|
135
162
|
end
|
136
163
|
|
164
|
+
def parse_key(line)
|
165
|
+
item = M3u8::KeyItem.parse line
|
166
|
+
playlist.items.push item
|
167
|
+
end
|
168
|
+
|
137
169
|
def parse_segment(line)
|
138
170
|
self.item = M3u8::SegmentItem.new
|
139
171
|
values = line.gsub(SEGMENT_START, '').gsub("\n", ',').split(',')
|
data/lib/m3u8/segment_item.rb
CHANGED
data/lib/m3u8/version.rb
CHANGED
data/lib/m3u8/writer.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
#EXTM3U
|
2
|
+
#EXT-X-VERSION:3
|
3
|
+
#EXT-X-MEDIA-SEQUENCE:7794
|
4
|
+
#EXT-X-TARGETDURATION:15
|
5
|
+
|
6
|
+
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"
|
7
|
+
|
8
|
+
#EXTINF:2.833,
|
9
|
+
http://media.example.com/fileSequence52-A.ts
|
10
|
+
#EXTINF:15.0,
|
11
|
+
http://media.example.com/fileSequence52-B.ts
|
12
|
+
#EXTINF:13.333,
|
13
|
+
http://media.example.com/fileSequence52-C.ts
|
14
|
+
|
15
|
+
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"
|
16
|
+
|
17
|
+
#EXTINF:15.0,
|
18
|
+
http://media.example.com/fileSequence53-A.ts
|
data/spec/fixtures/playlist.m3u8
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe M3u8::KeyItem do
|
4
|
+
it 'should initialize with hash' do
|
5
|
+
hash = { method: 'AES-128', uri: 'http://test.key',
|
6
|
+
iv: 'D512BBF', key_format: 'identity',
|
7
|
+
key_format_versions: '1/3' }
|
8
|
+
item = M3u8::KeyItem.new(hash)
|
9
|
+
expect(item.method).to eq 'AES-128'
|
10
|
+
expect(item.uri).to eq 'http://test.key'
|
11
|
+
expect(item.iv).to eq 'D512BBF'
|
12
|
+
expect(item.key_format).to eq 'identity'
|
13
|
+
expect(item.key_format_versions).to eq '1/3'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should provide m3u8 format representation' do
|
17
|
+
hash = { method: 'AES-128', uri: 'http://test.key',
|
18
|
+
iv: 'D512BBF', key_format: 'identity',
|
19
|
+
key_format_versions: '1/3' }
|
20
|
+
item = M3u8::KeyItem.new(hash)
|
21
|
+
expected = %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
|
22
|
+
%(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3")
|
23
|
+
expect(item.to_s).to eq expected
|
24
|
+
|
25
|
+
hash = { method: 'AES-128', uri: 'http://test.key' }
|
26
|
+
item = M3u8::KeyItem.new(hash)
|
27
|
+
expected = %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key")
|
28
|
+
expect(item.to_s).to eq expected
|
29
|
+
|
30
|
+
hash = { method: 'NONE' }
|
31
|
+
item = M3u8::KeyItem.new(hash)
|
32
|
+
expected = '#EXT-X-KEY:METHOD=NONE'
|
33
|
+
expect(item.to_s).to eq expected
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should parse m3u8 format into instance' do
|
37
|
+
format = %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
|
38
|
+
%(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3")
|
39
|
+
item = M3u8::KeyItem.parse format
|
40
|
+
expect(item.method).to eq 'AES-128'
|
41
|
+
expect(item.uri).to eq 'http://test.key'
|
42
|
+
expect(item.iv).to eq 'D512BBF'
|
43
|
+
expect(item.key_format).to eq 'identity'
|
44
|
+
expect(item.key_format_versions).to eq '1/3'
|
45
|
+
end
|
46
|
+
end
|
data/spec/reader_spec.rb
CHANGED
@@ -55,7 +55,7 @@ describe M3u8::Reader do
|
|
55
55
|
expect(item.duration).to eq 11.344644
|
56
56
|
expect(item.comment).to be_nil
|
57
57
|
|
58
|
-
expect(playlist.items.size).to eq
|
58
|
+
expect(playlist.items.size).to eq 139
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'should parse I-Frame playlist' do
|
@@ -94,7 +94,10 @@ describe M3u8::Reader do
|
|
94
94
|
expect(item.duration).to eq 11.344644
|
95
95
|
expect(item.comment).to eq 'anything'
|
96
96
|
|
97
|
-
|
97
|
+
item = playlist.items[1]
|
98
|
+
expect(item).to be_a(M3u8::DiscontinuityItem)
|
99
|
+
|
100
|
+
expect(playlist.items.size).to eq 139
|
98
101
|
end
|
99
102
|
|
100
103
|
it 'should parse variant playlist with audio options and groups' do
|
@@ -169,4 +172,17 @@ describe M3u8::Reader do
|
|
169
172
|
expect(item.data_id).to eq 'com.example.lyrics'
|
170
173
|
expect(item.uri).to eq 'lyrics.json'
|
171
174
|
end
|
175
|
+
|
176
|
+
it 'should parse encrypted playlist' do
|
177
|
+
file = File.open 'spec/fixtures/encrypted.m3u8'
|
178
|
+
reader = M3u8::Reader.new
|
179
|
+
playlist = reader.read file
|
180
|
+
|
181
|
+
expect(playlist.items.size).to eq 6
|
182
|
+
|
183
|
+
item = playlist.items[0]
|
184
|
+
expect(item).to be_a M3u8::KeyItem
|
185
|
+
expect(item.method).to eq 'AES-128'
|
186
|
+
expect(item.uri).to eq 'https://priv.example.com/key.php?r=52'
|
187
|
+
end
|
172
188
|
end
|
data/spec/writer_spec.rb
CHANGED
@@ -97,20 +97,28 @@ describe M3u8::Writer do
|
|
97
97
|
writer.write playlist
|
98
98
|
expect(io.string).to eq output
|
99
99
|
|
100
|
+
options = { method: 'AES-128', uri: 'http://test.key',
|
101
|
+
iv: 'D512BBF', key_format: 'identity',
|
102
|
+
key_format_versions: '1/3' }
|
103
|
+
item = M3u8::KeyItem.new options
|
104
|
+
playlist.items.push item
|
105
|
+
|
100
106
|
options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
|
101
107
|
item = M3u8::SegmentItem.new options
|
102
108
|
playlist.items.push item
|
103
109
|
|
104
110
|
output = "#EXTM3U\n" \
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
"#EXT-X-VERSION:3\n" \
|
112
|
+
"#EXT-X-MEDIA-SEQUENCE:0\n" \
|
113
|
+
"#EXT-X-ALLOW-CACHE:YES\n" \
|
114
|
+
"#EXT-X-TARGETDURATION:10\n" \
|
115
|
+
"#EXTINF:11.344644,\n" \
|
116
|
+
"1080-7mbps00000.ts\n" +
|
117
|
+
%(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
|
118
|
+
%(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3"\n) +
|
119
|
+
"#EXTINF:11.261233,\n" +
|
120
|
+
"1080-7mbps00001.ts\n" \
|
121
|
+
"#EXT-X-ENDLIST\n"
|
114
122
|
io = StringIO.open
|
115
123
|
writer = M3u8::Writer.new io
|
116
124
|
writer.write playlist
|
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.1
|
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-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -70,7 +70,9 @@ files:
|
|
70
70
|
- README.md
|
71
71
|
- Rakefile
|
72
72
|
- lib/m3u8.rb
|
73
|
+
- lib/m3u8/discontinuity_item.rb
|
73
74
|
- lib/m3u8/error.rb
|
75
|
+
- lib/m3u8/key_item.rb
|
74
76
|
- lib/m3u8/media_item.rb
|
75
77
|
- lib/m3u8/playlist.rb
|
76
78
|
- lib/m3u8/playlist_item.rb
|
@@ -80,6 +82,8 @@ files:
|
|
80
82
|
- lib/m3u8/version.rb
|
81
83
|
- lib/m3u8/writer.rb
|
82
84
|
- m3u8.gemspec
|
85
|
+
- spec/discontinuity_item_spec.rb
|
86
|
+
- spec/fixtures/encrypted.m3u8
|
83
87
|
- spec/fixtures/iframes.m3u8
|
84
88
|
- spec/fixtures/master.m3u8
|
85
89
|
- spec/fixtures/master_iframes.m3u8
|
@@ -88,6 +92,7 @@ files:
|
|
88
92
|
- spec/fixtures/session_data.m3u8
|
89
93
|
- spec/fixtures/variant_angles.m3u8
|
90
94
|
- spec/fixtures/variant_audio.m3u8
|
95
|
+
- spec/key_item_spec.rb
|
91
96
|
- spec/m3u8_spec.rb
|
92
97
|
- spec/media_item_spec.rb
|
93
98
|
- spec/playlist_item_spec.rb
|
@@ -122,6 +127,8 @@ signing_key:
|
|
122
127
|
specification_version: 4
|
123
128
|
summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).
|
124
129
|
test_files:
|
130
|
+
- spec/discontinuity_item_spec.rb
|
131
|
+
- spec/fixtures/encrypted.m3u8
|
125
132
|
- spec/fixtures/iframes.m3u8
|
126
133
|
- spec/fixtures/master.m3u8
|
127
134
|
- spec/fixtures/master_iframes.m3u8
|
@@ -130,6 +137,7 @@ test_files:
|
|
130
137
|
- spec/fixtures/session_data.m3u8
|
131
138
|
- spec/fixtures/variant_angles.m3u8
|
132
139
|
- spec/fixtures/variant_audio.m3u8
|
140
|
+
- spec/key_item_spec.rb
|
133
141
|
- spec/m3u8_spec.rb
|
134
142
|
- spec/media_item_spec.rb
|
135
143
|
- spec/playlist_item_spec.rb
|