diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 17ff8187..eff3097d 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -47,7 +47,6 @@ class VideoPlayerScreen extends StatefulWidget { final PlexMetadata metadata; final AudioTrack? preferredAudioTrack; final SubtitleTrack? preferredSubtitleTrack; - final double? preferredPlaybackRate; final int selectedMediaIndex; final bool isOffline; @@ -56,7 +55,6 @@ class VideoPlayerScreen extends StatefulWidget { required this.metadata, this.preferredAudioTrack, this.preferredSubtitleTrack, - this.preferredPlaybackRate, this.selectedMediaIndex = 0, this.isOffline = false, }); @@ -1216,6 +1214,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin if (!mounted || player == null) return; final profileSettings = context.read().profileSettings; + final settingsService = await SettingsService.getInstance(); final trackService = TrackSelectionService( player: player!, profileSettings: profileSettings, @@ -1226,7 +1225,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin await trackService.selectAndApplyTracks( preferredAudioTrack: widget.preferredAudioTrack, preferredSubtitleTrack: widget.preferredSubtitleTrack, - preferredPlaybackRate: widget.preferredPlaybackRate, + defaultPlaybackSpeed: settingsService.getDefaultPlaybackSpeed(), onAudioTrackChanged: _onAudioTrackChanged, onSubtitleTrackChanged: _onSubtitleTrackChanged, ); @@ -1462,7 +1461,6 @@ class VideoPlayerScreenState extends State with WidgetsBindin final currentAudioTrack = currentPlayer.state.track.audio; final currentSubtitleTrack = currentPlayer.state.track.subtitle; - final currentRate = currentPlayer.state.rate; // Pause and stop current playback currentPlayer.pause(); @@ -1479,7 +1477,6 @@ class VideoPlayerScreenState extends State with WidgetsBindin metadata: episodeMetadata, preferredAudioTrack: currentAudioTrack, preferredSubtitleTrack: currentSubtitleTrack, - preferredPlaybackRate: currentRate, usePushReplacement: true, ); } diff --git a/lib/services/episode_navigation_service.dart b/lib/services/episode_navigation_service.dart index 53e53d29..1dc56d7e 100644 --- a/lib/services/episode_navigation_service.dart +++ b/lib/services/episode_navigation_service.dart @@ -97,7 +97,6 @@ class EpisodeNavigationService { metadata: episode, preferredAudioTrack: currentAudioTrack, preferredSubtitleTrack: currentSubtitleTrack, - preferredPlaybackRate: currentPlaybackRate, usePushReplacement: usePushReplacement, ); } diff --git a/lib/services/keyboard_shortcuts_service.dart b/lib/services/keyboard_shortcuts_service.dart index 3102865f..8c4ca154 100644 --- a/lib/services/keyboard_shortcuts_service.dart +++ b/lib/services/keyboard_shortcuts_service.dart @@ -305,15 +305,18 @@ class KeyboardShortcutsService { onPreviousChapter?.call(); break; case 'speed_increase': - final newRate = (player.state.rate + 0.1).clamp(0.1, 3.0); - player.setRate(newRate); + final newRateUp = (player.state.rate + 0.1).clamp(0.1, 3.0); + player.setRate(newRateUp); + _settingsService.setDefaultPlaybackSpeed(newRateUp); break; case 'speed_decrease': - final newRate = (player.state.rate - 0.1).clamp(0.1, 3.0); - player.setRate(newRate); + final newRateDown = (player.state.rate - 0.1).clamp(0.1, 3.0); + player.setRate(newRateDown); + _settingsService.setDefaultPlaybackSpeed(newRateDown); break; case 'speed_reset': player.setRate(1.0); + _settingsService.setDefaultPlaybackSpeed(1.0); break; } } diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 373197d7..64b624c0 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -61,6 +61,7 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyMaxVolume = 'max_volume'; static const String _keyEnableDiscordRPC = 'enable_discord_rpc'; static const String _keyMatchContentFrameRate = 'match_content_frame_rate'; + static const String _keyDefaultPlaybackSpeed = 'default_playback_speed'; SettingsService._(); @@ -947,6 +948,15 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getBool(_keyMatchContentFrameRate) ?? false; // Default disabled } + // Default Playback Speed (0.5 to 3.0) + Future setDefaultPlaybackSpeed(double speed) async { + await prefs.setDouble(_keyDefaultPlaybackSpeed, speed.clamp(0.5, 3.0)); + } + + double getDefaultPlaybackSpeed() { + return prefs.getDouble(_keyDefaultPlaybackSpeed) ?? 1.0; // Default: normal speed + } + // Reset all settings to defaults Future resetAllSettings() async { await Future.wait([ @@ -989,6 +999,7 @@ class SettingsService extends BaseSharedPreferencesService { prefs.remove(_keyMpvConfigPresets), prefs.remove(_keyEnableDiscordRPC), prefs.remove(_keyMatchContentFrameRate), + prefs.remove(_keyDefaultPlaybackSpeed), ]); } diff --git a/lib/services/track_selection_service.dart b/lib/services/track_selection_service.dart index 1746d153..3c1a8103 100644 --- a/lib/services/track_selection_service.dart +++ b/lib/services/track_selection_service.dart @@ -446,7 +446,7 @@ class TrackSelectionService { Future selectAndApplyTracks({ AudioTrack? preferredAudioTrack, SubtitleTrack? preferredSubtitleTrack, - double? preferredPlaybackRate, + double? defaultPlaybackSpeed, Function(AudioTrack)? onAudioTrackChanged, Function(SubtitleTrack)? onSubtitleTrackChanged, }) async { @@ -491,9 +491,9 @@ class TrackSelectionService { onSubtitleTrackChanged(selectedSubtitleTrack); } - // Set playback rate if preferred rate was provided - if (preferredPlaybackRate != null) { - player.setRate(preferredPlaybackRate); + // Apply default playback speed from settings + if (defaultPlaybackSpeed != null && defaultPlaybackSpeed != 1.0) { + player.setRate(defaultPlaybackSpeed); } } } diff --git a/lib/utils/video_player_navigation.dart b/lib/utils/video_player_navigation.dart index 082fadd4..fa30aad3 100644 --- a/lib/utils/video_player_navigation.dart +++ b/lib/utils/video_player_navigation.dart @@ -19,7 +19,6 @@ const String kVideoPlayerRouteName = '/video_player'; /// - [metadata]: The Plex metadata for the content to play /// - [preferredAudioTrack]: Optional audio track to select on playback start /// - [preferredSubtitleTrack]: Optional subtitle track to select on playback start -/// - [preferredPlaybackRate]: Optional playback speed to set on playback start /// - [selectedMediaIndex]: Optional media version index to use; if not provided, /// loads the saved preference for the series/movie. Defaults to 0 if no preference exists. /// - [usePushReplacement]: If true, replaces current route instead of pushing; @@ -33,7 +32,6 @@ Future navigateToVideoPlayer( required PlexMetadata metadata, AudioTrack? preferredAudioTrack, SubtitleTrack? preferredSubtitleTrack, - double? preferredPlaybackRate, int? selectedMediaIndex, bool usePushReplacement = false, bool isOffline = false, @@ -72,7 +70,6 @@ Future navigateToVideoPlayer( metadata: metadata, preferredAudioTrack: preferredAudioTrack, preferredSubtitleTrack: preferredSubtitleTrack, - preferredPlaybackRate: preferredPlaybackRate, selectedMediaIndex: mediaIndex, isOffline: isOffline, ), @@ -108,7 +105,6 @@ Future navigateToVideoPlayerWithRefresh( VoidCallback? onRefresh, AudioTrack? preferredAudioTrack, SubtitleTrack? preferredSubtitleTrack, - double? preferredPlaybackRate, int? selectedMediaIndex, bool usePushReplacement = false, }) async { @@ -118,7 +114,6 @@ Future navigateToVideoPlayerWithRefresh( isOffline: isOffline, preferredAudioTrack: preferredAudioTrack, preferredSubtitleTrack: preferredSubtitleTrack, - preferredPlaybackRate: preferredPlaybackRate, selectedMediaIndex: selectedMediaIndex, usePushReplacement: usePushReplacement, ); diff --git a/lib/widgets/video_controls/sheets/video_settings_sheet.dart b/lib/widgets/video_controls/sheets/video_settings_sheet.dart index 4855db8a..4893f0ac 100644 --- a/lib/widgets/video_controls/sheets/video_settings_sheet.dart +++ b/lib/widgets/video_controls/sheets/video_settings_sheet.dart @@ -338,9 +338,14 @@ class _VideoSettingsSheetState extends State { return ListTile( title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)), trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null, - onTap: () { + onTap: () async { widget.player.setRate(speed); - Navigator.pop(context); // Close sheet after selection + // Save as default playback speed + final settings = await SettingsService.getInstance(); + await settings.setDefaultPlaybackSpeed(speed); + if (context.mounted) { + Navigator.pop(context); // Close sheet after selection + } }, ); },