"""Regression test for DASH ``org.w3.clearkey`` licensing in ``DASH.download_track``. The download path gated content-key licensing on ``isinstance(drm, (Widevine, PlayReady))``, so a ClearKeyCENC track never reached its license callback, left ``content_keys`` empty, and aborted at ``drm.decrypt`` with "Cannot decrypt a Track without any Content Keys". This drives the real ``download_track`` under ``DOWNLOAD_LICENCE_ONLY`` (returns straight after licensing, no segment fetch or decrypt) and asserts the ClearKeyCENC drm is licensed. """ from __future__ import annotations from functools import partial from typing import Any from uuid import UUID from unshackle.core.constants import DOWNLOAD_LICENCE_ONLY from unshackle.core.drm.clearkey_cenc import ClearKeyCENC from unshackle.core.manifests.dash import DASH from unshackle.core.tracks.track import DownloadContext KID = UUID("9eb4050d-e44b-4802-932e-27d75083e266") KEY = bytes.fromhex("ccd0064c43f7e9fcbaa9b12af3fd1f40") LAURL = "https://license.example.test/clearkey" MANIFEST_URL = "https://example.test/manifest.mpd" MPD = f""" {LAURL} """ class RecordingLicence: """License callback stub: records its call and populates content keys.""" def __init__(self) -> None: self.calls: list[tuple[Any, Any]] = [] def __call__(self, drm: Any, track_kid: Any = None) -> None: self.calls.append((drm, track_kid)) for kid in drm.kids: drm.content_keys[kid] = KEY.hex() def licence_only(track: Any, tmp_path: Any) -> RecordingLicence: """Run download_track as far as licensing and return the licence callback it used.""" licence = RecordingLicence() ctx = DownloadContext( save_path=tmp_path / "video.mp4", save_dir=tmp_path / "video_segments", progress=partial(lambda **_: None), license_widevine=licence, cdm=None, ) DOWNLOAD_LICENCE_ONLY.set() try: DASH.download_track(track=track, ctx=ctx) finally: DOWNLOAD_LICENCE_ONLY.clear() return licence def test_download_track_licenses_clearkey_cenc(tmp_path, monkeypatch) -> None: track = DASH.from_text(MPD, MANIFEST_URL).to_tracks().videos[0] # KID normally comes from the init segment; short-circuit the probe (offline). monkeypatch.setattr(track, "get_key_id", lambda *args, **kwargs: KID) licence = licence_only(track, tmp_path) assert len(licence.calls) == 1, "ClearKeyCENC track was never licensed" drm, track_kid = licence.calls[0] assert isinstance(drm, ClearKeyCENC) assert track_kid == KID # with content keys populated, decrypt has what it needs assert drm.content_keys == {KID: KEY.hex()} def test_download_track_keeps_service_drm_when_manifest_has_none(tmp_path, monkeypatch) -> None: """A service-set non-Widevine DRM survives a manifest with no ContentProtection, and is licensed.""" service_drm = [ClearKeyCENC([KID], LAURL)] bare_mpd = MPD.split("")[1] track = DASH.from_text(bare_mpd, MANIFEST_URL).to_tracks().videos[0] track.drm = service_drm monkeypatch.setattr(track, "get_key_id", lambda *args, **kwargs: KID) licence = licence_only(track, tmp_path) assert track.drm == service_drm assert len(licence.calls) == 1