mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-21 00:32:26 +00:00
Updated MediaSession.tsx
A new and more cleaner MediaSession.tsx also fixed TypeError: Failed to execute 'setPositionState' on 'MediaSession': The provided duration cannot be NaN.
This commit is contained in:
parent
31b3b0d369
commit
bf530902cc
1 changed files with 119 additions and 86 deletions
|
|
@ -10,176 +10,209 @@ export function MediaSession() {
|
||||||
(s) => s.setShouldStartFromBeginning,
|
(s) => s.setShouldStartFromBeginning,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mediaPlaying = usePlayerStore((s) => s.mediaPlaying);
|
||||||
|
const progress = usePlayerStore((s) => s.progress);
|
||||||
|
const meta = usePlayerStore((s) => s.meta);
|
||||||
|
const display = usePlayerStore((s) => s.display);
|
||||||
|
|
||||||
const shouldUpdatePositionState = useRef(false);
|
const shouldUpdatePositionState = useRef(false);
|
||||||
const lastPlaybackPosition = useRef(0);
|
const lastPlaybackPosition = useRef(0);
|
||||||
|
|
||||||
const data = usePlayerStore.getState();
|
|
||||||
|
|
||||||
const changeEpisode = useCallback(
|
const changeEpisode = useCallback(
|
||||||
(change: number) => {
|
(change: number) => {
|
||||||
const nextEp = data.meta?.episodes?.find(
|
const nextEp = meta?.episodes?.find(
|
||||||
(v) => v.number === (data.meta?.episode?.number ?? 0) + change,
|
(v) => v.number === (meta?.episode?.number ?? 0) + change,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.meta || !nextEp) return;
|
if (!meta || !nextEp) return;
|
||||||
const metaCopy = { ...data.meta };
|
const metaCopy = { ...meta };
|
||||||
metaCopy.episode = nextEp;
|
metaCopy.episode = nextEp;
|
||||||
setShouldStartFromBeginning(true);
|
setShouldStartFromBeginning(true);
|
||||||
setDirectMeta(metaCopy);
|
setDirectMeta(metaCopy);
|
||||||
},
|
},
|
||||||
[data.meta, setDirectMeta, setShouldStartFromBeginning],
|
[meta, setDirectMeta, setShouldStartFromBeginning],
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatePositionState = useCallback(
|
const updatePositionState = useCallback(
|
||||||
(position: number) => {
|
(position: number) => {
|
||||||
// If the browser doesn't support setPositionState, return
|
|
||||||
if (typeof navigator.mediaSession.setPositionState !== "function") return;
|
if (typeof navigator.mediaSession.setPositionState !== "function") return;
|
||||||
|
|
||||||
// If the updated position needs to be buffered, queue an update
|
const { duration, buffered } = progress;
|
||||||
if (position > data.progress.buffered) {
|
const { playbackRate } = mediaPlaying;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof duration !== "number" ||
|
||||||
|
Number.isNaN(duration) ||
|
||||||
|
!Number.isFinite(duration) ||
|
||||||
|
duration <= 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof position !== "number" ||
|
||||||
|
Number.isNaN(position) ||
|
||||||
|
position < 0
|
||||||
|
) {
|
||||||
|
position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position > buffered) {
|
||||||
shouldUpdatePositionState.current = true;
|
shouldUpdatePositionState.current = true;
|
||||||
}
|
}
|
||||||
if (position > data.progress.duration) return;
|
|
||||||
|
|
||||||
lastPlaybackPosition.current = data.progress.time;
|
if (position > duration) {
|
||||||
|
position = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPlaybackPosition.current = progress.time;
|
||||||
|
|
||||||
navigator.mediaSession.setPositionState({
|
navigator.mediaSession.setPositionState({
|
||||||
duration: data.progress.duration,
|
duration,
|
||||||
playbackRate: data.mediaPlaying.playbackRate,
|
playbackRate,
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[mediaPlaying, progress],
|
||||||
data.mediaPlaying.playbackRate,
|
|
||||||
data.progress.buffered,
|
|
||||||
data.progress.duration,
|
|
||||||
data.progress.time,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!("mediaSession" in navigator)) return;
|
if (!("mediaSession" in navigator)) return;
|
||||||
|
navigator.mediaSession.playbackState = mediaPlaying.isPaused
|
||||||
|
? "paused"
|
||||||
|
: "playing";
|
||||||
|
}, [mediaPlaying.isPaused]);
|
||||||
|
|
||||||
// If the media is paused, update the navigator
|
useEffect(() => {
|
||||||
if (data.mediaPlaying.isPaused) {
|
if (!("mediaSession" in navigator)) return;
|
||||||
navigator.mediaSession.playbackState = "paused";
|
if (
|
||||||
} else {
|
typeof progress.duration !== "number" ||
|
||||||
navigator.mediaSession.playbackState = "playing";
|
Number.isNaN(progress.duration) ||
|
||||||
|
progress.duration <= 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [data.mediaPlaying.isPaused]);
|
updatePositionState(progress.time);
|
||||||
|
}, [
|
||||||
|
progress.time,
|
||||||
|
mediaPlaying.playbackRate,
|
||||||
|
progress.duration,
|
||||||
|
updatePositionState,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!("mediaSession" in navigator)) return;
|
if (!("mediaSession" in navigator)) return;
|
||||||
|
|
||||||
updatePositionState(data.progress.time);
|
const { time, duration } = progress;
|
||||||
}, [data.progress.time, data.mediaPlaying.playbackRate, updatePositionState]);
|
const { isLoading } = mediaPlaying;
|
||||||
|
|
||||||
useEffect(() => {
|
if (
|
||||||
if (!("mediaSession" in navigator)) return;
|
typeof duration !== "number" ||
|
||||||
// If not already updating the position state, and the media is loading, queue an update
|
Number.isNaN(duration) ||
|
||||||
if (!shouldUpdatePositionState.current && data.mediaPlaying.isLoading) {
|
duration <= 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldUpdatePositionState.current && isLoading) {
|
||||||
shouldUpdatePositionState.current = true;
|
shouldUpdatePositionState.current = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has skipped (or MediaSession desynced) by more than 5 seconds, queue an update
|
|
||||||
if (
|
if (
|
||||||
Math.abs(data.progress.time - lastPlaybackPosition.current) >= 5 &&
|
!isLoading &&
|
||||||
!data.mediaPlaying.isLoading &&
|
!shouldUpdatePositionState.current &&
|
||||||
!shouldUpdatePositionState.current
|
Math.abs(time - lastPlaybackPosition.current) >= 5
|
||||||
) {
|
) {
|
||||||
shouldUpdatePositionState.current = true;
|
shouldUpdatePositionState.current = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not loading and the position state is queued, update it
|
if (shouldUpdatePositionState.current && !isLoading) {
|
||||||
if (shouldUpdatePositionState.current && !data.mediaPlaying.isLoading) {
|
|
||||||
shouldUpdatePositionState.current = false;
|
shouldUpdatePositionState.current = false;
|
||||||
updatePositionState(data.progress.time);
|
updatePositionState(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPlaybackPosition.current = data.progress.time;
|
lastPlaybackPosition.current = time;
|
||||||
}, [updatePositionState, data.progress.time, data.mediaPlaying.isLoading]);
|
}, [mediaPlaying, progress, updatePositionState]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
!("mediaSession" in navigator) ||
|
!("mediaSession" in navigator) ||
|
||||||
(!data.mediaPlaying.isLoading &&
|
(!mediaPlaying.isLoading && mediaPlaying.isPlaying && !display)
|
||||||
data.mediaPlaying.isPlaying &&
|
) {
|
||||||
!data.display)
|
|
||||||
)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let title: string | undefined;
|
let title: string | undefined;
|
||||||
let artist: string | undefined;
|
let artist: string | undefined;
|
||||||
|
|
||||||
if (data.meta?.type === "movie") {
|
if (meta?.type === "movie") {
|
||||||
title = data.meta?.title;
|
title = meta.title;
|
||||||
} else if (data.meta?.type === "show") {
|
} else if (meta?.type === "show") {
|
||||||
artist = data.meta?.title;
|
artist = meta.title;
|
||||||
title = `S${data.meta?.season?.number} E${data.meta?.episode?.number}: ${data.meta?.episode?.title}`;
|
title = `S${meta.season?.number} E${meta.episode?.number}: ${meta.episode?.title}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title,
|
title,
|
||||||
artist,
|
artist,
|
||||||
artwork: [
|
artwork: [
|
||||||
{
|
{ src: meta?.poster ?? "", sizes: "342x513", type: "image/png" },
|
||||||
src: data.meta?.poster ?? "",
|
|
||||||
sizes: "342x513",
|
|
||||||
type: "image/png",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
navigator.mediaSession.setActionHandler("play", () => {
|
navigator.mediaSession.setActionHandler("play", () => {
|
||||||
if (data.mediaPlaying.isLoading) return;
|
if (mediaPlaying.isLoading) return;
|
||||||
data.display?.play();
|
display?.play();
|
||||||
|
updatePositionState(progress.time);
|
||||||
updatePositionState(data.progress.time);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
navigator.mediaSession.setActionHandler("pause", () => {
|
navigator.mediaSession.setActionHandler("pause", () => {
|
||||||
if (data.mediaPlaying.isLoading) return;
|
if (mediaPlaying.isLoading) return;
|
||||||
data.display?.pause();
|
display?.pause();
|
||||||
|
updatePositionState(progress.time);
|
||||||
updatePositionState(data.progress.time);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
navigator.mediaSession.setActionHandler("seekto", (e) => {
|
navigator.mediaSession.setActionHandler("seekto", (e) => {
|
||||||
if (!e.seekTime) return;
|
if (e.seekTime == null) return;
|
||||||
data.display?.setTime(e.seekTime);
|
display?.setTime(e.seekTime);
|
||||||
updatePositionState(e.seekTime);
|
updatePositionState(e.seekTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((data.meta?.episode?.number ?? 1) !== 1) {
|
if ((meta?.episode?.number ?? 1) > 1) {
|
||||||
navigator.mediaSession.setActionHandler("previoustrack", () => {
|
navigator.mediaSession.setActionHandler("previoustrack", () =>
|
||||||
changeEpisode(-1);
|
changeEpisode(-1),
|
||||||
});
|
);
|
||||||
} else {
|
} else {
|
||||||
navigator.mediaSession.setActionHandler("previoustrack", null);
|
navigator.mediaSession.setActionHandler("previoustrack", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.meta?.episode?.number !== data.meta?.episodes?.length) {
|
const totalEpisodes = meta?.episodes?.length ?? 0;
|
||||||
navigator.mediaSession.setActionHandler("nexttrack", () => {
|
const currentEpisodeNumber = meta?.episode?.number ?? 0;
|
||||||
changeEpisode(1);
|
if (currentEpisodeNumber > 0 && currentEpisodeNumber < totalEpisodes) {
|
||||||
});
|
navigator.mediaSession.setActionHandler("nexttrack", () =>
|
||||||
|
changeEpisode(1),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
navigator.mediaSession.setActionHandler("nexttrack", null);
|
navigator.mediaSession.setActionHandler("nexttrack", null);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
changeEpisode,
|
changeEpisode,
|
||||||
updatePositionState,
|
updatePositionState,
|
||||||
data.mediaPlaying.hasPlayedOnce,
|
mediaPlaying.isLoading,
|
||||||
data.mediaPlaying.isLoading,
|
mediaPlaying.isPlaying,
|
||||||
data.progress.duration,
|
display,
|
||||||
data.progress.time,
|
progress.duration,
|
||||||
data.meta?.episode?.number,
|
progress.time,
|
||||||
data.meta?.episodes?.length,
|
meta?.episode?.number,
|
||||||
data.display,
|
meta?.episodes?.length,
|
||||||
data.mediaPlaying,
|
meta?.episode?.title,
|
||||||
data.meta?.episode?.title,
|
meta?.title,
|
||||||
data.meta?.title,
|
meta?.type,
|
||||||
data.meta?.type,
|
meta?.poster,
|
||||||
data.meta?.poster,
|
meta?.season?.number,
|
||||||
data.meta?.season?.number,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// what did we learn today? never use isNaN instead of Number.isNaN !!!
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue