fix MPV visualizer on macOS and handle exclusive mode UX (#1930)

This commit is contained in:
York
2026-04-14 11:47:03 +08:00
committed by GitHub
parent f5839bf39c
commit b99899f128
7 changed files with 58 additions and 6 deletions
@@ -1,16 +1,17 @@
import isElectron from 'is-electron';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useIsLocalVisualizerSurfaceVisible } from '/@/renderer/features/player/hooks/use-is-local-visualizer-surface-visible';
import { useVisualizerSystemAudio } from '/@/renderer/features/player/hooks/use-visualizer-system-audio';
import { closeLocalVisualizerSurfaces } from '/@/renderer/features/player/utils/close-local-visualizer-surfaces';
import { usePlaybackType } from '/@/renderer/store';
import { useMpvSettings, usePlaybackType } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { Modal } from '/@/shared/components/modal/modal';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast';
import { useDisclosure } from '/@/shared/hooks/use-disclosure';
import { PlayerType } from '/@/shared/types/types';
@@ -31,12 +32,21 @@ export function VisualizerSystemAudioBridgeHook() {
function VisualizerSystemAudioBridge() {
const { t } = useTranslation();
const playbackType = usePlaybackType();
const { audioExclusiveMode } = useMpvSettings();
const isVisualizerSurfaceVisible = useIsLocalVisualizerSurfaceVisible();
const [promptState, setPromptState] = useState<PromptState>('loading');
const [sessionAllowCapture, setSessionAllowCapture] = useState(false);
const wasBlockedByExclusiveModeRef = useRef(false);
const [isPromptOpen, { close: closePrompt, open: openPrompt, toggle: togglePrompt }] =
useDisclosure(false);
const isExclusiveModeEnabled = audioExclusiveMode === 'yes';
const isVisualizerBlockedByExclusiveMode =
isElectron() &&
playbackType === PlayerType.LOCAL &&
isVisualizerSurfaceVisible &&
isExclusiveModeEnabled;
const persistConsent = useCallback((granted: boolean) => {
if (!isElectron() || !window.api.localSettings) {
return;
@@ -67,6 +77,7 @@ function VisualizerSystemAudioBridge() {
const eligibleForPrompt =
isElectron() &&
playbackType === PlayerType.LOCAL &&
!isExclusiveModeEnabled &&
isVisualizerSurfaceVisible &&
promptState !== 'loading' &&
!promptState.consent &&
@@ -80,9 +91,25 @@ function VisualizerSystemAudioBridge() {
}
}, [eligibleForPrompt, closePrompt, openPrompt]);
useEffect(() => {
if (isVisualizerBlockedByExclusiveMode && !wasBlockedByExclusiveModeRef.current) {
toast.error({
message: t('visualizer.systemAudioExclusiveModeNotSupported', {
postProcess: 'sentenceCase',
}),
});
setSessionAllowCapture(false);
closePrompt();
closeLocalVisualizerSurfaces();
}
wasBlockedByExclusiveModeRef.current = isVisualizerBlockedByExclusiveMode;
}, [closePrompt, isVisualizerBlockedByExclusiveMode, t]);
const shouldAttemptConnection =
isElectron() &&
playbackType === PlayerType.LOCAL &&
!isExclusiveModeEnabled &&
isVisualizerSurfaceVisible &&
promptState !== 'loading' &&
(promptState.consent || sessionAllowCapture);