mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-28 03:52:32 +02:00
Add discovery
This commit is contained in:
@@ -263,19 +263,19 @@ class BHTelecomProvider(StreamingProvider):
|
||||
|
||||
def get_manifest(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
**kwargs,
|
||||
) -> Optional[str]:
|
||||
# Hits the cache logically
|
||||
channels = self.get_channels(fetch_manifests=False)
|
||||
for channel in channels:
|
||||
if channel.channel_id == channel_id:
|
||||
if channel.channel_id == content_id:
|
||||
return channel.manifest
|
||||
return None
|
||||
|
||||
def get_drm(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
**kwargs,
|
||||
) -> List[DRMConfig]:
|
||||
return []
|
||||
|
||||
@@ -125,6 +125,14 @@ CMS_INCLUDE_PARAMS: Final[str] = "default"
|
||||
CMS_PAGE_SIZE: Final[int] = 50
|
||||
CMS_SCHEDULE_INCLUDE_PARAMS: Final[str] = "items.video.items.video.badges,items.video.primaryChannel,items.video.images,items.video.edit"
|
||||
|
||||
# CMS route identifiers
|
||||
# /sports — the primary sports landing page; returns airing items directly in
|
||||
# the top-level `included` array (no nested collection hop required).
|
||||
# /sport-schedule — legacy schedule route; uses the deeper collection traversal
|
||||
# path and is kept as a fallback.
|
||||
CMS_ROUTE_SPORTS: Final[str] = "sports"
|
||||
CMS_ROUTE_SPORT_SCHEDULE: Final[str] = "sport-schedule"
|
||||
|
||||
# Known collection IDs for TV channels (will be discovered dynamically, but these are fallbacks)
|
||||
CHANNEL_COLLECTIONS: Final[Dict[str, List[str]]] = {
|
||||
"de": [
|
||||
@@ -142,6 +150,26 @@ CHANNEL_COLLECTIONS: Final[Dict[str, List[str]]] = {
|
||||
# Collection item types that represent channels
|
||||
CHANNEL_ITEM_TYPES: Final[List[str]] = ["distributionChannel", "channel", "linearChannel", "liveChannel"]
|
||||
|
||||
# ============================================================================
|
||||
# Airing / Event Badge Constants
|
||||
# ============================================================================
|
||||
|
||||
# Badge IDs returned on `airing` items in the CMS `/sports` route response.
|
||||
# These map directly to EventStatus values in the provider.
|
||||
#
|
||||
# "live" → EventStatus.LIVE (event is currently airing)
|
||||
# "release-state-up-next" → EventStatus.UPCOMING (event is scheduled soon)
|
||||
#
|
||||
# Note: The legacy event path used "release-state-upcoming" as the up-next badge
|
||||
# ID. The /sports airing items use "release-state-up-next" (with a trailing
|
||||
# "-next"). Both are handled in get_events() for backward compatibility.
|
||||
AIRING_BADGE_LIVE: Final[str] = "live"
|
||||
AIRING_BADGE_UP_NEXT: Final[str] = "release-state-up-next"
|
||||
AIRING_BADGE_UPCOMING_LEGACY: Final[str] = "release-state-upcoming"
|
||||
|
||||
# Type string for airing items returned on the /sports route
|
||||
AIRING_ITEM_TYPE: Final[str] = "airing"
|
||||
|
||||
# ============================================================================
|
||||
# User Agent Configuration
|
||||
# ============================================================================
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -337,7 +337,7 @@ class HRTiProvider(StreamingProvider):
|
||||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||
return channel
|
||||
|
||||
def get_manifest(self, channel_id: str, **kwargs) -> Optional[str]:
|
||||
def get_manifest(self, content_id: str, **kwargs) -> Optional[str]:
|
||||
"""
|
||||
Get manifest URL for a channel by authorizing a session
|
||||
"""
|
||||
@@ -345,8 +345,8 @@ class HRTiProvider(StreamingProvider):
|
||||
# Authorize session for this channel
|
||||
session_data = self.authenticator.authorize_session(
|
||||
content_type="tlive", # TV live
|
||||
content_ref_id=channel_id,
|
||||
channel_id=channel_id,
|
||||
content_ref_id=content_id,
|
||||
channel_id=content_id,
|
||||
)
|
||||
|
||||
if session_data and session_data.get("Authorized", False):
|
||||
@@ -354,17 +354,17 @@ class HRTiProvider(StreamingProvider):
|
||||
# The actual manifest will be resolved during playback with session authorization
|
||||
channels = self.get_channels()
|
||||
for channel in channels:
|
||||
if channel.channel_id == channel_id:
|
||||
if channel.channel_id == content_id:
|
||||
return channel.manifest_script # This is the streaming URL
|
||||
|
||||
logger.warning(f"Session authorization failed for channel {channel_id}")
|
||||
logger.warning(f"Session authorization failed for channel {content_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting manifest for channel {channel_id}: {e}")
|
||||
logger.error(f"Error getting manifest for channel {content_id}: {e}")
|
||||
return None
|
||||
|
||||
def get_drm(self, channel_id: str, session_data: Dict = None, **kwargs) -> List[DRMConfig]:
|
||||
def get_drm(self, content_id: str, session_data: Dict = None, **kwargs) -> List[DRMConfig]:
|
||||
"""
|
||||
Get DRM configurations for a channel with proper license data.
|
||||
If session_data is not provided, will authorize a new session.
|
||||
@@ -373,7 +373,7 @@ class HRTiProvider(StreamingProvider):
|
||||
# If no session data provided, authorize a new session
|
||||
if not session_data:
|
||||
logger.debug(
|
||||
f"No session data provided for DRM - authorizing new session for channel {channel_id}"
|
||||
f"No session data provided for DRM - authorizing new session for channel {content_id}"
|
||||
)
|
||||
|
||||
# Find the channel to get content type and streaming URL
|
||||
@@ -384,12 +384,12 @@ class HRTiProvider(StreamingProvider):
|
||||
)
|
||||
target_channel = None
|
||||
for ch in channels:
|
||||
if ch.channel_id == channel_id:
|
||||
if ch.channel_id == content_id:
|
||||
target_channel = ch
|
||||
break
|
||||
|
||||
if not target_channel:
|
||||
logger.error(f"Channel {channel_id} not found for DRM authorization")
|
||||
logger.error(f"Channel {content_id} not found for DRM authorization")
|
||||
return []
|
||||
|
||||
# Determine content type
|
||||
@@ -407,35 +407,35 @@ class HRTiProvider(StreamingProvider):
|
||||
content_drm_id = f"{path_parts[0]}_{path_parts[1]}"
|
||||
|
||||
logger.debug(
|
||||
f"Authorizing session for DRM - channel: {channel_id}, content_type: {content_type}, drm_id: {content_drm_id}"
|
||||
f"Authorizing session for DRM - channel: {content_id}, content_type: {content_type}, drm_id: {content_drm_id}"
|
||||
)
|
||||
|
||||
# Authorize session
|
||||
session_data = self.authenticator.authorize_session(
|
||||
content_type=content_type,
|
||||
content_ref_id=channel_id,
|
||||
content_ref_id=content_id,
|
||||
content_drm_id=content_drm_id,
|
||||
video_store_ids=None,
|
||||
channel_id=channel_id,
|
||||
channel_id=content_id,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
)
|
||||
|
||||
if not session_data:
|
||||
logger.error(f"Failed to authorize session for DRM - channel {channel_id}")
|
||||
logger.error(f"Failed to authorize session for DRM - channel {content_id}")
|
||||
return []
|
||||
|
||||
# Check if authorized
|
||||
if not session_data.get("Authorized", False):
|
||||
logger.warning(f"Session not authorized for DRM - channel {channel_id}")
|
||||
logger.warning(f"Session not authorized for DRM - channel {content_id}")
|
||||
return []
|
||||
|
||||
logger.debug(f"Session authorized for DRM - channel {channel_id}")
|
||||
logger.debug(f"Session authorized for DRM - channel {content_id}")
|
||||
|
||||
# Report session event (use full SessionId, not DrmId)
|
||||
session_id = session_data.get("SessionId")
|
||||
if session_id:
|
||||
self.authenticator.report_session_event(session_id, channel_id)
|
||||
self.authenticator.report_session_event(session_id, content_id)
|
||||
|
||||
# IMPORTANT: For license data, use DrmId (not SessionId)
|
||||
# DrmId is the short random string for DRM
|
||||
@@ -473,7 +473,7 @@ class HRTiProvider(StreamingProvider):
|
||||
|
||||
# Create the license configuration
|
||||
# Use the class method to create LicenseConfig with base64 encoded req_data
|
||||
license_config = LicenseConfig.create_with_base64_req_data(
|
||||
license_config = LicenseConfig.create_with_req_data(
|
||||
req_data_template="{CHA-RAW}", # The placeholder string
|
||||
server_url=self.hrti_config.license_url,
|
||||
use_http_get_request=False,
|
||||
@@ -486,11 +486,11 @@ class HRTiProvider(StreamingProvider):
|
||||
# Create the DRM configuration
|
||||
drm_config = DRMConfig(system=DRMSystem.WIDEVINE, priority=1, license=license_config)
|
||||
|
||||
logger.debug(f"Created DRM config for channel {channel_id} with DrmId {drm_id}")
|
||||
logger.debug(f"Created DRM config for channel {content_id} with DrmId {drm_id}")
|
||||
return [drm_config]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting DRM config for channel {channel_id}: {e}")
|
||||
logger.error(f"Error getting DRM config for channel {content_id}: {e}")
|
||||
import traceback
|
||||
|
||||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||
|
||||
@@ -574,7 +574,7 @@ class JoynProvider(StreamingProvider):
|
||||
|
||||
def get_manifest(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
content_type: str = CONTENT_TYPE_LIVE,
|
||||
video_config: Optional[Dict] = None,
|
||||
**kwargs,
|
||||
@@ -593,20 +593,20 @@ class JoynProvider(StreamingProvider):
|
||||
"""
|
||||
try:
|
||||
entitlement_token = self.get_entitlement_token(
|
||||
content_id=channel_id, content_type=content_type
|
||||
content_id=content_id, content_type=content_type
|
||||
)
|
||||
|
||||
playlist_data = self.get_channel_playlist(channel_id, entitlement_token, video_config)
|
||||
playlist_data = self.get_channel_playlist(content_id, entitlement_token, video_config)
|
||||
|
||||
return playlist_data.get("manifestUrl")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting manifest for channel {channel_id}: {e}")
|
||||
logger.error(f"Error getting manifest for channel {content_id}: {e}")
|
||||
return None
|
||||
|
||||
def get_drm(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
content_type: str = CONTENT_TYPE_LIVE,
|
||||
video_config: Optional[Dict] = None,
|
||||
**kwargs,
|
||||
@@ -625,10 +625,10 @@ class JoynProvider(StreamingProvider):
|
||||
"""
|
||||
try:
|
||||
entitlement_token = self.get_entitlement_token(
|
||||
content_id=channel_id, content_type=content_type
|
||||
content_id=content_id, content_type=content_type
|
||||
)
|
||||
|
||||
playlist_data = self.get_channel_playlist(channel_id, entitlement_token, video_config)
|
||||
playlist_data = self.get_channel_playlist(content_id, entitlement_token, video_config)
|
||||
|
||||
license_url = playlist_data.get("licenseUrl")
|
||||
if not license_url:
|
||||
@@ -655,7 +655,7 @@ class JoynProvider(StreamingProvider):
|
||||
return [drm_config]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting DRM configs for channel {channel_id}: {e}")
|
||||
logger.error(f"Error getting DRM configs for channel {content_id}: {e}")
|
||||
return []
|
||||
|
||||
def get_epg(
|
||||
|
||||
@@ -1208,7 +1208,7 @@ class M3UProvider(StreamingProvider):
|
||||
|
||||
def get_manifest(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
**kwargs,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
@@ -1222,13 +1222,13 @@ class M3UProvider(StreamingProvider):
|
||||
"""
|
||||
channels = self.get_channels()
|
||||
for channel in channels:
|
||||
if channel.channel_id == channel_id:
|
||||
if channel.channel_id == content_id:
|
||||
return channel.manifest
|
||||
return None
|
||||
|
||||
def get_drm(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
**kwargs,
|
||||
) -> List[DRMConfig]:
|
||||
"""
|
||||
@@ -1242,7 +1242,7 @@ class M3UProvider(StreamingProvider):
|
||||
"""
|
||||
channels = self.get_channels()
|
||||
for channel in channels:
|
||||
if channel.channel_id == channel_id:
|
||||
if channel.channel_id == content_id:
|
||||
if channel.drm_config is not None:
|
||||
# Return as a list for consistency
|
||||
return [channel.drm_config]
|
||||
|
||||
@@ -1254,10 +1254,10 @@ class Magenta2Provider(StreamingProvider):
|
||||
return None
|
||||
|
||||
def get_manifest(
|
||||
self, channel_id: str, content_type: str = CONTENT_TYPE_LIVE, **kwargs
|
||||
self, content_id: str, content_type: str = CONTENT_TYPE_LIVE, **kwargs
|
||||
) -> Optional[str]:
|
||||
"""Get MPD manifest URL using cached SMIL data"""
|
||||
smil_data = self._get_smil_data(channel_id)
|
||||
smil_data = self._get_smil_data(content_id)
|
||||
return smil_data.get("mpd_url") if smil_data else None
|
||||
|
||||
def get_catchup_manifest(
|
||||
@@ -1510,17 +1510,17 @@ class Magenta2Provider(StreamingProvider):
|
||||
return None
|
||||
|
||||
def get_drm(
|
||||
self, channel_id: str, content_type: str = CONTENT_TYPE_LIVE, **kwargs
|
||||
self, content_id: str, content_type: str = CONTENT_TYPE_LIVE, **kwargs
|
||||
) -> List[DRMConfig]:
|
||||
"""Get DRM configuration using unified SMIL data"""
|
||||
try:
|
||||
smil_data = self._get_smil_data(channel_id)
|
||||
smil_data = self._get_smil_data(content_id)
|
||||
if not smil_data:
|
||||
logger.error(f"No SMIL data found for channel {channel_id}")
|
||||
logger.error(f"No SMIL data found for channel {content_id}")
|
||||
return []
|
||||
|
||||
if not smil_data.get("release_pid"):
|
||||
logger.error(f"No releasePid found in SMIL for channel {channel_id}")
|
||||
logger.error(f"No releasePid found in SMIL for channel {content_id}")
|
||||
# Debug what we do have
|
||||
logger.debug(f"SMIL data keys: {list(smil_data.keys())}")
|
||||
if "content" in smil_data and smil_data["content"]:
|
||||
@@ -1576,12 +1576,12 @@ class Magenta2Provider(StreamingProvider):
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✓ DRM configuration created for channel {channel_id} (releasePid: {release_pid})"
|
||||
f"✓ DRM configuration created for channel {content_id} (releasePid: {release_pid})"
|
||||
)
|
||||
return [drm_config]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting DRM configs for channel {channel_id}: {e}")
|
||||
logger.error(f"Error getting DRM configs for channel {content_id}: {e}")
|
||||
return []
|
||||
|
||||
def _get_account_uri(self) -> str:
|
||||
|
||||
@@ -315,7 +315,7 @@ class MagentaEUProvider(StreamingProvider):
|
||||
|
||||
return True
|
||||
|
||||
def get_manifest(self, channel_id: str, **kwargs) -> Optional[str]:
|
||||
def get_manifest(self, content_id: str, **kwargs) -> Optional[str]:
|
||||
"""Get manifest URL for a channel by ID"""
|
||||
|
||||
# Ensure cache is populated
|
||||
@@ -324,10 +324,10 @@ class MagentaEUProvider(StreamingProvider):
|
||||
|
||||
# Look for the channel
|
||||
for channel in self._channels_cache:
|
||||
if channel.channel_id == channel_id:
|
||||
if channel.channel_id == content_id:
|
||||
return channel.manifest
|
||||
|
||||
logger.warning(f"Channel {channel_id} not found in available channels")
|
||||
logger.warning(f"Channel {content_id} not found in available channels")
|
||||
return None
|
||||
|
||||
def get_catchup_manifest(
|
||||
@@ -383,24 +383,24 @@ class MagentaEUProvider(StreamingProvider):
|
||||
logger.warning(f"Falling back to live manifest for channel {channel_id}")
|
||||
return base_manifest
|
||||
|
||||
def get_drm(self, channel_id: str, **kwargs) -> List[DRMConfig]:
|
||||
def get_drm(self, content_id: str, **kwargs) -> List[DRMConfig]:
|
||||
"""Get DRM configurations for channel by ID"""
|
||||
logger.info(f"=== get_drm_configs_by_id CALLED for channel_id: {channel_id} ===")
|
||||
logger.info(f"=== get_drm_configs_by_id CALLED for channel_id: {content_id} ===")
|
||||
|
||||
# Ensure cache is populated
|
||||
if not self._ensure_channels_cache():
|
||||
logger.warning(f"Cannot get DRM for {channel_id}, channels cache unavailable")
|
||||
logger.warning(f"Cannot get DRM for {content_id}, channels cache unavailable")
|
||||
return []
|
||||
|
||||
# Find channel in cache
|
||||
channel = None
|
||||
for cached_channel in self._channels_cache:
|
||||
if cached_channel.channel_id == channel_id:
|
||||
if cached_channel.channel_id == content_id:
|
||||
channel = cached_channel
|
||||
break
|
||||
|
||||
if not channel:
|
||||
logger.warning(f"Channel with ID {channel_id} not found in cache")
|
||||
logger.warning(f"Channel with ID {content_id} not found in cache")
|
||||
return []
|
||||
|
||||
# Get DRM config using the existing method
|
||||
|
||||
@@ -258,8 +258,8 @@ class RTLPlusProvider(StreamingProvider):
|
||||
logger.error(f"Error enriching channel data for {channel.name}: {e}")
|
||||
return channel
|
||||
|
||||
def get_manifest(self, channel_id: str, **kwargs) -> Optional[str]:
|
||||
manifest_url = self.rtl_config.get_manifest_url(channel_id)
|
||||
def get_manifest(self, content_id: str, **kwargs) -> Optional[str]:
|
||||
manifest_url = self.rtl_config.get_manifest_url(content_id)
|
||||
|
||||
try:
|
||||
logger.debug(f"RTL+ Manifest Request: GET {manifest_url}")
|
||||
@@ -334,13 +334,13 @@ class RTLPlusProvider(StreamingProvider):
|
||||
except Exception:
|
||||
return manifest_data
|
||||
|
||||
def get_drm(self, channel_id: str, **kwargs) -> List[DRMConfig]:
|
||||
def get_drm(self, content_id: str, **kwargs) -> List[DRMConfig]:
|
||||
"""
|
||||
Get DRM configurations for a channel from RTL+ streaming API
|
||||
"""
|
||||
try:
|
||||
# Fetch manifest data to get license information
|
||||
manifest_url = self.rtl_config.get_manifest_url(channel_id)
|
||||
manifest_url = self.rtl_config.get_manifest_url(content_id)
|
||||
|
||||
response = self.http_manager.get(manifest_url, operation="manifest")
|
||||
response.raise_for_status()
|
||||
@@ -405,10 +405,10 @@ class RTLPlusProvider(StreamingProvider):
|
||||
return drm_configs
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Error fetching DRM configs for RTL+ channel {channel_id}: {e}")
|
||||
logger.error(f"Error fetching DRM configs for RTL+ channel {content_id}: {e}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error parsing DRM configs for RTL+ channel {channel_id}: {e}")
|
||||
logger.error(f"Error parsing DRM configs for RTL+ channel {content_id}: {e}")
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -736,7 +736,7 @@ class ScriptsProvider(StreamingProvider):
|
||||
|
||||
def get_manifest(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
proxy_url: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> Optional[str]:
|
||||
@@ -755,12 +755,12 @@ class ScriptsProvider(StreamingProvider):
|
||||
channel = None
|
||||
channels = self.get_channels()
|
||||
for ch in channels:
|
||||
if ch.channel_id == channel_id:
|
||||
if ch.channel_id == content_id:
|
||||
channel = ch
|
||||
break
|
||||
|
||||
if not channel:
|
||||
logger.warning(f"Scripts ({self.provider_label}): Channel not found: {channel_id}")
|
||||
logger.warning(f"Scripts ({self.provider_label}): Channel not found: {content_id}")
|
||||
return None
|
||||
|
||||
# Parse manifest script parameters
|
||||
@@ -804,7 +804,7 @@ class ScriptsProvider(StreamingProvider):
|
||||
|
||||
def get_drm(
|
||||
self,
|
||||
channel_id: str,
|
||||
content_id: str,
|
||||
proxy_url: Optional[str] = None,
|
||||
force_refresh: bool = False,
|
||||
**kwargs,
|
||||
@@ -825,20 +825,20 @@ class ScriptsProvider(StreamingProvider):
|
||||
channel = None
|
||||
channels = self.get_channels()
|
||||
for ch in channels:
|
||||
if ch.channel_id == channel_id:
|
||||
if ch.channel_id == content_id:
|
||||
channel = ch
|
||||
break
|
||||
|
||||
if not channel:
|
||||
logger.warning(f"Scripts ({self.provider_label}): Channel not found: {channel_id}")
|
||||
logger.warning(f"Scripts ({self.provider_label}): Channel not found: {content_id}")
|
||||
return []
|
||||
|
||||
# Check cache
|
||||
if not force_refresh and not self._should_refresh_cdm_cache(channel_id):
|
||||
cached_config = self._cdm_cache.get(channel_id)
|
||||
if not force_refresh and not self._should_refresh_cdm_cache(content_id):
|
||||
cached_config = self._cdm_cache.get(content_id)
|
||||
if cached_config:
|
||||
self._stats["cache_hits"] += 1
|
||||
logger.debug(f"Scripts ({self.provider_label}): Returning DRM config from cache for {channel_id}")
|
||||
logger.debug(f"Scripts ({self.provider_label}): Returning DRM config from cache for {content_id}")
|
||||
return [cached_config]
|
||||
|
||||
self._stats["cache_misses"] += 1
|
||||
@@ -866,12 +866,12 @@ class ScriptsProvider(StreamingProvider):
|
||||
return []
|
||||
|
||||
# Parse DRM config
|
||||
drm_config = self._parse_cdm_output(output, channel_id)
|
||||
drm_config = self._parse_cdm_output(output, content_id)
|
||||
|
||||
if drm_config:
|
||||
# Update cache
|
||||
self._cdm_cache[channel_id] = drm_config
|
||||
self._cdm_cache_timestamps[channel_id] = time.time()
|
||||
self._cdm_cache[content_id] = drm_config
|
||||
self._cdm_cache_timestamps[content_id] = time.time()
|
||||
return [drm_config]
|
||||
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user