From 63997258222d1bedd57aeddd32290799bbc06411 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Mon, 8 Dec 2025 17:12:33 +0100 Subject: [PATCH] feat: Introduce a comprehensive video player screen supporting live, VOD, and series streams with full playback controls. --- lib/features/iptv/screens/player_screen.dart | 86 +++++++++----------- task.md | 6 ++ 2 files changed, 44 insertions(+), 48 deletions(-) create mode 100644 task.md diff --git a/lib/features/iptv/screens/player_screen.dart b/lib/features/iptv/screens/player_screen.dart index ee1f2bd..e4558ba 100644 --- a/lib/features/iptv/screens/player_screen.dart +++ b/lib/features/iptv/screens/player_screen.dart @@ -180,7 +180,9 @@ class _PlayerScreenState extends ConsumerState { // Check if we are using server-side transcoding for VOD/Series // If so, we must reload the player because we can't seek in a piped stream directly via JS final settings = ref.read(iptvSettingsProvider); - final isTranscoding = widget.streamType != StreamType.live && settings.streamQuality != StreamQuality.high; + // We now force transcoding (API stream) for ALL VOD content to ensure audio compatibility. + // So isTranscoding is effectively true for all non-live content. + final isTranscoding = widget.streamType != StreamType.live; if (isTranscoding) { debugPrint('Seeking in transcoded VOD - reloading player at ${value.round()}s'); @@ -345,54 +347,41 @@ class _PlayerScreenState extends ConsumerState { } else { // For VOD and Series - // CHECK QUALITY SETTING - // If Quality is NOT High, we force transcoding to ensure audio compatibility (AAC) - // This solves "No Sound" issues with AC3/DTS on mobile/web. - // NOTE: For MKV files with unsupported audio, user should select Low/Medium quality. - final settings = ref.read(iptvSettingsProvider); - final useTranscoding = settings.streamQuality != StreamQuality.high; + // FORCE API STREAMING (FFMPEG) + // We always use the backend /api/stream endpoint because it handles audio transcoding (to AAC) + // which is required for browsers to play content with AC3/DTS audio. + // For 'High' quality, the backend uses '-c:v copy', so video quality is preserved (Direct Stream). + setState(() { + _statusMessage = 'Preparing stream...'; + }); - if (useTranscoding) { - setState(() { - _statusMessage = 'Starting VOD Transcoding (Audio Fix)...'; - }); - - final baseUrl = html.window.location.origin; - final vodStreamUrl = widget.streamType == StreamType.vod - ? xtreamService.getVodStreamUrl(currentStreamId, widget.containerExtension) - : xtreamService.getSeriesStreamUrl(currentStreamId, widget.containerExtension); - - final encodedUrl = Uri.encodeComponent(vodStreamUrl); - final qualityParam = switch (settings.streamQuality) { - StreamQuality.low => 'low', - StreamQuality.medium => 'medium', - StreamQuality.high => 'high', - }; - - // Calculate start time for seeking support - // If override provided (seeking), use it. Else check provider resume. - double startSeconds = startTimeOverride ?? 0; - if (startTimeOverride == null && _contentId.isNotEmpty) { - final positions = ref.read(playbackPositionsProvider); - final saved = positions.getPosition(_contentId); - if (saved > 0) startSeconds = saved; - } - - // Construct URL with start param - // Note: We force extension to .ts for mpegts.js compatibility - final streamEndpoint = '$baseUrl/api/stream/$currentStreamId?url=$encodedUrl&quality=$qualityParam&start=${startSeconds.round()}&ext=.ts'; - - debugPrint('PlayerScreen: Streaming Transcoded VOD: $streamEndpoint'); - hlsUrl = streamEndpoint; - - } else { - // Direct Play (High Quality) - Original behavior - final streamUrl = widget.streamType == StreamType.vod - ? xtreamService.getVodStreamUrl(currentStreamId, widget.containerExtension) - : xtreamService.getSeriesStreamUrl(currentStreamId, widget.containerExtension); - hlsUrl = streamUrl; - debugPrint('PlayerScreen: Direct playback URL (High Quality): $hlsUrl'); + final baseUrl = html.window.location.origin; + final vodStreamUrl = widget.streamType == StreamType.vod + ? xtreamService.getVodStreamUrl(currentStreamId, widget.containerExtension) + : xtreamService.getSeriesStreamUrl(currentStreamId, widget.containerExtension); + + final encodedUrl = Uri.encodeComponent(vodStreamUrl); + final qualityParam = switch (settings.streamQuality) { + StreamQuality.low => 'low', + StreamQuality.medium => 'medium', + StreamQuality.high => 'high', + }; + + // Calculate start time for seeking support + // If override provided (seeking), use it. Else check provider resume. + double startSeconds = startTimeOverride ?? 0; + if (startTimeOverride == null && _contentId.isNotEmpty) { + final positions = ref.read(playbackPositionsProvider); + final saved = positions.getPosition(_contentId); + if (saved > 0) startSeconds = saved; } + + // Construct URL with start param + // Note: We force extension to .ts for mpegts.js compatibility + final streamEndpoint = '$baseUrl/api/stream/$currentStreamId?url=$encodedUrl&quality=$qualityParam&start=${startSeconds.round()}&ext=.ts'; + + debugPrint('PlayerScreen: Streaming VOD (Audio Fix enabled): $streamEndpoint'); + hlsUrl = streamEndpoint; } setState(() { @@ -418,7 +407,8 @@ class _PlayerScreenState extends ConsumerState { // If Direct Play, pass startTime. final settings = ref.read(iptvSettingsProvider); - final isTranscoding = widget.streamType != StreamType.live && settings.streamQuality != StreamQuality.high; + // We force transcoding/proxy usage for all VOD/Series now + final isTranscoding = widget.streamType != StreamType.live; final encodedHlsUrl = Uri.encodeComponent(hlsUrl); final playerUrl = (!isTranscoding && startTime > 0) diff --git a/task.md b/task.md new file mode 100644 index 0000000..692cccb --- /dev/null +++ b/task.md @@ -0,0 +1,6 @@ +# Audio Investigation in Movies + +- [ ] Investigate `player_screen.dart` to understand how the player is initialized. +- [ ] Determine differences between Movie, Series, and TV playback logic. +- [ ] Check for codec support or transcoding logic. +- [ ] Fix the audio issue for movies.