Files
script.service.ultimate/lib/streaming_providers/base/provider.py
T

171 lines
6.6 KiB
Python
Raw Normal View History

2025-12-23 16:51:49 +01:00
# streaming_providers/base/provider.py - Enhanced with Static Metadata
2025-12-19 23:17:20 +01:00
"""
2025-12-23 16:51:49 +01:00
Streaming Provider Base Class with Static Metadata Support
2025-12-19 23:17:20 +01:00
2025-12-23 16:51:49 +01:00
New Features:
- Class attributes for static metadata (PROVIDER_LABEL, etc.)
- Static methods to get metadata without instantiation
- Backward compatible with existing @property methods
2026-09-18 06:50:55 +02:00
Restructure note: this class used to hold every method itself (~90 methods,
~1900 lines). The capability areas — HTTP setup, auth, EPG, VOD, bookmarks,
recordings, timers, favorites, catchup, subscriptions, static metadata — have
been split into mixins under `provider_mixins/`, one file per area.
StreamingProvider composes all of them, so every existing method, property,
and signature is still available on it exactly as before; nothing that
imports `StreamingProvider` (or `AuthType`) from this module needs to change.
2025-12-19 23:17:20 +01:00
"""
2025-12-01 10:59:05 +01:00
2025-10-29 20:23:50 +01:00
import json
2026-01-06 16:41:03 +01:00
from abc import ABC, abstractmethod
2025-10-29 20:23:50 +01:00
from datetime import datetime
2026-09-18 06:50:55 +02:00
from typing import ClassVar, Dict, List, Optional, Tuple
2025-10-29 20:23:50 +01:00
2026-09-18 06:50:55 +02:00
from ..providers.auth import AuthContext, AuthStatus # noqa: F401 (re-exported for callers)
from .auth_type import AuthType # noqa: F401 (re-exported for callers)
2026-02-27 12:14:43 +01:00
from .models import DRMConfig, Event, StreamingChannel
2026-09-18 06:50:55 +02:00
from .provider_mixins.auth import ProviderAuthMixin
from .provider_mixins.bookmarks import ProviderBookmarksMixin
from .provider_mixins.catchup import ProviderCatchupMixin
from .provider_mixins.epg import ProviderEpgMixin
from .provider_mixins.favorites import ProviderFavoritesMixin
from .provider_mixins.http import ProviderHttpMixin
from .provider_mixins.metadata import ProviderMetadataMixin
from .provider_mixins.recordings import ProviderRecordingsMixin
from .provider_mixins.subscriptions import ProviderSubscriptionsMixin
from .provider_mixins.timers import ProviderTimersMixin
from .provider_mixins.vod import ProviderVodMixin
class StreamingProvider(
ABC,
ProviderMetadataMixin,
ProviderHttpMixin,
ProviderAuthMixin,
ProviderEpgMixin,
ProviderVodMixin,
ProviderBookmarksMixin,
ProviderRecordingsMixin,
ProviderTimersMixin,
ProviderFavoritesMixin,
ProviderCatchupMixin,
ProviderSubscriptionsMixin,
):
2025-10-29 20:23:50 +01:00
"""
2025-12-01 10:59:05 +01:00
Abstract base class for streaming providers with centralized HTTP and auth management
2025-10-29 20:23:50 +01:00
"""
2025-12-23 16:51:49 +01:00
# Class attributes for static metadata (accessible without instantiation)
PROVIDER_LABEL: ClassVar[str] = ""
"""Base provider label without country suffix (e.g., 'Joyn', 'RTL+')"""
SUPPORTED_AUTH_TYPES: ClassVar[List[str]] = []
"""Authentication types supported by this provider"""
PROVIDER_LOGO: ClassVar[str] = ""
"""URL to provider logo"""
SUPPORTED_COUNTRIES: ClassVar[List[str]] = []
"""List of ISO country codes this provider supports (empty = single country)"""
2025-12-12 23:00:02 +01:00
2026-01-06 16:41:03 +01:00
def __init__(self, country: str = "DE"):
2025-10-29 20:23:50 +01:00
self.country = country
self.channels: List[StreamingChannel] = []
self._http_manager = None
2026-01-06 16:41:03 +01:00
self._default_user_agent = "StreamingProvider/1.0"
2025-12-01 10:59:05 +01:00
self.authenticator = None # Optional: set by concrete providers
2025-12-23 16:51:49 +01:00
@property
@abstractmethod
def provider_name(self) -> str:
"""Return the provider name (e.g., 'joyn', 'zdf', 'ard')"""
pass
def get_channels(self, **kwargs) -> List[StreamingChannel]:
"""Fetch channels from the provider"""
2026-03-07 14:13:35 +01:00
return []
2025-12-23 16:51:49 +01:00
2026-02-27 12:14:43 +01:00
def get_events(
self,
start_time: Optional[datetime] = None,
end_time: Optional[datetime] = None,
**kwargs,
) -> List[Event]:
"""
Fetch one-time events (concerts, sports matches, etc.) from the provider.
Args:
start_time: Optional lower bound — only return events ending after this time.
end_time: Optional upper bound — only return events starting before this time.
If neither is provided, the provider returns all known events
(both upcoming and currently live).
Returns:
List of Event objects, or empty list if provider has no events.
"""
return []
2026-06-11 11:58:31 +02:00
def get_drm(self, content_id: str, drm_variant: Optional[str] = None, **kwargs) -> List[DRMConfig]:
"""Get all DRM configurations for a channel by ID
Args:
content_id: Content identifier
drm_variant: Optional DRM variant (e.g., 'auto', 'software')
**kwargs: Additional provider-specific parameters
"""
2025-12-23 16:51:49 +01:00
return []
2026-01-06 16:41:03 +01:00
def enrich_channel_data(
self, channel: StreamingChannel, **kwargs
) -> Optional[StreamingChannel]:
2026-02-27 12:14:43 +01:00
"""Optional: Enrich channel with additional data including manifest URL.
Override in subclasses that need pre-fetching of manifests/DRM before playback."""
2025-12-23 16:51:49 +01:00
return None
@abstractmethod
2026-03-01 19:58:59 +01:00
def get_manifest(self, content_id: str, **kwargs) -> Optional[str]:
2025-12-23 16:51:49 +01:00
"""Get manifest URL for a specific channel by ID"""
return None
2026-03-26 16:24:12 +01:00
def get_manifest_headers(self, content_id: str, **kwargs) -> Dict[str, str]:
"""
Return headers for manifest requests.
Override if provider requires specific headers.
"""
return {}
def get_segment_headers(self, content_id: str, **kwargs) -> Dict[str, str]:
"""
Return headers for segment requests (used by proxy).
Default implementation uses manifest headers.
"""
return self.get_manifest_headers(content_id, **kwargs)
def get_manifest_with_headers(self, content_id: str, **kwargs) -> Tuple[Optional[str], Dict[str, str]]:
"""
Convenience method that returns (manifest_url, headers).
Default implementation uses get_manifest() and get_manifest_headers().
Providers can override if they need more complex logic.
"""
url = self.get_manifest(content_id, **kwargs)
headers = self.get_manifest_headers(content_id, **kwargs)
return url, headers
2026-01-16 12:24:41 +01:00
def get_dynamic_manifest_params(self, channel: StreamingChannel, **kwargs) -> Optional[str]:
2025-12-23 16:51:49 +01:00
"""Optional: Get dynamic manifest parameters for a channel"""
return None
def to_output_format(self, channels: List[StreamingChannel] = None) -> Dict:
"""Convert channels to output format"""
if channels is None:
channels = self.channels
return {
2026-01-06 16:41:03 +01:00
"Provider": self.provider_name,
"Country": self.country,
"Channels": [channel.to_dict() for channel in channels],
2025-12-23 16:51:49 +01:00
}
def to_json(self, channels: List[StreamingChannel] = None, indent: int = 2) -> str:
"""Convert to JSON string"""
2026-09-18 06:50:55 +02:00
return json.dumps(self.to_output_format(channels), indent=indent, ensure_ascii=False)