From b244f1f80715bb6feae7a3d800fc2f0d5e46184f Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sun, 7 Dec 2025 15:29:00 +0100 Subject: [PATCH] feat: introduce core server with API, media streaming, Xtream proxy, and player UI --- bin/server.dart | 16 +++++++++++++--- lib/features/iptv/screens/player_screen.dart | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bin/server.dart b/bin/server.dart index 8a145c5..3a151cc 100644 --- a/bin/server.dart +++ b/bin/server.dart @@ -279,10 +279,17 @@ Future _streamVideoFile(Uri targetUrl, String? rangeHeader) async { // Forward the Range header for seeking support if (rangeHeader != null && rangeHeader.isNotEmpty) { - req.headers.set('Range', rangeHeader); + // Ensure we request bytes + if (!rangeHeader.startsWith('bytes=')) { + rangeHeader = 'bytes=$rangeHeader'; + } + req.headers.set(HttpHeaders.rangeHeader, rangeHeader); print('Video streaming with Range: $rangeHeader'); } + // Disable auto-decompression to strictly proxy the stream + client.autoUncompress = false; + final response = await req.close(); // Get content type from response @@ -303,11 +310,14 @@ Future _streamVideoFile(Uri targetUrl, String? rangeHeader) async { } // Add Content-Range if this is a partial response (206) - final contentRange = response.headers.value('content-range'); + final contentRange = response.headers.value(HttpHeaders.contentRangeHeader); if (contentRange != null) { - responseHeaders['content-range'] = contentRange; + responseHeaders[HttpHeaders.contentRangeHeader] = contentRange; } + // Propagate Accept-Ranges + responseHeaders[HttpHeaders.acceptRangesHeader] = 'bytes'; + // Return appropriate status code (200 for full, 206 for partial) return Response( response.statusCode, diff --git a/lib/features/iptv/screens/player_screen.dart b/lib/features/iptv/screens/player_screen.dart index e794e04..3de5d60 100644 --- a/lib/features/iptv/screens/player_screen.dart +++ b/lib/features/iptv/screens/player_screen.dart @@ -735,11 +735,11 @@ class _PlayerScreenState extends ConsumerState { // EPG Overlay if (widget.streamType == StreamType.live) Positioned( - bottom: 0, + bottom: 100, // Move up above progress bar area/OS safe area left: 0, right: 0, child: Container( - padding: const EdgeInsets.only(bottom: 20), + padding: const EdgeInsets.only(bottom: 8), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.bottomCenter,