m3u8 1.4.0 → 1.5.0
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +21 -0
- data/lib/m3u8/playlist.rb +16 -1
- data/lib/m3u8/reader.rb +1 -1
- data/lib/m3u8/version.rb +1 -1
- data/spec/lib/m3u8/builder_spec.rb +14 -0
- data/spec/lib/m3u8/playlist_spec.rb +102 -0
- data/spec/lib/m3u8/reader_spec.rb +14 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df37c1a4cc89e0bd918c6137c5bc86682d61638b407697240e76f40acb8983e7
|
|
4
|
+
data.tar.gz: e19fd10c1b3f732057285247f08bb235573fa6434085295b53a76be22f1a53aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4ff8a727525925718fcb0ff7fed0b8bb758556d8e262c97ebacaca4fe96550b4494817ce5d8f4a0525d961a274a1905b16af6b5a85b8ac57235254f54392f8a
|
|
7
|
+
data.tar.gz: 19ae07aa8cd202846254983b823973c4b7d0a9d4bde50e5cc8be7e1e4d91182205bec84731f628298f2659085434716120135a074656e1c9c2aa2c1988415b4f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
**1.5.0**
|
|
2
|
+
|
|
3
|
+
* Added `Playlist#freeze` for deep-freezing playlists, items, nested objects, and playlist-level objects. `Playlist.build` and `Playlist.read` now return frozen playlists. `Playlist.new` remains mutable until `freeze` is called explicitly.
|
|
4
|
+
|
|
5
|
+
***
|
|
6
|
+
|
|
1
7
|
**1.4.0**
|
|
2
8
|
|
|
3
9
|
* Added `Playlist#errors` method returning an array of validation error messages. `Playlist#valid?` now delegates to `errors.empty?`. Validates mixed item types, target duration, segment items, playlist items, media items, encryption keys, session keys, session data, and LL-HLS part items.
|
data/README.md
CHANGED
|
@@ -344,6 +344,27 @@ options = { width: 1920, height: 1080, codecs: 'avc1.66.30,mp4a.40.2',
|
|
|
344
344
|
item = M3u8::PlaylistItem.new(options)
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
+
## Frozen playlists
|
|
348
|
+
|
|
349
|
+
Playlists returned by `Playlist.build` and `Playlist.read` are frozen (deeply immutable). Items, nested objects, and the items array are all frozen, preventing accidental mutation after construction:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
playlist = M3u8::Playlist.read(File.open('master.m3u8'))
|
|
353
|
+
playlist.frozen? # => true
|
|
354
|
+
playlist.items.frozen? # => true
|
|
355
|
+
playlist.items.first.frozen? # => true
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Playlists created with `Playlist.new` remain mutable. Call `freeze` explicitly when ready:
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
playlist = M3u8::Playlist.new
|
|
362
|
+
playlist.items << M3u8::SegmentItem.new(duration: 10.0, segment: 'test.ts')
|
|
363
|
+
playlist.freeze
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Frozen playlists still support `to_s` and `write` for output.
|
|
367
|
+
|
|
347
368
|
## Validation
|
|
348
369
|
|
|
349
370
|
Check whether a playlist is valid and inspect specific errors:
|
data/lib/m3u8/playlist.rb
CHANGED
|
@@ -22,7 +22,7 @@ module M3u8
|
|
|
22
22
|
else
|
|
23
23
|
builder.instance_eval(&block)
|
|
24
24
|
end
|
|
25
|
-
playlist
|
|
25
|
+
playlist.freeze
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def self.codecs(options = {})
|
|
@@ -53,6 +53,14 @@ module M3u8
|
|
|
53
53
|
playlist_size.positive?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def freeze
|
|
57
|
+
items.each { |item| freeze_item(item) }
|
|
58
|
+
items.freeze
|
|
59
|
+
part_inf&.freeze
|
|
60
|
+
server_control&.freeze
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
|
|
56
64
|
def to_s
|
|
57
65
|
output = StringIO.open
|
|
58
66
|
write(output)
|
|
@@ -119,6 +127,13 @@ module M3u8
|
|
|
119
127
|
|
|
120
128
|
private
|
|
121
129
|
|
|
130
|
+
def freeze_item(item)
|
|
131
|
+
item.byterange&.freeze if item.respond_to?(:byterange)
|
|
132
|
+
item.program_date_time&.freeze if item.respond_to?(:program_date_time)
|
|
133
|
+
item.client_attributes&.freeze if item.respond_to?(:client_attributes)
|
|
134
|
+
item.freeze
|
|
135
|
+
end
|
|
136
|
+
|
|
122
137
|
def assign_options(options)
|
|
123
138
|
options = defaults.merge(options)
|
|
124
139
|
|
data/lib/m3u8/reader.rb
CHANGED
data/lib/m3u8/version.rb
CHANGED
|
@@ -191,6 +191,20 @@ describe M3u8::Builder do
|
|
|
191
191
|
expect(playlist.target).to eq(12)
|
|
192
192
|
end
|
|
193
193
|
|
|
194
|
+
it 'returns a frozen playlist' do
|
|
195
|
+
playlist = M3u8::Playlist.build do
|
|
196
|
+
segment duration: 10.0, segment: 'test.ts'
|
|
197
|
+
end
|
|
198
|
+
expect(playlist).to be_frozen
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'returns a frozen playlist with yielded form' do
|
|
202
|
+
playlist = M3u8::Playlist.build do |b|
|
|
203
|
+
b.segment duration: 10.0, segment: 'test.ts'
|
|
204
|
+
end
|
|
205
|
+
expect(playlist).to be_frozen
|
|
206
|
+
end
|
|
207
|
+
|
|
194
208
|
it 'supports yielded builder form' do
|
|
195
209
|
files = %w[seg1.ts seg2.ts]
|
|
196
210
|
playlist = M3u8::Playlist.build(version: 4) do |b|
|
|
@@ -654,6 +654,108 @@ describe M3u8::Playlist do
|
|
|
654
654
|
end
|
|
655
655
|
end
|
|
656
656
|
|
|
657
|
+
describe '#freeze' do
|
|
658
|
+
it 'freezes the playlist' do
|
|
659
|
+
playlist.freeze
|
|
660
|
+
expect(playlist).to be_frozen
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
it 'freezes the items array' do
|
|
664
|
+
playlist.freeze
|
|
665
|
+
expect(playlist.items).to be_frozen
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
it 'freezes each item' do
|
|
669
|
+
item = M3u8::SegmentItem.new(duration: 10.0, segment: 'test.ts')
|
|
670
|
+
playlist.items << item
|
|
671
|
+
playlist.freeze
|
|
672
|
+
expect(item).to be_frozen
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
it 'freezes nested byterange on items' do
|
|
676
|
+
item = M3u8::SegmentItem.new(
|
|
677
|
+
duration: 10.0, segment: 'test.ts',
|
|
678
|
+
byterange: { length: 4500, start: 600 }
|
|
679
|
+
)
|
|
680
|
+
playlist.items << item
|
|
681
|
+
playlist.freeze
|
|
682
|
+
expect(item.byterange).to be_frozen
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
it 'freezes nested program_date_time on items' do
|
|
686
|
+
time = M3u8::TimeItem.new(time: '2024-06-01T12:00:00Z')
|
|
687
|
+
item = M3u8::SegmentItem.new(
|
|
688
|
+
duration: 10.0, segment: 'test.ts',
|
|
689
|
+
program_date_time: time
|
|
690
|
+
)
|
|
691
|
+
playlist.items << item
|
|
692
|
+
playlist.freeze
|
|
693
|
+
expect(item.program_date_time).to be_frozen
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
it 'freezes nested client_attributes on items' do
|
|
697
|
+
item = M3u8::DateRangeItem.new(
|
|
698
|
+
id: 'ad-1', start_date: '2024-01-01T00:00:00Z',
|
|
699
|
+
client_attributes: { 'X-AD-ID' => '"foo"' }
|
|
700
|
+
)
|
|
701
|
+
playlist.items << item
|
|
702
|
+
playlist.freeze
|
|
703
|
+
expect(item.client_attributes).to be_frozen
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
it 'freezes part_inf' do
|
|
707
|
+
playlist.part_inf = M3u8::PartInfItem.new(part_target: 0.5)
|
|
708
|
+
playlist.freeze
|
|
709
|
+
expect(playlist.part_inf).to be_frozen
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
it 'freezes server_control' do
|
|
713
|
+
playlist.server_control = M3u8::ServerControlItem.new(
|
|
714
|
+
can_skip_until: 24.0
|
|
715
|
+
)
|
|
716
|
+
playlist.freeze
|
|
717
|
+
expect(playlist.server_control).to be_frozen
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it 'raises FrozenError on attribute set' do
|
|
721
|
+
playlist.freeze
|
|
722
|
+
expect { playlist.version = 7 }
|
|
723
|
+
.to raise_error(FrozenError)
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
it 'raises FrozenError on items append' do
|
|
727
|
+
playlist.freeze
|
|
728
|
+
item = M3u8::SegmentItem.new(
|
|
729
|
+
duration: 10.0, segment: 'test.ts'
|
|
730
|
+
)
|
|
731
|
+
expect { playlist.items << item }
|
|
732
|
+
.to raise_error(FrozenError)
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
it 'raises FrozenError on item mutation' do
|
|
736
|
+
item = M3u8::SegmentItem.new(
|
|
737
|
+
duration: 10.0, segment: 'test.ts'
|
|
738
|
+
)
|
|
739
|
+
playlist.items << item
|
|
740
|
+
playlist.freeze
|
|
741
|
+
expect { item.duration = 5.0 }
|
|
742
|
+
.to raise_error(FrozenError)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
it 'still supports to_s' do
|
|
746
|
+
item = M3u8::SegmentItem.new(
|
|
747
|
+
duration: 10.0, segment: 'test.ts'
|
|
748
|
+
)
|
|
749
|
+
playlist.items << item
|
|
750
|
+
playlist.freeze
|
|
751
|
+
expect(playlist.to_s).to include('#EXTM3U')
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
it 'returns self' do
|
|
755
|
+
expect(playlist.freeze).to equal(playlist)
|
|
756
|
+
end
|
|
757
|
+
end
|
|
758
|
+
|
|
657
759
|
describe '#write' do
|
|
658
760
|
context 'when playlist is valid' do
|
|
659
761
|
it 'returns playlist text' do
|
|
@@ -590,6 +590,20 @@ describe M3u8::Reader do
|
|
|
590
590
|
expect(item.segment).to eq('segment0.ts')
|
|
591
591
|
end
|
|
592
592
|
|
|
593
|
+
it 'returns a frozen master playlist' do
|
|
594
|
+
playlist = reader.read(
|
|
595
|
+
File.read('spec/fixtures/master.m3u8')
|
|
596
|
+
)
|
|
597
|
+
expect(playlist).to be_frozen
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
it 'returns a frozen media playlist' do
|
|
601
|
+
playlist = reader.read(
|
|
602
|
+
File.read('spec/fixtures/playlist.m3u8')
|
|
603
|
+
)
|
|
604
|
+
expect(playlist).to be_frozen
|
|
605
|
+
end
|
|
606
|
+
|
|
593
607
|
context 'when playlist source is invalid' do
|
|
594
608
|
it 'raises error with message' do
|
|
595
609
|
message = 'Playlist must start with a #EXTM3U tag, line read ' \
|