From f25e61573d0ee7aca3bb4189565c5adde2df6f83 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sun, 8 Mar 2026 19:38:22 +0100 Subject: [PATCH] Fix downloads not starting: reset session.running on startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: normalizeSessionStatuses() did not reset session.running to false on startup. If the app was closed/crashed while downloads were active, session.json retained running=true. On next launch, start() checked if (this.session.running) return — silently refusing to start any downloads. Also improved normalizeSessionStatuses to match the old DM behavior: - Reset session.running, paused, reconnectUntil, reconnectReason - Recover cancelled/Gestoppt items back to queued - Mark extracting/integrity_check items as completed (already downloaded) - Handle paused and reconnect_wait items - Cover all transient package statuses Updated update tests for beta repo name. Co-Authored-By: Claude Opus 4.6 --- src/main/download/download-manager.ts | 35 +++++++++++++++++++++++++-- tests/update.test.ts | 12 ++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/download/download-manager.ts b/src/main/download/download-manager.ts index 61883f6..b3c6500 100644 --- a/src/main/download/download-manager.ts +++ b/src/main/download/download-manager.ts @@ -1594,16 +1594,47 @@ export class DownloadManager extends EventEmitter { } private normalizeSessionStatuses(): void { + // Critical: reset session-level running state on startup. + // Without this, if the app crashes while running, session.running stays true + // in the persisted JSON and start() silently returns on next launch. + this.session.running = false; + this.session.paused = false; + this.session.reconnectUntil = 0; + this.session.reconnectReason = ""; + for (const item of Object.values(this.session.items)) { - if (item.status === "downloading" || item.status === "validating" || item.status === "integrity_check") { + // Items that were stopped mid-run should be re-queued + if (item.status === "cancelled" && item.fullStatus === "Gestoppt") { item.status = "queued"; item.fullStatus = "Wartet"; + item.lastError = ""; + item.speedBps = 0; + item.provider = null; + item.updatedAt = nowMs(); + continue; + } + // Items that were extracting/checking integrity are already fully downloaded + if (item.status === "extracting" || item.status === "integrity_check") { + item.status = "completed"; + item.fullStatus = `Fertig (${humanSize(item.downloadedBytes)})`; + item.speedBps = 0; + item.updatedAt = nowMs(); + continue; + } + // Active/paused/reconnecting items → re-queue + if (item.status === "downloading" || item.status === "validating" + || item.status === "paused" || item.status === "reconnect_wait") { + item.status = "queued"; + const pkg = this.session.packages[item.packageId]; + item.fullStatus = (pkg && pkg.enabled === false) ? "Paket gestoppt" : "Wartet"; item.speedBps = 0; item.updatedAt = nowMs(); } } for (const pkg of Object.values(this.session.packages)) { - if (pkg.status === "downloading" || pkg.status === "validating" || pkg.status === "extracting") { + if (pkg.status === "downloading" || pkg.status === "validating" + || pkg.status === "extracting" || pkg.status === "integrity_check" + || pkg.status === "paused" || pkg.status === "reconnect_wait") { pkg.status = "queued"; pkg.updatedAt = nowMs(); } diff --git a/tests/update.test.ts b/tests/update.test.ts index 2953f02..dde777b 100644 --- a/tests/update.test.ts +++ b/tests/update.test.ts @@ -46,7 +46,7 @@ afterEach(() => { describe("update", () => { it("normalizes update repo input", () => { - expect(normalizeUpdateRepo("")).toBe("Administrator/real-debrid-downloader"); + expect(normalizeUpdateRepo("")).toBe("Administrator/beta-real-debrid-downloader"); expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo"); expect(normalizeUpdateRepo("https://codeberg.org/owner/repo")).toBe("owner/repo"); expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo"); @@ -518,14 +518,14 @@ describe("normalizeUpdateRepo extended", () => { }); it("returns default for malformed inputs", () => { - expect(normalizeUpdateRepo("just-one-part")).toBe("Administrator/real-debrid-downloader"); - expect(normalizeUpdateRepo(" ")).toBe("Administrator/real-debrid-downloader"); + expect(normalizeUpdateRepo("just-one-part")).toBe("Administrator/beta-real-debrid-downloader"); + expect(normalizeUpdateRepo(" ")).toBe("Administrator/beta-real-debrid-downloader"); }); it("rejects traversal-like owner or repo segments", () => { - expect(normalizeUpdateRepo("../owner/repo")).toBe("Administrator/real-debrid-downloader"); - expect(normalizeUpdateRepo("owner/../repo")).toBe("Administrator/real-debrid-downloader"); - expect(normalizeUpdateRepo("https://codeberg.org/owner/../../repo")).toBe("Administrator/real-debrid-downloader"); + expect(normalizeUpdateRepo("../owner/repo")).toBe("Administrator/beta-real-debrid-downloader"); + expect(normalizeUpdateRepo("owner/../repo")).toBe("Administrator/beta-real-debrid-downloader"); + expect(normalizeUpdateRepo("https://codeberg.org/owner/../../repo")).toBe("Administrator/beta-real-debrid-downloader"); }); it("handles www prefix", () => {