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