fix: xREL – ersten movie-Treffer nehmen, P2P alle Seiten, korrekte Felder aus Debug
This commit is contained in:
@@ -88,12 +88,9 @@ function enrichMovie(movie, genreMap, fsk, rank) {
|
||||
};
|
||||
}
|
||||
|
||||
// ── xREL: 2-Schritt Ansatz ────────────────────────────────────────────────────
|
||||
// Schritt 1: Titel → ext_info_id (eindeutige xREL Film-ID)
|
||||
// Schritt 2: ext_info_id → alle Releases (Scene + P2P)
|
||||
// So gibt es keine falschen Film-Matches mehr.
|
||||
// ── xREL Helper ───────────────────────────────────────────────────────────────
|
||||
|
||||
async function xrelFetch(path, params = {}, timeoutMs = 6000) {
|
||||
async function xrelFetch(path, params = {}, timeoutMs = 7000) {
|
||||
const url = new URL(XREL_BASE + path);
|
||||
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, String(v));
|
||||
const res = await fetch(url.toString(), {
|
||||
@@ -104,72 +101,79 @@ async function xrelFetch(path, params = {}, timeoutMs = 6000) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// Findet die passende ext_info_id für einen Film-Titel
|
||||
// Prüft ob Titel ähnlich genug ist (exakte oder starke Übereinstimmung)
|
||||
// Findet ext_info_id: nimmt einfach den ersten movie-Treffer
|
||||
// (xREL's eigene Datenbank – der erste Treffer bei Filmtitel ist fast immer korrekt)
|
||||
async function findExtInfoId(title, originalTitle) {
|
||||
const queries = [originalTitle, title].filter(Boolean)
|
||||
.filter((v, i, a) => v && a.indexOf(v) === i);
|
||||
const queries = [originalTitle, title]
|
||||
.filter(Boolean)
|
||||
.filter((v, i, a) => a.indexOf(v) === i);
|
||||
|
||||
for (const q of queries) {
|
||||
try {
|
||||
const data = await xrelFetch('/search/ext_info.json', { q, type: 'movie', limit: 5 });
|
||||
// data.results = Array von ShortExtInfo
|
||||
const hits = Array.isArray(data.results) ? data.results : [];
|
||||
for (const hit of hits) {
|
||||
const hitTitle = (hit.title || '').toLowerCase().trim();
|
||||
const searchQ = q.toLowerCase().trim();
|
||||
// Titel muss sehr ähnlich sein – exakt oder beginnt damit
|
||||
if (hitTitle === searchQ || hitTitle.startsWith(searchQ) || searchQ.startsWith(hitTitle)) {
|
||||
return hit.id;
|
||||
}
|
||||
}
|
||||
// Ersten movie-Treffer nehmen
|
||||
const hit = hits.find(h => h.type === 'movie');
|
||||
if (hit?.id) return hit.id;
|
||||
} catch (_) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Holt alle deutschen Releases für eine ext_info_id
|
||||
// Holt alle deutschen Releases für eine ext_info_id (alle Seiten)
|
||||
async function getGermanReleasesById(extInfoId) {
|
||||
const results = [];
|
||||
|
||||
// Scene Releases (normalerweise 1 Seite reicht, max 3)
|
||||
try {
|
||||
// Scene Releases: { list: [...Release], total_count, pagination }
|
||||
const sceneData = await xrelFetch('/release/ext_info.json', { id: extInfoId, per_page: 50 });
|
||||
const sceneList = Array.isArray(sceneData.list) ? sceneData.list : [];
|
||||
for (const r of sceneList) {
|
||||
// Scene: German-Filter über dirname
|
||||
const page1 = await xrelFetch('/release/ext_info.json', { id: extInfoId, per_page: 100 });
|
||||
const list = Array.isArray(page1.list) ? page1.list : [];
|
||||
for (const r of list) {
|
||||
if (!/german|deutsch/i.test(r.dirname || '')) continue;
|
||||
results.push({
|
||||
dirname: r.dirname,
|
||||
size: r.size ? `${r.size.number} ${r.size.unit}` : null,
|
||||
category: 'Scene',
|
||||
url: r.link_href || `https://www.xrel.to/release/${r.id}.html`,
|
||||
url: r.link_href,
|
||||
date: r.time ? new Date(r.time * 1000).toISOString().slice(0, 10) : null,
|
||||
p2p: false,
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// P2P Releases – alle Seiten holen
|
||||
try {
|
||||
// P2P Releases: { list: [...P2PRelease], total_count, pagination }
|
||||
const p2pData = await xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 50 });
|
||||
const p2pList = Array.isArray(p2pData.list) ? p2pData.list : [];
|
||||
for (const r of p2pList) {
|
||||
// P2P: German-Filter über main_lang Feld (zuverlässig!)
|
||||
if (r.main_lang !== 'german') continue;
|
||||
const sizeMb = r.size_mb || 0;
|
||||
results.push({
|
||||
dirname: r.dirname,
|
||||
size: sizeMb ? `${(sizeMb / 1024).toFixed(1)} GB` : null,
|
||||
category: 'P2P',
|
||||
url: r.link_href || `https://www.xrel.to/p2p-release/${r.id}.html`,
|
||||
date: r.pub_time ? new Date(r.pub_time * 1000).toISOString().slice(0, 10) : null,
|
||||
p2p: true,
|
||||
});
|
||||
const page1 = await xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 100 });
|
||||
const totalPages = Math.min(page1.pagination?.total_pages || 1, 5);
|
||||
const allLists = [page1.list || []];
|
||||
|
||||
if (totalPages > 1) {
|
||||
const extra = await Promise.all(
|
||||
Array.from({ length: totalPages - 1 }, (_, i) =>
|
||||
xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 100, page: i + 2 })
|
||||
.then(d => d.list || []).catch(() => [])
|
||||
)
|
||||
);
|
||||
allLists.push(...extra);
|
||||
}
|
||||
|
||||
for (const list of allLists) {
|
||||
for (const r of list) {
|
||||
// P2P: main_lang === 'german' ist das zuverlässige Feld
|
||||
if (r.main_lang !== 'german') continue;
|
||||
const sizeMb = r.size_mb || 0;
|
||||
results.push({
|
||||
dirname: r.dirname,
|
||||
size: sizeMb ? `${(sizeMb / 1024).toFixed(1)} GB` : null,
|
||||
category: r.category?.sub_cat || 'P2P',
|
||||
url: r.link_href,
|
||||
date: r.pub_time ? new Date(r.pub_time * 1000).toISOString().slice(0, 10) : null,
|
||||
p2p: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// Neueste zuerst
|
||||
results.sort((a, b) => (b.date || '').localeCompare(a.date || ''));
|
||||
return results;
|
||||
}
|
||||
@@ -220,7 +224,6 @@ router.get('/movie/:id', authenticate, async (req, res) => {
|
||||
const fsk = extractFSK(detail.release_dates);
|
||||
const imdbId = extIds.imdb_id || detail.imdb_id || null;
|
||||
|
||||
// xREL: 2-Schritt
|
||||
let xrel = [];
|
||||
try {
|
||||
const extInfoId = await findExtInfoId(detail.title, detail.original_title);
|
||||
@@ -256,20 +259,26 @@ router.post('/xrel-check', authenticate, async (req, res) => {
|
||||
if (!movies.length) return res.json({});
|
||||
|
||||
const result = {};
|
||||
// 2er-Batches (2 API-Calls pro Film: search + releases)
|
||||
for (let i = 0; i < movies.length; i += 2) {
|
||||
const chunk = movies.slice(i, i + 2);
|
||||
// 3er-Batches
|
||||
for (let i = 0; i < movies.length; i += 3) {
|
||||
const chunk = movies.slice(i, i + 3);
|
||||
await Promise.all(chunk.map(async m => {
|
||||
try {
|
||||
const extInfoId = await findExtInfoId(m.title, m.original_title);
|
||||
if (!extInfoId) { result[m.id] = false; return; }
|
||||
const releases = await getGermanReleasesById(extInfoId);
|
||||
result[m.id] = releases.length > 0;
|
||||
// Schnell-Check: nur P2P Seite 1 prüfen ob german vorhanden
|
||||
const p2p = await xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 25 });
|
||||
const p2pHit = (p2p.list || []).some(r => r.main_lang === 'german');
|
||||
if (p2pHit) { result[m.id] = true; return; }
|
||||
// Auch Scene prüfen
|
||||
const scene = await xrelFetch('/release/ext_info.json', { id: extInfoId, per_page: 25 });
|
||||
const sceneHit = (scene.list || []).some(r => /german|deutsch/i.test(r.dirname || ''));
|
||||
result[m.id] = sceneHit;
|
||||
} catch (_) {
|
||||
result[m.id] = false;
|
||||
}
|
||||
}));
|
||||
if (i + 2 < movies.length) await new Promise(r => setTimeout(r, 300));
|
||||
if (i + 3 < movies.length) await new Promise(r => setTimeout(r, 250));
|
||||
}
|
||||
res.json(result);
|
||||
});
|
||||
@@ -307,39 +316,3 @@ router.put('/settings', authenticate, requireAdmin, (req, res) => {
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
// DEBUG – temporär, nach Test entfernen
|
||||
router.get('/debug-xrel', authenticate, async (req, res) => {
|
||||
const title = req.query.title || 'Mandalorian';
|
||||
const log = [];
|
||||
try {
|
||||
// Step 1: ext_info search
|
||||
const searchUrl = `${XREL_BASE}/search/ext_info.json?q=${encodeURIComponent(title)}&type=movie&limit=5`;
|
||||
log.push({ step: 'search_url', url: searchUrl });
|
||||
const searchRes = await fetch(searchUrl, { headers: { Accept: 'application/json' }, signal: AbortSignal.timeout(6000) });
|
||||
log.push({ step: 'search_status', status: searchRes.status });
|
||||
const searchData = await searchRes.json();
|
||||
log.push({ step: 'search_response', data: searchData });
|
||||
|
||||
if (searchData.results?.length) {
|
||||
const extInfoId = searchData.results[0].id;
|
||||
log.push({ step: 'ext_info_id', id: extInfoId });
|
||||
|
||||
// Step 2a: Scene releases
|
||||
const sceneUrl = `${XREL_BASE}/release/ext_info.json?id=${extInfoId}&per_page=5`;
|
||||
const sceneRes = await fetch(sceneUrl, { headers: { Accept: 'application/json' }, signal: AbortSignal.timeout(6000) });
|
||||
const sceneData = await sceneRes.json();
|
||||
log.push({ step: 'scene_releases', status: sceneRes.status, keys: Object.keys(sceneData), sample: JSON.stringify(sceneData).slice(0, 500) });
|
||||
|
||||
// Step 2b: P2P releases
|
||||
const p2pUrl = `${XREL_BASE}/p2p/releases.json?ext_info_id=${extInfoId}&per_page=5`;
|
||||
const p2pRes = await fetch(p2pUrl, { headers: { Accept: 'application/json' }, signal: AbortSignal.timeout(6000) });
|
||||
const p2pData = await p2pRes.json();
|
||||
log.push({ step: 'p2p_releases', status: p2pRes.status, keys: Object.keys(p2pData), sample: JSON.stringify(p2pData).slice(0, 500) });
|
||||
}
|
||||
} catch (e) {
|
||||
log.push({ step: 'error', message: e.message });
|
||||
}
|
||||
res.json(log);
|
||||
});
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user