Compare commits

..

No commits in common. "edf35e9636b5aff780314d7930b9e77ab1111785" and "5265bcd77ad39128872d7ffe84b8d201d0e50670" have entirely different histories.

3 changed files with 75 additions and 87 deletions

View File

@ -132,12 +132,6 @@ class UploadManager extends EventEmitter {
this.globalSemaphore = null; this.globalSemaphore = null;
this.globalThrottle = null; this.globalThrottle = null;
this.lastStartTime = {}; this.lastStartTime = {};
// Reset account-rotation state each batch. Otherwise a previously failed
// account (e.g. rate-limited during the last batch) stays permanently
// blacklisted until the app restarts — every upload would silently skip
// straight to the fallback even after the original recovered.
this._failedAccounts.clear();
this._accountOverrides.clear();
const { signal } = this.abortController; const { signal } = this.abortController;
const batchId = `batch-${Date.now()}`; const batchId = `batch-${Date.now()}`;
@ -467,17 +461,14 @@ class UploadManager extends EventEmitter {
return; return;
} }
// Account rotation: mark the current account failed, wait for main to // Account fallback: if this account hasn't failed before, try switching
// resolve the next fallback, then retry. Loop so A → B → C → ... works if (task.accountId && !this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
// for hosters with 3+ accounts (the old code only did one level: A → B
// and stopped, even if C would have worked).
while (task.accountId && !this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
if (signal.aborted || this.stopAfterActive) break;
this._failedAccounts.set(task.hoster + ':' + task.accountId, true); this._failedAccounts.set(task.hoster + ':' + task.accountId, true);
this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId }); this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId });
// Wait briefly for switchAccount() to be called from main process
await this._sleep(800, signal); await this._sleep(800, signal);
const override = this._accountOverrides.get(task.hoster); const override = this._accountOverrides.get(task.hoster);
if (!override || this._failedAccounts.has(task.hoster + ':' + override.id)) break; if (override && !this._failedAccounts.has(task.hoster + ':' + override.id)) {
// Switch to fallback account and retry this file // Switch to fallback account and retry this file
task.accountId = override.id; task.accountId = override.id;
task.username = override.username; task.username = override.username;
@ -488,9 +479,7 @@ class UploadManager extends EventEmitter {
speedKbs: 0, elapsed: 0, remaining: 0, speedKbs: 0, elapsed: 0, remaining: 0,
error: 'Account-Wechsel zu Fallback', result: null, attempt: 1, maxAttempts error: 'Account-Wechsel zu Fallback', result: null, attempt: 1, maxAttempts
}); });
// Retry loop with the new account. On exhausted failure, the while // Re-run retry loop with new account
// loop iterates: marks this account failed too, asks main for the next
// fallback, and so on.
for (let attempt = 1; attempt <= maxAttempts; attempt++) { for (let attempt = 1; attempt <= maxAttempts; attempt++) {
if (signal.aborted || this.stopAfterActive) break; if (signal.aborted || this.stopAfterActive) break;
if (attempt > 1) { if (attempt > 1) {
@ -506,8 +495,7 @@ class UploadManager extends EventEmitter {
let lastBytes = 0; let lastBytes = 0;
let lastSpeedTime = jobStart; let lastSpeedTime = jobStart;
let currentSpeedKbs = 0; let currentSpeedKbs = 0;
const activeEntry = { jobId, speedKbs: 0, bytesUploaded: 0 }; this.activeJobs.set(uploadId, { jobId, speedKbs: 0, bytesUploaded: 0 });
this.activeJobs.set(uploadId, activeEntry);
const progressCb = (bytesUploaded, bytesTotal) => { const progressCb = (bytesUploaded, bytesTotal) => {
const now = Date.now(); const now = Date.now();
@ -517,8 +505,7 @@ class UploadManager extends EventEmitter {
lastBytes = bytesUploaded; lastBytes = bytesUploaded;
lastSpeedTime = now; lastSpeedTime = now;
} }
activeEntry.speedKbs = currentSpeedKbs; this.activeJobs.set(uploadId, { jobId, speedKbs: currentSpeedKbs, bytesUploaded });
activeEntry.bytesUploaded = bytesUploaded;
const elapsed = Math.round((now - jobStart) / 1000); const elapsed = Math.round((now - jobStart) / 1000);
const remaining = currentSpeedKbs > 0 ? Math.round((bytesTotal - bytesUploaded) / (currentSpeedKbs * 1024)) : 0; const remaining = currentSpeedKbs > 0 ? Math.round((bytesTotal - bytesUploaded) / (currentSpeedKbs * 1024)) : 0;
this._emitProgress(uploadId, fileName, task.hoster, { this._emitProgress(uploadId, fileName, task.hoster, {
@ -549,6 +536,7 @@ class UploadManager extends EventEmitter {
} }
} }
} }
}
const error = lastError && lastError.message ? lastError.message : 'Unbekannter Fehler'; const error = lastError && lastError.message ? lastError.message : 'Unbekannter Fehler';
emitFinalStatus('error', { error }); emitFinalStatus('error', { error });

View File

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

View File

@ -1062,13 +1062,13 @@ function _onQueueScroll() {
const _collatorDE = new Intl.Collator('de', { sensitivity: 'base', numeric: true }); const _collatorDE = new Intl.Collator('de', { sensitivity: 'base', numeric: true });
const _collatorSimple = new Intl.Collator('de'); const _collatorSimple = new Intl.Collator('de');
// Queue sort memoization. Keys that don't change after a job enters the queue // Queue sort memoization. Keys that don't change during upload (filename, host,
// (filename, host) reuse the cached result across progress-driven re-renders. // size) reuse the cached result across progress-driven re-renders. Dynamic keys
// Dynamic keys (status/speed/progress) AND size (which goes 0 → actual when // (status/speed/progress) are recomputed each call since the sort order itself
// previews resolve / upload starts) are recomputed each call — otherwise a // moves every tick. For a queue of 1000+ jobs sorted by filename, this skips
// queue sorted by size during previews would be stuck in all-zeros order. // the Collator-based O(n log n) sort on every 200ms progress render.
let _queueSortCache = { sig: '', result: [] }; let _queueSortCache = { sig: '', result: [] };
const _STATIC_SORT_KEYS = new Set(['filename', 'host']); const _STATIC_SORT_KEYS = new Set(['filename', 'host', 'size']);
function sortQueueJobs(jobs) { function sortQueueJobs(jobs) {
const { key, direction } = queueSortState; const { key, direction } = queueSortState;