pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

authored by

Chris and committed by
Pas
bf530902 31b3b0d3

+119 -86
+119 -86
src/components/player/internals/MediaSession.tsx
··· 10 10 (s) => s.setShouldStartFromBeginning, 11 11 ); 12 12 13 + const mediaPlaying = usePlayerStore((s) => s.mediaPlaying); 14 + const progress = usePlayerStore((s) => s.progress); 15 + const meta = usePlayerStore((s) => s.meta); 16 + const display = usePlayerStore((s) => s.display); 17 + 13 18 const shouldUpdatePositionState = useRef(false); 14 19 const lastPlaybackPosition = useRef(0); 15 - 16 - const data = usePlayerStore.getState(); 17 20 18 21 const changeEpisode = useCallback( 19 22 (change: number) => { 20 - const nextEp = data.meta?.episodes?.find( 21 - (v) => v.number === (data.meta?.episode?.number ?? 0) + change, 23 + const nextEp = meta?.episodes?.find( 24 + (v) => v.number === (meta?.episode?.number ?? 0) + change, 22 25 ); 23 26 24 - if (!data.meta || !nextEp) return; 25 - const metaCopy = { ...data.meta }; 27 + if (!meta || !nextEp) return; 28 + const metaCopy = { ...meta }; 26 29 metaCopy.episode = nextEp; 27 30 setShouldStartFromBeginning(true); 28 31 setDirectMeta(metaCopy); 29 32 }, 30 - [data.meta, setDirectMeta, setShouldStartFromBeginning], 33 + [meta, setDirectMeta, setShouldStartFromBeginning], 31 34 ); 32 35 33 36 const updatePositionState = useCallback( 34 37 (position: number) => { 35 - // If the browser doesn't support setPositionState, return 36 38 if (typeof navigator.mediaSession.setPositionState !== "function") return; 37 39 38 - // If the updated position needs to be buffered, queue an update 39 - if (position > data.progress.buffered) { 40 + const { duration, buffered } = progress; 41 + const { playbackRate } = mediaPlaying; 42 + 43 + if ( 44 + typeof duration !== "number" || 45 + Number.isNaN(duration) || 46 + !Number.isFinite(duration) || 47 + duration <= 0 48 + ) { 49 + return; 50 + } 51 + 52 + if ( 53 + typeof position !== "number" || 54 + Number.isNaN(position) || 55 + position < 0 56 + ) { 57 + position = 0; 58 + } 59 + 60 + if (position > buffered) { 40 61 shouldUpdatePositionState.current = true; 41 62 } 42 - if (position > data.progress.duration) return; 63 + 64 + if (position > duration) { 65 + position = duration; 66 + } 67 + 68 + lastPlaybackPosition.current = progress.time; 43 69 44 - lastPlaybackPosition.current = data.progress.time; 45 70 navigator.mediaSession.setPositionState({ 46 - duration: data.progress.duration, 47 - playbackRate: data.mediaPlaying.playbackRate, 71 + duration, 72 + playbackRate, 48 73 position, 49 74 }); 50 75 }, 51 - [ 52 - data.mediaPlaying.playbackRate, 53 - data.progress.buffered, 54 - data.progress.duration, 55 - data.progress.time, 56 - ], 76 + [mediaPlaying, progress], 57 77 ); 58 78 59 79 useEffect(() => { 60 80 if (!("mediaSession" in navigator)) return; 81 + navigator.mediaSession.playbackState = mediaPlaying.isPaused 82 + ? "paused" 83 + : "playing"; 84 + }, [mediaPlaying.isPaused]); 61 85 62 - // If the media is paused, update the navigator 63 - if (data.mediaPlaying.isPaused) { 64 - navigator.mediaSession.playbackState = "paused"; 65 - } else { 66 - navigator.mediaSession.playbackState = "playing"; 86 + useEffect(() => { 87 + if (!("mediaSession" in navigator)) return; 88 + if ( 89 + typeof progress.duration !== "number" || 90 + Number.isNaN(progress.duration) || 91 + progress.duration <= 0 92 + ) { 93 + return; 67 94 } 68 - }, [data.mediaPlaying.isPaused]); 95 + updatePositionState(progress.time); 96 + }, [ 97 + progress.time, 98 + mediaPlaying.playbackRate, 99 + progress.duration, 100 + updatePositionState, 101 + ]); 69 102 70 103 useEffect(() => { 71 104 if (!("mediaSession" in navigator)) return; 72 105 73 - updatePositionState(data.progress.time); 74 - }, [data.progress.time, data.mediaPlaying.playbackRate, updatePositionState]); 106 + const { time, duration } = progress; 107 + const { isLoading } = mediaPlaying; 108 + 109 + if ( 110 + typeof duration !== "number" || 111 + Number.isNaN(duration) || 112 + duration <= 0 113 + ) { 114 + return; 115 + } 75 116 76 - useEffect(() => { 77 - if (!("mediaSession" in navigator)) return; 78 - // If not already updating the position state, and the media is loading, queue an update 79 - if (!shouldUpdatePositionState.current && data.mediaPlaying.isLoading) { 117 + if (!shouldUpdatePositionState.current && isLoading) { 80 118 shouldUpdatePositionState.current = true; 81 119 } 82 120 83 - // If the user has skipped (or MediaSession desynced) by more than 5 seconds, queue an update 84 121 if ( 85 - Math.abs(data.progress.time - lastPlaybackPosition.current) >= 5 && 86 - !data.mediaPlaying.isLoading && 87 - !shouldUpdatePositionState.current 122 + !isLoading && 123 + !shouldUpdatePositionState.current && 124 + Math.abs(time - lastPlaybackPosition.current) >= 5 88 125 ) { 89 126 shouldUpdatePositionState.current = true; 90 127 } 91 128 92 - // If not loading and the position state is queued, update it 93 - if (shouldUpdatePositionState.current && !data.mediaPlaying.isLoading) { 129 + if (shouldUpdatePositionState.current && !isLoading) { 94 130 shouldUpdatePositionState.current = false; 95 - updatePositionState(data.progress.time); 131 + updatePositionState(time); 96 132 } 97 133 98 - lastPlaybackPosition.current = data.progress.time; 99 - }, [updatePositionState, data.progress.time, data.mediaPlaying.isLoading]); 134 + lastPlaybackPosition.current = time; 135 + }, [mediaPlaying, progress, updatePositionState]); 100 136 101 137 useEffect(() => { 102 138 if ( 103 139 !("mediaSession" in navigator) || 104 - (!data.mediaPlaying.isLoading && 105 - data.mediaPlaying.isPlaying && 106 - !data.display) 107 - ) 140 + (!mediaPlaying.isLoading && mediaPlaying.isPlaying && !display) 141 + ) { 108 142 return; 143 + } 109 144 110 145 let title: string | undefined; 111 146 let artist: string | undefined; 112 147 113 - if (data.meta?.type === "movie") { 114 - title = data.meta?.title; 115 - } else if (data.meta?.type === "show") { 116 - artist = data.meta?.title; 117 - title = `S${data.meta?.season?.number} E${data.meta?.episode?.number}: ${data.meta?.episode?.title}`; 148 + if (meta?.type === "movie") { 149 + title = meta.title; 150 + } else if (meta?.type === "show") { 151 + artist = meta.title; 152 + title = `S${meta.season?.number} E${meta.episode?.number}: ${meta.episode?.title}`; 118 153 } 119 154 120 155 navigator.mediaSession.metadata = new MediaMetadata({ 121 156 title, 122 157 artist, 123 158 artwork: [ 124 - { 125 - src: data.meta?.poster ?? "", 126 - sizes: "342x513", 127 - type: "image/png", 128 - }, 159 + { src: meta?.poster ?? "", sizes: "342x513", type: "image/png" }, 129 160 ], 130 161 }); 131 162 132 163 navigator.mediaSession.setActionHandler("play", () => { 133 - if (data.mediaPlaying.isLoading) return; 134 - data.display?.play(); 135 - 136 - updatePositionState(data.progress.time); 164 + if (mediaPlaying.isLoading) return; 165 + display?.play(); 166 + updatePositionState(progress.time); 137 167 }); 138 168 139 169 navigator.mediaSession.setActionHandler("pause", () => { 140 - if (data.mediaPlaying.isLoading) return; 141 - data.display?.pause(); 142 - 143 - updatePositionState(data.progress.time); 170 + if (mediaPlaying.isLoading) return; 171 + display?.pause(); 172 + updatePositionState(progress.time); 144 173 }); 145 174 146 175 navigator.mediaSession.setActionHandler("seekto", (e) => { 147 - if (!e.seekTime) return; 148 - data.display?.setTime(e.seekTime); 176 + if (e.seekTime == null) return; 177 + display?.setTime(e.seekTime); 149 178 updatePositionState(e.seekTime); 150 179 }); 151 180 152 - if ((data.meta?.episode?.number ?? 1) !== 1) { 153 - navigator.mediaSession.setActionHandler("previoustrack", () => { 154 - changeEpisode(-1); 155 - }); 181 + if ((meta?.episode?.number ?? 1) > 1) { 182 + navigator.mediaSession.setActionHandler("previoustrack", () => 183 + changeEpisode(-1), 184 + ); 156 185 } else { 157 186 navigator.mediaSession.setActionHandler("previoustrack", null); 158 187 } 159 188 160 - if (data.meta?.episode?.number !== data.meta?.episodes?.length) { 161 - navigator.mediaSession.setActionHandler("nexttrack", () => { 162 - changeEpisode(1); 163 - }); 189 + const totalEpisodes = meta?.episodes?.length ?? 0; 190 + const currentEpisodeNumber = meta?.episode?.number ?? 0; 191 + if (currentEpisodeNumber > 0 && currentEpisodeNumber < totalEpisodes) { 192 + navigator.mediaSession.setActionHandler("nexttrack", () => 193 + changeEpisode(1), 194 + ); 164 195 } else { 165 196 navigator.mediaSession.setActionHandler("nexttrack", null); 166 197 } 167 198 }, [ 168 199 changeEpisode, 169 200 updatePositionState, 170 - data.mediaPlaying.hasPlayedOnce, 171 - data.mediaPlaying.isLoading, 172 - data.progress.duration, 173 - data.progress.time, 174 - data.meta?.episode?.number, 175 - data.meta?.episodes?.length, 176 - data.display, 177 - data.mediaPlaying, 178 - data.meta?.episode?.title, 179 - data.meta?.title, 180 - data.meta?.type, 181 - data.meta?.poster, 182 - data.meta?.season?.number, 201 + mediaPlaying.isLoading, 202 + mediaPlaying.isPlaying, 203 + display, 204 + progress.duration, 205 + progress.time, 206 + meta?.episode?.number, 207 + meta?.episodes?.length, 208 + meta?.episode?.title, 209 + meta?.title, 210 + meta?.type, 211 + meta?.poster, 212 + meta?.season?.number, 183 213 ]); 214 + 184 215 return null; 185 216 } 217 + 218 + // what did we learn today? never use isNaN instead of Number.isNaN !!!