Skip to content

Commit

Permalink
Add support for stable variant/rendition IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
vevv authored Jul 25, 2022
1 parent c0b134c commit 3f5fa1b
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 7 deletions.
22 changes: 18 additions & 4 deletions m3u8/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,8 @@ def __init__(self, uri, stream_info, media, base_uri):
frame_rate=stream_info.get('frame_rate'),
video_range=stream_info.get('video_range'),
hdcp_level=stream_info.get('hdcp_level'),
pathway_id=stream_info.get('pathway_id')
pathway_id=stream_info.get('pathway_id'),
stable_variant_id=stream_info.get('stable_variant_id')
)
self.media = []
for media_type in ('audio', 'video', 'subtitles'):
Expand Down Expand Up @@ -873,7 +874,8 @@ def __init__(self, base_uri, uri, iframe_stream_info):
video_range=iframe_stream_info.get('video_range'),
hdcp_level=iframe_stream_info.get('hdcp_level'),
frame_rate=None,
pathway_id=iframe_stream_info.get('pathway_id')
pathway_id=iframe_stream_info.get('pathway_id'),
stable_variant_id=iframe_stream_info.get('stable_variant_id')
)

def __str__(self):
Expand Down Expand Up @@ -906,6 +908,10 @@ def __str__(self):
iframe_stream_inf.append(
'PATHWAY-ID=' + quoted(self.iframe_stream_info.pathway_id)
)
if self.iframe_stream_info.stable_variant_id:
iframe_stream_inf.append(
'STABLE-VARIANT-ID=' + quoted(self.iframe_stream_info.stable_variant_id)
)

return '#EXT-X-I-FRAME-STREAM-INF:' + ','.join(iframe_stream_inf)

Expand All @@ -924,6 +930,7 @@ class StreamInfo(object):
video_range = None
hdcp_level = None
pathway_id = None
stable_variant_id = None

def __init__(self, **kwargs):
self.bandwidth = kwargs.get("bandwidth")
Expand All @@ -939,6 +946,7 @@ def __init__(self, **kwargs):
self.video_range = kwargs.get("video_range")
self.hdcp_level = kwargs.get("hdcp_level")
self.pathway_id = kwargs.get("pathway_id")
self.stable_variant_id = kwargs.get("stable_variant_id")

def __str__(self):
stream_inf = []
Expand All @@ -965,6 +973,8 @@ def __str__(self):
stream_inf.append('HDCP-LEVEL=%s' % self.hdcp_level)
if self.pathway_id is not None:
stream_inf.append('PATHWAY-ID=' + quoted(self.pathway_id))
if self.stable_variant_id is not None:
stream_inf.append('STABLE-VARIANT-ID=' + quoted(self.stable_variant_id))
return ",".join(stream_inf)


Expand All @@ -987,6 +997,7 @@ class Media(BasePathMixin):
`instream_id`
`characteristics`
`channels`
`stable_rendition_id`
attributes in the EXT-MEDIA tag
`base_uri`
Expand All @@ -995,8 +1006,8 @@ class Media(BasePathMixin):

def __init__(self, uri=None, type=None, group_id=None, language=None,
name=None, default=None, autoselect=None, forced=None,
characteristics=None, channels=None, assoc_language=None,
instream_id=None, base_uri=None, **extras):
characteristics=None, channels=None, stable_rendition_id=None,
assoc_language=None, instream_id=None, base_uri=None, **extras):
self.base_uri = base_uri
self.uri = uri
self.type = type
Expand All @@ -1010,6 +1021,7 @@ def __init__(self, uri=None, type=None, group_id=None, language=None,
self.instream_id = instream_id
self.characteristics = characteristics
self.channels = channels
self.stable_rendition_id = stable_rendition_id
self.extras = extras

def dumps(self):
Expand Down Expand Up @@ -1039,6 +1051,8 @@ def dumps(self):
media_out.append('CHARACTERISTICS=' + quoted(self.characteristics))
if self.channels:
media_out.append('CHANNELS=' + quoted(self.channels))
if self.stable_rendition_id:
media_out.append('STABLE-RENDITION-ID=' + quoted(self.stable_rendition_id))

return ('#EXT-X-MEDIA:' + ','.join(media_out))

Expand Down
6 changes: 3 additions & 3 deletions m3u8/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def _parse_attribute_list(prefix, line, atribute_parser):
def _parse_stream_inf(line, data, state):
data['is_variant'] = True
data['media_sequence'] = None
atribute_parser = remove_quotes_parser('codecs', 'audio', 'video', 'subtitles', 'closed_captions', 'pathway_id')
atribute_parser = remove_quotes_parser('codecs', 'audio', 'video', 'subtitles', 'closed_captions', 'pathway_id', 'stable_variant_id')
atribute_parser["program_id"] = int
atribute_parser["bandwidth"] = lambda x: int(float(x))
atribute_parser["average_bandwidth"] = int
Expand All @@ -313,7 +313,7 @@ def _parse_stream_inf(line, data, state):


def _parse_i_frame_stream_inf(line, data):
atribute_parser = remove_quotes_parser('codecs', 'uri', 'pathway_id')
atribute_parser = remove_quotes_parser('codecs', 'uri', 'pathway_id', 'stable_variant_id')
atribute_parser["program_id"] = int
atribute_parser["bandwidth"] = int
atribute_parser["average_bandwidth"] = int
Expand All @@ -327,7 +327,7 @@ def _parse_i_frame_stream_inf(line, data):


def _parse_media(line, data, state):
quoted = remove_quotes_parser('uri', 'group_id', 'language', 'assoc_language', 'name', 'instream_id', 'characteristics', 'channels')
quoted = remove_quotes_parser('uri', 'group_id', 'language', 'assoc_language', 'name', 'instream_id', 'characteristics', 'channels', 'stable_rendition_id')
media = _parse_attribute_list(protocol.ext_x_media, line, quoted)
data['media'].append(media)

Expand Down
13 changes: 13 additions & 0 deletions tests/playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -1199,4 +1199,17 @@
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=193350,CODECS="avc1.4d001f",URI="https://backup.example.com/content/videos/video12/video-1200k-iframes.m3u8",PATHWAY-ID="CDN-B"
'''

VARIANT_PLAYLIST_WITH_STABLE_VARIANT_ID = '''
#EXT-X-STREAM-INF:BANDWIDTH=1280000,STABLE-VARIANT-ID="eb9c6e4de930b36d9a67fbd38a30b39f865d98f4a203d2140bbf71fd58ad764e"
http://example.com/type0.m3u8
'''

VARIANT_PLAYLIST_WITH_IFRAME_STABLE_VARIANT_ID = '''
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=128000,STABLE-VARIANT-ID="415901312adff69b967a0644a54f8d00dc14004f36bc8293737e6b4251f60f3f",URI="http://example.com/type0-iframes.m3u8"
'''

VARIANT_PLAYLIST_WITH_STABLE_RENDITION_ID = '''
#EXT-X-MEDIA:TYPE=AUDIO,NAME="audio-aac-eng",STABLE-RENDITION-ID="a8213e27c12a158ea8660e0fe8bdcac6072ca26d984e7e8603652bc61fdceffa",URI="http://example.com/eng.m3u8"
'''

del abspath, dirname, join
12 changes: 12 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,15 @@ def test_cue_in_pops_scte35_data_and_duration():
assert data['segments'][10]['cue_in'] == False
assert data['segments'][10]['scte35'] == None
assert data['segments'][10]['scte35_duration'] == None

def test_playlist_with_stable_variant_id():
data = m3u8.parse(playlists.VARIANT_PLAYLIST_WITH_STABLE_VARIANT_ID)
assert data['playlists'][0]['stream_info']['stable_variant_id'] == 'eb9c6e4de930b36d9a67fbd38a30b39f865d98f4a203d2140bbf71fd58ad764e'

def test_iframe_with_stable_variant_id():
data = m3u8.parse(playlists.VARIANT_PLAYLIST_WITH_IFRAME_STABLE_VARIANT_ID)
assert data['iframe_playlists'][0]['iframe_stream_info']['stable_variant_id'] == '415901312adff69b967a0644a54f8d00dc14004f36bc8293737e6b4251f60f3f'

def test_media_with_stable_rendition_id():
data = m3u8.parse(playlists.VARIANT_PLAYLIST_WITH_STABLE_RENDITION_ID)
assert data['media'][0]['stable_rendition_id'] == 'a8213e27c12a158ea8660e0fe8bdcac6072ca26d984e7e8603652bc61fdceffa'

0 comments on commit 3f5fa1b

Please sign in to comment.