From 40f097249c1ad6e3f63e3e6be17bc9ac3843c127 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 9 Mar 2026 05:38:56 +0100 Subject: [PATCH] Fix manual extract relabeling --- src/main/download-manager.ts | 18 +++++--- src/renderer/App.tsx | 3 +- tests/download-manager.test.ts | 78 ++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 34278c9..586756f 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -5702,10 +5702,11 @@ export class DownloadManager extends EventEmitter { this.clearHybridArchiveState(packageId); const items = pkg.itemIds.map((id) => this.session.items[id]).filter(Boolean) as DownloadItem[]; const completedItems = items.filter((item) => item.status === "completed"); - if (completedItems.length === 0) return; + const targetItems = completedItems.filter((item) => !isExtractedLabel(item.fullStatus)); + if (targetItems.length === 0) return; pkg.status = "queued"; pkg.updatedAt = nowMs(); - for (const item of completedItems) { + for (const item of targetItems) { if (!isExtractedLabel(item.fullStatus)) { item.fullStatus = "Entpacken - Ausstehend"; item.updatedAt = nowMs(); @@ -5713,7 +5714,8 @@ export class DownloadManager extends EventEmitter { } logger.info(`Extraktion manuell wiederholt: pkg=${pkg.name}`); this.logPackageForPackage(pkg, "INFO", "Extraktion manuell wiederholt", { - completedItems: completedItems.length + completedItems: completedItems.length, + targetedItems: targetItems.length }); this.persistSoon(); this.emitState(true); @@ -5730,16 +5732,18 @@ export class DownloadManager extends EventEmitter { } const items = pkg.itemIds.map((id) => this.session.items[id]).filter(Boolean) as DownloadItem[]; const completedItems = items.filter((item) => item.status === "completed"); - if (completedItems.length === 0) return; + const targetItems = completedItems.filter((item) => !isExtractedLabel(item.fullStatus)); + if (targetItems.length === 0) return; pkg.status = "queued"; pkg.updatedAt = nowMs(); - for (const item of completedItems) { + for (const item of targetItems) { item.fullStatus = "Entpacken - Ausstehend"; item.updatedAt = nowMs(); } - logger.info(`Jetzt entpacken: pkg=${pkg.name}, completed=${completedItems.length}`); + logger.info(`Jetzt entpacken: pkg=${pkg.name}, completed=${completedItems.length}, targeted=${targetItems.length}`); this.logPackageForPackage(pkg, "INFO", "Jetzt entpacken ausgelöst", { - completedItems: completedItems.length + completedItems: completedItems.length, + targetedItems: targetItems.length }); this.persistSoon(); this.emitState(true); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index e0785c2..a958c1f 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -5581,7 +5581,7 @@ export function App(): ReactElement { {hasPackages && !multi && (() => { const pkg = snapshot.session.packages[contextMenu.packageId]; const items = pkg?.itemIds.map((id) => snapshot.session.items[id]).filter(Boolean) || []; - const someCompleted = items.some((item) => item && item.status === "completed"); + const someCompleted = items.some((item) => item && item.status === "completed" && !/^Entpackt\b/i.test(item.fullStatus || "")); return (<> {someCompleted && ( @@ -6121,4 +6121,3 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, stripe return true; }); - diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts index 0c34588..7251f60 100644 --- a/tests/download-manager.test.ts +++ b/tests/download-manager.test.ts @@ -227,6 +227,84 @@ describe("download manager", () => { expect((manager as any).shouldCollapseQuickPostProcessRequeue(packageId)).toBe(false); }); + it("extractNow only re-arms completed items that are not already extracted", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-extract-now-")); + tempDirs.push(root); + + const session = emptySession(); + const packageId = "extract-now-pkg"; + const createdAt = Date.now() - 20_000; + const outputDir = path.join(root, "downloads", "Extract Now Test"); + const extractDir = path.join(root, "extract", "Extract Now Test"); + fs.mkdirSync(outputDir, { recursive: true }); + fs.mkdirSync(extractDir, { recursive: true }); + + const specs = [ + { id: "extract-now-item-1", fileName: "show.e01.rar", fullStatus: "Entpackt - Done (<1s)" }, + { id: "extract-now-item-2", fileName: "show.e02.rar", fullStatus: "Entpackt - Done (1.2s)" }, + { id: "extract-now-item-3", fileName: "show.e03.rar", fullStatus: "Entpacken - Ausstehend" } + ] as const; + + session.packageOrder = [packageId]; + session.packages[packageId] = { + id: packageId, + name: "Extract Now Test", + outputDir, + extractDir, + status: "completed", + itemIds: specs.map((spec) => spec.id), + cancelled: false, + enabled: true, + createdAt, + updatedAt: createdAt + }; + + for (const spec of specs) { + const targetPath = path.join(outputDir, spec.fileName); + fs.writeFileSync(targetPath, Buffer.alloc(128, 1)); + session.items[spec.id] = { + id: spec.id, + packageId, + url: `https://example.com/${spec.fileName}`, + provider: "realdebrid", + status: "completed", + retries: 0, + speedBps: 0, + downloadedBytes: 128, + totalBytes: 128, + progressPercent: 100, + fileName: spec.fileName, + targetPath, + resumable: true, + attempts: 1, + lastError: "", + fullStatus: spec.fullStatus, + createdAt, + updatedAt: createdAt + }; + } + + const manager = new DownloadManager( + { + ...defaultSettings(), + token: "rd-token", + outputDir: path.join(root, "downloads"), + extractDir: path.join(root, "extract"), + autoExtract: true, + hybridExtract: true + }, + session, + createStoragePaths(path.join(root, "state")) + ); + + manager.extractNow(packageId); + + expect((manager as any).session.items["extract-now-item-1"].fullStatus).toBe("Entpackt - Done (<1s)"); + expect((manager as any).session.items["extract-now-item-2"].fullStatus).toBe("Entpackt - Done (1.2s)"); + expect((manager as any).session.items["extract-now-item-3"].fullStatus).toBe("Entpacken - Ausstehend"); + expect((manager as any).session.packages[packageId].status).toBe("queued"); + }); + function createCompletedArchiveSession(root: string, packageName: string, extractedFileName: string): { session: ReturnType; packageId: string;