diff --git a/lib/doodstream-upload.js b/lib/doodstream-upload.js index b672405..aac1baa 100644 --- a/lib/doodstream-upload.js +++ b/lib/doodstream-upload.js @@ -78,7 +78,7 @@ class DoodstreamUploader { /** * Login to DoodStream via web form */ - async login(username, password) { + async login(username, password, otp) { // GET homepage first to collect cookies const homeRes = await this._fetch(BASE_URL); await homeRes.text(); @@ -88,7 +88,7 @@ class DoodstreamUploader { op: 'login_ajax', login: username, password: password, - loginotp: '' + loginotp: otp || '' }); // Use raw fetch with redirect: 'manual' to detect success redirects @@ -122,6 +122,11 @@ class DoodstreamUploader { if (json && json.status === 'success') { // Explicit success response + } else if (json && json.message && /otp/i.test(json.message)) { + // OTP required — signal caller to collect OTP from user + const err = new Error(`Doodstream Login: ${json.message}`); + err.otpRequired = true; + throw err; } else if (json && json.status === 'fail') { throw new Error(`Doodstream Login: ${json.message || 'Login fehlgeschlagen'}`); } else if (body.includes('Dashboard')) { diff --git a/main.js b/main.js index 8334ee8..cc61fa9 100644 --- a/main.js +++ b/main.js @@ -188,7 +188,7 @@ function buildUploadTasksFromJobs(config, jobs) { }); } -async function checkDoodstreamHealth(hosterConfig) { +async function checkDoodstreamHealth(hosterConfig, otp) { const username = hosterConfig && hosterConfig.username ? String(hosterConfig.username).trim() : ''; @@ -199,7 +199,14 @@ async function checkDoodstreamHealth(hosterConfig) { // Login-based check (preferred) if (username && password) { const uploader = new DoodstreamUploader(); - await uploader.login(username, password); + try { + await uploader.login(username, password, otp || undefined); + } catch (err) { + if (err.otpRequired) { + return { status: 'otp_required', message: err.message || 'OTP erforderlich' }; + } + throw err; + } return { status: 'ok', message: 'Login ok, Upload-Seite bereit' }; } @@ -402,7 +409,7 @@ async function runHosterHealthCheck(config, requestedChecks) { checks = requestedChecks; } - const results = await Promise.all(checks.map(async ({ hoster, accountId }) => { + const results = await Promise.all(checks.map(async ({ hoster, accountId, otp }) => { if (!allowed.includes(hoster)) { return { hoster, accountId, status: 'skipped', message: 'Kein Health-Check fuer diesen Hoster' }; } @@ -414,7 +421,7 @@ async function runHosterHealthCheck(config, requestedChecks) { try { let result; if (hoster === 'doodstream.com') { - result = await withTimeout(checkDoodstreamHealth(hosterConfig), HEALTH_CHECK_TIMEOUT, 'Doodstream-Check'); + result = await withTimeout(checkDoodstreamHealth(hosterConfig, otp), HEALTH_CHECK_TIMEOUT, 'Doodstream-Check'); } else if (hoster === 'vidmoly.me') { result = await withTimeout(checkVidmolyHealth(hosterConfig), HEALTH_CHECK_TIMEOUT, 'Vidmoly-Check'); } else if (hoster === 'voe.sx') { diff --git a/renderer/app.js b/renderer/app.js index 53dec2b..8534435 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -2627,6 +2627,7 @@ function openAccountModal(editAccountId) { function closeAccountModal() { document.getElementById('accountModal').style.display = 'none'; + _hideOtpField(); editingAccountId = null; } @@ -2734,15 +2735,30 @@ async function saveAccount() { renderHosterModal(); renderSettings(); - // Run health check for this specific account + // Check if OTP was entered (for retry after OTP prompt) + const otpInput = document.getElementById('accField_otp'); + const otp = otpInput ? otpInput.value.trim() : ''; + + // Run health check for this specific account (include OTP if provided) + const checkPayload = { hoster: hosterName, accountId }; + if (otp) checkPayload.otp = otp; + try { - const result = await window.api.runHealthCheck({ hosters: [{ hoster: hosterName, accountId }] }); + const result = await window.api.runHealthCheck({ hosters: [checkPayload] }); const rows = result && Array.isArray(result.results) ? result.results : []; const row = rows.find(r => r.accountId === accountId); - if (row && (row.status === 'ok' || row.status === 'warn')) { + if (row && row.status === 'otp_required') { + // Show OTP input field if not already visible + accountStatuses[accountId] = { status: 'error', message: row.message || 'OTP erforderlich' }; + statusEl.textContent = row.message || 'OTP wurde an deine E-Mail gesendet.'; + statusEl.className = 'account-modal-status error'; + _showOtpField(); + saveBtn.textContent = 'OTP bestätigen'; + } else if (row && (row.status === 'ok' || row.status === 'warn')) { accountStatuses[accountId] = { status: row.status || 'ok', message: row.message || '' }; statusEl.textContent = row.status === 'warn' ? row.message || 'Prüfung mit Warnung abgeschlossen.' : 'Login erfolgreich!'; statusEl.className = 'account-modal-status ok'; + _hideOtpField(); setTimeout(() => closeAccountModal(), 1200); } else { const msg = (row && row.message) || 'Login fehlgeschlagen'; @@ -2764,6 +2780,24 @@ async function saveAccount() { } } +function _showOtpField() { + if (document.getElementById('accField_otp')) return; // already visible + const container = document.getElementById('accountCredsFields'); + const otpHtml = ` +