fix(vidmoly): use new /api/upload/config endpoint

The Vidmoly SPA redesign removed the /?op=upload HTML form — the old
regex-scrape of hidden inputs no longer works. The site now exposes
GET /api/upload/config which returns { sess_id, upload_url } plus the
allowed extensions. Rewrote getUploadParams() to use that endpoint;
the rest of the multipart upload flow (sess_id + utype + file_0) is
the same classic XFS shape.
This commit is contained in:
Administrator 2026-04-19 22:24:10 +02:00
parent 8e49733241
commit 0e7ae5ee7b

View File

@ -111,75 +111,43 @@ class VidmolyUploader {
throw new Error('Vidmoly Login fehlgeschlagen: Falscher Username oder Passwort'); throw new Error('Vidmoly Login fehlgeschlagen: Falscher Username oder Passwort');
} }
// Verify session by fetching a logged-in-only page. Vidmoly redesigned // Verify session by hitting the new SPA dashboard at /my. On the old XFS
// the site and changed cookie names, so the old "has cookie 'login' or // site the login page included a <form name="password">; the SPA dashboard
// 'xfsts'" heuristic broke. Instead we hit /?op=my_account (or similar) // doesn't contain that form when the session is valid (it's rendered by
// and confirm we're not redirected to the login form. // JS), so the absence of a password field + presence of any known marker
// (Dashboard / Logout / the user's own username) indicates we're in.
if (this.cookies.size === 0) { 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 verifyRes = await this._fetch(`${BASE_URL}/my`);
const verifyBody = await verifyRes.text(); const verifyBody = await verifyRes.text();
// A logged-out page shows the login form (name="login" + name="password"); const hasPasswordForm = /<form[^>]*>[\s\S]{0,800}?name=["']password["']/i.test(verifyBody);
// a logged-in page shows account info ("Logout", "My Account", "My Files"). const hasDashboardMarker = /(?:Dashboard|Video[- ]Manager|Logout|dashboard|my[_ -]?(account|files))/i.test(verifyBody);
const looksLoggedIn = /(?:logout|my[_ ]?account|my[_ ]?files)/i.test(verifyBody) if (hasPasswordForm || !hasDashboardMarker) {
&& !/<form[^>]*>\s*[\s\S]{0,500}?name=["']password["']/i.test(verifyBody);
if (!looksLoggedIn) {
throw new Error('Vidmoly Login fehlgeschlagen: Session konnte nicht verifiziert werden'); throw new Error('Vidmoly Login fehlgeschlagen: Session konnte nicht verifiziert werden');
} }
} }
/** /**
* Get upload form parameters from the upload page * Fetch the upload session config from Vidmoly's new SPA API.
* Replaces the old HTML-form scrape at /?op=upload which the redesign
* removed. Returns an XFS-style session token + a transit-server URL.
*/ */
async getUploadParams() { async getUploadParams() {
const res = await this._fetch(`${BASE_URL}/?op=upload`); const res = await this._fetch(`${BASE_URL}/api/upload/config`);
const html = await res.text(); const body = await res.text();
let payload = null;
// Parse hidden form fields from XFS upload form try { payload = JSON.parse(body); } catch {
const params = {}; throw new Error('Vidmoly: /api/upload/config lieferte kein JSON — evtl. nicht eingeloggt?');
const inputRegex = /<input[^>]*type=["']hidden["'][^>]*>/gi;
let match;
while ((match = inputRegex.exec(html)) !== null) {
const tag = match[0];
const nameMatch = tag.match(/name=["']([^"']+)["']/);
const valueMatch = tag.match(/value=["']([^"']*?)["']/);
if (nameMatch) {
params[nameMatch[1]] = valueMatch ? valueMatch[1] : '';
} }
if (!payload || !payload.sess_id || !payload.upload_url) {
throw new Error('Vidmoly: /api/upload/config unvollständig (sess_id/upload_url fehlt)');
} }
return {
// Extract form action uploadUrl: payload.upload_url,
const formMatch = html.match(/<form[^>]*id=["']?file_upload["']?[^>]*action=["']([^"']+)["']/i) params: { sess_id: payload.sess_id, utype: 'reg' },
|| html.match(/<form[^>]*enctype=["']multipart\/form-data["'][^>]*action=["']([^"']+)["']/i) fileFieldName: 'file_0'
|| html.match(/<form[^>]*action=["']([^"']+)["'][^>]*enctype=["']multipart\/form-data["']/i); };
let uploadUrl = null;
if (formMatch) {
uploadUrl = formMatch[1];
} else if (params.srv_tmp_url) {
uploadUrl = params.srv_tmp_url;
}
if (!uploadUrl) {
const cgiMatch = html.match(/(https?:\/\/[^"'\s]+\/cgi-bin\/upload\.cgi[^"'\s]*)/i)
|| html.match(/(https?:\/\/[^"'\s]+\/upload\/\d+)/i);
if (cgiMatch) uploadUrl = cgiMatch[1];
}
if (!uploadUrl) {
throw new Error('Vidmoly Upload-URL nicht gefunden. Bist du eingeloggt?');
}
let fileFieldName = 'file';
const fileInputMatch = html.match(/<input[^>]*type=["']file["'][^>]*name=["']([^"']+)["']/i)
|| html.match(/<input[^>]*name=["']([^"']+)["'][^>]*type=["']file["']/i);
if (fileInputMatch && fileInputMatch[1]) {
fileFieldName = fileInputMatch[1].trim();
}
return { uploadUrl, params, fileFieldName };
} }
/** /**