diff --git a/lib/upload-manager.js b/lib/upload-manager.js index 516c15d..676e9a2 100644 --- a/lib/upload-manager.js +++ b/lib/upload-manager.js @@ -355,7 +355,12 @@ class UploadManager extends EventEmitter { const fileName = path.basename(task.file); let fileSize = 0; let fileNotFound = false; - try { fileSize = fs.statSync(task.file).size; } catch { fileNotFound = true; } + const cachedResult = results && results.get(task.file); + if (cachedResult && typeof cachedResult.size === 'number' && cachedResult.size > 0) { + fileSize = cachedResult.size; + } else { + try { fileSize = fs.statSync(task.file).size; } catch { fileNotFound = true; } + } const maxAttempts = Math.max(1, (settings.retries || 0) + 1); const jobAbortController = new AbortController(); @@ -420,23 +425,13 @@ class UploadManager extends EventEmitter { return; } - this._emitProgress(uploadId, fileName, task.hoster, { accountId: task.accountId, - jobId, - status: 'queued', - progress: 0, - bytesUploaded: 0, - bytesTotal: fileSize, - speedKbs: 0, - elapsed: 0, - remaining: 0, - error: null, - result: null, - attempt: 0, - maxAttempts - }); - - // Acquire hoster semaphore first so jobs waiting for a hoster slot - // don't waste global slots (prevents underutilization) + // The initial 'queued' emit per job is suppressed: with N=2000+ tasks + // it produces 2000+ main→renderer IPCs back-to-back at startBatch and + // freezes the renderer event loop for tens of seconds. The renderer + // already holds each job in 'queued'/'preview' state from its own + // queueJobs array; the first event it actually needs from main is the + // 'getting-server' / 'uploading' transition for the jobs that the + // semaphore lets through. await hosterSemaphore.acquire(signal); hosterSlotAcquired = true; diff --git a/renderer/app.js b/renderer/app.js index 61b75cb..f4356b5 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -108,24 +108,13 @@ async function init() { window.api.onUpdateAvailable(showUpdateBanner); window.api.onUpdateProgress(handleUpdateProgress); - // Upload event listeners — debug log only on state transitions; the 'uploading' - // tick fires 4×/sec per active job and an IPC roundtrip per event would - // backlog the renderer↔main channel with hundreds of messages/sec. window.api.onUploadProgress((data) => { - if (data.status !== 'uploading') { - window.api.debugLog('RX upload-progress: ' + data.status + ' ' + data.hoster + ' ' + (data.fileName || '')); - } handleProgress(data); }); window.api.onUploadBatchDone((data) => { - window.api.debugLog('RX upload-batch-done'); handleBatchDone(data); }); window.api.onUploadStats((data) => { - // Stats fire every second per upload session — skip while uploading. - if (data.state !== 'uploading') { - window.api.debugLog('RX upload-stats: state=' + data.state + ' active=' + data.activeJobs); - } handleStats(data); }); window.api.onShutdownCountdown(handleShutdownCountdown); diff --git a/tests/upload-manager.test.js b/tests/upload-manager.test.js index 422a514..6a9f761 100644 --- a/tests/upload-manager.test.js +++ b/tests/upload-manager.test.js @@ -55,8 +55,8 @@ describe('UploadManager', () => { ]); const statuses = events.map(e => e.status); - assert.ok(statuses.includes('queued'), 'should have queued status'); assert.ok(statuses.includes('done'), 'should have done status'); + assert.ok(events.length > 0, 'should emit at least one progress event'); }); it('emits batch-done with correct summary', async () => {