diff --git a/lib/upload-manager.js b/lib/upload-manager.js index acc1fb4..f03d2c8 100644 --- a/lib/upload-manager.js +++ b/lib/upload-manager.js @@ -43,46 +43,6 @@ class UploadManager extends EventEmitter { this._accountOverrides = new Map(); // hoster -> fallback account object this._doodApiKeyCache = new Map(); // accountId/username -> derived doodstream API key ('' = tried, none) this._baselineCache = new Map(); // hoster:apiKey -> Promise> (one fetch shared across all jobs in batch) - this._networkOnline = true; - this._networkWaiters = []; - } - - setNetworkOnline(online) { - const next = !!online; - if (next === this._networkOnline) return; - this._networkOnline = next; - if (next) { - const waiters = this._networkWaiters.splice(0); - for (const resolve of waiters) { try { resolve(); } catch {} } - this._rotLog('network-online', { releasedWaiters: waiters.length }); - } else { - this._rotLog('network-offline', {}); - } - } - - isNetworkOnline() { - return this._networkOnline; - } - - _waitForNetwork(signal) { - if (this._networkOnline) return Promise.resolve(); - if (signal && signal.aborted) return Promise.reject(new Error('Abgebrochen')); - return new Promise((resolve, reject) => { - let settled = false; - const onResume = () => { - if (settled) return; - settled = true; - if (signal) signal.removeEventListener('abort', onAbort); - resolve(); - }; - const onAbort = () => { - if (settled) return; - settled = true; - reject(new Error('Abgebrochen')); - }; - this._networkWaiters.push(onResume); - if (signal) signal.addEventListener('abort', onAbort, { once: true }); - }); } switchAccount(hoster, fallbackAccount) { @@ -545,12 +505,6 @@ class UploadManager extends EventEmitter { for (let attempt = 1; attempt <= maxAttempts; attempt++) { if (signal.aborted || this.stopAfterActive) break; - if (!this._networkOnline) { - this._rotLog('network-wait', { jobId, hoster: task.hoster, fileName, attempt }); - await this._waitForNetwork(signal); - if (signal.aborted || this.stopAfterActive) break; - } - if (attempt > 1) { this._emitProgress(uploadId, fileName, task.hoster, { accountId: task.accountId, jobId, @@ -719,18 +673,6 @@ class UploadManager extends EventEmitter { } lastError = err; - // Network outage in progress: the failure is the outage, not the - // file or the account. Hold the job until the gate reopens and - // retry WITHOUT consuming an attempt — a 30-minute ISP flake must - // not burn through the whole retry budget. - if (this._isTransientNetworkError(err) && !this._networkOnline) { - this._rotLog('network-hold', { - jobId, hoster: task.hoster, fileName, accountId: task.accountId, attempt - }); - attempt--; - try { await this._waitForNetwork(signal); } catch { lastError = new Error('Abgebrochen'); break; } - continue; - } // File-specific rejection — re-uploading won't change the server's // mind. Break out immediately; the outer file-rejected branch then // records the final error without burning through 5 × 3s retries. diff --git a/main.js b/main.js index 6d06bf9..eea8e61 100644 --- a/main.js +++ b/main.js @@ -229,57 +229,6 @@ function rotLog(msg, ts) { } catch {} } -const NET_CHECK_HOSTS = ['one.one.one.one', 'dns.google']; -let _netMonitorTimer = null; -let _netOnline = true; -let _netFails = 0; -let _netOks = 0; -let _netHostIdx = 0; - -function _dnsProbe(host) { - return new Promise((resolve) => { - const timer = setTimeout(() => resolve(false), 5000); - try { - require('dns').resolve(host, (err) => { clearTimeout(timer); resolve(!err); }); - } catch { clearTimeout(timer); resolve(false); } - }); -} - -async function _netCheckTick() { - const host = NET_CHECK_HOSTS[_netHostIdx++ % NET_CHECK_HOSTS.length]; - const ok = await _dnsProbe(host); - if (ok) { _netOks++; _netFails = 0; } else { _netFails++; _netOks = 0; } - if (_netOnline && _netFails >= 2) { - _netOnline = false; - debugLog('network-monitor: OFFLINE (2 consecutive DNS probe failures)'); - rotLog('network-monitor: offline — holding job starts + retries'); - if (uploadManager && typeof uploadManager.setNetworkOnline === 'function') uploadManager.setNetworkOnline(false); - safeSend('network-status', { online: false }); - } else if (!_netOnline && _netOks >= 2) { - _netOnline = true; - debugLog('network-monitor: ONLINE again (2 consecutive DNS probe successes)'); - rotLog('network-monitor: online — resuming held jobs'); - if (uploadManager && typeof uploadManager.setNetworkOnline === 'function') uploadManager.setNetworkOnline(true); - safeSend('network-status', { online: true }); - } -} - -function startNetworkMonitor() { - if (_netMonitorTimer) return; - _netOnline = true; _netFails = 0; _netOks = 0; - _netMonitorTimer = setInterval(() => { _netCheckTick().catch(() => {}); }, 8000); - debugLog('network-monitor: started (8s probe interval)'); -} - -function stopNetworkMonitor() { - if (_netMonitorTimer) { clearInterval(_netMonitorTimer); _netMonitorTimer = null; } - if (!_netOnline && uploadManager && typeof uploadManager.setNetworkOnline === 'function') { - uploadManager.setNetworkOnline(true); - } - _netOnline = true; - debugLog('network-monitor: stopped'); -} - function _sleepMs(ms) { return new Promise((r) => setTimeout(r, ms)); } async function _postWebhookWithRetry(req, maxAttempts) { @@ -1265,7 +1214,6 @@ app.on('window-all-closed', () => { app.on('before-quit', () => { if (uploadManager) try { uploadManager.cancel(); } catch {} - try { stopNetworkMonitor(); } catch {} try { folderMonitor.stop(); } catch {} try { if (remoteServer) { remoteServer.stop(); remoteServer = null; } @@ -1733,7 +1681,6 @@ ipcMain.handle('start-upload', (_event, payload) => { debugLog(`batch-done: total=${summary.total} ok=${summary.succeeded} fail=${summary.failed}`); logMarker('BATCH END', { total: summary.total, ok: summary.succeeded, fail: summary.failed }); logMemorySnapshot('batch-done'); - stopNetworkMonitor(); const _batchDurationSec = _thisManager && _thisManager.startTime ? Math.round((Date.now() - _thisManager.startTime) / 1000) : 0; @@ -1763,13 +1710,11 @@ ipcMain.handle('start-upload', (_event, payload) => { process.nextTick(() => { if (!uploadManager) { debugLog('nextTick: uploadManager was nulled before startBatch'); return; } debugLog(`nextTick: calling startBatch now (priming ${_sessionFailedAccounts.size} failed accounts, ${_sessionAccountOverrides.size} overrides from session)`); - startNetworkMonitor(); uploadManager.startBatch(tasks, { primeFailedAccounts: Array.from(_sessionFailedAccounts.keys()), primeOverrides: Array.from(_sessionAccountOverrides.entries()) }).catch((err) => { debugLog(`startBatch REJECTED: ${err && err.stack ? err.stack : err}`); - stopNetworkMonitor(); const errorSummary = { id: 'error', timestamp: new Date().toISOString(), diff --git a/preload.js b/preload.js index 142bb25..b80a328 100644 --- a/preload.js +++ b/preload.js @@ -120,9 +120,6 @@ contextBridge.exposeInMainWorld('api', { resetAllSessionFailedAccounts: () => ipcRenderer.invoke('reset-all-session-failed-accounts'), getLogPaths: () => ipcRenderer.invoke('get-log-paths'), testWebhook: (payload) => ipcRenderer.invoke('test-webhook', payload), - onNetworkStatus: (callback) => { - ipcRenderer.on('network-status', (_event, data) => callback(data)); - }, revealLogFile: (target) => ipcRenderer.invoke('reveal-log-file', target), setLogVerbose: (enabled) => ipcRenderer.invoke('set-log-verbose', enabled), createSupportBundle: () => ipcRenderer.invoke('create-support-bundle'), diff --git a/renderer/app.js b/renderer/app.js index 26ed0f9..5883d80 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -140,18 +140,6 @@ async function init() { handleStats(data); }); window.api.onShutdownCountdown(handleShutdownCountdown); - if (window.api.onNetworkStatus) { - window.api.onNetworkStatus((data) => { - if (!data || typeof data !== 'object') return; - _networkOffline = !data.online; - if (_networkOffline) { - showCopyToast('Netzwerk-Ausfall erkannt — Uploads pausiert bis die Verbindung zurück ist.', 10000); - } else { - showCopyToast('Netzwerk wieder verfügbar — Uploads werden fortgesetzt.', 6000); - } - updateStatusBar(); - }); - } window.api.onUploadLogFallback((data) => { const path = data && data.fallbackPath ? data.fallbackPath : '(Fallback)'; showCopyToast(`Log-Pfad nicht beschreibbar — schreibe nach: ${path}`, 8000); @@ -2173,7 +2161,6 @@ function handleBatchDone(summary) { } let _sessionFailedKeys = new Set(); -let _networkOffline = false; const _autoRetryState = { round: 0, timer: null }; function _cancelAutoRetry(resetRound) { @@ -2631,15 +2618,13 @@ function updateStatusBar() { ? Math.round(stats.bytesRemaining / (lastUploadStats.globalSpeedKbs * 1024)) : 0; - const stateText = (_networkOffline && (uploading || lastUploadStats.state === 'uploading')) - ? 'Netzwerk-Ausfall — pausiert' - : lastUploadStats.state === 'uploading' - ? 'Upload läuft...' - : lastUploadStats.state === 'stopping' - ? 'Stoppt nach aktiven Uploads...' - : uploading - ? 'Upload vorbereitet...' - : 'Bereit'; + const stateText = lastUploadStats.state === 'uploading' + ? 'Upload läuft...' + : lastUploadStats.state === 'stopping' + ? 'Stoppt nach aktiven Uploads...' + : uploading + ? 'Upload vorbereitet...' + : 'Bereit'; document.getElementById('sbState').textContent = stateText; document.getElementById('sbSpeed').textContent = formatSpeed(lastUploadStats.globalSpeedKbs || 0); diff --git a/tests/upload-manager.test.js b/tests/upload-manager.test.js index e4e5a06..7c10273 100644 --- a/tests/upload-manager.test.js +++ b/tests/upload-manager.test.js @@ -46,49 +46,6 @@ describe('UploadManager', () => { UploadManager = require('../lib/upload-manager'); }); - it('network gate: _waitForNetwork resolves immediately when online', async () => { - const mgr = new UploadManager({}); - assert.strictEqual(mgr.isNetworkOnline(), true); - await mgr._waitForNetwork(); - }); - - it('network gate: waiters block while offline and release on setNetworkOnline(true)', async () => { - const mgr = new UploadManager({}); - mgr.setNetworkOnline(false); - assert.strictEqual(mgr.isNetworkOnline(), false); - let resolved = false; - const waiter = mgr._waitForNetwork().then(() => { resolved = true; }); - await new Promise(r => setTimeout(r, 30)); - assert.strictEqual(resolved, false, 'must still be blocked while offline'); - mgr.setNetworkOnline(true); - await waiter; - assert.strictEqual(resolved, true); - }); - - it('network gate: abort signal rejects a pending waiter', async () => { - const mgr = new UploadManager({}); - mgr.setNetworkOnline(false); - const ac = new AbortController(); - const waiter = mgr._waitForNetwork(ac.signal); - ac.abort(); - await assert.rejects(waiter, /Abgebrochen/); - }); - - it('network gate: batch with offline gate holds queued job until resume', async () => { - const mgr = new UploadManager({}); - mgr.setNetworkOnline(false); - const statuses = []; - mgr.on('progress', (d) => statuses.push(d.status)); - const batch = mgr.startBatch([ - { file: '/test/video1.mp4', hoster: 'doodstream.com', apiKey: 'key1' } - ]); - await new Promise(r => setTimeout(r, 60)); - assert.ok(!statuses.includes('done'), 'job must not complete while gate is closed'); - mgr.setNetworkOnline(true); - await batch; - assert.ok(statuses.includes('done'), 'job completes after gate reopens'); - }); - it('emits progress events for each task', async () => { const mgr = new UploadManager({}); const events = [];