From e65c0237721bfbba411f9aac8cb882300e0ca14f Mon Sep 17 00:00:00 2001 From: erik Date: Sat, 2 May 2026 18:24:44 +0200 Subject: [PATCH] Handle macOS media controls --- src/routes/Player/Player.js | 24 ++++++++++++++++++------ src/routes/Player/useMediaSession.ts | 17 +++++++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index ccc59a7d6..5b650fe1f 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -255,6 +255,12 @@ const Player = ({ urlParams, queryParams }) => { } }, [player.nextVideo, handleNextVideoNavigation, profile.settings]); + const onPreviousTrackRequested = React.useCallback(() => { + if (video.state.time !== null && video.state.time > 5000) { + onSeekRequested(0); + } + }, [video.state.time, onSeekRequested]); + const onVideoClick = React.useCallback(() => { if (video.state.paused !== null && !longPress.current) { if (video.state.paused) { @@ -534,13 +540,21 @@ const Player = ({ urlParams, queryParams }) => { } }, [settings.pauseOnMinimize, shell.windowClosed, shell.windowHidden]); - useMediaSession(video.state, player, onPlayRequested, onPauseRequested, onNextVideoRequested); + useMediaSession(video.state, player, onPlayRequested, onPauseRequested, onNextVideoRequested, onPreviousTrackRequested); React.useEffect(() => { const onMediaKey = (action) => { switch (action) { case 'play-pause': - video.state.paused ? onPlayRequested() : onPauseRequested(); + if (video.state.paused !== null) { + video.state.paused ? onPlayRequested() : onPauseRequested(); + } + break; + case 'play': + onPlayRequested(); + break; + case 'pause': + onPauseRequested(); break; case 'next-track': if (player.nextVideo !== null) { @@ -549,15 +563,13 @@ const Player = ({ urlParams, queryParams }) => { } break; case 'previous-track': - if (video.state.time !== null && video.state.time > 5000) { - onSeekRequested(0); - } + onPreviousTrackRequested(); break; } }; shell.on('media-key', onMediaKey); return () => shell.off('media-key', onMediaKey); - }, [video.state.paused, video.state.time, player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested, onSeekRequested]); + }, [video.state.paused, player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested, onPreviousTrackRequested]); onShortcut('seekForward', (combo) => { if (video.state.time !== null) { diff --git a/src/routes/Player/useMediaSession.ts b/src/routes/Player/useMediaSession.ts index 7a63423bd..bbd941e20 100644 --- a/src/routes/Player/useMediaSession.ts +++ b/src/routes/Player/useMediaSession.ts @@ -6,11 +6,12 @@ const useMediaSession = ( onPlayRequested: () => void, onPauseRequested: () => void, onNextVideoRequested: () => void, + onPreviousTrackRequested: () => void, ) => { useEffect(() => { if (!navigator.mediaSession) return; - const playbackState = !videoState.paused ? 'playing' : 'paused'; + const playbackState = videoState.paused === null ? 'none' : videoState.paused ? 'paused' : 'playing'; navigator.mediaSession.playbackState = playbackState; return () => { @@ -49,9 +50,17 @@ const useMediaSession = ( navigator.mediaSession.setActionHandler('play', onPlayRequested); navigator.mediaSession.setActionHandler('pause', onPauseRequested); - const nexVideoCallback = player.nextVideo ? onNextVideoRequested : null; - navigator.mediaSession.setActionHandler('nexttrack', nexVideoCallback); - }, [player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested]); + const nextVideoCallback = player.nextVideo ? onNextVideoRequested : null; + navigator.mediaSession.setActionHandler('nexttrack', nextVideoCallback); + navigator.mediaSession.setActionHandler('previoustrack', onPreviousTrackRequested); + + return () => { + navigator.mediaSession.setActionHandler('play', null); + navigator.mediaSession.setActionHandler('pause', null); + navigator.mediaSession.setActionHandler('nexttrack', null); + navigator.mediaSession.setActionHandler('previoustrack', null); + }; + }, [player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested, onPreviousTrackRequested]); }; export default useMediaSession;