From 6c7b1bb088d13d881828e0dc9125e5d2e781fc3f Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Tue, 10 Mar 2026 12:30:43 +0100 Subject: [PATCH] Keep failed packages out of package cleanup --- src/main/download-manager.ts | 7 +++ tests/download-manager.test.ts | 89 ++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index ad56374..58e26f3 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -10622,6 +10622,10 @@ export class DownloadManager extends EventEmitter { return; } + if (pkg.status !== "completed") { + return; + } + const allDone = pkg.itemIds.every((itemId) => { const item = this.session.items[itemId]; return !item || item.status === "completed" || item.status === "cancelled" || item.status === "failed"; @@ -10691,6 +10695,9 @@ export class DownloadManager extends EventEmitter { } if (policy === "package_done") { + if (pkg.status !== "completed") { + return; + } const hasOpen = pkg.itemIds.some((id) => { const item = this.session.items[id]; return item != null && item.status !== "completed" && item.status !== "cancelled" && item.status !== "failed"; diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts index 6615b92..6e56ed7 100644 --- a/tests/download-manager.test.ts +++ b/tests/download-manager.test.ts @@ -6292,6 +6292,95 @@ describe("download manager", () => { } }, 35000); + it("keeps partially failed packages under package_done cleanup policy", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-")); + tempDirs.push(root); + + const session = emptySession(); + const packageId = "package-done-partial-failure-pkg"; + const completedItemId = "package-done-partial-failure-completed"; + const failedItemId = "package-done-partial-failure-failed"; + const createdAt = Date.now() - 10_000; + + session.packageOrder = [packageId]; + session.packages[packageId] = { + id: packageId, + name: "package-done-partial-failure", + outputDir: path.join(root, "downloads", "package-done-partial-failure"), + extractDir: path.join(root, "extract", "package-done-partial-failure"), + status: "failed", + itemIds: [completedItemId, failedItemId], + cancelled: false, + enabled: true, + createdAt, + updatedAt: createdAt + }; + session.items[completedItemId] = { + id: completedItemId, + packageId, + url: "https://dummy/package-done-partial-failure-completed", + provider: "realdebrid", + status: "completed", + retries: 0, + speedBps: 0, + downloadedBytes: 1024, + totalBytes: 1024, + progressPercent: 100, + fileName: "episode01.rar", + targetPath: path.join(root, "downloads", "package-done-partial-failure", "episode01.rar"), + resumable: true, + attempts: 1, + lastError: "", + fullStatus: "Entpackt - Done (<1s)", + createdAt, + updatedAt: createdAt + }; + session.items[failedItemId] = { + id: failedItemId, + packageId, + url: "https://dummy/package-done-partial-failure-failed", + provider: "realdebrid", + status: "failed", + retries: 1, + speedBps: 0, + downloadedBytes: 0, + totalBytes: 1024, + progressPercent: 0, + fileName: "episode02.rar", + targetPath: path.join(root, "downloads", "package-done-partial-failure", "episode02.rar"), + resumable: true, + attempts: 1, + lastError: "checksum", + fullStatus: "Entpack-Fehler: Checksum error", + createdAt, + updatedAt: createdAt + }; + + const manager = new DownloadManager( + { + ...defaultSettings(), + token: "rd-token", + outputDir: path.join(root, "downloads"), + extractDir: path.join(root, "extract"), + autoExtract: true, + enableIntegrityCheck: false, + cleanupMode: "delete", + completedCleanupPolicy: "package_done" + }, + session, + createStoragePaths(path.join(root, "state")) + ); + + (manager as any).applyPackageDoneCleanup(packageId); + (manager as any).applyCompletedCleanupPolicy(packageId, completedItemId); + + const snapshot = manager.getSnapshot(); + expect(snapshot.session.packages[packageId]).toBeDefined(); + expect(snapshot.session.items[completedItemId]).toBeDefined(); + expect(snapshot.session.items[failedItemId]).toBeDefined(); + expect(snapshot.session.packageOrder).toEqual([packageId]); + }); + it("waits for deferred MKV collection before package_done cleanup removes the package", async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-")); tempDirs.push(root);