Multi-Hoster-Upload/lib/throttle.js
Administrator 25b2afbf11 feat: add queue system, per-hoster settings, retry logic, and full UI overhaul
- Add FIFO semaphore for per-hoster concurrency control
- Add token-bucket speed limiter with abort signal support
- Rewrite upload-manager with retry loop, speed monitoring, and rich progress events
- Add per-hoster settings: retries, max speed, parallel count, restart below speed, time interval, max size
- Add context menu with shutdown-after-finish (sleep/shutdown/restart), always-on-top
- Add z-o-o-m-style queue table with 8 columns, status-colored rows, progress bars
- Add debounced queue rendering with scroll position preservation
- Add statusbar with global speed, total bytes, elapsed time
- Fix speedMonitor interval leak on error and scoping bug
- Fix throttle not respecting abort signal during cancellation
- Fix combined signal listener cleanup
- Bump version to 1.1.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 05:57:00 +01:00

43 lines
1.0 KiB
JavaScript

/**
* Token-bucket speed limiter for bandwidth throttling.
* maxBytesPerSec = 0 means unlimited (passthrough).
*/
class Throttle {
constructor(maxBytesPerSec) {
this.maxBps = maxBytesPerSec || 0;
this.tokens = this.maxBps;
this.lastRefill = Date.now();
}
async consume(bytes, signal) {
if (this.maxBps <= 0) return; // unlimited
while (bytes > 0) {
if (signal && signal.aborted) return;
this._refill();
const available = Math.min(bytes, Math.floor(this.tokens));
if (available > 0) {
this.tokens -= available;
bytes -= available;
}
if (bytes > 0) {
// Wait 50ms for tokens to refill
await new Promise((r) => setTimeout(r, 50));
}
}
}
_refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxBps, this.tokens + elapsed * this.maxBps);
this.lastRefill = now;
}
updateRate(maxBytesPerSec) {
this.maxBps = maxBytesPerSec || 0;
}
}
module.exports = Throttle;