From 19769ea0bb14c383a38129462489b13de6c9cf51 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 2 Mar 2026 23:32:30 +0100 Subject: [PATCH] Fix combined progress display during extraction, fix pause showing Warte auf Daten Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- src/main/download-manager.ts | 41 +++++++++++++++++++++++++++++++++--- src/renderer/App.tsx | 7 +++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 14040ed..3ae8cd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.5.33", + "version": "1.5.34", "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 cb25bf7..40d91d9 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -10,6 +10,7 @@ import { DownloadSummary, DownloadStatus, DuplicatePolicy, + HistoryEntry, PackageEntry, ParsedPackageInput, SessionState, @@ -132,11 +133,18 @@ function retryLimitToMaxRetries(retryLimit: number): number { return retryLimit <= 0 ? Number.MAX_SAFE_INTEGER : retryLimit; } +type HistoryEntryCallback = (entry: HistoryEntry) => void; + type DownloadManagerOptions = { megaWebUnrestrict?: MegaWebUnrestrictor; invalidateMegaSession?: () => void; + onHistoryEntry?: HistoryEntryCallback; }; +function generateHistoryId(): string { + return `hist-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; +} + function cloneSession(session: SessionState): SessionState { const clonedItems: Record = {}; for (const key of Object.keys(session.items)) { @@ -787,6 +795,8 @@ export class DownloadManager extends EventEmitter { private lastStaleResetAt = 0; + private onHistoryEntryCallback?: HistoryEntryCallback; + public constructor(settings: AppSettings, session: SessionState, storagePaths: StoragePaths, options: DownloadManagerOptions = {}) { super(); this.settings = settings; @@ -795,6 +805,7 @@ export class DownloadManager extends EventEmitter { this.storagePaths = storagePaths; this.debridService = new DebridService(settings, { megaWebUnrestrict: options.megaWebUnrestrict }); this.invalidateMegaSessionFn = options.invalidateMegaSession; + this.onHistoryEntryCallback = options.onHistoryEntry; this.applyOnStartCleanupPolicy(); this.normalizeSessionStatuses(); void this.recoverRetryableItems("startup").catch((err) => logger.warn(`recoverRetryableItems Fehler (startup): ${compactErrorText(err)}`)); @@ -3028,7 +3039,31 @@ export class DownloadManager extends EventEmitter { void this.runPackagePostProcessing(packageId).catch((err) => logger.warn(`runPackagePostProcessing Fehler (extractNow): ${compactErrorText(err)}`)); } - private removePackageFromSession(packageId: string, itemIds: string[]): void { + private removePackageFromSession(packageId: string, itemIds: string[], reason: "completed" | "deleted" = "deleted"): void { + const pkg = this.session.packages[packageId]; + if (pkg && this.onHistoryEntryCallback) { + const completedItems = itemIds.map(id => this.session.items[id]).filter(Boolean) as DownloadItem[]; + const completedCount = completedItems.filter(item => item.status === "completed").length; + if (completedCount > 0 && (reason === "completed" || pkg.status === "completed")) { + const totalBytes = completedItems.reduce((sum, item) => sum + (item.downloadedBytes || 0), 0); + const durationSeconds = pkg.createdAt > 0 ? Math.max(1, Math.floor((nowMs() - pkg.createdAt) / 1000)) : 1; + const providers = new Set(completedItems.map(item => item.provider).filter(Boolean)); + const provider = providers.size === 1 ? [...providers][0] : null; + const entry: HistoryEntry = { + id: generateHistoryId(), + name: pkg.name, + totalBytes, + downloadedBytes: totalBytes, + fileCount: completedCount, + provider, + completedAt: nowMs(), + durationSeconds, + status: reason === "completed" ? "completed" : "deleted", + outputDir: pkg.outputDir + }; + this.onHistoryEntryCallback(entry); + } + } const postProcessController = this.packagePostProcessAbortControllers.get(packageId); if (postProcessController && !postProcessController.signal.aborted) { postProcessController.abort("package_removed"); @@ -4265,7 +4300,7 @@ export class DownloadManager extends EventEmitter { if (nowTick - lastDataAt < idlePulseMs) { return; } - if (item.status === "paused") { + if (item.status === "paused" || this.session.paused) { return; } item.status = "downloading"; @@ -5403,7 +5438,7 @@ export class DownloadManager extends EventEmitter { } } - this.removePackageFromSession(packageId, [...pkg.itemIds]); + this.removePackageFromSession(packageId, [...pkg.itemIds], "completed"); } private applyCompletedCleanupPolicy(packageId: string, itemId: string): void { diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 27ee1b9..d8b1903 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -2773,6 +2773,7 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs return sum; }, 0); const exProgress = Math.floor(((extracted + extractingProgress) / total) * 50); + const combinedProgress = extracting ? dlProgress + exProgress : dlProgress; const onKeyDown = (e: KeyboardEvent): void => { if (e.key === "Enter") { onFinishEdit(pkg.id, pkg.name, editingName); } @@ -2810,9 +2811,9 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs - - {dlProgress}% - {dlProgress}% + + {combinedProgress}% + {combinedProgress}% {(() => {