diff --git a/public/index.html b/public/hindex.html similarity index 99% rename from public/index.html rename to public/hindex.html index fdd23d9..545cb9f 100644 --- a/public/index.html +++ b/public/hindex.html @@ -1135,28 +1135,38 @@ async function startUpload() { // Falls back to server-proxied chunked upload if presigned fails. // ============================================================ // Upload speed tracker — smoothed speed + ETA +// Uses monotonically-increasing cumulative bytes with a 15-second sliding +// window so that parallel chunk uploads don't cause wild speed fluctuations. class SpeedTracker { constructor(totalBytes) { this.total = totalBytes; this.startTime = Date.now(); - this.samples = []; // {time, bytes} + this.samples = []; // {time, bytes} — cumulative bytes only this.uploaded = 0; + this._peak = 0; // highest cumulative value seen (monotonic guard) + this._lastSpeed = 0; // cache last good speed for display continuity } update(bytes) { + // Enforce monotonic: with parallel chunks, in-flight progress can + // temporarily report a lower total when a new chunk starts from 0. + if (bytes < this._peak) return; + this._peak = bytes; this.uploaded = bytes; const now = Date.now(); this.samples.push({ time: now, bytes }); - // Keep last 5 seconds of samples for smoothing - const cutoff = now - 5000; + // 15-second sliding window — long enough to smooth out inter-chunk gaps + const cutoff = now - 15000; this.samples = this.samples.filter(s => s.time >= cutoff); } speed() { - if (this.samples.length < 2) return 0; + if (this.samples.length < 2) return this._lastSpeed; const first = this.samples[0]; const last = this.samples[this.samples.length - 1]; const dt = (last.time - first.time) / 1000; - if (dt < 0.5) return 0; - return (last.bytes - first.bytes) / dt; + if (dt < 1) return this._lastSpeed; // need >= 1 s of data + const s = (last.bytes - first.bytes) / dt; + if (s > 0) this._lastSpeed = s; + return this._lastSpeed; } eta() { const s = this.speed();