OverviewFilesCommitsIssuesDocsReleasesPeers

stremio-vsembed / test_vidcore_variants.py

172 lines · python ref:
"""One stream row per bitrate, and the variant selection behind it.

vidcore hands us a master playlist; we serve a single VARIANT (see _cap_variant's
comment for why: the master has no VOD/ENDLIST markers so Kodi calls it live and
refuses pause, and bare ffmpeg picks the highest bandwidth, which on this title
set means a 2160p stream the Pi cannot decode). That left exactly one quality.

These cover offering the user a choice of quality instead, without giving up
either property: every row is still a single variant playlist.

Run:  .venv/bin/python3 -m pytest test_vidcore_variants.py -q
"""

import pytest

import addon

MASTER_URL = "https://cdn.example/vd/TOKEN/master.m3u8"

MASTER = """#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=15800000,RESOLUTION=3840x2160,CODECS="avc1.640033"
sd/52/index-s2160p-v1-a1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=6047207,RESOLUTION=1920x1080,CODECS="avc1.640028"
sd/52/index-s1080p-v1-a1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3049124,RESOLUTION=1280x720,CODECS="avc1.4d401f"
sd/52/index-s720p-v1-a1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1287760,RESOLUTION=852x480,CODECS="avc1.4d401e"
sd/52/index-s480p-v1-a1.m3u8
"""

MEDIA = """#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6.0,
seg-1.ts
#EXT-X-ENDLIST
"""

# A master whose best is 720p, for the "nothing at or below the ask" case.
LOW_MASTER = """#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=3049124,RESOLUTION=1280x720
only-720.m3u8
"""


class _Resp:
    def __init__(self, text):
        self.text = text

    def raise_for_status(self):
        pass


@pytest.fixture(autouse=True)
def no_network(monkeypatch):
    monkeypatch.setattr(addon, "_variant_cache", {})
    monkeypatch.setattr(addon.requests, "get",
                        lambda *a, **k: _Resp(_no_network.body))
    yield


class _NoNetwork:
    body = MASTER


_no_network = _NoNetwork()


@pytest.fixture(autouse=True)
def reset_body():
    _no_network.body = MASTER
    yield
    _no_network.body = MASTER


# --- picking a variant ------------------------------------------------------

def test_caps_to_the_best_at_or_below_the_ask():
    assert addon._cap_variant(MASTER_URL, 1080).endswith("index-s1080p-v1-a1.m3u8")
    assert addon._cap_variant(MASTER_URL, 720).endswith("index-s720p-v1-a1.m3u8")
    assert addon._cap_variant(MASTER_URL, 480).endswith("index-s480p-v1-a1.m3u8")


def test_each_height_is_a_distinct_playable_url():
    urls = {addon._cap_variant(MASTER_URL, h) for h in addon.VIDCORE_HEIGHTS}
    assert len(urls) == len(addon.VIDCORE_HEIGHTS)
    assert all(u.startswith("https://cdn.example/vd/TOKEN/") for u in urls)


def test_relative_uri_is_resolved_against_the_master():
    assert addon._cap_variant(MASTER_URL, 720) == \
        "https://cdn.example/vd/TOKEN/sd/52/index-s720p-v1-a1.m3u8"


def test_4k_is_never_selected():
    """The Pi's V4L2 decoder cannot decode it, so no row may reach it."""
    for h in addon.VIDCORE_HEIGHTS:
        assert "2160" not in addon._cap_variant(MASTER_URL, h)


def test_falls_back_to_the_lowest_variant_not_the_master():
    """Asking for 480p from a 720p-only master must still yield a VARIANT.

    Returning the master here would hand Kodi a playlist with no ENDLIST, which
    it treats as live and refuses to pause -- the exact bug _cap_variant exists
    to prevent, reintroduced through the fallback path.
    """
    _no_network.body = LOW_MASTER
    assert addon._cap_variant(MASTER_URL, 480).endswith("only-720.m3u8")


# --- not making things worse ------------------------------------------------

def test_media_playlist_passes_through():
    _no_network.body = MEDIA
    assert addon._cap_variant(MASTER_URL, 1080) == MASTER_URL


def test_fetch_failure_serves_the_master(monkeypatch):
    def boom(*a, **k):
        raise RuntimeError("connection reset")
    monkeypatch.setattr(addon.requests, "get", boom)
    assert addon._cap_variant(MASTER_URL, 1080) == MASTER_URL


def test_a_failed_fetch_is_not_cached(monkeypatch):
    calls = {"n": 0}

    def boom(*a, **k):
        calls["n"] += 1
        raise RuntimeError("connection reset")
    monkeypatch.setattr(addon.requests, "get", boom)
    addon._cap_variant(MASTER_URL, 1080)
    addon._cap_variant(MASTER_URL, 1080)
    assert calls["n"] == 2


def test_the_master_is_parsed_once_per_title():
    """Three quality rows must not mean three fetches of the same playlist."""
    calls = {"n": 0}
    real = addon.requests.get

    def counting(*a, **k):
        calls["n"] += 1
        return real(*a, **k)
    addon.requests.get = counting
    try:
        for h in addon.VIDCORE_HEIGHTS:
            addon._cap_variant(MASTER_URL, h)
    finally:
        addon.requests.get = real
    assert calls["n"] == 1


# --- the rows themselves ----------------------------------------------------

def test_row_carries_the_height_in_url_and_label():
    row = addon._vidcore_row("host:7220", "series", "tmdb:138502", 2, 7, height=720)
    assert row.url == "http://host:7220/vidcore/series/tmdb:138502:2:7.m3u8?h=720"
    assert "720p" in row.title_extra


def test_series_rows_keep_the_SxxExx_tag():
    """Jacktook drops TV results whose title has no SxxEyy tag."""
    for h in addon.VIDCORE_HEIGHTS:
        row = addon._vidcore_row("h:1", "series", "tmdb:1", 2, 7, height=h)
        assert "S02E07" in row.title_extra


def test_highest_quality_is_offered_first():
    assert list(addon.VIDCORE_HEIGHTS) == sorted(addon.VIDCORE_HEIGHTS, reverse=True)
    assert addon.VIDCORE_HEIGHTS[0] == addon.VIDCORE_MAX_HEIGHT