mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-27 19:42:37 +02:00
343 lines
12 KiB
Python
343 lines
12 KiB
Python
# streaming_providers/providers/discovery/constants.py
|
|
"""
|
|
Discovery+ Provider Constants
|
|
|
|
All configuration values for the Discovery+ streaming provider.
|
|
"""
|
|
from enum import Enum
|
|
from typing import Dict, List, Final
|
|
|
|
|
|
# ============================================================================
|
|
# Enums for Type Safety
|
|
# ============================================================================
|
|
|
|
class StreamingMode(str, Enum):
|
|
"""Streaming modes"""
|
|
LIVE = "live"
|
|
VOD = "vod"
|
|
|
|
|
|
class CDMMode(str, Enum):
|
|
"""CDM (Content Decryption Module) modes"""
|
|
EXTERNAL = "external"
|
|
INTERNAL = "internal"
|
|
|
|
|
|
class VideoQuality(str, Enum):
|
|
"""Video quality settings"""
|
|
BEST = "best"
|
|
HD = "hd"
|
|
SD = "sd"
|
|
|
|
|
|
class DRMSystem(str, Enum):
|
|
"""DRM systems"""
|
|
WIDEVINE = "widevine"
|
|
CLEARKEY = "clearkey"
|
|
|
|
|
|
class AuthProvider(str, Enum):
|
|
"""Authentication providers"""
|
|
USERNAME_PASSWORD = "USERNAME_PASSWORD"
|
|
ANONYMOUS = "anonymous"
|
|
|
|
|
|
# ============================================================================
|
|
# Basic Configuration
|
|
# ============================================================================
|
|
|
|
# Provider information
|
|
DISCOVERY_LOGO: Final[
|
|
str] = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Discovery%2B_logo.svg/2560px-Discovery%2B_logo.svg.png"
|
|
|
|
# Bootstrap endpoint for dynamic configuration
|
|
DISCOVERY_BOOTSTRAP_URL: Final[
|
|
str] = "https://default.any-any.prd.api.discoveryplus.com/session-context/headwaiter/v1/bootstrap"
|
|
|
|
# Default realm
|
|
DEFAULT_REALM: Final[str] = "bolt"
|
|
|
|
# ============================================================================
|
|
# Authentication Configuration
|
|
# ============================================================================
|
|
|
|
# Supported auth types
|
|
SUPPORTED_AUTH_TYPES: Final[List[str]] = [
|
|
"anonymous", # Anonymous token
|
|
"user_credentials", # Username/password login
|
|
]
|
|
|
|
# ============================================================================
|
|
# Country/Region Configuration
|
|
# ============================================================================
|
|
|
|
# Supported countries (EMEA region)
|
|
SUPPORTED_COUNTRIES: Final[List[str]] = [
|
|
"de", "at", "ch", "dk", "fi", "no", "se", "it", "nl", "es", "uk", "ie",
|
|
]
|
|
|
|
# Home market mapping (immutable)
|
|
HOME_MARKET_MAPPING: Final[Dict[str, str]] = {
|
|
"de": "emea", "at": "emea", "ch": "emea",
|
|
"dk": "emea", "fi": "emea", "no": "emea", "se": "emea",
|
|
"it": "emea", "nl": "emea", "es": "emea",
|
|
"uk": "uk", "ie": "uk",
|
|
}
|
|
|
|
# Tenant
|
|
DEFAULT_TENANT: Final[str] = "dplus"
|
|
|
|
# Environment
|
|
DEFAULT_ENV: Final[str] = "prd"
|
|
|
|
# Domain
|
|
DEFAULT_DOMAIN: Final[str] = "api.discoveryplus.com"
|
|
|
|
# Default country
|
|
DEFAULT_COUNTRY: Final[str] = "de"
|
|
|
|
# ============================================================================
|
|
# CMS Configuration
|
|
# ============================================================================
|
|
|
|
CMS_INCLUDE_PARAMS: Final[str] = "default"
|
|
CMS_PAGE_SIZE: Final[int] = 50
|
|
|
|
# Known collection IDs for TV channels (will be discovered dynamically, but these are fallbacks)
|
|
CHANNEL_COLLECTIONS: Final[Dict[str, List[str]]] = {
|
|
"de": [
|
|
"158509151486067887066538918357725817419", # "Unsere TV-Kanäle" (Germany)
|
|
"191019300316436018732917730141471355952", # DMAX Austria
|
|
],
|
|
"at": [
|
|
"191019300316436018732917730141471355952", # DMAX Austria
|
|
],
|
|
"ch": [],
|
|
"uk": [],
|
|
"ie": [],
|
|
}
|
|
|
|
# Collection item types that represent channels
|
|
CHANNEL_ITEM_TYPES: Final[List[str]] = ["distributionChannel", "channel", "linearChannel", "liveChannel"]
|
|
|
|
# ============================================================================
|
|
# User Agent Configuration
|
|
# ============================================================================
|
|
|
|
DISCOVERY_USER_AGENT: Final[
|
|
str] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
|
|
|
# ============================================================================
|
|
# Device Configuration
|
|
# ============================================================================
|
|
|
|
# Device ID (consistent across sessions for anonymous tracking)
|
|
DEFAULT_DEVICE_ID: Final[str] = "a2f463fa-2052-4af1-ae16-26d8289c6b94"
|
|
|
|
# ============================================================================
|
|
# Client / Header Configuration
|
|
# ============================================================================
|
|
|
|
# Client version string — used in x-disco-client and x-device-info headers
|
|
DISCOVERY_CLIENT_VERSION: Final[str] = "6.14.0"
|
|
|
|
# Full x-disco-client header value — WEB:{os_version}:dplus:{client_version}
|
|
# OS version is "0.0.0" for web platform
|
|
|
|
# x-disco-params header value
|
|
DISCOVERY_DISCO_PARAMS: Final[str] = "realm=bolt,bid=dplus,features=ar"
|
|
|
|
# x-device-info header template — format with device_id and session_id
|
|
DISCOVERY_DEVICE_INFO_TEMPLATE: Final[str] = (
|
|
f"dplus/{DISCOVERY_CLIENT_VERSION} (desktop/desktop; Linux/x86_64; {{device_id}}/{{session_id}})"
|
|
)
|
|
|
|
# x-wbd-device-consent header value
|
|
DISCOVERY_DEVICE_CONSENT: Final[str] = "gpc=0"
|
|
|
|
# Default timezone for x-wbd-time-zone header
|
|
DISCOVERY_DEFAULT_TIMEZONE: Final[str] = "Europe/Berlin"
|
|
|
|
# Origin / Referer for auth requests (login, bootstrap)
|
|
DISCOVERY_AUTH_ORIGIN: Final[str] = "https://auth.discoveryplus.com"
|
|
DISCOVERY_AUTH_REFERER: Final[str] = "https://auth.discoveryplus.com/"
|
|
|
|
# Arkose (FunCaptcha) — required before POST /login
|
|
DISCOVERY_ARKOSE_SITEKEY: Final[str] = "09CB1E22-0FB7-4AD8-957C-4F06CAFB3C7E"
|
|
DISCOVERY_ARKOSE_DATA_URL: Final[str] = (
|
|
"https://default.dplus-emea.prd.api.discoveryplus.com/users/arkose/data"
|
|
)
|
|
DISCOVERY_ARKOSE_DATA_PAYLOAD: Final[Dict] = {
|
|
"data": {
|
|
"attributes": {"relativePath": "/login", "styleTheme": "dplus"},
|
|
"type": "arkoseDataExchange",
|
|
}
|
|
}
|
|
# Arkose FunCaptcha token exchange — Discovery+ hosts their own FC endpoint
|
|
DISCOVERY_ARKOSE_FC_URL: Final[str] = (
|
|
f"https://a4gds3vfh.discoveryplus.com/fc/gt2/public_key/{DISCOVERY_ARKOSE_SITEKEY}"
|
|
)
|
|
|
|
# ============================================================================
|
|
# HMAC / Client ID Configuration
|
|
# ============================================================================
|
|
|
|
# HMAC key for web platform (base64-decoded from feature flags response)
|
|
# Raw b64: NTVlZWExODktZTliNi00NzlmLWJjNTEtMjIyNGNmZGE1NmZl
|
|
DISCOVERY_HMAC_KEY: Final[str] = "55eea189-e9b6-479f-bc51-2224cfda56fe"
|
|
|
|
# x-disco-client-id prefix matches hmacKeys "id" field for web
|
|
DISCOVERY_CLIENT_ID_PREFIX: Final[str] = "web1_prd"
|
|
|
|
# x-disco-client format: WEB:{os_version}:dplus:{client_version}
|
|
# OS version is "0.0.0" for web (not x86_64 as previously assumed)
|
|
DISCOVERY_DISCO_CLIENT: Final[str] = f"WEB:0.0.0:dplus:{DISCOVERY_CLIENT_VERSION}"
|
|
|
|
# Feature flags endpoint — provides hmacKeys, gisdk clientId, arkose config etc.
|
|
# x-gisdk clientId comes from the response and is session-specific.
|
|
DISCOVERY_FEATURE_FLAGS_URL: Final[str] = (
|
|
"https://default.any-any.prd.api.discoveryplus.com/labs/api/v1/sessions/feature-flags/decisions"
|
|
)
|
|
DISCOVERY_FEATURE_FLAGS_PAYLOAD: Final[Dict] = {
|
|
"context": {"deviceType": "desktop", "domain": "discoveryplus.com"},
|
|
"projectId": "7e52f0d0-d8b5-4eda-983b-597e4e2102a2",
|
|
}
|
|
|
|
|
|
def get_default_device_info() -> Dict[str, any]:
|
|
"""
|
|
Return a fresh copy of default device info.
|
|
|
|
Returns:
|
|
Dictionary containing device information
|
|
"""
|
|
return {
|
|
"deviceId": DEFAULT_DEVICE_ID,
|
|
"browser": {"name": "Chrome", "version": "144.0.0.0"},
|
|
"make": "desktop",
|
|
"model": "desktop",
|
|
"os": {"name": "Linux", "version": "0.0.0"},
|
|
"platform": "WEB",
|
|
"deviceType": "web",
|
|
"player": {
|
|
"sdk": {"name": "Beam Player Desktop", "version": "6.14.0"},
|
|
"mediaEngine": {
|
|
"name": "GLUON_BROWSER",
|
|
"version": "7.0.0",
|
|
"labsConfigVersion": "playback_engine_gluon@web-1.2.0,playback_engine_gluon_disable_network_request_event@default-1.0.0,playback_engine_gluon_segment_download_during_drminit@1.0.0,playback_engine_gluon_abr_protocol_hybrid@1.0.0,playback_engine_gluon_initial_track_selection@1.0.0",
|
|
},
|
|
"playerView": {"height": 1080, "width": 1920},
|
|
},
|
|
}
|
|
|
|
|
|
def get_default_capabilities() -> Dict[str, any]:
|
|
"""
|
|
Return a fresh copy of default capabilities.
|
|
|
|
Returns:
|
|
Dictionary containing device capabilities
|
|
"""
|
|
return {
|
|
"manifests": {"formats": {"dash": {}}},
|
|
"codecs": {
|
|
"audio": {"decoders": [{"codec": "aac", "profiles": ["lc", "hev", "hev2"]}]},
|
|
"video": {
|
|
"decoders": [
|
|
{
|
|
"codec": "h264",
|
|
"profiles": ["high", "main", "baseline"],
|
|
"maxLevel": "5.2",
|
|
"levelConstraints": {
|
|
"width": {"min": 0, "max": 973},
|
|
"height": {"min": 0, "max": 919},
|
|
"framerate": {"min": 0, "max": 60},
|
|
},
|
|
},
|
|
{
|
|
"codec": "h265",
|
|
"profiles": ["main10", "main"],
|
|
"maxLevel": "5.2",
|
|
"levelConstraints": {
|
|
"width": {"min": 0, "max": 973},
|
|
"height": {"min": 0, "max": 919},
|
|
"framerate": {"min": 0, "max": 60},
|
|
},
|
|
},
|
|
],
|
|
"hdrFormats": [],
|
|
},
|
|
},
|
|
"contentProtection": {
|
|
"contentDecryptionModules": [
|
|
{"drmKeySystem": "clearkey"},
|
|
{"drmKeySystem": "widevine", "maxSecurityLevel": "l3"},
|
|
]
|
|
},
|
|
"devicePlatform": {
|
|
"memory": {"allocatedMemory": 0, "freeAvailableMemory": 1.7976931348623157e308},
|
|
"network": {
|
|
"capabilities": {"protocols": {"http": {"byteRangeRequests": True}}},
|
|
"lastKnownStatus": {"networkTransportType": "unknown"},
|
|
},
|
|
"videoSink": {
|
|
"capabilities": {"colorGamuts": ["standard"], "hdrFormats": []},
|
|
"lastKnownStatus": {"height": 1080, "width": 1920},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# Playback Configuration
|
|
# ============================================================================
|
|
|
|
DEFAULT_PLAYBACK_SESSION_ID: Final[str] = "fb20e22e-abcd-490a-a83d-8c71f79e7435"
|
|
DEFAULT_APPLICATION_SESSION_ID: Final[str] = "74985592-a061-4597-ab5b-51492d0fc2c6"
|
|
|
|
# ============================================================================
|
|
# DRM Configuration
|
|
# ============================================================================
|
|
|
|
DRM_SYSTEM_WIDEVINE: Final[str] = DRMSystem.WIDEVINE.value
|
|
|
|
|
|
def get_drm_request_headers() -> Dict[str, str]:
|
|
"""
|
|
Return a fresh copy of DRM request headers.
|
|
|
|
Returns:
|
|
Dictionary containing DRM request headers
|
|
"""
|
|
return {
|
|
"Content-Type": "application/octet-stream",
|
|
"User-Agent": DISCOVERY_USER_AGENT,
|
|
"Origin": "https://play.discoveryplus.com",
|
|
"Referer": "https://play.discoveryplus.com/",
|
|
"traceparent": "00-aba275155d9ed5c2b54d576682cb30e6-ddfd854be17b3a6e-01",
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# Request Configuration
|
|
# ============================================================================
|
|
|
|
DEFAULT_REQUEST_TIMEOUT: Final[int] = 30
|
|
DEFAULT_MAX_RETRIES: Final[int] = 3
|
|
DEFAULT_RETRY_DELAY: Final[int] = 1
|
|
|
|
|
|
# ============================================================================
|
|
# Error Codes
|
|
# ============================================================================
|
|
|
|
|
|
class ErrorCode(str, Enum):
|
|
"""Discovery+ error codes"""
|
|
PLAYBACK_RESTRICTED = "PLAYBACK_RESTRICTED"
|
|
UNAUTHORIZED = "UNAUTHORIZED"
|
|
NOT_FOUND = "NOT_FOUND"
|
|
GEOBLOCKED = "GEOBLOCKED"
|
|
INVALID_CREDENTIALS = "INVALID_CREDENTIALS"
|
|
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED" |