Bild-Proxy entpackt Check24-Wrapper und lädt direkt von Originalquelle (umgeht Hotlink-Schutz) – alle Fotos laden
This commit is contained in:
@@ -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' };
|
||||||
|
|||||||
Reference in New Issue
Block a user