fix(voe): use 2-step CDN upload flow + add health check
This commit is contained in:
parent
71e9232269
commit
34fbfcb016
@ -141,7 +141,7 @@ class VoeUploader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the upload page and extract CSRF token + any upload params
|
* Get the upload page and extract CSRF token
|
||||||
*/
|
*/
|
||||||
async _getUploadParams() {
|
async _getUploadParams() {
|
||||||
const res = await this._fetch(`${BASE_URL}/file-upload`);
|
const res = await this._fetch(`${BASE_URL}/file-upload`);
|
||||||
@ -155,6 +155,28 @@ class VoeUploader {
|
|||||||
return { csrfToken };
|
return { csrfToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get upload server URL from /engine/delivery-node
|
||||||
|
* Returns { server: "https://cdn-xxx.edgeon-bandwidth.com/node/u/01", session_id: "..." }
|
||||||
|
*/
|
||||||
|
async _getDeliveryNode(csrfToken) {
|
||||||
|
const res = await this._fetch(`${BASE_URL}/engine/delivery-node`, {
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const body = await res.text();
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.server) {
|
||||||
|
throw new Error('VOE: Kein Upload-Server erhalten von delivery-node');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { uploadServer: data.server, sessionId: data.session_id || '' };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List current files via VOE API (for result polling fallback)
|
* List current files via VOE API (for result polling fallback)
|
||||||
*/
|
*/
|
||||||
@ -182,18 +204,29 @@ class VoeUploader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload a file to VOE.sx via login session
|
* Upload a file to VOE.sx via login session
|
||||||
|
* Flow: GET delivery-node → POST file to CDN server
|
||||||
*/
|
*/
|
||||||
async upload(filePath, onProgress, signal, throttle) {
|
async upload(filePath, onProgress, signal, throttle) {
|
||||||
const fileName = path.basename(filePath);
|
const fileName = path.basename(filePath);
|
||||||
const fileSize = fs.statSync(filePath).size;
|
const fileSize = fs.statSync(filePath).size;
|
||||||
const baselineCodes = await this._captureFileCodes();
|
const baselineCodes = await this._captureFileCodes();
|
||||||
|
|
||||||
|
// Step 1: Get CSRF token from upload page
|
||||||
const { csrfToken } = await this._getUploadParams();
|
const { csrfToken } = await this._getUploadParams();
|
||||||
|
|
||||||
|
// Step 2: Get CDN upload server from delivery-node
|
||||||
|
const { uploadServer, sessionId } = await this._getDeliveryNode(csrfToken);
|
||||||
|
|
||||||
const boundary = '----FormBoundary' + crypto.randomBytes(16).toString('hex');
|
const boundary = '----FormBoundary' + crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
// Build multipart body
|
// Build multipart body
|
||||||
let preamble = '';
|
let preamble = '';
|
||||||
|
// Include session_id if provided
|
||||||
|
if (sessionId) {
|
||||||
|
preamble += `--${boundary}\r\n`;
|
||||||
|
preamble += `Content-Disposition: form-data; name="session_id"\r\n\r\n`;
|
||||||
|
preamble += `${sessionId}\r\n`;
|
||||||
|
}
|
||||||
preamble += `--${boundary}\r\n`;
|
preamble += `--${boundary}\r\n`;
|
||||||
preamble += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
|
preamble += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
|
||||||
preamble += `Content-Type: application/octet-stream\r\n\r\n`;
|
preamble += `Content-Type: application/octet-stream\r\n\r\n`;
|
||||||
@ -219,10 +252,8 @@ class VoeUploader {
|
|||||||
yield epilogueBuf;
|
yield epilogueBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST to /engine/delivery-node
|
// Step 3: POST file to CDN upload server
|
||||||
const uploadUrl = `${BASE_URL}/engine/delivery-node`;
|
const { body, statusCode, headers } = await request(uploadServer, {
|
||||||
|
|
||||||
const { body, statusCode, headers } = await request(uploadUrl, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: generate(),
|
body: generate(),
|
||||||
signal,
|
signal,
|
||||||
|
|||||||
54
main.js
54
main.js
@ -202,8 +202,51 @@ async function checkVidmolyHealth(hosterConfig) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkVoeHealth(hosterConfig) {
|
||||||
|
const username = hosterConfig && hosterConfig.username
|
||||||
|
? String(hosterConfig.username).trim()
|
||||||
|
: '';
|
||||||
|
const password = hosterConfig && hosterConfig.password
|
||||||
|
? String(hosterConfig.password).trim()
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
// Fall back to API key check if no login
|
||||||
|
const apiKey = hosterConfig && hosterConfig.apiKey
|
||||||
|
? String(hosterConfig.apiKey).trim()
|
||||||
|
: '';
|
||||||
|
if (!apiKey) {
|
||||||
|
return { status: 'error', message: 'Login oder API Key fehlt' };
|
||||||
|
}
|
||||||
|
// Quick API check
|
||||||
|
const res = await fetch(`https://voe.sx/api/upload/server?key=${encodeURIComponent(apiKey)}`, { method: 'GET' });
|
||||||
|
const data = await res.json().catch(() => null);
|
||||||
|
if (data && data.result && typeof data.result === 'string' && /^https?:\/\//i.test(data.result.trim())) {
|
||||||
|
return { status: 'ok', message: 'API Key gueltig, Upload-Server verfuegbar' };
|
||||||
|
}
|
||||||
|
const msg = data && (data.msg || data.message) ? String(data.msg || data.message).trim() : '';
|
||||||
|
if (/no servers/i.test(msg)) {
|
||||||
|
return { status: 'warn', message: 'API Key gueltig, aktuell kein Server verfuegbar' };
|
||||||
|
}
|
||||||
|
return { status: 'error', message: msg || 'API Key ungueltig oder Server nicht erreichbar' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploader = new VoeUploader();
|
||||||
|
await uploader.login(username, password);
|
||||||
|
const { csrfToken } = await uploader._getUploadParams();
|
||||||
|
|
||||||
|
if (!csrfToken) {
|
||||||
|
return { status: 'error', message: 'Login ok, aber Upload-Seite liefert kein CSRF-Token' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'ok',
|
||||||
|
message: 'Login ok, Upload-Seite bereit'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function runHosterHealthCheck(config, requestedHosters) {
|
async function runHosterHealthCheck(config, requestedHosters) {
|
||||||
const allowed = ['doodstream.com', 'vidmoly.me'];
|
const allowed = ['doodstream.com', 'vidmoly.me', 'voe.sx'];
|
||||||
const source = Array.isArray(requestedHosters) && requestedHosters.length > 0
|
const source = Array.isArray(requestedHosters) && requestedHosters.length > 0
|
||||||
? requestedHosters
|
? requestedHosters
|
||||||
: allowed;
|
: allowed;
|
||||||
@ -238,6 +281,15 @@ async function runHosterHealthCheck(config, requestedHosters) {
|
|||||||
return { hoster, ...result };
|
return { hoster, ...result };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hoster === 'voe.sx') {
|
||||||
|
const result = await withTimeout(
|
||||||
|
checkVoeHealth(hosterConfig),
|
||||||
|
HEALTH_CHECK_TIMEOUT,
|
||||||
|
'VOE-Check'
|
||||||
|
);
|
||||||
|
return { hoster, ...result };
|
||||||
|
}
|
||||||
|
|
||||||
return { hoster, status: 'skipped', message: 'Kein Health-Check fuer diesen Hoster' };
|
return { hoster, status: 'skipped', message: 'Kein Health-Check fuer diesen Hoster' };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -652,7 +652,7 @@ async function startUpload() {
|
|||||||
|
|
||||||
// Auto health check
|
// Auto health check
|
||||||
if (autoHealthCheckEnabled) {
|
if (autoHealthCheckEnabled) {
|
||||||
const checkHosters = hosters.filter(name => name === 'doodstream.com' || name === 'vidmoly.me');
|
const checkHosters = hosters.filter(name => name === 'doodstream.com' || name === 'vidmoly.me' || name === 'voe.sx');
|
||||||
if (checkHosters.length > 0) {
|
if (checkHosters.length > 0) {
|
||||||
healthCheckRunning = true;
|
healthCheckRunning = true;
|
||||||
try {
|
try {
|
||||||
@ -850,9 +850,9 @@ async function executeHealthCheck(hosters, mode) {
|
|||||||
|
|
||||||
async function runHealthCheck() {
|
async function runHealthCheck() {
|
||||||
if (healthCheckRunning || uploading) return;
|
if (healthCheckRunning || uploading) return;
|
||||||
const hosters = getSelectedHosters().filter(n => n === 'doodstream.com' || n === 'vidmoly.me');
|
const hosters = getSelectedHosters().filter(n => n === 'doodstream.com' || n === 'vidmoly.me' || n === 'voe.sx');
|
||||||
if (hosters.length === 0) {
|
if (hosters.length === 0) {
|
||||||
const allHosters = ['doodstream.com', 'vidmoly.me'].filter(n => hosterHasCredentials(n, config.hosters[n] || {}));
|
const allHosters = ['doodstream.com', 'vidmoly.me', 'voe.sx'].filter(n => hosterHasCredentials(n, config.hosters[n] || {}));
|
||||||
if (allHosters.length === 0) { alert('Keine Hoster mit Zugangsdaten fuer Health-Check.'); return; }
|
if (allHosters.length === 0) { alert('Keine Hoster mit Zugangsdaten fuer Health-Check.'); return; }
|
||||||
hosters.push(...allHosters);
|
hosters.push(...allHosters);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user