@@ -47,7 +47,6 @@ class VideoPlayerScreen extends StatefulWidget {
|
|||||||
final PlexMetadata metadata;
|
final PlexMetadata metadata;
|
||||||
final AudioTrack? preferredAudioTrack;
|
final AudioTrack? preferredAudioTrack;
|
||||||
final SubtitleTrack? preferredSubtitleTrack;
|
final SubtitleTrack? preferredSubtitleTrack;
|
||||||
final double? preferredPlaybackRate;
|
|
||||||
final int selectedMediaIndex;
|
final int selectedMediaIndex;
|
||||||
final bool isOffline;
|
final bool isOffline;
|
||||||
|
|
||||||
@@ -56,7 +55,6 @@ class VideoPlayerScreen extends StatefulWidget {
|
|||||||
required this.metadata,
|
required this.metadata,
|
||||||
this.preferredAudioTrack,
|
this.preferredAudioTrack,
|
||||||
this.preferredSubtitleTrack,
|
this.preferredSubtitleTrack,
|
||||||
this.preferredPlaybackRate,
|
|
||||||
this.selectedMediaIndex = 0,
|
this.selectedMediaIndex = 0,
|
||||||
this.isOffline = false,
|
this.isOffline = false,
|
||||||
});
|
});
|
||||||
@@ -1216,6 +1214,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
if (!mounted || player == null) return;
|
if (!mounted || player == null) return;
|
||||||
|
|
||||||
final profileSettings = context.read<UserProfileProvider>().profileSettings;
|
final profileSettings = context.read<UserProfileProvider>().profileSettings;
|
||||||
|
final settingsService = await SettingsService.getInstance();
|
||||||
final trackService = TrackSelectionService(
|
final trackService = TrackSelectionService(
|
||||||
player: player!,
|
player: player!,
|
||||||
profileSettings: profileSettings,
|
profileSettings: profileSettings,
|
||||||
@@ -1226,7 +1225,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
await trackService.selectAndApplyTracks(
|
await trackService.selectAndApplyTracks(
|
||||||
preferredAudioTrack: widget.preferredAudioTrack,
|
preferredAudioTrack: widget.preferredAudioTrack,
|
||||||
preferredSubtitleTrack: widget.preferredSubtitleTrack,
|
preferredSubtitleTrack: widget.preferredSubtitleTrack,
|
||||||
preferredPlaybackRate: widget.preferredPlaybackRate,
|
defaultPlaybackSpeed: settingsService.getDefaultPlaybackSpeed(),
|
||||||
onAudioTrackChanged: _onAudioTrackChanged,
|
onAudioTrackChanged: _onAudioTrackChanged,
|
||||||
onSubtitleTrackChanged: _onSubtitleTrackChanged,
|
onSubtitleTrackChanged: _onSubtitleTrackChanged,
|
||||||
);
|
);
|
||||||
@@ -1462,7 +1461,6 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
|
|
||||||
final currentAudioTrack = currentPlayer.state.track.audio;
|
final currentAudioTrack = currentPlayer.state.track.audio;
|
||||||
final currentSubtitleTrack = currentPlayer.state.track.subtitle;
|
final currentSubtitleTrack = currentPlayer.state.track.subtitle;
|
||||||
final currentRate = currentPlayer.state.rate;
|
|
||||||
|
|
||||||
// Pause and stop current playback
|
// Pause and stop current playback
|
||||||
currentPlayer.pause();
|
currentPlayer.pause();
|
||||||
@@ -1479,7 +1477,6 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
metadata: episodeMetadata,
|
metadata: episodeMetadata,
|
||||||
preferredAudioTrack: currentAudioTrack,
|
preferredAudioTrack: currentAudioTrack,
|
||||||
preferredSubtitleTrack: currentSubtitleTrack,
|
preferredSubtitleTrack: currentSubtitleTrack,
|
||||||
preferredPlaybackRate: currentRate,
|
|
||||||
usePushReplacement: true,
|
usePushReplacement: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ class EpisodeNavigationService {
|
|||||||
metadata: episode,
|
metadata: episode,
|
||||||
preferredAudioTrack: currentAudioTrack,
|
preferredAudioTrack: currentAudioTrack,
|
||||||
preferredSubtitleTrack: currentSubtitleTrack,
|
preferredSubtitleTrack: currentSubtitleTrack,
|
||||||
preferredPlaybackRate: currentPlaybackRate,
|
|
||||||
usePushReplacement: usePushReplacement,
|
usePushReplacement: usePushReplacement,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,15 +305,18 @@ class KeyboardShortcutsService {
|
|||||||
onPreviousChapter?.call();
|
onPreviousChapter?.call();
|
||||||
break;
|
break;
|
||||||
case 'speed_increase':
|
case 'speed_increase':
|
||||||
final newRate = (player.state.rate + 0.1).clamp(0.1, 3.0);
|
final newRateUp = (player.state.rate + 0.1).clamp(0.1, 3.0);
|
||||||
player.setRate(newRate);
|
player.setRate(newRateUp);
|
||||||
|
_settingsService.setDefaultPlaybackSpeed(newRateUp);
|
||||||
break;
|
break;
|
||||||
case 'speed_decrease':
|
case 'speed_decrease':
|
||||||
final newRate = (player.state.rate - 0.1).clamp(0.1, 3.0);
|
final newRateDown = (player.state.rate - 0.1).clamp(0.1, 3.0);
|
||||||
player.setRate(newRate);
|
player.setRate(newRateDown);
|
||||||
|
_settingsService.setDefaultPlaybackSpeed(newRateDown);
|
||||||
break;
|
break;
|
||||||
case 'speed_reset':
|
case 'speed_reset':
|
||||||
player.setRate(1.0);
|
player.setRate(1.0);
|
||||||
|
_settingsService.setDefaultPlaybackSpeed(1.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
|||||||
static const String _keyMaxVolume = 'max_volume';
|
static const String _keyMaxVolume = 'max_volume';
|
||||||
static const String _keyEnableDiscordRPC = 'enable_discord_rpc';
|
static const String _keyEnableDiscordRPC = 'enable_discord_rpc';
|
||||||
static const String _keyMatchContentFrameRate = 'match_content_frame_rate';
|
static const String _keyMatchContentFrameRate = 'match_content_frame_rate';
|
||||||
|
static const String _keyDefaultPlaybackSpeed = 'default_playback_speed';
|
||||||
|
|
||||||
SettingsService._();
|
SettingsService._();
|
||||||
|
|
||||||
@@ -947,6 +948,15 @@ class SettingsService extends BaseSharedPreferencesService {
|
|||||||
return prefs.getBool(_keyMatchContentFrameRate) ?? false; // Default disabled
|
return prefs.getBool(_keyMatchContentFrameRate) ?? false; // Default disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default Playback Speed (0.5 to 3.0)
|
||||||
|
Future<void> 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
|
// Reset all settings to defaults
|
||||||
Future<void> resetAllSettings() async {
|
Future<void> resetAllSettings() async {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
@@ -989,6 +999,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
|||||||
prefs.remove(_keyMpvConfigPresets),
|
prefs.remove(_keyMpvConfigPresets),
|
||||||
prefs.remove(_keyEnableDiscordRPC),
|
prefs.remove(_keyEnableDiscordRPC),
|
||||||
prefs.remove(_keyMatchContentFrameRate),
|
prefs.remove(_keyMatchContentFrameRate),
|
||||||
|
prefs.remove(_keyDefaultPlaybackSpeed),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ class TrackSelectionService {
|
|||||||
Future<void> selectAndApplyTracks({
|
Future<void> selectAndApplyTracks({
|
||||||
AudioTrack? preferredAudioTrack,
|
AudioTrack? preferredAudioTrack,
|
||||||
SubtitleTrack? preferredSubtitleTrack,
|
SubtitleTrack? preferredSubtitleTrack,
|
||||||
double? preferredPlaybackRate,
|
double? defaultPlaybackSpeed,
|
||||||
Function(AudioTrack)? onAudioTrackChanged,
|
Function(AudioTrack)? onAudioTrackChanged,
|
||||||
Function(SubtitleTrack)? onSubtitleTrackChanged,
|
Function(SubtitleTrack)? onSubtitleTrackChanged,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -491,9 +491,9 @@ class TrackSelectionService {
|
|||||||
onSubtitleTrackChanged(selectedSubtitleTrack);
|
onSubtitleTrackChanged(selectedSubtitleTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set playback rate if preferred rate was provided
|
// Apply default playback speed from settings
|
||||||
if (preferredPlaybackRate != null) {
|
if (defaultPlaybackSpeed != null && defaultPlaybackSpeed != 1.0) {
|
||||||
player.setRate(preferredPlaybackRate);
|
player.setRate(defaultPlaybackSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ const String kVideoPlayerRouteName = '/video_player';
|
|||||||
/// - [metadata]: The Plex metadata for the content to play
|
/// - [metadata]: The Plex metadata for the content to play
|
||||||
/// - [preferredAudioTrack]: Optional audio track to select on playback start
|
/// - [preferredAudioTrack]: Optional audio track to select on playback start
|
||||||
/// - [preferredSubtitleTrack]: Optional subtitle 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,
|
/// - [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.
|
/// loads the saved preference for the series/movie. Defaults to 0 if no preference exists.
|
||||||
/// - [usePushReplacement]: If true, replaces current route instead of pushing;
|
/// - [usePushReplacement]: If true, replaces current route instead of pushing;
|
||||||
@@ -33,7 +32,6 @@ Future<bool?> navigateToVideoPlayer(
|
|||||||
required PlexMetadata metadata,
|
required PlexMetadata metadata,
|
||||||
AudioTrack? preferredAudioTrack,
|
AudioTrack? preferredAudioTrack,
|
||||||
SubtitleTrack? preferredSubtitleTrack,
|
SubtitleTrack? preferredSubtitleTrack,
|
||||||
double? preferredPlaybackRate,
|
|
||||||
int? selectedMediaIndex,
|
int? selectedMediaIndex,
|
||||||
bool usePushReplacement = false,
|
bool usePushReplacement = false,
|
||||||
bool isOffline = false,
|
bool isOffline = false,
|
||||||
@@ -72,7 +70,6 @@ Future<bool?> navigateToVideoPlayer(
|
|||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
preferredAudioTrack: preferredAudioTrack,
|
preferredAudioTrack: preferredAudioTrack,
|
||||||
preferredSubtitleTrack: preferredSubtitleTrack,
|
preferredSubtitleTrack: preferredSubtitleTrack,
|
||||||
preferredPlaybackRate: preferredPlaybackRate,
|
|
||||||
selectedMediaIndex: mediaIndex,
|
selectedMediaIndex: mediaIndex,
|
||||||
isOffline: isOffline,
|
isOffline: isOffline,
|
||||||
),
|
),
|
||||||
@@ -108,7 +105,6 @@ Future<bool?> navigateToVideoPlayerWithRefresh(
|
|||||||
VoidCallback? onRefresh,
|
VoidCallback? onRefresh,
|
||||||
AudioTrack? preferredAudioTrack,
|
AudioTrack? preferredAudioTrack,
|
||||||
SubtitleTrack? preferredSubtitleTrack,
|
SubtitleTrack? preferredSubtitleTrack,
|
||||||
double? preferredPlaybackRate,
|
|
||||||
int? selectedMediaIndex,
|
int? selectedMediaIndex,
|
||||||
bool usePushReplacement = false,
|
bool usePushReplacement = false,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -118,7 +114,6 @@ Future<bool?> navigateToVideoPlayerWithRefresh(
|
|||||||
isOffline: isOffline,
|
isOffline: isOffline,
|
||||||
preferredAudioTrack: preferredAudioTrack,
|
preferredAudioTrack: preferredAudioTrack,
|
||||||
preferredSubtitleTrack: preferredSubtitleTrack,
|
preferredSubtitleTrack: preferredSubtitleTrack,
|
||||||
preferredPlaybackRate: preferredPlaybackRate,
|
|
||||||
selectedMediaIndex: selectedMediaIndex,
|
selectedMediaIndex: selectedMediaIndex,
|
||||||
usePushReplacement: usePushReplacement,
|
usePushReplacement: usePushReplacement,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -338,9 +338,14 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)),
|
title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)),
|
||||||
trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null,
|
trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
widget.player.setRate(speed);
|
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
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user