diff --git a/web/backend/src/MediaPlayer.ts b/web/backend/src/MediaPlayer.ts index 96386a3..ea068a9 100644 --- a/web/backend/src/MediaPlayer.ts +++ b/web/backend/src/MediaPlayer.ts @@ -539,7 +539,10 @@ export class MediaPlayer { if (result?.error !== "success") { if (fallbackEligible) { this.removePendingCandidate(url); - if (![...this.entryCandidates.values()].includes(url)) this.fallbackCandidates.delete(url); + if (!this.pendingCandidateUrls.includes(url) && + ![...this.entryCandidates.values()].includes(url)) { + this.fallbackCandidates.delete(url); + } } return; } @@ -784,6 +787,13 @@ export class MediaPlayer { const file = this.entryCandidates.get(entryId) || response.file || (entryId === this.activeEntryId ? this.activeUrl : null); if (!file) return; + if (this.activeEntryId === null && !this.fallbackSuppressedUntilStart) { + // MPV can clear its current entry before delivering end-file. Restore + // this event's known entry from its playlist_entry_id for mirroring. + this.activeEntryId = entryId; + this.activeUrl = file; + this.currentFile = file; + } const errorMessage = response.file_error || response.error || response["file-error"] || "Unknown playback error"; this.playbackErrors.set(file, errorMessage); this.handleEvent(UserEvent.PlaybackError, { filename: file, error: errorMessage }); diff --git a/web/backend/test/MediaPlayerFallback.test.cjs b/web/backend/test/MediaPlayerFallback.test.cjs index 921b1c5..2a2f368 100644 --- a/web/backend/test/MediaPlayerFallback.test.cjs +++ b/web/backend/test/MediaPlayerFallback.test.cjs @@ -119,6 +119,9 @@ test("failure is correlated from MPV end-file entry id without querying a cleare assert.notEqual(command, "get_property", "failure correlation must use the emitted entry id"); return { data: [] }; }; + player.activeEntryId = null; + player.activeUrl = null; + player.currentFile = null; send(player, { reason: "error", file_error: "network unavailable" }, 42); await settle(); assert.deepEqual(starts, [url]);