diff --git a/routes/__init__.py b/routes/__init__.py index d29715c..fe5d18b 100644 --- a/routes/__init__.py +++ b/routes/__init__.py @@ -13,21 +13,22 @@ LIB_PATH = os.path.join(app_dir, "lib") if os.path.exists(LIB_PATH) and LIB_PATH not in sys.path: sys.path.insert(0, LIB_PATH) +from .cache import setup_cache_routes +from .config import setup_config_routes +from .drm import setup_drm_routes +from .epg import setup_epg_routes +from .m3u import setup_m3u_routes + # Now import the route setup functions from .providers import setup_provider_routes from .streams import setup_stream_routes -from .m3u import setup_m3u_routes -from .drm import setup_drm_routes -from .cache import setup_cache_routes -from .config import setup_config_routes -from .epg import setup_epg_routes __all__ = [ - 'setup_provider_routes', - 'setup_stream_routes', - 'setup_m3u_routes', - 'setup_drm_routes', - 'setup_cache_routes', - 'setup_config_routes', - 'setup_epg_routes' -] \ No newline at end of file + "setup_provider_routes", + "setup_stream_routes", + "setup_m3u_routes", + "setup_drm_routes", + "setup_cache_routes", + "setup_config_routes", + "setup_epg_routes", +] diff --git a/routes/m3u.py b/routes/m3u.py index 0fbceed..7e62f98 100644 --- a/routes/m3u.py +++ b/routes/m3u.py @@ -289,8 +289,8 @@ def setup_m3u_routes(app, manager, service): except: provider_label = provider_name - # Build stream URL - stream_url = f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream" + # Build stream URL with /index.mpd + stream_url = f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream/index.mpd" # Add M3U entry m3u_content += f'#EXTINF:-1 tvg-id="{channel_id}" tvg-logo="{channel_logo}" group-title="{provider_label}",{channel_name}\n' diff --git a/routes/streams.py b/routes/streams.py index f62a5d9..50502e2 100644 --- a/routes/streams.py +++ b/routes/streams.py @@ -20,9 +20,7 @@ def setup_stream_routes(app, manager, service): try: # Build the stream URL (which will handle both proxy and non-proxy) base_url = f"{request.urlparts.scheme}://{request.urlparts.netloc}" - stream_url = ( - f"{base_url}/api/providers/{provider}/channels/{channel_id}/stream" - ) + stream_url = f"{base_url}/api/providers/{provider}/channels/{channel_id}/stream/index.mpd" # Add country parameter if provided country = request.query.get("country") @@ -32,7 +30,7 @@ def setup_stream_routes(app, manager, service): return { "provider": provider, "channel_id": channel_id, - "manifest_url": stream_url, # Always point to /stream endpoint + "manifest_url": stream_url, # Now points to /stream/index.mpd endpoint } except ValueError as val_err: @@ -48,7 +46,7 @@ def setup_stream_routes(app, manager, service): response.status = 500 return {"error": f"Internal server error: {str(api_err)}"} - @app.route("/api/providers//channels//stream") + @app.route("/api/providers//channels//stream/index.mpd") def get_channel_stream(provider, channel_id): """ Returns HTTP 302 redirect to the actual manifest or rewritten manifest endpoint. @@ -330,7 +328,9 @@ def setup_stream_routes(app, manager, service): response.status = 500 return {"error": f"Internal server error: {str(api_err)}"} - @app.route("/api/providers//channels//stream/decrypted") + @app.route( + "/api/providers//channels//stream/decrypted/index.mpd" + ) def get_channel_stream_decrypted(provider, channel_id): """ Returns rewritten manifest for decrypted playback via media proxy. diff --git a/service.py b/service.py index 7a0884e..0aa00bc 100644 --- a/service.py +++ b/service.py @@ -585,9 +585,9 @@ class UltimateService: channel_name = channel.name channel_logo = channel.logo_url or "" - # Build stream URL + # Build stream URL with /index.mpd stream_url = ( - f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream" + f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream/index.mpd" ) # Get provider instance to access provider_label @@ -621,6 +621,50 @@ class UltimateService: return entry_content + @staticmethod + def _generate_m3u_decrypted_channel_entry( + base_url, + provider_name, + provider_label, + channel, + clearkey_data, + provider_proxy_url, + ): + """ + Generate M3U entry for a ClearKey encrypted channel with decryption URL. + + Args: + base_url: Base URL for stream endpoints + provider_name: Name of the provider + provider_label: Display label for the provider + channel: StreamingChannel object + clearkey_data: ClearKey DRM data + provider_proxy_url: Optional provider proxy URL + + Returns: + M3U entry as string + """ + entry_content = "" + + channel_id = channel.channel_id + channel_name = channel.name + channel_logo = channel.logo_url or "" + + # Build decrypted stream URL via media proxy with /index.mpd + # Format: /api/providers/{provider}/channels/{channel_id}/stream/decrypted/index.mpd + stream_url = f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream/decrypted/index.mpd" + + # Add M3U entry with extended info + entry_content += f'#EXTINF:-1 tvg-id="{channel_id}" tvg-logo="{channel_logo}" group-title="{provider_label}",{channel_name}\n' + + # Add KODIPROP for inputstream.adaptive (still needed for DASH playback) + entry_content += "#KODIPROP:inputstream=inputstream.adaptive\n" + + # Add stream URL + entry_content += f"{stream_url}\n" + + return entry_content + def _generate_m3u_decrypted_content( self, providers=None, save_to_cache=True, cache_filename=None ): @@ -765,50 +809,6 @@ class UltimateService: return m3u_content - def _generate_m3u_decrypted_channel_entry( - self, - base_url, - provider_name, - provider_label, - channel, - clearkey_data, - provider_proxy_url, - ): - """ - Generate M3U entry for a ClearKey encrypted channel with decryption URL. - - Args: - base_url: Base URL for stream endpoints - provider_name: Name of the provider - provider_label: Display label for the provider - channel: StreamingChannel object - clearkey_data: ClearKey DRM data - provider_proxy_url: Optional provider proxy URL - - Returns: - M3U entry as string - """ - entry_content = "" - - channel_id = channel.channel_id - channel_name = channel.name - channel_logo = channel.logo_url or "" - - # Build decrypted stream URL via media proxy - # Format: /api/providers/{provider}/channels/{channel_id}/stream/decrypted - stream_url = f"{base_url}/api/providers/{provider_name}/channels/{channel_id}/stream/decrypted" - - # Add M3U entry with extended info - entry_content += f'#EXTINF:-1 tvg-id="{channel_id}" tvg-logo="{channel_logo}" group-title="{provider_label}",{channel_name}\n' - - # Add KODIPROP for inputstream.adaptive (still needed for DASH playback) - entry_content += "#KODIPROP:inputstream=inputstream.adaptive\n" - - # Add stream URL - entry_content += f"{stream_url}\n" - - return entry_content - def _generate_m3u_decrypted_all(self, save_to_cache: bool = False) -> str: """Internal method to generate decrypted M3U for all providers.""" logger.info("Generating decrypted M3U playlist for all providers")