Files
sm-emby-share/lib/providers/player_settings_provider.dart
T
2026-07-14 11:11:36 +08:00

242 lines
8.8 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/contracts/player_gestures.dart';
import 'shared/settings_persistence.dart';
class PlayerSettingsState {
final String hwdecMode;
final double volume;
final int seekForwardSeconds;
final int seekBackwardSeconds;
final double keyboardHoldLeftSpeed;
final double keyboardHoldRightSpeed;
final GestureAction mouseLeftClickAction;
final GestureAction mouseLeftDoubleClickAction;
final GestureAction mouseRightClickAction;
final GestureAction mouseRightDoubleClickAction;
final bool autoPip;
final bool pipShowEpisodeActions;
final double? androidBrightness;
const PlayerSettingsState({
this.hwdecMode = 'auto-copy',
this.volume = 100.0,
this.seekForwardSeconds = 15,
this.seekBackwardSeconds = 15,
this.keyboardHoldLeftSpeed = 0.5,
this.keyboardHoldRightSpeed = 3.0,
this.mouseLeftClickAction = GestureAction.playPause,
this.mouseLeftDoubleClickAction = GestureAction.fullscreen,
this.mouseRightClickAction = GestureAction.toggleControls,
this.mouseRightDoubleClickAction = GestureAction.none,
this.autoPip = true,
this.pipShowEpisodeActions = true,
this.androidBrightness,
});
PlayerSettingsState copyWith({
String? hwdecMode,
double? volume,
int? seekForwardSeconds,
int? seekBackwardSeconds,
double? keyboardHoldLeftSpeed,
double? keyboardHoldRightSpeed,
GestureAction? mouseLeftClickAction,
GestureAction? mouseLeftDoubleClickAction,
GestureAction? mouseRightClickAction,
GestureAction? mouseRightDoubleClickAction,
bool? autoPip,
bool? pipShowEpisodeActions,
double? androidBrightness,
}) => PlayerSettingsState(
hwdecMode: hwdecMode ?? this.hwdecMode,
volume: volume ?? this.volume,
seekForwardSeconds: seekForwardSeconds ?? this.seekForwardSeconds,
seekBackwardSeconds: seekBackwardSeconds ?? this.seekBackwardSeconds,
keyboardHoldLeftSpeed: keyboardHoldLeftSpeed ?? this.keyboardHoldLeftSpeed,
keyboardHoldRightSpeed:
keyboardHoldRightSpeed ?? this.keyboardHoldRightSpeed,
mouseLeftClickAction: mouseLeftClickAction ?? this.mouseLeftClickAction,
mouseLeftDoubleClickAction:
mouseLeftDoubleClickAction ?? this.mouseLeftDoubleClickAction,
mouseRightClickAction: mouseRightClickAction ?? this.mouseRightClickAction,
mouseRightDoubleClickAction:
mouseRightDoubleClickAction ?? this.mouseRightDoubleClickAction,
autoPip: autoPip ?? this.autoPip,
pipShowEpisodeActions: pipShowEpisodeActions ?? this.pipShowEpisodeActions,
androidBrightness: androidBrightness ?? this.androidBrightness,
);
}
class PlayerSettingsNotifier extends AsyncNotifier<PlayerSettingsState>
with SettingsPersistence {
static const _keyHwdec = 'smplayer.player_hwdec';
static const _legacyKeyEnginePreference = 'smplayer.player_engine_preference';
static const _keyVolume = 'smplayer.player_volume';
static const _keySeekForward = 'smplayer.player_seek_forward';
static const _keySeekBackward = 'smplayer.player_seek_backward';
static const _keyKbdHoldLeft = 'smplayer.gesture_kbd_hold_left_speed';
static const _keyKbdHoldRight = 'smplayer.gesture_kbd_hold_right_speed';
static const _keyMouseLeftClick = 'smplayer.gesture_mouse_left_click';
static const _keyMouseLeftDblClick = 'smplayer.gesture_mouse_left_dbl_click';
static const _keyMouseRightClick = 'smplayer.gesture_mouse_right_click';
static const _keyMouseRightDblClick =
'smplayer.gesture_mouse_right_dbl_click';
static const _keyAutoPip = 'smplayer.player_auto_pip';
static const _keyPipEpisodeActions = 'smplayer.player_pip_episode_actions';
static const _keyAndroidBrightness = 'smplayer.player_android_brightness';
static const _speedAllowed = <double>[
0.5,
0.75,
1.0,
1.25,
1.5,
1.75,
2.0,
2.5,
3.0,
4.0,
8.0,
];
static double _normalizeSpeed(double v, double fallback) {
for (final s in _speedAllowed) {
if ((s - v).abs() < 0.001) return s;
}
return fallback;
}
@override
Future<PlayerSettingsState> build() async {
final prefs = await getPrefs();
if (prefs.containsKey(_keyHwdec)) {
await prefs.remove(_keyHwdec);
}
const hwdec = 'auto-copy';
final volume = prefs.getDouble(_keyVolume) ?? 100.0;
final seekFwd = prefs.getInt(_keySeekForward) ?? 15;
final seekBwd = prefs.getInt(_keySeekBackward) ?? 15;
if (prefs.containsKey(_legacyKeyEnginePreference)) {
await prefs.remove(_legacyKeyEnginePreference);
}
final holdLeft = _normalizeSpeed(
prefs.getDouble(_keyKbdHoldLeft) ?? 0.5,
0.5,
);
final holdRight = _normalizeSpeed(
prefs.getDouble(_keyKbdHoldRight) ?? 3.0,
3.0,
);
final autoPip = prefs.getBool(_keyAutoPip) ?? true;
final pipEpisodeActions = prefs.getBool(_keyPipEpisodeActions) ?? true;
final androidBrightness = prefs
.getDouble(_keyAndroidBrightness)
?.clamp(0.0, 1.0);
return PlayerSettingsState(
hwdecMode: hwdec,
volume: volume.clamp(0.0, 200.0),
seekForwardSeconds: seekFwd.clamp(5, 120),
seekBackwardSeconds: seekBwd.clamp(5, 120),
keyboardHoldLeftSpeed: holdLeft,
keyboardHoldRightSpeed: holdRight,
mouseLeftClickAction: gestureActionFromName(
prefs.getString(_keyMouseLeftClick),
GestureAction.playPause,
),
mouseLeftDoubleClickAction: gestureActionFromName(
prefs.getString(_keyMouseLeftDblClick),
GestureAction.fullscreen,
),
mouseRightClickAction: gestureActionFromName(
prefs.getString(_keyMouseRightClick),
GestureAction.toggleControls,
),
mouseRightDoubleClickAction: gestureActionFromName(
prefs.getString(_keyMouseRightDblClick),
GestureAction.none,
),
autoPip: autoPip,
pipShowEpisodeActions: pipEpisodeActions,
androidBrightness: androidBrightness,
);
}
Future<void> setVolume(double volume) async {
final clamped = volume.clamp(0.0, 200.0);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(volume: clamped));
await persistField(_keyVolume, clamped);
}
Future<void> setSeekForwardSeconds(int seconds) async {
final clamped = seconds.clamp(5, 120);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(seekForwardSeconds: clamped));
await persistField(_keySeekForward, clamped);
}
Future<void> setSeekBackwardSeconds(int seconds) async {
final clamped = seconds.clamp(5, 120);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(seekBackwardSeconds: clamped));
await persistField(_keySeekBackward, clamped);
}
Future<void> setKeyboardHoldLeftSpeed(double v) async {
final normalized = _normalizeSpeed(v, 0.5);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(
current.copyWith(keyboardHoldLeftSpeed: normalized),
);
await persistField(_keyKbdHoldLeft, normalized);
}
Future<void> setKeyboardHoldRightSpeed(double v) async {
final normalized = _normalizeSpeed(v, 3.0);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(
current.copyWith(keyboardHoldRightSpeed: normalized),
);
await persistField(_keyKbdHoldRight, normalized);
}
Future<void> setMouseLeftClickAction(GestureAction a) async {
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(mouseLeftClickAction: a));
await persistField(_keyMouseLeftClick, a.name);
}
Future<void> setMouseLeftDoubleClickAction(GestureAction a) async {
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(mouseLeftDoubleClickAction: a));
await persistField(_keyMouseLeftDblClick, a.name);
}
Future<void> setMouseRightClickAction(GestureAction a) async {
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(mouseRightClickAction: a));
await persistField(_keyMouseRightClick, a.name);
}
Future<void> setMouseRightDoubleClickAction(GestureAction a) async {
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(mouseRightDoubleClickAction: a));
await persistField(_keyMouseRightDblClick, a.name);
}
Future<void> setAndroidBrightness(double brightness) async {
final clamped = brightness.clamp(0.0, 1.0);
final current = state.value ?? const PlayerSettingsState();
state = AsyncValue.data(current.copyWith(androidBrightness: clamped));
await persistField(_keyAndroidBrightness, clamped);
}
}
final playerSettingsProvider =
AsyncNotifierProvider<PlayerSettingsNotifier, PlayerSettingsState>(
PlayerSettingsNotifier.new,
);