From 0f97723379b7e0575fb9acc8162e47132afd7931 Mon Sep 17 00:00:00 2001 From: Alex Rans Date: Mon, 20 Jul 2026 17:55:52 +0200 Subject: [PATCH 1/2] Previous and next chapter buttons --- .../video_player/video_player_controls.dart | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lib/screens/video_player/video_player_controls.dart b/lib/screens/video_player/video_player_controls.dart index a119adb7a..98edf3ca2 100644 --- a/lib/screens/video_player/video_player_controls.dart +++ b/lib/screens/video_player/video_player_controls.dart @@ -11,6 +11,7 @@ import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:fladder/models/item_base_model.dart'; +import 'package:fladder/models/items/chapters_model.dart'; import 'package:fladder/models/items/media_segments_model.dart'; import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/media_playback_model.dart'; @@ -428,6 +429,7 @@ class _DesktopControlsState extends ConsumerState { ), ), previousButton, + previousChapterButton(ref), seekBackwardButton(ref), IconButton.filledTonal( iconSize: 38, @@ -439,6 +441,7 @@ class _DesktopControlsState extends ConsumerState { ), ), seekForwardButton(ref), + nextChapterButton(ref), nextVideoButton, Flexible( flex: 2, @@ -683,6 +686,66 @@ class _DesktopControlsState extends ConsumerState { ], ), ); + } + + Widget nextChapterButton(WidgetRef ref) { + return Consumer(builder: (context, ref2, child) { + final chapters = ref.read(playBackModel.select((value) => value?.chapters)); + final enabled = chapters?.isNotEmpty == true; + return IconButton( + onPressed: enabled ? () => _nextChapter(ref) : null, + iconSize: 30, + tooltip: context.localized.nextChapter, + icon: const Icon(IconsaxPlusLinear.arrow_right_1), + ); + }); + } + + Widget previousChapterButton(WidgetRef ref) { + return Consumer(builder: (context, ref2, child) { + final chapters = ref.read(playBackModel.select((value) => value?.chapters)); + final enabled = chapters?.isNotEmpty == true; + return IconButton( + onPressed: enabled ? () => _previousChapter(ref) : null, + iconSize: 30, + tooltip: context.localized.nextChapter, + icon: const Icon(IconsaxPlusLinear.arrow_left_3), + ); + }); + } + + void _previousChapter(WidgetRef ref) { + final chapters = ref.read(playBackModel.select((value) => value?.chapters)) ?? []; + if (chapters.isEmpty) return; + final position = ref.read(mediaPlaybackProvider).position; + final threshold = const Duration(milliseconds: 3000); + Chapter? prev; + final earlier = chapters.where((c) => c.startPosition < position - threshold).toList(); + if (earlier.isNotEmpty) prev = earlier.last; + final target = prev ?? chapters.first; + ref.read(videoPlayerProvider).seek(target.startPosition); + resetTimer(); + } + + void _nextChapter(WidgetRef ref) { + final chapters = ref.read(playBackModel.select((value) => value?.chapters)) ?? []; + if (chapters.isEmpty) return; + final position = ref.read(mediaPlaybackProvider).position; + Chapter? next; + for (final c in chapters) { + if (c.startPosition > position) { + next = c; + break; + } + } + if (next != null) { + ref.read(videoPlayerProvider).seek(next.startPosition); + } else { + // fallback: load next video + final nextVideo = ref.read(playBackModel.select((value) => value?.nextVideo)); + if (nextVideo != null) ref.read(playbackModelHelper).loadNewVideo(nextVideo); + } + resetTimer(); } void skipToSegmentEnd(MediaSegment? mediaSegment, String? segmentId) { From 400d498173d035014352ac8904878dfb3ae29783 Mon Sep 17 00:00:00 2001 From: Alex Rans Date: Mon, 20 Jul 2026 18:09:44 +0200 Subject: [PATCH 2/2] Formatting --- lib/screens/video_player/video_player_controls.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/video_player/video_player_controls.dart b/lib/screens/video_player/video_player_controls.dart index 98edf3ca2..4d0d5bc7a 100644 --- a/lib/screens/video_player/video_player_controls.dart +++ b/lib/screens/video_player/video_player_controls.dart @@ -686,7 +686,7 @@ class _DesktopControlsState extends ConsumerState { ], ), ); - } + } Widget nextChapterButton(WidgetRef ref) { return Consumer(builder: (context, ref2, child) {