mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-18 23:22:38 +02:00
183 lines
6.4 KiB
Python
183 lines
6.4 KiB
Python
# streaming_providers/base/models/drm_models.py
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, Optional, List
|
|
import base64
|
|
from enum import Enum
|
|
|
|
class DRMSystem(str, Enum):
|
|
WIDEVINE = "com.widevine.alpha"
|
|
PLAYREADY = "com.microsoft.playready"
|
|
WISEPLAY = "com.huawei.wiseplay"
|
|
CLEARKEY = "org.w3.clearkey"
|
|
FAIRPLAY = "com.apple.fps"
|
|
GENERIC = "generic"
|
|
|
|
@property
|
|
def system_uuid(self) -> str:
|
|
"""Get the standard UUID for this DRM system"""
|
|
uuid_mapping = {
|
|
self.WIDEVINE: "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
|
|
self.PLAYREADY: "9a04f079-9840-4286-ab92-e65be0885f95",
|
|
self.CLEARKEY: "e2719d58-a985-b3c9-781a-b030af78d30e",
|
|
self.WISEPLAY: "3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c",
|
|
self.FAIRPLAY: "94ce86fb-07ff-4f43-adb8-93d2fa968ca2",
|
|
self.GENERIC: "" # No UUID for generic plugins
|
|
}
|
|
return uuid_mapping.get(self, "")
|
|
|
|
@classmethod
|
|
def from_uuid(cls, uuid: str) -> Optional['DRMSystem']:
|
|
"""Get DRM system from UUID"""
|
|
uuid_lower = uuid.lower().replace("-", "")
|
|
uuid_mapping = {
|
|
"edef8ba979d64acea3c827dcd51d21ed": cls.WIDEVINE,
|
|
"9a04f07998404286ab92e65be0885f95": cls.PLAYREADY,
|
|
"e2719d58a985b3c9781ab030af78d30e": cls.CLEARKEY,
|
|
"3d5e6d359b9a41e8b843dd3c6e72c42c": cls.WISEPLAY
|
|
}
|
|
return uuid_mapping.get(uuid_lower)
|
|
|
|
class WrapperType(str, Enum):
|
|
BASE64 = "base64"
|
|
URLENC = "urlenc"
|
|
NONE = "none"
|
|
|
|
class UnwrapperType(str, Enum):
|
|
AUTO = "auto"
|
|
BASE64 = "base64"
|
|
JSON = "json"
|
|
XML = "xml"
|
|
NONE = "none"
|
|
|
|
@dataclass
|
|
class PSSHData:
|
|
"""
|
|
Protection System Specific Header data for DRM systems
|
|
"""
|
|
system_id: str # UUID of the DRM system
|
|
pssh_box: str # Base64 encoded PSSH box data
|
|
key_ids: List[str] = field(default_factory=list) # Optional key IDs
|
|
|
|
@property
|
|
def drm_system(self) -> Optional[DRMSystem]:
|
|
"""Get the corresponding DRM system for this PSSH"""
|
|
return DRMSystem.from_uuid(self.system_id)
|
|
|
|
def validate(self):
|
|
"""Validate the PSSH data"""
|
|
if not self.system_id:
|
|
raise ValueError("system_id is required")
|
|
|
|
if not self.pssh_box:
|
|
raise ValueError("pssh_box is required")
|
|
|
|
try:
|
|
base64.b64decode(self.pssh_box)
|
|
except Exception:
|
|
raise ValueError("pssh_box must be valid base64")
|
|
|
|
# Validate key IDs if present
|
|
for kid in self.key_ids:
|
|
if not all(c in "0123456789abcdefABCDEF-" for c in kid):
|
|
raise ValueError(f"Invalid key ID format: {kid}")
|
|
|
|
@dataclass
|
|
class LicenseUnwrapperParams:
|
|
path_data: Optional[str] = None
|
|
path_data_traverse: bool = False
|
|
path_hdcp_res: Optional[str] = None
|
|
path_hdcp_res_traverse: bool = False
|
|
path_hdcp_ver: Optional[str] = None
|
|
path_hdcp_ver_traverse: bool = False
|
|
|
|
@dataclass
|
|
class LicenseConfig:
|
|
server_url: Optional[str] = None
|
|
server_certificate: Optional[str] = None
|
|
use_http_get_request: bool = False
|
|
req_headers: Optional[str] = None
|
|
req_params: Optional[str] = None
|
|
req_data: Optional[str] = None
|
|
wrapper: Optional[str] = None
|
|
unwrapper: Optional[str] = None
|
|
unwrapper_params: Optional[LicenseUnwrapperParams] = None
|
|
keyids: Dict[str, str] = field(default_factory=dict) # For ClearKey
|
|
|
|
def validate(self):
|
|
"""Validate the license configuration"""
|
|
if self.server_certificate:
|
|
try:
|
|
base64.b64decode(self.server_certificate)
|
|
except Exception:
|
|
raise ValueError("server_certificate must be valid base64")
|
|
|
|
if self.req_data:
|
|
try:
|
|
base64.b64decode(self.req_data)
|
|
except Exception:
|
|
raise ValueError("req_data must be valid base64")
|
|
|
|
if self.keyids:
|
|
for kid, key in self.keyids.items():
|
|
if not all(c in "0123456789abcdefABCDEF" for c in kid):
|
|
raise ValueError(f"Invalid KID format: {kid}")
|
|
if not all(c in "0123456789abcdefABCDEF" for c in key):
|
|
raise ValueError(f"Invalid KEY format: {key}")
|
|
|
|
@classmethod
|
|
def create_with_base64_req_data(cls, req_data_template: str, **kwargs):
|
|
"""Helper to ensure req_data is base64 encoded"""
|
|
import base64
|
|
req_data_encoded = base64.b64encode(req_data_template.encode('utf-8')).decode('utf-8')
|
|
return cls(req_data=req_data_encoded, **kwargs)
|
|
|
|
@dataclass
|
|
class DRMConfig:
|
|
system: DRMSystem
|
|
priority: int = 0
|
|
license: Optional[LicenseConfig] = None
|
|
|
|
def validate(self):
|
|
"""Validate the DRM configuration"""
|
|
if self.license:
|
|
self.license.validate()
|
|
|
|
def to_dict(self) -> Dict:
|
|
"""Convert to dictionary format expected by players"""
|
|
result = {
|
|
str(self.system.value): { # Use .value to get the actual string
|
|
"priority": self.priority
|
|
}
|
|
}
|
|
|
|
if self.license:
|
|
license_dict = {}
|
|
if self.license.server_url:
|
|
license_dict["server_url"] = self.license.server_url
|
|
if self.license.server_certificate:
|
|
license_dict["server_certificate"] = self.license.server_certificate
|
|
if self.license.use_http_get_request:
|
|
license_dict["use_http_get_request"] = self.license.use_http_get_request
|
|
if self.license.req_headers:
|
|
license_dict["req_headers"] = self.license.req_headers
|
|
if self.license.req_params:
|
|
license_dict["req_params"] = self.license.req_params
|
|
if self.license.req_data:
|
|
license_dict["req_data"] = self.license.req_data
|
|
if self.license.wrapper:
|
|
license_dict["wrapper"] = self.license.wrapper
|
|
if self.license.unwrapper:
|
|
license_dict["unwrapper"] = self.license.unwrapper
|
|
if self.license.unwrapper_params:
|
|
license_dict["unwrapper_params"] = {
|
|
k: v for k, v in vars(self.license.unwrapper_params).items()
|
|
if v is not None
|
|
}
|
|
if self.license.keyids:
|
|
license_dict["keyids"] = self.license.keyids
|
|
|
|
if license_dict:
|
|
result[str(self.system.value)]["license"] = license_dict
|
|
|
|
return result
|