m3u8 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f41fb7682951bc08373079dfee9f08b6989b350a
4
- data.tar.gz: bd20a5df1f8db01cab94eccc1be9b451071712a3
3
+ metadata.gz: 8bead9657f94696922af4e09d7f19800b80572ff
4
+ data.tar.gz: b654a6d47b3cfad36b08f58cb95ffd12e46c3b66
5
5
  SHA512:
6
- metadata.gz: fe388514a463c631a000fff5ce7cffaa0b93475ec2f3c0911701b049bf5cd97383a19ffc69e5e37f4943320652582d9e26de625a8b888d8dd6a57e33e7c8f65c
7
- data.tar.gz: d68bafed0338082337721ce185286577c1079b57eb99eefa9049b4775e4bca46c38f5bbe2993aa6dd69c7d0a27050194f5971e0e6ce9b74bbd0b79c48bc40954
6
+ metadata.gz: fc8d26287b07199eaba044e66330f519a6c96b44b52e607ea48cafb286d4a005e22d1f43cdfb2d353c9ccc79e4ea92fd188ed7e4cdee570580110b7b23ff5f6f
7
+ data.tar.gz: 9b82bbb066aa4ccbb6e3f9827ec7ca56fbb845f56ac57826fbe29d37bdd06631bf1800b43cd42e28b582630c5fd50a6c91818af1c87b223e76cac54ed71b0062
@@ -1,8 +1,12 @@
1
- ### 0.5.3 (2/24/2015) - [stan3](https://github.com/stan3) fixed issue recently introduced in Reader where EXT-X-STREAM-INF missing resolution would break parsing.
1
+ #### 0.6.1 (8/15/2015) - Added support for EXT-X-PROGRAM-DATE-TIME tags.
2
+
3
+ #### 0.6.0 (6/20/2015) - Added support for EXT-X-MAP tags, introduced ByteRange class to handled shared byterange parsing/serialization functionality. Breaking changes: SegmentItem now exposes a single byterange instance accessor rather than byterange_length and byterange_start, made all parse methods static for consistency (ex: MediaItem.parse).
4
+
5
+ #### 0.5.3 (2/24/2015) - [stan3](https://github.com/stan3) fixed issue recently introduced in Reader where EXT-X-STREAM-INF missing resolution would break parsing.
2
6
 
3
7
  #### 0.5.2 (2/18/2015) - Fix issue where PlaylistItem.average_bandwidth would default to 0 instead of nil when not present in m3u8 being parsed.
4
8
 
5
- #### 0.5.1 (2/16/2015) - [takashisite](https://github.com/takashisite) added support for [EXT-X-DISCONTINUITY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.3). Added support for [EXT-X-KEY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.4) (keys for encrypted segments).
9
+ #### 0.5.1 (2/16/2015) - [takashisite](https://github.com/takashisite) added support for [EXT-X-DISCONTINUITY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.3). Added support for [EXT-X-KEY](https://tools.ietf.org/html/draft-pantos-http-live-streaming-14#section-4.3.2.4) (keys for encrypted segments).
6
10
  ***
7
11
  #### 0.5.0 (2/10/2015) - BREAKING: renamed PlaylistItem.playlist to PlaylistItem.uri, MediaItem.group to MediaItem.group_id, and PlaylistItem.bitrate to PlaylistItem.bandwidth so attributes more closely match the spec. Added support for EXT-X-I-FRAME-STREAM-INF, EXT-X-I-FRAMES-ONLY, EXT-X-BYTERANGE, and EXT-X-SESSION-DATA.
8
12
  ***
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'coveralls', require: false
7
+ gem 'simplecov', require: false
@@ -5,8 +5,11 @@ require 'm3u8/playlist_item'
5
5
  require 'm3u8/segment_item'
6
6
  require 'm3u8/discontinuity_item'
7
7
  require 'm3u8/key_item'
8
+ require 'm3u8/map_item'
8
9
  require 'm3u8/media_item'
10
+ require 'm3u8/time_item'
9
11
  require 'm3u8/session_data_item'
12
+ require 'm3u8/byte_range'
10
13
  require 'm3u8/reader'
11
14
  require 'm3u8/writer'
12
15
  require 'm3u8/error'
@@ -21,4 +24,11 @@ module M3u8
21
24
  def parse_yes_no(value)
22
25
  value == 'YES' ? true : false
23
26
  end
27
+
28
+ def intialize_with_byterange(params = {})
29
+ params.each do |key, value|
30
+ value = ByteRange.new(value) if value.is_a?(Hash)
31
+ instance_variable_set("@#{key}", value)
32
+ end
33
+ end
24
34
  end
@@ -0,0 +1,31 @@
1
+ module M3u8
2
+ # ByteRange represents sub range of a resource
3
+ class ByteRange
4
+ attr_accessor :length, :start
5
+
6
+ def initialize(params = {})
7
+ params.each do |key, value|
8
+ instance_variable_set("@#{key}", value)
9
+ end
10
+ end
11
+
12
+ def self.parse(text)
13
+ values = text.split '@'
14
+ length_value = values[0].to_i
15
+ start_value = values[1].to_i unless values[1].nil?
16
+ options = { length: length_value, start: start_value }
17
+ ByteRange.new options
18
+ end
19
+
20
+ def to_s
21
+ "#{length}#{start_format}"
22
+ end
23
+
24
+ private
25
+
26
+ def start_format
27
+ return if start.nil?
28
+ "@#{start}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module M3u8
2
+ # MapItem represents a EXT-X-MAP tag which specifies how to obtain the Media
3
+ # Initialization Section
4
+ class MapItem
5
+ extend M3u8
6
+ include M3u8
7
+ attr_accessor :uri, :byterange
8
+
9
+ def initialize(params = {})
10
+ intialize_with_byterange(params)
11
+ end
12
+
13
+ def self.parse(text)
14
+ attributes = parse_attributes text
15
+ range_value = attributes['BYTERANGE']
16
+ range = ByteRange.parse(range_value) unless range_value.nil?
17
+ options = { uri: attributes['URI'], byterange: range }
18
+ MapItem.new options
19
+ end
20
+
21
+ def to_s
22
+ %(#EXT-X-MAP:URI="#{uri}"#{byterange_format})
23
+ end
24
+
25
+ private
26
+
27
+ def byterange_format
28
+ return if byterange.nil?
29
+ %(,BYTERANGE="#{byterange}")
30
+ end
31
+ end
32
+ end
@@ -1,7 +1,7 @@
1
1
  module M3u8
2
2
  # MediaItem represents a set of EXT-X-MEDIA attributes
3
3
  class MediaItem
4
- include M3u8
4
+ extend M3u8
5
5
  attr_accessor :type, :group_id, :language, :assoc_language, :name,
6
6
  :autoselect, :default, :uri, :forced
7
7
 
@@ -11,7 +11,7 @@ module M3u8
11
11
  end
12
12
  end
13
13
 
14
- def parse(text)
14
+ def self.parse(text)
15
15
  attributes = parse_attributes text
16
16
  options = { type: attributes['TYPE'], group_id: attributes['GROUP-ID'],
17
17
  language: attributes['LANGUAGE'],
@@ -21,7 +21,7 @@ module M3u8
21
21
  default: parse_yes_no(attributes['DEFAULT']),
22
22
  forced: parse_yes_no(attributes['FORCED']),
23
23
  uri: attributes['URI'] }
24
- initialize options
24
+ MediaItem.new options
25
25
  end
26
26
 
27
27
  def to_s
@@ -82,7 +82,7 @@ module M3u8
82
82
  end
83
83
 
84
84
  def to_yes_no(boolean)
85
- boolean == true ? 'YES' : 'NO'
85
+ boolean == true ? 'YES' : 'NO'
86
86
  end
87
87
  end
88
88
  end
@@ -69,11 +69,11 @@ module M3u8
69
69
  end
70
70
 
71
71
  def playlist_size
72
- items.select { |item| item.is_a?(PlaylistItem) }.size
72
+ items.count { |item| item.is_a?(PlaylistItem) }
73
73
  end
74
74
 
75
75
  def segment_size
76
- items.select { |item| item.is_a?(SegmentItem) }.size
76
+ items.count { |item| item.is_a?(SegmentItem) }
77
77
  end
78
78
  end
79
79
  end
@@ -2,7 +2,7 @@ module M3u8
2
2
  # PlaylistItem represents a set of EXT-X-STREAM-INF or
3
3
  # EXT-X-I-FRAME-STREAM-INF attributes
4
4
  class PlaylistItem
5
- include M3u8
5
+ extend M3u8
6
6
  attr_accessor :program_id, :width, :height, :codecs, :bandwidth,
7
7
  :audio_codec, :level, :profile, :video, :audio, :uri,
8
8
  :average_bandwidth, :subtitles, :closed_captions, :iframe
@@ -15,18 +15,20 @@ module M3u8
15
15
  end
16
16
  end
17
17
 
18
- def parse(text)
18
+ def self.parse(text)
19
19
  attributes = parse_attributes text
20
+ resolution = parse_resolution attributes['RESOLUTION']
20
21
  average = parse_average_bandwidth attributes['AVERAGE-BANDWIDTH']
21
22
  options = { program_id: attributes['PROGRAM-ID'],
22
23
  codecs: attributes['CODECS'],
24
+ width: resolution[:width],
25
+ height: resolution[:height],
23
26
  bandwidth: attributes['BANDWIDTH'].to_i,
24
27
  average_bandwidth: average,
25
28
  video: attributes['VIDEO'], audio: attributes['AUDIO'],
26
29
  uri: attributes['URI'], subtitles: attributes['SUBTITLES'],
27
30
  closed_captions: attributes['CLOSED-CAPTIONS'] }
28
- initialize options
29
- parse_resolution attributes['RESOLUTION']
31
+ PlaylistItem.new options
30
32
  end
31
33
 
32
34
  def resolution
@@ -58,14 +60,17 @@ module M3u8
58
60
 
59
61
  private
60
62
 
61
- def parse_average_bandwidth(value)
63
+ def self.parse_average_bandwidth(value)
62
64
  value.to_i unless value.nil?
63
65
  end
64
66
 
65
- def parse_resolution(resolution)
66
- return if resolution.nil?
67
- self.width = resolution.split('x')[0].to_i
68
- self.height = resolution.split('x')[1].to_i
67
+ def self.parse_resolution(resolution)
68
+ return { width: nil, height: nil } if resolution.nil?
69
+
70
+ values = resolution.split('x')
71
+ width = values[0].to_i
72
+ height = values[1].to_i
73
+ { width: width, height: height }
69
74
  end
70
75
 
71
76
  def validate
@@ -22,31 +22,33 @@ module M3u8
22
22
  private
23
23
 
24
24
  def basic_tags
25
- { '#EXT-X-VERSION' => proc { |line| parse_version line } }
25
+ { '#EXT-X-VERSION' => ->(line) { parse_version line } }
26
26
  end
27
27
 
28
28
  def media_segment_tags
29
- { '#EXTINF' => proc { |line| parse_segment line },
30
- '#EXT-X-DISCONTINUITY' => proc { |line| parse_discontinuity line },
31
- '#EXT-X-BYTERANGE' => proc { |line| parse_byterange line },
32
- '#EXT-X-KEY' => proc { |line| parse_key line }
29
+ { '#EXTINF' => ->(line) { parse_segment line },
30
+ '#EXT-X-DISCONTINUITY' => ->(line) { parse_discontinuity line },
31
+ '#EXT-X-BYTERANGE' => ->(line) { parse_byterange line },
32
+ '#EXT-X-KEY' => ->(line) { parse_key line },
33
+ '#EXT-X-MAP' => ->(line) { parse_map line },
34
+ '#EXT-X-PROGRAM-DATE-TIME' => ->(line) { parse_time line }
33
35
  }
34
36
  end
35
37
 
36
38
  def media_playlist_tags
37
- { '#EXT-X-MEDIA-SEQUENCE' => proc { |line| parse_sequence line },
38
- '#EXT-X-ALLOW-CACHE' => proc { |line| parse_cache line },
39
- '#EXT-X-TARGETDURATION' => proc { |line| parse_target line },
39
+ { '#EXT-X-MEDIA-SEQUENCE' => ->(line) { parse_sequence line },
40
+ '#EXT-X-ALLOW-CACHE' => ->(line) { parse_cache line },
41
+ '#EXT-X-TARGETDURATION' => ->(line) { parse_target line },
40
42
  '#EXT-X-I-FRAMES-ONLY' => proc { playlist.iframes_only = true },
41
- '#EXT-X-PLAYLIST-TYPE' => proc { |line| parse_playlist_type line }
43
+ '#EXT-X-PLAYLIST-TYPE' => ->(line) { parse_playlist_type line }
42
44
  }
43
45
  end
44
46
 
45
47
  def master_playlist_tags
46
- { '#EXT-X-MEDIA' => proc { |line| parse_media line },
47
- '#EXT-X-SESSION-DATA' => proc { |line| parse_session_data line },
48
- '#EXT-X-STREAM-INF' => proc { |line| parse_stream line },
49
- '#EXT-X-I-FRAME-STREAM-INF' => proc { |line| parse_iframe_stream line }
48
+ { '#EXT-X-MEDIA' => ->(line) { parse_media line },
49
+ '#EXT-X-SESSION-DATA' => ->(line) { parse_session_data line },
50
+ '#EXT-X-STREAM-INF' => ->(line) { parse_stream line },
51
+ '#EXT-X-I-FRAME-STREAM-INF' => ->(line) { parse_iframe_stream line }
50
52
  }
51
53
  end
52
54
 
@@ -87,16 +89,14 @@ module M3u8
87
89
  self.master = true
88
90
  self.open = true
89
91
 
90
- self.item = M3u8::PlaylistItem.new
91
- item.parse line
92
+ self.item = M3u8::PlaylistItem.parse line
92
93
  end
93
94
 
94
95
  def parse_iframe_stream(line)
95
96
  self.master = true
96
97
  self.open = false
97
98
 
98
- self.item = M3u8::PlaylistItem.new
99
- item.parse line
99
+ self.item = M3u8::PlaylistItem.parse line
100
100
  item.iframe = true
101
101
  playlist.items.push item
102
102
  end
@@ -114,6 +114,11 @@ module M3u8
114
114
  playlist.items.push item
115
115
  end
116
116
 
117
+ def parse_map(line)
118
+ item = M3u8::MapItem.parse line
119
+ playlist.items.push item
120
+ end
121
+
117
122
  def parse_segment(line)
118
123
  self.item = M3u8::SegmentItem.new
119
124
  values = line.gsub('#EXTINF:', '').gsub("\n", ',').split(',')
@@ -125,9 +130,8 @@ module M3u8
125
130
  end
126
131
 
127
132
  def parse_byterange(line)
128
- values = line.gsub('#EXT-X-BYTERANGE:', '').gsub("\n", ',').split '@'
129
- item.byterange_length = values[0].to_i
130
- item.byterange_start = values[1].to_i unless values[1].nil?
133
+ values = line.gsub('#EXT-X-BYTERANGE:', '').gsub("\n", ',')
134
+ item.byterange = M3u8::ByteRange.parse values
131
135
  end
132
136
 
133
137
  def parse_session_data(line)
@@ -137,11 +141,15 @@ module M3u8
137
141
 
138
142
  def parse_media(line)
139
143
  self.open = false
140
- self.item = M3u8::MediaItem.new
141
- item.parse line
144
+ self.item = M3u8::MediaItem.parse line
142
145
  playlist.items.push item
143
146
  end
144
147
 
148
+ def parse_time(line)
149
+ self.open = false
150
+ playlist.items.push M3u8::TimeItem.parse line
151
+ end
152
+
145
153
  def parse_next_line(line)
146
154
  value = line.gsub "\n", ''
147
155
  if master
@@ -2,13 +2,11 @@ module M3u8
2
2
  # SegmentItem represents EXTINF attributes with the URI that follows,
3
3
  # optionally allowing an EXT-X-BYTERANGE tag to be set.
4
4
  class SegmentItem
5
- attr_accessor :duration, :segment, :comment, :byterange_length,
6
- :byterange_start
5
+ include M3u8
6
+ attr_accessor :duration, :segment, :comment, :byterange
7
7
 
8
8
  def initialize(params = {})
9
- params.each do |key, value|
10
- instance_variable_set("@#{key}", value)
11
- end
9
+ intialize_with_byterange(params)
12
10
  end
13
11
 
14
12
  def to_s
@@ -18,13 +16,8 @@ module M3u8
18
16
  private
19
17
 
20
18
  def byterange_format
21
- return if byterange_length.nil?
22
- "\n#EXT-X-BYTERANGE:#{byterange_length}#{byterange_start_format}"
23
- end
24
-
25
- def byterange_start_format
26
- return if byterange_start.nil?
27
- "@#{byterange_start}"
19
+ return if byterange.nil?
20
+ "\n#EXT-X-BYTERANGE:#{byterange}"
28
21
  end
29
22
  end
30
23
  end
@@ -0,0 +1,30 @@
1
+ module M3u8
2
+ # TimeItem represents EXT-X-PROGRAM-DATE-TIME
3
+ class TimeItem
4
+ extend M3u8
5
+ attr_accessor :time
6
+
7
+ def initialize(params = {})
8
+ params.each do |key, value|
9
+ instance_variable_set("@#{key}", value)
10
+ end
11
+ end
12
+
13
+ def self.parse(text)
14
+ time = text.gsub('#EXT-X-PROGRAM-DATE-TIME:', '')
15
+ options = { time: Time.parse(time) }
16
+ TimeItem.new options
17
+ end
18
+
19
+ def to_s
20
+ %(#EXT-X-PROGRAM-DATE-TIME:#{time_format})
21
+ end
22
+
23
+ private
24
+
25
+ def time_format
26
+ return time if time.is_a?(String)
27
+ time.iso8601
28
+ end
29
+ end
30
+ end
@@ -1,4 +1,4 @@
1
1
  # M3u8 provides parsing, generation, and validation of m3u8 playlists
2
2
  module M3u8
3
- VERSION = '0.5.3'
3
+ VERSION = '0.6.1'
4
4
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::ByteRange do
4
+ it 'should provide m3u8 format representation' do
5
+ hash = { length: 4500, start: 600 }
6
+ item = M3u8::ByteRange.new(hash)
7
+ output = item.to_s
8
+ expected = '4500@600'
9
+ expect(output).to eq expected
10
+
11
+ hash = { length: 3300 }
12
+ item = M3u8::ByteRange.new(hash)
13
+ output = item.to_s
14
+ expected = '3300'
15
+ expect(output).to eq expected
16
+ end
17
+
18
+ it 'should parse instance from string' do
19
+ input = '3500@300'
20
+ range = M3u8::ByteRange.parse(input)
21
+ expect(range.length).to eq 3500
22
+ expect(range.start).to eq 300
23
+
24
+ input = '4000'
25
+ range = M3u8::ByteRange.parse(input)
26
+ expect(range.length).to eq 4000
27
+ expect(range.start).to be_nil
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ #EXTM3U
2
+ #EXT-X-PLAYLIST-TYPE:VOD
3
+ #EXT-X-VERSION:5
4
+ #EXT-X-MEDIA-SEQUENCE:1
5
+ #EXT-X-ALLOW-CACHE:NO
6
+ #EXT-X-TARGETDURATION:12
7
+ #EXT-X-MAP:URI="frelo/prog_index.m3u8",BYTERANGE="4500@600"
8
+ #EXT-X-ENDLIST
@@ -11,6 +11,7 @@
11
11
  1080-7mbps00001.ts
12
12
  #EXTINF:7.507489,
13
13
  1080-7mbps00002.ts
14
+ #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23Z
14
15
  #EXTINF:11.261233,
15
16
  1080-7mbps00003.ts
16
17
  #EXTINF:11.261233,
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::MapItem do
4
+ it 'should provide m3u8 format representation' do
5
+ hash = { uri: 'frelo/prog_index.m3u8',
6
+ byterange: {
7
+ length: 4500,
8
+ start: 600
9
+ }
10
+ }
11
+ item = M3u8::MapItem.new(hash)
12
+ output = item.to_s
13
+ expected = '#EXT-X-MAP:URI="frelo/prog_index.m3u8",' \
14
+ 'BYTERANGE="4500@600"'
15
+ expect(output).to eq expected
16
+
17
+ hash = { uri: 'frehi/prog_index.m3u8' }
18
+ item = M3u8::MapItem.new(hash)
19
+ output = item.to_s
20
+ expected = '#EXT-X-MAP:URI="frehi/prog_index.m3u8"'
21
+ expect(output).to eq expected
22
+ end
23
+
24
+ it 'should parse m3u8 text into instance' do
25
+ input = '#EXT-X-MAP:URI="frelo/prog_index.m3u8",' \
26
+ 'BYTERANGE="3500@300"'
27
+
28
+ item = M3u8::MapItem.parse(input)
29
+
30
+ expect(item.uri).to eq 'frelo/prog_index.m3u8'
31
+ expect(item.byterange.length).to eq 3500
32
+ expect(item.byterange.start).to eq 300
33
+
34
+ input = '#EXT-X-MAP:URI="frelo/prog_index.m3u8"'
35
+
36
+ item = M3u8::MapItem.parse(input)
37
+
38
+ expect(item.uri).to eq 'frelo/prog_index.m3u8'
39
+ expect(item.byterange).to be_nil
40
+ end
41
+ end
@@ -27,8 +27,7 @@ describe M3u8::MediaItem do
27
27
  format = '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",' \
28
28
  'ASSOC-LANGUAGE="spoken",NAME="Francais",AUTOSELECT=YES,' +
29
29
  %("DEFAULT=NO,URI="frelo/prog_index.m3u8",FORCED=YES\n")
30
- item = M3u8::MediaItem.new
31
- item.parse format
30
+ item = M3u8::MediaItem.parse format
32
31
 
33
32
  expect(item.type).to eq 'AUDIO'
34
33
  expect(item.group_id).to eq 'audio-lo'
@@ -20,8 +20,7 @@ describe M3u8::PlaylistItem do
20
20
  %(PROGRAM-ID=1,RESOLUTION=1920x1080,) +
21
21
  %(AVERAGE-BANDWIDTH=550,AUDIO="test",VIDEO="test2",) +
22
22
  %(SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url")
23
- item = M3u8::PlaylistItem.new
24
- item.parse(format)
23
+ item = M3u8::PlaylistItem.parse(format)
25
24
  expect(item.program_id).to eq '1'
26
25
  expect(item.codecs).to eq 'avc'
27
26
  expect(item.bandwidth).to eq 540
@@ -37,8 +36,7 @@ describe M3u8::PlaylistItem do
37
36
  format = %(#EXT-X-STREAM-INF:CODECS="avc",BANDWIDTH=540,) +
38
37
  %(PROGRAM-ID=1,AUDIO="test",VIDEO="test2",) +
39
38
  %(SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url")
40
- item = M3u8::PlaylistItem.new
41
- item.parse(format)
39
+ item = M3u8::PlaylistItem.parse(format)
42
40
  expect(item.program_id).to eq '1'
43
41
  expect(item.codecs).to eq 'avc'
44
42
  expect(item.bandwidth).to eq 540
@@ -68,7 +68,11 @@ describe M3u8::Reader do
68
68
  expect(item.duration).to eq 11.344644
69
69
  expect(item.comment).to be_nil
70
70
 
71
- expect(playlist.items.size).to eq 139
71
+ item = playlist.items[4]
72
+ expect(item).to be_a(M3u8::TimeItem)
73
+ expect(item.time).to eq(Time.iso8601('2010-02-19T14:54:23Z'))
74
+
75
+ expect(playlist.items.size).to eq 140
72
76
  end
73
77
 
74
78
  it 'should parse I-Frame playlist' do
@@ -82,13 +86,13 @@ describe M3u8::Reader do
82
86
  item = playlist.items[0]
83
87
  expect(item).to be_a(M3u8::SegmentItem)
84
88
  expect(item.duration).to eq 4.12
85
- expect(item.byterange_length).to eq 9400
86
- expect(item.byterange_start).to eq 376
89
+ expect(item.byterange.length).to eq 9400
90
+ expect(item.byterange.start).to eq 376
87
91
  expect(item.segment).to eq 'segment1.ts'
88
92
 
89
93
  item = playlist.items[1]
90
- expect(item.byterange_length).to eq 7144
91
- expect(item.byterange_start).to be_nil
94
+ expect(item.byterange.length).to eq 7144
95
+ expect(item.byterange.start).to be_nil
92
96
  end
93
97
 
94
98
  it 'should parse segment playlist with comments' do
@@ -198,4 +202,18 @@ describe M3u8::Reader do
198
202
  expect(item.method).to eq 'AES-128'
199
203
  expect(item.uri).to eq 'https://priv.example.com/key.php?r=52'
200
204
  end
205
+
206
+ it 'should parse map (media intialization section) playlists' do
207
+ file = File.open 'spec/fixtures/map_playlist.m3u8'
208
+ reader = M3u8::Reader.new
209
+ playlist = reader.read file
210
+
211
+ expect(playlist.items.size).to eq 1
212
+
213
+ item = playlist.items[0]
214
+ expect(item).to be_a M3u8::MapItem
215
+ expect(item.uri).to eq 'frelo/prog_index.m3u8'
216
+ expect(item.byterange.length).to eq 4500
217
+ expect(item.byterange.start).to eq 600
218
+ end
201
219
  end
@@ -7,13 +7,18 @@ describe M3u8::SegmentItem do
7
7
  expect(item.duration).to eq 10.991
8
8
  expect(item.segment).to eq 'test.ts'
9
9
  expect(item.comment).to be_nil
10
+ expect(item.byterange).to be_nil
10
11
 
11
12
  hash = { duration: 10.991, segment: 'test.ts', comment: 'anything',
12
- byterange_length: 4500, byterange_start: 600 }
13
+ byterange: {
14
+ length: 4500,
15
+ start: 600
16
+ }
17
+ }
13
18
  item = M3u8::SegmentItem.new(hash)
14
19
  expect(item.duration).to eq 10.991
15
- expect(item.byterange_length).to eq 4500
16
- expect(item.byterange_start).to eq 600
20
+ expect(item.byterange.length).to eq 4500
21
+ expect(item.byterange.start).to eq 600
17
22
  expect(item.segment).to eq 'test.ts'
18
23
  expect(item.comment).to eq 'anything'
19
24
  end
@@ -32,14 +37,21 @@ describe M3u8::SegmentItem do
32
37
  expect(output).to eq expected
33
38
 
34
39
  hash = { duration: 10.991, segment: 'test.ts', comment: 'anything',
35
- byterange_length: 4500, byterange_start: 600 }
40
+ byterange: {
41
+ length: 4500,
42
+ start: 600
43
+ }
44
+ }
36
45
  item = M3u8::SegmentItem.new(hash)
37
46
  output = item.to_s
38
47
  expected = "#EXTINF:10.991,anything\n#EXT-X-BYTERANGE:4500@600\ntest.ts"
39
48
  expect(output).to eq expected
40
49
 
41
50
  hash = { duration: 10.991, segment: 'test.ts', comment: 'anything',
42
- byterange_length: 4500 }
51
+ byterange: {
52
+ length: 4500
53
+ }
54
+ }
43
55
  item = M3u8::SegmentItem.new(hash)
44
56
  output = item.to_s
45
57
  expected = "#EXTINF:10.991,anything\n#EXT-X-BYTERANGE:4500\ntest.ts"
@@ -1,7 +1,12 @@
1
1
  require 'm3u8'
2
-
2
+ require 'simplecov'
3
3
  require 'coveralls'
4
- Coveralls.wear!
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+ SimpleCov.start
5
10
 
6
11
  # This file was generated by the `rspec --init` command. Conventionally, all
7
12
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe M3u8::TimeItem do
4
+ it 'should provide m3u8 format representation' do
5
+ options = { time: '2010-02-19T14:54:23.031' }
6
+ item = M3u8::TimeItem.new(options)
7
+ output = item.to_s
8
+ expected = '#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031'
9
+ expect(output).to eq expected
10
+
11
+ options = { time: Time.iso8601('2010-02-19T14:54:23.031Z') }
12
+ item = M3u8::TimeItem.new(options)
13
+ output = item.to_s
14
+ expected = '#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23Z'
15
+ expect(output).to eq expected
16
+ end
17
+
18
+ it 'should parse m3u8 text into instance' do
19
+ input = '#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031Z'
20
+ item = M3u8::TimeItem.parse(input)
21
+ expect(item.time).to eq Time.iso8601('2010-02-19T14:54:23.031Z')
22
+ end
23
+ end
@@ -116,7 +116,7 @@ describe M3u8::Writer do
116
116
  "1080-7mbps00000.ts\n" +
117
117
  %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
118
118
  %(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3"\n) +
119
- "#EXTINF:11.261233,\n" +
119
+ "#EXTINF:11.261233,\n" \
120
120
  "1080-7mbps00001.ts\n" \
121
121
  "#EXT-X-ENDLIST\n"
122
122
  io = StringIO.open
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.5.3
4
+ version: 0.6.1
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-02-24 00:00:00.000000000 Z
11
+ date: 2015-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,21 +70,26 @@ files:
70
70
  - README.md
71
71
  - Rakefile
72
72
  - lib/m3u8.rb
73
+ - lib/m3u8/byte_range.rb
73
74
  - lib/m3u8/discontinuity_item.rb
74
75
  - lib/m3u8/error.rb
75
76
  - lib/m3u8/key_item.rb
77
+ - lib/m3u8/map_item.rb
76
78
  - lib/m3u8/media_item.rb
77
79
  - lib/m3u8/playlist.rb
78
80
  - lib/m3u8/playlist_item.rb
79
81
  - lib/m3u8/reader.rb
80
82
  - lib/m3u8/segment_item.rb
81
83
  - lib/m3u8/session_data_item.rb
84
+ - lib/m3u8/time_item.rb
82
85
  - lib/m3u8/version.rb
83
86
  - lib/m3u8/writer.rb
84
87
  - m3u8.gemspec
88
+ - spec/byte_range_spec.rb
85
89
  - spec/discontinuity_item_spec.rb
86
90
  - spec/fixtures/encrypted.m3u8
87
91
  - spec/fixtures/iframes.m3u8
92
+ - spec/fixtures/map_playlist.m3u8
88
93
  - spec/fixtures/master.m3u8
89
94
  - spec/fixtures/master_iframes.m3u8
90
95
  - spec/fixtures/playlist.m3u8
@@ -94,6 +99,7 @@ files:
94
99
  - spec/fixtures/variant_audio.m3u8
95
100
  - spec/key_item_spec.rb
96
101
  - spec/m3u8_spec.rb
102
+ - spec/map_item_spec.rb
97
103
  - spec/media_item_spec.rb
98
104
  - spec/playlist_item_spec.rb
99
105
  - spec/playlist_spec.rb
@@ -101,6 +107,7 @@ files:
101
107
  - spec/segment_item_spec.rb
102
108
  - spec/session_data_item_spec.rb
103
109
  - spec/spec_helper.rb
110
+ - spec/time_item_spec.rb
104
111
  - spec/writer_spec.rb
105
112
  homepage: https://github.com/sethdeckard/m3u8
106
113
  licenses:
@@ -122,14 +129,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
129
  version: '0'
123
130
  requirements: []
124
131
  rubyforge_project:
125
- rubygems_version: 2.4.4
132
+ rubygems_version: 2.4.5
126
133
  signing_key:
127
134
  specification_version: 4
128
135
  summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).
129
136
  test_files:
137
+ - spec/byte_range_spec.rb
130
138
  - spec/discontinuity_item_spec.rb
131
139
  - spec/fixtures/encrypted.m3u8
132
140
  - spec/fixtures/iframes.m3u8
141
+ - spec/fixtures/map_playlist.m3u8
133
142
  - spec/fixtures/master.m3u8
134
143
  - spec/fixtures/master_iframes.m3u8
135
144
  - spec/fixtures/playlist.m3u8
@@ -139,6 +148,7 @@ test_files:
139
148
  - spec/fixtures/variant_audio.m3u8
140
149
  - spec/key_item_spec.rb
141
150
  - spec/m3u8_spec.rb
151
+ - spec/map_item_spec.rb
142
152
  - spec/media_item_spec.rb
143
153
  - spec/playlist_item_spec.rb
144
154
  - spec/playlist_spec.rb
@@ -146,4 +156,5 @@ test_files:
146
156
  - spec/segment_item_spec.rb
147
157
  - spec/session_data_item_spec.rb
148
158
  - spec/spec_helper.rb
159
+ - spec/time_item_spec.rb
149
160
  - spec/writer_spec.rb