m3u8 0.5.3 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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