From b8b8158abbcf16fc95ef8c038423739b6414c590 Mon Sep 17 00:00:00 2001 From: Administrator Date: Wed, 11 Mar 2026 01:36:50 +0100 Subject: [PATCH] fix: doodstream login redirect handling + queue only adds files after confirm - Fix doodstream login: handle redirect on success (server returns HTML dashboard instead of JSON) - Fix sess_id extraction: match hidden input field format - Files are now only added to queue after clicking "Uebernehmen" in hoster modal - Cancel/Escape/click-outside discards pending files Co-Authored-By: Claude Opus 4.6 --- lib/doodstream-upload.js | 57 ++++++++++++++++++++++++++++++---------- renderer/app.js | 51 +++++++++++++++++++++-------------- 2 files changed, 74 insertions(+), 34 deletions(-) diff --git a/lib/doodstream-upload.js b/lib/doodstream-upload.js index c2d3d9c..aa908ac 100644 --- a/lib/doodstream-upload.js +++ b/lib/doodstream-upload.js @@ -84,23 +84,45 @@ class DoodstreamUploader { loginotp: '' }); - const res = await this._fetch(BASE_URL + '/', { + // Use raw fetch with redirect: 'manual' to detect success redirects + const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Referer': BASE_URL + '/', + 'X-Requested-With': 'XMLHttpRequest', + 'User-Agent': USER_AGENT + }; + if (this.cookies.size > 0) { + headers['Cookie'] = this._cookieHeader(); + } + + const res = await fetch(BASE_URL + '/', { method: 'POST', body: loginData.toString(), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Referer': BASE_URL + '/', - 'X-Requested-With': 'XMLHttpRequest' - } + headers, + redirect: 'manual' }); - const body = await res.text(); - let json; - try { json = JSON.parse(body); } catch { json = null; } + this._parseCookiesFromHeaders(res.headers); - if (!json || json.status !== 'success') { - const msg = (json && json.message) || 'Login fehlgeschlagen'; - throw new Error(`Doodstream Login: ${msg}`); + // On successful login, server may redirect (3xx) to dashboard + if ([301, 302, 303, 307, 308].includes(res.status)) { + try { await res.text(); } catch {} + // Redirect means login succeeded + } else { + const body = await res.text(); + let json; + try { json = JSON.parse(body); } catch { json = null; } + + if (json && json.status === 'success') { + // Explicit success response + } else if (json && json.status === 'fail') { + throw new Error(`Doodstream Login: ${json.message || 'Login fehlgeschlagen'}`); + } else if (body.includes('Dashboard')) { + // Got dashboard HTML directly — login worked + } else { + const msg = (json && json.message) || 'Login fehlgeschlagen'; + throw new Error(`Doodstream Login: ${msg}`); + } } // Extract sess_id from the upload page @@ -111,14 +133,21 @@ class DoodstreamUploader { const res = await this._fetch(BASE_URL + '/?op=upload'); const html = await res.text(); - // Look for sess_id in the page (Vue component prop or hidden field) + // Hidden input: + const hiddenMatch = html.match(/name=["']sess_id["'][^>]*value=["']([a-zA-Z0-9]+)["']/); + if (hiddenMatch) { + this.sessId = hiddenMatch[1]; + return; + } + + // Vue component prop or JS: sess_id: "xxx" or sess_id="xxx" const sessMatch = html.match(/sess_id['":\s]+['"]([a-zA-Z0-9]+)['"]/); if (sessMatch) { this.sessId = sessMatch[1]; return; } - // Alternative: look in script or data attributes + // Assignment: sess_id = 'xxx' const altMatch = html.match(/sess_id\s*=\s*['"]([a-zA-Z0-9]+)['"]/); if (altMatch) { this.sessId = altMatch[1]; diff --git a/renderer/app.js b/renderer/app.js index 9dc3908..b8d590d 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -193,10 +193,19 @@ function closeHosterModal() { function applyHosterSelection() { selectedUploadHosters = Array.from(document.querySelectorAll('input[data-hoster-modal]:checked')) .map(input => input.dataset.hosterModal); + // Move pending files to selectedFiles on confirm + if (_pendingFiles.length > 0) { + selectedFiles.push(..._pendingFiles); + _pendingFiles = []; + } renderHosterSummary(); - if (!uploading && selectedFiles.length > 0) buildQueuePreview(); - updateStartButton(); + updateUploadView(); persistQueueStateSoon(); + document.getElementById('hosterModal').style.display = 'none'; +} + +function cancelHosterModal() { + _pendingFiles = []; closeHosterModal(); } @@ -337,34 +346,36 @@ function setupDragDrop() { }); } +let _pendingFiles = []; // Files waiting for hoster modal confirmation + function addDroppedFiles(fileList) { - let added = 0; const files = Array.from(fileList); + const newFiles = []; for (const file of files) { - if (!selectedFiles.find(f => f.path === file.path)) { - selectedFiles.push({ path: file.path, name: file.name, size: file.size }); - added++; + if (!selectedFiles.find(f => f.path === file.path) && !_pendingFiles.find(f => f.path === file.path)) { + newFiles.push({ path: file.path, name: file.name, size: file.size }); } } - updateUploadView(); - persistQueueStateSoon(); - if (added > 0) openHosterModal(); + if (newFiles.length > 0) { + _pendingFiles.push(...newFiles); + openHosterModal(); + } } async function pickFiles() { const paths = await window.api.selectFiles(); if (!paths) return; - let added = 0; + const newFiles = []; for (const p of paths) { - if (!selectedFiles.find(f => f.path === p)) { + if (!selectedFiles.find(f => f.path === p) && !_pendingFiles.find(f => f.path === p)) { const name = p.split('\\').pop().split('/').pop(); - selectedFiles.push({ path: p, name, size: null }); // size resolved by upload-manager - added++; + newFiles.push({ path: p, name, size: null }); } } - updateUploadView(); - persistQueueStateSoon(); - if (added > 0) openHosterModal(); + if (newFiles.length > 0) { + _pendingFiles.push(...newFiles); + openHosterModal(); + } } function updateUploadView() { @@ -675,7 +686,7 @@ document.addEventListener('click', (e) => { document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { hideContextMenu(); - closeHosterModal(); + cancelHosterModal(); } }); @@ -1517,8 +1528,8 @@ function setupListeners() { retrySelectedJobs(); }); document.getElementById('confirmHosterModalBtn').addEventListener('click', applyHosterSelection); - document.getElementById('cancelHosterModalBtn').addEventListener('click', closeHosterModal); - document.getElementById('closeHosterModalBtn').addEventListener('click', closeHosterModal); + document.getElementById('cancelHosterModalBtn').addEventListener('click', cancelHosterModal); + document.getElementById('closeHosterModalBtn').addEventListener('click', cancelHosterModal); document.getElementById('selectAllHostersBtn').addEventListener('click', () => { document.querySelectorAll('input[data-hoster-modal]').forEach(input => { input.checked = true; @@ -1577,7 +1588,7 @@ function setupListeners() { }); document.getElementById('hosterModal').addEventListener('click', (e) => { - if (e.target.id === 'hosterModal') closeHosterModal(); + if (e.target.id === 'hosterModal') cancelHosterModal(); }); // Account management