Refactor joyn

This commit is contained in:
Nirvana
2026-05-28 10:32:11 +02:00
parent a6078e98e2
commit 4edac568c1
4 changed files with 982 additions and 1233 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,89 +1,67 @@
# streaming_providers/providers/joyn/constants.py
# ============================================================================
# SSO Discovery Configuration
# ============================================================================
# -*- coding: utf-8 -*-
"""
Joyn provider constants - Cleaned and organized
"""
# SSO endpoints discovery URL
JOYN_SSO_DISCOVERY_URL = "https://auth.joyn.de/sso/endpoints"
# ============================================================================
# Provider Metadata
# ============================================================================
JOYN_LOGO = "https://upload.wikimedia.org/wikipedia/de/thumb/7/74/Joyn_%28Streaminganbieter%29_logo.svg/2560px-Joyn_%28Streaminganbieter%29_logo.svg.png"
# Default client IDs for different platforms (fallback)
# ============================================================================
# Authentication - 7pass OIDC
# ============================================================================
# 7pass base URL (OIDC provider)
JOYN_7PASS_BASE_URL = "https://auth.7pass.de"
# 7pass OIDC endpoints (discovered via OIDC discovery)
JOYN_7PASS_ENDPOINTS = {
"AUTHORIZE": f"{JOYN_7PASS_BASE_URL}/authorize",
"TOKEN": f"{JOYN_7PASS_BASE_URL}/token",
"LOGIN": f"{JOYN_7PASS_BASE_URL}/login-srv/login",
"CONSENT_ACCEPT": f"{JOYN_7PASS_BASE_URL}/consent-management-srv/consent/scope/accept",
"PRECHECK_CONTINUE": f"{JOYN_7PASS_BASE_URL}/login-srv/precheck/continue",
"USER_CHECK_EXISTS": f"{JOYN_7PASS_BASE_URL}/users-srv/user/checkexists",
"REGISTRATION_SETUP": f"{JOYN_7PASS_BASE_URL}/registration-setup-srv/public/list",
"VERIFICATION_CONFIGURED": f"{JOYN_7PASS_BASE_URL}/verification-srv/v2/setup/public/configured/list",
}
# Joyn auth endpoints (non-OIDC)
JOYN_AUTH_ENDPOINTS = {
"REFRESH": "https://auth.joyn.de/auth/refresh", # Token refresh endpoint
}
# OAuth2 Configuration
JOYN_OAUTH_SCOPE = "openid email profile offline_access"
# Device IDs for different platforms (fallback for client identification)
DEVICE_IDS = {
"web": "709115c2-f87e-4bad-9b94-28ac08d72cd9",
"android": "05f5f3df-1130-4707-a761-c04d0c50b7f2",
"ios": "21218403-52ec-4a65-abf4-f36a0eadd631",
}
# OAuth2 Configuration
JOYN_OAUTH_SCOPE = "openid email profile offline_access"
# ============================================================================
# Authentication Configuration
# HTTP Headers & User Agent
# ============================================================================
# Base authentication URL
JOYN_AUTH_BASE_URL = "https://auth.joyn.de/auth"
# Authentication endpoints
JOYN_AUTH_ENDPOINTS = {
"ANONYMOUS": f"{JOYN_AUTH_BASE_URL}/anonymous", # Client credentials flow
"REFRESH": f"{JOYN_AUTH_BASE_URL}/refresh", # Token refresh
"LOGOUT": f"{JOYN_AUTH_BASE_URL}/logout", # Logout
}
# ============================================================================
# Cidaas/7pass Configuration
# ============================================================================
# Cidaas base URL (7pass authentication service)
JOYN_CIDAAS_BASE_URL = "https://auth.7pass.de"
# Cidaas API endpoints
JOYN_CIDAAS_ENDPOINTS = {
"VERIFICATION_INITIATE": f"{JOYN_CIDAAS_BASE_URL}/verification-srv/v2/authenticate/initiate/PASSWORD",
"VERIFICATION_AUTHENTICATE": f"{JOYN_CIDAAS_BASE_URL}/verification-srv/v2/authenticate/authenticate/PASSWORD",
"LOGIN_VERIFICATION": f"{JOYN_CIDAAS_BASE_URL}/login-srv/verification/login",
"REGISTRATION_SETUP": f"{JOYN_CIDAAS_BASE_URL}/registration-setup-srv/public/list",
"USER_CHECK_EXISTS": f"{JOYN_CIDAAS_BASE_URL}/users-srv/user/checkexists",
"VERIFICATION_LIST": f"{JOYN_CIDAAS_BASE_URL}/verification-srv/v2/setup/public/configured/list",
"CONSENT_ACCEPT": f"{JOYN_CIDAAS_BASE_URL}/consent-management-srv/consent/scope/accept",
"LOGIN_CONTINUE": f"{JOYN_CIDAAS_BASE_URL}/login-srv/precheck/continue",
}
# Base URLs
JOYN_BASE_URLS = {
"ORIGIN": "https://www.joyn.de",
"REFERER": "https://www.joyn.de/",
"SIGNIN_BASE": "https://signin.7pass.de",
}
# ============================================================================
# API Configuration
# ============================================================================
# Client version used in API requests
JOYN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
JOYN_CLIENT_VERSION = "5.1344.1"
# Platform identifier
DEFAULT_PLATFORM = "web"
# Default user agent for all requests
JOYN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
# Base64 encoded secret key for signature generation
SIGNATURE_SECRET_KEY = "MzU0MzM3MzgzMzM4MzMzNjM1NDMzNzM4MzYzNDM2MzYzNTQzMzczODM2MzYzMzM4MzIzNjM1NDMzNzM4MzMzMDM2MzQzNTM5MzU0MzM3MzgzMzM5MzMzNTMyMzQzNTQzMzczODM2MzUzMzM5MzU0MzM3MzgzMzM4MzMzMjMzNDYzNTQzMzczODM2MzYzMzMzMzM0NDMzNDIzNTQzMzczODMzMzgzNjM2MzMzNQ=="
# Base authentication headers (without dynamic values)
JOYN_AUTH_HEADERS_BASE = {
"User-Agent": JOYN_USER_AGENT,
"Accept": "application/json",
"Content-Type": "application/json",
"Origin": JOYN_BASE_URLS["ORIGIN"],
"Origin": "https://www.joyn.de", # Base origin, overridden per country
"joyn-client-version": JOYN_CLIENT_VERSION,
# Note: 'joyn-platform' is added dynamically in auth.py and provider.py
}
# Base API headers (without dynamic auth tokens)
# Base API headers (without auth token)
JOYN_API_BASE_HEADERS = {
"Accept": "application/json",
"Content-Type": "application/json",
@@ -94,7 +72,6 @@ JOYN_API_BASE_HEADERS = {
# GraphQL Configuration
# ============================================================================
# GraphQL base URL
JOYN_GRAPHQL_BASE_URL = "https://api.joyn.de/graphql"
# GraphQL persisted query hashes
@@ -109,7 +86,7 @@ JOYN_GRAPHQL_ENDPOINTS = {
"LIVE_CHANNELS": f"{JOYN_GRAPHQL_BASE_URL}?operationName=LiveChannelsAndEpg&enable_user_location=true&watch_assistant_variant=true",
}
# Base GraphQL headers (without country-specific ones)
# Base GraphQL headers
JOYN_GRAPHQL_BASE_HEADERS = {
"X-Api-Key": "4f0fd9f18abbe3cf0e87fdb556bc39c8",
"Accept": "application/json",
@@ -117,10 +94,8 @@ JOYN_GRAPHQL_BASE_HEADERS = {
"User-Agent": JOYN_USER_AGENT,
}
# GraphQL persisted query version
GRAPHQL_PERSISTED_QUERY_VERSION = 1
# GraphQL query defaults
GRAPHQL_PERSISTED_QUERY_VERSION = 1
GRAPHQL_LIVE_CHANNELS_FILTER = "DEFAULT"
GRAPHQL_MAX_RESULTS = 5000
GRAPHQL_OFFSET = 0
@@ -129,26 +104,12 @@ GRAPHQL_OFFSET = 0
# Streaming Configuration
# ============================================================================
# Streaming API endpoints
JOYN_STREAMING_ENDPOINTS = {
"ENTITLEMENT": "https://entitlement.p7s1.io/api/user/entitlement-token",
"PLAYLIST": "https://api.vod-prd.s.joyn.de/v1/channel/{channel_id}/playlist",
}
# Default video data payload configuration
"""
DEFAULT_VIDEO_CONFIG = {
'manufacturer': 'unknown',
'platform': 'browser',
'maxSecurityLevel': 1,
'model': 'unknown',
'protectionSystem': 'widevine',
'streamingFormat': 'dash',
'enableSubtitles': True,
'maxResolution': 1080,
'version': 'v1',
}
"""
# Default video configuration for playlist requests
DEFAULT_VIDEO_CONFIG = {
"enableDolbyAtmos": True,
"enableSubtitles": True,
@@ -163,23 +124,22 @@ DEFAULT_VIDEO_CONFIG = {
"maxSecurityLevel": 5,
}
# Signature secret key (base64 encoded)
SIGNATURE_SECRET_KEY = "MzU0MzM3MzgzMzM4MzMzNjM1NDMzNzM4MzYzNDM2MzYzNTQzMzczODM2MzYzMzM4MzIzNjM1NDMzNzM4MzMzMDM2MzQzNTM5MzU0MzM3MzgzMzM5MzMzNTMyMzQzNTQzMzczODM2MzUzMzM5MzU0MzM3MzgzMzM4MzMzMjMzNDYzNTQzMzczODM2MzYzMzMzMzM0NDMzNDIzNTQzMzczODMzMzgzNjM2MzMzNQ=="
# ============================================================================
# Content Configuration
# Content Types & Modes
# ============================================================================
# Content types
CONTENT_TYPE_LIVE = "LIVE"
CONTENT_TYPE_VOD = "VOD"
# Stream types
STREAM_TYPE_LINEAR = "LINEAR"
STREAM_TYPE_EVENT = "EVENT"
STREAM_TYPE_ON_DEMAND = "ON_DEMAND"
# Livestream types for GraphQL queries
DEFAULT_LIVESTREAM_TYPES = ["EVENT", "LINEAR", "ON_DEMAND"]
# Stream modes
MODE_LIVE = "live"
MODE_VOD = "vod"
@@ -187,22 +147,27 @@ MODE_VOD = "vod"
# Error Codes
# ============================================================================
# Known error codes from Joyn API
ERROR_CODES = {
"PLAYBACK_RESTRICTED": "ENT_RVOD_Playback_Restricted",
"UNAUTHORIZED": "ENT_Unauthorized",
"NOT_FOUND": "ENT_Not_Found",
"GEOBLOCKED": "ENT_Geoblocked",
"VALIDATION_ERROR": "VALIDATION_ERROR", # Added for token refresh
"INVALID_JWT": "INVALID_JWT", # Added for expired tokens
"VALIDATION_ERROR": "VALIDATION_ERROR",
"INVALID_JWT": "INVALID_JWT",
}
# ============================================================================
# Country/Region Configuration
# Country Configuration
# ============================================================================
# Country to distribution tenant mapping
COUNTRY_TENANT_MAPPING = {"de": "JOYN", "at": "JOYN_AT", "ch": "JOYN_CH"}
SUPPORTED_COUNTRIES = ["de", "at", "ch"]
DEFAULT_COUNTRY = "de"
COUNTRY_TENANT_MAPPING = {
"de": "JOYN",
"at": "JOYN_AT",
"ch": "JOYN_CH",
}
JOYN_DOMAINS = {
"de": "https://www.joyn.de",
@@ -210,32 +175,22 @@ JOYN_DOMAINS = {
"ch": "https://www.joyn.ch",
}
def get_oauth_redirect_uri(country: str) -> str:
"""Get country-specific OAuth redirect URI"""
# Joyn uses same redirect URI for all countries
return "https://www.joyn.de/oauth"
# Supported countries
SUPPORTED_COUNTRIES = list(COUNTRY_TENANT_MAPPING.keys())
# Default country
DEFAULT_COUNTRY = "de"
# ============================================================================
# DRM Configuration
# ============================================================================
# DRM system
DRM_SYSTEM_WIDEVINE = "widevine"
# DRM request headers
DRM_REQUEST_HEADERS = {
"Content-Type": "application/octet-stream",
"User-Agent": JOYN_USER_AGENT,
}
# DRM license request template (without bearer token)
DRM_LICENSE_HEADERS_BASE = {
"Content-Type": "application/octet-stream",
"User-Agent": JOYN_USER_AGENT,
@@ -245,20 +200,14 @@ DRM_LICENSE_HEADERS_BASE = {
# Request Configuration
# ============================================================================
# Default timeout for HTTP requests (seconds)
DEFAULT_REQUEST_TIMEOUT = 30
# Default maximum retries for failed requests
DEFAULT_MAX_RETRIES = 3
# Default time window for EPG queries (hours)
DEFAULT_EPG_WINDOW_HOURS = 3
# ============================================================================
# Channel Configuration
# ============================================================================
# Default channel settings
DEFAULT_CHANNEL_CONFIG = {
"video": "best",
"on_demand": True,
@@ -268,5 +217,4 @@ DEFAULT_CHANNEL_CONFIG = {
"session_manifest": False,
}
# Default language
DEFAULT_LANGUAGE = "de"
DEFAULT_LANGUAGE = "de"
@@ -3,7 +3,6 @@
import hashlib
import json
import time
import datetime
import urllib.parse
from base64 import b64decode
from datetime import datetime, timedelta
@@ -583,7 +582,7 @@ class JoynProvider(StreamingProvider):
Get manifest URL for a specific channel by ID
Args:
channel_id: ID of the channel to get manifest for
content_id: ID of the channel to get manifest for
content_type: Content type ('LIVE' or 'VOD')
video_config: Optional video configuration dictionary
**kwargs: Additional parameters
@@ -615,7 +614,7 @@ class JoynProvider(StreamingProvider):
Get all DRM configurations for a channel by ID
Args:
channel_id: ID of the channel to get DRM configs
content_id: ID of the channel to get DRM configs
content_type: Content type ('LIVE' or 'VOD')
video_config: Optional video configuration dictionary
**kwargs: Additional parameters