fix: vidmoly login verification + retry stale-uploadId + faster account toggle

Three fixes bundled:

  - Vidmoly redesign broke login: the old check required either the
    'login' or 'xfsts' cookie, but the new site sets different cookie
    names. Now we verify by fetching /?op=my_account and looking for
    logged-in markers (Logout / My Account / My Files) in the body
    instead of relying on specific cookie names.

  - retrySelectedJobs left the stale uploadId in _jobIndexByUploadId
    when resetting a job. A late 'aborted'/'error' event from the
    original (cancelled) upload could route back to the reset job
    and overwrite its 'preview' state. Now the old uploadId is
    removed from the index and marked in _deletedJobIds so those
    stragglers get dropped.

  - toggleAccount did two IPC round-trips (saveConfig + getConfig) on
    every enable/disable click, plus four re-renders (Accounts,
    HosterSummary, HosterModal, Settings). Rapid clicks felt laggy.
    The getConfig refetch is redundant since we mutated the flag in
    place, and HosterModal/Settings don't depend on account enabled
    state. Click now renders immediately and the save runs async.
This commit is contained in:
Administrator 2026-04-19 22:08:22 +02:00
parent 976be2f566
commit 2dc94084ab
2 changed files with 33 additions and 10 deletions

View File

@ -111,12 +111,22 @@ class VidmolyUploader {
throw new Error('Vidmoly Login fehlgeschlagen: Falscher Username oder Passwort'); throw new Error('Vidmoly Login fehlgeschlagen: Falscher Username oder Passwort');
} }
// Check for login cookie // Verify session by fetching a logged-in-only page. Vidmoly redesigned
const hasSession = this.cookies.has('login') || this.cookies.has('xfsts') || // the site and changed cookie names, so the old "has cookie 'login' or
this.cookies.size > 1; // 'xfsts'" heuristic broke. Instead we hit /?op=my_account (or similar)
if (!hasSession) { // and confirm we're not redirected to the login form.
if (this.cookies.size === 0) {
throw new Error('Vidmoly Login fehlgeschlagen: Keine Session erhalten'); throw new Error('Vidmoly Login fehlgeschlagen: Keine Session erhalten');
} }
const verifyRes = await this._fetch(`${BASE_URL}/?op=my_account`);
const verifyBody = await verifyRes.text();
// A logged-out page shows the login form (name="login" + name="password");
// a logged-in page shows account info ("Logout", "My Account", "My Files").
const looksLoggedIn = /(?:logout|my[_ ]?account|my[_ ]?files)/i.test(verifyBody)
&& !/<form[^>]*>\s*[\s\S]{0,500}?name=["']password["']/i.test(verifyBody);
if (!looksLoggedIn) {
throw new Error('Vidmoly Login fehlgeschlagen: Session konnte nicht verifiziert werden');
}
} }
/** /**

View File

@ -1886,8 +1886,19 @@ function handleStats(data) {
// --- Retry --- // --- Retry ---
async function retrySelectedJobs() { async function retrySelectedJobs() {
const retryJobs = []; const retryJobs = [];
// Build a Set for O(1) selectedFiles dedup below.
const existingFilePaths = new Set();
for (const f of selectedFiles) existingFilePaths.add(f.path);
queueJobs.forEach(j => { queueJobs.forEach(j => {
if (selectedJobIds.has(j.id) && ['error', 'done', 'aborted', 'skipped'].includes(j.status)) { if (selectedJobIds.has(j.id) && ['error', 'done', 'aborted', 'skipped'].includes(j.status)) {
// Invalidate the old uploadId: retire the index entry and mark it so
// any late progress event from the previous (cancelled/completed)
// upload can't overwrite the freshly-reset state.
if (j.uploadId) {
_jobIndexByUploadId.delete(j.uploadId);
_deletedJobIds.add(j.uploadId);
}
j.status = uploading ? 'queued' : 'preview'; j.status = uploading ? 'queued' : 'preview';
j.error = null; j.error = null;
j.result = null; j.result = null;
@ -1898,8 +1909,9 @@ async function retrySelectedJobs() {
j.progress = 0; j.progress = 0;
j.uploadId = null; j.uploadId = null;
retryJobs.push(j); retryJobs.push(j);
if (!selectedFiles.find(f => f.path === j.file || f.name === j.fileName)) { if (!existingFilePaths.has(j.file)) {
selectedFiles.push({ path: j.file, name: j.fileName, size: j.bytesTotal }); selectedFiles.push({ path: j.file, name: j.fileName, size: j.bytesTotal });
existingFilePaths.add(j.file);
} }
} }
}); });
@ -2868,14 +2880,15 @@ function setupAccountDragReorder(container) {
async function toggleAccount(accountId) { async function toggleAccount(accountId) {
const found = findAccountById(accountId); const found = findAccountById(accountId);
if (!found) return; if (!found) return;
found.account.enabled = found.account.enabled === false ? true : false; // Flip in place; re-render immediately so the UI responds before the
await window.api.saveConfig({ hosters: config.hosters }); // disk write completes. The saveConfig is fire-and-forget-ish here because
config = await window.api.getConfig(); // we already know the new state locally — a full getConfig round-trip
// after every toggle made rapid enable/disable clicks feel laggy.
found.account.enabled = !found.account.enabled;
syncSelectedUploadHosters(); syncSelectedUploadHosters();
renderAccounts(); renderAccounts();
renderHosterSummary(); renderHosterSummary();
renderHosterModal(); try { await window.api.saveConfig({ hosters: config.hosters }); } catch {}
renderSettings();
} }
async function checkSingleAccount(accountId) { async function checkSingleAccount(accountId) {