diff --git a/lib/main.dart b/lib/main.dart index 9d86ac53..ff9f8314 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,8 +7,6 @@ import 'package:url_launcher/url_launcher.dart'; import 'screens/main_screen.dart'; import 'screens/auth_screen.dart'; import 'services/storage_service.dart'; -import 'services/plex_auth_service.dart'; -import 'services/server_connection_service.dart'; import 'services/macos_titlebar_service.dart'; import 'services/fullscreen_state_manager.dart'; import 'services/update_service.dart'; @@ -26,7 +24,6 @@ import 'services/data_aggregation_service.dart'; import 'services/server_registry.dart'; import 'utils/language_codes.dart'; import 'utils/app_logger.dart'; -import 'utils/provider_extensions.dart'; import 'utils/orientation_helper.dart'; import 'i18n/strings.g.dart'; diff --git a/lib/models/plex_server_info.dart b/lib/models/plex_server_info.dart deleted file mode 100644 index 35b586db..00000000 --- a/lib/models/plex_server_info.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'plex_server_info.g.dart'; - -@JsonSerializable() -class PlexServerInfo { - final String name; - final String? host; - final int? port; - final String? machineIdentifier; - final String version; - final bool? owned; - final bool? https; - - PlexServerInfo({ - required this.name, - this.host, - this.port, - this.machineIdentifier, - required this.version, - this.owned, - this.https, - }); - - factory PlexServerInfo.fromJson(Map json) => - _$PlexServerInfoFromJson(json); - - Map toJson() => _$PlexServerInfoToJson(this); -} diff --git a/lib/models/plex_server_info.g.dart b/lib/models/plex_server_info.g.dart deleted file mode 100644 index d36d3126..00000000 --- a/lib/models/plex_server_info.g.dart +++ /dev/null @@ -1,29 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'plex_server_info.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -PlexServerInfo _$PlexServerInfoFromJson(Map json) => - PlexServerInfo( - name: json['name'] as String, - host: json['host'] as String?, - port: (json['port'] as num?)?.toInt(), - machineIdentifier: json['machineIdentifier'] as String?, - version: json['version'] as String, - owned: json['owned'] as bool?, - https: json['https'] as bool?, - ); - -Map _$PlexServerInfoToJson(PlexServerInfo instance) => - { - 'name': instance.name, - 'host': instance.host, - 'port': instance.port, - 'machineIdentifier': instance.machineIdentifier, - 'version': instance.version, - 'owned': instance.owned, - 'https': instance.https, - }; diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart index e24543d9..79c0db91 100644 --- a/lib/screens/auth_screen.dart +++ b/lib/screens/auth_screen.dart @@ -7,7 +7,6 @@ import 'package:qr_flutter/qr_flutter.dart'; import '../services/plex_auth_service.dart'; import '../services/storage_service.dart'; import '../services/server_registry.dart'; -import '../services/multi_server_manager.dart'; import '../providers/multi_server_provider.dart'; import '../providers/plex_client_provider.dart'; import '../i18n/strings.g.dart'; diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index 88c9dd4a..274940b4 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -5,16 +5,12 @@ import 'package:provider/provider.dart'; import '../client/plex_client.dart'; import '../models/plex_metadata.dart'; import '../models/plex_hub.dart'; -import '../providers/plex_client_provider.dart'; import '../providers/multi_server_provider.dart'; -import '../services/storage_service.dart'; -import '../services/plex_auth_service.dart'; import '../widgets/media_card.dart'; import '../widgets/desktop_app_bar.dart'; import '../widgets/user_avatar_widget.dart'; import '../widgets/horizontal_scroll_with_arrows.dart'; import '../widgets/hub_section.dart'; -import '../widgets/server_badge.dart'; import 'profile_switch_screen.dart'; import '../providers/user_profile_provider.dart'; import '../providers/settings_provider.dart'; diff --git a/lib/screens/libraries_screen.dart b/lib/screens/libraries_screen.dart index cbeb3bb6..b9bb047f 100644 --- a/lib/screens/libraries_screen.dart +++ b/lib/screens/libraries_screen.dart @@ -1111,7 +1111,6 @@ class _LibraryManagementSheet extends StatefulWidget { class _LibraryManagementSheetState extends State<_LibraryManagementSheet> { late List _tempLibraries; List? _serverOrder; - bool _isLoadingServerOrder = true; @override void initState() { @@ -1128,7 +1127,6 @@ class _LibraryManagementSheetState extends State<_LibraryManagementSheet> { if (mounted) { setState(() { _serverOrder = savedOrder; - _isLoadingServerOrder = false; }); } } @@ -1441,43 +1439,6 @@ class _LibraryManagementSheetState extends State<_LibraryManagementSheet> { ); } - /// Reorder libraries within a server group - void _reorderLibrariesInServer( - String serverKey, - List serverLibraries, - int oldIndex, - int newIndex, - ) { - setState(() { - if (newIndex > oldIndex) { - newIndex -= 1; - } - - // Get the library being moved - final library = serverLibraries[oldIndex]; - - // Find global indices - final globalOldIndex = _tempLibraries.indexOf(library); - - // Calculate the new global index - // We need to find where this library should go in the global list - final targetLibrary = newIndex < serverLibraries.length - ? serverLibraries[newIndex] - : serverLibraries.last; - final globalNewIndex = _tempLibraries.indexOf(targetLibrary); - - // Reorder in global list - _tempLibraries.removeAt(globalOldIndex); - _tempLibraries.insert( - globalOldIndex < globalNewIndex ? globalNewIndex : globalNewIndex, - library, - ); - }); - - // Apply immediately - widget.onReorder(_tempLibraries); - } - /// Build a single library tile Widget _buildLibraryTile( PlexLibrary library, diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 8d5687d8..9cc02966 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -3,9 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../client/plex_client.dart'; import '../i18n/strings.g.dart'; -import '../mixins/item_updatable.dart'; import '../mixins/refreshable.dart'; import '../models/plex_metadata.dart'; import '../providers/multi_server_provider.dart'; @@ -13,10 +11,8 @@ import '../providers/settings_provider.dart'; import '../services/settings_service.dart'; import '../utils/app_logger.dart'; import '../utils/grid_cross_axis_extent.dart'; -import '../utils/provider_extensions.dart'; import '../widgets/desktop_app_bar.dart'; import '../widgets/media_card.dart'; -import '../widgets/server_badge.dart'; class SearchScreen extends StatefulWidget { const SearchScreen({super.key}); diff --git a/lib/screens/season_detail_screen.dart b/lib/screens/season_detail_screen.dart index 1c24a4bd..99f6ec65 100644 --- a/lib/screens/season_detail_screen.dart +++ b/lib/screens/season_detail_screen.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:provider/provider.dart'; import '../client/plex_client.dart'; import '../models/plex_metadata.dart'; import '../utils/provider_extensions.dart'; import '../utils/video_player_navigation.dart'; import '../utils/duration_formatter.dart'; -import '../utils/app_logger.dart'; import '../widgets/desktop_app_bar.dart'; import '../widgets/media_context_menu.dart'; import '../mixins/item_updatable.dart'; diff --git a/lib/services/keyboard_shortcuts_service.dart b/lib/services/keyboard_shortcuts_service.dart index 86361db3..a8593acc 100644 --- a/lib/services/keyboard_shortcuts_service.dart +++ b/lib/services/keyboard_shortcuts_service.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:media_kit/media_kit.dart'; import 'package:hotkey_manager/hotkey_manager.dart'; import 'settings_service.dart'; +import '../utils/player_utils.dart'; class KeyboardShortcutsService { static KeyboardShortcutsService? _instance; @@ -230,21 +231,6 @@ class KeyboardShortcutsService { return KeyEventResult.ignored; } - /// Seeks by the given offset (can be positive or negative) while clamping - /// the result between 0 and the video duration - void _seekWithClamping(Player player, Duration offset) { - final currentPosition = player.state.position; - final duration = player.state.duration; - final newPosition = currentPosition + offset; - - // Clamp between 0 and video duration - final clampedPosition = newPosition.isNegative - ? Duration.zero - : (newPosition > duration ? duration : newPosition); - - player.seek(clampedPosition); - } - void _executeAction( String action, Player player, @@ -270,16 +256,16 @@ class KeyboardShortcutsService { _settingsService.setVolume(newVolume); break; case 'seek_forward': - _seekWithClamping(player, Duration(seconds: _seekTimeSmall)); + seekWithClamping(player, Duration(seconds: _seekTimeSmall)); break; case 'seek_backward': - _seekWithClamping(player, Duration(seconds: -_seekTimeSmall)); + seekWithClamping(player, Duration(seconds: -_seekTimeSmall)); break; case 'seek_forward_large': - _seekWithClamping(player, Duration(seconds: _seekTimeLarge)); + seekWithClamping(player, Duration(seconds: _seekTimeLarge)); break; case 'seek_backward_large': - _seekWithClamping(player, Duration(seconds: -_seekTimeLarge)); + seekWithClamping(player, Duration(seconds: -_seekTimeLarge)); break; case 'fullscreen_toggle': onToggleFullscreen?.call(); diff --git a/lib/services/server_registry.dart b/lib/services/server_registry.dart index c871f501..ca34b9e6 100644 --- a/lib/services/server_registry.dart +++ b/lib/services/server_registry.dart @@ -7,9 +7,6 @@ import 'storage_service.dart'; /// Centralized server configuration registry /// Manages which servers are available, enabled/disabled, and their configurations class ServerRegistry { - static const String _keyServersList = 'servers_list'; - static const String _keyEnabledServers = 'enabled_servers'; - final StorageService _storage; ServerRegistry(this._storage); diff --git a/lib/utils/player_utils.dart b/lib/utils/player_utils.dart new file mode 100644 index 00000000..4f1de3e1 --- /dev/null +++ b/lib/utils/player_utils.dart @@ -0,0 +1,16 @@ +import 'package:media_kit/media_kit.dart'; + +/// Seeks by the given offset (can be positive or negative) while clamping +/// the result between 0 and the video duration +void seekWithClamping(Player player, Duration offset) { + final currentPosition = player.state.position; + final duration = player.state.duration; + final newPosition = currentPosition + offset; + + // Clamp between 0 and video duration + final clampedPosition = newPosition.isNegative + ? Duration.zero + : (newPosition > duration ? duration : newPosition); + + player.seek(clampedPosition); +} diff --git a/lib/utils/video_control_icons.dart b/lib/utils/video_control_icons.dart new file mode 100644 index 00000000..a31b9f8b --- /dev/null +++ b/lib/utils/video_control_icons.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +/// Get the replay icon based on the duration +/// Returns numbered icons (replay_5, replay_10, replay_30) when available, +/// otherwise returns generic replay icon +IconData getReplayIcon(int seconds) { + switch (seconds) { + case 5: + return Icons.replay_5; + case 10: + return Icons.replay_10; + case 30: + return Icons.replay_30; + default: + return Icons.replay; // Generic icon for custom durations + } +} + +/// Get the forward icon based on the duration +/// Returns numbered icons (forward_5, forward_10, forward_30) when available, +/// otherwise returns generic forward icon +IconData getForwardIcon(int seconds) { + switch (seconds) { + case 5: + return Icons.forward_5; + case 10: + return Icons.forward_10; + case 30: + return Icons.forward_30; + default: + return Icons.forward; // Generic icon for custom durations + } +} diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index cbef4693..f5c12bee 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -4,8 +4,6 @@ import 'package:provider/provider.dart'; import '../client/plex_client.dart'; import '../models/plex_metadata.dart'; import '../models/plex_playlist.dart'; -import '../providers/plex_client_provider.dart'; -import '../providers/multi_server_provider.dart'; import '../providers/playback_state_provider.dart'; import '../utils/provider_extensions.dart'; import '../utils/app_logger.dart'; diff --git a/lib/widgets/playlist_item_card.dart b/lib/widgets/playlist_item_card.dart index d612528a..46682a3f 100644 --- a/lib/widgets/playlist_item_card.dart +++ b/lib/widgets/playlist_item_card.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../client/plex_client.dart'; import '../models/plex_metadata.dart'; diff --git a/lib/widgets/video_controls/desktop_video_controls.dart b/lib/widgets/video_controls/desktop_video_controls.dart index 7380b6c8..ea06a9ae 100644 --- a/lib/widgets/video_controls/desktop_video_controls.dart +++ b/lib/widgets/video_controls/desktop_video_controls.dart @@ -10,7 +10,7 @@ import '../../utils/desktop_window_padding.dart'; import '../../utils/duration_formatter.dart'; import '../../i18n/strings.g.dart'; import '../app_bar_back_button.dart'; -import 'painters/chapter_marker_painter.dart'; +import 'widgets/timeline_slider.dart'; /// Desktop-specific video controls layout with top bar and bottom controls class DesktopVideoControls extends StatelessWidget { @@ -188,9 +188,13 @@ class DesktopVideoControls extends StatelessWidget { ), const SizedBox(width: 12), Expanded( - child: _buildTimelineWithChapters( + child: TimelineSlider( position: position, duration: duration, + chapters: chapters, + chaptersLoaded: chaptersLoaded, + onSeek: onSeek, + onSeekEnd: onSeekEnd, ), ), const SizedBox(width: 12), @@ -313,88 +317,4 @@ class DesktopVideoControls extends StatelessWidget { ), ); } - - Widget _buildTimelineWithChapters({ - required Duration position, - required Duration duration, - }) { - return Stack( - alignment: Alignment.center, - children: [ - // Chapter markers layer - if (chaptersLoaded && - chapters.isNotEmpty && - duration.inMilliseconds > 0) - Positioned.fill( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( - children: - chapters.map((chapter) { - final chapterPosition = - (chapter.startTimeOffset ?? 0) / - duration.inMilliseconds; - return Expanded( - flex: (chapterPosition * 1000).toInt(), - child: const SizedBox(), - ); - }).toList()..add( - Expanded( - flex: - 1000 - - chapters.fold( - 0, - (sum, chapter) => - sum + - ((chapter.startTimeOffset ?? 0) / - duration.inMilliseconds * - 1000) - .toInt(), - ), - child: const SizedBox(), - ), - ), - ), - ), - ), - // Slider - Semantics( - label: t.videoControls.timelineSlider, - slider: true, - child: Slider( - value: duration.inMilliseconds > 0 - ? position.inMilliseconds.toDouble() - : 0.0, - min: 0.0, - max: duration.inMilliseconds.toDouble(), - onChanged: (value) { - onSeek(Duration(milliseconds: value.toInt())); - }, - onChangeEnd: (value) { - onSeekEnd(Duration(milliseconds: value.toInt())); - }, - activeColor: Colors.white, - inactiveColor: Colors.white.withValues(alpha: 0.3), - ), - ), - // Chapter marker indicators - if (chaptersLoaded && - chapters.isNotEmpty && - duration.inMilliseconds > 0) - Positioned.fill( - child: IgnorePointer( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: CustomPaint( - painter: ChapterMarkerPainter( - chapters: chapters, - duration: duration, - ), - ), - ), - ), - ), - ], - ); - } } diff --git a/lib/widgets/video_controls/mobile_video_controls.dart b/lib/widgets/video_controls/mobile_video_controls.dart index 62eab711..de12f53a 100644 --- a/lib/widgets/video_controls/mobile_video_controls.dart +++ b/lib/widgets/video_controls/mobile_video_controls.dart @@ -6,9 +6,11 @@ import 'package:media_kit/media_kit.dart'; import '../../models/plex_media_info.dart'; import '../../models/plex_metadata.dart'; import '../../utils/duration_formatter.dart'; +import '../../utils/player_utils.dart'; +import '../../utils/video_control_icons.dart'; import '../../i18n/strings.g.dart'; import '../app_bar_back_button.dart'; -import 'painters/chapter_marker_painter.dart'; +import 'widgets/timeline_slider.dart'; /// Mobile video controls layout for Plex video player /// @@ -142,13 +144,13 @@ class MobileVideoControls extends StatelessWidget { excludeSemantics: true, child: IconButton( icon: Icon( - _getReplayIcon(seekTimeSmall), + getReplayIcon(seekTimeSmall), color: Colors.white, size: 48, ), iconSize: 48, onPressed: () { - _seekWithClamping(Duration(seconds: -seekTimeSmall)); + seekWithClamping(player, Duration(seconds: -seekTimeSmall)); }, ), ), @@ -198,13 +200,13 @@ class MobileVideoControls extends StatelessWidget { excludeSemantics: true, child: IconButton( icon: Icon( - _getForwardIcon(seekTimeSmall), + getForwardIcon(seekTimeSmall), color: Colors.white, size: 48, ), iconSize: 48, onPressed: () { - _seekWithClamping(Duration(seconds: seekTimeSmall)); + seekWithClamping(player, Duration(seconds: seekTimeSmall)); }, ), ), @@ -234,9 +236,13 @@ class MobileVideoControls extends StatelessWidget { return Column( children: [ - _buildTimelineWithChapters( + TimelineSlider( position: position, duration: duration, + chapters: chapters, + chaptersLoaded: chaptersLoaded, + onSeek: onSeek, + onSeekEnd: onSeekEnd, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), @@ -270,90 +276,6 @@ class MobileVideoControls extends StatelessWidget { ); } - Widget _buildTimelineWithChapters({ - required Duration position, - required Duration duration, - }) { - return Stack( - alignment: Alignment.center, - children: [ - // Chapter markers layer - if (chaptersLoaded && - chapters.isNotEmpty && - duration.inMilliseconds > 0) - Positioned.fill( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( - children: - chapters.map((chapter) { - final chapterPosition = - (chapter.startTimeOffset ?? 0) / - duration.inMilliseconds; - return Expanded( - flex: (chapterPosition * 1000).toInt(), - child: const SizedBox(), - ); - }).toList()..add( - Expanded( - flex: - 1000 - - chapters.fold( - 0, - (sum, chapter) => - sum + - ((chapter.startTimeOffset ?? 0) / - duration.inMilliseconds * - 1000) - .toInt(), - ), - child: const SizedBox(), - ), - ), - ), - ), - ), - // Slider - Semantics( - label: t.videoControls.timelineSlider, - slider: true, - child: Slider( - value: duration.inMilliseconds > 0 - ? position.inMilliseconds.toDouble() - : 0.0, - min: 0.0, - max: duration.inMilliseconds.toDouble(), - onChanged: (value) { - onSeek(Duration(milliseconds: value.toInt())); - }, - onChangeEnd: (value) { - onSeekEnd(Duration(milliseconds: value.toInt())); - }, - activeColor: Colors.white, - inactiveColor: Colors.white.withValues(alpha: 0.3), - ), - ), - // Chapter marker indicators - if (chaptersLoaded && - chapters.isNotEmpty && - duration.inMilliseconds > 0) - Positioned.fill( - child: IgnorePointer( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: CustomPaint( - painter: ChapterMarkerPainter( - chapters: chapters, - duration: duration, - ), - ), - ), - ), - ), - ], - ); - } - /// Conditionally wraps child with SafeArea only in portrait mode Widget _conditionalSafeArea({ required BuildContext context, @@ -372,49 +294,4 @@ class MobileVideoControls extends StatelessWidget { // In landscape, return child without SafeArea return child; } - - void _seekWithClamping(Duration offset) { - final currentPosition = player.state.position; - final duration = player.state.duration; - final newPosition = currentPosition + offset; - - // Clamp between 0 and video duration - final clampedPosition = newPosition.isNegative - ? Duration.zero - : (newPosition > duration ? duration : newPosition); - - player.seek(clampedPosition); - } - - /// Get the replay icon based on the duration - /// Returns numbered icons (replay_5, replay_10, replay_30) when available, - /// otherwise returns generic replay icon - IconData _getReplayIcon(int seconds) { - switch (seconds) { - case 5: - return Icons.replay_5; - case 10: - return Icons.replay_10; - case 30: - return Icons.replay_30; - default: - return Icons.replay; // Generic icon for custom durations - } - } - - /// Get the forward icon based on the duration - /// Returns numbered icons (forward_5, forward_10, forward_30) when available, - /// otherwise returns generic forward icon - IconData _getForwardIcon(int seconds) { - switch (seconds) { - case 5: - return Icons.forward_5; - case 10: - return Icons.forward_10; - case 30: - return Icons.forward_30; - default: - return Icons.forward; // Generic icon for custom durations - } - } } diff --git a/lib/widgets/video_controls/video_controls.dart b/lib/widgets/video_controls/video_controls.dart index 813e1701..71ea012f 100644 --- a/lib/widgets/video_controls/video_controls.dart +++ b/lib/widgets/video_controls/video_controls.dart @@ -15,7 +15,9 @@ import '../../screens/video_player_screen.dart'; import '../../services/keyboard_shortcuts_service.dart'; import '../../services/settings_service.dart'; import '../../utils/platform_detector.dart'; +import '../../utils/player_utils.dart'; import '../../utils/provider_extensions.dart'; +import '../../utils/video_control_icons.dart'; import '../../i18n/strings.g.dart'; import 'widgets/volume_control.dart'; import 'widgets/track_chapter_controls.dart'; @@ -399,7 +401,7 @@ class _PlexVideoControlsState extends State void _seekToPreviousChapter() { if (_chapters.isEmpty) { // No chapters - seek backward by configured amount - _seekWithClamping(Duration(seconds: -_seekTimeSmall)); + seekWithClamping(widget.player, Duration(seconds: -_seekTimeSmall)); return; } @@ -422,7 +424,7 @@ class _PlexVideoControlsState extends State void _seekToNextChapter() { if (_chapters.isEmpty) { // No chapters - seek forward by configured amount - _seekWithClamping(Duration(seconds: _seekTimeSmall)); + seekWithClamping(widget.player, Duration(seconds: _seekTimeSmall)); return; } @@ -438,21 +440,6 @@ class _PlexVideoControlsState extends State } } - /// Seeks by the given offset (can be positive or negative) while clamping - /// the result between 0 and the video duration - void _seekWithClamping(Duration offset) { - final currentPosition = widget.player.state.position; - final duration = widget.player.state.duration; - final newPosition = currentPosition + offset; - - // Clamp between 0 and video duration - final clampedPosition = newPosition.isNegative - ? Duration.zero - : (newPosition > duration ? duration : newPosition); - - widget.player.seek(clampedPosition); - } - /// Throttled seek for timeline slider - only sends seek events at most every 100ms void _throttledSeek(Duration position) { // Store the pending position @@ -487,42 +474,11 @@ class _PlexVideoControlsState extends State _pendingSeekPosition = null; } - /// Get the replay icon based on the duration - /// Returns numbered icons (replay_5, replay_10, replay_30) when available, - /// otherwise returns generic replay icon - IconData _getReplayIcon(int seconds) { - switch (seconds) { - case 5: - return Icons.replay_5; - case 10: - return Icons.replay_10; - case 30: - return Icons.replay_30; - default: - return Icons.replay; // Generic icon for custom durations - } - } - - /// Get the forward icon based on the duration - /// Returns numbered icons (forward_5, forward_10, forward_30) when available, - /// otherwise returns generic forward icon - IconData _getForwardIcon(int seconds) { - switch (seconds) { - case 5: - return Icons.forward_5; - case 10: - return Icons.forward_10; - case 30: - return Icons.forward_30; - default: - return Icons.forward; // Generic icon for custom durations - } - } - /// Handle double-tap skip forward or backward void _handleDoubleTapSkip({required bool isForward}) { // Perform the seek - _seekWithClamping( + seekWithClamping( + widget.player, Duration(seconds: isForward ? _seekTimeSmall : -_seekTimeSmall), ); @@ -573,8 +529,8 @@ class _PlexVideoControlsState extends State ), child: Icon( _lastDoubleTapWasForward - ? _getForwardIcon(_seekTimeSmall) - : _getReplayIcon(_seekTimeSmall), + ? getForwardIcon(_seekTimeSmall) + : getReplayIcon(_seekTimeSmall), color: Colors.white, size: 48, ), @@ -713,8 +669,8 @@ class _PlexVideoControlsState extends State onSeekToNextChapter: _seekToNextChapter, onSeek: _throttledSeek, onSeekEnd: _finalizeSeek, - getReplayIcon: _getReplayIcon, - getForwardIcon: _getForwardIcon, + getReplayIcon: getReplayIcon, + getForwardIcon: getForwardIcon, ), ), ),