fix(api): follow the cdm config key order for the server CDM

This commit is contained in:
imSp4rky
2026-10-13 13:40:38 -06:00
parent 2ba442f50e
commit 3e64a7d684
3 changed files with 25 additions and 4 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ height is known. If the value is still a dict after that, unshackle uses
When an entry holds `widevine` and `playready`, their order sets which device unshackle loads When an entry holds `widevine` and `playready`, their order sets which device unshackle loads
first. A track that offers the two DRM systems licenses with that system. A track that offers first. A track that offers the two DRM systems licenses with that system. A track that offers
only the other system uses the other device. A `widevine` / `playready` dict in a quality key only the other system uses the other device. A `widevine` / `playready` dict in a quality key
does not set the order. does not set the order. The server CDM of a remote session follows the same order.
A quality key can also hold a dict of `widevine` / `playready` devices, and so can the A quality key can also hold a dict of `widevine` / `playready` devices, and so can the
top-level `default` entry: top-level `default` entry:
+20 -1
View File
@@ -17,7 +17,7 @@ from pywidevine.pssh import PSSH
from rich.table import Table from rich.table import Table
from unshackle.commands.dl import dl from unshackle.commands.dl import dl
from unshackle.core.api.handlers import load_full_cdm, validate_download_parameters from unshackle.core.api.handlers import load_full_cdm, resolve_server_cdm, validate_download_parameters
from unshackle.core.cdm.detect import cdm_type_stub, is_widevine_cdm from unshackle.core.cdm.detect import cdm_type_stub, is_widevine_cdm
from unshackle.core.drm import PlayReady, Widevine from unshackle.core.drm import PlayReady, Widevine
from unshackle.core.manifests import HLS from unshackle.core.manifests import HLS
@@ -306,3 +306,22 @@ def test_the_first_drm_system_of_the_config_entry_loads_first(
assert cmd.get_cdm("EXAMPLE") == expected assert cmd.get_cdm("EXAMPLE") == expected
assert load_full_cdm("EXAMPLE", None) == expected assert load_full_cdm("EXAMPLE", None) == expected
assert cmd.get_cdm("EXAMPLE", drm="widevine") == "wv_device" assert cmd.get_cdm("EXAMPLE", drm="widevine") == "wv_device"
@pytest.mark.parametrize(
("entry", "expected"),
[
({"playready": "pr_device", "widevine": "wv_device"}, "playready"),
({"widevine": "wv_device", "playready": "pr_device"}, "widevine"),
],
)
def test_the_server_cdm_stub_is_the_first_drm_system_of_the_config_entry(
monkeypatch: pytest.MonkeyPatch, entry: dict, expected: str
) -> None:
monkeypatch.setattr("unshackle.core.config.config.cdm", {"EXAMPLE": entry})
monkeypatch.setattr(
"unshackle.core.api.handlers.detect_cdm_type",
lambda name, _: "playready" if name == "pr_device" else "widevine",
)
assert is_widevine_cdm(resolve_server_cdm("EXAMPLE", None, None)) is (expected == "widevine")
+4 -2
View File
@@ -3914,7 +3914,8 @@ def resolve_server_cdm(service: str, profile: Optional[str], cdm_type: Optional[
Checks the server's own CDM config (``config.cdm[service]``) to Checks the server's own CDM config (``config.cdm[service]``) to
determine the CDM type without loading the full CDM object. This determine the CDM type without loading the full CDM object. This
makes sure that when you use ``server_cdm: true``, the server's CDM makes sure that when you use ``server_cdm: true``, the server's CDM
determines device selection (e.g. PlayReady vs Widevine). determines device selection (e.g. PlayReady vs Widevine). An entry with
``widevine`` and ``playready`` gives the DRM system that it lists first.
Falls back to a lightweight stub from *cdm_type* only if the configuration Falls back to a lightweight stub from *cdm_type* only if the configuration
has no server CDM for the service. has no server CDM for the service.
@@ -3926,7 +3927,8 @@ def resolve_server_cdm(service: str, profile: Optional[str], cdm_type: Optional[
if isinstance(cdm_name, dict): if isinstance(cdm_name, dict):
lower_keys = {k.lower(): v for k, v in cdm_name.items()} lower_keys = {k.lower(): v for k, v in cdm_name.items()}
if {"widevine", "playready"} & lower_keys.keys(): if {"widevine", "playready"} & lower_keys.keys():
cdm_name = lower_keys.get("playready") or lower_keys.get("widevine") systems = [lower_keys[key] for key in lower_keys if key in ("widevine", "playready")]
cdm_name = next(filter(None, systems), None)
else: else:
cdm_name = cdm_name.get("default") or next(iter(cdm_name.values()), None) cdm_name = cdm_name.get("default") or next(iter(cdm_name.values()), None)