Files
unshackle/tests/core/test_cdm_detect.py
T
imSp4rky 4f188b866f fix(cdm): skip the server header probe for pywidevine and pyplayready remote CDMs
A reverse proxy or CDN rewrites the Server header, so upstream refused a working serve API.
2026-09-25 22:54:08 -06:00

209 lines
6.8 KiB
Python

"""CDM detection and load-time DRM stamping for local and remote CDMs.
Covers the predicates in ``unshackle.core.cdm.detect`` (playready/widevine, local/remote,
and the remote combinations) and the loader stamping that sets a remote CDM's DRM type at
load time instead of guessing it on each call. Services such as EXAMPLE choose a device
profile from that type, so a wrong classification here is what produces a licence denial
for the requested resolution.
"""
from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
import pytest
from unshackle.core.cdm import loader as loader_mod
from unshackle.core.cdm.detect import (
cdm_location,
is_local_cdm,
is_playready_cdm,
is_remote_cdm,
is_remote_playready_cdm,
is_remote_widevine_cdm,
is_widevine_cdm,
)
from unshackle.core.cdm.loader import stamp_remote
pytestmark = pytest.mark.unit
REPO = Path(__file__).resolve().parents[2]
PRD_DIR = REPO / "unshackle" / "PRDs"
def test_stamped_remote_playready():
cdm = SimpleNamespace(drm="playready", is_remote_cdm=True)
assert is_playready_cdm(cdm)
assert not is_widevine_cdm(cdm)
assert is_remote_cdm(cdm)
assert is_remote_playready_cdm(cdm)
assert not is_remote_widevine_cdm(cdm)
assert cdm_location(cdm) == "remote"
def test_stamped_remote_widevine():
cdm = SimpleNamespace(drm="widevine", is_remote_cdm=True)
assert is_widevine_cdm(cdm)
assert not is_playready_cdm(cdm)
assert is_remote_widevine_cdm(cdm)
assert not is_remote_playready_cdm(cdm)
assert cdm_location(cdm) == "remote"
def test_drm_stamp_beats_conflicting_is_playready_attr():
# The stamped drm takes priority over the wrapper's own is_playready guess.
cdm = SimpleNamespace(drm="playready", is_playready=False, is_remote_cdm=True)
assert is_playready_cdm(cdm)
assert not is_widevine_cdm(cdm)
def test_drm_stamp_case_insensitive():
cdm = SimpleNamespace(drm="PlayReady", is_remote_cdm=True)
assert is_playready_cdm(cdm)
def test_none_classifies_as_nothing():
for fn in (
is_playready_cdm,
is_widevine_cdm,
is_remote_cdm,
is_local_cdm,
is_remote_playready_cdm,
is_remote_widevine_cdm,
):
assert fn(None) is False
assert cdm_location(None) == "unknown"
@pytest.mark.parametrize("device_name", ["SL2", "SL3", "SL2000", "SL3000", "sl3000"])
def test_decryptlabs_sl_names_are_playready(device_name):
from unshackle.core.cdm.decrypt_labs_remote_cdm import DecryptLabsRemoteCDM
cdm = DecryptLabsRemoteCDM(secret="x", device_name=device_name)
assert cdm.is_playready
assert is_playready_cdm(cdm)
assert is_remote_cdm(cdm)
assert is_remote_playready_cdm(cdm)
assert not is_local_cdm(cdm)
@pytest.mark.parametrize("device_name", ["ChromeCDM", "L1", "L2"])
def test_decryptlabs_widevine_names(device_name):
from unshackle.core.cdm.decrypt_labs_remote_cdm import DecryptLabsRemoteCDM
cdm = DecryptLabsRemoteCDM(secret="x", device_name=device_name)
assert not cdm.is_playready
assert is_widevine_cdm(cdm)
assert is_remote_widevine_cdm(cdm)
def test_decryptlabs_device_type_forces_playready():
from unshackle.core.cdm.decrypt_labs_remote_cdm import DecryptLabsRemoteCDM
cdm = DecryptLabsRemoteCDM(secret="x", device_name="ChromeCDM", device_type="PLAYREADY")
assert cdm.is_playready
@pytest.mark.parametrize(
("device", "expect_pr"),
[
({"name": "SL3000"}, True),
({"name": "SL2"}, True),
({"name": "ChromeCDM"}, False),
({"type": "PLAYREADY", "name": "ChromeCDM"}, True),
],
)
def test_custom_remote_cdm_classification(device, expect_pr):
from unshackle.core.cdm.custom_remote_cdm import CustomRemoteCDM
cdm = CustomRemoteCDM(host="http://example.invalid", device=device)
assert cdm.is_playready is expect_pr
assert is_playready_cdm(cdm) is expect_pr
assert is_remote_cdm(cdm)
def test_stamp_remote_sets_attrs_and_returns_same_object():
obj = SimpleNamespace()
out = stamp_remote(obj, "playready")
assert out is obj
assert obj.drm == "playready"
assert obj.is_remote_cdm is True
def test_stamp_remote_is_best_effort_on_unsettable_object():
class Slotted:
__slots__ = ()
obj = Slotted()
out = stamp_remote(obj, "widevine") # must not raise
assert out is obj
assert not hasattr(obj, "drm")
def test_load_remote_decryptlabs_playready_is_stamped():
cdm_api = {"name": "dl-pr", "type": "decrypt_labs", "secret": "x", "device_name": "SL3000"}
cdm = loader_mod.load_remote_cdm(dict(cdm_api), "dl-pr", "EXAMPLE", None)
assert cdm.drm == "playready"
assert cdm.is_remote_cdm is True
assert is_remote_playready_cdm(cdm)
def test_load_remote_decryptlabs_widevine_is_stamped():
cdm_api = {"name": "dl-wv", "type": "decrypt_labs", "secret": "x", "device_name": "ChromeCDM"}
cdm = loader_mod.load_remote_cdm(dict(cdm_api), "dl-wv", "EXAMPLE", None)
assert cdm.drm == "widevine"
assert is_remote_widevine_cdm(cdm)
def test_load_remote_native_playready_is_stamped(monkeypatch):
import unshackle.core.cdm.unprobed_remote_cdm as unprobed_mod
class FakePlayReadyRemote:
def __init__(self, **kwargs):
self.kwargs = kwargs
monkeypatch.setattr(unprobed_mod, "UnprobedPlayReadyRemoteCdm", FakePlayReadyRemote)
cdm_api = {"name": "pr", "Device Type": "PLAYREADY", "host": "h", "secret": "s", "device_name": "d"}
cdm = loader_mod.load_remote_cdm(dict(cdm_api), "pr", "EXAMPLE", None)
assert isinstance(cdm, FakePlayReadyRemote)
assert cdm.drm == "playready"
assert cdm.is_remote_cdm is True
assert is_remote_playready_cdm(cdm)
def test_load_remote_native_widevine_is_stamped(monkeypatch):
import unshackle.core.cdm.unprobed_remote_cdm as unprobed_mod
class FakeWidevineRemote:
def __init__(self, **kwargs):
self.kwargs = kwargs
monkeypatch.setattr(unprobed_mod, "UnprobedWidevineRemoteCdm", FakeWidevineRemote)
cdm_api = {"name": "wv", "Device Type": "ANDROID", "host": "h", "secret": "s", "device_name": "d"}
cdm = loader_mod.load_remote_cdm(dict(cdm_api), "wv", "EXAMPLE", None)
assert isinstance(cdm, FakeWidevineRemote)
assert cdm.drm == "widevine"
assert is_remote_widevine_cdm(cdm)
@pytest.fixture
def local_playready_cdm():
prds = sorted(PRD_DIR.glob("*.prd"))
if not prds:
pytest.skip(f"no local .prd present in {PRD_DIR}")
from pyplayready.cdm import Cdm
from pyplayready.device import Device
return Cdm.from_device(Device.load(str(prds[0])))
def test_local_playready_is_playready_and_local(local_playready_cdm):
cdm = local_playready_cdm
assert is_playready_cdm(cdm)
assert not is_widevine_cdm(cdm)
assert is_local_cdm(cdm)
assert not is_remote_cdm(cdm)
assert not is_remote_playready_cdm(cdm)
assert cdm_location(cdm) == "local"