From a9a1127d6bd5586b9779d5d43752704cecb6786a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 11 Mar 2026 07:31:42 +0100 Subject: [PATCH] fix(server): add direct .ts proxy route for recordings and fix Cascade fall-through --- bin/api/proxy_handler.dart | 4 ++-- bin/api/streaming_handler.dart | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/bin/api/proxy_handler.dart b/bin/api/proxy_handler.dart index 2e17ed1..0a2fa12 100644 --- a/bin/api/proxy_handler.dart +++ b/bin/api/proxy_handler.dart @@ -81,8 +81,8 @@ class ProxyHandler { // Only handle /api/xtream/* requests // CRITICAL: Check path BEFORE anything else so non-proxy requests fall through to static handler if (!path.startsWith('api/xtream/')) { - // Return 404 so Cascade tries next handler (staticHandler) - return Response.notFound('Not an xtream proxy request'); + // Return 404 so Cascade tries next handler (streamingRouter) + return Response.notFound(null); } // NOTE: Authentication REMOVED from proxy to allow browser-initiated requests (img src, etc.) diff --git a/bin/api/streaming_handler.dart b/bin/api/streaming_handler.dart index 82fbc84..97e8aa5 100644 --- a/bin/api/streaming_handler.dart +++ b/bin/api/streaming_handler.dart @@ -212,6 +212,33 @@ Handler createLiveStreamHandler( }); }); + // Route: /api/live/{streamId}.ts (Direct Proxy for internal recordings or raw playback) + router.get('/.ts', (Request request, String streamId) async { + final playlist = await getPlaylist(request); + if (playlist == null) return Response.forbidden('No playlist'); + + final targetUrl = + '${playlist.dns}/live/${playlist.username}/${playlist.password}/$streamId.ts'; + print('[Live Proxy] Forwarding $streamId: $targetUrl'); + + final client = http.Client(); + final proxyRequest = http.Request('GET', Uri.parse(targetUrl)); + proxyRequest.headers['User-Agent'] = 'VLC/3.0.18 LibVLC/3.0.18'; + proxyRequest.headers['Accept'] = '*/*'; + + final response = await client.send(proxyRequest); + + return Response( + response.statusCode, + body: response.stream, + headers: { + 'Content-Type': 'video/mp2t', + 'Access-Control-Allow-Origin': '*', + 'Connection': 'keep-alive', + }, + ); + }); + // Route: /api/live/{streamId}/{segment} router.get('//', (Request request, String streamId, String segment) async {