Compare commits
No commits in common. "796aeb520dedda932b3c4740911561b431837f2e" and "9b5184f76fbc058c944083a3d795abb136697a5f" have entirely different histories.
796aeb520d
...
9b5184f76f
@ -43,17 +43,7 @@ class UploadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switchAccount(hoster, fallbackAccount) {
|
switchAccount(hoster, fallbackAccount) {
|
||||||
const prev = this._accountOverrides.get(hoster);
|
|
||||||
this._accountOverrides.set(hoster, fallbackAccount);
|
this._accountOverrides.set(hoster, fallbackAccount);
|
||||||
this._rotLog('switchAccount', {
|
|
||||||
hoster,
|
|
||||||
prevOverrideId: prev ? prev.id : null,
|
|
||||||
toAccountId: fallbackAccount ? fallbackAccount.id : null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_rotLog(event, data) {
|
|
||||||
this.emit('rot-log', { ts: Date.now(), event, ...data });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSettings(hosterSettings, globalSettings) {
|
updateSettings(hosterSettings, globalSettings) {
|
||||||
@ -148,7 +138,6 @@ class UploadManager extends EventEmitter {
|
|||||||
// straight to the fallback even after the original recovered.
|
// straight to the fallback even after the original recovered.
|
||||||
this._failedAccounts.clear();
|
this._failedAccounts.clear();
|
||||||
this._accountOverrides.clear();
|
this._accountOverrides.clear();
|
||||||
this._rotLog('batch-start', { taskCount: tasks.length });
|
|
||||||
|
|
||||||
const { signal } = this.abortController;
|
const { signal } = this.abortController;
|
||||||
const batchId = `batch-${Date.now()}`;
|
const batchId = `batch-${Date.now()}`;
|
||||||
@ -273,19 +262,10 @@ class UploadManager extends EventEmitter {
|
|||||||
if (task.accountId && this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
|
if (task.accountId && this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
|
||||||
const override = this._accountOverrides.get(task.hoster);
|
const override = this._accountOverrides.get(task.hoster);
|
||||||
if (override && !this._failedAccounts.has(task.hoster + ':' + override.id)) {
|
if (override && !this._failedAccounts.has(task.hoster + ':' + override.id)) {
|
||||||
this._rotLog('pre-job-swap', {
|
|
||||||
hoster: task.hoster, fileName, fromAccountId: task.accountId, toAccountId: override.id
|
|
||||||
});
|
|
||||||
task.accountId = override.id;
|
task.accountId = override.id;
|
||||||
task.username = override.username;
|
task.username = override.username;
|
||||||
task.password = override.password;
|
task.password = override.password;
|
||||||
task.apiKey = override.apiKey;
|
task.apiKey = override.apiKey;
|
||||||
} else {
|
|
||||||
this._rotLog('pre-job-swap-blocked', {
|
|
||||||
hoster: task.hoster, fileName, accountId: task.accountId,
|
|
||||||
hasOverride: !!override,
|
|
||||||
overrideAlsoFailed: override ? this._failedAccounts.has(task.hoster + ':' + override.id) : false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,39 +471,14 @@ class UploadManager extends EventEmitter {
|
|||||||
// resolve the next fallback, then retry. Loop so A → B → C → ... works
|
// resolve the next fallback, then retry. Loop so A → B → C → ... works
|
||||||
// for hosters with 3+ accounts (the old code only did one level: A → B
|
// for hosters with 3+ accounts (the old code only did one level: A → B
|
||||||
// and stopped, even if C would have worked).
|
// and stopped, even if C would have worked).
|
||||||
this._rotLog('retries-exhausted', {
|
|
||||||
hoster: task.hoster, fileName, accountId: task.accountId,
|
|
||||||
lastError: lastError ? lastError.message : null
|
|
||||||
});
|
|
||||||
while (task.accountId && !this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
|
while (task.accountId && !this._failedAccounts.has(task.hoster + ':' + task.accountId)) {
|
||||||
if (signal.aborted || this.stopAfterActive) break;
|
if (signal.aborted || this.stopAfterActive) break;
|
||||||
this._failedAccounts.set(task.hoster + ':' + task.accountId, true);
|
this._failedAccounts.set(task.hoster + ':' + task.accountId, true);
|
||||||
this._rotLog('mark-failed', {
|
|
||||||
hoster: task.hoster, fileName, accountId: task.accountId,
|
|
||||||
lastError: lastError ? lastError.message : null
|
|
||||||
});
|
|
||||||
this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId });
|
this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId });
|
||||||
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) {
|
if (!override || this._failedAccounts.has(task.hoster + ':' + override.id)) break;
|
||||||
this._rotLog('rotation-end', {
|
|
||||||
hoster: task.hoster, fileName, reason: 'no-override-set',
|
|
||||||
lastFailedAccountId: task.accountId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (this._failedAccounts.has(task.hoster + ':' + override.id)) {
|
|
||||||
this._rotLog('rotation-end', {
|
|
||||||
hoster: task.hoster, fileName, reason: 'override-already-failed',
|
|
||||||
overrideId: override.id, lastFailedAccountId: task.accountId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Switch to fallback account and retry this file
|
// Switch to fallback account and retry this file
|
||||||
this._rotLog('rotate', {
|
|
||||||
hoster: task.hoster, fileName,
|
|
||||||
fromAccountId: task.accountId, toAccountId: override.id
|
|
||||||
});
|
|
||||||
task.accountId = override.id;
|
task.accountId = override.id;
|
||||||
task.username = override.username;
|
task.username = override.username;
|
||||||
task.password = override.password;
|
task.password = override.password;
|
||||||
@ -596,9 +551,6 @@ class UploadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const error = lastError && lastError.message ? lastError.message : 'Unbekannter Fehler';
|
const error = lastError && lastError.message ? lastError.message : 'Unbekannter Fehler';
|
||||||
this._rotLog('final-error', {
|
|
||||||
hoster: task.hoster, fileName, lastFailedAccountId: task.accountId, error
|
|
||||||
});
|
|
||||||
emitFinalStatus('error', { error });
|
emitFinalStatus('error', { error });
|
||||||
recordFinalResult('error', { error });
|
recordFinalResult('error', { error });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
73
main.js
73
main.js
@ -68,58 +68,6 @@ function debugLog(msg) {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dedicated account-rotation log so users can trace fallback decisions
|
|
||||||
// without wading through general debug output. Writes to account-rotation.log
|
|
||||||
// in the same directory as fileuploader.log (honors user's configured path).
|
|
||||||
function getRotLogPath() {
|
|
||||||
const base = getLogFilePath();
|
|
||||||
const dir = path.dirname(base);
|
|
||||||
return path.join(dir, 'account-rotation.log');
|
|
||||||
}
|
|
||||||
const _rotLogBuffer = [];
|
|
||||||
let _rotLogFlushTimer = null;
|
|
||||||
let _rotLogWriting = false;
|
|
||||||
|
|
||||||
function _flushRotLog() {
|
|
||||||
if (_rotLogWriting || _rotLogBuffer.length === 0) return;
|
|
||||||
const chunk = _rotLogBuffer.join('');
|
|
||||||
_rotLogBuffer.length = 0;
|
|
||||||
_rotLogWriting = true;
|
|
||||||
const tryTargets = [
|
|
||||||
getRotLogPath(),
|
|
||||||
path.join(app.getPath('desktop') || app.getPath('userData'), 'account-rotation.log'),
|
|
||||||
path.join(app.getPath('userData'), 'account-rotation.log')
|
|
||||||
];
|
|
||||||
const write = (i) => {
|
|
||||||
if (i >= tryTargets.length) { _rotLogWriting = false; return; }
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(path.dirname(tryTargets[i]), { recursive: true });
|
|
||||||
} catch {}
|
|
||||||
fs.appendFile(tryTargets[i], chunk, 'utf-8', (err) => {
|
|
||||||
if (err) return write(i + 1);
|
|
||||||
_rotLogWriting = false;
|
|
||||||
if (_rotLogBuffer.length) setImmediate(_flushRotLog);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
write(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function rotLog(msg, ts) {
|
|
||||||
try {
|
|
||||||
const iso = new Date(ts || Date.now()).toISOString();
|
|
||||||
const line = `[${iso}] ${msg}\n`;
|
|
||||||
_rotLogBuffer.push(line);
|
|
||||||
// Mirror into the main debug log for single-file-grep convenience.
|
|
||||||
_debugLogBuffer.push(`[${iso}] [ROT] ${msg}\n`);
|
|
||||||
if (!_rotLogFlushTimer) {
|
|
||||||
_rotLogFlushTimer = setTimeout(() => { _rotLogFlushTimer = null; _flushRotLog(); }, 500);
|
|
||||||
}
|
|
||||||
if (!_debugLogFlushTimer) {
|
|
||||||
_debugLogFlushTimer = setTimeout(() => { _debugLogFlushTimer = null; _flushDebugLog(); }, 500);
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catch unhandled rejections from fire-and-forget async calls
|
// Catch unhandled rejections from fire-and-forget async calls
|
||||||
process.on('unhandledRejection', (reason) => {
|
process.on('unhandledRejection', (reason) => {
|
||||||
debugLog(`UNHANDLED REJECTION: ${reason && reason.stack ? reason.stack : reason}`);
|
debugLog(`UNHANDLED REJECTION: ${reason && reason.stack ? reason.stack : reason}`);
|
||||||
@ -854,12 +802,6 @@ app.on('before-quit', () => {
|
|||||||
_uploadLogBuffer.length = 0;
|
_uploadLogBuffer.length = 0;
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
|
||||||
if (_rotLogBuffer.length) {
|
|
||||||
fs.appendFileSync(getRotLogPath(), _rotLogBuffer.join(''), 'utf-8');
|
|
||||||
_rotLogBuffer.length = 0;
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- IPC Handlers ---
|
// --- IPC Handlers ---
|
||||||
@ -1077,24 +1019,13 @@ ipcMain.handle('start-upload', (_event, payload) => {
|
|||||||
const cfg = configStore.load();
|
const cfg = configStore.load();
|
||||||
const fallback = getNextFallbackAccount(cfg, hoster, accountId);
|
const fallback = getNextFallbackAccount(cfg, hoster, accountId);
|
||||||
if (fallback) {
|
if (fallback) {
|
||||||
rotLog(`main: account-failed ${hoster} ${accountId} → resolved fallback ${fallback.id}`);
|
debugLog(`account-failed: ${hoster} ${accountId} → fallback to ${fallback.id}`);
|
||||||
uploadManager.switchAccount(hoster, fallback);
|
uploadManager.switchAccount(hoster, fallback);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('account-switched', { hoster, fromAccountId: accountId, toAccountId: fallback.id });
|
mainWindow.webContents.send('account-switched', { hoster, fromAccountId: accountId, toAccountId: fallback.id });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rotLog(`main: account-failed ${hoster} ${accountId} → NO fallback available (end of chain)`);
|
debugLog(`account-failed: ${hoster} ${accountId} → no fallback available`);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uploadManager.on('rot-log', (entry) => {
|
|
||||||
const { ts, event, ...rest } = entry;
|
|
||||||
const pairs = Object.entries(rest)
|
|
||||||
.map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)
|
|
||||||
.join(' ');
|
|
||||||
rotLog(`[${event}] ${pairs}`, ts);
|
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
||||||
mainWindow.webContents.send('account-rotation-log', entry);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "3.0.3",
|
"version": "3.0.2",
|
||||||
"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": {
|
||||||
|
|||||||
@ -104,9 +104,6 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
onUploadLogFallback: (callback) => {
|
onUploadLogFallback: (callback) => {
|
||||||
ipcRenderer.on('upload-log-fallback', (_event, data) => callback(data));
|
ipcRenderer.on('upload-log-fallback', (_event, data) => callback(data));
|
||||||
},
|
},
|
||||||
onAccountRotationLog: (callback) => {
|
|
||||||
ipcRenderer.on('account-rotation-log', (_event, data) => callback(data));
|
|
||||||
},
|
|
||||||
// Remote Control
|
// Remote Control
|
||||||
remoteGetSettings: () => ipcRenderer.invoke('remote:get-settings'),
|
remoteGetSettings: () => ipcRenderer.invoke('remote:get-settings'),
|
||||||
remoteSaveSettings: (settings) => ipcRenderer.invoke('remote:save-settings', settings),
|
remoteSaveSettings: (settings) => ipcRenderer.invoke('remote:save-settings', settings),
|
||||||
|
|||||||
@ -120,19 +120,6 @@ async function init() {
|
|||||||
window.api.onUploadLogFallback((data) => {
|
window.api.onUploadLogFallback((data) => {
|
||||||
alert('Der konfigurierte Log-Pfad konnte nicht beschrieben werden.\n\nNeue Einträge werden zwischenzeitlich hier gespeichert:\n' + (data && data.fallbackPath ? data.fallbackPath : '(Fallback)') + '\n\nBitte in den Einstellungen einen gültigen Pfad setzen.');
|
alert('Der konfigurierte Log-Pfad konnte nicht beschrieben werden.\n\nNeue Einträge werden zwischenzeitlich hier gespeichert:\n' + (data && data.fallbackPath ? data.fallbackPath : '(Fallback)') + '\n\nBitte in den Einstellungen einen gültigen Pfad setzen.');
|
||||||
});
|
});
|
||||||
window.api.onAccountRotationLog((entry) => {
|
|
||||||
// Surface only the user-visible rotation events as toasts; full detail
|
|
||||||
// goes to account-rotation.log. Keep it quiet otherwise.
|
|
||||||
if (!entry || !entry.event) return;
|
|
||||||
const hosterLabel = entry.hoster ? getHosterLabel(entry.hoster) : '';
|
|
||||||
if (entry.event === 'rotate') {
|
|
||||||
showCopyToast(`${hosterLabel}: Account-Wechsel → Fallback`);
|
|
||||||
} else if (entry.event === 'rotation-end') {
|
|
||||||
showCopyToast(`${hosterLabel}: Keine weiteren Fallback-Accounts verfügbar`);
|
|
||||||
} else if (entry.event === 'final-error') {
|
|
||||||
showCopyToast(`${hosterLabel}: Alle Accounts ausgeschöpft`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Folder monitor: auto-queue new files
|
// Folder monitor: auto-queue new files
|
||||||
window.api.onFolderMonitorNewFiles((files) => {
|
window.api.onFolderMonitorNewFiles((files) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user