From c26dac2154d8d6d5eb0433b0d3179089ce2885ae Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 6 Oct 2025 14:09:01 +0200 Subject: [PATCH] feat(Player): add media session support --- src/routes/Player/Player.js | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 436f1a781..e9583ec3c 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -532,6 +532,53 @@ const Player = ({ urlParams, queryParams }) => { } }, [settings.pauseOnMinimize, shell.windowClosed, shell.windowHidden]); + // Media Session PlaybackState + React.useEffect(() => { + if (!navigator.mediaSession) return; + + const playbackState = !video.state.paused ? 'playing' : 'paused'; + navigator.mediaSession.playbackState = playbackState; + + return () => navigator.mediaSession.playbackState = 'none'; + }, [video.state.paused]); + + // Media Session Metadata + React.useEffect(() => { + if (!navigator.mediaSession) return; + + const metaItem = player.metaItem && player.metaItem.type === 'Ready' ? player.metaItem.content : null; + const videoId = player.selected ? player.selected.streamRequest.path.id : null; + const video = metaItem ? metaItem.videos.find(({ id }) => id === videoId) : null; + + const videoInfo = video && video.season && video.episode ? ` (${video.season}x${video.episode})`: null; + const videoTitle = video ? `${video.title}${videoInfo}` : null; + const metaTitle = metaItem ? metaItem.name : null; + const imageUrl = metaItem ? metaItem.poster : null; + + const title = videoTitle ?? metaTitle; + const artist = videoTitle ? metaTitle : undefined; + const artwork = imageUrl ? [{ src: imageUrl }] : undefined; + + if (title) { + navigator.mediaSession.metadata = new MediaMetadata({ + title, + artist, + artwork, + }); + } + }, [player.metaItem, player.selected]); + + // Media Session Actions + React.useEffect(() => { + if (!navigator.mediaSession) return; + + 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]); + React.useLayoutEffect(() => { const onKeyDown = (event) => { switch (event.code) {