fix(anime): clean up discarded queued subtitles in background

- Let queued episode discard return before subtitle preparation finishes
- Remove the redundant queued subtitle loading command
This commit is contained in:
2026-08-15 00:16:00 -07:00
parent 935e4c145f
commit 333ee5eea4
4 changed files with 21 additions and 9 deletions
-1
View File
@@ -65,7 +65,6 @@ test('queued playback carries file-local title and language preferences into mpv
), ),
); );
assert.deepEqual(buildQueuedPlaybackCommands(options), [ assert.deepEqual(buildQueuedPlaybackCommands(options), [
['script-message', 'subminer-managed-subtitles-loading'],
[ [
'loadfile', 'loadfile',
'https://video.example/episode.m3u8', 'https://video.example/episode.m3u8',
+1 -4
View File
@@ -126,10 +126,7 @@ export function buildPlaybackCommands(options: BuildPlaybackOptions): MpvCommand
/** Append a fully resolved stream without replacing the file playing now. */ /** Append a fully resolved stream without replacing the file playing now. */
export function buildQueuedPlaybackCommands(options: BuildPlaybackOptions): MpvCommand[] { export function buildQueuedPlaybackCommands(options: BuildPlaybackOptions): MpvCommand[] {
return [ return [['loadfile', options.stream.url, 'append-play', -1, buildQueuedLoadfileOptions(options)]];
['script-message', 'subminer-managed-subtitles-loading'],
['loadfile', options.stream.url, 'append-play', -1, buildQueuedLoadfileOptions(options)],
];
} }
/** /**
@@ -141,6 +141,7 @@ test('queued video can append while subtitle caching continues in the background
], ],
} as unknown as AnimeBridgeClient; } as unknown as AnimeBridgeClient;
const commands: Array<Array<string | number>> = []; const commands: Array<Array<string | number>> = [];
let removedDirs = 0;
const playback = createAnimeBrowserPlayback({ const playback = createAnimeBrowserPlayback({
deps: { deps: {
sendMpvCommand: (command) => void commands.push(command), sendMpvCommand: (command) => void commands.push(command),
@@ -149,7 +150,9 @@ test('queued video can append while subtitle caching continues in the background
fetch: async () => await fetchPending, fetch: async () => await fetchPending,
makeTempDir: async () => '/tmp/subminer-queued-test', makeTempDir: async () => '/tmp/subminer-queued-test',
writeFile: async () => undefined, writeFile: async () => undefined,
removeDir: async () => undefined, removeDir: async () => {
removedDirs += 1;
},
}, },
log: () => undefined, log: () => undefined,
}, },
@@ -164,12 +167,20 @@ test('queued video can append while subtitle caching continues in the background
playback.appendEpisode(result.playback); playback.appendEpisode(result.playback);
assert.equal(commands.at(-1)?.[2], 'append-play'); assert.equal(commands.at(-1)?.[2], 'append-play');
const discardResult = await Promise.race([
playback.discardEpisode(result.playback).then(() => 'discarded'),
new Promise<'pending'>((resolve) => setImmediate(() => resolve('pending'))),
]);
assert.equal(discardResult, 'discarded');
finishFetch({ finishFetch({
ok: true, ok: true,
status: 200, status: 200,
arrayBuffer: async () => arrayBuffer: async () =>
new TextEncoder().encode('1\n00:00:00,000 --> 00:00:01,000\n字幕').buffer, new TextEncoder().encode('1\n00:00:00,000 --> 00:00:01,000\n字幕').buffer,
}); });
await playback.discardEpisode(result.playback); await result.playback.trackPreparation;
await new Promise(setImmediate);
assert.equal(removedDirs, 1);
await playback.dispose(); await playback.dispose();
}); });
+7 -2
View File
@@ -239,8 +239,13 @@ export function createAnimeBrowserPlayback(options: AnimeBrowserPlaybackOptions)
deps.showMpvOsd?.(playback.metadata.displayTitle); deps.showMpvOsd?.(playback.metadata.displayTitle);
} }
async function discardEpisode(playback: PreparedAnimeBrowserPlayback): Promise<void> { function discardEpisode(playback: PreparedAnimeBrowserPlayback): Promise<void> {
await releasePreparedTracks(await playback.trackPreparation); void playback.trackPreparation
.then((preparedTracks) => releasePreparedTracks(preparedTracks))
.catch((error) => {
deps.log(`[anime-browser] queued track cleanup failed: ${String(error)}`);
});
return Promise.resolve();
} }
async function releasePreparedTracks(preparedTracks: PreparedTrackSetup): Promise<void> { async function releasePreparedTracks(preparedTracks: PreparedTrackSetup): Promise<void> {