diff --git a/package.json b/package.json index 26749be..0a156dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.4.97", + "version": "1.4.98", "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 4a63b55..86006d5 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -718,7 +718,7 @@ export class DownloadManager extends EventEmitter { private persistTimer: NodeJS.Timeout | null = null; - private speedEvents: Array<{ at: number; bytes: number }> = []; + private speedEvents: Array<{ at: number; bytes: number; pid: string }> = []; private summary: DownloadSummary | null = null; @@ -872,7 +872,10 @@ export class DownloadManager extends EventEmitter { canStop: this.session.running, canPause: this.session.running, clipboardActive: this.settings.clipboardWatch, - reconnectSeconds: Math.ceil(reconnectMs / 1000) + reconnectSeconds: Math.ceil(reconnectMs / 1000), + packageSpeedBps: paused ? {} : Object.fromEntries( + [...this.speedBytesPerPackage].map(([pid, bytes]) => [pid, Math.floor(bytes / 3)]) + ) }; } @@ -932,6 +935,7 @@ export class DownloadManager extends EventEmitter { this.speedEvents = []; this.speedEventsHead = 0; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.statsCache = null; this.statsCacheAt = 0; } @@ -1136,6 +1140,7 @@ export class DownloadManager extends EventEmitter { this.speedEvents = []; this.speedEventsHead = 0; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.packagePostProcessTasks.clear(); this.packagePostProcessAbortControllers.clear(); this.hybridExtractRequeue.clear(); @@ -2324,6 +2329,7 @@ export class DownloadManager extends EventEmitter { this.session.reconnectReason = ""; this.speedEvents = []; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.speedEventsHead = 0; this.lastGlobalProgressBytes = 0; this.lastGlobalProgressAt = nowMs(); @@ -2352,6 +2358,7 @@ export class DownloadManager extends EventEmitter { this.consecutiveReconnects = 0; this.speedEvents = []; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.speedEventsHead = 0; this.lastGlobalProgressBytes = 0; this.lastGlobalProgressAt = nowMs(); @@ -2443,6 +2450,7 @@ export class DownloadManager extends EventEmitter { this.speedEvents = []; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.speedEventsHead = 0; this.runItemIds.clear(); this.runPackageIds.clear(); @@ -2658,11 +2666,16 @@ export class DownloadManager extends EventEmitter { } private speedEventsHead = 0; + private speedBytesPerPackage = new Map(); private pruneSpeedEvents(now: number): void { const cutoff = now - 3000; while (this.speedEventsHead < this.speedEvents.length && this.speedEvents[this.speedEventsHead].at < cutoff) { - this.speedBytesLastWindow = Math.max(0, this.speedBytesLastWindow - this.speedEvents[this.speedEventsHead].bytes); + const ev = this.speedEvents[this.speedEventsHead]; + this.speedBytesLastWindow = Math.max(0, this.speedBytesLastWindow - ev.bytes); + const pkgBytes = (this.speedBytesPerPackage.get(ev.pid) ?? 0) - ev.bytes; + if (pkgBytes <= 0) this.speedBytesPerPackage.delete(ev.pid); + else this.speedBytesPerPackage.set(ev.pid, pkgBytes); this.speedEventsHead += 1; } if (this.speedEventsHead > 200) { @@ -2673,19 +2686,20 @@ export class DownloadManager extends EventEmitter { private lastSpeedPruneAt = 0; - private recordSpeed(bytes: number): void { + private recordSpeed(bytes: number, packageId: string = ""): void { const now = nowMs(); if (bytes > 0 && this.consecutiveReconnects > 0) { this.consecutiveReconnects = 0; } const bucket = now - (now % 120); const last = this.speedEvents[this.speedEvents.length - 1]; - if (last && last.at === bucket) { + if (last && last.at === bucket && last.pid === packageId) { last.bytes += bytes; } else { - this.speedEvents.push({ at: bucket, bytes }); + this.speedEvents.push({ at: bucket, bytes, pid: packageId }); } this.speedBytesLastWindow += bytes; + this.speedBytesPerPackage.set(packageId, (this.speedBytesPerPackage.get(packageId) ?? 0) + bytes); if (now - this.lastSpeedPruneAt >= 1500) { this.pruneSpeedEvents(now); this.lastSpeedPruneAt = now; @@ -4251,7 +4265,7 @@ export class DownloadManager extends EventEmitter { windowBytes += buffer.length; this.session.totalDownloadedBytes += buffer.length; this.itemContributedBytes.set(active.itemId, (this.itemContributedBytes.get(active.itemId) || 0) + buffer.length); - this.recordSpeed(buffer.length); + this.recordSpeed(buffer.length, item.packageId); throughputWindowBytes += buffer.length; const throughputNow = nowMs(); @@ -5380,6 +5394,7 @@ export class DownloadManager extends EventEmitter { this.speedEvents = []; this.speedEventsHead = 0; this.speedBytesLastWindow = 0; + this.speedBytesPerPackage.clear(); this.globalSpeedLimitQueue = Promise.resolve(); this.globalSpeedLimitNextAt = 0; this.nonResumableActive = 0; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 14b1dbe..222a0c6 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -77,7 +77,7 @@ const emptySnapshot = (): UiSnapshot => ({ paused: false, running: false, updatedAt: Date.now() }, summary: null, stats: emptyStats(), speedText: "Geschwindigkeit: 0 B/s", etaText: "ETA: --", - canStart: true, canStop: false, canPause: false, clipboardActive: false, reconnectSeconds: 0 + canStart: true, canStop: false, canPause: false, clipboardActive: false, reconnectSeconds: 0, packageSpeedBps: {} }); const cleanupLabels: Record = { @@ -1704,13 +1704,11 @@ export function App(): ReactElement { const packageSpeedMap = useMemo(() => { const map = new Map(); - for (const item of Object.values(snapshot.session.items)) { - if (item.speedBps > 0) { - map.set(item.packageId, (map.get(item.packageId) ?? 0) + item.speedBps); - } + for (const [pid, bps] of Object.entries(snapshot.packageSpeedBps)) { + if (bps > 0) map.set(pid, bps); } return map; - }, [snapshot.session.items]); + }, [snapshot.packageSpeedBps]); return (
)} {!multi && contextMenu.itemId && ( - + )} {multi && hasItems && ( )} {hasPackages && ( )}
@@ -2683,10 +2690,10 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs {packageSpeed > 0 ? formatSpeedMbps(packageSpeed) : "-"} - {!collapsed &&
+
{extracting &&
} -
} +
{!collapsed && items.map((item) => (
{ e.stopPropagation(); onSelect(item.id, e.ctrlKey); }} onMouseDown={(e) => { e.stopPropagation(); onSelectMouseDown(item.id, e); }} onMouseEnter={() => onSelectMouseEnter(item.id)} onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); onContextMenu(pkg.id, item.id, e.clientX, e.clientY); }}> {item.fileName} diff --git a/src/shared/types.ts b/src/shared/types.ts index ef945f0..155490f 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -161,6 +161,7 @@ export interface UiSnapshot { canPause: boolean; clipboardActive: boolean; reconnectSeconds: number; + packageSpeedBps: Record; } export interface AddLinksPayload {