From 9b460758f9055ceaab8359a837efd5f2542708f4 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Tue, 3 Mar 2026 18:15:57 +0100 Subject: [PATCH] Add parallel extraction (2 concurrent) and better status labels - Replace serial packagePostProcessQueue with semaphore (max 2 concurrent) - Hybrid-extract: items waiting for parts show "Entpacken - Warten auf Parts" - Failed hybrid extraction shows "Entpacken - Error" instead of "Fertig" Co-Authored-By: Claude Opus 4.6 --- src/main/download-manager.ts | 62 +++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 8359735..a3ce47b 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -773,6 +773,10 @@ export class DownloadManager extends EventEmitter { private packagePostProcessQueue: Promise = Promise.resolve(); + private packagePostProcessActive = 0; + + private packagePostProcessWaiters: Array<() => void> = []; + private packagePostProcessTasks = new Map>(); private packagePostProcessAbortControllers = new Map(); @@ -1170,6 +1174,9 @@ export class DownloadManager extends EventEmitter { this.packagePostProcessAbortControllers.clear(); this.hybridExtractRequeue.clear(); this.packagePostProcessQueue = Promise.resolve(); + this.packagePostProcessActive = 0; + for (const waiter of this.packagePostProcessWaiters) { waiter(); } + this.packagePostProcessWaiters = []; this.summary = null; this.nonResumableActive = 0; this.retryAfterByItem.clear(); @@ -2989,6 +2996,26 @@ export class DownloadManager extends EventEmitter { } } + private async acquirePostProcessSlot(): Promise { + const maxConcurrent = 2; + if (this.packagePostProcessActive < maxConcurrent) { + this.packagePostProcessActive += 1; + return; + } + await new Promise((resolve) => { + this.packagePostProcessWaiters.push(resolve); + }); + this.packagePostProcessActive += 1; + } + + private releasePostProcessSlot(): void { + this.packagePostProcessActive -= 1; + const next = this.packagePostProcessWaiters.shift(); + if (next) { + next(); + } + } + private runPackagePostProcessing(packageId: string): Promise { const existing = this.packagePostProcessTasks.get(packageId); if (existing) { @@ -2999,15 +3026,14 @@ export class DownloadManager extends EventEmitter { const abortController = new AbortController(); this.packagePostProcessAbortControllers.set(packageId, abortController); - const task = this.packagePostProcessQueue - .catch(() => undefined) - .then(async () => { + const task = (async () => { + await this.acquirePostProcessSlot(); + try { await this.handlePackagePostProcessing(packageId, abortController.signal); - }) - .catch((error) => { + } catch (error) { logger.warn(`Post-Processing für Paket fehlgeschlagen: ${compactErrorText(error)}`); - }) - .finally(() => { + } finally { + this.releasePostProcessSlot(); this.packagePostProcessTasks.delete(packageId); this.packagePostProcessAbortControllers.delete(packageId); this.persistSoon(); @@ -3017,10 +3043,10 @@ export class DownloadManager extends EventEmitter { logger.warn(`runPackagePostProcessing Fehler (hybridRequeue): ${compactErrorText(err)}`) ); } - }); + } + })(); this.packagePostProcessTasks.set(packageId, task); - this.packagePostProcessQueue = task; return task; } @@ -5229,12 +5255,18 @@ export class DownloadManager extends EventEmitter { this.emitState(); }; - // Mark items not yet being extracted as pending - for (const entry of hybridItems) { - if (!isExtractedLabel(entry.fullStatus)) { - entry.fullStatus = "Entpacken - Ausstehend"; - entry.updatedAt = nowMs(); + // Mark hybrid items as pending, others as waiting for parts + const hybridItemIds = new Set(hybridItems.map((item) => item.id)); + for (const entry of completedItems) { + if (isExtractedLabel(entry.fullStatus)) { + continue; } + if (hybridItemIds.has(entry.id)) { + entry.fullStatus = "Entpacken - Ausstehend"; + } else { + entry.fullStatus = "Entpacken - Warten auf Parts"; + } + entry.updatedAt = nowMs(); } this.emitState(); @@ -5302,7 +5334,7 @@ export class DownloadManager extends EventEmitter { if (result.extracted > 0 && result.failed === 0) { entry.fullStatus = "Entpackt - Done"; } else { - entry.fullStatus = `Fertig (${humanSize(entry.downloadedBytes)})`; + entry.fullStatus = "Entpacken - Error"; } entry.updatedAt = updatedAt; }