From b21df2f9abb9c8d5da0b77f2d3756802f95a1ad2 Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 13:35:06 +0800 Subject: feat: display download rate and progress with concurrency support --- ui/src/lib/DownloadMonitor.svelte | 98 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) (limited to 'ui/src/lib/DownloadMonitor.svelte') diff --git a/ui/src/lib/DownloadMonitor.svelte b/ui/src/lib/DownloadMonitor.svelte index b796591..e4920ad 100644 --- a/ui/src/lib/DownloadMonitor.svelte +++ b/ui/src/lib/DownloadMonitor.svelte @@ -9,11 +9,16 @@ downloaded: number; // in bytes total: number; // in bytes status: string; + completed_files: number; + total_files: number; + total_downloaded_bytes: number; } let currentFile = ""; - let progress = 0; // percentage 0-100 + let progress = 0; // percentage 0-100 (current file) + let totalProgress = 0; // percentage 0-100 (all files) let totalFiles = 0; + let completedFiles = 0; let statusText = "Preparing..."; let unlistenProgress: () => void; let unlistenStart: () => void; @@ -21,13 +26,30 @@ let downloadedBytes = 0; let totalBytes = 0; + // Speed and ETA tracking + let downloadSpeed = 0; // bytes per second + let etaSeconds = 0; + let startTime = 0; + let totalDownloadedBytes = 0; + let lastUpdateTime = 0; + let lastTotalBytes = 0; + onMount(async () => { unlistenStart = await listen("download-start", (event) => { visible = true; totalFiles = event.payload; + completedFiles = 0; progress = 0; + totalProgress = 0; statusText = "Starting download..."; currentFile = ""; + // Reset speed tracking + startTime = Date.now(); + totalDownloadedBytes = 0; + downloadSpeed = 0; + etaSeconds = 0; + lastUpdateTime = Date.now(); + lastTotalBytes = 0; }); unlistenProgress = await listen( @@ -36,8 +58,7 @@ const payload = event.payload; currentFile = payload.file; - // Simple file progress for now. Global progress would require tracking all files. - // For single file (Client jar), this is accurate. + // Current file progress downloadedBytes = payload.downloaded; totalBytes = payload.total; @@ -46,12 +67,45 @@ if (payload.total > 0) { progress = (payload.downloaded / payload.total) * 100; } + + // Total progress (all files) + completedFiles = payload.completed_files; + totalFiles = payload.total_files; + if (totalFiles > 0) { + totalProgress = (completedFiles / totalFiles) * 100; + } + + // Calculate download speed (using moving average) + totalDownloadedBytes = payload.total_downloaded_bytes; + const now = Date.now(); + const timeDiff = (now - lastUpdateTime) / 1000; // seconds + + if (timeDiff >= 0.5) { // Update speed every 0.5 seconds + const bytesDiff = totalDownloadedBytes - lastTotalBytes; + const instantSpeed = bytesDiff / timeDiff; + // Smooth the speed with exponential moving average + downloadSpeed = downloadSpeed === 0 ? instantSpeed : downloadSpeed * 0.7 + instantSpeed * 0.3; + lastUpdateTime = now; + lastTotalBytes = totalDownloadedBytes; + } + + // Estimate remaining time based on files remaining + if (downloadSpeed > 0 && completedFiles < totalFiles) { + // Rough estimate: assume average file size based on downloaded data + const avgBytesPerFile = completedFiles > 0 ? totalDownloadedBytes / completedFiles : totalDownloadedBytes; + const remainingFiles = totalFiles - completedFiles; + const estimatedRemainingBytes = avgBytesPerFile * remainingFiles; + etaSeconds = estimatedRemainingBytes / downloadSpeed; + } else { + etaSeconds = 0; + } } ); unlistenComplete = await listen("download-complete", () => { statusText = "Done!"; progress = 100; + totalProgress = 100; setTimeout(() => { visible = false; }, 2000); @@ -71,6 +125,24 @@ const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } + + function formatSpeed(bytesPerSecond: number) { + if (bytesPerSecond === 0) return "-- /s"; + return formatBytes(bytesPerSecond) + "/s"; + } + + function formatTime(seconds: number) { + if (seconds <= 0 || !isFinite(seconds)) return "--"; + if (seconds < 60) return `${Math.round(seconds)}s`; + if (seconds < 3600) { + const mins = Math.floor(seconds / 60); + const secs = Math.round(seconds % 60); + return `${mins}m ${secs}s`; + } + const hours = Math.floor(seconds / 3600); + const mins = Math.floor((seconds % 3600) / 60); + return `${hours}h ${mins}m`; + } {#if visible} @@ -82,11 +154,29 @@ {statusText} + +
+
+ Total Progress + {completedFiles} / {totalFiles} files +
+
+
+
+
+ {formatSpeed(downloadSpeed)} · ETA: {formatTime(etaSeconds)} + {completedFiles < totalFiles ? Math.floor(totalProgress) : 100}% +
+
+
{currentFile || "Waiting..."}
- +
Date: Wed, 14 Jan 2026 13:50:25 +0800 Subject: feat: Consider incorporating totalDownloadedBytes into the calculation for a smoother progress indicator Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ui/src/lib/DownloadMonitor.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'ui/src/lib/DownloadMonitor.svelte') diff --git a/ui/src/lib/DownloadMonitor.svelte b/ui/src/lib/DownloadMonitor.svelte index e4920ad..5dc8269 100644 --- a/ui/src/lib/DownloadMonitor.svelte +++ b/ui/src/lib/DownloadMonitor.svelte @@ -72,7 +72,9 @@ completedFiles = payload.completed_files; totalFiles = payload.total_files; if (totalFiles > 0) { - totalProgress = (completedFiles / totalFiles) * 100; + const currentFileFraction = + payload.total > 0 ? payload.downloaded / payload.total : 0; + totalProgress = ((completedFiles + currentFileFraction) / totalFiles) * 100; } // Calculate download speed (using moving average) -- cgit v1.2.3-70-g09d2 From e6295ae4e80e4eb31163a86815d0257ce2091d3b Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 13:52:15 +0800 Subject: Update ui/src/lib/DownloadMonitor.svelte Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ui/src/lib/DownloadMonitor.svelte | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'ui/src/lib/DownloadMonitor.svelte') diff --git a/ui/src/lib/DownloadMonitor.svelte b/ui/src/lib/DownloadMonitor.svelte index 5dc8269..79adff5 100644 --- a/ui/src/lib/DownloadMonitor.svelte +++ b/ui/src/lib/DownloadMonitor.svelte @@ -83,12 +83,19 @@ const timeDiff = (now - lastUpdateTime) / 1000; // seconds if (timeDiff >= 0.5) { // Update speed every 0.5 seconds - const bytesDiff = totalDownloadedBytes - lastTotalBytes; - const instantSpeed = bytesDiff / timeDiff; - // Smooth the speed with exponential moving average - downloadSpeed = downloadSpeed === 0 ? instantSpeed : downloadSpeed * 0.7 + instantSpeed * 0.3; - lastUpdateTime = now; - lastTotalBytes = totalDownloadedBytes; + // On the first update, initialize lastTotalBytes to avoid counting + // bytes downloaded before the timer window started. + if (lastTotalBytes === 0 && totalDownloadedBytes > 0) { + lastTotalBytes = totalDownloadedBytes; + lastUpdateTime = now; + } else { + const bytesDiff = totalDownloadedBytes - lastTotalBytes; + const instantSpeed = bytesDiff / timeDiff; + // Smooth the speed with exponential moving average + downloadSpeed = downloadSpeed === 0 ? instantSpeed : downloadSpeed * 0.7 + instantSpeed * 0.3; + lastUpdateTime = now; + lastTotalBytes = totalDownloadedBytes; + } } // Estimate remaining time based on files remaining -- cgit v1.2.3-70-g09d2 From 2e1a90fa5681d4ef40a8675820300717d8294de0 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 13:52:46 +0800 Subject: Update ui/src/lib/DownloadMonitor.svelte Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ui/src/lib/DownloadMonitor.svelte | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'ui/src/lib/DownloadMonitor.svelte') diff --git a/ui/src/lib/DownloadMonitor.svelte b/ui/src/lib/DownloadMonitor.svelte index 79adff5..9adef67 100644 --- a/ui/src/lib/DownloadMonitor.svelte +++ b/ui/src/lib/DownloadMonitor.svelte @@ -98,12 +98,19 @@ } } - // Estimate remaining time based on files remaining + // Estimate remaining time if (downloadSpeed > 0 && completedFiles < totalFiles) { - // Rough estimate: assume average file size based on downloaded data - const avgBytesPerFile = completedFiles > 0 ? totalDownloadedBytes / completedFiles : totalDownloadedBytes; const remainingFiles = totalFiles - completedFiles; - const estimatedRemainingBytes = avgBytesPerFile * remainingFiles; + let estimatedRemainingBytes: number; + + if (completedFiles > 0) { + // Use average size of completed files to estimate remaining files + const avgBytesPerCompletedFile = totalDownloadedBytes / completedFiles; + estimatedRemainingBytes = avgBytesPerCompletedFile * remainingFiles; + } else { + // No completed files yet: estimate based only on current file's remaining bytes + estimatedRemainingBytes = Math.max(totalBytes - downloadedBytes, 0); + } etaSeconds = estimatedRemainingBytes / downloadSpeed; } else { etaSeconds = 0; -- cgit v1.2.3-70-g09d2