feat(remote): match the server CDM to the client's DRM and read vaults first

A dual widevine/playready cdm entry now loads the device of the client's DRM system, for old clients too since they already send cdm_type. A new client hands a capped track to its own device for the track's DRM system, and raises the refusal only after its local vaults miss.
This commit is contained in:
imSp4rky
2026-11-03 10:51:29 -07:00
parent b710b5b0c9
commit b6429ecdfd
12 changed files with 309 additions and 68 deletions
@@ -13,7 +13,7 @@ from uuid import UUID
import pytest
from rich.table import Table
from tests.remote.key_source import remote_keys
from tests.remote.key_source import CAPPED, PR_DEVICE, one_title, remote_keys, remote_session
from unshackle.commands.dl import dl
pytestmark = pytest.mark.unit
@@ -114,6 +114,41 @@ def test_keys_read_from_a_vault_are_not_pushed_back():
assert vaults.added == {NEW: "k_new"}
def capped_run(vaults):
"""A batch licence refuses the track for a local PlayReady device, then dl prepares its Widevine DRM."""
cmd = make_cmd(vaults)
svc = remote_session(PR_DEVICE, CAPPED)
cmd._remote_service = svc
drm = type("Widevine", (FakeWidevine,), {})([CACHED])
track = SimpleNamespace(id="vid", height=2160, drm=[drm], drm_preference=None)
svc.resolve_server_keys(one_title(track))
table = Table.grid()
table.add_row("")
cmd.prepare_drm(
track=track,
title=None,
drm=drm,
licence=lambda **kwargs: svc.proxy_license(b"", track, "widevine"),
certificate=None,
track_kid=CACHED,
table=table,
)
return drm
def test_a_capped_track_takes_a_local_vault_key_before_the_refusal():
assert capped_run(FakeVaults({CACHED: "k_cached"})).content_keys == {CACHED: "k_cached"}
def test_a_capped_track_no_vault_holds_raises_the_refusal():
import click
with pytest.raises(click.ClickException, match="only Widevine DRM for your PlayReady device"):
capped_run(FakeVaults())
def test_batch_resolved_keys_are_cached_before_download():
"""The batch licence fills track.drm before prepare_drm, so dl caches it at the call site."""
vaults = FakeVaults()