mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-06-24 01:47:23 +00:00
feat(drm): add native DASH ClearKey (org.w3.clearkey) support
unshackle's DASH parser only recognised Widevine and PlayReady ContentProtection, so services using W3C EME ClearKey had to fake a Widevine object and monkey-patch get_content_keys. Add a first-class ClearKeyCENC DRM type so services just implement a license callback. - ClearKeyCENC (core/drm/clearkey_cenc.py): KID-based, no CDM/PSSH; builds the W3C JSON license request (unpadded base64url), parses the JWK Set response (dict/str/bytes), falls back to POSTing the manifest Laurl when the service returns None, decrypts via the same shaka/ mp4decrypt CENC path as Widevine - DASH.get_drm emits ClearKeyCENC for scheme e2719d58-...; KID from own or sibling mp4protection cenc:default_KID, Laurl across dashif/legacy/ bare namespaces - track.download dispatches prepare_drm for ClearKeyCENC; dl.prepare_drm gains a clearkey branch (cache/vault lookup, license-failure tolerated when content_keys pre-populated, vault push, export) - Service.get_clearkey_license base callback (default None -> Laurl); drm_from_dict reconstructs ClearKeyCENC for export/import round-trip - EXAMPLE service + config demo the callback - Tests: tests/core/test_clearkey_cenc.py and an export round-trip case - Docs: DRM_CONFIG.md ClearKey section
This commit is contained in:
@@ -64,7 +64,7 @@ class EXAMPLE(Service):
|
||||
get_chapters Chapters() with named + unnamed markers
|
||||
get_widevine_* service cert + license (per-segment PSSH via `track`)
|
||||
get_playready_license PlayReady challenge POST
|
||||
get_clearkey DRM-free / ClearKey fallback (commented alternate)
|
||||
get_clearkey_license DASH org.w3.clearkey JWK Set POST (Laurl fallback)
|
||||
"""
|
||||
|
||||
# ALIASES: extra CLI tags that resolve to this service (e.g. `dl EX ...`).
|
||||
@@ -486,7 +486,20 @@ class EXAMPLE(Service):
|
||||
response.raise_for_status()
|
||||
return response.content
|
||||
|
||||
# For ClearKey or unencrypted content there is no license callback; instead the
|
||||
# KID:KEY pair comes from the manifest or a side endpoint and is attached to the
|
||||
def get_clearkey_license(
|
||||
self, *, challenge: bytes, title: Title_T, track: AnyTrack
|
||||
) -> Optional[Union[bytes, str, dict]]:
|
||||
# DASH org.w3.clearkey: `challenge` is the W3C JSON license request; return the
|
||||
# JWK Set response. Omit this method entirely when the manifest carries a Laurl —
|
||||
# the framework then POSTs the challenge there with no service code at all.
|
||||
license_url = self.config["endpoints"].get("clearkey_license")
|
||||
if not license_url:
|
||||
return None # fall back to the manifest-provided Laurl, if any
|
||||
response = self.session.post(url=license_url, data=challenge)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
# For HLS AES-128 ClearKey or unencrypted content there is no license callback;
|
||||
# the key comes from the manifest or a side endpoint and is attached to the
|
||||
# track's DRM directly. Vaults (`self.cache` is separate) cache KID:KEY so repeat
|
||||
# downloads skip the license round-trip entirely.
|
||||
|
||||
Reference in New Issue
Block a user