Add m3u playlists

This commit is contained in:
Nirvana
2026-02-15 18:16:41 +01:00
parent bed3095f86
commit 0c1e86325b
2 changed files with 82 additions and 26 deletions
+32 -17
View File
@@ -56,6 +56,21 @@ class ProviderManager:
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)
@@ -103,7 +118,7 @@ class ProviderManager:
# ==========================================================================
def get_channels(
self, provider_name: str, fetch_manifests: bool = False, **kwargs
self, provider_name: str, fetch_manifests: bool = False, **kwargs
) -> List[StreamingChannel]:
return self.channel_ops.get_channels(provider_name, fetch_manifests, **kwargs)
@@ -111,7 +126,7 @@ class ProviderManager:
return self.channel_ops.get_channel_manifest(provider_name, channel_id, **kwargs)
def get_all_channels(
self, fetch_manifests: bool = True, **kwargs
self, fetch_manifests: bool = True, **kwargs
) -> Dict[str, List[StreamingChannel]]:
return self.channel_ops.get_all_channels(fetch_manifests, **kwargs)
@@ -158,26 +173,26 @@ class ProviderManager:
# ==========================================================================
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,
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,
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
@@ -291,4 +306,4 @@ class ProviderManager:
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)
return self.drm_ops._extract_pssh_from_manifest(manifest_url)
+50 -9
View File
@@ -36,7 +36,7 @@ def setup_provider_routes(app, manager, service):
# Check specific auth type needs
needs_user_creds = "user_credentials" in supported_auth_types
needs_client_creds = (
"client_credentials" in supported_auth_types
"client_credentials" in supported_auth_types
)
is_network_based = "network_based" in supported_auth_types
is_anonymous = "anonymous" in supported_auth_types
@@ -63,10 +63,10 @@ def setup_provider_routes(app, manager, service):
# Derived summary for UI
"needs_user_input": needs_user_creds or uses_device_reg,
"needs_configuration": needs_user_creds
or needs_client_creds,
or needs_client_creds,
"is_automatic": is_network_based
or is_anonymous
or uses_embedded,
or is_anonymous
or uses_embedded,
},
# Token properties
"primary_token_scope": getattr(
@@ -98,7 +98,7 @@ def setup_provider_routes(app, manager, service):
channels = manager.get_channels(
provider_name=provider,
fetch_manifests=request.query.get("fetch_manifests", "false").lower()
== "true",
== "true",
country=request.query.get("country"),
)
@@ -342,8 +342,8 @@ def setup_provider_routes(app, manager, service):
if "not registered" in message.lower():
response.status = 404
elif (
"invalid" in message.lower()
or "validation failed" in message.lower()
"invalid" in message.lower()
or "validation failed" in message.lower()
):
response.status = 400
else:
@@ -514,8 +514,8 @@ def setup_provider_routes(app, manager, service):
if "not registered" in message.lower():
response.status = 404
elif (
"invalid" in message.lower()
or "validation failed" in message.lower()
"invalid" in message.lower()
or "validation failed" in message.lower()
):
response.status = 400
else:
@@ -810,3 +810,44 @@ def setup_provider_routes(app, manager, service):
logger.error(f"Error setting enabled status for {provider}: {e}")
response.status = 500
return {"error": str(e)}
@app.route("/api/providers/rediscover", method="POST")
def rediscover_providers():
"""
Re-scan for new providers (e.g., newly added M3U playlists).
Accepts optional query parameter:
- country: Country code for provider discovery (default: service default_country)
Example: POST /api/providers/rediscover
Example: POST /api/providers/rediscover?country=US
Returns:
JSON with list of all discovered providers and count
"""
try:
# Get country from query param or use service default
country = request.query.get("country", service.default_country)
logger.info(f"API: Rediscovering providers for country '{country}'")
# Trigger provider rediscovery
discovered = manager.rediscover_providers(country)
# Get updated metadata
all_metadata = manager.get_all_providers_metadata()
enabled_count = sum(1 for m in all_metadata if m["enabled"])
return {
"success": True,
"message": f"Re-discovered {len(discovered)} providers ({enabled_count} enabled)",
"providers": discovered,
"count": len(discovered),
"enabled_count": enabled_count,
"country": country,
}
except Exception as api_err:
logger.error(f"API Error in POST /api/providers/rediscover: {str(api_err)}")
response.status = 500
return {"error": f"Failed to rediscover providers: {str(api_err)}"}