Keep fallback active while adding queued media

This commit is contained in:
2026-11-07 10:59:49 -08:00
parent 946771d406
commit 463bb823d8
2 changed files with 34 additions and 4 deletions
+12 -1
View File
@@ -76,6 +76,7 @@ export class MediaPlayer {
private activeEntryId: number | null = null;
private activeUrl: string | null = null;
private fallbackSuppressedUntilStart = false;
private fallbackPendingUrl: string | null = null;
private browserUrl: string | null = null;
private browserFallback = new ChromiumFallback((url) => {
if (this.browserUrl === url) {
@@ -384,7 +385,10 @@ export class MediaPlayer {
}
public async append(url: string) {
await this.loadFile(url, "append-play");
const mode = this.browserUrl || this.fallbackPendingUrl || this.browserFallback.isRunning
? "append"
: "append-play";
await this.loadFile(url, mode);
}
public async replace(url: string) {
@@ -740,6 +744,7 @@ export class MediaPlayer {
const hadBrowser = !!this.browserUrl || this.browserFallback.isRunning;
this.activeAttemptGeneration++;
this.fallbackSuppressedUntilStart = false;
this.fallbackPendingUrl = null;
this.browserUrl = null;
if (hadBrowser) void this.browserFallback.stop();
const entryId = typeof response.playlist_entry_id === "number"
@@ -805,6 +810,7 @@ export class MediaPlayer {
if (this.fallbackCandidates.has(file) &&
(entryId === this.activeEntryId || (this.activeEntryId === null && !this.fallbackSuppressedUntilStart)) &&
attemptGeneration === this.activeAttemptGeneration) {
this.fallbackPendingUrl = file;
void this.startBrowserFallback(file, attemptGeneration, entryId);
}
}
@@ -851,6 +857,10 @@ export class MediaPlayer {
}
} catch (error) {
console.error(`Failed to start Chromium fallback for ${url}:`, error);
} finally {
if (generation === this.activeAttemptGeneration && this.fallbackPendingUrl === url) {
this.fallbackPendingUrl = null;
}
}
}
@@ -860,6 +870,7 @@ export class MediaPlayer {
this.activeEntryId = null;
this.activeUrl = null;
this.currentFile = null;
this.fallbackPendingUrl = null;
this.browserUrl = null;
await this.browserFallback.stop();
}
+22 -3
View File
@@ -5,6 +5,7 @@ const { MediaPlayer } = require("../build/MediaPlayer");
const makePlayer = (url = "https://example.test/watch?v=abc", playlist = [{ id: 42, filename: url }]) => {
const player = Object.create(MediaPlayer.prototype);
const starts = [];
const commands = [];
let stops = 0;
const state = {
eventSubscribers: [],
@@ -38,13 +39,14 @@ const makePlayer = (url = "https://example.test/watch?v=abc", playlist = [{ id:
player.browserFallback.isRunning = false;
},
};
player.writeCommand = async (command) => {
player.writeCommand = async (command, args) => {
commands.push([command, args]);
if (command === "loadfile") return { error: "success" };
if (command === "get_property") return { data: playlist };
return { error: "success" };
};
player.fetchMetadataAndNotify = async () => undefined;
return { player, starts, get stops() { return stops; }, set playlist(value) { playlist = value; } };
return { player, starts, commands, get stops() { return stops; }, set playlist(value) { playlist = value; } };
};
const begin = (player, entryId = 42) => player.receiveData(`${JSON.stringify({ event: "start-file", playlist_entry_id: entryId })}\n`);
@@ -84,12 +86,13 @@ test("actual MPV error opens the exact original URL and mirrors indeterminate st
test("queue append leaves active A eligible when queued B is added", async () => {
const a = "https://example.test/a";
const b = "https://example.test/b";
const { player, starts } = makePlayer(a, [{ id: 42, filename: a }]);
const { player, starts, commands } = makePlayer(a, [{ id: 42, filename: a }]);
player.fallbackCandidates.add(a);
player.entryCandidates.set(42, a);
begin(player, 42);
await player.append(b);
await settle();
assert.equal(commands.filter(([name]) => name === "loadfile").at(-1)[1][1], "append-play");
send(player, { reason: "error", file_error: "failed A" }, 42);
await settle();
assert.deepEqual(starts, [a]);
@@ -97,6 +100,22 @@ test("queue append leaves active A eligible when queued B is added", async () =>
assert.equal(player.fallbackCandidates.has(b), true);
});
test("queue append during fallback startup keeps browser A open without starting B", async () => {
const a = "https://example.test/a";
const b = "https://example.test/b";
const { player, starts, commands } = makePlayer(a, [{ id: 42, filename: a }]);
player.fallbackCandidates.add(a);
player.entryCandidates.set(42, a);
begin(player, 42);
send(player, { reason: "error", file_error: "failed A" }, 42);
await player.append(b);
await settle();
const load = commands.filter(([name]) => name === "loadfile").at(-1);
assert.equal(load[1][1], "append");
assert.deepEqual(starts, [a]);
assert.equal(player.browserUrl, a);
});
test("append then skipTo keeps the selected entry eligible for its actual MPV error", async () => {
const url = "https://example.test/shared-original?x=1&y=2";
const { player, starts } = makePlayer(url, [{ id: 42, filename: url }]);