From d7d256f716382b21467e3b4fc0ae42fc6a86306b Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Tue, 3 Mar 2026 14:59:03 +0100 Subject: [PATCH] Fix hybrid-extract: check per-archive prefix instead of whole package The previous fix blocked ALL multi-part extractions when any item in the package was pending. Now checks only parts of the SAME archive (by prefix match on fileName/targetPath), so E01 can extract while E06 downloads. Co-Authored-By: Claude Opus 4.6 --- src/main/download-manager.ts | 42 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 62e2523..2fe0188 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -4988,24 +4988,42 @@ export class DownloadManager extends EventEmitter { } } - // Pre-compute: does the package still have any non-terminal items? - const packageHasPendingItems = pkg.itemIds.some((itemId) => { - const item = this.session.items[itemId]; - return item != null && item.status !== "completed" && item.status !== "failed" && item.status !== "cancelled"; - }); - for (const candidate of candidates) { const partsOnDisk = collectArchiveCleanupTargets(candidate, dirFiles); const allPartsCompleted = partsOnDisk.every((part) => completedPaths.has(pathKey(part))); if (allPartsCompleted) { const candidateBase = path.basename(candidate).toLowerCase(); - // For multi-part archives (.part1.rar), require ALL package items to be terminal. - // partsOnDisk only contains parts found ON DISK — pending parts that haven't been - // downloaded yet (no targetPath, no fileName) would slip through the regular checks. - if (/\.part0*1\.rar$/i.test(candidateBase) && packageHasPendingItems) { - logger.info(`Hybrid-Extract: ${path.basename(candidate)} übersprungen – Paket hat noch ausstehende Items`); - continue; + // For multi-part archives (.part1.rar), check if parts of THIS SPECIFIC archive + // are still pending. We match by archive prefix so E01 parts don't block E02. + const multiMatch = candidateBase.match(/^(.*)\.part0*1\.rar$/i); + if (multiMatch) { + const prefix = multiMatch[1].toLowerCase(); + const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const partPattern = new RegExp(`^${escapedPrefix}\\.part\\d+\\.rar$`, "i"); + const hasRelatedPending = pkg.itemIds.some((itemId) => { + const item = this.session.items[itemId]; + if (!item || item.status === "completed" || item.status === "failed" || item.status === "cancelled") { + return false; + } + // Check fileName (set early from link URL) + if (item.fileName && partPattern.test(item.fileName)) { + return true; + } + // Check targetPath basename (set when download starts) + if (item.targetPath && partPattern.test(path.basename(item.targetPath))) { + return true; + } + // Item has no identity at all — might be an unresolved part, be conservative + if (!item.fileName && !item.targetPath) { + return true; + } + return false; + }); + if (hasRelatedPending) { + logger.info(`Hybrid-Extract: ${path.basename(candidate)} übersprungen – zugehörige Parts noch ausstehend`); + continue; + } } const hasUnstartedParts = [...pendingPaths].some((pendingPath) => {