Entfernt die myTischtennis-Integration aus dem Backend und Frontend. Löscht Controller, Routen und Service für myTischtennis. Aktualisiert die Datenmodelle, um die neue ExternalServiceAccount-Integration zu unterstützen. Ändert die API-Routen und Frontend-Komponenten, um die neuen Endpunkte zu verwenden.
This commit is contained in:
603
backend/clients/hettvClient.js
Normal file
603
backend/clients/hettvClient.js
Normal file
@@ -0,0 +1,603 @@
|
||||
import axios from 'axios';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const BASE_URL = 'https://ttde-id.liga.nu';
|
||||
const CLICK_TT_BASE = 'https://httv.click-tt.de';
|
||||
|
||||
class HettvClient {
|
||||
constructor() {
|
||||
this.baseURL = BASE_URL;
|
||||
this.client = axios.create({
|
||||
baseURL: this.baseURL,
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7'
|
||||
},
|
||||
maxRedirects: 5, // Folge den OAuth2-Redirects
|
||||
validateStatus: (status) => status >= 200 && status < 400
|
||||
});
|
||||
|
||||
// Einfache Cookie-Jar nach Host -> { name: value }
|
||||
this.cookieJar = new Map();
|
||||
this.defaultHeaders = {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0',
|
||||
'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to HeTTV via OAuth2
|
||||
* @param {string} username - HeTTV username (email)
|
||||
* @param {string} password - HeTTV password
|
||||
* @returns {Promise<Object>} Login response with session data
|
||||
*/
|
||||
async login(username, password) {
|
||||
try {
|
||||
console.log('[HettvClient] - Starting login for:', username);
|
||||
|
||||
// Schritt 1: OAuth2-Authorization-Endpoint aufrufen - das sollte zur Login-Seite weiterleiten
|
||||
const oauthParams = new URLSearchParams({
|
||||
'scope': 'nuLiga',
|
||||
'response_type': 'code',
|
||||
'redirect_uri': 'https://httv.click-tt.de/cgi-bin/WebObjects/nuLigaTTDE.woa/wa/oAuthLogin',
|
||||
'state': 'nonce=' + Math.random().toString(36).substring(2, 15),
|
||||
'client_id': 'XtVpGjXKAhz3BZuu'
|
||||
});
|
||||
|
||||
// OAuth2 Start
|
||||
|
||||
// Der OAuth2-Endpoint sollte direkt zur Login-Seite weiterleiten
|
||||
const loginPageResponse = await this.client.get(`/oauth2/authz/ttde?${oauthParams.toString()}`, {
|
||||
maxRedirects: 5, // Folge den Redirects zur Login-Seite
|
||||
validateStatus: (status) => status >= 200 && status < 400,
|
||||
headers: {
|
||||
...this.defaultHeaders
|
||||
}
|
||||
});
|
||||
|
||||
// Login-Seite erreicht
|
||||
|
||||
// Session-Cookie aus der Login-Seite extrahieren
|
||||
const setCookies = loginPageResponse.headers['set-cookie'];
|
||||
if (!setCookies || !Array.isArray(setCookies)) {
|
||||
console.error('[HettvClient] - No cookies from login page');
|
||||
return {
|
||||
success: false,
|
||||
error: 'Keine Session-Cookie von Login-Seite erhalten'
|
||||
};
|
||||
}
|
||||
|
||||
const sessionCookie = setCookies.find(cookie => cookie.startsWith('nusportingress='));
|
||||
if (!sessionCookie) {
|
||||
console.error('[HettvClient] - No nusportingress cookie from login page');
|
||||
return {
|
||||
success: false,
|
||||
error: 'Keine nusportingress Session von Login-Seite erhalten'
|
||||
};
|
||||
}
|
||||
|
||||
// Extrahiere t:formdata aus dem HTML der Login-Seite
|
||||
const htmlContent = loginPageResponse.data;
|
||||
// HTML erhalten
|
||||
|
||||
// Suche nach t:formdata im HTML - verschiedene mögliche Formate
|
||||
let formDataMatch = htmlContent.match(/name="t:formdata"\s+value="([^"]+)"/);
|
||||
|
||||
if (!formDataMatch) {
|
||||
// Versuche andere Formate
|
||||
formDataMatch = htmlContent.match(/name='t:formdata'\s+value='([^']+)'/);
|
||||
}
|
||||
|
||||
if (!formDataMatch) {
|
||||
// Suche nach hidden input mit t:formdata (value vor name)
|
||||
formDataMatch = htmlContent.match(/<input[^>]*value="([^"]+)"[^>]*name="t:formdata"/);
|
||||
}
|
||||
|
||||
if (!formDataMatch) {
|
||||
// Suche nach hidden input mit t:formdata (name vor value)
|
||||
formDataMatch = htmlContent.match(/<input[^>]*name="t:formdata"[^>]*value="([^"]+)"/);
|
||||
}
|
||||
|
||||
if (!formDataMatch) {
|
||||
// Suche nach t:formdata ohne Anführungszeichen
|
||||
formDataMatch = htmlContent.match(/name=t:formdata\s+value=([^\s>]+)/);
|
||||
}
|
||||
|
||||
if (!formDataMatch) {
|
||||
console.error('[HettvClient] - No t:formdata found in login page');
|
||||
console.log('[HettvClient] - HTML snippet:', htmlContent.substring(0, 2000));
|
||||
|
||||
// Debug: Suche nach allen hidden inputs
|
||||
const hiddenInputs = htmlContent.match(/<input[^>]*type="hidden"[^>]*>/g);
|
||||
console.log('[HettvClient] - Hidden inputs found:', hiddenInputs);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Keine t:formdata von Login-Seite erhalten'
|
||||
};
|
||||
}
|
||||
|
||||
const tFormData = formDataMatch[1];
|
||||
// CSRF-Token gefunden
|
||||
|
||||
// Schritt 2: Login mit den korrekten Daten durchführen
|
||||
// Verwende die Session-Cookie für den Login-Request
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('t:submit', '["submit_0","submit_0"]');
|
||||
formData.append('t:ac', 'ttde');
|
||||
formData.append('t:formdata', tFormData);
|
||||
formData.append('username', username);
|
||||
formData.append('password', password);
|
||||
|
||||
const loginResponse = await this.client.post('/oauth2/login.loginform', formData.toString(), {
|
||||
headers: {
|
||||
'Cookie': sessionCookie.split(';')[0],
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
...this.defaultHeaders,
|
||||
'Referer': `${BASE_URL}/oauth2/login.loginform`
|
||||
},
|
||||
maxRedirects: 5,
|
||||
validateStatus: (status) => status >= 200 && status < 400
|
||||
});
|
||||
|
||||
// Login-Antwort erhalten
|
||||
|
||||
// Prüfe ob wir erfolgreich eingeloggt sind
|
||||
// Login-Response geprüft
|
||||
|
||||
// Prüfe den Response-Inhalt um zu sehen ob wir noch auf der Login-Seite sind
|
||||
const responseContent = loginResponse.data;
|
||||
const isLoginPage = responseContent.includes('click-TT ID') &&
|
||||
responseContent.includes('Username') &&
|
||||
responseContent.includes('Password');
|
||||
|
||||
// Login-Page-Erkennung durchgeführt
|
||||
|
||||
if (isLoginPage) {
|
||||
console.log('[HettvClient] - Still on login page, login failed');
|
||||
console.log('[HettvClient] - Response snippet:', responseContent.substring(0, 500));
|
||||
return {
|
||||
success: false,
|
||||
error: 'Login fehlgeschlagen - ungültige Zugangsdaten'
|
||||
};
|
||||
}
|
||||
|
||||
// Prüfe auf OAuth2-Redirect oder Erfolg
|
||||
const hasOAuthRedirect = responseContent.includes('oauth2') ||
|
||||
responseContent.includes('redirect') ||
|
||||
loginResponse.status >= 300;
|
||||
|
||||
// OAuth Redirect erkannt
|
||||
|
||||
// Extrahiere die finale Session-Cookie
|
||||
const finalCookies = loginResponse.headers['set-cookie'];
|
||||
const finalSessionCookie = finalCookies?.find(cookie => cookie.startsWith('nusportingress='));
|
||||
|
||||
const sessionId = (finalSessionCookie || sessionCookie).match(/nusportingress=([^;]+)/)?.[1];
|
||||
|
||||
console.log('[HettvClient] - Login erfolgreich (HeTTV).');
|
||||
|
||||
// Versuche die finale OAuth-Weiterleitung zu httv.click-tt.de aufzurufen, um PHPSESSID zu erhalten
|
||||
let finalUrl = loginResponse.request?.res?.responseUrl;
|
||||
console.log('[HettvClient] - Login finalUrl:', finalUrl);
|
||||
let phpSessIdCookie = null;
|
||||
let finalHtml = null;
|
||||
try {
|
||||
if (finalUrl && finalUrl.includes('oAuthLogin')) {
|
||||
const clickTTClient = axios.create({
|
||||
timeout: 15000,
|
||||
maxRedirects: 0,
|
||||
validateStatus: (status) => status >= 200 && status < 400
|
||||
});
|
||||
|
||||
// Folge der Redirect-Kette manuell, übernehme Cookies
|
||||
let currentUrl = finalUrl;
|
||||
let lastResp = null;
|
||||
let hop = 0;
|
||||
const maxHops = 10;
|
||||
while (hop++ < maxHops && currentUrl) {
|
||||
lastResp = await clickTTClient.get(currentUrl, {
|
||||
headers: {
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
...this.defaultHeaders,
|
||||
'Referer': hop === 1 ? `${BASE_URL}/oauth2/login.loginform` : (lastResp?.request?.res?.responseUrl || currentUrl),
|
||||
'Cookie': this._cookieHeaderForUrl(currentUrl)
|
||||
}
|
||||
});
|
||||
|
||||
this._ingestSetCookiesFromResponse(currentUrl, lastResp.headers['set-cookie']);
|
||||
|
||||
const loc = lastResp.headers['location'];
|
||||
if (loc) {
|
||||
// Absolut vs relativ
|
||||
if (/^https?:\/\//i.test(loc)) {
|
||||
currentUrl = loc;
|
||||
} else {
|
||||
const u = new URL(currentUrl);
|
||||
currentUrl = `${u.origin}${loc}`;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break; // keine weitere Location => final
|
||||
}
|
||||
|
||||
const clickTTResp = lastResp;
|
||||
finalHtml = typeof clickTTResp.data === 'string' ? clickTTResp.data : '';
|
||||
const ctSetCookies = clickTTResp.headers['set-cookie'];
|
||||
if (Array.isArray(ctSetCookies)) {
|
||||
phpSessIdCookie = ctSetCookies.find(c => c.startsWith('PHPSESSID='))?.split(';')[0] || null;
|
||||
}
|
||||
// Finale click-TT URL ermittelt
|
||||
}
|
||||
} catch (e) {
|
||||
// Finale click-TT Seite konnte nicht geladen werden
|
||||
}
|
||||
|
||||
// Baue kombinierte Cookie-Kette (falls PHPSESSID vorhanden)
|
||||
const baseCookie = (finalSessionCookie || sessionCookie).split(';')[0];
|
||||
const combinedCookie = phpSessIdCookie ? `${baseCookie}; ${phpSessIdCookie}` : baseCookie;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
sessionId: sessionId,
|
||||
cookie: combinedCookie,
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
expiresAt: null,
|
||||
user: {
|
||||
finalUrl: finalUrl || null,
|
||||
htmlSnippet: finalHtml ? finalHtml.substring(0, 2000) : null
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('HeTTV login error:', error.message);
|
||||
console.error('Error details:', error.response?.status, error.response?.statusText);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || 'Login fehlgeschlagen',
|
||||
status: error.response?.status || 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify login credentials
|
||||
* @param {string} username - HeTTV username
|
||||
* @param {string} password - HeTTV password
|
||||
* @returns {Promise<boolean>} True if credentials are valid
|
||||
*/
|
||||
async verifyCredentials(username, password) {
|
||||
const result = await this.login(username, password);
|
||||
return result.success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an authenticated request to click-TT
|
||||
* @param {string} endpoint - API endpoint
|
||||
* @param {string} cookie - JSESSIONID cookie
|
||||
* @param {Object} options - Additional axios options
|
||||
* @returns {Promise<Object>} API response
|
||||
*/
|
||||
async authenticatedRequest(endpoint, cookie, options = {}, finalUrl = null) {
|
||||
try {
|
||||
// Bestimme Basis-URL dynamisch aus finalUrl, falls vorhanden
|
||||
let baseURL = CLICK_TT_BASE;
|
||||
if (finalUrl) {
|
||||
try {
|
||||
const url = new URL(finalUrl);
|
||||
baseURL = url.origin;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
const isAbsolute = /^https?:\/\//i.test(endpoint);
|
||||
const client = axios.create({
|
||||
baseURL: isAbsolute ? undefined : baseURL,
|
||||
timeout: 15000,
|
||||
maxRedirects: 0,
|
||||
validateStatus: (status) => status >= 200 && status < 400
|
||||
});
|
||||
|
||||
// Manuelles Redirect-Following inkl. Cookies/Referer
|
||||
let currentUrl = isAbsolute ? endpoint : `${baseURL}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
|
||||
let lastResp = null;
|
||||
const trace = [];
|
||||
let hop = 0;
|
||||
const maxHops = 10;
|
||||
|
||||
console.log(`[HettvClient] - Starting redirect chain from: ${currentUrl}`);
|
||||
|
||||
while (hop++ < maxHops && currentUrl) {
|
||||
console.log(`[HettvClient] - Redirect ${hop}: GET ${currentUrl}`);
|
||||
|
||||
lastResp = await client.request({
|
||||
method: options.method || 'GET',
|
||||
url: currentUrl,
|
||||
data: options.data,
|
||||
headers: {
|
||||
...this.defaultHeaders,
|
||||
...(options.headers || {}),
|
||||
'Cookie': this._mergeCookieHeader(cookie, this._cookieHeaderForUrl(currentUrl)),
|
||||
'Referer': hop === 1 ? (finalUrl || baseURL) : (lastResp?.request?.res?.responseUrl || currentUrl)
|
||||
}
|
||||
});
|
||||
|
||||
this._ingestSetCookiesFromResponse(currentUrl, lastResp.headers['set-cookie']);
|
||||
const loc = lastResp.headers['location'];
|
||||
|
||||
console.log(`[HettvClient] - Response: ${lastResp.status} ${lastResp.statusText}`);
|
||||
console.log(`[HettvClient] - Location header: ${loc || 'none'}`);
|
||||
console.log(`[HettvClient] - Set-Cookie header: ${lastResp.headers['set-cookie'] ? 'present' : 'none'}`);
|
||||
console.log(`[HettvClient] - Content-Type: ${lastResp.headers['content-type'] || 'none'}`);
|
||||
|
||||
// Speichere jede Seite zur Analyse
|
||||
try {
|
||||
const dir = path.resolve(process.cwd(), 'backend', 'uploads');
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
const filename = `hettv_redirect_${hop}_${Date.now()}.html`;
|
||||
const filePath = path.join(dir, filename);
|
||||
const content = typeof lastResp.data === 'string' ? lastResp.data : JSON.stringify(lastResp.data, null, 2);
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
console.log(`[HettvClient] - Saved page to: ${filename}`);
|
||||
} catch (e) {
|
||||
console.log(`[HettvClient] - Could not save page ${hop}:`, e.message);
|
||||
}
|
||||
|
||||
trace.push({
|
||||
url: currentUrl,
|
||||
status: lastResp.status,
|
||||
location: loc || null
|
||||
});
|
||||
|
||||
if (loc) {
|
||||
const newUrl = /^https?:\/\//i.test(loc) ? loc : `${new URL(currentUrl).origin}${loc}`;
|
||||
console.log(`[HettvClient] - Following redirect to: ${newUrl}`);
|
||||
currentUrl = newUrl;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[HettvClient] - Final response: ${lastResp.status} (no more redirects)`);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: lastResp?.data,
|
||||
trace
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('HeTTV API error:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || 'API-Anfrage fehlgeschlagen',
|
||||
status: error.response?.status || 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to main HeTTV page and find Downloads menu
|
||||
* @param {string} cookie - Session cookie
|
||||
* @returns {Promise<Object>} Response with main page content and download links
|
||||
*/
|
||||
async getMainPageWithDownloads(cookie, finalUrl = null) {
|
||||
try {
|
||||
console.log('[HettvClient] - Loading main HeTTV page...');
|
||||
|
||||
// Kandidaten für Einstiegs-URL bestimmen
|
||||
let origin = CLICK_TT_BASE;
|
||||
if (finalUrl) {
|
||||
try { origin = new URL(finalUrl).origin; } catch (_) {}
|
||||
}
|
||||
|
||||
const candidates = [];
|
||||
// Direkt zu HeTTV navigieren
|
||||
candidates.push('http://httv.click-tt.de/');
|
||||
candidates.push('http://httv.click-tt.de/wa/');
|
||||
candidates.push('http://httv.click-tt.de/cgi-bin/WebObjects/nuLigaTTDE.woa/wa/');
|
||||
|
||||
// Wenn wir eine finalUrl haben, verwende diese auch
|
||||
if (finalUrl) {
|
||||
candidates.push(finalUrl);
|
||||
}
|
||||
|
||||
console.log('[HettvClient] - URL candidates:', candidates);
|
||||
|
||||
let mainPageResponse = null;
|
||||
let mainTrace = [];
|
||||
let lastError = null;
|
||||
for (const candidate of candidates) {
|
||||
const resp = await this.authenticatedRequest(candidate, cookie, {}, finalUrl);
|
||||
if (resp.success && typeof resp.data === 'string' && resp.data.length > 0) {
|
||||
mainPageResponse = resp;
|
||||
mainTrace = resp.trace || [];
|
||||
break;
|
||||
}
|
||||
lastError = resp;
|
||||
}
|
||||
|
||||
if (!mainPageResponse) {
|
||||
return lastError || { success: false, error: 'HeTTV Einstiegsseite nicht erreichbar', status: 404 };
|
||||
}
|
||||
|
||||
const htmlContent = mainPageResponse.data;
|
||||
console.log('[HettvClient] - Main page loaded, HTML length:', htmlContent.length);
|
||||
|
||||
// Erkenne Fehlerseite (Session ungültig)
|
||||
if (/click-TT\s*-\s*Fehlerseite/i.test(htmlContent) || /ungültige oder nicht mehr gültige URL/i.test(htmlContent)) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Session ungültig oder abgelaufen',
|
||||
status: 401,
|
||||
data: { htmlSnippet: htmlContent.substring(0, 1000) }
|
||||
};
|
||||
}
|
||||
|
||||
// Speichere HTML zur Analyse
|
||||
let savedFile = null;
|
||||
try {
|
||||
const dir = path.resolve(process.cwd(), 'backend', 'uploads');
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
const filename = `hettv_main_${Date.now()}.html`;
|
||||
const filePath = path.join(dir, filename);
|
||||
fs.writeFileSync(filePath, htmlContent, 'utf8');
|
||||
savedFile = filePath;
|
||||
} catch (e) {
|
||||
// Ignoriere Speicherfehler still, nur für Debug
|
||||
}
|
||||
|
||||
// Suche nach Downloads-Links im HTML
|
||||
const downloadLinks = [];
|
||||
|
||||
// 1) URL-Heuristiken
|
||||
const urlPatterns = [
|
||||
/href="([^"]*download[^"]*)"/gi,
|
||||
/href="([^"]*downloads[^"]*)"/gi,
|
||||
/href="([^"]*Download[^"]*)"/gi,
|
||||
/href="([^"]*Downloads[^"]*)"/gi
|
||||
];
|
||||
|
||||
urlPatterns.forEach(pattern => {
|
||||
let match;
|
||||
while ((match = pattern.exec(htmlContent)) !== null) {
|
||||
const link = match[1];
|
||||
if (link && !downloadLinks.includes(link)) {
|
||||
downloadLinks.push(link);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 2) Linktext-Heuristik: <a ...>Downloads</a>
|
||||
const anchorPattern = /<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
||||
let aMatch;
|
||||
while ((aMatch = anchorPattern.exec(htmlContent)) !== null) {
|
||||
const href = aMatch[1];
|
||||
const text = aMatch[2].replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (/\bdownloads?\b/i.test(text)) {
|
||||
if (href && !downloadLinks.includes(href)) {
|
||||
downloadLinks.push(href);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Fallback: Menüpunkte in Navigationen (role="navigation" etc.)
|
||||
if (downloadLinks.length === 0) {
|
||||
const navSectionRegex = /<nav[\s\S]*?<\/nav>/gi;
|
||||
let nav;
|
||||
while ((nav = navSectionRegex.exec(htmlContent)) !== null) {
|
||||
const section = nav[0];
|
||||
let m;
|
||||
anchorPattern.lastIndex = 0;
|
||||
while ((m = anchorPattern.exec(section)) !== null) {
|
||||
const href = m[1];
|
||||
const text = m[2].replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
if (/\bdownloads?\b/i.test(text)) {
|
||||
if (href && !downloadLinks.includes(href)) {
|
||||
downloadLinks.push(href);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[HettvClient] - Found download links:', downloadLinks);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
htmlContent: htmlContent,
|
||||
downloadLinks: downloadLinks,
|
||||
htmlSnippet: htmlContent.substring(0, 2000), // Erste 2000 Zeichen für Analyse
|
||||
savedFile,
|
||||
trace: mainTrace,
|
||||
lastUrl: mainTrace.length ? mainTrace[mainTrace.length - 1].url : null,
|
||||
lastStatus: mainTrace.length ? mainTrace[mainTrace.length - 1].status : null
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('HeTTV main page error:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Fehler beim Laden der Hauptseite',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific download page
|
||||
* @param {string} downloadUrl - URL to the download page
|
||||
* @param {string} cookie - Session cookie
|
||||
* @returns {Promise<Object>} Response with download page content
|
||||
*/
|
||||
async loadDownloadPage(downloadUrl, cookie, finalUrl = null) {
|
||||
try {
|
||||
console.log('[HettvClient] - Loading download page:', downloadUrl);
|
||||
|
||||
const response = await this.authenticatedRequest(downloadUrl, cookie, {}, finalUrl);
|
||||
if (!response.success) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const htmlContent = response.data;
|
||||
console.log('[HettvClient] - Download page loaded, HTML length:', htmlContent.length);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
url: downloadUrl,
|
||||
htmlContent: htmlContent,
|
||||
htmlSnippet: htmlContent.substring(0, 3000) // Erste 3000 Zeichen für Analyse
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('HeTTV download page error:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Fehler beim Laden der Download-Seite',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cookie-Helfer ---
|
||||
_ingestSetCookiesFromResponse(currentUrl, setCookies) {
|
||||
if (!Array.isArray(setCookies) || setCookies.length === 0) return;
|
||||
const { host } = new URL(currentUrl);
|
||||
if (!this.cookieJar.has(host)) this.cookieJar.set(host, new Map());
|
||||
const jar = this.cookieJar.get(host);
|
||||
setCookies.forEach((cookieStr) => {
|
||||
const pair = cookieStr.split(';')[0];
|
||||
const eq = pair.indexOf('=');
|
||||
if (eq > 0) {
|
||||
const name = pair.substring(0, eq).trim();
|
||||
const value = pair.substring(eq + 1).trim();
|
||||
jar.set(name, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_cookieHeaderForUrl(currentUrl) {
|
||||
const { host } = new URL(currentUrl);
|
||||
const jar = this.cookieJar.get(host);
|
||||
if (!jar || jar.size === 0) return '';
|
||||
return Array.from(jar.entries()).map(([k, v]) => `${k}=${v}`).join('; ');
|
||||
}
|
||||
|
||||
_mergeCookieHeader(primary, secondary) {
|
||||
const items = [];
|
||||
if (primary) items.push(primary);
|
||||
if (secondary) items.push(secondary);
|
||||
return items.filter(Boolean).join('; ');
|
||||
}
|
||||
}
|
||||
|
||||
export default new HettvClient();
|
||||
|
||||
Reference in New Issue
Block a user