mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 20:40:21 +02:00
fix MPV visualizer on macOS and handle exclusive mode UX (#1930)
This commit is contained in:
@@ -19,6 +19,7 @@ export function useVisualizerSystemAudio(options: {
|
||||
onDeniedRef.current = onSystemAudioCaptureDenied;
|
||||
onSuccessRef.current = onSystemAudioCaptureSuccess;
|
||||
const playbackType = usePlaybackType();
|
||||
const isMacOS = Boolean(window.api?.utils?.isMacOS?.());
|
||||
const { setWebAudio, webAudio } = useWebAudio();
|
||||
const webAudioRef = useRef(webAudio);
|
||||
const streamRef = useRef<MediaStream | null>(null);
|
||||
@@ -80,7 +81,7 @@ export function useVisualizerSystemAudio(options: {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia({
|
||||
audio: true,
|
||||
video: false,
|
||||
video: isMacOS, // On macOS, getDisplayMedia requires video to be requested in order to capture system audio
|
||||
});
|
||||
|
||||
const audioTracks = stream.getAudioTracks();
|
||||
@@ -124,7 +125,7 @@ export function useVisualizerSystemAudio(options: {
|
||||
} finally {
|
||||
connectInFlightRef.current = false;
|
||||
}
|
||||
}, [disconnect, setWebAudio]);
|
||||
}, [disconnect, isMacOS, setWebAudio]);
|
||||
|
||||
const connectRef = useRef(connect);
|
||||
connectRef.current = connect;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user