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 <noreply@anthropic.com>
This commit is contained in:
parent
d94156943b
commit
b8b8158abb
@ -84,23 +84,45 @@ class DoodstreamUploader {
|
|||||||
loginotp: ''
|
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',
|
method: 'POST',
|
||||||
body: loginData.toString(),
|
body: loginData.toString(),
|
||||||
headers: {
|
headers,
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
redirect: 'manual'
|
||||||
'Referer': BASE_URL + '/',
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await res.text();
|
this._parseCookiesFromHeaders(res.headers);
|
||||||
let json;
|
|
||||||
try { json = JSON.parse(body); } catch { json = null; }
|
|
||||||
|
|
||||||
if (!json || json.status !== 'success') {
|
// On successful login, server may redirect (3xx) to dashboard
|
||||||
const msg = (json && json.message) || 'Login fehlgeschlagen';
|
if ([301, 302, 303, 307, 308].includes(res.status)) {
|
||||||
throw new Error(`Doodstream Login: ${msg}`);
|
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
|
// Extract sess_id from the upload page
|
||||||
@ -111,14 +133,21 @@ class DoodstreamUploader {
|
|||||||
const res = await this._fetch(BASE_URL + '/?op=upload');
|
const res = await this._fetch(BASE_URL + '/?op=upload');
|
||||||
const html = await res.text();
|
const html = await res.text();
|
||||||
|
|
||||||
// Look for sess_id in the page (Vue component prop or hidden field)
|
// Hidden input: <input type="hidden" name="sess_id" value="xxx">
|
||||||
|
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]+)['"]/);
|
const sessMatch = html.match(/sess_id['":\s]+['"]([a-zA-Z0-9]+)['"]/);
|
||||||
if (sessMatch) {
|
if (sessMatch) {
|
||||||
this.sessId = sessMatch[1];
|
this.sessId = sessMatch[1];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternative: look in script or data attributes
|
// Assignment: sess_id = 'xxx'
|
||||||
const altMatch = html.match(/sess_id\s*=\s*['"]([a-zA-Z0-9]+)['"]/);
|
const altMatch = html.match(/sess_id\s*=\s*['"]([a-zA-Z0-9]+)['"]/);
|
||||||
if (altMatch) {
|
if (altMatch) {
|
||||||
this.sessId = altMatch[1];
|
this.sessId = altMatch[1];
|
||||||
|
|||||||
@ -193,10 +193,19 @@ function closeHosterModal() {
|
|||||||
function applyHosterSelection() {
|
function applyHosterSelection() {
|
||||||
selectedUploadHosters = Array.from(document.querySelectorAll('input[data-hoster-modal]:checked'))
|
selectedUploadHosters = Array.from(document.querySelectorAll('input[data-hoster-modal]:checked'))
|
||||||
.map(input => input.dataset.hosterModal);
|
.map(input => input.dataset.hosterModal);
|
||||||
|
// Move pending files to selectedFiles on confirm
|
||||||
|
if (_pendingFiles.length > 0) {
|
||||||
|
selectedFiles.push(..._pendingFiles);
|
||||||
|
_pendingFiles = [];
|
||||||
|
}
|
||||||
renderHosterSummary();
|
renderHosterSummary();
|
||||||
if (!uploading && selectedFiles.length > 0) buildQueuePreview();
|
updateUploadView();
|
||||||
updateStartButton();
|
|
||||||
persistQueueStateSoon();
|
persistQueueStateSoon();
|
||||||
|
document.getElementById('hosterModal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelHosterModal() {
|
||||||
|
_pendingFiles = [];
|
||||||
closeHosterModal();
|
closeHosterModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,34 +346,36 @@ function setupDragDrop() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _pendingFiles = []; // Files waiting for hoster modal confirmation
|
||||||
|
|
||||||
function addDroppedFiles(fileList) {
|
function addDroppedFiles(fileList) {
|
||||||
let added = 0;
|
|
||||||
const files = Array.from(fileList);
|
const files = Array.from(fileList);
|
||||||
|
const newFiles = [];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!selectedFiles.find(f => f.path === file.path)) {
|
if (!selectedFiles.find(f => f.path === file.path) && !_pendingFiles.find(f => f.path === file.path)) {
|
||||||
selectedFiles.push({ path: file.path, name: file.name, size: file.size });
|
newFiles.push({ path: file.path, name: file.name, size: file.size });
|
||||||
added++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUploadView();
|
if (newFiles.length > 0) {
|
||||||
persistQueueStateSoon();
|
_pendingFiles.push(...newFiles);
|
||||||
if (added > 0) openHosterModal();
|
openHosterModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pickFiles() {
|
async function pickFiles() {
|
||||||
const paths = await window.api.selectFiles();
|
const paths = await window.api.selectFiles();
|
||||||
if (!paths) return;
|
if (!paths) return;
|
||||||
let added = 0;
|
const newFiles = [];
|
||||||
for (const p of paths) {
|
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();
|
const name = p.split('\\').pop().split('/').pop();
|
||||||
selectedFiles.push({ path: p, name, size: null }); // size resolved by upload-manager
|
newFiles.push({ path: p, name, size: null });
|
||||||
added++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUploadView();
|
if (newFiles.length > 0) {
|
||||||
persistQueueStateSoon();
|
_pendingFiles.push(...newFiles);
|
||||||
if (added > 0) openHosterModal();
|
openHosterModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUploadView() {
|
function updateUploadView() {
|
||||||
@ -675,7 +686,7 @@ document.addEventListener('click', (e) => {
|
|||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
hideContextMenu();
|
hideContextMenu();
|
||||||
closeHosterModal();
|
cancelHosterModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1517,8 +1528,8 @@ function setupListeners() {
|
|||||||
retrySelectedJobs();
|
retrySelectedJobs();
|
||||||
});
|
});
|
||||||
document.getElementById('confirmHosterModalBtn').addEventListener('click', applyHosterSelection);
|
document.getElementById('confirmHosterModalBtn').addEventListener('click', applyHosterSelection);
|
||||||
document.getElementById('cancelHosterModalBtn').addEventListener('click', closeHosterModal);
|
document.getElementById('cancelHosterModalBtn').addEventListener('click', cancelHosterModal);
|
||||||
document.getElementById('closeHosterModalBtn').addEventListener('click', closeHosterModal);
|
document.getElementById('closeHosterModalBtn').addEventListener('click', cancelHosterModal);
|
||||||
document.getElementById('selectAllHostersBtn').addEventListener('click', () => {
|
document.getElementById('selectAllHostersBtn').addEventListener('click', () => {
|
||||||
document.querySelectorAll('input[data-hoster-modal]').forEach(input => {
|
document.querySelectorAll('input[data-hoster-modal]').forEach(input => {
|
||||||
input.checked = true;
|
input.checked = true;
|
||||||
@ -1577,7 +1588,7 @@ function setupListeners() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('hosterModal').addEventListener('click', (e) => {
|
document.getElementById('hosterModal').addEventListener('click', (e) => {
|
||||||
if (e.target.id === 'hosterModal') closeHosterModal();
|
if (e.target.id === 'hosterModal') cancelHosterModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Account management
|
// Account management
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user