feat: move Stable Volume toggle into in-player settings menu instead of app Settings

This commit is contained in:
KhooLy 2026-07-31 17:28:17 +03:00
parent 53b8fe196c
commit abbc8e43c8
5 changed files with 31 additions and 18 deletions

View file

@ -237,6 +237,13 @@ export function ReactPlayerOverlay({ closePlayer, onFirstFrame, initialTitle, in
const { anime4kEnabled, toggleAnime4k, cycleAnime4kMode } = usePlayerAnime4k({ prefs, persistPreference: setSubtitlePref, flashFeedback });
const [stableVolumeEnabled, setStableVolumeEnabled] = useState(() => prefs?.stableVolume === true);
const toggleStableVolume = useCallback((enabled: boolean) => {
sendCmd(`set af ${enabled ? 'dynaudnorm' : '""'}`);
setStableVolumeEnabled(enabled);
setSubtitlePref('stableVolume', enabled);
}, [setSubtitlePref]);
const subtitleControls = usePlayerSubtitleControls({ prefs, persistPreference: setSubtitlePref, flashFeedback });
useEffect(() => { cycleAnime4kModeRef.current = cycleAnime4kMode; }, [cycleAnime4kMode]);
@ -324,7 +331,7 @@ export function ReactPlayerOverlay({ closePlayer, onFirstFrame, initialTitle, in
cast={castPopoverOpen ? { devices: castDevices, discovering: castDiscovering, activeDeviceId: activeCastDeviceId, anchorRef: castBtnRef, onClose: () => setCastPopoverOpen(false), onSelectDevice: (device) => void selectCastDevice(device), onDisconnect: disconnectCast } : undefined}
torrent={showTorrentPopover ? { stats: torrentStatsSnap, anchorRef: torrentBtnRef, onClose: () => setShowTorrentPopover(false) } : undefined}
marker={showSegmentMarker && ((introDbSubmitEnabled && introDbApiKey) || (skipDbSubmitEnabled && skipDbApiKey) || (theIntroDbSubmitEnabled && theIntroDbApiKey)) ? { onClose: () => setShowSegmentMarker(false), getPosMs: () => posRef.current * 1000, imdbId: introDbImdbId, tmdbId: introDbTmdbId, mediaType: metaRef?.current?.type === 'series' ? 'tv' : 'movie', season: currentEpisode?.season ?? null, episode: currentEpisode?.episode ?? currentEpisode?.number ?? null, introDbApiKey: introDbSubmitEnabled ? introDbApiKey : '', skipDbApiKey: skipDbSubmitEnabled ? skipDbApiKey : '', theIntroDbApiKey: theIntroDbSubmitEnabled ? theIntroDbApiKey : '', coverage: skipSegmentCoverage ?? {} } : undefined}
settings={showPlayerSettings ? { anchorRef: playerSettingsBtnRef, onClose: () => setShowPlayerSettings(false), anime4kEnabled, onToggleAnime4k: toggleAnime4k } : undefined}
settings={showPlayerSettings ? { anchorRef: playerSettingsBtnRef, onClose: () => setShowPlayerSettings(false), anime4kEnabled, onToggleAnime4k: toggleAnime4k, stableVolumeEnabled, onToggleStableVolume: toggleStableVolume } : undefined}
shortcuts={showShortcutsHelp ? { overrides: shortcutOverrides, onClose: () => setShowShortcutsHelp(false) } : undefined}
stats={showStats ? { stats: statsSnap, torrentStats: torrentStatsSnap, bufferHistory: bufferHistoryRef.current, networkSpeedHistory: netSpeedHistoryRef.current, torrentSpeedHistory, stallCount: stallCountRef.current } : undefined}
/>

View file

@ -7,25 +7,34 @@ interface PlayerSettingsPopoverProps {
onClose: () => void;
anime4kEnabled: boolean;
onToggleAnime4k: (enabled: boolean) => void;
stableVolumeEnabled: boolean;
onToggleStableVolume: (enabled: boolean) => void;
}
export function PlayerSettingsPopover({ anchorRef, onClose, anime4kEnabled, onToggleAnime4k }: PlayerSettingsPopoverProps) {
function SettingsToggleRow({ label, checked, onToggle }: { label: string; checked: boolean; onToggle: () => void }) {
return (
<div
onClick={onToggle}
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.75rem', padding: '0.5rem 0.875rem', cursor: 'pointer' }}
>
<span style={{ color: '#fff', fontSize: '0.8125rem' }}>{label}</span>
<div
style={{ flexShrink: 0, width: '2.75rem', height: '1.625rem', borderRadius: '62.4375rem', background: checked ? 'var(--primary-accent-color)' : 'rgba(255,255,255,0.14)', position: 'relative', transition: 'background 0.18s' }}
>
<div style={{ position: 'absolute', top: '0.1875rem', left: checked ? 21 : 3, width: '1.25rem', height: '1.25rem', borderRadius: '50%', background: checked ? '#000000' : 'rgba(255,255,255,0.80)', transition: 'left 0.18s' }} />
</div>
</div>
);
}
export function PlayerSettingsPopover({ anchorRef, onClose, anime4kEnabled, onToggleAnime4k, stableVolumeEnabled, onToggleStableVolume }: PlayerSettingsPopoverProps) {
return (
<Popover open onClose={onClose} anchorRef={anchorRef} placement="top" width="15rem">
<div style={{ color: 'rgba(255,255,255,0.45)', fontSize: '0.6875rem', fontWeight: 700, letterSpacing: '0.05rem', padding: '0.25rem 0.875rem 0.5rem', textTransform: 'uppercase' }}>
{t('player.settings')}
</div>
<div
onClick={() => onToggleAnime4k(!anime4kEnabled)}
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.75rem', padding: '0.5rem 0.875rem', cursor: 'pointer' }}
>
<span style={{ color: '#fff', fontSize: '0.8125rem' }}>{t('player.anime4k')}</span>
<div
style={{ flexShrink: 0, width: '2.75rem', height: '1.625rem', borderRadius: '62.4375rem', background: anime4kEnabled ? 'var(--primary-accent-color)' : 'rgba(255,255,255,0.14)', position: 'relative', transition: 'background 0.18s' }}
>
<div style={{ position: 'absolute', top: '0.1875rem', left: anime4kEnabled ? 21 : 3, width: '1.25rem', height: '1.25rem', borderRadius: '50%', background: anime4kEnabled ? '#000000' : 'rgba(255,255,255,0.80)', transition: 'left 0.18s' }} />
</div>
</div>
<SettingsToggleRow label={t('player.anime4k')} checked={anime4kEnabled} onToggle={() => onToggleAnime4k(!anime4kEnabled)} />
<SettingsToggleRow label={t('player.stable_volume')} checked={stableVolumeEnabled} onToggle={() => onToggleStableVolume(!stableVolumeEnabled)} />
</Popover>
);
}

View file

@ -155,7 +155,6 @@ export function PlaybackSection({ prefs, setPref }: { prefs: Prefs; setPref: <K
void invoke('player_set_seek_thumbnail_enabled', { enabled: v });
}}
/>
<ToggleTile title={t('settings.stable_volume')} subtitle={t('settings.stable_volume_desc')} checked={prefs.stableVolume} onToggle={(v) => setPref('stableVolume', v)} />
<ToggleTile
title="HDR output"
subtitle="Use HDR output when the display and renderer support it. Falls back to SDR when unavailable."

View file

@ -696,8 +696,6 @@
"settings.playback_speed_desc": "Sets the normal playback speed for videos.",
"settings.resume_playback_desc": "Continues from the last saved position when you reopen a title.",
"settings.forward_rewind_desc": "Sets how far the seek buttons jump forward or back.",
"settings.stable_volume": "Stable Volume",
"settings.stable_volume_desc": "Evens out loud and quiet moments so volume stays consistent.",
"settings.hold_to_speed": "Hold to Speed",
"settings.hold_to_speed_desc": "Temporarily speeds up playback while you hold the video.",
"settings.hold_speed": "Hold Speed",
@ -1427,6 +1425,7 @@
"player.picture_in_picture": "Mini player",
"player.settings": "Settings",
"player.anime4k": "Anime4K",
"player.stable_volume": "Stable Volume",
"player.restore_window": "Restore window",
"player.subtitle_delay_earlier": "Subtitles -0.1s",
"player.subtitle_delay_later": "Subtitles +0.1s",

View file

@ -696,8 +696,6 @@
"settings.playback_speed_desc": "Videolar için normal oynatma hızını belirler.",
"settings.resume_playback_desc": "Bir içeriği tekrar açtığında son kaydedilen yerden devam eder.",
"settings.forward_rewind_desc": "İleri ve geri sarma düğmelerinin kaç saniye atlayacağını belirler.",
"settings.stable_volume": "Sabit Ses Düzeyi",
"settings.stable_volume_desc": "Yüksek ve alçak sesli anları dengeleyerek ses düzeyini tutarlı tutar.",
"settings.hold_to_speed": "Basılı Tutunca Hızlandır",
"settings.hold_to_speed_desc": "Videoya basılı tuttuğun sürece oynatmayı geçici olarak hızlandırır.",
"settings.hold_speed": "Basılı Tutma Hızı",
@ -1427,6 +1425,7 @@
"player.picture_in_picture": "Mini oynatıcı",
"player.settings": "Ayarlar",
"player.anime4k": "Anime4K",
"player.stable_volume": "Sabit Ses Düzeyi",
"player.restore_window": "Pencereyi geri yükle",
"player.subtitle_delay_earlier": "Altyazı -0.1s",
"player.subtitle_delay_later": "Altyazı +0.1s",