From 8b7cecc1c51a9dbde67bfdd831561f5743e24a43 Mon Sep 17 00:00:00 2001 From: zarzet Date: Fri, 15 May 2026 01:00:20 +0700 Subject: [PATCH] refactor: extract download progress label formatting - Extract _formatDownloadProgressLabel() for cleaner code - Show received/total size when bytesTotal is available - Estimate total size from progress when only bytesReceived is known - Add text overflow handling with ellipsis --- go_backend/extension_runtime_file.go | 20 +++- lib/providers/download_queue_provider.dart | 2 +- lib/screens/queue_tab.dart | 109 +++++++++++++-------- 3 files changed, 85 insertions(+), 46 deletions(-) diff --git a/go_backend/extension_runtime_file.go b/go_backend/extension_runtime_file.go index 3c445c4f..8ef83fdc 100644 --- a/go_backend/extension_runtime_file.go +++ b/go_backend/extension_runtime_file.go @@ -244,7 +244,7 @@ func (r *extensionRuntime) fileDownload(call goja.FunctionCall) goja.Value { } contentLength := resp.ContentLength - shouldTrackItemBytes := activeItemID != "" && onProgress == nil + shouldTrackItemBytes := activeItemID != "" if shouldTrackItemBytes && contentLength > 0 { SetItemBytesTotal(activeItemID, contentLength) } @@ -301,6 +301,14 @@ func (r *extensionRuntime) fileDownload(call goja.FunctionCall) goja.Value { } } + if shouldTrackItemBytes { + if contentLength > 0 { + SetItemProgress(activeItemID, float64(written)/float64(contentLength), written, contentLength) + } else if written > 0 { + SetItemBytesReceived(activeItemID, written) + } + } + GoLog("[Extension:%s] Downloaded %d bytes to %s\n", r.extensionID, written, fullPath) return r.vm.ToValue(map[string]interface{}{ @@ -383,7 +391,7 @@ func (r *extensionRuntime) fileDownloadChunked(client *http.Client, urlStr, full SetItemDownloading(activeItemID) } - shouldTrackItemBytes := activeItemID != "" && onProgress == nil + shouldTrackItemBytes := activeItemID != "" if shouldTrackItemBytes && totalSize > 0 { SetItemBytesTotal(activeItemID, totalSize) } @@ -526,6 +534,14 @@ func (r *extensionRuntime) fileDownloadChunked(client *http.Client, urlStr, full } } + if shouldTrackItemBytes { + if totalSize > 0 { + SetItemProgress(activeItemID, float64(totalWritten)/float64(totalSize), totalWritten, totalSize) + } else if totalWritten > 0 { + SetItemBytesReceived(activeItemID, totalWritten) + } + } + GoLog("[Extension:%s] Chunked download complete: %d bytes to %s\n", r.extensionID, totalWritten, fullPath) return r.vm.ToValue(map[string]interface{}{ diff --git a/lib/providers/download_queue_provider.dart b/lib/providers/download_queue_provider.dart index ed49b122..8f066b9d 100644 --- a/lib/providers/download_queue_provider.dart +++ b/lib/providers/download_queue_provider.dart @@ -2340,7 +2340,7 @@ class DownloadQueueNotifier extends Notifier { ); } else { _log.d( - 'Progress [$itemId]: ${(percentage * 100).toStringAsFixed(1)}% (DASH segments/unknown size) @ ${speedMBps.toStringAsFixed(2)} MB/s', + 'Progress [$itemId]: ${(percentage * 100).toStringAsFixed(1)}% (stream/unknown size) @ ${speedMBps.toStringAsFixed(2)} MB/s', ); } } diff --git a/lib/screens/queue_tab.dart b/lib/screens/queue_tab.dart index 30206a44..347d1eb2 100644 --- a/lib/screens/queue_tab.dart +++ b/lib/screens/queue_tab.dart @@ -42,6 +42,49 @@ import 'package:spotiflac_android/widgets/animation_utils.dart'; part 'queue_tab_helpers.dart'; part 'queue_tab_widgets.dart'; +String _formatDownloadSizeMB(num bytes) { + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; +} + +String _formatDownloadProgressLabel(BuildContext context, DownloadItem item) { + final progress = item.progress.clamp(0.0, 1.0); + final speedSuffix = item.speedMBps > 0 + ? ' • ${item.speedMBps.toStringAsFixed(1)} MB/s' + : ''; + + if (item.bytesTotal > 0) { + final received = item.bytesReceived > 0 + ? item.bytesReceived + : item.bytesTotal * progress; + final percent = (progress * 100).toStringAsFixed(0); + return '${_formatDownloadSizeMB(received)} / ${_formatDownloadSizeMB(item.bytesTotal)} • $percent%$speedSuffix'; + } + + if (item.bytesReceived > 0) { + final canEstimateTotal = progress > 0.01 && progress < 0.995; + if (canEstimateTotal) { + final estimatedTotal = item.bytesReceived / progress; + if (estimatedTotal > item.bytesReceived) { + return '${_formatDownloadSizeMB(item.bytesReceived)} / ~${_formatDownloadSizeMB(estimatedTotal)}$speedSuffix'; + } + } + return '${_formatDownloadSizeMB(item.bytesReceived)}$speedSuffix'; + } + + if (progress > 0) { + final percent = (progress * 100).toStringAsFixed(0); + return '$percent%$speedSuffix'; + } + + if (item.speedMBps > 0) { + return context.l10n.queueDownloadSpeedStatus( + item.speedMBps.toStringAsFixed(1), + ); + } + + return context.l10n.queueDownloadStarting; +} + class QueueTab extends ConsumerStatefulWidget { final PageController? parentPageController; final int parentPageIndex; @@ -5591,51 +5634,31 @@ class _QueueTabState extends ConsumerState { ?.copyWith(color: colorScheme.onSurfaceVariant), ), if (item.status == DownloadStatus.downloading) ...[ - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: ClipRRect( - borderRadius: BorderRadius.circular(4), - child: LinearProgressIndicator( - value: item.progress > 0 - ? item.progress - : null, - backgroundColor: - colorScheme.surfaceContainerHighest, + const SizedBox(height: 6), + Align( + alignment: Alignment.centerRight, + child: Text( + _formatDownloadProgressLabel(context, item), + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.right, + style: Theme.of(context).textTheme.labelSmall + ?.copyWith( color: colorScheme.primary, - minHeight: 6, + fontWeight: FontWeight.bold, ), - ), - ), - const SizedBox(width: 8), - Text( - item.bytesTotal > 0 - ? '${(item.progress * 100).toStringAsFixed(0)}%' - : (item.bytesReceived > 0 - ? '${(item.bytesReceived / (1024 * 1024)).toStringAsFixed(1)} MB${item.speedMBps > 0 ? ' • ${item.speedMBps.toStringAsFixed(1)} MB/s' : ''}' - : (item.progress > 0 - ? (item.speedMBps > 0 - ? '${(item.progress * 100).toStringAsFixed(0)}% • ${item.speedMBps.toStringAsFixed(1)} MB/s' - : '${(item.progress * 100).toStringAsFixed(0)}%') - : (item.speedMBps > 0 - ? context.l10n - .queueDownloadSpeedStatus( - item.speedMBps - .toStringAsFixed( - 1, - ), - ) - : context - .l10n - .queueDownloadStarting))), - style: Theme.of(context).textTheme.labelSmall - ?.copyWith( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), - ), - ], + ), + ), + const SizedBox(height: 3), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: item.progress > 0 ? item.progress : null, + backgroundColor: + colorScheme.surfaceContainerHighest, + color: colorScheme.primary, + minHeight: 6, + ), ), ], if (item.status == DownloadStatus.failed) ...[