Merge pull request #1248 from Stremio/feat/shell-media-session

Dev: Implement media controls ipc
This commit is contained in:
Tim 2026-05-04 08:57:25 +02:00 committed by GitHub
commit 0b3b2d3302
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 17 deletions

View file

@ -27,6 +27,10 @@ export type WindowState = {
state: number;
};
export type MediaStatus = {
paused: boolean;
};
const createId = () => Math.floor(Math.random() * 9999) + 1;
const useShell = () => {

View file

@ -1,4 +1,6 @@
import { useEffect } from 'react';
import { useShell } from 'stremio/common';
import { MediaStatus } from 'stremio/common/useShell';
const useMediaSession = (
videoState: VideoState,
@ -7,20 +9,30 @@ const useMediaSession = (
onPauseRequested: () => void,
onNextVideoRequested: () => void,
) => {
useEffect(() => {
if (!navigator.mediaSession) return;
const shell = useShell();
const playbackState = videoState.paused === null ? 'none' : videoState.paused ? 'paused' : 'playing';
navigator.mediaSession.playbackState = playbackState;
// Playback state
useEffect(() => {
if (navigator.mediaSession) {
const playbackState = videoState.paused === null ? 'none' : videoState.paused ? 'paused' : 'playing';
navigator.mediaSession.playbackState = playbackState;
}
if (shell.active) {
shell.send('media.status', {
paused: !!videoState.paused,
});
}
return () => {
navigator.mediaSession.playbackState = 'none';
if (navigator.mediaSession) {
navigator.mediaSession.playbackState = 'none';
}
};
}, [videoState.paused]);
// Metadata
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);
@ -35,27 +47,47 @@ const useMediaSession = (
const artwork = imageUrl ? [{ src: imageUrl }] : undefined;
if (title) {
navigator.mediaSession.metadata = new MediaMetadata({
title,
artist,
artwork,
});
if (navigator.mediaSession) {
navigator.mediaSession.metadata = new MediaMetadata({
title,
artist,
artwork,
});
}
if (shell.active) {
shell.send('media.metadata', {
title,
artist,
artUrl: imageUrl,
});
}
}
}, [player.metaItem, player.selected]);
// Callbacks
useEffect(() => {
if (!navigator.mediaSession) return;
if (navigator.mediaSession) {
navigator.mediaSession.setActionHandler('play', onPlayRequested);
navigator.mediaSession.setActionHandler('pause', onPauseRequested);
}
navigator.mediaSession.setActionHandler('play', onPlayRequested);
navigator.mediaSession.setActionHandler('pause', onPauseRequested);
const nexVideoCallback = player.nextVideo ? onNextVideoRequested : null;
if (navigator.mediaSession && nexVideoCallback) {
navigator.mediaSession.setActionHandler('nexttrack', nexVideoCallback);
}
const nextVideoCallback = player.nextVideo ? onNextVideoRequested : null;
navigator.mediaSession.setActionHandler('nexttrack', nextVideoCallback);
const onMediaStatus = ({ paused }: MediaStatus) => {
paused ? onPauseRequested() : onPlayRequested();
};
shell.on('media.status', onMediaStatus);
return () => {
navigator.mediaSession.setActionHandler('play', null);
navigator.mediaSession.setActionHandler('pause', null);
navigator.mediaSession.setActionHandler('nexttrack', null);
shell.on('media.status', onMediaStatus);
};
}, [player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested]);
};