m3u8 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afc4ec1f4627283241fc7e4f5eeb33d3ee69b17b
4
- data.tar.gz: 4e04ba6b95728b5d5aa0c448e27d12331c37e5c5
3
+ metadata.gz: 941d9ae1544d5853fa64790089c269ce9b7cdc9c
4
+ data.tar.gz: fc32d9311ab491b6049b3a9c27ec6757886237d1
5
5
  SHA512:
6
- metadata.gz: d360dd63410659e7e98b8d001886ae76ef925a8118c6850b7876cd6bbd1a88fc7917565cb407846b3e498aab15f510a3e5c921e36b46d76daa52059ff5c26266
7
- data.tar.gz: 02dc01399f31cf0111c20123c06c86316ff1c54bb76238e8cbc884af44d2de5d974dda25a81287b19c44872ed12d088c661c462983762d06d361b357ffa4ca30
6
+ metadata.gz: e5bb40055129102f6b578533fcd0eb2904e9f43b7bfd3d5a9e6e2f14440095d401fbd4cb0fc04c10920ab60195703a5a7d7c1a94cfaafe939d093fbabe5e3521
7
+ data.tar.gz: bb47741dc6419161bb23e46c8c56478e978d2a6c71757b4665e80394ce74c676e078a8925de7601a8fc77e67561b2968fff5a87792d6e390c52d7f89dba4504f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
+ ### 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 disired. Simplified validation and other aspects during this refactoring.
2
+
1
3
  ### 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.
2
4
 
3
5
  ### 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.
data/README.md CHANGED
@@ -66,6 +66,8 @@ Or install it yourself as:
66
66
 
67
67
  #You can also access the playlist as a string
68
68
  playlist.to_s
69
+
70
+ #There is a M3u8::Writer class if you want more control over the write process
69
71
 
70
72
  #values for :audio (Codec name)
71
73
  #aac-lc, he-aac, mp3
@@ -81,8 +83,7 @@ Or install it yourself as:
81
83
  ## Parsing Usage (new in v.2.0)
82
84
 
83
85
  file = File.open 'spec/fixtures/master.m3u8'
84
- reader = M3u8::Reader.new
85
- playlist = reader.read file
86
+ playlist = M3u8::Playlist.read file
86
87
  playlist.master?
87
88
  # => true
88
89
 
@@ -101,6 +102,8 @@ Or install it yourself as:
101
102
  item = M3u8::PlaylistItem.new options
102
103
  #add it to the top of the playlist
103
104
  playlist.items.insert 0, item
105
+
106
+ #There is a M3u8::Reader class if you want more control over parsing
104
107
 
105
108
  ## Features
106
109
  * Distinction between segment and master playlists is handled automatically (no need to use a different class).
data/lib/m3u8.rb CHANGED
@@ -4,6 +4,7 @@ require 'm3u8/playlist'
4
4
  require 'm3u8/playlist_item'
5
5
  require 'm3u8/segment_item'
6
6
  require 'm3u8/reader'
7
+ require 'm3u8/writer'
7
8
  require 'm3u8/error'
8
9
 
9
10
  module M3u8
data/lib/m3u8/playlist.rb CHANGED
@@ -1,11 +1,6 @@
1
1
  module M3u8
2
2
  class Playlist
3
3
  attr_accessor :items, :version, :cache, :target, :sequence
4
- NON_MASTER_ERROR_MESSAGE = 'Playlist is not a master playlist, playlist' \
5
- ' can not be added.'
6
- MASTER_ERROR_MESSAGE = 'Playlist is a master playlist, segment can not ' \
7
- 'be added.'
8
- MIXED_TYPE_ERROR_MESSAGE = 'Playlist contains mixed types of items'
9
4
 
10
5
  def initialize(options = {})
11
6
  assign_options options
@@ -17,37 +12,14 @@ module M3u8
17
12
  item.codecs
18
13
  end
19
14
 
20
- def add_playlist(program_id, playlist, bitrate, options = {})
21
- validate_playlist_type true
22
-
23
- params = { program_id: program_id, playlist: playlist, bitrate: bitrate,
24
- width: options[:width], height: options[:height],
25
- audio: options[:audio], profile: options[:profile],
26
- level: options[:level] }
27
- item = PlaylistItem.new params
28
- items.push item
29
- end
30
-
31
- def add_segment(duration, segment)
32
- validate_playlist_type false
33
-
34
- params = { duration: duration, segment: segment }
35
- item = SegmentItem.new params
36
- items.push item
15
+ def self.read(input)
16
+ reader = Reader.new
17
+ reader.read input
37
18
  end
38
19
 
39
20
  def write(output)
40
- validate
41
-
42
- output.puts '#EXTM3U'
43
- write_header(output) unless master?
44
-
45
- items.each do |item|
46
- output.puts item.to_s
47
- end
48
-
49
- return if master?
50
- output.puts '#EXT-X-ENDLIST'
21
+ writer = Writer.new output
22
+ writer.write self
51
23
  end
52
24
 
53
25
  def master?
@@ -82,20 +54,6 @@ module M3u8
82
54
  self.target = options[:target]
83
55
  end
84
56
 
85
- def validate
86
- return if valid?
87
- fail PlaylistTypeError, MIXED_TYPE_ERROR_MESSAGE
88
- end
89
-
90
- def validate_playlist_type(master)
91
- return if items.size == 0
92
- if master && !master?
93
- fail PlaylistTypeError, NON_MASTER_ERROR_MESSAGE
94
- elsif !master && master?
95
- fail PlaylistTypeError, MASTER_ERROR_MESSAGE
96
- end
97
- end
98
-
99
57
  def playlist_size
100
58
  items.select { |item| item.is_a?(PlaylistItem) }.size
101
59
  end
@@ -103,16 +61,5 @@ module M3u8
103
61
  def segment_size
104
62
  items.select { |item| item.is_a?(SegmentItem) }.size
105
63
  end
106
-
107
- def write_header(output)
108
- output.puts "#EXT-X-VERSION:#{version}"
109
- output.puts "#EXT-X-MEDIA-SEQUENCE:#{sequence}"
110
- output.puts "#EXT-X-ALLOW-CACHE:#{cache_string}"
111
- output.puts "#EXT-X-TARGETDURATION:#{target}"
112
- end
113
-
114
- def cache_string
115
- cache ? 'YES' : 'NO'
116
- end
117
64
  end
118
65
  end
data/lib/m3u8/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module M3u8
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,41 @@
1
+ module M3u8
2
+ class Writer
3
+ attr_accessor :io
4
+
5
+ def initialize(io)
6
+ self.io = io
7
+ end
8
+
9
+ def write(playlist)
10
+ validate playlist
11
+
12
+ io.puts '#EXTM3U'
13
+ write_header(playlist) unless playlist.master?
14
+
15
+ playlist.items.each do |item|
16
+ io.puts item.to_s
17
+ end
18
+
19
+ return if playlist.master?
20
+ io.puts '#EXT-X-ENDLIST'
21
+ end
22
+
23
+ private
24
+
25
+ def validate(playlist)
26
+ return if playlist.valid?
27
+ fail PlaylistTypeError, 'Playlist is invalid.'
28
+ end
29
+
30
+ def write_header(playlist)
31
+ io.puts "#EXT-X-VERSION:#{playlist.version}"
32
+ io.puts "#EXT-X-MEDIA-SEQUENCE:#{playlist.sequence}"
33
+ io.puts "#EXT-X-ALLOW-CACHE:#{cache(playlist)}"
34
+ io.puts "#EXT-X-TARGETDURATION:#{playlist.target}"
35
+ end
36
+
37
+ def cache(playlist)
38
+ playlist.cache ? 'YES' : 'NO'
39
+ end
40
+ end
41
+ end
@@ -8,19 +8,23 @@ describe M3u8::Playlist do
8
8
  end
9
9
 
10
10
  it 'should render master playlist' do
11
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
12
+ audio: 'mp3' }
13
+ item = M3u8::PlaylistItem.new options
11
14
  playlist = M3u8::Playlist.new
12
- playlist.add_playlist '1', 'playlist_url', 6400, audio: 'mp3'
15
+ playlist.items.push item
13
16
 
14
17
  output = "#EXTM3U\n" +
15
18
  %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
16
19
  ",BANDWIDTH=6400\nplaylist_url\n"
17
-
18
20
  expect(playlist.to_s).to eq output
19
21
 
20
- playlist = M3u8::Playlist.new
21
- options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
22
+ options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
23
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
22
24
  audio: 'aac-lc' }
23
- playlist.add_playlist '2', 'playlist_url', 50_000, options
25
+ item = M3u8::PlaylistItem.new options
26
+ playlist = M3u8::Playlist.new
27
+ playlist.items.push item
24
28
 
25
29
  output = "#EXTM3U\n" \
26
30
  '#EXT-X-STREAM-INF:PROGRAM-ID=2,RESOLUTION=1920x1080,' +
@@ -30,23 +34,29 @@ describe M3u8::Playlist do
30
34
  expect(playlist.to_s).to eq output
31
35
 
32
36
  playlist = M3u8::Playlist.new
33
- playlist.add_playlist '1', 'playlist_url', 6400, audio: 'mp3'
34
- options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
37
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
38
+ audio: 'mp3' }
39
+ item = M3u8::PlaylistItem.new options
40
+ playlist.items.push item
41
+ options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
42
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
35
43
  audio: 'aac-lc' }
36
- playlist.add_playlist '2', 'playlist_url', 50_000, options
44
+ item = M3u8::PlaylistItem.new options
45
+ playlist.items.push item
37
46
 
38
47
  output = "#EXTM3U\n" +
39
48
  %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
40
49
  ",BANDWIDTH=6400\nplaylist_url\n#EXT-X-STREAM-INF:PROGRAM-ID=2," +
41
50
  %(RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2") +
42
51
  ",BANDWIDTH=50000\nplaylist_url\n"
43
-
44
52
  expect(playlist.to_s).to eq output
45
53
  end
46
54
 
47
55
  it 'should render playlist' do
56
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
57
+ item = M3u8::SegmentItem.new options
48
58
  playlist = M3u8::Playlist.new
49
- playlist.add_segment 11.344644, '1080-7mbps00000.ts'
59
+ playlist.items.push item
50
60
 
51
61
  output = "#EXTM3U\n" \
52
62
  "#EXT-X-VERSION:3\n" \
@@ -56,10 +66,11 @@ describe M3u8::Playlist do
56
66
  "#EXTINF:11.344644,\n" \
57
67
  "1080-7mbps00000.ts\n" \
58
68
  "#EXT-X-ENDLIST\n"
59
-
60
69
  expect(playlist.to_s).to eq output
61
70
 
62
- playlist.add_segment 11.261233, '1080-7mbps00001.ts'
71
+ options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
72
+ item = M3u8::SegmentItem.new options
73
+ playlist.items.push item
63
74
 
64
75
  output = "#EXTM3U\n" \
65
76
  "#EXT-X-VERSION:3\n" \
@@ -71,12 +82,13 @@ describe M3u8::Playlist do
71
82
  "#EXTINF:11.261233,\n" \
72
83
  "1080-7mbps00001.ts\n" \
73
84
  "#EXT-X-ENDLIST\n"
74
-
75
85
  expect(playlist.to_s).to eq output
76
86
 
77
87
  options = { version: 1, cache: false, target: 12, sequence: 1 }
78
88
  playlist = M3u8::Playlist.new options
79
- playlist.add_segment 11.344644, '1080-7mbps00000.ts'
89
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
90
+ item = M3u8::SegmentItem.new options
91
+ playlist.items.push item
80
92
 
81
93
  output = "#EXTM3U\n" \
82
94
  "#EXT-X-VERSION:1\n" \
@@ -93,7 +105,10 @@ describe M3u8::Playlist do
93
105
  it 'should write playlist to io' do
94
106
  test_io = StringIO.new
95
107
  playlist = M3u8::Playlist.new
96
- playlist.add_playlist '1', 'playlist_url', 6400, audio: 'mp3'
108
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
109
+ audio: 'mp3' }
110
+ item = M3u8::PlaylistItem.new options
111
+ playlist.items.push item
97
112
  playlist.write test_io
98
113
 
99
114
  output = "#EXTM3U\n" +
@@ -115,26 +130,14 @@ describe M3u8::Playlist do
115
130
  it 'should report if it is a master playlist' do
116
131
  playlist = M3u8::Playlist.new
117
132
  expect(playlist.master?).to be false
133
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
134
+ audio: 'mp3' }
135
+ item = M3u8::PlaylistItem.new options
136
+ playlist.items.push item
118
137
 
119
- playlist.add_playlist '1', 'playlist_url', 6400, audio: 'mp3'
120
138
  expect(playlist.master?).to be true
121
139
  end
122
140
 
123
- it 'should raise error if type of playlist is changed' do
124
- playlist = M3u8::Playlist.new
125
- playlist.add_playlist '1', 'playlist_url', 6400, audio: 'mp3'
126
-
127
- message = 'Playlist is a master playlist, segment can not be added.'
128
- expect { playlist.add_segment 11.344644, '1080-7mbps00000.ts' }
129
- .to raise_error(M3u8::PlaylistTypeError, message)
130
-
131
- playlist = M3u8::Playlist.new
132
- playlist.add_segment 11.344644, '1080-7mbps00000.ts'
133
- message = 'Playlist is not a master playlist, playlist can not be added.'
134
- expect { playlist.add_playlist '1', 'playlist_url', 6400 }
135
- .to raise_error(M3u8::PlaylistTypeError, message)
136
- end
137
-
138
141
  it 'should raise error on write if item types are mixed' do
139
142
  playlist = M3u8::Playlist.new
140
143
 
@@ -147,7 +150,7 @@ describe M3u8::Playlist do
147
150
  item = M3u8::SegmentItem.new(hash)
148
151
  playlist.items.push item
149
152
 
150
- message = 'Playlist contains mixed types of items'
153
+ message = 'Playlist is invalid.'
151
154
  io = StringIO.new
152
155
  expect { playlist.write io }
153
156
  .to raise_error(M3u8::PlaylistTypeError, message)
@@ -184,4 +187,11 @@ describe M3u8::Playlist do
184
187
  expect(playlist.target).to be 12
185
188
  expect(playlist.sequence).to be 1
186
189
  end
190
+
191
+ it 'should allow reading of playlists' do
192
+ file = File.open 'spec/fixtures/master.m3u8'
193
+ playlist = M3u8::Playlist.read file
194
+ expect(playlist.master?).to be true
195
+ expect(playlist.items.size).to eq 6
196
+ end
187
197
  end
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'm3u8/playlist'
2
2
  require 'm3u8/playlist_item'
3
3
  require 'm3u8/segment_item'
4
4
  require 'm3u8/reader'
5
+ require 'm3u8/writer'
5
6
  require 'm3u8/error'
6
7
 
7
8
  require 'coveralls'
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::Writer do
4
+ it 'should render master playlist' do
5
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
6
+ audio: 'mp3' }
7
+ item = M3u8::PlaylistItem.new options
8
+ playlist = M3u8::Playlist.new
9
+ playlist.items.push item
10
+
11
+ output = "#EXTM3U\n" +
12
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
13
+ ",BANDWIDTH=6400\nplaylist_url\n"
14
+
15
+ io = StringIO.open
16
+ writer = M3u8::Writer.new io
17
+ writer.write playlist
18
+ expect(io.string).to eq output
19
+
20
+ options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
21
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
22
+ audio: 'aac-lc' }
23
+ item = M3u8::PlaylistItem.new options
24
+ playlist = M3u8::Playlist.new
25
+ playlist.items.push item
26
+
27
+ output = "#EXTM3U\n" \
28
+ '#EXT-X-STREAM-INF:PROGRAM-ID=2,RESOLUTION=1920x1080,' +
29
+ %(CODECS="avc1.640028,mp4a.40.2",BANDWIDTH=50000\n) +
30
+ "playlist_url\n"
31
+
32
+ io = StringIO.open
33
+ writer = M3u8::Writer.new io
34
+ writer.write playlist
35
+ expect(io.string).to eq output
36
+
37
+ playlist = M3u8::Playlist.new
38
+ options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
39
+ audio: 'mp3' }
40
+ item = M3u8::PlaylistItem.new options
41
+ playlist.items.push item
42
+ options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
43
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
44
+ audio: 'aac-lc' }
45
+ item = M3u8::PlaylistItem.new options
46
+ playlist.items.push item
47
+
48
+ output = "#EXTM3U\n" +
49
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
50
+ ",BANDWIDTH=6400\nplaylist_url\n#EXT-X-STREAM-INF:PROGRAM-ID=2," +
51
+ %(RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2") +
52
+ ",BANDWIDTH=50000\nplaylist_url\n"
53
+
54
+ io = StringIO.open
55
+ writer = M3u8::Writer.new io
56
+ writer.write playlist
57
+ expect(io.string).to eq output
58
+ end
59
+
60
+ it 'should render playlist' do
61
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
62
+ item = M3u8::SegmentItem.new options
63
+ playlist = M3u8::Playlist.new
64
+ playlist.items.push item
65
+
66
+ output = "#EXTM3U\n" \
67
+ "#EXT-X-VERSION:3\n" \
68
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
69
+ "#EXT-X-ALLOW-CACHE:YES\n" \
70
+ "#EXT-X-TARGETDURATION:10\n" \
71
+ "#EXTINF:11.344644,\n" \
72
+ "1080-7mbps00000.ts\n" \
73
+ "#EXT-X-ENDLIST\n"
74
+ io = StringIO.open
75
+ writer = M3u8::Writer.new io
76
+ writer.write playlist
77
+ expect(io.string).to eq output
78
+
79
+ options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
80
+ item = M3u8::SegmentItem.new options
81
+ playlist.items.push item
82
+
83
+ output = "#EXTM3U\n" \
84
+ "#EXT-X-VERSION:3\n" \
85
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
86
+ "#EXT-X-ALLOW-CACHE:YES\n" \
87
+ "#EXT-X-TARGETDURATION:10\n" \
88
+ "#EXTINF:11.344644,\n" \
89
+ "1080-7mbps00000.ts\n" \
90
+ "#EXTINF:11.261233,\n" \
91
+ "1080-7mbps00001.ts\n" \
92
+ "#EXT-X-ENDLIST\n"
93
+ io = StringIO.open
94
+ writer = M3u8::Writer.new io
95
+ writer.write playlist
96
+ expect(io.string).to eq output
97
+
98
+ options = { version: 1, cache: false, target: 12, sequence: 1 }
99
+ playlist = M3u8::Playlist.new options
100
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
101
+ item = M3u8::SegmentItem.new options
102
+ playlist.items.push item
103
+
104
+ output = "#EXTM3U\n" \
105
+ "#EXT-X-VERSION:1\n" \
106
+ "#EXT-X-MEDIA-SEQUENCE:1\n" \
107
+ "#EXT-X-ALLOW-CACHE:NO\n" \
108
+ "#EXT-X-TARGETDURATION:12\n" \
109
+ "#EXTINF:11.344644,\n" \
110
+ "1080-7mbps00000.ts\n" \
111
+ "#EXT-X-ENDLIST\n"
112
+ io = StringIO.open
113
+ writer = M3u8::Writer.new io
114
+ writer.write playlist
115
+ expect(io.string).to eq output
116
+ end
117
+
118
+ it 'should raise error on write if item types are mixed' do
119
+ playlist = M3u8::Playlist.new
120
+
121
+ hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
122
+ bitrate: 540, playlist: 'test.url' }
123
+ item = M3u8::PlaylistItem.new(hash)
124
+ playlist.items.push item
125
+
126
+ hash = { duration: 10.991, segment: 'test.ts' }
127
+ item = M3u8::SegmentItem.new(hash)
128
+ playlist.items.push item
129
+
130
+ message = 'Playlist is invalid.'
131
+ io = StringIO.new
132
+ writer = M3u8::Writer.new io
133
+ expect { writer.write playlist }
134
+ .to raise_error(M3u8::PlaylistTypeError, message)
135
+ end
136
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: m3u8
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Deckard
@@ -74,6 +74,7 @@ files:
74
74
  - lib/m3u8/reader.rb
75
75
  - lib/m3u8/segment_item.rb
76
76
  - lib/m3u8/version.rb
77
+ - lib/m3u8/writer.rb
77
78
  - m3u8.gemspec
78
79
  - spec/fixtures/master.m3u8
79
80
  - spec/fixtures/playlist.m3u8
@@ -82,6 +83,7 @@ files:
82
83
  - spec/reader_spec.rb
83
84
  - spec/segment_item_spec.rb
84
85
  - spec/spec_helper.rb
86
+ - spec/writer_spec.rb
85
87
  homepage: https://github.com/sethdeckard/m3u8
86
88
  licenses:
87
89
  - MIT
@@ -114,3 +116,4 @@ test_files:
114
116
  - spec/reader_spec.rb
115
117
  - spec/segment_item_spec.rb
116
118
  - spec/spec_helper.rb
119
+ - spec/writer_spec.rb