From 60d8d18a0ff9ebd2a16244fcc8562cc59a2881d7 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 28 Nov 2025 14:29:39 -0800 Subject: [PATCH] add mediasession --- .../player/components/audio-players.tsx | 2 + .../player/hooks/use-media-session.ts | 139 +++++++----------- 2 files changed, 59 insertions(+), 82 deletions(-) diff --git a/src/renderer/features/player/components/audio-players.tsx b/src/renderer/features/player/components/audio-players.tsx index b8cbbd2f5..5934c03e8 100644 --- a/src/renderer/features/player/components/audio-players.tsx +++ b/src/renderer/features/player/components/audio-players.tsx @@ -5,6 +5,7 @@ import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/ev import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc'; import { MpvPlayer } from '/@/renderer/features/player/audio-player/mpv-player'; import { WebPlayer } from '/@/renderer/features/player/audio-player/web-player'; +import { useMediaSession } from '/@/renderer/features/player/hooks/use-media-session'; import { useMPRIS } from '/@/renderer/features/player/hooks/use-mpris'; import { usePowerSaveBlocker } from '/@/renderer/features/player/hooks/use-power-save-blocker'; import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble'; @@ -25,6 +26,7 @@ export const AudioPlayers = () => { usePowerSaveBlocker(); useDiscordRpc(); useMPRIS(); + useMediaSession(); // Listen to favorite and rating events to update queue songs useEffect(() => { diff --git a/src/renderer/features/player/hooks/use-media-session.ts b/src/renderer/features/player/hooks/use-media-session.ts index f74e2e4a9..7864986d9 100644 --- a/src/renderer/features/player/hooks/use-media-session.ts +++ b/src/renderer/features/player/hooks/use-media-session.ts @@ -1,73 +1,66 @@ -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; -import { - usePlaybackSettings, - usePlayerSong, - usePlayerStatus, - useSettingsStore, -} from '/@/renderer/store'; +import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events'; +import { usePlayer } from '/@/renderer/features/player/context/player-context'; +import { usePlaybackSettings, useSettingsStore, useTimestampStoreBase } from '/@/renderer/store'; import { PlayerStatus } from '/@/shared/types/types'; -export const useMediaSession = ({ - handleNextTrack, - handlePause, - handlePlay, - handlePrevTrack, - handleSeekSlider, - handleSkipBackward, - handleSkipForward, - handleStop, -}: { - handleNextTrack: () => void; - handlePause: () => void; - handlePlay: () => void; - handlePrevTrack: () => void; - handleSeekSlider: (e: any | number) => void; - handleSkipBackward: (seconds: number) => void; - handleSkipForward: (seconds: number) => void; - handleStop: () => void; -}) => { +export const useMediaSession = () => { const { mediaSession: mediaSessionEnabled } = usePlaybackSettings(); - const playerStatus = usePlayerStatus(); - const currentSong = usePlayerSong(); + const player = usePlayer(); const mediaSession = navigator.mediaSession; const skip = useSettingsStore((state) => state.general.skipButtons); + const isMediaSessionEnabled = useMemo(() => { + return mediaSessionEnabled && mediaSession; + }, [mediaSessionEnabled, mediaSession]); + useEffect(() => { - if (!mediaSessionEnabled || !mediaSession) { + if (!isMediaSessionEnabled) { return; } mediaSession.setActionHandler('nexttrack', () => { - handleNextTrack(); + player.mediaNext(); }); mediaSession.setActionHandler('pause', () => { - handlePause(); + player.mediaPause(); }); mediaSession.setActionHandler('play', () => { - handlePlay(); + player.mediaPlay(); }); mediaSession.setActionHandler('previoustrack', () => { - handlePrevTrack(); + player.mediaPrevious(); }); mediaSession.setActionHandler('seekto', (e) => { - handleSeekSlider(e.seekTime); + if (e.seekTime) { + player.mediaSeekToTimestamp(e.seekTime); + } else if (e.seekOffset) { + const currentTimestamp = useTimestampStoreBase.getState().timestamp; + player.mediaSeekToTimestamp(currentTimestamp + e.seekOffset); + } }); mediaSession.setActionHandler('stop', () => { - handleStop(); + player.mediaStop(); }); mediaSession.setActionHandler('seekbackward', (e) => { - handleSkipBackward(e.seekOffset || skip?.skipBackwardSeconds || 5); + const currentTimestamp = useTimestampStoreBase.getState().timestamp; + player.mediaSeekToTimestamp( + currentTimestamp - (e.seekOffset || skip?.skipBackwardSeconds || 5), + ); }); mediaSession.setActionHandler('seekforward', (e) => { - handleSkipForward(e.seekOffset || skip?.skipForwardSeconds || 5); + const currentTimestamp = useTimestampStoreBase.getState().timestamp; + player.mediaSeekToTimestamp( + currentTimestamp + (e.seekOffset || skip?.skipForwardSeconds || 5), + ); }); return () => { @@ -81,55 +74,37 @@ export const useMediaSession = ({ mediaSession.setActionHandler('seekforward', null); }; }, [ - handleNextTrack, - handlePause, - handlePlay, - handlePrevTrack, - handleSeekSlider, - handleSkipBackward, - handleSkipForward, - handleStop, - mediaSession, - mediaSessionEnabled, + player, skip?.skipBackwardSeconds, skip?.skipForwardSeconds, + isMediaSessionEnabled, + mediaSession, ]); - useEffect(() => { - if (!mediaSessionEnabled || !mediaSession) { - return; - } + usePlayerEvents( + { + onCurrentSongChange: (properties) => { + if (!isMediaSessionEnabled) { + return; + } - const updateMetadata = () => { - mediaSession.metadata = new MediaMetadata({ - album: currentSong?.album ?? '', - artist: currentSong?.artistName ?? '', - artwork: currentSong?.imageUrl - ? [{ src: currentSong.imageUrl, type: 'image/png' }] - : [], - title: currentSong?.name ?? '', - }); - }; + const song = properties.song; + mediaSession.metadata = new MediaMetadata({ + album: song?.album ?? '', + artist: song?.artistName ?? '', + artwork: song?.imageUrl ? [{ src: song.imageUrl, type: 'image/png' }] : [], + title: song?.name ?? '', + }); + }, + onPlayerStatus: (properties) => { + if (!isMediaSessionEnabled) { + return; + } - updateMetadata(); - - return () => { - mediaSession.metadata = null; - }; - }, [currentSong, mediaSession, mediaSessionEnabled]); - - useEffect(() => { - if (!mediaSessionEnabled || !mediaSession) { - return; - } - - if (mediaSession) { - const status = playerStatus === PlayerStatus.PLAYING ? 'playing' : 'paused'; - mediaSession.playbackState = status; - } - - return () => { - mediaSession.playbackState = 'none'; - }; - }, [playerStatus, mediaSession, mediaSessionEnabled]); + const status = properties.status; + mediaSession.playbackState = status === PlayerStatus.PLAYING ? 'playing' : 'paused'; + }, + }, + [isMediaSessionEnabled, mediaSession], + ); };