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();