From 0ca359e509e8cb1c0fea270c0dde9d4f7ff6e852 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Wed, 4 Mar 2026 21:23:34 +0100 Subject: [PATCH] Round 6 bug fixes (pre-release) Co-Authored-By: Claude Opus 4.6 --- src/main/debrid.ts | 4 ++-- src/main/download-manager.ts | 21 ++++++++++++++++----- src/main/extractor.ts | 3 ++- src/main/realdebrid.ts | 2 +- src/renderer/App.tsx | 27 ++++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/main/debrid.ts b/src/main/debrid.ts index d011485..04e40da 100644 --- a/src/main/debrid.ts +++ b/src/main/debrid.ts @@ -650,7 +650,7 @@ class MegaDebridClient { await sleepWithSignal(retryDelay(attempt), signal); } } - throw new Error(lastError || "Mega-Web Unrestrict fehlgeschlagen"); + throw new Error(String(lastError || "Mega-Web Unrestrict fehlgeschlagen").replace(/^Error:\s*/i, "")); } } @@ -954,7 +954,7 @@ class AllDebridClient { } } - throw new Error(lastError || "AllDebrid Unrestrict fehlgeschlagen"); + throw new Error(String(lastError || "AllDebrid Unrestrict fehlgeschlagen").replace(/^Error:\s*/i, "")); } } diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index f6dae52..7b1b1e0 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -1102,16 +1102,21 @@ export class DownloadManager extends EventEmitter { active.abortController.abort("cancel"); } const pkg = this.session.packages[item.packageId]; + let removedByPackageCleanup = false; if (pkg) { pkg.itemIds = pkg.itemIds.filter((id) => id !== itemId); if (pkg.itemIds.length === 0) { this.removePackageFromSession(item.packageId, [itemId]); + removedByPackageCleanup = true; } else { pkg.updatedAt = nowMs(); } } - delete this.session.items[itemId]; - this.itemCount = Math.max(0, this.itemCount - 1); + // removePackageFromSession already deletes the item and decrements itemCount + if (!removedByPackageCleanup) { + delete this.session.items[itemId]; + this.itemCount = Math.max(0, this.itemCount - 1); + } this.retryAfterByItem.delete(itemId); this.retryStateByItem.delete(itemId); this.dropItemContribution(itemId); @@ -1268,6 +1273,7 @@ export class DownloadManager extends EventEmitter { this.packagePostProcessTasks.clear(); this.packagePostProcessAbortControllers.clear(); this.hybridExtractRequeue.clear(); + this.providerFailures.clear(); this.packagePostProcessQueue = Promise.resolve(); this.packagePostProcessActive = 0; for (const waiter of this.packagePostProcessWaiters) { waiter.resolve(); } @@ -1885,7 +1891,7 @@ export class DownloadManager extends EventEmitter { } for (const entry of entries) { - if (entry.isFile()) { + if (entry.isFile() && !isIgnorableEmptyDirFileName(entry.name)) { return true; } if (entry.isDirectory()) { @@ -2739,6 +2745,7 @@ export class DownloadManager extends EventEmitter { item.updatedAt = nowMs(); this.retryAfterByItem.delete(itemId); this.retryStateByItem.delete(itemId); + this.releaseTargetPath(itemId); this.recordRunOutcome(itemId, "cancelled"); affectedPackageIds.add(item.packageId); } @@ -2746,15 +2753,16 @@ export class DownloadManager extends EventEmitter { const pkg = this.session.packages[pkgId]; if (pkg) this.refreshPackageStatus(pkg); } - // Trigger extraction if all items are now in a terminal state and some completed + // Trigger extraction if all items are now in a terminal state and some completed (no failures) if (this.settings.autoExtract) { for (const pkgId of affectedPackageIds) { const pkg = this.session.packages[pkgId]; if (!pkg || pkg.cancelled || this.packagePostProcessTasks.has(pkgId)) continue; const pkgItems = pkg.itemIds.map((id) => this.session.items[id]).filter(Boolean) as DownloadItem[]; const hasPending = pkgItems.some((i) => i.status !== "completed" && i.status !== "failed" && i.status !== "cancelled"); + const hasFailed = pkgItems.some((i) => i.status === "failed"); const hasUnextracted = pkgItems.some((i) => i.status === "completed" && !isExtractedLabel(i.fullStatus || "")); - if (!hasPending && hasUnextracted) { + if (!hasPending && !hasFailed && hasUnextracted) { for (const it of pkgItems) { if (it.status === "completed" && !isExtractedLabel(it.fullStatus || "")) { it.fullStatus = "Entpacken - Ausstehend"; @@ -2935,6 +2943,9 @@ export class DownloadManager extends EventEmitter { this.runCompletedPackages.clear(); this.retryAfterByItem.clear(); this.retryStateByItem.clear(); + this.itemContributedBytes.clear(); + this.reservedTargetPaths.clear(); + this.claimedTargetPathByItem.clear(); this.session.running = true; this.session.paused = false; this.session.runStartedAt = nowMs(); diff --git a/src/main/extractor.ts b/src/main/extractor.ts index d0888df..ffbc292 100644 --- a/src/main/extractor.ts +++ b/src/main/extractor.ts @@ -1597,7 +1597,8 @@ async function extractZipArchive(archivePath: string, targetDir: string, conflic const limitMb = Math.ceil(memoryLimitBytes / (1024 * 1024)); throw new Error(`ZIP-Eintrag zu groß für internen Entpacker (${entryMb} MB > ${limitMb} MB)`); } - if (data.length > Math.max(uncompressedSize, compressedSize) * 20) { + const maxDeclaredSize = Math.max(uncompressedSize, compressedSize); + if (maxDeclaredSize > 0 && data.length > maxDeclaredSize * 20) { throw new Error(`ZIP-Eintrag verdächtig groß nach Entpacken (${entry.entryName})`); } await fs.promises.writeFile(outputPath, data); diff --git a/src/main/realdebrid.ts b/src/main/realdebrid.ts index 6a4d9cc..4fcd099 100644 --- a/src/main/realdebrid.ts +++ b/src/main/realdebrid.ts @@ -196,6 +196,6 @@ export class RealDebridClient { } } - throw new Error(lastError || "Unrestrict fehlgeschlagen"); + throw new Error(String(lastError || "Unrestrict fehlgeschlagen").replace(/^Error:\s*/i, "")); } } diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 4b86c37..4aa2a7e 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1369,10 +1369,18 @@ export function App(): ReactElement { pendingPackageOrderRef.current = [...order]; pendingPackageOrderAtRef.current = Date.now(); packageOrderRef.current = [...order]; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: [...order] } }; + }); void window.rd.reorderPackages(order).catch((error) => { pendingPackageOrderRef.current = null; pendingPackageOrderAtRef.current = 0; packageOrderRef.current = serverPackageOrderRef.current; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: serverPackageOrderRef.current } }; + }); showToast(`Sortierung fehlgeschlagen: ${String(error)}`, 2400); }); }, [showToast]); @@ -1389,10 +1397,18 @@ export function App(): ReactElement { pendingPackageOrderRef.current = [...nextOrder]; pendingPackageOrderAtRef.current = Date.now(); packageOrderRef.current = [...nextOrder]; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: [...nextOrder] } }; + }); void window.rd.reorderPackages(nextOrder).catch((error) => { pendingPackageOrderRef.current = null; pendingPackageOrderAtRef.current = 0; packageOrderRef.current = serverPackageOrderRef.current; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: serverPackageOrderRef.current } }; + }); showToast(`Sortierung fehlgeschlagen: ${String(error)}`, 2400); }); }, [showToast]); @@ -2375,10 +2391,18 @@ export function App(): ReactElement { pendingPackageOrderRef.current = [...sorted]; pendingPackageOrderAtRef.current = Date.now(); packageOrderRef.current = sorted; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: [...sorted] } }; + }); void window.rd.reorderPackages(sorted).catch((error) => { pendingPackageOrderRef.current = null; pendingPackageOrderAtRef.current = 0; packageOrderRef.current = serverPackageOrderRef.current; + setSnapshot((prev) => { + if (!prev) return prev; + return { ...prev, session: { ...prev.session, packageOrder: serverPackageOrderRef.current } }; + }); showToast(`Sortierung fehlgeschlagen: ${String(error)}`, 2400); }); } : undefined} @@ -2863,7 +2887,7 @@ export function App(): ReactElement {