From 947da2390f9b5158631efad6c111c764f5b881ac Mon Sep 17 00:00:00 2001 From: PartyDonut <42371342+PartyDonut@users.noreply.github.com> Date: Sat, 17 May 2025 16:36:33 +0200 Subject: [PATCH] feat: Improve segments visibility logic (#346) Co-authored-by: PartyDonut --- lib/models/items/media_segments_model.dart | 15 ++++++++++++++- .../video_player_controls_extras.dart | 10 +++++++--- .../video_player/video_player_controls.dart | 17 +++++++++-------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/models/items/media_segments_model.dart b/lib/models/items/media_segments_model.dart index 971b883..b3e9284 100644 --- a/lib/models/items/media_segments_model.dart +++ b/lib/models/items/media_segments_model.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto; import 'package:fladder/util/localization_helper.dart'; @@ -39,7 +40,19 @@ class MediaSegment with _$MediaSegment { bool inRange(Duration position) => (position.compareTo(start) >= 0 && position.compareTo(end) <= 0); - bool forceShow(Duration position) => (position - start).inSeconds < (end - start).inSeconds * 0.20; + SegmentVisibility visibility(Duration position, {bool force = false}) { + if (force) return SegmentVisibility.visible; + var difference = (position - start); + if (difference > const Duration(minutes: 1, seconds: 30)) return SegmentVisibility.hidden; + Duration clamp = ((end - start) * 0.20).clamp(Duration.zero, const Duration(minutes: 1)); + return difference < clamp ? SegmentVisibility.visible : SegmentVisibility.partially; + } +} + +enum SegmentVisibility { + hidden, + partially, + visible; } const Map defaultSegmentSkipValues = { diff --git a/lib/screens/video_player/components/video_player_controls_extras.dart b/lib/screens/video_player/components/video_player_controls_extras.dart index 32f0ad2..b44e790 100644 --- a/lib/screens/video_player/components/video_player_controls_extras.dart +++ b/lib/screens/video_player/components/video_player_controls_extras.dart @@ -66,13 +66,13 @@ class OpenQueueButton extends ConsumerWidget { class SkipSegmentButton extends ConsumerWidget { final MediaSegment? segment; final SegmentSkip? skipType; - final bool isOverlayVisible; + final SegmentVisibility visibility; final Function() pressedSkip; const SkipSegmentButton({ required this.segment, this.skipType, - required this.isOverlayVisible, + required this.visibility, required this.pressedSkip, super.key, }); @@ -82,7 +82,11 @@ class SkipSegmentButton extends ConsumerWidget { return AnimatedFadeSize( child: segment != null && skipType != SegmentSkip.none ? AnimatedOpacity( - opacity: isOverlayVisible ? 1 : 0.15, + opacity: switch (visibility) { + SegmentVisibility.hidden => 0, + SegmentVisibility.partially => 0.15, + SegmentVisibility.visible => 1.0, + }, duration: const Duration(milliseconds: 500), child: ElevatedButton( onPressed: pressedSkip, diff --git a/lib/screens/video_player/video_player_controls.dart b/lib/screens/video_player/video_player_controls.dart index c8e9de4..11b6c83 100644 --- a/lib/screens/video_player/video_player_controls.dart +++ b/lib/screens/video_player/video_player_controls.dart @@ -168,11 +168,13 @@ class _DesktopControlsState extends ConsumerState { builder: (context, ref, child) { final position = ref.watch(mediaPlaybackProvider.select((value) => value.position)); MediaSegment? segment = mediaSegments?.atPosition(position); - bool forceShow = segment?.forceShow(position) ?? false; + SegmentVisibility forceShow = + segment?.visibility(position, force: showOverlay) ?? SegmentVisibility.hidden; final segmentSkipType = ref .watch(videoPlayerSettingsProvider.select((value) => value.segmentSkipSettings[segment?.type])); - final autoSkip = - forceShow == true && segmentSkipType == SegmentSkip.skip && player.lastState?.buffering == false; + final autoSkip = forceShow != SegmentVisibility.hidden && + segmentSkipType == SegmentSkip.skip && + player.lastState?.buffering == false; if (autoSkip) { skipToSegmentEnd(segment); } @@ -185,7 +187,7 @@ class _DesktopControlsState extends ConsumerState { child: SkipSegmentButton( segment: segment, skipType: segmentSkipType, - isOverlayVisible: forceShow ? true : showOverlay, + visibility: forceShow, pressedSkip: () => skipToSegmentEnd(segment), ), ), @@ -441,10 +443,9 @@ class _DesktopControlsState extends ConsumerState { final List details = [ if (AdaptiveLayout.of(context).isDesktop) item?.label(context), mediaPlayback.duration.inMinutes > 1 - ? context.localized.endsAt(DateTime.now().add( - Duration(milliseconds: - (mediaPlayback.duration.inMilliseconds - mediaPlayback.position.inMilliseconds) ~/ ref.read(playbackRateProvider))) - ) + ? context.localized.endsAt(DateTime.now().add(Duration( + milliseconds: (mediaPlayback.duration.inMilliseconds - mediaPlayback.position.inMilliseconds) ~/ + ref.read(playbackRateProvider)))) : null ]; return Column(