class M3u8::Reader

Reader provides parsing of m3u8 playlists

Attributes

item[RW]
master[RW]
open[RW]
playlist[RW]
tags[RW]

Public Class Methods

new(*) click to toggle source
# File lib/m3u8/reader.rb, line 8
def initialize(*)
  @tags = [basic_tags,
           media_segment_tags,
           media_playlist_tags,
           master_playlist_tags,
           universal_tags].inject(:merge)
end

Public Instance Methods

read(input) click to toggle source
# File lib/m3u8/reader.rb, line 16
def read(input)
  @playlist = Playlist.new
  input.each_line.with_index do |line, index|
    validate_file_format(line) if index.zero?
    parse_line(line)
  end
  playlist
end

Private Instance Methods

basic_tags() click to toggle source
# File lib/m3u8/reader.rb, line 27
def basic_tags
  { '#EXT-X-VERSION' => ->(line) { parse_version(line) } }
end
master_playlist_tags() click to toggle source
# File lib/m3u8/reader.rb, line 56
def master_playlist_tags
  {
    '#EXT-X-MEDIA' => ->(line) { parse_media(line) },
    '#EXT-X-SESSION-DATA' => ->(line) { parse_session_data(line) },
    '#EXT-X-SESSION-KEY' => ->(line) { parse_session_key(line) },
    '#EXT-X-STREAM-INF' => ->(line) { parse_stream(line) },
    '#EXT-X-I-FRAME-STREAM-INF' => ->(line) { parse_iframe_stream(line) }
  }
end
match_tag(line) click to toggle source
# File lib/m3u8/reader.rb, line 80
def match_tag(line)
  tag = @tags.select do |key|
    line.start_with?(key) && !line.start_with?("#{key}-")
  end

  return unless tag.values.first
  tag.values.first.call(line)
  true
end
media_playlist_tags() click to toggle source
# File lib/m3u8/reader.rb, line 43
def media_playlist_tags
  {
    '#EXT-X-MEDIA-SEQUENCE' => ->(line) { parse_sequence(line) },
    '#EXT-X-DISCONTINUITY-SEQUENCE' => lambda do |line|
      parse_discontinuity_sequence(line)
    end,
    '#EXT-X-ALLOW-CACHE' => ->(line) { parse_cache(line) },
    '#EXT-X-TARGETDURATION' => ->(line) { parse_target(line) },
    '#EXT-X-I-FRAMES-ONLY' => proc { playlist.iframes_only = true },
    '#EXT-X-PLAYLIST-TYPE' => ->(line) { parse_playlist_type(line) }
  }
end
media_segment_tags() click to toggle source
# File lib/m3u8/reader.rb, line 31
def media_segment_tags
  {
    '#EXTINF' => ->(line) { parse_segment(line) },
    '#EXT-X-DISCONTINUITY' => ->(line) { parse_discontinuity(line) },
    '#EXT-X-BYTERANGE' => ->(line) { parse_byterange(line) },
    '#EXT-X-KEY' => ->(line) { parse_key(line) },
    '#EXT-X-MAP' => ->(line) { parse_map(line) },
    '#EXT-X-PROGRAM-DATE-TIME' => ->(line) { parse_time(line) },
    '#EXT-X-DATERANGE' => ->(line) { parse_date_range(line) }
  }
end
parse_byterange(line) click to toggle source
# File lib/m3u8/reader.rb, line 160
def parse_byterange(line)
  values = line.gsub('#EXT-X-BYTERANGE:', '').delete("\n")
  item.byterange = M3u8::ByteRange.parse values
end
parse_cache(line) click to toggle source
# File lib/m3u8/reader.rb, line 102
def parse_cache(line)
  line = line.gsub('#EXT-X-ALLOW-CACHE:', '')
  playlist.cache = parse_yes_no(line)
end
parse_date_range(line) click to toggle source
# File lib/m3u8/reader.rb, line 196
def parse_date_range(line)
  item = M3u8::DateRangeItem.new
  item.parse(line)
  playlist.items << item
end
parse_discontinuity(*) click to toggle source
# File lib/m3u8/reader.rb, line 127
def parse_discontinuity(*)
  self.master = false
  self.open = false

  self.item = M3u8::DiscontinuityItem.new
  playlist.items << item
end
parse_discontinuity_sequence(line) click to toggle source
# File lib/m3u8/reader.rb, line 135
def parse_discontinuity_sequence(line)
  value = line.gsub('#EXT-X-DISCONTINUITY-SEQUENCE:', '').strip
  playlist.discontinuity_sequence = Integer(value)
end
parse_iframe_stream(line) click to toggle source
# File lib/m3u8/reader.rb, line 118
def parse_iframe_stream(line)
  self.master = true
  self.open = false

  self.item = M3u8::PlaylistItem.parse(line)
  item.iframe = true
  playlist.items << item
end
parse_key(line) click to toggle source
# File lib/m3u8/reader.rb, line 140
def parse_key(line)
  item = M3u8::KeyItem.parse(line)
  playlist.items << item
end
parse_line(line) click to toggle source
# File lib/m3u8/reader.rb, line 75
def parse_line(line)
  return if match_tag(line)
  parse_next_line(line) if !item.nil? && open
end
parse_map(line) click to toggle source
# File lib/m3u8/reader.rb, line 145
def parse_map(line)
  item = M3u8::MapItem.parse(line)
  playlist.items << item
end
parse_media(line) click to toggle source
# File lib/m3u8/reader.rb, line 175
def parse_media(line)
  self.open = false
  self.item = M3u8::MediaItem.parse(line)
  playlist.items << item
end
parse_next_line(line) click to toggle source
# File lib/m3u8/reader.rb, line 202
def parse_next_line(line)
  value = line.delete("\n").delete("\r")
  if master
    item.uri = value
  else
    item.segment = value
  end
  playlist.items << item
  self.open = false
end
parse_playlist_type(line) click to toggle source
# File lib/m3u8/reader.rb, line 90
def parse_playlist_type(line)
  playlist.type = line.gsub('#EXT-X-PLAYLIST-TYPE:', '').delete!("\n")
end
parse_segment(line) click to toggle source
# File lib/m3u8/reader.rb, line 150
def parse_segment(line)
  self.item = M3u8::SegmentItem.new
  values = line.gsub('#EXTINF:', '').tr("\n", ',').split(',')
  item.duration = values[0].to_f
  item.comment = values[1] unless values[1].nil?

  self.master = false
  self.open = true
end
parse_sequence(line) click to toggle source
# File lib/m3u8/reader.rb, line 98
def parse_sequence(line)
  playlist.sequence = line.gsub('#EXT-X-MEDIA-SEQUENCE:', '').to_i
end
parse_session_data(line) click to toggle source
# File lib/m3u8/reader.rb, line 165
def parse_session_data(line)
  item = M3u8::SessionDataItem.parse(line)
  playlist.items << item
end
parse_session_key(line) click to toggle source
# File lib/m3u8/reader.rb, line 170
def parse_session_key(line)
  item = M3u8::SessionKeyItem.parse(line)
  playlist.items << item
end
parse_start(line) click to toggle source
# File lib/m3u8/reader.rb, line 181
def parse_start(line)
  item = M3u8::PlaybackStart.new
  item.parse(line)
  playlist.items << item
end
parse_stream(line) click to toggle source
# File lib/m3u8/reader.rb, line 111
def parse_stream(line)
  self.master = true
  self.open = true

  self.item = M3u8::PlaylistItem.parse(line)
end
parse_target(line) click to toggle source
# File lib/m3u8/reader.rb, line 107
def parse_target(line)
  playlist.target = line.gsub('#EXT-X-TARGETDURATION:', '').to_i
end
parse_time(line) click to toggle source
# File lib/m3u8/reader.rb, line 187
def parse_time(line)
  if open
    item.program_date_time = M3u8::TimeItem.parse(line)
  else
    self.open = false
    playlist.items << M3u8::TimeItem.parse(line)
  end
end
parse_version(line) click to toggle source
# File lib/m3u8/reader.rb, line 94
def parse_version(line)
  playlist.version = line.gsub('#EXT-X-VERSION:', '').to_i
end
universal_tags() click to toggle source
# File lib/m3u8/reader.rb, line 66
def universal_tags
  {
    '#EXT-X-START' => ->(line) { parse_start(line) },
    '#EXT-X-INDEPENDENT-SEGMENTS' => proc do
      playlist.independent_segments = true
    end
  }
end
validate_file_format(line) click to toggle source
# File lib/m3u8/reader.rb, line 213
def validate_file_format(line)
  return if line.rstrip == '#EXTM3U'
  message = 'Playlist must start with a #EXTM3U tag, line read ' \
            "contained the value: #{line}"
  raise InvalidPlaylistError, message
end