diff --git a/docs/reference/configuration/drm.md b/docs/reference/configuration/drm.md index 4170b4cc..1d53575f 100644 --- a/docs/reference/configuration/drm.md +++ b/docs/reference/configuration/drm.md @@ -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 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 -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 top-level `default` entry: diff --git a/tests/core/test_all_drm.py b/tests/core/test_all_drm.py index ce5079af..6c4a6ac4 100644 --- a/tests/core/test_all_drm.py +++ b/tests/core/test_all_drm.py @@ -17,7 +17,7 @@ from pywidevine.pssh import PSSH from rich.table import Table 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.drm import PlayReady, Widevine 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 load_full_cdm("EXAMPLE", None) == expected 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") diff --git a/unshackle/core/api/handlers.py b/unshackle/core/api/handlers.py index c13c3b27..2a52f66d 100644 --- a/unshackle/core/api/handlers.py +++ b/unshackle/core/api/handlers.py @@ -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 determine the CDM type without loading the full CDM object. This 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 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): lower_keys = {k.lower(): v for k, v in cdm_name.items()} 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: cdm_name = cdm_name.get("default") or next(iter(cdm_name.values()), None)