Bild-Proxy entpackt Check24-Wrapper und lädt direkt von Originalquelle (umgeht Hotlink-Schutz) – alle Fotos laden

This commit is contained in:
2026-06-04 09:13:47 +00:00
parent 92e479a1c4
commit 337c279854
+27 -5
View File
@@ -37,8 +37,27 @@ let state = loadState();
// ---------- Image proxy with retry + memory + disk cache ---------- // ---------- Image proxy with retry + memory + disk cache ----------
const imgCache = new Map(); const imgCache = new Map();
const UPSTREAM_HOSTS = [
/(^|\.)travelapi\.com$/, /(^|\.)hotelbeds\.com$/, /(^|\.)paximum\.com$/,
/(^|\.)hotelston\.com$/, /(^|\.)sunhotels\.net$/, /(^|\.)giatamedia\.com$/,
/(^|\.)tui\.com$/, /(^|\.)bstatic\.com$/, /(^|\.)dnatatravel\.com$/
];
function isCheck24(host) { return /(^|\.)urlaub\.check24\.de$/.test(host); }
function hostAllowed(host) { function hostAllowed(host) {
return /(^|\.)urlaub\.check24\.de$/.test(host) || host === 'files.ahoi-schiff.de'; return isCheck24(host) || host === 'files.ahoi-schiff.de' || UPSTREAM_HOSTS.some(re => re.test(host));
}
// Check24 wraps the real image URL (base64) inside its CDN path. Their CDN blocks
// server-side hotlinking, but the underlying source CDNs (travelapi, hotelbeds, …)
// serve freely — so we unwrap and fetch the original directly.
function unwrap(u) {
try {
const m = u.match(/source=([^!\/]+)/);
if (m) {
const real = Buffer.from(m[1].replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
if (/^https?:\/\//.test(real)) return real;
}
} catch (e) {}
return u;
} }
const keyFor = u => crypto.createHash('sha1').update(u).digest('hex'); const keyFor = u => crypto.createHash('sha1').update(u).digest('hex');
function diskGet(u) { function diskGet(u) {
@@ -52,14 +71,17 @@ function diskPut(u, buf, type) {
try { fs.writeFileSync(f, buf); fs.writeFileSync(f + '.t', type); } catch (e) {} try { fs.writeFileSync(f, buf); fs.writeFileSync(f + '.t', type); } catch (e) {}
} }
async function fetchImg(u, tries = 3) { async function fetchImg(u, tries = 3) {
let host; try { host = new URL(u).host; } catch (e) { return null; } let host0; try { host0 = new URL(u).host; } catch (e) { return null; }
const referer = host.endsWith('ahoi-schiff.de') ? 'https://www.ahoi-schiff.de/' : 'https://urlaub.check24.de/'; // If it's a Check24 CDN wrapper, fetch the original source URL directly.
const target = isCheck24(host0) ? unwrap(u) : u;
let thost; try { thost = new URL(target).host; } catch (e) { thost = host0; }
const referer = thost.endsWith('ahoi-schiff.de') ? 'https://www.ahoi-schiff.de/' : ('https://' + thost + '/');
for (let i = 0; i < tries; i++) { for (let i = 0; i < tries; i++) {
try { try {
const ac = new AbortController(); const ac = new AbortController();
const t = setTimeout(() => ac.abort(), 9000); const t = setTimeout(() => ac.abort(), 9000);
const r = await fetch(u, { signal: ac.signal, headers: { const r = await fetch(target, { signal: ac.signal, headers: {
'Referer': referer, 'User-Agent': 'Mozilla/5.0 (compatible; HeidrichReise/1.0)', 'Accept': 'image/*' 'Referer': referer, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', 'Accept': 'image/avif,image/webp,image/*,*/*'
}}); }});
clearTimeout(t); clearTimeout(t);
if (r.ok) return { buf: Buffer.from(await r.arrayBuffer()), type: r.headers.get('content-type') || 'image/jpeg' }; if (r.ok) return { buf: Buffer.from(await r.arrayBuffer()), type: r.headers.get('content-type') || 'image/jpeg' };