Fix: Post-Process-Identity-Guard (J) + Remux-Temp nie ins Library sammeln (Q)
J: runPackagePostProcessing loescht im finally die Map-Eintraege fuer das Paket. Hatte ein Abort den Handle schon entfernt und ein neuer Lauf einen frischen Task+Controller gesetzt, riss das spaete finall des alten Tasks diesen neuen Eintrag mit raus -> nicht abbrechbarer Waisen-Task + doppeltes paralleles Post-Processing. Jetzt nur loeschen wenn Map noch auf DIESEN Task/Controller zeigt. Q: collectFilesByExtensions filtert jetzt ~rd-Praefix (unsere Remux-Temp/Orphan- Sidecars) aus, damit eine bei einem Crash mitten im Remux liegengebliebene Teil-Datei nie in die MKV-Library gesammelt wird. (dropItemContribution: Kommentar ergaenzt, dass das Nicht-Abziehen der Session-Totals Absicht ist — kumulative Session-Zaehler, per Test abgesichert.)
This commit is contained in:
parent
4432fa25e8
commit
3c33b988c3
@ -3546,6 +3546,11 @@ export class DownloadManager extends EventEmitter {
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
// Never collect our own remux temp/orphan sidecars (~rd<token>.<ext>): a
|
||||
// partial file left by a crash mid-remux must not be swept into the library.
|
||||
if (entry.name.startsWith("~rd")) {
|
||||
continue;
|
||||
}
|
||||
const extension = path.extname(entry.name).toLowerCase();
|
||||
if (!normalizedExtensions.has(extension)) {
|
||||
continue;
|
||||
@ -6107,6 +6112,11 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
|
||||
private dropItemContribution(itemId: string): void {
|
||||
// NOTE: deliberately does NOT subtract from session.totalDownloadedBytes /
|
||||
// sessionDownloadedBytes. Those are cumulative-session counters and must stay
|
||||
// put when a completed item is removed from the queue (see the test "keeps
|
||||
// cumulative session totals when completed items are removed from the queue").
|
||||
// The retry path subtracts on its own because those bytes get re-downloaded.
|
||||
this.itemContributedBytes.delete(itemId);
|
||||
this.invalidateStatsCache();
|
||||
}
|
||||
@ -7151,6 +7161,9 @@ export class DownloadManager extends EventEmitter {
|
||||
const abortController = new AbortController();
|
||||
this.packagePostProcessAbortControllers.set(packageId, abortController);
|
||||
|
||||
// Holder so the task's own finally can identity-check itself (the task Promise
|
||||
// cannot reference its own const inside its initializer). Assigned right after.
|
||||
const handle: { task?: Promise<void> } = {};
|
||||
const task = (async () => {
|
||||
const slotWaitStart = nowMs();
|
||||
await this.acquirePostProcessSlot(packageId);
|
||||
@ -7197,8 +7210,16 @@ export class DownloadManager extends EventEmitter {
|
||||
} while (this.hybridExtractRequeue.has(packageId));
|
||||
} finally {
|
||||
this.releasePostProcessSlot();
|
||||
this.packagePostProcessTasks.delete(packageId);
|
||||
this.packagePostProcessAbortControllers.delete(packageId);
|
||||
// Identity guard: only clear the map entries if they still point to THIS
|
||||
// task/controller. After an abort deletes our handle a new run can install
|
||||
// a fresh task+controller for the same packageId; a blind delete here would
|
||||
// orphan that newer task (uncancellable) and allow a duplicate concurrent run.
|
||||
if (this.packagePostProcessTasks.get(packageId) === handle.task) {
|
||||
this.packagePostProcessTasks.delete(packageId);
|
||||
}
|
||||
if (this.packagePostProcessAbortControllers.get(packageId) === abortController) {
|
||||
this.packagePostProcessAbortControllers.delete(packageId);
|
||||
}
|
||||
this.persistSoon();
|
||||
this.emitState();
|
||||
if (this.hybridExtractRequeue.delete(packageId)) {
|
||||
@ -7209,6 +7230,7 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
})();
|
||||
|
||||
handle.task = task;
|
||||
this.packagePostProcessTasks.set(packageId, task);
|
||||
return task;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user