fix web player resuming on queue modification (#2250)

- During queue edits, stream URL reloads briefly reported a bogus duration (~0.07s). The gapless handler treated that as near end-of-track and called .play() while the player was still paused
This commit is contained in:
jeffvli
2026-07-21 00:29:20 -07:00
parent d991d4e1b8
commit 390b231e06
2 changed files with 39 additions and 2 deletions
@@ -263,13 +263,15 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
networkRetryCount2.current = 0; networkRetryCount2.current = 0;
}, [src1, src2]); }, [src1, src2]);
// When not transitioning, ensure only the active player can play (e.g. after seek/prev during transition) // When not playing, always pause both players — even during a transition
useEffect(() => { useEffect(() => {
if (isTransitioning) return;
if (playerStatus !== PlayerStatus.PLAYING) { if (playerStatus !== PlayerStatus.PLAYING) {
pauseBothPlayers(); pauseBothPlayers();
return; return;
} }
if (isTransitioning) {
return;
}
if (playerNum === 1) { if (playerNum === 1) {
player2Ref.current?.getInternalPlayer()?.pause(); player2Ref.current?.getInternalPlayer()?.pause();
} else { } else {
@@ -136,6 +136,10 @@ export function WebPlayer() {
return; return;
} }
if (usePlayerStoreBase.getState().player.status !== PlayerStatus.PLAYING) {
return;
}
switch (transitionType) { switch (transitionType) {
case PlayerStyle.CROSSFADE: case PlayerStyle.CROSSFADE:
crossfadeHandler({ crossfadeHandler({
@@ -195,6 +199,10 @@ export function WebPlayer() {
return; return;
} }
if (usePlayerStoreBase.getState().player.status !== PlayerStatus.PLAYING) {
return;
}
switch (transitionType) { switch (transitionType) {
case PlayerStyle.CROSSFADE: case PlayerStyle.CROSSFADE:
crossfadeHandler({ crossfadeHandler({
@@ -286,6 +294,11 @@ export function WebPlayer() {
onCurrentSongChange: () => { onCurrentSongChange: () => {
setIsTransitioning(false); setIsTransitioning(false);
}, },
onPlayerQueueChange: () => {
if (usePlayerStoreBase.getState().player.status !== PlayerStatus.PLAYING) {
setIsTransitioning(false);
}
},
onPlayerSeekToTimestamp: (properties) => { onPlayerSeekToTimestamp: (properties) => {
setIsTransitioning(false); setIsTransitioning(false);
@@ -607,6 +620,13 @@ function crossfadeHandler(args: {
} = args; } = args;
const player = `player${playerNum}`; const player = `player${playerNum}`;
if (usePlayerStoreBase.getState().player.status !== PlayerStatus.PLAYING) {
if (isTransitioning) {
setIsTransitioning(false);
}
return;
}
// If there is no next song to transition to, ensure we don't enter or stay in a transition // If there is no next song to transition to, ensure we don't enter or stay in a transition
if (!hasNextSong) { if (!hasNextSong) {
currentPlayer.setVolume(volume); currentPlayer.setVolume(volume);
@@ -714,10 +734,25 @@ function gaplessHandler(args: {
setIsTransitioning, setIsTransitioning,
} = args; } = args;
if (usePlayerStoreBase.getState().player.status !== PlayerStatus.PLAYING) {
if (isTransitioning) {
setIsTransitioning(false);
}
return null;
}
if (!hasNextSong) { if (!hasNextSong) {
return null; return null;
} }
// Ignore invalid durations (e.g. during URL load or empty source placeholder)
if (!Number.isFinite(duration) || duration < 2) {
if (isTransitioning) {
setIsTransitioning(false);
}
return null;
}
if (!isTransitioning) { if (!isTransitioning) {
if (currentTime > duration - 2) { if (currentTime > duration - 2) {
return setIsTransitioning(true); return setIsTransitioning(true);