From 693f7b482ab7ca2e03ab6bc8750a3ce3ce02b0ea Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Wed, 4 Mar 2026 05:54:41 +0100 Subject: [PATCH] Release v1.6.3 Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- src/main/app-controller.ts | 3 +++ src/main/download-manager.ts | 37 ++++++++++++++++++++---------------- src/renderer/App.tsx | 12 ++++++------ 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f1bba84..cdff46e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.6.2", + "version": "1.6.3", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/main/app-controller.ts b/src/main/app-controller.ts index c345e01..0e5a160 100644 --- a/src/main/app-controller.ts +++ b/src/main/app-controller.ts @@ -286,6 +286,9 @@ export class AppController { this.settings = restoredSettings; saveSettings(this.storagePaths, this.settings); this.manager.setSettings(this.settings); + // Stop the manager BEFORE saving the restored session to prevent + // the auto-save timer from overwriting it with the old in-memory session. + this.manager.stop(); const restoredSession = parsed.session as ReturnType; saveSession(this.storagePaths, restoredSession); return { restored: true, message: "Backup wiederhergestellt. Bitte App neustarten." }; diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index f81dfea..f8294e5 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -2576,8 +2576,6 @@ export class DownloadManager extends EventEmitter { const p = this.session.packages[order[i]]; if (p && p.priority === "high") { insertAt = i + 1; - } else { - break; } } order.splice(insertAt, 0, packageId); @@ -2890,6 +2888,7 @@ export class DownloadManager extends EventEmitter { // duration, average speed, and ETA are calculated relative to the current run, not cumulative. this.session.runStartedAt = nowMs(); this.session.totalDownloadedBytes = 0; + this.sessionDownloadedBytes = 0; this.session.summaryText = ""; this.session.reconnectUntil = 0; this.session.reconnectReason = ""; @@ -3110,8 +3109,8 @@ export class DownloadManager extends EventEmitter { } // Clear stale transient status texts from previous session if (item.status === "queued") { - const fs = (item.fullStatus || "").trim(); - if (fs !== "Wartet" && fs !== "Paket gestoppt" && fs !== "Online") { + const statusText = (item.fullStatus || "").trim(); + if (statusText !== "Wartet" && statusText !== "Paket gestoppt" && statusText !== "Online") { item.fullStatus = "Wartet"; } } @@ -3120,8 +3119,8 @@ export class DownloadManager extends EventEmitter { item.onlineStatus = undefined; } if (item.status === "completed") { - const fs = (item.fullStatus || "").trim(); - if (fs && !isExtractedLabel(fs) && !/^Fertig\b/i.test(fs)) { + const statusText = (item.fullStatus || "").trim(); + if (statusText && !isExtractedLabel(statusText) && !/^Fertig\b/i.test(statusText)) { item.fullStatus = `Fertig (${humanSize(item.downloadedBytes)})`; } } @@ -3165,7 +3164,7 @@ export class DownloadManager extends EventEmitter { if (failed > 0) { pkg.status = "failed"; } else if (cancelled > 0) { - pkg.status = success > 0 ? "failed" : "cancelled"; + pkg.status = success > 0 ? "completed" : "cancelled"; } else if (success > 0) { pkg.status = "completed"; } @@ -4927,9 +4926,12 @@ export class DownloadManager extends EventEmitter { if (writeMode === "w" && item.totalBytes && item.totalBytes > 0 && process.platform === "win32") { try { const fd = await fs.promises.open(effectiveTargetPath, "w"); - await fd.truncate(item.totalBytes); - await fd.close(); - preAllocated = true; + try { + await fd.truncate(item.totalBytes); + preAllocated = true; + } finally { + await fd.close(); + } } catch { /* best-effort */ } } @@ -5281,6 +5283,11 @@ export class DownloadManager extends EventEmitter { if (!stream.destroyed) { stream.destroy(); } + // If the body read succeeded but the final flush or stream close failed, + // propagate the error so the download is retried instead of marked complete. + if (bodyError) { + throw bodyError; + } } // Detect tiny error-response files (e.g. hoster returning "Forbidden" with HTTP 200). @@ -6330,7 +6337,7 @@ export class DownloadManager extends EventEmitter { } else if (failed > 0) { pkg.status = "failed"; } else if (cancelled > 0) { - pkg.status = success > 0 ? "failed" : "cancelled"; + pkg.status = success > 0 ? "completed" : "cancelled"; } else { pkg.status = "completed"; } @@ -6504,21 +6511,19 @@ export class DownloadManager extends EventEmitter { if (event) { bandwidthSamples.push({ timestamp: event.at, - speedBps: event.bytes * 3 + speedBps: Math.floor(event.bytes * (1000 / 120)) }); } } const paused = this.session.running && this.session.paused; - const currentSpeedBps = paused ? 0 : this.speedBytesLastWindow / 3; + const currentSpeedBps = paused ? 0 : this.speedBytesLastWindow / SPEED_WINDOW_SECONDS; - let totalBytes = 0; let maxSpeed = 0; for (let i = this.speedEventsHead; i < this.speedEvents.length; i += 1) { const event = this.speedEvents[i]; if (event) { - totalBytes += event.bytes; - const speed = event.bytes * 3; + const speed = Math.floor(event.bytes * (1000 / 120)); if (speed > maxSpeed) { maxSpeed = speed; } diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 3345d9c..9b61a4e 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -233,9 +233,9 @@ const BandwidthChart = memo(function BandwidthChart({ items, running, paused, sp ctx.textAlign = "center"; ctx.textBaseline = "top"; - ctx.fillText("0s", padding.left, height - padding.bottom + 8); + ctx.fillText("60s", padding.left, height - padding.bottom + 8); ctx.fillText("30s", padding.left + chartWidth / 2, height - padding.bottom + 8); - ctx.fillText("60s", width - padding.right, height - padding.bottom + 8); + ctx.fillText("0s", width - padding.right, height - padding.bottom + 8); if (history.length < 2) { ctx.fillStyle = textColor; @@ -2320,9 +2320,9 @@ export function App(): ReactElement { {selectedHistoryIds.size > 0 && ( @@ -3091,7 +3091,7 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs const failed = items.filter((item) => item.status === "failed").length; const cancelled = items.filter((item) => item.status === "cancelled").length; const extracted = items.filter((item) => item.fullStatus?.startsWith("Entpackt")).length; - const extracting = items.some((item) => item.fullStatus?.startsWith("Entpack")); + const extracting = items.some((item) => item.fullStatus?.startsWith("Entpacken")); const total = Math.max(1, items.length); // Use 50/50 split when extraction is active OR package is in extracting state // (prevents bar jumping from 100% to 50% when extraction starts)