From ed7dae601a968885566418f449eb0d77377c4630 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 1 May 2026 06:43:58 +0200 Subject: [PATCH] fix: avoid duplicate play next countdown close #942 --- lib/screens/video_player_screen.dart | 10 +++++-- .../video_controls/video_controls.dart | 28 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index eb3e1f9e..f8214f1e 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -2528,7 +2528,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin } } - void _onVideoCompleted(bool completed) async { + void _onVideoCompleted(bool completed, {bool skipAutoPlayCountdown = false}) async { // Live TV streams are continuous — ignore spurious EOF events caused by // inter-segment gaps in the chunked MKV transcode stream. if (widget.isLive) return; @@ -2562,6 +2562,11 @@ class VideoPlayerScreenState extends State with WidgetsBindin final settings = await SettingsService.getInstance(); final autoPlayEnabled = settings.read(SettingsService.autoPlayNextEpisode); + if (skipAutoPlayCountdown && autoPlayEnabled) { + unawaited(_playNext()); + return; + } + if (!mounted) return; setState(() { _showPlayNextDialog = true; @@ -3787,7 +3792,8 @@ class VideoPlayerScreenState extends State with WidgetsBindin } }, onBack: _handleBackButton, - onReachedEnd: () => _onVideoCompleted(true), + onReachedEnd: ({skipAutoPlayCountdown = false}) => + _onVideoCompleted(true, skipAutoPlayCountdown: skipAutoPlayCountdown), canControl: canControl, hasFirstFrame: _hasFirstFrame, playNextFocusNode: _showPlayNextDialog ? _playNextConfirmFocusNode : null, diff --git a/lib/widgets/video_controls/video_controls.dart b/lib/widgets/video_controls/video_controls.dart index 65db3b6e..59143837 100644 --- a/lib/widgets/video_controls/video_controls.dart +++ b/lib/widgets/video_controls/video_controls.dart @@ -88,7 +88,7 @@ Widget plexVideoControlsBuilder( Function(SubtitleTrack)? onSecondarySubtitleTrackChanged, Function(Duration position)? onSeekCompleted, VoidCallback? onBack, - VoidCallback? onReachedEnd, + void Function({required bool skipAutoPlayCountdown})? onReachedEnd, bool canControl = true, ValueNotifier? hasFirstFrame, FocusNode? playNextFocusNode, @@ -219,9 +219,9 @@ class PlexVideoControls extends StatefulWidget { final VoidCallback? onBack; /// Called when the video has effectively reached the end (e.g. credits extend - /// to EOF and can't be seeked past). Parent should route this into its - /// normal completion flow so the auto-play-next setting is honored. - final VoidCallback? onReachedEnd; + /// to EOF and can't be seeked past). Parent should route this into its normal + /// completion flow so the auto-play-next setting is honored. + final void Function({required bool skipAutoPlayCountdown})? onReachedEnd; /// Whether the user can control playback (false in host-only mode for non-host). final bool canControl; @@ -582,7 +582,7 @@ class _PlexVideoControlsState extends State with WindowListen }); } - Future _skipMarker() async { + Future _skipMarker({bool skipAutoPlayCountdown = false}) async { if (_currentMarker == null) return; final marker = _currentMarker!; @@ -591,10 +591,14 @@ class _PlexVideoControlsState extends State with WindowListen final isAtEnd = duration > Duration.zero && (duration - endTime).inMilliseconds <= 1000; if (marker.isCredits && isAtEnd) { - // Seeking to EOF is unreliable due to position stream throttling, - // so pause and defer to the parent's completion flow. - await widget.player.pause(); - widget.onReachedEnd?.call(); + if (!skipAutoPlayCountdown && widget.onNext != null) { + widget.onNext!.call(); + } else { + // Seeking to EOF is unreliable due to position stream throttling, + // so pause and defer to the parent's completion flow. + await widget.player.pause(); + widget.onReachedEnd?.call(skipAutoPlayCountdown: skipAutoPlayCountdown); + } } else { await _seekToPosition(endTime); } @@ -632,7 +636,7 @@ class _PlexVideoControlsState extends State with WindowListen if (timer.tick >= totalTicks) { timer.cancel(); - _performAutoSkip(); + _performAutoSkip(skipAutoPlayCountdown: true); } }); } @@ -666,9 +670,9 @@ class _PlexVideoControlsState extends State with WindowListen } /// Perform the appropriate skip action based on marker type and next episode availability - void _performAutoSkip() { + void _performAutoSkip({bool skipAutoPlayCountdown = false}) { if (_currentMarker == null) return; - unawaited(_skipMarker()); + unawaited(_skipMarker(skipAutoPlayCountdown: skipAutoPlayCountdown)); } /// Check if auto-skip should be active for the current marker