From c78230340753a9d3c0a622c241dfdc261ef40553 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Apr 2026 17:12:51 +0200 Subject: [PATCH] refactor: move media session logic to hook --- src/routes/Player/Player.js | 48 +---------------------- src/routes/Player/useMediaSession.ts | 57 ++++++++++++++++++++++++++++ src/routes/Player/videoState.d.ts | 3 ++ src/types/models/Player.d.ts | 1 - 4 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 src/routes/Player/useMediaSession.ts create mode 100644 src/routes/Player/videoState.d.ts diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 662717b70..77e8a952c 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -28,6 +28,7 @@ const useVideo = require('./useVideo'); const styles = require('./styles'); const Video = require('./Video'); const { default: Indicator } = require('./Indicator/Indicator'); +const { default: useMediaSession } = require('./useMediaSession'); const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); const findTrackById = (tracks, id) => tracks.find((track) => track.id === id); @@ -596,52 +597,7 @@ 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.logo : 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]); + useMediaSession(video.state, player, onPlayRequested, onPauseRequested, onNextVideoRequested); onShortcut('seekForward', (combo) => { if (video.state.time !== null) { diff --git a/src/routes/Player/useMediaSession.ts b/src/routes/Player/useMediaSession.ts new file mode 100644 index 000000000..7a63423bd --- /dev/null +++ b/src/routes/Player/useMediaSession.ts @@ -0,0 +1,57 @@ +import { useEffect } from 'react'; + +const useMediaSession = ( + videoState: VideoState, + player: Player, + onPlayRequested: () => void, + onPauseRequested: () => void, + onNextVideoRequested: () => void, +) => { + useEffect(() => { + if (!navigator.mediaSession) return; + + const playbackState = !videoState.paused ? 'playing' : 'paused'; + navigator.mediaSession.playbackState = playbackState; + + return () => { + navigator.mediaSession.playbackState = 'none'; + }; + }, [videoState.paused]); + + useEffect(() => { + if (!navigator.mediaSession) return; + + const metaItem = player.metaItem && player.metaItem?.type === 'Ready' ? player.metaItem.content as MetaItemPlayer : null; + const videoId = player.selected ? player.selected?.streamRequest?.path?.id : null; + const video = metaItem?.videos.find(({ id }) => id === videoId); + + const videoInfo = 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.logo : 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]); + + 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]); +}; + +export default useMediaSession; diff --git a/src/routes/Player/videoState.d.ts b/src/routes/Player/videoState.d.ts new file mode 100644 index 000000000..0f8a78c10 --- /dev/null +++ b/src/routes/Player/videoState.d.ts @@ -0,0 +1,3 @@ +type VideoState = { + paused?: boolean; +}; diff --git a/src/types/models/Player.d.ts b/src/types/models/Player.d.ts index 321127316..14e78a768 100644 --- a/src/types/models/Player.d.ts +++ b/src/types/models/Player.d.ts @@ -5,7 +5,6 @@ type LibraryItemPlayer = Pick & { type VideoPlayer = Video & { upcoming: boolean, watched: boolean, - progress: boolean | null, scheduled: boolean, deepLinks: VideoDeepLinks, };