mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-18 15:12:12 +02:00
309 lines
12 KiB
Python
309 lines
12 KiB
Python
# streaming_providers/base/manager.py
|
|
"""
|
|
Main ProviderManager as a facade coordinating all operations.
|
|
Maintains backward compatibility while delegating to specialized classes.
|
|
"""
|
|
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from .catchup_operations import CatchupOperations
|
|
from .channel_operations import ChannelOperations
|
|
from .drm_operations import DRMOperations
|
|
from .epg_operations import EPGOperations
|
|
from .models import StreamingChannel
|
|
from .provider_registry import ProviderRegistry
|
|
from .subscription_operations import SubscriptionOperations
|
|
from .utils.logger import logger
|
|
|
|
|
|
class ProviderManager:
|
|
"""
|
|
Facade coordinating all provider-related operations.
|
|
Maintains backward compatibility while delegating to specialized classes.
|
|
"""
|
|
|
|
def __init__(self):
|
|
# Core components
|
|
self.registry = ProviderRegistry()
|
|
|
|
# Specialized operations
|
|
self.channel_ops = ChannelOperations(self.registry)
|
|
self.epg_ops = EPGOperations(self.registry)
|
|
self.drm_ops = DRMOperations(self.registry)
|
|
self.catchup_ops = CatchupOperations(self.registry, self.drm_ops)
|
|
self.subscription_ops = SubscriptionOperations(self.registry)
|
|
|
|
# Backward compatibility - expose managers directly
|
|
self.drm_plugin_manager = self.drm_ops.drm_plugin_manager
|
|
self.epg_manager = self.epg_ops.epg_manager
|
|
|
|
# Legacy compatibility - expose providers dict
|
|
self.providers = self.registry.providers
|
|
self.provider_metadata = self.registry.provider_metadata
|
|
|
|
logger.info("ProviderManager: Initialized with modular architecture")
|
|
|
|
# ==========================================================================
|
|
# REGISTRY OPERATIONS (delegate to ProviderRegistry)
|
|
# ==========================================================================
|
|
|
|
def discover_all_providers(self, default_country: str = "DE") -> List[str]:
|
|
return self.registry.discover_all_providers(default_country)
|
|
|
|
def discover_providers(self, country: str = "DE", detected_providers: Dict = None) -> List[str]:
|
|
"""Legacy method for backward compatibility."""
|
|
if not self.registry.provider_metadata:
|
|
self.registry.discover_all_providers(country)
|
|
return self.registry.get_enabled_providers()
|
|
|
|
def rediscover_providers(self, country: str = "DE") -> List[str]:
|
|
"""
|
|
Re-scan for new providers, particularly useful for dynamically added M3U playlists.
|
|
|
|
Args:
|
|
country: Country code for provider discovery
|
|
|
|
Returns:
|
|
List of all discovered provider names
|
|
"""
|
|
logger.info(f"ProviderManager: Re-discovering providers for country '{country}'...")
|
|
discovered = self.registry.discover_all_providers(country)
|
|
logger.info(f"ProviderManager: Re-discovery complete. Found {len(discovered)} providers")
|
|
return discovered
|
|
|
|
def get_provider(self, provider_name: str):
|
|
return self.registry.get_provider(provider_name)
|
|
|
|
def set_provider_enabled(self, provider_name: str, enabled: bool) -> bool:
|
|
return self.registry.set_provider_enabled(provider_name, enabled)
|
|
|
|
def get_all_providers_metadata(self) -> List[Dict[str, Any]]:
|
|
return self.registry.get_all_providers_metadata()
|
|
|
|
def reinitialize_provider(self, provider_name: str) -> bool:
|
|
return self.registry.reinitialize_provider(provider_name)
|
|
|
|
def reinitialize_providers(self, provider_names: List[str]) -> Dict[str, bool]:
|
|
return {name: self.registry.reinitialize_provider(name) for name in provider_names}
|
|
|
|
def reinitialize_all_providers(self) -> Dict[str, bool]:
|
|
enabled = self.registry.get_enabled_providers()
|
|
return self.reinitialize_providers(enabled)
|
|
|
|
def list_providers(self) -> List[str]:
|
|
return self.registry.list_providers()
|
|
|
|
def list_all_providers(self) -> List[str]:
|
|
return self.registry.list_all_providers()
|
|
|
|
def clear_providers(self):
|
|
self.registry.clear_providers()
|
|
|
|
@staticmethod
|
|
def get_provider_class(provider_name: str):
|
|
"""Get provider class from AVAILABLE_PROVIDERS registry."""
|
|
from streaming_providers import AVAILABLE_PROVIDERS
|
|
|
|
# Remove country suffix if present
|
|
base_name = provider_name
|
|
if "_" in provider_name:
|
|
name_parts = provider_name.rsplit("_", 1)
|
|
if len(name_parts[1]) in (2, 3) and name_parts[1].isalpha():
|
|
base_name = name_parts[0]
|
|
|
|
return AVAILABLE_PROVIDERS.get(base_name)
|
|
|
|
# ==========================================================================
|
|
# CHANNEL OPERATIONS (delegate to ChannelOperations)
|
|
# ==========================================================================
|
|
|
|
def get_channels(
|
|
self, provider_name: str, fetch_manifests: bool = False, **kwargs
|
|
) -> List[StreamingChannel]:
|
|
return self.channel_ops.get_channels(provider_name, fetch_manifests, **kwargs)
|
|
|
|
def get_channel_manifest(self, provider_name: str, channel_id: str, **kwargs) -> Optional[str]:
|
|
return self.channel_ops.get_channel_manifest(provider_name, channel_id, **kwargs)
|
|
|
|
def get_all_channels(
|
|
self, fetch_manifests: bool = True, **kwargs
|
|
) -> Dict[str, List[StreamingChannel]]:
|
|
return self.channel_ops.get_all_channels(fetch_manifests, **kwargs)
|
|
|
|
# ==========================================================================
|
|
# EPG OPERATIONS (delegate to EPGOperations)
|
|
# ==========================================================================
|
|
|
|
def get_channel_epg(self, provider_name: str, channel_id: str, **kwargs) -> List[Dict]:
|
|
return self.epg_ops.get_channel_epg(provider_name, channel_id, **kwargs)
|
|
|
|
def get_provider_epg_xmltv(self, provider_name: str, **kwargs) -> Optional[str]:
|
|
return self.epg_ops.get_provider_epg_xmltv(provider_name, **kwargs)
|
|
|
|
def clear_epg_cache(self) -> bool:
|
|
return self.epg_ops.clear_epg_cache()
|
|
|
|
def reload_epg_mapping(self) -> bool:
|
|
return self.epg_ops.reload_epg_mapping()
|
|
|
|
def get_epg_cache_info(self) -> Optional[Dict]:
|
|
return self.epg_ops.get_epg_cache_info()
|
|
|
|
def get_epg_mapping_stats(self) -> Dict:
|
|
return self.epg_ops.get_epg_mapping_stats()
|
|
|
|
def has_epg_mapping(self, provider_name: str, channel_id: str) -> bool:
|
|
return self.epg_ops.has_epg_mapping(provider_name, channel_id)
|
|
|
|
# ==========================================================================
|
|
# DRM OPERATIONS (delegate to DRMOperations)
|
|
# ==========================================================================
|
|
|
|
def get_channel_drm_configs(self, provider_name: str, channel_id: str, **kwargs) -> List:
|
|
return self.drm_ops.get_channel_drm_configs(provider_name, channel_id, **kwargs)
|
|
|
|
def list_drm_plugins(self) -> Dict:
|
|
return self.drm_ops.list_drm_plugins()
|
|
|
|
def clear_drm_plugins(self):
|
|
self.drm_ops.clear_drm_plugins()
|
|
|
|
# ==========================================================================
|
|
# CATCHUP OPERATIONS (delegate to CatchupOperations)
|
|
# ==========================================================================
|
|
|
|
def get_catchup_manifest(
|
|
self,
|
|
provider_name: str,
|
|
channel_id: str,
|
|
start_time: int,
|
|
end_time: int,
|
|
epg_id: Optional[str] = None,
|
|
country: Optional[str] = None,
|
|
) -> Optional[str]:
|
|
return self.catchup_ops.get_catchup_manifest(
|
|
provider_name, channel_id, start_time, end_time, epg_id, country
|
|
)
|
|
|
|
def get_catchup_drm_configs(
|
|
self,
|
|
provider_name: str,
|
|
channel_id: str,
|
|
start_time: int,
|
|
end_time: int,
|
|
epg_id: Optional[str] = None,
|
|
country: Optional[str] = None,
|
|
) -> List:
|
|
return self.catchup_ops.get_catchup_drm_configs(
|
|
provider_name, channel_id, start_time, end_time, epg_id, country
|
|
)
|
|
|
|
def get_catchup_window(self, provider_name: str, channel_id: Optional[str] = None) -> int:
|
|
return self.catchup_ops.get_catchup_window(provider_name, channel_id)
|
|
|
|
def supports_catchup(self, provider_name: str) -> bool:
|
|
return self.catchup_ops.supports_catchup(provider_name)
|
|
|
|
def get_all_catchup_capabilities(self) -> Dict[str, Dict]:
|
|
return self.catchup_ops.get_all_catchup_capabilities()
|
|
|
|
# ==========================================================================
|
|
# SUBSCRIPTION OPERATIONS (delegate to SubscriptionOperations)
|
|
# ==========================================================================
|
|
|
|
def get_subscription_status(self, provider_name: str, **kwargs):
|
|
return self.subscription_ops.get_subscription_status(provider_name, **kwargs)
|
|
|
|
def get_subscribed_channels(self, provider_name: str, **kwargs):
|
|
return self.subscription_ops.get_subscribed_channels(provider_name, **kwargs)
|
|
|
|
def get_available_packages(self, provider_name: str, **kwargs):
|
|
return self.subscription_ops.get_available_packages(provider_name, **kwargs)
|
|
|
|
def is_channel_accessible(self, provider_name: str, channel_id: str, **kwargs):
|
|
return self.subscription_ops.is_channel_accessible(provider_name, channel_id, **kwargs)
|
|
|
|
# ==========================================================================
|
|
# UTILITY METHODS (remain in ProviderManager as helpers)
|
|
# ==========================================================================
|
|
|
|
def get_provider_http_manager(self, provider_name: str):
|
|
"""Get HTTP manager for a specific provider."""
|
|
provider = self.get_provider(provider_name)
|
|
if not provider:
|
|
logger.warning(f"ProviderManager: Provider '{provider_name}' not found")
|
|
return None
|
|
|
|
http_manager = provider.http_manager
|
|
if not http_manager:
|
|
logger.warning(f"ProviderManager: Provider '{provider_name}' has no HTTP manager")
|
|
return None
|
|
|
|
logger.debug(f"ProviderManager: Retrieved HTTP manager for '{provider_name}'")
|
|
return http_manager
|
|
|
|
def needs_proxy(self, provider_name: str) -> bool:
|
|
"""Check if a provider needs proxy support."""
|
|
http_manager = self.get_provider_http_manager(provider_name)
|
|
if not http_manager:
|
|
return False
|
|
|
|
has_proxy = http_manager.config.proxy_config is not None
|
|
logger.debug(
|
|
f"ProviderManager: Provider '{provider_name}' {'requires' if has_proxy else 'does not require'} proxy"
|
|
)
|
|
return has_proxy
|
|
|
|
def get_provider_choices(self) -> Dict[int, str]:
|
|
"""
|
|
Get numbered provider choices for user selection.
|
|
Includes an 'all' option as the last choice.
|
|
"""
|
|
enabled_providers = self.registry.get_enabled_providers()
|
|
choices = {i + 1: name for i, name in enumerate(enabled_providers)}
|
|
choices[len(choices) + 1] = "all"
|
|
return choices
|
|
|
|
def get_selected_providers(self, choices_input: str) -> List[str]:
|
|
"""
|
|
Convert user input string to list of provider names.
|
|
|
|
Args:
|
|
choices_input: Comma-separated string of choice numbers
|
|
|
|
Returns:
|
|
List of selected provider names
|
|
"""
|
|
available = self.registry.get_enabled_providers()
|
|
|
|
if not available:
|
|
logger.warning("ProviderManager: No enabled providers available for selection")
|
|
return []
|
|
|
|
selected = []
|
|
for choice in choices_input.split(","):
|
|
choice = choice.strip()
|
|
if choice.isdigit():
|
|
idx = int(choice) - 1
|
|
if 0 <= idx < len(available):
|
|
selected.append(available[idx])
|
|
elif idx == len(available): # 'all' option
|
|
selected = available.copy()
|
|
break
|
|
|
|
result = selected if selected else available
|
|
logger.debug(
|
|
f"ProviderManager: Selected {len(result)} providers from input '{choices_input}'"
|
|
)
|
|
return result
|
|
|
|
# ==========================================================================
|
|
# LEGACY COMPATIBILITY METHODS
|
|
# ==========================================================================
|
|
|
|
def _extract_pssh_from_manifest(self, manifest_url: str) -> List:
|
|
"""
|
|
Extract PSSH data from a manifest URL.
|
|
Kept for backward compatibility but delegated to DRMOperations.
|
|
"""
|
|
return self.drm_ops._extract_pssh_from_manifest(manifest_url) |