lib/m3u8/playlist.rb in m3u8-0.1.3 vs lib/m3u8/playlist.rb in m3u8-0.2.0
- old
+ new
@@ -1,27 +1,32 @@
module M3u8
class Playlist
- attr_accessor :io, :options, :header, :empty, :master
+ attr_accessor :items, :version, :cache, :target, :sequence
MISSING_CODEC_MESSAGE = 'An audio or video codec should be provided.'
NON_MASTER_ERROR_MESSAGE = 'Playlist is not a master playlist, playlist' \
' can not be added.'
MASTER_ERROR_MESSAGE = 'Playlist is a master playlist, segment can not ' \
'be added.'
+ MIXED_TYPE_ERROR_MESSAGE = 'Playlist contains mixed types of items'
def initialize(options = {})
- self.options = {
+ assign_options options
+ self.items = []
+ end
+
+ def assign_options(options)
+ options = {
version: 3,
sequence: 0,
cache: true,
target: 10
}.merge options
- self.header = false
- self.empty = true
- self.master = nil
- self.io = StringIO.open
- io.puts '#EXTM3U'
+ self.version = options[:version]
+ self.sequence = options[:sequence]
+ self.cache = options[:cache]
+ self.target = options[:target]
end
def self.codecs(options = {})
playlist = Playlist.new
playlist.codecs options
@@ -35,34 +40,28 @@
level: nil,
audio: nil
}.merge options
validate_playlist_type true
- self.master = true
- self.empty = false
- resolution = resolution options[:width], options[:height]
codecs = codecs(audio: options[:audio], profile: options[:profile],
level: options[:level])
fail MissingCodecError, MISSING_CODEC_MESSAGE if codecs.nil?
- io.puts "#EXT-X-STREAM-INF:PROGRAM-ID=#{program_id},#{resolution}" +
- %Q{CODECS="#{codecs}",BANDWIDTH=#{bitrate}}
- io.puts playlist
+
+ params = { program_id: program_id, playlist: playlist, bitrate: bitrate,
+ width: options[:width], height: options[:height],
+ codecs: codecs }
+ item = PlaylistItem.new params
+ items.push item
end
def add_segment(duration, segment)
validate_playlist_type false
- self.master = false
- self.empty = false
- unless header
- write_header
- self.header = true
- end
-
- io.puts "#EXTINF:#{duration},"
- io.puts segment
+ params = { duration: duration, segment: segment }
+ item = SegmentItem.new params
+ items.push item
end
def codecs(options = {})
options = {
audio: nil,
@@ -83,46 +82,72 @@
end
end
end
def write(output)
- output.puts to_s
+ validate
+
+ output.puts '#EXTM3U'
+ write_header(output) unless master?
+
+ items.each do |item|
+ output.puts item.to_s
+ end
+
+ return if master?
+ output.puts '#EXT-X-ENDLIST'
end
def master?
- return false if empty
- master
+ return false if playlist_size == 0 && segment_size == 0
+ playlist_size > 0
end
def to_s
- if master?
- io.string
- else
- "#{io.string}#EXT-X-ENDLIST"
- end
+ output = StringIO.open
+ write output
+ output.string
end
+ def valid?
+ return false if playlist_size > 0 && segment_size > 0
+ true
+ end
+
private
+ def validate
+ return if valid?
+ fail PlaylistTypeError, MIXED_TYPE_ERROR_MESSAGE
+ end
+
def validate_playlist_type(master)
- return if empty
+ return if items.size == 0
if master && !master?
fail PlaylistTypeError, NON_MASTER_ERROR_MESSAGE
elsif !master && master?
fail PlaylistTypeError, MASTER_ERROR_MESSAGE
end
end
- def write_header
- io.puts "#EXT-X-VERSION:#{options[:version]}"
- io.puts "#EXT-X-MEDIA-SEQUENCE:#{options[:sequence]}"
- io.puts "#EXT-X-ALLOW-CACHE:#{cache_string}"
- io.puts "#EXT-X-TARGETDURATION:#{options[:target]}"
+ def playlist_size
+ items.select { |item| item.is_a?(PlaylistItem) }.size
end
+ def segment_size
+ items.select { |item| item.is_a?(SegmentItem) }.size
+ end
+
+ def write_header(output)
+ output.puts "#EXT-X-VERSION:#{version}"
+ output.puts "#EXT-X-MEDIA-SEQUENCE:#{sequence}"
+ output.puts "#EXT-X-ALLOW-CACHE:#{cache_string}"
+ output.puts "#EXT-X-TARGETDURATION:#{target}"
+ end
+
def cache_string
- options[:cache] ? 'YES' : 'NO'
+ cache ? 'YES' : 'NO'
end
def audio_codec(audio)
return if audio.nil?
return 'mp4a.40.2' if audio.downcase == 'aac-lc'
@@ -140,13 +165,8 @@
return 'avc1.4d001f' if profile == 'main' && level == 3.1
return 'avc1.4d0028' if profile == 'main' && level == 4.0
return 'avc1.64001f' if profile == 'high' && level == 3.1
return 'avc1.640028' if profile == 'high' &&
(level == 4.0 || level == 4.1)
- end
-
- def resolution(width, height)
- return if width.nil?
- "RESOLUTION=#{width}x#{height},"
end
end
end