Compare commits

...

2 Commits

Author SHA1 Message Date
Administrator
22356864c3 release: v3.1.7 2026-04-21 19:33:57 +02:00
Administrator
058c8a2674 perf(renderer): coalesce status-change UI updates into one rAF frame
Non-uploading progress events (queued/getting-server/retrying/done/
error/aborted/skipped) were firing renderQueueTable +
updateQueueActionButtons + updateStatusBar + updateStatsPanel
synchronously on EVERY event. At batch start, 500 jobs going
preview→queued→getting-server within milliseconds meant ~2000 sync DOM
updates — visible jank on large batches.

New scheduleStatusChangeUpdate() uses requestAnimationFrame to coalesce
the four-helper call into at most one run per frame (~60 Hz). Functional
result is identical; the user just sees smooth flips instead of a
briefly frozen renderer.

The uploading-progress throttle (200ms) is unchanged since those events
are much more frequent and the user doesn't need 60 Hz upload-byte
updates.
2026-04-21 19:33:33 +02:00
2 changed files with 22 additions and 6 deletions

View File

@ -1,6 +1,6 @@
{
"name": "multi-hoster-uploader",
"version": "3.1.6",
"version": "3.1.7",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js",
"scripts": {

View File

@ -918,6 +918,23 @@ function scheduleThrottledUIUpdate() {
}, UI_UPDATE_INTERVAL);
}
// Coalesces status-change updates (done/error/retrying/queued/…) into one
// frame. Without this, a batch of 500 jobs flipping queued→getting-server
// →uploading synchronously fires 1500+ updateStatusBar/Buttons/Stats calls
// and janks the renderer. rAF caps it to ~60 Hz.
let _statusChangeUpdateQueued = false;
function scheduleStatusChangeUpdate() {
if (_statusChangeUpdateQueued) return;
_statusChangeUpdateQueued = true;
requestAnimationFrame(() => {
_statusChangeUpdateQueued = false;
renderQueueTable();
updateQueueActionButtons();
updateStatusBar();
updateStatsPanel();
});
}
function buildRowHtml(job) {
const statusClass = `status-${job.status}`;
const rowClass = `queue-row ${statusClass}${selectedJobIds.has(job.id) ? ' selected' : ''}`;
@ -1859,14 +1876,13 @@ function handleProgress(data) {
queueJobs = queueJobs.filter(j => j !== job);
}
// Status changes (done/error/etc) get immediate render; ongoing progress is throttled
// Status changes (done/error/etc) get one coalesced update per frame so a
// burst of 500 parallel jobs flipping state doesn't fire 2000 sync DOM
// updates. Ongoing uploading progress is throttled at 200ms.
if (data.status === 'uploading') {
scheduleThrottledUIUpdate();
} else {
scheduleQueueRender();
updateQueueActionButtons();
updateStatusBar();
updateStatsPanel();
scheduleStatusChangeUpdate();
}
persistQueueStateSoon();
}