From 34252b9ba7e06428eacccf2b74e91cd0ea6a7ce6 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:31:17 +0100 Subject: [PATCH] fix: persist aspect ratio and shader settings Closes #602, closes #603 --- lib/screens/video_player_screen.dart | 13 +++++++++---- lib/services/settings_service.dart | 11 +++++++++++ lib/services/video_filter_manager.dart | 9 +++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 32758941..c1784657 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -1106,6 +1106,8 @@ class VideoPlayerScreenState extends State with WidgetsBindin player: player!, availableVersions: _availableVersions, selectedMediaIndex: widget.selectedMediaIndex, + initialBoxFitMode: settingsService.getDefaultBoxFitMode(), + onBoxFitModeChanged: (mode) => settingsService.setDefaultBoxFitMode(mode), ); // Update video filter once dimensions are available _videoFilterManager!.updateVideoFilter(); @@ -1265,15 +1267,18 @@ class VideoPlayerScreenState extends State with WidgetsBindin } } - /// Apply the saved shader preset on playback start + /// Apply the saved shader preset on playback start. + /// Reads directly from SettingsService (synchronous SharedPreferences) to + /// avoid a race with ShaderProvider's async initialization. Future _applySavedShaderPreset() async { if (_shaderService == null || !_shaderService!.isSupported) return; try { - final shaderProvider = context.read(); - final preset = shaderProvider.savedPreset; + final settings = await SettingsService.getInstance(); + final presetId = settings.getGlobalShaderPreset(); + final preset = ShaderPreset.fromId(presetId) ?? ShaderPreset.none; await _shaderService!.applyPreset(preset); - shaderProvider.setCurrentPreset(preset); + context.read().setCurrentPreset(preset); } catch (e) { appLogger.d('Could not apply shader preset', error: e); } diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 7231583c..90981872 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -70,6 +70,7 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyMatchContentFrameRate = 'match_content_frame_rate'; static const String _keyTunneledPlayback = 'tunneled_playback'; static const String _keyDefaultPlaybackSpeed = 'default_playback_speed'; + static const String _keyDefaultBoxFitMode = 'default_box_fit_mode'; static const String _keyAutoPlayNextEpisode = 'auto_play_next_episode'; static const String _keyUseExoPlayer = 'use_exoplayer'; static const String _keyAlwaysKeepSidebarOpen = 'always_keep_sidebar_open'; @@ -1041,6 +1042,15 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getDouble(_keyDefaultPlaybackSpeed) ?? 1.0; // Default: normal speed } + // Default BoxFit Mode (0=contain, 1=cover, 2=fill) + Future setDefaultBoxFitMode(int mode) async { + await prefs.setInt(_keyDefaultBoxFitMode, mode.clamp(0, 2)); + } + + int getDefaultBoxFitMode() { + return prefs.getInt(_keyDefaultBoxFitMode) ?? 0; // Default: contain + } + // Auto-Play Next Episode Future setAutoPlayNextEpisode(bool enabled) async { await prefs.setBool(_keyAutoPlayNextEpisode, enabled); @@ -1244,6 +1254,7 @@ class SettingsService extends BaseSharedPreferencesService { prefs.remove(_keyMatchContentFrameRate), prefs.remove(_keyTunneledPlayback), prefs.remove(_keyDefaultPlaybackSpeed), + prefs.remove(_keyDefaultBoxFitMode), prefs.remove(_keyAutoPlayNextEpisode), prefs.remove(_keyUseExoPlayer), prefs.remove(_keyAlwaysKeepSidebarOpen), diff --git a/lib/services/video_filter_manager.dart b/lib/services/video_filter_manager.dart index 9ad1c732..f0d3b54c 100644 --- a/lib/services/video_filter_manager.dart +++ b/lib/services/video_filter_manager.dart @@ -21,7 +21,7 @@ class VideoFilterManager { final int selectedMediaIndex; /// BoxFit mode state: 0=contain (letterbox), 1=cover (fill screen), 2=fill (stretch) - int _boxFitMode = 0; + int _boxFitMode; /// Store the boxFitMode before entering PiP so it can be restored int? _prePipBoxFitMode; @@ -41,7 +41,10 @@ class VideoFilterManager { /// Debounced video filter update with leading edge execution late final Debounce _debouncedUpdateVideoFilter; - VideoFilterManager({required this.player, required this.availableVersions, required this.selectedMediaIndex}) { + /// Callback invoked when boxFitMode changes, for external persistence + final void Function(int mode)? onBoxFitModeChanged; + + VideoFilterManager({required this.player, required this.availableVersions, required this.selectedMediaIndex, int initialBoxFitMode = 0, this.onBoxFitModeChanged}) : _boxFitMode = initialBoxFitMode { _debouncedUpdateVideoFilter = debounce( updateVideoFilter, const Duration(milliseconds: 50), @@ -59,6 +62,7 @@ class VideoFilterManager { /// Cycle through BoxFit modes: contain → cover → fill → contain (for button) void cycleBoxFitMode() { _boxFitMode = (_boxFitMode + 1) % 3; + onBoxFitModeChanged?.call(_boxFitMode); updateVideoFilter(); } @@ -73,6 +77,7 @@ class VideoFilterManager { /// Toggle between contain and cover modes only (for pinch gesture) void toggleContainCover() { _boxFitMode = _boxFitMode == 0 ? 1 : 0; + onBoxFitModeChanged?.call(_boxFitMode); updateVideoFilter(); }