m3u8 0.3.2 → 0.4.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: 1b0a12ed7f44279220cc4ff7875ac7489d3acdd8
4
- data.tar.gz: 6d402ff9367f418ba336d43b82fd31d3b53a713d
3
+ metadata.gz: 3b76853323b76f301feb3e6c92d787b8b961d22a
4
+ data.tar.gz: 1edfa96734f62f346022af27459486337eeb8e85
5
5
  SHA512:
6
- metadata.gz: 67e830b28ab20def54ceb22c1d37c4900faaf75888fcf6d64e84b5d0446a7f979c418935c20cf6ce0bba661a8bc3f28f1d4fa1dda86682096f08c0c939749a59
7
- data.tar.gz: 120d949eee406b2770d9e216cf8436b1f3a79c64aaa2337fd5037d86bc027772404d18e7688ae4f7073866759bcc76185ebcd77f7ed23670a286d87d80925547
6
+ metadata.gz: 5f7687b04510afe4354969801d1367b2a7ac403895f42d17a8990a2eb79fbad59a1c07e2a1594976d55656a264ef81d1712ac51038eff8cd2f5b7d03451759df
7
+ data.tar.gz: ff17446d9b23074e4db0cf8b6aa735f7f5c76f8e406aaef17172c1ebb83e7ad25a94f9cf9e6ee8bb222f068150bcd89eaa3c1ae826d2c902e11ac756ed42795e
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/spec_helper.rb'
1
4
  Style/StringLiterals:
2
- EnforcedStyle: single_quotes
3
- SupportedStyles:
4
- - single_quotes
5
- - double_quotes
5
+ EnforcedStyle: single_quotes
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
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.
1
+ ### 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. SegmentItems now support comments (Thanks @elsurudo). A bug was also fixed in Reader that prevented reuse of the instance.
2
+
3
+ ### 0.3.2 (1/16/2015) - PROGRAM-ID was removed in protocol version 6, if not provided it will now be omitted. Thanks @elsurudo
4
+
5
+ ### 0.3.1 (1/15/2015) - Added duration method to Playlist to get the total length of segments contained within it. Thanks @DaKaZ
6
+
7
+ ### 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.
2
8
 
3
9
  ### 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.
4
10
 
data/README.md CHANGED
@@ -33,7 +33,7 @@ Or install it yourself as:
33
33
  playlist = M3u8::Playlist.new
34
34
  #create a new playlist item with options
35
35
  options = { program_id: 1, width: 1920, height: 1080, width: 1920, height: 1080,
36
- profile: 'high', level: 4.1, audio: 'aac-lc', bitrate: 540,
36
+ profile: 'high', level: 4.1, audio_codec: 'aac-lc', bitrate: 540,
37
37
  playlist: 'test.url' }
38
38
  item = M3u8::PlaylistItem.new options
39
39
  playlist.items.push item
@@ -50,7 +50,7 @@ Or install it yourself as:
50
50
  playlist.items.push item
51
51
 
52
52
  #just get the codec string for custom use
53
- options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
53
+ options = { profile: 'baseline', level: 3.0, audio_codec: 'aac-lc' }
54
54
  codecs = M3u8::Playlist.codecs options
55
55
  # => "avc1.66.30,mp4a.40.2"
56
56
 
@@ -69,7 +69,7 @@ Or install it yourself as:
69
69
 
70
70
  #There is a M3u8::Writer class if you want more control over the write process
71
71
 
72
- #values for :audio (Codec name)
72
+ #values for :audio_codec (Codec name)
73
73
  #aac-lc, he-aac, mp3
74
74
 
75
75
  #values for :profile (H.264 Profile)
@@ -97,7 +97,7 @@ Or install it yourself as:
97
97
 
98
98
  #create a new playlist item with options
99
99
  options = { program_id: 1, width: 1920, height: 1080, width: 1920, height: 1080,
100
- profile: 'high', level: 4.1, audio: 'aac-lc', bitrate: 540,
100
+ profile: 'high', level: 4.1, audio_codec: 'aac-lc', bitrate: 540,
101
101
  playlist: 'test.url' }
102
102
  item = M3u8::PlaylistItem.new options
103
103
  #add it to the top of the playlist
@@ -115,10 +115,14 @@ Or install it yourself as:
115
115
 
116
116
  ## Missing (but planned) Features
117
117
  * Support for encryption keys and DRM.
118
- * Support for more obscure tags.
118
+ * Support I-Frame and Byte-Range.
119
+ * Validation of all attributes to match the spec completely.
120
+ * Support for all additional attributes in the latest version of the spec.
119
121
 
120
122
  ## Contributing
121
123
 
124
+ (Be sure to check to dev branch to make sure I'm not already working on it)
125
+
122
126
  1. Fork it ( https://github.com/sethdeckard/m3u8/fork )
123
127
  2. Create your feature branch (`git checkout -b my-new-feature`)
124
128
  3. Run the specs, make sure they pass and that new features are covered
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new
5
5
 
6
- task :default => :spec
7
- task :test => :spec
6
+ task default: :spec
7
+ task test: :spec
data/lib/m3u8.rb CHANGED
@@ -3,6 +3,7 @@ require 'm3u8/version'
3
3
  require 'm3u8/playlist'
4
4
  require 'm3u8/playlist_item'
5
5
  require 'm3u8/segment_item'
6
+ require 'm3u8/media_item'
6
7
  require 'm3u8/reader'
7
8
  require 'm3u8/writer'
8
9
  require 'm3u8/error'
@@ -0,0 +1,74 @@
1
+ module M3u8
2
+ # MediaItem represents a set of EXT-X-MEDIA attributes
3
+ class MediaItem
4
+ attr_accessor :type, :group, :language, :assoc_language, :name,
5
+ :autoselect, :default, :uri, :forced
6
+
7
+ def initialize(params = {})
8
+ params.each do |key, value|
9
+ instance_variable_set("@#{key}", value)
10
+ end
11
+ end
12
+
13
+ def to_s
14
+ attributes = [type_format,
15
+ group_format,
16
+ language_format,
17
+ assoc_language_format,
18
+ name_format,
19
+ autoselect_format,
20
+ default_format,
21
+ uri_format,
22
+ forced_format].compact.join(',')
23
+ "#EXT-X-MEDIA:#{attributes}"
24
+ end
25
+
26
+ private
27
+
28
+ def type_format
29
+ "TYPE=#{type}"
30
+ end
31
+
32
+ def group_format
33
+ %(GROUP-ID="#{group}")
34
+ end
35
+
36
+ def language_format
37
+ return if language.nil?
38
+ %(LANGUAGE="#{language}")
39
+ end
40
+
41
+ def assoc_language_format
42
+ return if assoc_language.nil?
43
+ %(ASSOC-LANGUAGE="#{assoc_language}")
44
+ end
45
+
46
+ def name_format
47
+ %(NAME="#{name}")
48
+ end
49
+
50
+ def autoselect_format
51
+ return if autoselect.nil?
52
+ "AUTOSELECT=#{to_yes_no autoselect}"
53
+ end
54
+
55
+ def default_format
56
+ return if default.nil?
57
+ "DEFAULT=#{to_yes_no default}"
58
+ end
59
+
60
+ def uri_format
61
+ return if uri.nil?
62
+ %(URI="#{uri}")
63
+ end
64
+
65
+ def forced_format
66
+ return if forced.nil?
67
+ "FORCED=#{to_yes_no forced}"
68
+ end
69
+
70
+ def to_yes_no(boolean)
71
+ boolean == true ? 'YES' : 'NO'
72
+ end
73
+ end
74
+ end
data/lib/m3u8/playlist.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module M3u8
2
+ # Playlist represents an m3u8 playlist, it can be a master playlist or a set
3
+ # of media segments
2
4
  class Playlist
3
- attr_accessor :items, :version, :cache, :target, :sequence
5
+ attr_accessor :items, :version, :cache, :target, :sequence, :type
4
6
 
5
7
  def initialize(options = {})
6
8
  assign_options options
@@ -60,6 +62,7 @@ module M3u8
60
62
  self.sequence = options[:sequence]
61
63
  self.cache = options[:cache]
62
64
  self.target = options[:target]
65
+ self.type = options[:type]
63
66
  end
64
67
 
65
68
  def playlist_size
@@ -1,7 +1,9 @@
1
1
  module M3u8
2
+ # PlaylistItem represents a set of EXT-X-STREAM-INF attributes
2
3
  class PlaylistItem
3
4
  attr_accessor :program_id, :width, :height, :codecs, :bitrate, :playlist,
4
- :audio, :level, :profile
5
+ :audio_codec, :level, :profile, :video, :audio,
6
+ :average_bandwidth, :subtitles, :closed_captions
5
7
  MISSING_CODEC_MESSAGE = 'Audio or video codec info should be provided.'
6
8
 
7
9
  def initialize(params = {})
@@ -18,7 +20,6 @@ module M3u8
18
20
  def codecs
19
21
  return @codecs unless @codecs.nil?
20
22
 
21
- audio_codec = audio_codec audio
22
23
  video_codec = video_codec profile, level
23
24
 
24
25
  if video_codec.nil?
@@ -38,7 +39,12 @@ module M3u8
38
39
  attributes = [program_id_format,
39
40
  resolution_format,
40
41
  codecs_format,
41
- bandwidth_format].compact.join(',')
42
+ bandwidth_format,
43
+ average_bandwidth_format,
44
+ audio_format,
45
+ video_format,
46
+ subtitles_format,
47
+ closed_captions_format].compact.join(',')
42
48
  "#EXT-X-STREAM-INF:#{attributes}\n#{playlist}"
43
49
  end
44
50
 
@@ -59,18 +65,43 @@ module M3u8
59
65
  end
60
66
 
61
67
  def codecs_format
62
- "CODECS=\"#{codecs}\""
68
+ %(CODECS="#{codecs}")
63
69
  end
64
70
 
65
71
  def bandwidth_format
66
72
  "BANDWIDTH=#{bitrate}"
67
73
  end
68
74
 
69
- def audio_codec(audio)
75
+ def average_bandwidth_format
76
+ return if average_bandwidth.nil?
77
+ "AVERAGE-BANDWIDTH=#{average_bandwidth}"
78
+ end
79
+
80
+ def audio_format
70
81
  return if audio.nil?
71
- return 'mp4a.40.2' if audio.downcase == 'aac-lc'
72
- return 'mp4a.40.5' if audio.downcase == 'he-aac'
73
- return 'mp4a.40.34' if audio.downcase == 'mp3'
82
+ %(AUDIO="#{audio}")
83
+ end
84
+
85
+ def video_format
86
+ return if video.nil?
87
+ %(VIDEO="#{video}")
88
+ end
89
+
90
+ def subtitles_format
91
+ return if subtitles.nil?
92
+ %(SUBTITLES="#{subtitles}")
93
+ end
94
+
95
+ def closed_captions_format
96
+ return if closed_captions.nil?
97
+ %(CLOSED-CAPTIONS="#{closed_captions}")
98
+ end
99
+
100
+ def audio_codec
101
+ return if @audio_codec.nil?
102
+ return 'mp4a.40.2' if @audio_codec.downcase == 'aac-lc'
103
+ return 'mp4a.40.5' if @audio_codec.downcase == 'he-aac'
104
+ return 'mp4a.40.34' if @audio_codec.downcase == 'mp3'
74
105
  end
75
106
 
76
107
  def video_codec(profile, level)
data/lib/m3u8/reader.rb CHANGED
@@ -2,22 +2,37 @@ module M3u8
2
2
  class Reader
3
3
  attr_accessor :playlist, :item, :open, :master
4
4
  PLAYLIST_START = '#EXTM3U'
5
+ PLAYLIST_TYPE_START = '#EXT-X-PLAYLIST-TYPE:'
5
6
  VERSION_START = '#EXT-X-VERSION:'
6
7
  SEQUENCE_START = '#EXT-X-MEDIA-SEQUENCE:'
7
8
  CACHE_START = '#EXT-X-ALLOW-CACHE:'
8
9
  TARGET_START = '#EXT-X-TARGETDURATION:'
9
10
  STREAM_START = '#EXT-X-STREAM-INF:'
11
+ MEDIA_START = '#EXT-X-MEDIA:'
10
12
  SEGMENT_START = '#EXTINF:'
13
+ # EXT-X-STREAM-INF:
11
14
  PROGRAM_ID = 'PROGRAM-ID'
12
15
  RESOLUTION = 'RESOLUTION'
13
16
  CODECS = 'CODECS'
14
17
  BANDWIDTH = 'BANDWIDTH'
15
-
16
- def initialize
17
- self.playlist = Playlist.new
18
- end
18
+ AVERAGE_BANDWIDTH = 'AVERAGE-BANDWIDTH'
19
+ VIDEO = 'VIDEO'
20
+ AUDIO = 'AUDIO'
21
+ SUBTITLES = 'SUBTITLES'
22
+ CLOSED_CAPTIONS = 'CLOSED-CAPTIONS'
23
+ # EXT-X-MEDIA:
24
+ TYPE = 'TYPE'
25
+ GROUP_ID = 'GROUP-ID'
26
+ LANGUAGE = 'LANGUAGE'
27
+ ASSOC_LANGUAGE = 'ASSOC-LANGUAGE'
28
+ NAME = 'NAME'
29
+ AUTOSELECT = 'AUTOSELECT'
30
+ DEFAULT = 'DEFAULT'
31
+ URI = 'URI'
32
+ FORCED = 'FORCED'
19
33
 
20
34
  def read(input)
35
+ self.playlist = Playlist.new
21
36
  input.each_line do |line|
22
37
  parse_line line
23
38
  end
@@ -29,7 +44,9 @@ module M3u8
29
44
  def parse_line(line)
30
45
  return if line.start_with? PLAYLIST_START
31
46
 
32
- if line.start_with? VERSION_START
47
+ if line.start_with? PLAYLIST_TYPE_START
48
+ parse_playlist_type line
49
+ elsif line.start_with? VERSION_START
33
50
  parse_version line
34
51
  elsif line.start_with? SEQUENCE_START
35
52
  parse_sequence line
@@ -41,11 +58,17 @@ module M3u8
41
58
  parse_stream line
42
59
  elsif line.start_with? SEGMENT_START
43
60
  parse_segment line
61
+ elsif line.start_with? MEDIA_START
62
+ parse_media line
44
63
  elsif !item.nil? && open
45
64
  parse_value(line)
46
65
  end
47
66
  end
48
67
 
68
+ def parse_playlist_type(line)
69
+ playlist.type = line.gsub(PLAYLIST_TYPE_START, '').delete!("\n")
70
+ end
71
+
49
72
  def parse_version(line)
50
73
  playlist.version = line.gsub(VERSION_START, '').to_i
51
74
  end
@@ -85,6 +108,16 @@ module M3u8
85
108
  item.codecs = value
86
109
  when BANDWIDTH
87
110
  item.bitrate = value.to_i
111
+ when AVERAGE_BANDWIDTH
112
+ item.average_bandwidth = value.to_i
113
+ when AUDIO
114
+ item.audio = value
115
+ when VIDEO
116
+ item.video = value
117
+ when SUBTITLES
118
+ item.subtitles = value
119
+ when CLOSED_CAPTIONS
120
+ item.closed_captions = value
88
121
  end
89
122
  end
90
123
  end
@@ -93,9 +126,42 @@ module M3u8
93
126
  self.master = false
94
127
  self.open = true
95
128
 
129
+ values = line.gsub(SEGMENT_START, '').gsub("\n", ',').split(',')
130
+
96
131
  self.item = M3u8::SegmentItem.new
97
- item.duration = line.gsub(SEGMENT_START, '').gsub("\n", '').gsub(',', '')
98
- .to_f
132
+ item.duration = values[0].to_f
133
+ item.comment = values[1] unless values[1].nil?
134
+ end
135
+
136
+ def parse_media(line)
137
+ self.open = false
138
+ self.item = M3u8::MediaItem.new
139
+ line = line.gsub MEDIA_START, ''
140
+ attributes = line.scan(/([A-z-]+)\s*=\s*("[^"]*"|[^,]*)/)
141
+ attributes.each do |pair|
142
+ value = pair[1].gsub("\n", '').gsub('"', '')
143
+ case pair[0]
144
+ when TYPE
145
+ item.type = value
146
+ when GROUP_ID
147
+ item.group = value
148
+ when LANGUAGE
149
+ item.language = value
150
+ when ASSOC_LANGUAGE
151
+ item.assoc_language = value
152
+ when NAME
153
+ item.name = value
154
+ when AUTOSELECT
155
+ item.autoselect = parse_yes_no value
156
+ when DEFAULT
157
+ item.default = parse_yes_no value
158
+ when URI
159
+ item.uri = value
160
+ when FORCED
161
+ item.forced = parse_yes_no value
162
+ end
163
+ end
164
+ playlist.items.push item
99
165
  end
100
166
 
101
167
  def parse_resolution(resolution)
@@ -1,6 +1,6 @@
1
1
  module M3u8
2
2
  class SegmentItem
3
- attr_accessor :duration, :segment
3
+ attr_accessor :duration, :segment, :comment
4
4
 
5
5
  def initialize(params = {})
6
6
  params.each do |key, value|
@@ -9,7 +9,7 @@ module M3u8
9
9
  end
10
10
 
11
11
  def to_s
12
- "#EXTINF:#{duration},\n#{segment}"
12
+ "#EXTINF:#{duration},#{comment}\n#{segment}"
13
13
  end
14
14
  end
15
15
  end
data/lib/m3u8/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module M3u8
2
- VERSION = '0.3.2'
2
+ VERSION = '0.4.0'
3
3
  end
data/lib/m3u8/writer.rb CHANGED
@@ -28,6 +28,7 @@ module M3u8
28
28
  end
29
29
 
30
30
  def write_header(playlist)
31
+ io.puts "#EXT-X-PLAYLIST-TYPE:#{playlist.type}" unless playlist.type.nil?
31
32
  io.puts "#EXT-X-VERSION:#{playlist.version}"
32
33
  io.puts "#EXT-X-MEDIA-SEQUENCE:#{playlist.sequence}"
33
34
  io.puts "#EXT-X-ALLOW-CACHE:#{cache(playlist)}"
data/m3u8.gemspec CHANGED
@@ -4,21 +4,21 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'm3u8/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "m3u8"
7
+ spec.name = 'm3u8'
8
8
  spec.version = M3u8::VERSION
9
- spec.authors = ["Seth Deckard"]
10
- spec.email = ["seth@deckard.me"]
9
+ spec.authors = ['Seth Deckard']
10
+ spec.email = ['seth@deckard.me']
11
11
  spec.summary = %q{Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).}
12
12
  spec.description = %q{Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).}
13
- spec.homepage = "https://github.com/sethdeckard/m3u8"
14
- spec.license = "MIT"
13
+ spec.homepage = 'https://github.com/sethdeckard/m3u8'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "bundler", ">= 1.6"
22
- spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency "rspec", ">= 2.11"
21
+ spec.add_development_dependency 'bundler', '>= 1.6'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec', '>= 2.11'
24
24
  end
@@ -1,4 +1,5 @@
1
1
  #EXTM3U
2
+ #EXT-X-PLAYLIST-TYPE:VOD
2
3
  #EXT-X-VERSION:4
3
4
  #EXT-X-MEDIA-SEQUENCE:1
4
5
  #EXT-X-ALLOW-CACHE:NO
@@ -0,0 +1,283 @@
1
+ #EXTM3U
2
+ #EXT-X-PLAYLIST-TYPE:VOD
3
+ #EXT-X-VERSION:4
4
+ #EXT-X-MEDIA-SEQUENCE:1
5
+ #EXT-X-ALLOW-CACHE:NO
6
+ #EXT-X-TARGETDURATION:12
7
+ #EXTINF:11.344644,anything
8
+ 1080-7mbps00000.ts
9
+ #EXTINF:11.261233,anything
10
+ 1080-7mbps00001.ts
11
+ #EXTINF:7.507489,anything
12
+ 1080-7mbps00002.ts
13
+ #EXTINF:11.261233,anything
14
+ 1080-7mbps00003.ts
15
+ #EXTINF:11.261233,anything
16
+ 1080-7mbps00004.ts
17
+ #EXTINF:7.507489,anything
18
+ 1080-7mbps00005.ts
19
+ #EXTINF:11.261233,anything
20
+ 1080-7mbps00006.ts
21
+ #EXTINF:11.261233,anything
22
+ 1080-7mbps00007.ts
23
+ #EXTINF:7.507478,anything
24
+ 1080-7mbps00008.ts
25
+ #EXTINF:11.261233,anything
26
+ 1080-7mbps00009.ts
27
+ #EXTINF:11.261233,anything
28
+ 1080-7mbps00010.ts
29
+ #EXTINF:7.507478,anything
30
+ 1080-7mbps00011.ts
31
+ #EXTINF:11.261233,anything
32
+ 1080-7mbps00012.ts
33
+ #EXTINF:11.261233,anything
34
+ 1080-7mbps00013.ts
35
+ #EXTINF:7.507489,anything
36
+ 1080-7mbps00014.ts
37
+ #EXTINF:11.261233,anything
38
+ 1080-7mbps00015.ts
39
+ #EXTINF:11.261233,anything
40
+ 1080-7mbps00016.ts
41
+ #EXTINF:7.507489,anything
42
+ 1080-7mbps00017.ts
43
+ #EXTINF:11.261233,anything
44
+ 1080-7mbps00018.ts
45
+ #EXTINF:11.261233,anything
46
+ 1080-7mbps00019.ts
47
+ #EXTINF:7.507478,anything
48
+ 1080-7mbps00020.ts
49
+ #EXTINF:11.261233,anything
50
+ 1080-7mbps00021.ts
51
+ #EXTINF:11.261233,anything
52
+ 1080-7mbps00022.ts
53
+ #EXTINF:7.507478,anything
54
+ 1080-7mbps00023.ts
55
+ #EXTINF:11.261233,anything
56
+ 1080-7mbps00024.ts
57
+ #EXTINF:11.261233,anything
58
+ 1080-7mbps00025.ts
59
+ #EXTINF:7.507489,anything
60
+ 1080-7mbps00026.ts
61
+ #EXTINF:11.261233,anything
62
+ 1080-7mbps00027.ts
63
+ #EXTINF:11.261233,anything
64
+ 1080-7mbps00028.ts
65
+ #EXTINF:7.507489,anything
66
+ 1080-7mbps00029.ts
67
+ #EXTINF:11.261233,anything
68
+ 1080-7mbps00030.ts
69
+ #EXTINF:11.261233,anything
70
+ 1080-7mbps00031.ts
71
+ #EXTINF:7.507478,anything
72
+ 1080-7mbps00032.ts
73
+ #EXTINF:11.261233,anything
74
+ 1080-7mbps00033.ts
75
+ #EXTINF:11.261233,anything
76
+ 1080-7mbps00034.ts
77
+ #EXTINF:7.507489,anything
78
+ 1080-7mbps00035.ts
79
+ #EXTINF:11.261233,anything
80
+ 1080-7mbps00036.ts
81
+ #EXTINF:11.261222,anything
82
+ 1080-7mbps00037.ts
83
+ #EXTINF:7.507489,anything
84
+ 1080-7mbps00038.ts
85
+ #EXTINF:11.261233,anything
86
+ 1080-7mbps00039.ts
87
+ #EXTINF:11.261233,anything
88
+ 1080-7mbps00040.ts
89
+ #EXTINF:7.507489,anything
90
+ 1080-7mbps00041.ts
91
+ #EXTINF:11.261233,anything
92
+ 1080-7mbps00042.ts
93
+ #EXTINF:11.261233,anything
94
+ 1080-7mbps00043.ts
95
+ #EXTINF:7.507478,anything
96
+ 1080-7mbps00044.ts
97
+ #EXTINF:11.261233,anything
98
+ 1080-7mbps00045.ts
99
+ #EXTINF:11.261233,anything
100
+ 1080-7mbps00046.ts
101
+ #EXTINF:7.507489,anything
102
+ 1080-7mbps00047.ts
103
+ #EXTINF:11.261233,anything
104
+ 1080-7mbps00048.ts
105
+ #EXTINF:11.261222,anything
106
+ 1080-7mbps00049.ts
107
+ #EXTINF:7.507489,anything
108
+ 1080-7mbps00050.ts
109
+ #EXTINF:11.261233,anything
110
+ 1080-7mbps00051.ts
111
+ #EXTINF:11.261233,anything
112
+ 1080-7mbps00052.ts
113
+ #EXTINF:7.507489,anything
114
+ 1080-7mbps00053.ts
115
+ #EXTINF:11.261233,anything
116
+ 1080-7mbps00054.ts
117
+ #EXTINF:11.261233,anything
118
+ 1080-7mbps00055.ts
119
+ #EXTINF:7.507478,anything
120
+ 1080-7mbps00056.ts
121
+ #EXTINF:11.261233,anything
122
+ 1080-7mbps00057.ts
123
+ #EXTINF:11.261233,anything
124
+ 1080-7mbps00058.ts
125
+ #EXTINF:7.507489,anything
126
+ 1080-7mbps00059.ts
127
+ #EXTINF:11.261233,anything
128
+ 1080-7mbps00060.ts
129
+ #EXTINF:11.261222,anything
130
+ 1080-7mbps00061.ts
131
+ #EXTINF:7.507489,anything
132
+ 1080-7mbps00062.ts
133
+ #EXTINF:11.261233,anything
134
+ 1080-7mbps00063.ts
135
+ #EXTINF:11.261233,anything
136
+ 1080-7mbps00064.ts
137
+ #EXTINF:7.507489,anything
138
+ 1080-7mbps00065.ts
139
+ #EXTINF:11.261233,anything
140
+ 1080-7mbps00066.ts
141
+ #EXTINF:11.261233,anything
142
+ 1080-7mbps00067.ts
143
+ #EXTINF:7.507478,anything
144
+ 1080-7mbps00068.ts
145
+ #EXTINF:11.261233,anything
146
+ 1080-7mbps00069.ts
147
+ #EXTINF:11.261233,anything
148
+ 1080-7mbps00070.ts
149
+ #EXTINF:7.507489,anything
150
+ 1080-7mbps00071.ts
151
+ #EXTINF:11.261233,anything
152
+ 1080-7mbps00072.ts
153
+ #EXTINF:11.261233,anything
154
+ 1080-7mbps00073.ts
155
+ #EXTINF:7.507489,anything
156
+ 1080-7mbps00074.ts
157
+ #EXTINF:11.261222,anything
158
+ 1080-7mbps00075.ts
159
+ #EXTINF:11.261233,anything
160
+ 1080-7mbps00076.ts
161
+ #EXTINF:7.507489,anything
162
+ 1080-7mbps00077.ts
163
+ #EXTINF:11.261233,anything
164
+ 1080-7mbps00078.ts
165
+ #EXTINF:11.261233,anything
166
+ 1080-7mbps00079.ts
167
+ #EXTINF:7.507478,anything
168
+ 1080-7mbps00080.ts
169
+ #EXTINF:11.261233,anything
170
+ 1080-7mbps00081.ts
171
+ #EXTINF:11.261233,anything
172
+ 1080-7mbps00082.ts
173
+ #EXTINF:7.507489,anything
174
+ 1080-7mbps00083.ts
175
+ #EXTINF:11.261233,anything
176
+ 1080-7mbps00084.ts
177
+ #EXTINF:11.261233,anything
178
+ 1080-7mbps00085.ts
179
+ #EXTINF:7.507489,anything
180
+ 1080-7mbps00086.ts
181
+ #EXTINF:11.261222,anything
182
+ 1080-7mbps00087.ts
183
+ #EXTINF:11.261233,anything
184
+ 1080-7mbps00088.ts
185
+ #EXTINF:7.507489,anything
186
+ 1080-7mbps00089.ts
187
+ #EXTINF:11.261233,anything
188
+ 1080-7mbps00090.ts
189
+ #EXTINF:11.261233,anything
190
+ 1080-7mbps00091.ts
191
+ #EXTINF:7.507478,anything
192
+ 1080-7mbps00092.ts
193
+ #EXTINF:11.261233,anything
194
+ 1080-7mbps00093.ts
195
+ #EXTINF:11.261233,anything
196
+ 1080-7mbps00094.ts
197
+ #EXTINF:7.507489,anything
198
+ 1080-7mbps00095.ts
199
+ #EXTINF:11.261233,anything
200
+ 1080-7mbps00096.ts
201
+ #EXTINF:11.261233,anything
202
+ 1080-7mbps00097.ts
203
+ #EXTINF:7.507489,anything
204
+ 1080-7mbps00098.ts
205
+ #EXTINF:11.261222,anything
206
+ 1080-7mbps00099.ts
207
+ #EXTINF:11.261233,anything
208
+ 1080-7mbps00100.ts
209
+ #EXTINF:7.507489,anything
210
+ 1080-7mbps00101.ts
211
+ #EXTINF:11.261233,anything
212
+ 1080-7mbps00102.ts
213
+ #EXTINF:11.261233,anything
214
+ 1080-7mbps00103.ts
215
+ #EXTINF:7.507478,anything
216
+ 1080-7mbps00104.ts
217
+ #EXTINF:11.261233,anything
218
+ 1080-7mbps00105.ts
219
+ #EXTINF:11.261233,anything
220
+ 1080-7mbps00106.ts
221
+ #EXTINF:7.507489,anything
222
+ 1080-7mbps00107.ts
223
+ #EXTINF:11.261233,anything
224
+ 1080-7mbps00108.ts
225
+ #EXTINF:11.261233,anything
226
+ 1080-7mbps00109.ts
227
+ #EXTINF:7.507489,anything
228
+ 1080-7mbps00110.ts
229
+ #EXTINF:11.261233,anything
230
+ 1080-7mbps00111.ts
231
+ #EXTINF:11.261233,anything
232
+ 1080-7mbps00112.ts
233
+ #EXTINF:7.507478,anything
234
+ 1080-7mbps00113.ts
235
+ #EXTINF:11.261233,anything
236
+ 1080-7mbps00114.ts
237
+ #EXTINF:11.261233,anything
238
+ 1080-7mbps00115.ts
239
+ #EXTINF:7.507478,anything
240
+ 1080-7mbps00116.ts
241
+ #EXTINF:11.261233,anything
242
+ 1080-7mbps00117.ts
243
+ #EXTINF:7.507478,anything
244
+ 1080-7mbps00118.ts
245
+ #EXTINF:11.261233,anything
246
+ 1080-7mbps00119.ts
247
+ #EXTINF:11.261233,anything
248
+ 1080-7mbps00120.ts
249
+ #EXTINF:7.507489,anything
250
+ 1080-7mbps00121.ts
251
+ #EXTINF:11.261233,anything
252
+ 1080-7mbps00122.ts
253
+ #EXTINF:11.261233,anything
254
+ 1080-7mbps00123.ts
255
+ #EXTINF:7.507489,anything
256
+ 1080-7mbps00124.ts
257
+ #EXTINF:11.261222,anything
258
+ 1080-7mbps00125.ts
259
+ #EXTINF:11.261233,anything
260
+ 1080-7mbps00126.ts
261
+ #EXTINF:7.507489,anything
262
+ 1080-7mbps00127.ts
263
+ #EXTINF:11.261233,anything
264
+ 1080-7mbps00128.ts
265
+ #EXTINF:11.261233,anything
266
+ 1080-7mbps00129.ts
267
+ #EXTINF:7.507478,anything
268
+ 1080-7mbps00130.ts
269
+ #EXTINF:11.261233,anything
270
+ 1080-7mbps00131.ts
271
+ #EXTINF:11.261233,anything
272
+ 1080-7mbps00132.ts
273
+ #EXTINF:7.507489,anything
274
+ 1080-7mbps00133.ts
275
+ #EXTINF:11.261233,anything
276
+ 1080-7mbps00134.ts
277
+ #EXTINF:11.261233,anything
278
+ 1080-7mbps00135.ts
279
+ #EXTINF:7.507489,anything
280
+ 1080-7mbps00136.ts
281
+ #EXTINF:1.793444,anything
282
+ 1080-7mbps00137.ts
283
+ #EXT-X-ENDLIST
@@ -0,0 +1,14 @@
1
+ #EXTM3U
2
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle1",AUTOSELECT=YES,DEFAULT=YES
3
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle2",AUTOSELECT=YES,DEFAULT=NO,URI="Angle2/200kbs/prog_index.m3u8"
4
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle3",AUTOSELECT=YES,DEFAULT=NO,URI="Angle3/200kbs/prog_index.m3u8"
5
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle1",AUTOSELECT=YES,DEFAULT=YES
6
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle2",AUTOSELECT=YES,DEFAULT=NO,URI="Angle2/500kbs/prog_index.m3u8"
7
+ #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle3",AUTOSELECT=YES,DEFAULT=NO,URI="Angle3/500kbs/prog_index.m3u8"
8
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",LANGUAGE="eng",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="eng/prog_index.m3u8"
9
+ #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Subtiles",AUTOSELECT=NO,DEFAULT=NO,URI="titles_file"
10
+ #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="captions",NAME="Closed Captions",AUTOSELECT=NO,DEFAULT=NO,URI="captions_file"
11
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000,CODECS="mp4a.40.2,avc1.4d401e",VIDEO="200kbs",AUDIO="aac",AVERAGE-BANDWIDTH=300001,SUBTITLES="subs",CLOSED-CAPTIONS="captions"
12
+ Angle1/200kbs/prog_index.m3u8
13
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=754857,CODECS="mp4a.40.2,avc1.4d401e",VIDEO="500kbs",AUDIO="aac"
14
+ Angle1/500kbs/prog_index.m3u8
@@ -0,0 +1,15 @@
1
+ #EXTM3U
2
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="eng",ASSOC-LANGUAGE="spoken",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="englo/prog_index.m3u8",FORCED=YES
3
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",NAME="Français",AUTOSELECT=YES,DEFAULT=NO,URI="frelo/prog_index.m3u8"
4
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="sp",NAME="Espanol",AUTOSELECT=YES,DEFAULT=NO,URI="splo/prog_index.m3u8"
5
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="eng",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="eng/prog_index.m3u8"
6
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="fre",NAME="Français",AUTOSELECT=YES,DEFAULT=NO,URI="fre/prog_index.m3u8"
7
+ #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="sp",NAME="Espanol",AUTOSELECT=YES,DEFAULT=NO,URI="sp/prog_index.m3u8"
8
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=195023,CODECS="mp4a.40.5",AUDIO="audio-lo"
9
+ lo/prog_index.m3u8
10
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=260000,CODECS="avc1.42e01e,mp4a.40.2",AUDIO="audio-lo"
11
+ hi/prog_index.m3u8
12
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=591680,CODECS="mp4a.40.2, avc1.64001e",AUDIO="audio-hi"
13
+ lo/prog_index.m3u8
14
+ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=650000,CODECS="avc1.42e01e,mp4a.40.2",AUDIO="audio-hi"
15
+ hi/prog_index.m3u8
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::MediaItem do
4
+ it 'should provide m3u8 format representation' do
5
+ hash = { type: 'AUDIO', group: 'audio-lo', language: 'fre',
6
+ name: 'Francais', autoselect: true, default: false,
7
+ uri: 'frelo/prog_index.m3u8' }
8
+ item = M3u8::MediaItem.new(hash)
9
+ output = item.to_s
10
+ expected = '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",' \
11
+ 'NAME="Francais",AUTOSELECT=YES,DEFAULT=NO,' \
12
+ 'URI="frelo/prog_index.m3u8"'
13
+ expect(output).to eq expected
14
+
15
+ hash = { type: 'AUDIO', group: 'audio-lo', language: 'fre',
16
+ assoc_language: 'spoken', name: 'Francais', autoselect: true,
17
+ default: false, forced: true, uri: 'frelo/prog_index.m3u8' }
18
+ item = M3u8::MediaItem.new(hash)
19
+ output = item.to_s
20
+ expected = '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",' \
21
+ 'ASSOC-LANGUAGE="spoken",NAME="Francais",AUTOSELECT=YES,' \
22
+ 'DEFAULT=NO,URI="frelo/prog_index.m3u8",FORCED=YES'
23
+ expect(output).to eq expected
24
+ end
25
+ end
@@ -29,6 +29,16 @@ describe M3u8::PlaylistItem do
29
29
  expected = '#EXT-X-STREAM-INF:PROGRAM-ID=1,' +
30
30
  %(CODECS="avc",BANDWIDTH=540\ntest.url)
31
31
  expect(output).to eq expected
32
+
33
+ hash = { codecs: 'avc', bitrate: 540, playlist: 'test.url', audio: 'test',
34
+ video: 'test2', average_bandwidth: 550, subtitles: 'subs',
35
+ closed_captions: 'caps' }
36
+ item = M3u8::PlaylistItem.new(hash)
37
+ output = item.to_s
38
+ expected = %(#EXT-X-STREAM-INF:CODECS="avc",BANDWIDTH=540,) +
39
+ %(AVERAGE-BANDWIDTH=550,AUDIO="test",VIDEO="test2",) +
40
+ %(SUBTITLES="subs",CLOSED-CAPTIONS="caps"\ntest.url)
41
+ expect(output).to eq expected
32
42
  end
33
43
 
34
44
  it 'should generate codecs string' do
@@ -38,36 +48,36 @@ describe M3u8::PlaylistItem do
38
48
  item = M3u8::PlaylistItem.new codecs: 'test'
39
49
  expect(item.codecs).to eq 'test'
40
50
 
41
- item = M3u8::PlaylistItem.new audio: 'aac-lc'
51
+ item = M3u8::PlaylistItem.new audio_codec: 'aac-lc'
42
52
  expect(item.codecs).to eq 'mp4a.40.2'
43
53
 
44
- item = M3u8::PlaylistItem.new audio: 'AAC-LC'
54
+ item = M3u8::PlaylistItem.new audio_codec: 'AAC-LC'
45
55
  expect(item.codecs).to eq 'mp4a.40.2'
46
56
 
47
- item = M3u8::PlaylistItem.new audio: 'he-aac'
57
+ item = M3u8::PlaylistItem.new audio_codec: 'he-aac'
48
58
  expect(item.codecs).to eq 'mp4a.40.5'
49
59
 
50
- item = M3u8::PlaylistItem.new audio: 'HE-AAC'
60
+ item = M3u8::PlaylistItem.new audio_codec: 'HE-AAC'
51
61
  expect(item.codecs).to eq 'mp4a.40.5'
52
62
 
53
- item = M3u8::PlaylistItem.new audio: 'he-acc1'
63
+ item = M3u8::PlaylistItem.new audio_codec: 'he-acc1'
54
64
  expect(item.codecs).to be_nil
55
65
 
56
- item = M3u8::PlaylistItem.new audio: 'mp3'
66
+ item = M3u8::PlaylistItem.new audio_codec: 'mp3'
57
67
  expect(item.codecs).to eq 'mp4a.40.34'
58
68
 
59
- item = M3u8::PlaylistItem.new audio: 'MP3'
69
+ item = M3u8::PlaylistItem.new audio_codec: 'MP3'
60
70
  expect(item.codecs).to eq 'mp4a.40.34'
61
71
 
62
72
  options = { profile: 'baseline', level: 3.0 }
63
73
  item = M3u8::PlaylistItem.new options
64
74
  expect(item.codecs).to eq 'avc1.66.30'
65
75
 
66
- options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
76
+ options = { profile: 'baseline', level: 3.0, audio_codec: 'aac-lc' }
67
77
  item = M3u8::PlaylistItem.new options
68
78
  expect(item.codecs).to eq 'avc1.66.30,mp4a.40.2'
69
79
 
70
- options = { profile: 'baseline', level: 3.0, audio: 'mp3' }
80
+ options = { profile: 'baseline', level: 3.0, audio_codec: 'mp3' }
71
81
  item = M3u8::PlaylistItem.new options
72
82
  expect(item.codecs).to eq 'avc1.66.30,mp4a.40.34'
73
83
 
@@ -75,7 +85,7 @@ describe M3u8::PlaylistItem do
75
85
  item = M3u8::PlaylistItem.new options
76
86
  expect(item.codecs).to eq 'avc1.42001f'
77
87
 
78
- options = { profile: 'baseline', level: 3.1, audio: 'he-aac' }
88
+ options = { profile: 'baseline', level: 3.1, audio_codec: 'he-aac' }
79
89
  item = M3u8::PlaylistItem.new options
80
90
  expect(item.codecs).to eq 'avc1.42001f,mp4a.40.5'
81
91
 
@@ -83,7 +93,7 @@ describe M3u8::PlaylistItem do
83
93
  item = M3u8::PlaylistItem.new options
84
94
  expect(item.codecs).to eq 'avc1.77.30'
85
95
 
86
- options = { profile: 'main', level: 3.0, audio: 'aac-lc' }
96
+ options = { profile: 'main', level: 3.0, audio_codec: 'aac-lc' }
87
97
  item = M3u8::PlaylistItem.new options
88
98
  expect(item.codecs).to eq 'avc1.77.30,mp4a.40.2'
89
99
 
@@ -2,14 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  describe M3u8::Playlist do
4
4
  it 'should generate codecs string' do
5
- options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
5
+ options = { profile: 'baseline', level: 3.0, audio_codec: 'aac-lc' }
6
6
  codecs = M3u8::Playlist.codecs options
7
7
  expect(codecs).to eq 'avc1.66.30,mp4a.40.2'
8
8
  end
9
9
 
10
10
  it 'should render master playlist' do
11
11
  options = { playlist: 'playlist_url', bitrate: 6400,
12
- audio: 'mp3' }
12
+ audio_codec: 'mp3' }
13
13
  item = M3u8::PlaylistItem.new options
14
14
  playlist = M3u8::Playlist.new
15
15
  playlist.items.push item
@@ -20,7 +20,7 @@ describe M3u8::Playlist do
20
20
  expect(playlist.to_s).to eq output
21
21
 
22
22
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
23
- audio: 'mp3' }
23
+ audio_codec: 'mp3' }
24
24
  item = M3u8::PlaylistItem.new options
25
25
  playlist = M3u8::Playlist.new
26
26
  playlist.items.push item
@@ -32,7 +32,7 @@ describe M3u8::Playlist do
32
32
 
33
33
  options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
34
34
  width: 1920, height: 1080, profile: 'high', level: 4.1,
35
- audio: 'aac-lc' }
35
+ audio_codec: 'aac-lc' }
36
36
  item = M3u8::PlaylistItem.new options
37
37
  playlist = M3u8::Playlist.new
38
38
  playlist.items.push item
@@ -46,12 +46,12 @@ describe M3u8::Playlist do
46
46
 
47
47
  playlist = M3u8::Playlist.new
48
48
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
49
- audio: 'mp3' }
49
+ audio_codec: 'mp3' }
50
50
  item = M3u8::PlaylistItem.new options
51
51
  playlist.items.push item
52
52
  options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
53
53
  width: 1920, height: 1080, profile: 'high', level: 4.1,
54
- audio: 'aac-lc' }
54
+ audio_codec: 'aac-lc' }
55
55
  item = M3u8::PlaylistItem.new options
56
56
  playlist.items.push item
57
57
 
@@ -95,13 +95,15 @@ describe M3u8::Playlist do
95
95
  "#EXT-X-ENDLIST\n"
96
96
  expect(playlist.to_s).to eq output
97
97
 
98
- options = { version: 1, cache: false, target: 12, sequence: 1 }
98
+ options = { version: 1, cache: false, target: 12, sequence: 1,
99
+ type: 'VOD' }
99
100
  playlist = M3u8::Playlist.new options
100
101
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
101
102
  item = M3u8::SegmentItem.new options
102
103
  playlist.items.push item
103
104
 
104
105
  output = "#EXTM3U\n" \
106
+ "#EXT-X-PLAYLIST-TYPE:VOD\n" \
105
107
  "#EXT-X-VERSION:1\n" \
106
108
  "#EXT-X-MEDIA-SEQUENCE:1\n" \
107
109
  "#EXT-X-ALLOW-CACHE:NO\n" \
@@ -117,7 +119,7 @@ describe M3u8::Playlist do
117
119
  test_io = StringIO.new
118
120
  playlist = M3u8::Playlist.new
119
121
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
120
- audio: 'mp3' }
122
+ audio_codec: 'mp3' }
121
123
  item = M3u8::PlaylistItem.new options
122
124
  playlist.items.push item
123
125
  playlist.write test_io
@@ -142,7 +144,7 @@ describe M3u8::Playlist do
142
144
  playlist = M3u8::Playlist.new
143
145
  expect(playlist.master?).to be false
144
146
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
145
- audio: 'mp3' }
147
+ audio_codec: 'mp3' }
146
148
  item = M3u8::PlaylistItem.new options
147
149
  playlist.items.push item
148
150
 
@@ -191,12 +193,14 @@ describe M3u8::Playlist do
191
193
  end
192
194
 
193
195
  it 'should expose options as attributes' do
194
- options = { version: 1, cache: false, target: 12, sequence: 1 }
196
+ options = { version: 1, cache: false, target: 12, sequence: 1,
197
+ type: 'VOD' }
195
198
  playlist = M3u8::Playlist.new options
196
199
  expect(playlist.version).to be 1
197
200
  expect(playlist.cache).to be false
198
201
  expect(playlist.target).to be 12
199
202
  expect(playlist.sequence).to be 1
203
+ expect(playlist.type).to eq('VOD')
200
204
  end
201
205
 
202
206
  it 'should allow reading of playlists' do
data/spec/reader_spec.rb CHANGED
@@ -20,7 +20,7 @@ describe M3u8::Reader do
20
20
  expect(playlist.items.size).to eq 6
21
21
 
22
22
  item = playlist.items.last
23
- expect(item.resolution).to be nil
23
+ expect(item.resolution).to be_nil
24
24
  end
25
25
 
26
26
  it 'should parse segment playlist' do
@@ -28,15 +28,96 @@ describe M3u8::Reader do
28
28
  reader = M3u8::Reader.new
29
29
  playlist = reader.read file
30
30
  expect(playlist.master?).to be false
31
+ expect(playlist.version).to eq 4
32
+ expect(playlist.sequence).to eq 1
33
+ expect(playlist.cache).to be false
34
+ expect(playlist.target).to eq 12
35
+ expect(playlist.type).to eq 'VOD'
36
+
37
+ item = playlist.items[0]
38
+ expect(item).to be_a(M3u8::SegmentItem)
39
+ expect(item.duration).to eq 11.344644
40
+ expect(item.comment).to be_nil
41
+
42
+ expect(playlist.items.size).to eq 138
43
+ end
44
+
45
+ it 'should parse segment playlist with comments' do
46
+ file = File.open 'spec/fixtures/playlist_with_comments.m3u8'
47
+ reader = M3u8::Reader.new
48
+ playlist = reader.read file
49
+ expect(playlist.master?).to be false
31
50
  expect(playlist.version).to be 4
32
51
  expect(playlist.sequence).to be 1
33
52
  expect(playlist.cache).to be false
34
53
  expect(playlist.target).to be 12
54
+ expect(playlist.type).to eq 'VOD'
35
55
 
36
56
  item = playlist.items[0]
37
57
  expect(item).to be_a(M3u8::SegmentItem)
38
58
  expect(item.duration).to eq 11.344644
59
+ expect(item.comment).to eq 'anything'
39
60
 
40
61
  expect(playlist.items.size).to eq 138
41
62
  end
63
+
64
+ it 'should parse variant playlist with audio options and groups' do
65
+ file = File.open 'spec/fixtures/variant_audio.m3u8'
66
+ reader = M3u8::Reader.new
67
+ playlist = reader.read file
68
+
69
+ expect(playlist.master?).to be true
70
+ expect(playlist.items.size).to eq 10
71
+
72
+ item = playlist.items[0]
73
+ expect(item).to be_a M3u8::MediaItem
74
+ expect(item.type).to eq 'AUDIO'
75
+ expect(item.group).to eq 'audio-lo'
76
+ expect(item.language).to eq 'eng'
77
+ expect(item.assoc_language).to eq 'spoken'
78
+ expect(item.name).to eq 'English'
79
+ expect(item.autoselect).to be true
80
+ expect(item.default).to be true
81
+ expect(item.uri).to eq 'englo/prog_index.m3u8'
82
+ expect(item.forced).to be true
83
+ end
84
+
85
+ it 'should parse variant playlist with camera angles' do
86
+ file = File.open 'spec/fixtures/variant_angles.m3u8'
87
+ reader = M3u8::Reader.new
88
+ playlist = reader.read file
89
+
90
+ expect(playlist.master?).to be true
91
+ expect(playlist.items.size).to eq 11
92
+
93
+ item = playlist.items[1]
94
+ expect(item).to be_a M3u8::MediaItem
95
+ expect(item.type).to eq 'VIDEO'
96
+ expect(item.group).to eq '200kbs'
97
+ expect(item.language).to be_nil
98
+ expect(item.name).to eq 'Angle2'
99
+ expect(item.autoselect).to be true
100
+ expect(item.default).to be false
101
+ expect(item.uri).to eq 'Angle2/200kbs/prog_index.m3u8'
102
+
103
+ item = playlist.items[9]
104
+ expect(item.average_bandwidth).to eq 300_001
105
+ expect(item.audio).to eq 'aac'
106
+ expect(item.video).to eq '200kbs'
107
+ expect(item.closed_captions).to eq 'captions'
108
+ expect(item.subtitles).to eq 'subs'
109
+ end
110
+
111
+ it 'should process multiple reads as separate playlists' do
112
+ file = File.open 'spec/fixtures/master.m3u8'
113
+ reader = M3u8::Reader.new
114
+ playlist = reader.read file
115
+
116
+ expect(playlist.items.size).to eq 6
117
+
118
+ file = File.open 'spec/fixtures/master.m3u8'
119
+ playlist = reader.read file
120
+
121
+ expect(playlist.items.size).to eq 6
122
+ end
42
123
  end
@@ -6,6 +6,13 @@ describe M3u8::SegmentItem do
6
6
  item = M3u8::SegmentItem.new(hash)
7
7
  expect(item.duration).to eq 10.991
8
8
  expect(item.segment).to eq 'test.ts'
9
+ expect(item.comment).to be_nil
10
+
11
+ hash = { duration: 10.991, segment: 'test.ts', comment: 'anything' }
12
+ item = M3u8::SegmentItem.new(hash)
13
+ expect(item.duration).to eq 10.991
14
+ expect(item.segment).to eq 'test.ts'
15
+ expect(item.comment).to eq 'anything'
9
16
  end
10
17
 
11
18
  it 'should provide m3u8 format representation' do
@@ -14,5 +21,11 @@ describe M3u8::SegmentItem do
14
21
  output = item.to_s
15
22
  expected = "#EXTINF:10.991,\ntest.ts"
16
23
  expect(output).to eq expected
24
+
25
+ hash = { duration: 10.991, segment: 'test.ts', comment: 'anything' }
26
+ item = M3u8::SegmentItem.new(hash)
27
+ output = item.to_s
28
+ expected = "#EXTINF:10.991,anything\ntest.ts"
29
+ expect(output).to eq expected
17
30
  end
18
31
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'm3u8/playlist'
2
2
  require 'm3u8/playlist_item'
3
3
  require 'm3u8/segment_item'
4
+ require 'm3u8/media_item'
4
5
  require 'm3u8/reader'
5
6
  require 'm3u8/writer'
6
7
  require 'm3u8/error'
data/spec/writer_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe M3u8::Writer do
4
4
  it 'should render master playlist' do
5
5
  options = { playlist: 'playlist_url', bitrate: 6400,
6
- audio: 'mp3' }
6
+ audio_codec: 'mp3' }
7
7
  item = M3u8::PlaylistItem.new options
8
8
  playlist = M3u8::Playlist.new
9
9
  playlist.items.push item
@@ -18,7 +18,7 @@ describe M3u8::Writer do
18
18
  expect(io.string).to eq output
19
19
 
20
20
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
21
- audio: 'mp3' }
21
+ audio_codec: 'mp3' }
22
22
  item = M3u8::PlaylistItem.new options
23
23
  playlist = M3u8::Playlist.new
24
24
  playlist.items.push item
@@ -34,7 +34,7 @@ describe M3u8::Writer do
34
34
 
35
35
  options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
36
36
  width: 1920, height: 1080, profile: 'high', level: 4.1,
37
- audio: 'aac-lc' }
37
+ audio_codec: 'aac-lc' }
38
38
  item = M3u8::PlaylistItem.new options
39
39
  playlist = M3u8::Playlist.new
40
40
  playlist.items.push item
@@ -51,12 +51,12 @@ describe M3u8::Writer do
51
51
 
52
52
  playlist = M3u8::Playlist.new
53
53
  options = { program_id: '1', playlist: 'playlist_url', bitrate: 6400,
54
- audio: 'mp3' }
54
+ audio_codec: 'mp3' }
55
55
  item = M3u8::PlaylistItem.new options
56
56
  playlist.items.push item
57
57
  options = { program_id: '2', playlist: 'playlist_url', bitrate: 50_000,
58
58
  width: 1920, height: 1080, profile: 'high', level: 4.1,
59
- audio: 'aac-lc' }
59
+ audio_codec: 'aac-lc' }
60
60
  item = M3u8::PlaylistItem.new options
61
61
  playlist.items.push item
62
62
 
@@ -110,13 +110,15 @@ describe M3u8::Writer do
110
110
  writer.write playlist
111
111
  expect(io.string).to eq output
112
112
 
113
- options = { version: 1, cache: false, target: 12, sequence: 1 }
113
+ options = { version: 1, cache: false, target: 12, sequence: 1,
114
+ type: 'EVENT' }
114
115
  playlist = M3u8::Playlist.new options
115
116
  options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
116
117
  item = M3u8::SegmentItem.new options
117
118
  playlist.items.push item
118
119
 
119
120
  output = "#EXTM3U\n" \
121
+ "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
120
122
  "#EXT-X-VERSION:1\n" \
121
123
  "#EXT-X-MEDIA-SEQUENCE:1\n" \
122
124
  "#EXT-X-ALLOW-CACHE:NO\n" \
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.3.2
4
+ version: 0.4.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: 2015-01-16 00:00:00.000000000 Z
11
+ date: 2015-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,6 +71,7 @@ files:
71
71
  - Rakefile
72
72
  - lib/m3u8.rb
73
73
  - lib/m3u8/error.rb
74
+ - lib/m3u8/media_item.rb
74
75
  - lib/m3u8/playlist.rb
75
76
  - lib/m3u8/playlist_item.rb
76
77
  - lib/m3u8/reader.rb
@@ -80,6 +81,10 @@ files:
80
81
  - m3u8.gemspec
81
82
  - spec/fixtures/master.m3u8
82
83
  - spec/fixtures/playlist.m3u8
84
+ - spec/fixtures/playlist_with_comments.m3u8
85
+ - spec/fixtures/variant_angles.m3u8
86
+ - spec/fixtures/variant_audio.m3u8
87
+ - spec/media_item_spec.rb
83
88
  - spec/playlist_item_spec.rb
84
89
  - spec/playlist_spec.rb
85
90
  - spec/reader_spec.rb
@@ -113,6 +118,10 @@ summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).
113
118
  test_files:
114
119
  - spec/fixtures/master.m3u8
115
120
  - spec/fixtures/playlist.m3u8
121
+ - spec/fixtures/playlist_with_comments.m3u8
122
+ - spec/fixtures/variant_angles.m3u8
123
+ - spec/fixtures/variant_audio.m3u8
124
+ - spec/media_item_spec.rb
116
125
  - spec/playlist_item_spec.rb
117
126
  - spec/playlist_spec.rb
118
127
  - spec/reader_spec.rb