4189ef9899
useMediaSession() wires navigator.mediaSession metadata + action handlers (play/pause/previous/next/seekto) + positionState to the player store via $effect. Exports nothing stateful — callers just invoke it once at root-layout mount.
57 lines
1.4 KiB
TypeScript
57 lines
1.4 KiB
TypeScript
import {
|
|
player,
|
|
togglePlay,
|
|
skipNext,
|
|
skipPrev,
|
|
seekTo
|
|
} from './store.svelte';
|
|
|
|
export function useMediaSession(): void {
|
|
if (typeof navigator === 'undefined' || !('mediaSession' in navigator)) return;
|
|
const ms = navigator.mediaSession;
|
|
|
|
$effect(() => {
|
|
const t = player.current;
|
|
if (!t) {
|
|
ms.metadata = null;
|
|
return;
|
|
}
|
|
ms.metadata = new MediaMetadata({
|
|
title: t.title,
|
|
artist: t.artist_name,
|
|
album: t.album_title,
|
|
artwork: [{
|
|
src: `/api/albums/${t.album_id}/cover`,
|
|
sizes: '512x512',
|
|
type: 'image/jpeg'
|
|
}]
|
|
});
|
|
});
|
|
|
|
ms.setActionHandler('play', () => togglePlay());
|
|
ms.setActionHandler('pause', () => togglePlay());
|
|
ms.setActionHandler('previoustrack', () => skipPrev());
|
|
ms.setActionHandler('nexttrack', () => skipNext());
|
|
ms.setActionHandler('seekto', (e) => {
|
|
if (typeof e.seekTime === 'number') seekTo(e.seekTime);
|
|
});
|
|
|
|
$effect(() => {
|
|
if (player.duration > 0 && 'setPositionState' in ms) {
|
|
try {
|
|
ms.setPositionState({
|
|
duration: player.duration,
|
|
position: Math.min(player.position, player.duration),
|
|
playbackRate: 1
|
|
});
|
|
} catch {
|
|
// setPositionState can throw on NaN/Infinity briefly between tracks.
|
|
}
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
ms.playbackState = player.isPlaying ? 'playing' : 'paused';
|
|
});
|
|
}
|