display upload speed and ETA

This commit is contained in:
xenofem 2022-05-23 19:59:05 -04:00
parent 8f00eb27e2
commit 8b5e9b76bb
2 changed files with 53 additions and 3 deletions

View file

@ -1,5 +1,7 @@
const FILE_CHUNK_SIZE = 16384; const FILE_CHUNK_SIZE = 16384;
const MAX_FILES = 256; const MAX_FILES = 256;
const SAMPLE_WINDOW = 100;
const STALL_THRESHOLD = 1;
let files = []; let files = [];
@ -8,6 +10,7 @@ let fileIndex = 0;
let byteIndex = 0; let byteIndex = 0;
let bytesSent = 0; let bytesSent = 0;
let totalBytes = 0; let totalBytes = 0;
let timestamps = [];
let maxSize = null; let maxSize = null;
@ -156,6 +159,7 @@ function beginUpload() {
fileIndex = 0; fileIndex = 0;
byteIndex = 0; byteIndex = 0;
bytesSent = 0; bytesSent = 0;
timestamps = [];
let websocketUrl = new URL('upload', window.location); let websocketUrl = new URL('upload', window.location);
websocketUrl.protocol = (window.location.protocol === 'http:') ? 'ws:' : 'wss:'; websocketUrl.protocol = (window.location.protocol === 'http:') ? 'ws:' : 'wss:';
@ -249,6 +253,16 @@ function sendData() {
socket.send(data); socket.send(data);
byteIndex = endpoint; byteIndex = endpoint;
bytesSent += data.size; bytesSent += data.size;
// It's ok if the monotonically increasing fields like
// percentage are updating super quickly, but it's awkward for
// rate and ETA
const now = Date.now() / 1000;
if (timestamps.length === 0 || now - timestamps.at(-1)[0] > 1) {
timestamps.push([now, bytesSent]);
if (timestamps.length > SAMPLE_WINDOW) { timestamps.shift(); }
}
updateProgress(); updateProgress();
} else { } else {
fileIndex += 1; fileIndex += 1;
@ -264,7 +278,22 @@ function updateProgress() {
} else { } else {
percentage = `${(bytesSent*100/totalBytes).toFixed(1)}%`; percentage = `${(bytesSent*100/totalBytes).toFixed(1)}%`;
} }
progress.textContent = `${percentage} (${displaySize(bytesSent)}/${displaySize(totalBytes)})`; let progressString = `${percentage} - ${displaySize(bytesSent)}/${displaySize(totalBytes)}`;
if (timestamps.length >= 2) {
const start = timestamps.at(0);
const end = timestamps.at(-1);
if (end[0] - start[0] > 0) {
const rate = (end[1] - start[1])/(end[0] - start[0]);
if (rate > STALL_THRESHOLD) {
// Use the value from timestamps rather than bytesSent to avoid awkward UI thrashing
const remaining = (totalBytes - end[1]) / rate;
progressString += ` - ${displaySize(rate)}/s - ${displayTime(remaining)} remaining`;
} else {
progressString += " - stalled";
}
}
}
progress.textContent = progressString;
progressBar.style.backgroundSize = percentage; progressBar.style.backgroundSize = percentage;
const fileEntries = Array.from(fileList.children); const fileEntries = Array.from(fileList.children);

View file

@ -1,14 +1,35 @@
const UNITS = [ const SIZE_UNITS = [
{ name: 'GB', size: Math.pow(10, 9) }, { name: 'GB', size: Math.pow(10, 9) },
{ name: 'MB', size: Math.pow(10, 6) }, { name: 'MB', size: Math.pow(10, 6) },
{ name: 'KB', size: Math.pow(10, 3) }, { name: 'KB', size: Math.pow(10, 3) },
]; ];
function displaySize(bytes) { function displaySize(bytes) {
for (const unit of UNITS) { for (const unit of SIZE_UNITS) {
if (bytes >= unit.size) { if (bytes >= unit.size) {
return `${(bytes / unit.size).toFixed(1)}${unit.name}`; return `${(bytes / unit.size).toFixed(1)}${unit.name}`;
} }
} }
return `${bytes}B`; return `${bytes}B`;
} }
const TIME_UNITS = [
{ name: 'd', size: 86400 },
{ name: 'h', size: 3600 },
{ name: 'm', size: 60 },
{ name: 's', size: 1 },
];
function displayTime(seconds) {
let result = "";
for (const unit of TIME_UNITS) {
if (seconds >= unit.size || result !== "") {
result += `${Math.floor(seconds / unit.size)}${unit.name}`;
seconds = seconds % unit.size;
}
}
if (result === "") {
result = "0s";
}
return result;
}