Keep fallback active while adding queued media
This commit is contained in:
@@ -76,6 +76,7 @@ export class MediaPlayer {
|
|||||||
private activeEntryId: number | null = null;
|
private activeEntryId: number | null = null;
|
||||||
private activeUrl: string | null = null;
|
private activeUrl: string | null = null;
|
||||||
private fallbackSuppressedUntilStart = false;
|
private fallbackSuppressedUntilStart = false;
|
||||||
|
private fallbackPendingUrl: string | null = null;
|
||||||
private browserUrl: string | null = null;
|
private browserUrl: string | null = null;
|
||||||
private browserFallback = new ChromiumFallback((url) => {
|
private browserFallback = new ChromiumFallback((url) => {
|
||||||
if (this.browserUrl === url) {
|
if (this.browserUrl === url) {
|
||||||
@@ -384,7 +385,10 @@ export class MediaPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async append(url: string) {
|
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) {
|
public async replace(url: string) {
|
||||||
@@ -740,6 +744,7 @@ export class MediaPlayer {
|
|||||||
const hadBrowser = !!this.browserUrl || this.browserFallback.isRunning;
|
const hadBrowser = !!this.browserUrl || this.browserFallback.isRunning;
|
||||||
this.activeAttemptGeneration++;
|
this.activeAttemptGeneration++;
|
||||||
this.fallbackSuppressedUntilStart = false;
|
this.fallbackSuppressedUntilStart = false;
|
||||||
|
this.fallbackPendingUrl = null;
|
||||||
this.browserUrl = null;
|
this.browserUrl = null;
|
||||||
if (hadBrowser) void this.browserFallback.stop();
|
if (hadBrowser) void this.browserFallback.stop();
|
||||||
const entryId = typeof response.playlist_entry_id === "number"
|
const entryId = typeof response.playlist_entry_id === "number"
|
||||||
@@ -805,6 +810,7 @@ export class MediaPlayer {
|
|||||||
if (this.fallbackCandidates.has(file) &&
|
if (this.fallbackCandidates.has(file) &&
|
||||||
(entryId === this.activeEntryId || (this.activeEntryId === null && !this.fallbackSuppressedUntilStart)) &&
|
(entryId === this.activeEntryId || (this.activeEntryId === null && !this.fallbackSuppressedUntilStart)) &&
|
||||||
attemptGeneration === this.activeAttemptGeneration) {
|
attemptGeneration === this.activeAttemptGeneration) {
|
||||||
|
this.fallbackPendingUrl = file;
|
||||||
void this.startBrowserFallback(file, attemptGeneration, entryId);
|
void this.startBrowserFallback(file, attemptGeneration, entryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -851,6 +857,10 @@ export class MediaPlayer {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to start Chromium fallback for ${url}:`, 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.activeEntryId = null;
|
||||||
this.activeUrl = null;
|
this.activeUrl = null;
|
||||||
this.currentFile = null;
|
this.currentFile = null;
|
||||||
|
this.fallbackPendingUrl = null;
|
||||||
this.browserUrl = null;
|
this.browserUrl = null;
|
||||||
await this.browserFallback.stop();
|
await this.browserFallback.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const { MediaPlayer } = require("../build/MediaPlayer");
|
|||||||
const makePlayer = (url = "https://example.test/watch?v=abc", playlist = [{ id: 42, filename: url }]) => {
|
const makePlayer = (url = "https://example.test/watch?v=abc", playlist = [{ id: 42, filename: url }]) => {
|
||||||
const player = Object.create(MediaPlayer.prototype);
|
const player = Object.create(MediaPlayer.prototype);
|
||||||
const starts = [];
|
const starts = [];
|
||||||
|
const commands = [];
|
||||||
let stops = 0;
|
let stops = 0;
|
||||||
const state = {
|
const state = {
|
||||||
eventSubscribers: [],
|
eventSubscribers: [],
|
||||||
@@ -38,13 +39,14 @@ const makePlayer = (url = "https://example.test/watch?v=abc", playlist = [{ id:
|
|||||||
player.browserFallback.isRunning = false;
|
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 === "loadfile") return { error: "success" };
|
||||||
if (command === "get_property") return { data: playlist };
|
if (command === "get_property") return { data: playlist };
|
||||||
return { error: "success" };
|
return { error: "success" };
|
||||||
};
|
};
|
||||||
player.fetchMetadataAndNotify = async () => undefined;
|
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`);
|
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 () => {
|
test("queue append leaves active A eligible when queued B is added", async () => {
|
||||||
const a = "https://example.test/a";
|
const a = "https://example.test/a";
|
||||||
const b = "https://example.test/b";
|
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.fallbackCandidates.add(a);
|
||||||
player.entryCandidates.set(42, a);
|
player.entryCandidates.set(42, a);
|
||||||
begin(player, 42);
|
begin(player, 42);
|
||||||
await player.append(b);
|
await player.append(b);
|
||||||
await settle();
|
await settle();
|
||||||
|
assert.equal(commands.filter(([name]) => name === "loadfile").at(-1)[1][1], "append-play");
|
||||||
send(player, { reason: "error", file_error: "failed A" }, 42);
|
send(player, { reason: "error", file_error: "failed A" }, 42);
|
||||||
await settle();
|
await settle();
|
||||||
assert.deepEqual(starts, [a]);
|
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);
|
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 () => {
|
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 url = "https://example.test/shared-original?x=1&y=2";
|
||||||
const { player, starts } = makePlayer(url, [{ id: 42, filename: url }]);
|
const { player, starts } = makePlayer(url, [{ id: 42, filename: url }]);
|
||||||
|
|||||||
Reference in New Issue
Block a user