From cc7263884f442210ae4c3881d28f758f9a4597ff Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 8 Nov 2025 03:00:19 +0000 Subject: [PATCH] fix(cdm): resolve session key handling for partial cached keys When decrypt-labs returns cached keys that don't cover all required KIDs, the CDM now properly stores them in session["cached_keys"] instead of session["keys"]. This allows parse_license() to correctly combine vault_keys + cached_keys + license_keys, fixing downloads that previously failed when mixing cached and fresh licenses. --- unshackle/core/cdm/decrypt_labs_remote_cdm.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/unshackle/core/cdm/decrypt_labs_remote_cdm.py b/unshackle/core/cdm/decrypt_labs_remote_cdm.py index 8645b4e..3806107 100644 --- a/unshackle/core/cdm/decrypt_labs_remote_cdm.py +++ b/unshackle/core/cdm/decrypt_labs_remote_cdm.py @@ -474,7 +474,6 @@ class DecryptLabsRemoteCDM: if "vault_keys" in session: all_available_keys.extend(session["vault_keys"]) - session["keys"] = all_available_keys session["tried_cache"] = True if self._required_kids: @@ -505,10 +504,7 @@ class DecryptLabsRemoteCDM: license_request_data = request_data.copy() license_request_data["get_cached_keys_if_exists"] = False - session["decrypt_labs_session_id"] = None - session["challenge"] = None - session["tried_cache"] = False - + # Make license request for missing keys response = self._http_session.post( f"{self.host}/get-request", json=license_request_data, timeout=30 ) @@ -522,8 +518,12 @@ class DecryptLabsRemoteCDM: return b"" else: + # All required keys are available from cache + session["keys"] = all_available_keys return b"" else: + # No required KIDs specified - return cached keys + session["keys"] = all_available_keys return b"" if message_type == "license-request" or "challenge" in data: @@ -572,7 +572,9 @@ class DecryptLabsRemoteCDM: session = self._sessions[session_id] - if session["keys"] and not (self.is_playready and "cached_keys" in session): + # Skip parsing if we already have final keys (no cached keys to combine) + # If cached_keys exist (Widevine or PlayReady), we need to combine them with license keys + if session["keys"] and "cached_keys" not in session: return if not session.get("challenge") or not session.get("decrypt_labs_session_id"):