feat(cdm): Add fallback to Widevine common cert for L1 devices

- Use default Widevine common privacy certificate when no service certificate is provided for L1 devices
- Add get_widevine_service_certificate method to EXAMPLE service for config-based certificates
- Improve certificate handling with more descriptive return messages
This commit is contained in:
Andy
2025-09-06 20:30:11 +00:00
parent e462f07b7a
commit ad66502c0c
2 changed files with 12 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional, Union
from uuid import UUID from uuid import UUID
import requests import requests
from pywidevine.cdm import Cdm as WidevineCdm
from pywidevine.device import DeviceTypes from pywidevine.device import DeviceTypes
from requests import Session from requests import Session
@@ -308,8 +309,13 @@ class DecryptLabsRemoteCDM:
raise DecryptLabsRemoteCDMExceptions.InvalidSession(f"Invalid session ID: {session_id.hex()}") raise DecryptLabsRemoteCDMExceptions.InvalidSession(f"Invalid session ID: {session_id.hex()}")
if certificate is None: if certificate is None:
if not self._is_playready and self.device_name == "L1":
certificate = WidevineCdm.common_privacy_cert
self._sessions[session_id]["service_certificate"] = base64.b64decode(certificate)
return "Using default Widevine common privacy certificate for L1"
else:
self._sessions[session_id]["service_certificate"] = None self._sessions[session_id]["service_certificate"] = None
return "Removed" return "No certificate set (not required for this device type)"
if isinstance(certificate, str): if isinstance(certificate, str):
certificate = base64.b64decode(certificate) certificate = base64.b64decode(certificate)

View File

@@ -282,6 +282,10 @@ class EXAMPLE(Service):
return chapters return chapters
def get_widevine_service_certificate(self, **_: any) -> str:
"""Return the Widevine service certificate from config, if available."""
return self.config.get("certificate")
def get_playready_license(self, *, challenge: bytes, title: Title_T, track: AnyTrack) -> Optional[bytes]: def get_playready_license(self, *, challenge: bytes, title: Title_T, track: AnyTrack) -> Optional[bytes]:
"""Retrieve a PlayReady license for a given track.""" """Retrieve a PlayReady license for a given track."""