2014-02-08 16:08:39 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
# ex:ts=4:sw=4:sts=4:et
|
|
|
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
|
|
# The unittest framwork doesn't play nice with pylint:
|
|
|
|
# pylint: disable-msg=C0103
|
2016-04-03 19:06:45 +02:00
|
|
|
# We're a test, we go where ever we want (within reason, of course):
|
|
|
|
# pylint: disable-msg=protected-access
|
2021-08-14 15:16:21 +02:00
|
|
|
import os
|
2014-02-08 16:08:39 +01:00
|
|
|
import unittest
|
2019-08-25 00:40:39 +02:00
|
|
|
|
2021-08-14 15:16:21 +02:00
|
|
|
import requests_mock
|
|
|
|
from svtplay_dl import fetcher
|
|
|
|
from svtplay_dl.fetcher.hls import _hlsparse
|
2022-12-10 14:05:56 +01:00
|
|
|
from svtplay_dl.fetcher.m3u8 import M3U8
|
2021-08-14 15:16:21 +02:00
|
|
|
from svtplay_dl.utils.parser import setup_defaults
|
|
|
|
|
|
|
|
|
|
|
|
def parse(playlist):
|
|
|
|
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "m3u8-playlists", playlist)) as fd:
|
|
|
|
manifest = fd.read()
|
|
|
|
streams = {}
|
|
|
|
for i in list(_hlsparse(setup_defaults(), manifest, "http://localhost.com/", {})):
|
|
|
|
if isinstance(i, fetcher.VideoRetriever):
|
|
|
|
streams[i.bitrate] = i
|
|
|
|
return streams
|
|
|
|
|
2015-09-15 20:10:32 +02:00
|
|
|
|
2022-06-05 16:05:45 +02:00
|
|
|
def parse_m3u8(playlist):
|
|
|
|
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "m3u8-playlists", playlist)) as fd:
|
|
|
|
manifest = fd.read()
|
|
|
|
return M3U8(manifest)
|
|
|
|
|
|
|
|
|
2018-01-19 18:33:14 +01:00
|
|
|
# Example HLS playlist, source:
|
|
|
|
# loosly inspired by
|
|
|
|
# https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8
|
2019-08-25 00:27:31 +02:00
|
|
|
M3U_EXAMPLE = """#EXTM3U
|
2018-01-19 18:33:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=232370,CODECS="mp4a.40.2, avc1.4d4015"
|
|
|
|
something1/else.m3u8
|
|
|
|
|
|
|
|
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=649879,CODECS="mp4a.40.2, avc1.4d401e"
|
|
|
|
something2/else.m3u8
|
|
|
|
|
|
|
|
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=991714,CODECS="mp4a.40.2, avc1.4d401e"
|
|
|
|
something3/else.m3u8
|
|
|
|
|
|
|
|
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1927833,CODECS="mp4a.40.2, avc1.4d401f"
|
|
|
|
something4/else.m3u8
|
|
|
|
|
|
|
|
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=41457,CODECS="mp4a.40.2"
|
|
|
|
something0/else.m3u8
|
2019-08-25 00:27:31 +02:00
|
|
|
"""
|
2018-01-19 18:33:14 +01:00
|
|
|
|
2021-08-14 15:16:21 +02:00
|
|
|
M3U_EXAMPLE2 = """#EXTM3U
|
|
|
|
#EXT-X-VERSION:4
|
|
|
|
## Created with Unified Streaming Platform(version=1.9.5)
|
|
|
|
#EXT-X-PLAYLIST-TYPE:VOD
|
|
|
|
#EXT-X-MEDIA-SEQUENCE:1
|
|
|
|
#EXT-X-INDEPENDENT-SEGMENTS
|
|
|
|
#EXT-X-TARGETDURATION:60
|
|
|
|
#USP-X-TIMESTAMP-MAP:MPEGTS=900000,LOCAL=1970-01-01T00:00:00Z
|
|
|
|
#EXTINF:60, no desc
|
|
|
|
pid200028961_3910568(3910568_ISMUSP)-textstream_swe=1000-1.webvtt
|
|
|
|
#EXTINF:60, no desc
|
|
|
|
pid200028961_3910568(3910568_ISMUSP)-textstream_swe=1000-2.webvtt
|
|
|
|
#EXTINF:60, no desc
|
|
|
|
pid200028961_3910568(3910568_ISMUSP)-textstream_swe=1000-3.webvtt
|
|
|
|
#EXTINF:60, no desc
|
|
|
|
pid200028961_3910568(3910568_ISMUSP)-textstream_swe=1000-4.webvtt
|
|
|
|
"""
|
|
|
|
|
2018-01-30 20:11:37 +01:00
|
|
|
|
2014-02-08 16:08:39 +01:00
|
|
|
class HlsTest(unittest.TestCase):
|
2018-01-05 21:47:42 +01:00
|
|
|
def test_parse_m3u8(self):
|
2018-01-19 18:33:14 +01:00
|
|
|
self.maxDiff = None
|
2018-01-05 21:47:42 +01:00
|
|
|
for test in [
|
|
|
|
# full http:// url as media segment in playlist
|
|
|
|
{
|
2019-08-25 00:27:31 +02:00
|
|
|
"playlist": M3U_EXAMPLE,
|
|
|
|
"expected": [
|
|
|
|
{
|
|
|
|
"PROGRAM-ID": "1",
|
|
|
|
"BANDWIDTH": "232370",
|
|
|
|
"TAG": "EXT-X-STREAM-INF",
|
|
|
|
"URI": "something1/else.m3u8",
|
|
|
|
"CODECS": "mp4a.40.2, avc1.4d4015",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"PROGRAM-ID": "1",
|
|
|
|
"BANDWIDTH": "649879",
|
|
|
|
"TAG": "EXT-X-STREAM-INF",
|
|
|
|
"URI": "something2/else.m3u8",
|
|
|
|
"CODECS": "mp4a.40.2, avc1.4d401e",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"PROGRAM-ID": "1",
|
|
|
|
"BANDWIDTH": "991714",
|
|
|
|
"TAG": "EXT-X-STREAM-INF",
|
|
|
|
"URI": "something3/else.m3u8",
|
|
|
|
"CODECS": "mp4a.40.2, avc1.4d401e",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"PROGRAM-ID": "1",
|
|
|
|
"BANDWIDTH": "1927833",
|
|
|
|
"TAG": "EXT-X-STREAM-INF",
|
|
|
|
"URI": "something4/else.m3u8",
|
|
|
|
"CODECS": "mp4a.40.2, avc1.4d401f",
|
|
|
|
},
|
2019-09-06 22:49:49 +02:00
|
|
|
{"PROGRAM-ID": "1", "BANDWIDTH": "41457", "TAG": "EXT-X-STREAM-INF", "URI": "something0/else.m3u8", "CODECS": "mp4a.40.2"},
|
2019-08-25 00:27:31 +02:00
|
|
|
],
|
2020-12-26 13:10:56 +01:00
|
|
|
},
|
2018-01-05 21:47:42 +01:00
|
|
|
# More examples can be found on "https://developer.apple.com/streaming/examples/"
|
|
|
|
]:
|
2019-08-31 01:02:59 +02:00
|
|
|
assert M3U8(test["playlist"]).master_playlist == test["expected"]
|
2021-08-14 15:16:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_noaudio_segment():
|
|
|
|
with requests_mock.Mocker() as m:
|
|
|
|
m.get("http://localhost.com/pid200028961_3910568(3910568_ISMUSP)-textstream_swe=1000.m3u8", text=M3U_EXAMPLE2)
|
|
|
|
data = parse("no-audio-uri.m3u8")
|
|
|
|
assert data[3642].segments is False
|
|
|
|
assert data[3642].audio is None
|
|
|
|
|
|
|
|
|
|
|
|
def test_audio_top():
|
|
|
|
with requests_mock.Mocker() as m:
|
|
|
|
m.get("http://localhost.com/text/text-0.m3u8", text=M3U_EXAMPLE2)
|
|
|
|
data = parse("audio-uri-top.m3u8")
|
|
|
|
assert data[3295].segments
|
|
|
|
assert data[3295].audio
|
|
|
|
|
|
|
|
|
|
|
|
def test_audio_bottom():
|
|
|
|
data = parse("audio-uri-bottom.m3u8")
|
|
|
|
assert data[2639].segments
|
|
|
|
assert data[2639].audio
|
2022-06-05 16:05:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_x_byterange():
|
|
|
|
data = parse_m3u8("ext-x-byterange.m3u8")
|
|
|
|
assert data.media_segment[0]["EXT-X-BYTERANGE"]["n"] == 748
|
|
|
|
assert data.media_segment[1]["EXT-X-BYTERANGE"]["n"] == 242398
|
|
|
|
assert data.media_segment[1]["EXT-X-BYTERANGE"]["n"] + data.media_segment[1]["EXT-X-BYTERANGE"]["o"] - 1 == 245225
|
|
|
|
|
|
|
|
|
|
|
|
def test_x_map():
|
|
|
|
data = parse_m3u8("x-map.m3u8")
|
|
|
|
assert data.media_segment[0]["URI"] == "video/init.mp4"
|
2022-06-05 17:46:45 +02:00
|
|
|
assert data.media_segment[2]["URI"] == "video/2.m4s"
|