feat: add ETA and improved progress metadata to download queue

This commit is contained in:
Dariusz L
2026-06-19 21:20:40 +02:00
parent 78697ffc49
commit c3caeb0a24
3 changed files with 62 additions and 8 deletions
+11
View File
@@ -2417,10 +2417,21 @@
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-top: 6px;
font-size: 11px;
color: var(--mr-text-muted);
}
.mr-progress-text > span:first-child {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.mr-progress-text > span:last-child {
flex: 0 0 auto;
text-align: right;
white-space: nowrap;
}
/* Scrollbar */
.mr-scrollable {
+2 -2
View File
@@ -762,7 +762,7 @@ export const queueMethods = {
const nodeLabel = info.missing?.subgraph_name || info.missing?.node_type || (info.missing?.subgraph_id ? 'Subgraph' : 'Node');
const downloaded = this.formatBytes(progress.downloaded || 0);
const total = progress.total_size ? this.formatBytes(progress.total_size) : '';
const speed = progress.speed ? `${this.formatBytes(progress.speed)}/s` : '';
const progressMeta = this.formatDownloadProgressMeta?.(progress) || '';
const status = progress.status || info.lastStatus || 'starting';
const statusLabel = status === 'downloading'
? `${Math.round(percent)}%`
@@ -795,7 +795,7 @@ export const queueMethods = {
}
html += `<div class="mr-download-queue-progress">`;
html += `<div class="mr-progress-bar"><div class="mr-progress-fill" style="width: ${percent}%;"></div></div>`;
html += `<div class="mr-progress-text"><span>${this.escapeHtml(sizeText)}</span><span>${this.escapeHtml(speed)}</span></div>`;
html += `<div class="mr-progress-text"><span>${this.escapeHtml(sizeText)}</span><span>${this.escapeHtml(progressMeta)}</span></div>`;
html += `</div>`;
html += `<div class="mr-queue-item-actions"><button type="button" class="mr-btn mr-btn-danger mr-btn-sm mr-download-queue-cancel" data-download-id="${this.escapeHtml(downloadId)}">Cancel</button></div>`;
html += `</div>`;
@@ -779,14 +779,14 @@ export const resolveDownloadMethods = {
const percent = Math.max(0, Math.min(100, Number(progress.progress) || 0));
const downloaded = this.formatBytes(progress.downloaded || 0);
const total = this.formatBytes(progress.total_size || 0);
const speed = progress.speed ? this.formatBytes(progress.speed) + '/s' : '';
const progressMeta = this.formatDownloadProgressMeta(progress);
const leftText = progress.total_size
? `${downloaded} / ${total} (${percent}%)`
: '<span class="mr-info-accent-text">Connecting...</span>';
progressDiv.innerHTML = this.renderProgressWithAction({
percent,
leftText,
rightText: speed,
rightText: progressMeta,
actionClass: canCancel ? 'cancel-download-btn mr-btn mr-btn-danger mr-btn-sm' : '',
actionText: canCancel ? 'Cancel' : '',
actionDataAttr: canCancel ? `data-download-id="${downloadId}"` : '',
@@ -2391,10 +2391,53 @@ export const resolveDownloadMethods = {
* Format bytes to human readable string
*/
formatBytes(bytes) {
if (bytes === 0) return '0 B';
const value = Number(bytes) || 0;
if (!Number.isFinite(value) || value <= 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.min(sizes.length - 1, Math.floor(Math.log(value) / Math.log(k)));
return parseFloat((value / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
},
formatDuration(seconds) {
const totalSeconds = Math.max(0, Math.ceil(Number(seconds) || 0));
if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) return '';
if (totalSeconds < 60) return `${totalSeconds}s`;
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const remainingSeconds = totalSeconds % 60;
if (hours > 0) {
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
}
return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
},
getDownloadEtaText(progress = {}) {
const totalSize = Number(progress.total_size) || 0;
const downloaded = Number(progress.downloaded) || 0;
const speed = Number(progress.speed) || 0;
if (totalSize <= 0 || downloaded <= 0 || speed <= 0 || downloaded >= totalSize) return '';
const duration = this.formatDuration((totalSize - downloaded) / speed);
return duration ? `ETA ${duration}` : '';
},
formatDownloadProgressMeta(progress = {}) {
const parts = [];
const speed = Number(progress.speed) || 0;
if (speed > 0) {
parts.push(`${this.formatBytes(speed)}/s`);
}
const eta = this.getDownloadEtaText(progress);
if (eta) {
parts.push(eta);
}
return parts.join(' | ');
}
};