From 9a00304a9330d93d8730894f87ece2037f0965f8 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Wed, 4 Mar 2026 20:34:50 +0100 Subject: [PATCH] Release v1.6.27 Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- src/main/download-manager.ts | 28 ++++++++++++++++++++-------- src/main/extractor.ts | 9 +++++++-- src/renderer/App.tsx | 5 ++++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 74e5905..2f8589e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.6.26", + "version": "1.6.27", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 17d5309..f61b915 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -1710,8 +1710,8 @@ export class DownloadManager extends EventEmitter { item.lastError = "Datei nicht gefunden auf Rapidgator"; item.onlineStatus = "offline"; item.updatedAt = nowMs(); - if (this.runItemIds.has(itemId)) { - this.recordRunOutcome(itemId, "failed"); + if (this.runItemIds.has(item.id)) { + this.recordRunOutcome(item.id, "failed"); } // Refresh package status since item was set to failed const pkg = this.session.packages[item.packageId]; @@ -2575,6 +2575,10 @@ export class DownloadManager extends EventEmitter { if (postProcessController && !postProcessController.signal.aborted) { postProcessController.abort("reset"); } + this.packagePostProcessAbortControllers.delete(packageId); + this.packagePostProcessTasks.delete(packageId); + this.hybridExtractRequeue.delete(packageId); + this.runCompletedPackages.delete(packageId); // 3. Clean up extraction progress manifest (.rd_extract_progress.json) if (pkg.outputDir) { @@ -3418,7 +3422,9 @@ export class DownloadManager extends EventEmitter { if (this.settings.autoExtract) { const allExtracted = pkg.itemIds.every((id) => { const item = this.session.items[id]; - return !item || isExtractedLabel(item.fullStatus || ""); + if (!item) return true; + if (item.status === "failed" || item.status === "cancelled") return true; + return isExtractedLabel(item.fullStatus || ""); }); if (!allExtracted) continue; } @@ -4927,6 +4933,7 @@ export class DownloadManager extends EventEmitter { } } this.releaseTargetPath(item.id); + this.dropItemContribution(item.id); this.queueRetry(item, active, 300, "Netzwerkfehler erkannt, frischer Retry"); item.lastError = ""; item.downloadedBytes = 0; @@ -6373,11 +6380,12 @@ export class DownloadManager extends EventEmitter { } const status = entry.fullStatus || ""; if (/^Entpacken\b/i.test(status)) { - if (result.extracted > 0 && result.failed === 0) { - entry.fullStatus = formatExtractDone(nowMs() - hybridExtractStartMs); - } else { + if (result.failed > 0) { entry.fullStatus = "Entpacken - Error"; + } else if (result.extracted > 0) { + entry.fullStatus = formatExtractDone(nowMs() - hybridExtractStartMs); } + // extracted === 0 && failed === 0: keep current status (no archives to process) entry.updatedAt = updatedAt; } } @@ -6690,7 +6698,9 @@ export class DownloadManager extends EventEmitter { const timeoutReason = `Entpacken Timeout nach ${Math.ceil(extractTimeoutMs / 1000)}s`; logger.error(`Post-Processing Entpacken Timeout: pkg=${pkg.name}`); for (const entry of completedItems) { - entry.fullStatus = `Entpack-Fehler: ${timeoutReason}`; + if (!isExtractedLabel(entry.fullStatus)) { + entry.fullStatus = `Entpack-Fehler: ${timeoutReason}`; + } entry.updatedAt = nowMs(); } pkg.status = "failed"; @@ -6713,7 +6723,9 @@ export class DownloadManager extends EventEmitter { const reason = compactErrorText(error); logger.error(`Post-Processing Entpacken Exception: pkg=${pkg.name}, reason=${reason}`); for (const entry of completedItems) { - entry.fullStatus = `Entpack-Fehler: ${reason}`; + if (!isExtractedLabel(entry.fullStatus)) { + entry.fullStatus = `Entpack-Fehler: ${reason}`; + } entry.updatedAt = nowMs(); } pkg.status = "failed"; diff --git a/src/main/extractor.ts b/src/main/extractor.ts index 23683f5..6856e15 100644 --- a/src/main/extractor.ts +++ b/src/main/extractor.ts @@ -424,9 +424,12 @@ async function writeExtractResumeState(packageDir: string, completedArchives: Se .map((name) => archiveNameKey(name)) .sort((a, b) => a.localeCompare(b)) }; - const tmpPath = progressPath + ".tmp"; + const tmpPath = progressPath + "." + Date.now() + "." + Math.random().toString(36).slice(2, 8) + ".tmp"; await fs.promises.writeFile(tmpPath, JSON.stringify(payload, null, 2), "utf8"); - await fs.promises.rename(tmpPath, progressPath); + await fs.promises.rename(tmpPath, progressPath).catch(async () => { + // rename may fail if another writer renamed tmpPath first (parallel workers) + await fs.promises.rm(tmpPath, { force: true }).catch(() => {}); + }); } catch (error) { logger.warn(`ExtractResumeState schreiben fehlgeschlagen: ${String(error)}`); } @@ -896,6 +899,8 @@ function resolveJvmExtractorLayout(): JvmExtractorLayout | null { }) || ""; if (!javaCommand) { + cachedJvmLayout = null; + cachedJvmLayoutNullSince = Date.now(); return null; } diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index acef83f..929fae5 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -482,6 +482,7 @@ export function App(): ReactElement { tabRef.current = tab; const stateFlushTimerRef = useRef | null>(null); const toastTimerRef = useRef | null>(null); + const onImportDlcRef = useRef<() => Promise>(() => Promise.resolve()); const [dragOver, setDragOver] = useState(false); const [editingPackageId, setEditingPackageId] = useState(null); const [editingName, setEditingName] = useState(""); @@ -1199,6 +1200,8 @@ export function App(): ReactElement { }); }; + onImportDlcRef.current = onImportDlc; + const onDrop = async (event: DragEvent): Promise => { event.preventDefault(); dragDepthRef.current = 0; @@ -1929,7 +1932,7 @@ export function App(): ReactElement { if (inInput) return; e.preventDefault(); setOpenMenu(null); - void onImportDlc(); + void onImportDlcRef.current(); return; } if (!e.shiftKey && e.key.toLowerCase() === "a") {