Class: M3Uzi

Inherits:
Object
  • Object
show all
Defined in:
lib/m3uzi.rb,
lib/m3uzi/tag.rb,
lib/m3uzi/file.rb,
lib/m3uzi/stream.rb,
lib/m3uzi/version.rb

Defined Under Namespace

Classes: File, Stream, Tag

Constant Summary collapse

VALID_TAGS =

Unsupported: PROGRAM-DATE-TIME DISCONTINUITY

%w{TARGETDURATION MEDIA-SEQUENCE ALLOW-CACHE ENDLIST KEY}
VERSION =
'0.2.1'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeM3Uzi

Returns a new instance of M3Uzi.



17
18
19
20
21
22
23
24
# File 'lib/m3uzi.rb', line 17

def initialize
  @files = []
  @streams = []
  @tags = []
  @comments = []
  @final_media_file = true
  @version = 1
end

Instance Attribute Details

#commentsObject

Returns the value of attribute comments.



13
14
15
# File 'lib/m3uzi.rb', line 13

def comments
  @comments
end

#filesObject

Returns the value of attribute files.



12
13
14
# File 'lib/m3uzi.rb', line 12

def files
  @files
end

#final_media_fileObject

Returns the value of attribute final_media_file.



14
15
16
# File 'lib/m3uzi.rb', line 14

def final_media_file
  @final_media_file
end

#streamsObject

Returns the value of attribute streams.



12
13
14
# File 'lib/m3uzi.rb', line 12

def streams
  @streams
end

#tagsObject

Returns the value of attribute tags.



13
14
15
# File 'lib/m3uzi.rb', line 13

def tags
  @tags
end

#versionObject

Returns the value of attribute version.



15
16
17
# File 'lib/m3uzi.rb', line 15

def version
  @version
end

Class Method Details

.read(path) ⇒ Object


Read/Write M3U8 Files




31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/m3uzi.rb', line 31

def self.read(path)
  m3u = self.new
  lines = ::File.readlines(path)
  lines.each_with_index do |line, i|
    case type(line)
    when :tag
      name, value = parse_general_tag(line)
      m3u.add_tag do |tag|
        tag.name = name
        tag.value = value
      end
    when :info
      duration, description = parse_file_tag(line)
      m3u.add_file do |file|
        file.path = lines[i+1].strip
        file.duration = duration
        file.description = description
      end
      m3u.final_media_file = false
    when :stream
      attributes = parse_stream_tag(line)
      m3u.add_stream do |stream|
        stream.path = lines[i+1].strip
        attributes.each_pair do |k,v|
          k = k.to_s.downcase.sub('-','_')
          next unless [:bandwidth, :program_id, :codecs, :resolution].include?(k)
          v = $1 if v.to_s =~ /^"(.*)"$/
          stream.send("#{k}=", v)
        end
      end
    when :final
      m3u.final_media_file = true
    else
      next
    end
  end
  m3u
end

Instance Method Details

#<<(comment) ⇒ Object



168
169
170
# File 'lib/m3uzi.rb', line 168

def <<(comment)
  add_comment(comment)
end

#[](key) ⇒ Object



146
147
148
149
150
# File 'lib/m3uzi.rb', line 146

def [](key)
  tag_name = key.to_s.upcase.gsub("_", "-")
  obj = tags.detect { |tag| tag.name == tag_name }
  obj && obj.value
end

#[]=(key, value) ⇒ Object



152
153
154
155
156
157
# File 'lib/m3uzi.rb', line 152

def []=(key, value)
  add_tag do |tag|
    tag.name = key
    tag.value = value
  end
end

#add_comment(comment) ⇒ Object


Comments




164
165
166
# File 'lib/m3uzi.rb', line 164

def add_comment(comment)
  @comments << comment
end

#add_file {|new_file| ... } ⇒ Object


Files


Yields:

  • (new_file)


110
111
112
113
114
# File 'lib/m3uzi.rb', line 110

def add_file(&block)
  new_file = M3Uzi::File.new
  yield(new_file)
  @files << new_file
end

#add_stream {|new_stream| ... } ⇒ Object


Streams


Yields:

  • (new_stream)


125
126
127
128
129
# File 'lib/m3uzi.rb', line 125

def add_stream(&block)
  new_stream = M3Uzi::Stream.new
  yield(new_stream)
  @streams << new_stream
end

#add_tag {|new_tag| ... } ⇒ Object


Tags


Yields:

  • (new_tag)


140
141
142
143
144
# File 'lib/m3uzi.rb', line 140

def add_tag(&block)
  new_tag = M3Uzi::Tag.new
  yield(new_tag)
  @tags << new_tag
end

#check_version_restrictionsObject



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/m3uzi.rb', line 172

def check_version_restrictions
  @version = 1

  # Version 2 Features
  if @tags.detect { |tag| tag.name == 'KEY' && tag.value.to_s =~ /,IV=/ }
    @version = 2 if @version < 2
  end

  # Version 3 Features
  if @files.detect { |file| file.duration.kind_of?(Float) }
    @version = 3 if @version < 3
  end

  # Version 4 Features
  if @files.detect { |file| file.byterange }
    @version = 4 if @version < 4
  end
  if @tags.detect { |tag| ['MEDIA','I-FRAMES-ONLY'].include?(tag.name) }
    @version = 4 if @version < 4
  end

  # NOTES
  #   EXT-X-I-FRAME-STREAM-INF is supposed to be ignored by older clients.
  #   AUDIO/VIDEO attributes of X-STREAM-INF are used in conjunction with MEDIA, so it should trigger v4.

  @version
end

#filenamesObject



116
117
118
# File 'lib/m3uzi.rb', line 116

def filenames
  files.map { |file| file.path }
end

#stream_namesObject



131
132
133
# File 'lib/m3uzi.rb', line 131

def stream_names
  streams.map { |stream| stream.path }
end

#write(path) ⇒ Object



98
99
100
101
102
103
# File 'lib/m3uzi.rb', line 98

def write(path)
  check_version_restrictions
  f = ::File.open(path, "w")
  write_to_io(f)
  f.close()
end

#write_to_io(io_stream) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/m3uzi.rb', line 70

def write_to_io(io_stream)
  check_version_restrictions
  io_stream << "#EXTM3U\n"
  io_stream << "#EXT-X-VERSION:#{@version.to_i}\n" if @version > 1
  comments.each do |comment|
    io_stream << "##{comment}\n"
  end
  tags.each do |tag|
    next if %w{M3U ENDLIST}.include?(tag.name.to_s.upcase)
    if VALID_TAGS.include?(tag.name.to_s.upcase)
      io_stream << "#EXT-X-#{tag.name.to_s.upcase}"
    else
      io_stream << "##{tag.name.to_s.upcase}"
    end
    tag.value && io_stream << ":#{tag.value}"
    io_stream << "\n"
  end
  files.each do |file|
    io_stream << "#EXTINF:#{file.attribute_string}"
    io_stream << "\n#{file.path}\n"
  end
  streams.each do |stream|
    io_stream << "#EXT-X-STREAM-INF:#{stream.attribute_string}"
    io_stream << "\n#{stream.path}\n"
  end
  io_stream << "#EXT-X-ENDLIST\n" if files.length > 0 && final_media_file
end