m3u8 0.6.9 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a4f00671fa34a2515f395b91535721e0f9c8801d
4
- data.tar.gz: 6444a8131f48f8534837def5f39cef7e0dc132ca
3
+ metadata.gz: cb897f32a24dabcd87ebd26a367425c8eae33113
4
+ data.tar.gz: 5726036c12d84a49d24ae14f21cf52e817c3bc1c
5
5
  SHA512:
6
- metadata.gz: 70cc595f2f165d3d2f337437f88b9d0021fb74cf764e4393949e83fe2470ea55f40b95f64ef3772a0f572875f6773acdf0e1ad122a9011a2587daf3c93584d87
7
- data.tar.gz: 6bb1ca0567ff9ce9b69accedda38d154050bb9c9e72fcd910694fd4a6c1399c3f69da2c686ddc14e3015f86a61fa1cd06ded8cc5f4373a4c8d82350712081cf3
6
+ metadata.gz: db0747bc9be34249f3fbf35d40255763e8b96cddcf9d857495361e8186df81fc4285e6740bc54abab804d2617504c77b8ac7befbb2cc2604e7de0a52d2963d65
7
+ data.tar.gz: ee311cc358cca4494c6d5c1a4fa8e64e50977076d8001d3a1258ca1a4762354f6886b903293f06a424136044b6112e1f4c501dfe449508238d4e6d5e1e7be0dd
data/.gitignore CHANGED
@@ -12,3 +12,4 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ .ruby-version
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  sudo: true
3
3
  rvm:
4
- - 2.3.0
5
- - 2.2.4
4
+ - 2.4.0
5
+ - 2.3.3
6
+ - 2.2.6
6
7
 
7
8
  script: rspec spec
@@ -1,3 +1,5 @@
1
+ 0.7.0 (2/3/2017) - Added support for EXT-X-INDEPENDENT-SEGMENTS and EXT-X-START tags. Minor version bumped due to changes of default values for new playlists.
2
+
1
3
  0.6.9 (4/24/2016) - Merged pull request #17 from [ghn](https://github.com/ghn). Removes return when parsing next line in playlist.
2
4
 
3
5
  ***
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/m3u8.svg)](http://badge.fury.io/rb/m3u8)
2
2
  [![Build Status](https://travis-ci.org/sethdeckard/m3u8.svg?branch=master)](https://travis-ci.org/sethdeckard/m3u8)
3
- [![Coverage Status](https://coveralls.io/repos/sethdeckard/m3u8/badge.png)](https://coveralls.io/r/sethdeckard/m3u8)
3
+ [![Coverage Status](https://coveralls.io/repos/github/sethdeckard/m3u8/badge.svg?branch=master)](https://coveralls.io/github/sethdeckard/m3u8?branch=master)
4
4
  [![Code Climate](https://codeclimate.com/github/sethdeckard/m3u8/badges/gpa.svg)](https://codeclimate.com/github/sethdeckard/m3u8)
5
5
  [![Dependency Status](https://gemnasium.com/sethdeckard/m3u8.svg)](https://gemnasium.com/sethdeckard/m3u8)
6
6
  [![security](https://hakiri.io/github/sethdeckard/m3u8/master.svg)](https://hakiri.io/github/sethdeckard/m3u8/master)
@@ -145,7 +145,7 @@ M3u8::Reader is the class handles parsing if you want more control over the proc
145
145
  * Supports session data in master playlists.
146
146
  * Supports keys for encrypted media segments (EXT-X-KEY, EXT-SESSION-KEY).
147
147
 
148
- ## HLS Spec Status (version 17)
148
+ ## HLS Spec Status (version 20)
149
149
  ### Implemented:
150
150
  * EXTM3U
151
151
  * EXT-X-VERSION
@@ -165,11 +165,10 @@ M3u8::Reader is the class handles parsing if you want more control over the proc
165
165
  * EXT-X-I-FRAME-STREAM-INF
166
166
  * EXT-X-SESSION-DATA
167
167
  * EXT-X-SESSION-KEY
168
+ * EXT-X-START
168
169
 
169
170
  ### TODO:
170
- * EXT-X-DISCONTINUITY-SEQUENCE
171
- * EXT-X-INDEPENDENT-SEGMENTS
172
- * EXT-X-START
171
+ * EXT-X-DATERANGE
173
172
 
174
173
  ## Roadmap
175
174
  * Add the last remaining tags for latest version of the spec.
@@ -187,4 +186,4 @@ M3u8::Reader is the class handles parsing if you want more control over the proc
187
186
 
188
187
 
189
188
  ## License
190
- MIT License - See [LICENSE.txt](https://github.com/sethdeckard/m3u8/blob/master/LICENSE.txt) for details.
189
+ MIT License - See [LICENSE.txt](https://github.com/sethdeckard/m3u8/blob/master/LICENSE.txt) for details.
@@ -4,6 +4,13 @@ Dir[File.dirname(__FILE__) + '/m3u8/*.rb'].sort.each { |file| require file }
4
4
 
5
5
  # M3u8 provides parsing, generation, and validation of m3u8 playlists
6
6
  module M3u8
7
+ def intialize_with_byterange(params = {})
8
+ params.each do |key, value|
9
+ value = ByteRange.new(value) if value.is_a?(Hash)
10
+ instance_variable_set("@#{key}", value)
11
+ end
12
+ end
13
+
7
14
  def parse_attributes(line)
8
15
  array = line.delete("\n").scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
9
16
  Hash[array.map { |key, value| [key, value.delete('"')] }]
@@ -13,10 +20,7 @@ module M3u8
13
20
  value == 'YES' ? true : false
14
21
  end
15
22
 
16
- def intialize_with_byterange(params = {})
17
- params.each do |key, value|
18
- value = ByteRange.new(value) if value.is_a?(Hash)
19
- instance_variable_set("@#{key}", value)
20
- end
23
+ def to_yes_no(boolean)
24
+ boolean == true ? 'YES' : 'NO'
21
25
  end
22
26
  end
@@ -0,0 +1,33 @@
1
+ module M3u8
2
+ # PlaybackStart represents a #EXT-X-START tag and attributes
3
+ class PlaybackStart
4
+ include M3u8
5
+ attr_accessor :time_offset, :precise
6
+
7
+ def initialize(options = {})
8
+ options.each do |key, value|
9
+ instance_variable_set("@#{key}", value)
10
+ end
11
+ end
12
+
13
+ def parse(text)
14
+ attributes = parse_attributes(text)
15
+ @time_offset = attributes['TIME-OFFSET'].to_f
16
+ precise = attributes['PRECISE']
17
+ @precise = parse_yes_no(precise) unless precise.nil?
18
+ end
19
+
20
+ def to_s
21
+ attributes = ["TIME-OFFSET=#{time_offset}",
22
+ precise_format].compact.join(',')
23
+ "#EXT-X-START:#{attributes}"
24
+ end
25
+
26
+ private
27
+
28
+ def precise_format
29
+ return if precise.nil?
30
+ "PRECISE=#{to_yes_no(precise)}"
31
+ end
32
+ end
33
+ end
@@ -3,7 +3,7 @@ module M3u8
3
3
  # of media segments
4
4
  class Playlist
5
5
  attr_accessor :items, :version, :cache, :target, :sequence, :type,
6
- :iframes_only
6
+ :iframes_only, :independent_segments
7
7
 
8
8
  def initialize(options = {})
9
9
  assign_options options
@@ -21,8 +21,8 @@ module M3u8
21
21
  end
22
22
 
23
23
  def write(output)
24
- writer = Writer.new output
25
- writer.write self
24
+ writer = Writer.new(output)
25
+ writer.write(self)
26
26
  end
27
27
 
28
28
  def master?
@@ -32,7 +32,7 @@ module M3u8
32
32
 
33
33
  def to_s
34
34
  output = StringIO.open
35
- write output
35
+ write(output)
36
36
  output.string
37
37
  end
38
38
 
@@ -52,13 +52,7 @@ module M3u8
52
52
  private
53
53
 
54
54
  def assign_options(options)
55
- options = {
56
- version: 3,
57
- sequence: 0,
58
- cache: true,
59
- target: 10,
60
- iframes_only: false
61
- }.merge options
55
+ options = defaults.merge(options)
62
56
 
63
57
  self.version = options[:version]
64
58
  self.sequence = options[:sequence]
@@ -66,6 +60,16 @@ module M3u8
66
60
  self.target = options[:target]
67
61
  self.type = options[:type]
68
62
  self.iframes_only = options[:iframes_only]
63
+ self.independent_segments = options[:independent_segments]
64
+ end
65
+
66
+ def defaults
67
+ {
68
+ sequence: 0,
69
+ target: 10,
70
+ iframes_only: false,
71
+ independent_segments: false
72
+ }
69
73
  end
70
74
 
71
75
  def playlist_size
@@ -8,13 +8,14 @@ module M3u8
8
8
  @tags = [basic_tags,
9
9
  media_segment_tags,
10
10
  media_playlist_tags,
11
- master_playlist_tags].inject(:merge)
11
+ master_playlist_tags,
12
+ universal_tags].inject(:merge)
12
13
  end
13
14
 
14
15
  def read(input)
15
16
  self.playlist = Playlist.new
16
17
  input.each_line do |line|
17
- parse_line line
18
+ parse_line(line)
18
19
  end
19
20
  playlist
20
21
  end
@@ -53,6 +54,18 @@ module M3u8
53
54
  }
54
55
  end
55
56
 
57
+ def universal_tags
58
+ { '#EXT-X-START' => ->(line) { parse_start(line) },
59
+ '#EXT-X-INDEPENDENT-SEGMENTS' => proc do
60
+ playlist.independent_segments = true
61
+ end
62
+ }
63
+ end
64
+
65
+ def parse_independent_segments(line)
66
+ parse_independent_segments
67
+ end
68
+
56
69
  def parse_line(line)
57
70
  return if match_tag(line)
58
71
  parse_next_line(line) if !item.nil? && open
@@ -151,6 +164,12 @@ module M3u8
151
164
  playlist.items << item
152
165
  end
153
166
 
167
+ def parse_start(line)
168
+ item = M3u8::PlaybackStart.new
169
+ item.parse(line)
170
+ playlist.items << item
171
+ end
172
+
154
173
  def parse_time(line)
155
174
  if open
156
175
  item.program_date_time = M3u8::TimeItem.parse(line)
@@ -1,4 +1,4 @@
1
1
  # M3u8 provides parsing, generation, and validation of m3u8 playlists
2
2
  module M3u8
3
- VERSION = '0.6.9'
3
+ VERSION = '0.7.0'
4
4
  end
@@ -8,10 +8,8 @@ module M3u8
8
8
  end
9
9
 
10
10
  def write(playlist)
11
- validate playlist
12
-
13
- io.puts '#EXTM3U'
14
- write_header(playlist) unless playlist.master?
11
+ validate(playlist)
12
+ write_header(playlist)
15
13
 
16
14
  playlist.items.each do |item|
17
15
  io.puts item.to_s
@@ -23,26 +21,55 @@ module M3u8
23
21
 
24
22
  private
25
23
 
24
+ def target_duration_format(playlist)
25
+ format('#EXT-X-TARGETDURATION:%d', playlist.target)
26
+ end
27
+
26
28
  def validate(playlist)
27
29
  return if playlist.valid?
28
- fail PlaylistTypeError, 'Playlist is invalid.'
30
+ raise PlaylistTypeError, 'Playlist is invalid.'
31
+ end
32
+
33
+ def write_cache_tag(cache)
34
+ return if cache.nil?
35
+
36
+ io.puts "#EXT-X-ALLOW-CACHE:#{cache ? 'YES' : 'NO'}"
29
37
  end
30
38
 
31
39
  def write_header(playlist)
40
+ io.puts '#EXTM3U'
41
+ if playlist.master?
42
+ write_master_playlist_header(playlist)
43
+ else
44
+ write_media_playlist_header(playlist)
45
+ end
46
+ end
47
+
48
+ def write_independent_segments_tag(independent_segments)
49
+ return unless independent_segments
50
+
51
+ io.puts '#EXT-X-INDEPENDENT-SEGMENTS'
52
+ end
53
+
54
+ def write_master_playlist_header(playlist)
55
+ write_version_tag(playlist.version)
56
+ write_independent_segments_tag(playlist.independent_segments)
57
+ end
58
+
59
+ def write_media_playlist_header(playlist)
32
60
  io.puts "#EXT-X-PLAYLIST-TYPE:#{playlist.type}" unless playlist.type.nil?
33
- io.puts "#EXT-X-VERSION:#{playlist.version}"
61
+ write_version_tag(playlist.version)
62
+ write_independent_segments_tag(playlist.independent_segments)
34
63
  io.puts '#EXT-X-I-FRAMES-ONLY' if playlist.iframes_only
35
64
  io.puts "#EXT-X-MEDIA-SEQUENCE:#{playlist.sequence}"
36
- io.puts "#EXT-X-ALLOW-CACHE:#{cache(playlist)}"
65
+ write_cache_tag(playlist.cache)
37
66
  io.puts target_duration_format(playlist)
38
67
  end
39
68
 
40
- def cache(playlist)
41
- playlist.cache ? 'YES' : 'NO'
42
- end
69
+ def write_version_tag(version)
70
+ return if version.nil?
43
71
 
44
- def target_duration_format(playlist)
45
- format('#EXT-X-TARGETDURATION:%d', playlist.target)
72
+ io.puts "#EXT-X-VERSION:#{version}"
46
73
  end
47
74
  end
48
75
  end
@@ -1,5 +1,7 @@
1
1
  #EXTM3U
2
+ #EXT-X-INDEPENDENT-SEGMENTS
2
3
  #EXT-X-SESSION-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"
4
+ #EXT-X-START:TIME-OFFSET=20.2
3
5
  #EXT-X-STREAM-INF:PROGRAM-ID=1,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2",BANDWIDTH=5042000
4
6
  hls/1080-7mbps/1080-7mbps.m3u8
5
7
  #EXT-X-STREAM-INF:PROGRAM-ID=1,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2",BANDWIDTH=4853000
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::PlaybackStart do
4
+ describe '#intialize' do
5
+ it 'assigns attributes' do
6
+ start = described_class.new(time_offset: -12.9, precise: true)
7
+ expect(start.time_offset).to eq(-12.9)
8
+ expect(start.precise).to be true
9
+ end
10
+ end
11
+
12
+ describe '#parse' do
13
+ it 'parses tag with all attributes' do
14
+ start = described_class.new
15
+ tag = '#EXT-X-START:TIME-OFFSET=20.0,PRECISE=YES'
16
+ start.parse(tag)
17
+
18
+ expect(start.time_offset).to eq(20.0)
19
+ expect(start.precise).to be true
20
+ end
21
+
22
+ it 'parses tag without optional attributes' do
23
+ start = described_class.new
24
+ tag = '#EXT-X-START:TIME-OFFSET=-12.9'
25
+ start.parse(tag)
26
+
27
+ expect(start.time_offset).to eq(-12.9)
28
+ expect(start.precise).to be_nil
29
+ end
30
+ end
31
+
32
+ describe '#to_s' do
33
+ it 'returns tag with attributes' do
34
+ start = described_class.new(time_offset: 9.2, precise: true)
35
+ tag = start.to_s
36
+
37
+ expect(tag).to eq('#EXT-X-START:TIME-OFFSET=9.2,PRECISE=YES')
38
+ end
39
+
40
+ it 'returns tag without optional attributes' do
41
+ start = described_class.new(time_offset: 9.2)
42
+ tag = start.to_s
43
+
44
+ expect(tag).to eq('#EXT-X-START:TIME-OFFSET=9.2')
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,33 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe M3u8::Playlist do
4
+ let(:playlist) { described_class.new }
5
+
6
+ describe '#new' do
7
+ it 'initializes with defaults' do
8
+ expect(playlist.version).to be_nil
9
+ expect(playlist.cache).to be_nil
10
+ expect(playlist.target).to be 10
11
+ expect(playlist.sequence).to be 0
12
+ expect(playlist.type).to be_nil
13
+ expect(playlist.iframes_only).to be false
14
+ expect(playlist.independent_segments).to be false
15
+ end
16
+
17
+ it 'initializes from hash' do
18
+ options = { version: 7, cache: false, target: 12, sequence: 1,
19
+ type: 'VOD', independent_segments: true }
20
+ playlist = M3u8::Playlist.new(options)
21
+ expect(playlist.version).to be 7
22
+ expect(playlist.cache).to be false
23
+ expect(playlist.target).to be 12
24
+ expect(playlist.sequence).to be 1
25
+ expect(playlist.type).to eq('VOD')
26
+ expect(playlist.iframes_only).to be false
27
+ expect(playlist.independent_segments).to be true
28
+ end
29
+ end
30
+
4
31
  it 'should generate codecs string' do
5
32
  options = { profile: 'baseline', level: 3.0, audio_codec: 'aac-lc' }
6
33
  codecs = M3u8::Playlist.codecs options
@@ -11,10 +38,11 @@ describe M3u8::Playlist do
11
38
  options = { uri: 'playlist_url', bandwidth: 6400,
12
39
  audio_codec: 'mp3' }
13
40
  item = M3u8::PlaylistItem.new options
14
- playlist = M3u8::Playlist.new
15
- playlist.items.push item
41
+ playlist = M3u8::Playlist.new(independent_segments: true)
42
+ playlist.items << item
16
43
 
17
- output = "#EXTM3U\n" +
44
+ output = "#EXTM3U\n" \
45
+ "#EXT-X-INDEPENDENT-SEGMENTS\n" +
18
46
  %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
19
47
  ",BANDWIDTH=6400\nplaylist_url\n"
20
48
  expect(playlist.to_s).to eq output
@@ -23,7 +51,7 @@ describe M3u8::Playlist do
23
51
  audio_codec: 'mp3' }
24
52
  item = M3u8::PlaylistItem.new options
25
53
  playlist = M3u8::Playlist.new
26
- playlist.items.push item
54
+ playlist.items << item
27
55
 
28
56
  output = "#EXTM3U\n" +
29
57
  %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
@@ -35,7 +63,7 @@ describe M3u8::Playlist do
35
63
  audio_codec: 'aac-lc' }
36
64
  item = M3u8::PlaylistItem.new options
37
65
  playlist = M3u8::Playlist.new
38
- playlist.items.push item
66
+ playlist.items << item
39
67
 
40
68
  output = "#EXTM3U\n" \
41
69
  '#EXT-X-STREAM-INF:PROGRAM-ID=2,RESOLUTION=1920x1080,' +
@@ -48,12 +76,12 @@ describe M3u8::Playlist do
48
76
  options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
49
77
  audio_codec: 'mp3' }
50
78
  item = M3u8::PlaylistItem.new options
51
- playlist.items.push item
79
+ playlist.items << item
52
80
  options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
53
81
  width: 1920, height: 1080, profile: 'high', level: 4.1,
54
82
  audio_codec: 'aac-lc' }
55
83
  item = M3u8::PlaylistItem.new options
56
- playlist.items.push item
84
+ playlist.items << item
57
85
 
58
86
  output = "#EXTM3U\n" +
59
87
  %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
@@ -65,14 +93,12 @@ describe M3u8::Playlist do
65
93
 
66
94
  it 'should render playlist' do
67
95
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
68
- item = M3u8::SegmentItem.new options
96
+ item = M3u8::SegmentItem.new(options)
69
97
  playlist = M3u8::Playlist.new
70
- playlist.items.push item
98
+ playlist.items << item
71
99
 
72
100
  output = "#EXTM3U\n" \
73
- "#EXT-X-VERSION:3\n" \
74
101
  "#EXT-X-MEDIA-SEQUENCE:0\n" \
75
- "#EXT-X-ALLOW-CACHE:YES\n" \
76
102
  "#EXT-X-TARGETDURATION:10\n" \
77
103
  "#EXTINF:11.344644,\n" \
78
104
  "1080-7mbps00000.ts\n" \
@@ -80,13 +106,11 @@ describe M3u8::Playlist do
80
106
  expect(playlist.to_s).to eq output
81
107
 
82
108
  options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
83
- item = M3u8::SegmentItem.new options
84
- playlist.items.push item
109
+ item = M3u8::SegmentItem.new(options)
110
+ playlist.items << item
85
111
 
86
112
  output = "#EXTM3U\n" \
87
- "#EXT-X-VERSION:3\n" \
88
113
  "#EXT-X-MEDIA-SEQUENCE:0\n" \
89
- "#EXT-X-ALLOW-CACHE:YES\n" \
90
114
  "#EXT-X-TARGETDURATION:10\n" \
91
115
  "#EXTINF:11.344644,\n" \
92
116
  "1080-7mbps00000.ts\n" \
@@ -95,16 +119,16 @@ describe M3u8::Playlist do
95
119
  "#EXT-X-ENDLIST\n"
96
120
  expect(playlist.to_s).to eq output
97
121
 
98
- options = { version: 1, cache: false, target: 12, sequence: 1,
122
+ options = { version: 7, cache: false, target: 12, sequence: 1,
99
123
  type: 'VOD' }
100
124
  playlist = M3u8::Playlist.new options
101
125
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
102
126
  item = M3u8::SegmentItem.new options
103
- playlist.items.push item
127
+ playlist.items << item
104
128
 
105
129
  output = "#EXTM3U\n" \
106
130
  "#EXT-X-PLAYLIST-TYPE:VOD\n" \
107
- "#EXT-X-VERSION:1\n" \
131
+ "#EXT-X-VERSION:7\n" \
108
132
  "#EXT-X-MEDIA-SEQUENCE:1\n" \
109
133
  "#EXT-X-ALLOW-CACHE:NO\n" \
110
134
  "#EXT-X-TARGETDURATION:12\n" \
@@ -121,7 +145,7 @@ describe M3u8::Playlist do
121
145
  options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
122
146
  audio_codec: 'mp3' }
123
147
  item = M3u8::PlaylistItem.new options
124
- playlist.items.push item
148
+ playlist.items << item
125
149
  playlist.write test_io
126
150
 
127
151
  output = "#EXTM3U\n" +
@@ -146,7 +170,7 @@ describe M3u8::Playlist do
146
170
  options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
147
171
  audio_codec: 'mp3' }
148
172
  item = M3u8::PlaylistItem.new options
149
- playlist.items.push item
173
+ playlist.items << item
150
174
 
151
175
  expect(playlist.master?).to be true
152
176
  end
@@ -157,11 +181,11 @@ describe M3u8::Playlist do
157
181
  hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
158
182
  bandwidth: 540, uri: 'test.url' }
159
183
  item = M3u8::PlaylistItem.new(hash)
160
- playlist.items.push item
184
+ playlist.items << item
161
185
 
162
186
  hash = { duration: 10.991, segment: 'test.ts' }
163
187
  item = M3u8::SegmentItem.new(hash)
164
- playlist.items.push item
188
+ playlist.items << item
165
189
 
166
190
  message = 'Playlist is invalid.'
167
191
  io = StringIO.new
@@ -176,51 +200,39 @@ describe M3u8::Playlist do
176
200
  hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
177
201
  bandwidth: 540, uri: 'test.url' }
178
202
  item = M3u8::PlaylistItem.new(hash)
179
- playlist.items.push item
203
+ playlist.items << item
180
204
  expect(playlist.valid?).to be true
181
205
 
182
206
  hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
183
207
  bandwidth: 540, uri: 'test.url' }
184
208
  item = M3u8::PlaylistItem.new(hash)
185
- playlist.items.push item
209
+ playlist.items << item
186
210
  expect(playlist.valid?).to be true
187
211
 
188
212
  hash = { duration: 10.991, segment: 'test.ts' }
189
213
  item = M3u8::SegmentItem.new(hash)
190
- playlist.items.push item
214
+ playlist.items << item
191
215
 
192
216
  expect(playlist.valid?).to be false
193
217
  end
194
218
 
195
- it 'should expose options as attributes' do
196
- options = { version: 1, cache: false, target: 12, sequence: 1,
197
- type: 'VOD' }
198
- playlist = M3u8::Playlist.new options
199
- expect(playlist.version).to be 1
200
- expect(playlist.cache).to be false
201
- expect(playlist.target).to be 12
202
- expect(playlist.sequence).to be 1
203
- expect(playlist.type).to eq('VOD')
204
- expect(playlist.iframes_only).to be false
205
- end
206
-
207
219
  it 'should allow reading of playlists' do
208
220
  file = File.open 'spec/fixtures/master.m3u8'
209
221
  playlist = M3u8::Playlist.read file
210
222
  expect(playlist.master?).to be true
211
- expect(playlist.items.size).to eq(7)
223
+ expect(playlist.items.size).to eq(8)
212
224
  end
213
225
 
214
226
  it 'should return the total duration of a playlist' do
215
227
  playlist = M3u8::Playlist.new
216
228
  item = M3u8::SegmentItem.new(duration: 10.991, segment: 'test_01.ts')
217
- playlist.items.push item
229
+ playlist.items << item
218
230
  item = M3u8::SegmentItem.new(duration: 9.891, segment: 'test_02.ts')
219
- playlist.items.push item
231
+ playlist.items << item
220
232
  item = M3u8::SegmentItem.new(duration: 10.556, segment: 'test_03.ts')
221
- playlist.items.push item
233
+ playlist.items << item
222
234
  item = M3u8::SegmentItem.new(duration: 8.790, segment: 'test_04.ts')
223
- playlist.items.push item
235
+ playlist.items << item
224
236
 
225
237
  expect(playlist.duration.round(3)).to eq(40.228)
226
238
  end
@@ -7,12 +7,18 @@ describe M3u8::Reader do
7
7
  playlist = reader.read file
8
8
  expect(playlist.master?).to be true
9
9
 
10
+ expect(playlist.independent_segments).to be true
11
+
10
12
  item = playlist.items[0]
11
13
  expect(item).to be_a(M3u8::SessionKeyItem)
12
14
  expect(item.method).to eq('AES-128')
13
15
  expect(item.uri).to eq('https://priv.example.com/key.php?r=52')
14
16
 
15
17
  item = playlist.items[1]
18
+ expect(item).to be_a(M3u8::PlaybackStart)
19
+ expect(item.time_offset).to eq(20.2)
20
+
21
+ item = playlist.items[2]
16
22
  expect(item).to be_a(M3u8::PlaylistItem)
17
23
  expect(item.uri).to eq('hls/1080-7mbps/1080-7mbps.m3u8')
18
24
  expect(item.program_id).to eq('1')
@@ -24,7 +30,7 @@ describe M3u8::Reader do
24
30
  expect(item.iframe).to be false
25
31
  expect(item.average_bandwidth).to be_nil
26
32
 
27
- item = playlist.items[6]
33
+ item = playlist.items[7]
28
34
  expect(item).to be_a(M3u8::PlaylistItem)
29
35
  expect(item.uri).to eq('hls/64k/64k.m3u8')
30
36
  expect(item.program_id).to eq('1')
@@ -36,7 +42,7 @@ describe M3u8::Reader do
36
42
  expect(item.iframe).to be false
37
43
  expect(item.average_bandwidth).to be_nil
38
44
 
39
- expect(playlist.items.size).to eq(7)
45
+ expect(playlist.items.size).to eq(8)
40
46
 
41
47
  item = playlist.items.last
42
48
  expect(item.resolution).to be_nil
@@ -174,12 +180,12 @@ describe M3u8::Reader do
174
180
  reader = M3u8::Reader.new
175
181
  playlist = reader.read file
176
182
 
177
- expect(playlist.items.size).to eq(7)
183
+ expect(playlist.items.size).to eq(8)
178
184
 
179
185
  file = File.open 'spec/fixtures/master.m3u8'
180
186
  playlist = reader.read file
181
187
 
182
- expect(playlist.items.size).to eq(7)
188
+ expect(playlist.items.size).to eq(8)
183
189
  end
184
190
 
185
191
  it 'should parse playlist with session data' do
@@ -4,11 +4,13 @@ describe M3u8::Writer do
4
4
  it 'should render master playlist' do
5
5
  options = { uri: 'playlist_url', bandwidth: 6400,
6
6
  audio_codec: 'mp3' }
7
- item = M3u8::PlaylistItem.new options
8
- playlist = M3u8::Playlist.new
9
- playlist.items.push item
7
+ item = M3u8::PlaylistItem.new(options)
8
+ playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
9
+ playlist.items << item
10
10
 
11
- output = "#EXTM3U\n" +
11
+ output = "#EXTM3U\n" \
12
+ "#EXT-X-VERSION:6\n" \
13
+ "#EXT-X-INDEPENDENT-SEGMENTS\n" +
12
14
  %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
13
15
  ",BANDWIDTH=6400\nplaylist_url\n"
14
16
 
@@ -19,7 +21,7 @@ describe M3u8::Writer do
19
21
 
20
22
  options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
21
23
  audio_codec: 'mp3' }
22
- item = M3u8::PlaylistItem.new options
24
+ item = M3u8::PlaylistItem.new(options)
23
25
  playlist = M3u8::Playlist.new
24
26
  playlist.items.push item
25
27
 
@@ -80,37 +82,35 @@ describe M3u8::Writer do
80
82
 
81
83
  it 'should render playlist' do
82
84
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
83
- item = M3u8::SegmentItem.new options
84
- playlist = M3u8::Playlist.new
85
- playlist.items.push item
85
+ item = M3u8::SegmentItem.new(options)
86
+ playlist = M3u8::Playlist.new(version: 7)
87
+ playlist.items << item
86
88
 
87
89
  output = "#EXTM3U\n" \
88
- "#EXT-X-VERSION:3\n" \
90
+ "#EXT-X-VERSION:7\n" \
89
91
  "#EXT-X-MEDIA-SEQUENCE:0\n" \
90
- "#EXT-X-ALLOW-CACHE:YES\n" \
91
92
  "#EXT-X-TARGETDURATION:10\n" \
92
93
  "#EXTINF:11.344644,\n" \
93
94
  "1080-7mbps00000.ts\n" \
94
95
  "#EXT-X-ENDLIST\n"
95
96
  io = StringIO.open
96
- writer = M3u8::Writer.new io
97
+ writer = M3u8::Writer.new(io)
97
98
  writer.write playlist
98
99
  expect(io.string).to eq output
99
100
 
100
101
  options = { method: 'AES-128', uri: 'http://test.key',
101
102
  iv: 'D512BBF', key_format: 'identity',
102
103
  key_format_versions: '1/3' }
103
- item = M3u8::KeyItem.new options
104
- playlist.items.push item
104
+ item = M3u8::KeyItem.new(options)
105
+ playlist.items << item
105
106
 
106
107
  options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
107
108
  item = M3u8::SegmentItem.new options
108
- playlist.items.push item
109
+ playlist.items << item
109
110
 
110
111
  output = "#EXTM3U\n" \
111
- "#EXT-X-VERSION:3\n" \
112
+ "#EXT-X-VERSION:7\n" \
112
113
  "#EXT-X-MEDIA-SEQUENCE:0\n" \
113
- "#EXT-X-ALLOW-CACHE:YES\n" \
114
114
  "#EXT-X-TARGETDURATION:10\n" \
115
115
  "#EXTINF:11.344644,\n" \
116
116
  "1080-7mbps00000.ts\n" +
@@ -120,16 +120,16 @@ describe M3u8::Writer do
120
120
  "1080-7mbps00001.ts\n" \
121
121
  "#EXT-X-ENDLIST\n"
122
122
  io = StringIO.open
123
- writer = M3u8::Writer.new io
123
+ writer = M3u8::Writer.new(io)
124
124
  writer.write playlist
125
125
  expect(io.string).to eq output
126
126
 
127
127
  options = { version: 4, cache: false, target: 12, sequence: 1,
128
128
  type: 'EVENT', iframes_only: true }
129
- playlist = M3u8::Playlist.new options
129
+ playlist = M3u8::Playlist.new(options)
130
130
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
131
- item = M3u8::SegmentItem.new options
132
- playlist.items.push item
131
+ item = M3u8::SegmentItem.new(options)
132
+ playlist.items << item
133
133
 
134
134
  output = "#EXTM3U\n" \
135
135
  "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
@@ -13,8 +13,18 @@ describe do
13
13
 
14
14
  it 'should parse yes/no string' do
15
15
  value = 'YES'
16
- expect(test_class.parse_yes_no value).to be true
16
+ expect(test_class.parse_yes_no(value)).to be true
17
17
  value = 'NO'
18
- expect(test_class.parse_yes_no value).to be false
18
+ expect(test_class.parse_yes_no(value)).to be false
19
+ end
20
+
21
+ describe '#to_yes_no' do
22
+ it 'converts true to YES' do
23
+ expect(test_class.to_yes_no(true)).to eq('YES')
24
+ end
25
+
26
+ it 'converts false to NO' do
27
+ expect(test_class.to_yes_no(false)).to eq('NO')
28
+ end
19
29
  end
20
30
  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.6.9
4
+ version: 0.7.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: 2016-04-24 00:00:00.000000000 Z
11
+ date: 2017-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -119,7 +119,6 @@ files:
119
119
  - ".hound.yml"
120
120
  - ".rspec"
121
121
  - ".rubocop.yml"
122
- - ".ruby-version"
123
122
  - ".travis.yml"
124
123
  - CHANGELOG.md
125
124
  - Gemfile
@@ -135,6 +134,7 @@ files:
135
134
  - lib/m3u8/key_item.rb
136
135
  - lib/m3u8/map_item.rb
137
136
  - lib/m3u8/media_item.rb
137
+ - lib/m3u8/playback_start.rb
138
138
  - lib/m3u8/playlist.rb
139
139
  - lib/m3u8/playlist_item.rb
140
140
  - lib/m3u8/reader.rb
@@ -161,6 +161,7 @@ files:
161
161
  - spec/lib/m3u8/key_item_spec.rb
162
162
  - spec/lib/m3u8/map_item_spec.rb
163
163
  - spec/lib/m3u8/media_item_spec.rb
164
+ - spec/lib/m3u8/playback_start_spec.rb
164
165
  - spec/lib/m3u8/playlist_item_spec.rb
165
166
  - spec/lib/m3u8/playlist_spec.rb
166
167
  - spec/lib/m3u8/reader_spec.rb
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
192
  version: '0'
192
193
  requirements: []
193
194
  rubyforge_project:
194
- rubygems_version: 2.4.5.1
195
+ rubygems_version: 2.6.8
195
196
  signing_key:
196
197
  specification_version: 4
197
198
  summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).
@@ -212,6 +213,7 @@ test_files:
212
213
  - spec/lib/m3u8/key_item_spec.rb
213
214
  - spec/lib/m3u8/map_item_spec.rb
214
215
  - spec/lib/m3u8/media_item_spec.rb
216
+ - spec/lib/m3u8/playback_start_spec.rb
215
217
  - spec/lib/m3u8/playlist_item_spec.rb
216
218
  - spec/lib/m3u8/playlist_spec.rb
217
219
  - spec/lib/m3u8/reader_spec.rb
@@ -1 +0,0 @@
1
- 2.2.3