feat(cdm): Enhance DecryptLabsRemoteCDM to support cached keys and improve license handling

This commit is contained in:
Andy
2025-08-28 17:09:55 +00:00
parent eb30620626
commit b4efdf3f2c

View File

@@ -26,7 +26,9 @@ class DecryptLabsRemoteCDM(RemoteCdm):
self.api_session_ids = {}
self.license_request = None
self.service_name = service_name
self.device_name = device_name
self.keys = {}
self.scheme = "L1" if device_name == "L1" else "widevine"
try:
super().__init__(device_type, system_id, security_level, host, secret, device_name)
except Exception:
@@ -66,16 +68,49 @@ class DecryptLabsRemoteCDM(RemoteCdm):
) -> bytes:
self.pssh = pssh
res = self.session(
self.host + "/get-request",
{
request_data = {
"init_data": self.pssh.dumps(),
"service_certificate": self.req_session.signed_device_certificate,
"scheme": "widevine",
"scheme": self.scheme,
"service": self.service_name,
},
}
# Add required parameter for L1 scheme
if self.scheme == "L1":
request_data["get_cached_keys_if_exists"] = True
res = self.session(
self.host + "/get-request",
request_data,
)
# Check if we got cached keys instead of a challenge
if res.get("message_type") == "cached-keys":
# Store cached keys directly
if session_id not in self.keys:
self.keys[session_id] = []
session_keys = self.keys[session_id]
for cached_key in res.get("cached_keys", []):
# Handle KID format - could be hex string or UUID string
kid_str = cached_key["kid"]
try:
# Try as UUID string first
kid_uuid = UUID(kid_str)
except ValueError:
try:
# Try as hex string (like the existing code)
kid_uuid = UUID(bytes=bytes.fromhex(kid_str))
except ValueError:
# Fallback: use Key.kid_to_uuid
kid_uuid = Key.kid_to_uuid(kid_str)
session_keys.append(Key(kid=kid_uuid, type_="CONTENT", key=bytes.fromhex(cached_key["key"])))
# Return empty challenge since we already have the keys
self.license_request = ""
self.api_session_ids[session_id] = None
return b""
# Normal challenge response
self.license_request = res["challenge"]
self.api_session_ids[session_id] = res["session_id"]
@@ -87,6 +122,10 @@ class DecryptLabsRemoteCDM(RemoteCdm):
self.keys[session_id] = []
session_keys = self.keys[session_id]
# If we already have cached keys and no session_id_api, skip processing
if session_id_api is None and session_keys:
return
if isinstance(license_message, dict) and "keys" in license_message:
session_keys.extend(
[
@@ -96,14 +135,21 @@ class DecryptLabsRemoteCDM(RemoteCdm):
)
else:
# Ensure license_message is base64 encoded
if isinstance(license_message, bytes):
license_response_b64 = base64.b64encode(license_message).decode()
elif isinstance(license_message, str):
license_response_b64 = license_message
else:
license_response_b64 = str(license_message)
res = self.session(
self.host + "/decrypt-response",
{
"session_id": session_id_api,
"init_data": self.pssh.dumps(),
"license_request": self.license_request,
"license_response": license_message,
"scheme": "widevine",
"license_response": license_response_b64,
"scheme": self.scheme,
},
)