/* ── Downloads page ─────────────────────────────────────────── */ function escH(s) { return String(s).replace(/&/g,'&').replace(//g,'>'); } function fmtSpeed(b) { return b>1048576?(b/1048576).toFixed(1)+' MB/s':b>1024?(b/1024).toFixed(0)+' KB/s':b+' B/s'; } function fmtEta(s) { if (s < 0) return ''; if (s < 60) return s + 's'; if (s < 3600) return Math.floor(s/60) + 'm ' + (s%60) + 's'; return Math.floor(s/3600) + 'h ' + Math.floor((s%3600)/60) + 'm'; } function renderDownloads() { fetch(BASE + '/api/downloads', {cache: 'no-store'}) .then(function(r) { return r.json(); }) .then(function(dl) { var el = document.getElementById('dl-table'); if (!dl.length) { el.innerHTML = '
No downloads yet.
'; return; } var html = '| Name | Status | Progress | Speed | ETA | ||
|---|---|---|---|---|---|---|
| ' + thumb + ' | ' + escH(d.name) + ' | ' + escH(d.status) + ' | '; if (d.status === 'downloading') html += ' ' + pct + '% (' + mb + 'MB)'; else html += pct + '%'; html += ' | ' + (d.status==='downloading'&&d.speed_bps>0 ? fmtSpeed(d.speed_bps) : '—') + ' | '; html += '' + (d.status==='downloading'&&d.eta_s>=0 ? fmtEta(d.eta_s) : '—') + ' | '; if (d.status==='downloading' || d.status==='queued') html += ''; html += ' |