fix: xREL 3 Suchstrategien, normalisierte Queries, debug-xrel3 Endpoint

This commit is contained in:
2026-06-11 09:32:46 +02:00
parent 7afedfbb02
commit cc38347363

View File

@@ -126,36 +126,64 @@ async function getXrelData(tmdbId, imdbId, title, originalTitle) {
const cached = cacheGet(cacheKey); const cached = cacheGet(cacheKey);
if (cached !== undefined) return cached; if (cached !== undefined) return cached;
// Alle Suchen parallel originaltitel und deutsch gleichzeitig const clean = (s) => normalizeUmlauts(s||'').replace(/[*#!?:]/g,'').trim().toLowerCase();
const clean = (s) => (s||'').replace(/[*#!?:]/g,'').trim().toLowerCase();
const queries = [originalTitle, title].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i);
const searchResults = await Promise.all(queries.map(async q => { // Suchqueries: original, deutsch, und normalisierte Varianten
// Wichtig: xREL nutzt oft den englischen Originaltitel auch bei deutschen Filmen
const rawQueries = [originalTitle, title].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i);
// Normalisierte Versionen (Umlaute) als zusätzliche Queries
const normQueries = rawQueries.map(q => normalizeUmlauts(q).replace(/[*#!?:]/g,'').trim())
.filter(q => !rawQueries.map(r => r.replace(/[*#!?:]/g,'').trim()).includes(q));
const allQueries = [...rawQueries, ...normQueries].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i);
// Alle parallel suchen
const searchResults = await Promise.all(allQueries.map(async q => {
try { try {
return await xrelFetch('/search/ext_info.json', { return {
q: normalizeUmlauts(q).replace(/[*#!?:]/g,''), type:'movie', limit:10 q,
}); data: await xrelFetch('/search/ext_info.json', {
q: normalizeUmlauts(q).replace(/[*#!?:]/g,''), type:'movie', limit:10
})
};
} catch(_) { return null; } } catch(_) { return null; }
})); }));
let extInfoId = null; let extInfoId = null;
// IMDb-URI Match zuerst
// Strategie 1: IMDb-URI exakter Match (zuverlässigste Methode)
if (imdbId) { if (imdbId) {
for (const data of searchResults) { for (const r of searchResults) {
const hit = (data?.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`)); if (!r) continue;
if (hit?.id) { extInfoId = hit.id; break; } const hit = (r.data?.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`));
}
}
// Exakter Titel-Match als Fallback
if (!extInfoId) {
for (const data of searchResults) {
const hit = (data?.results||[]).find(h => clean(h.title)===clean(queries[0]) || clean(h.alt_title)===clean(queries[0]));
if (hit?.id) { extInfoId = hit.id; break; } if (hit?.id) { extInfoId = hit.id; break; }
} }
} }
// Strategie 2: Exakter Titel-Match
if (!extInfoId) { if (!extInfoId) {
cacheSet(cacheKey, [], 60*60*1000); // 1h für nicht gefunden for (const r of searchResults) {
if (!r) continue;
const hit = (r.data?.results||[]).find(h =>
clean(h.title) === clean(r.q) || clean(h.alt_title) === clean(r.q)
);
if (hit?.id) { extInfoId = hit.id; break; }
}
}
// Strategie 3: Einziger Treffer bei IMDb-Suche (nur wenn exakt 1 Ergebnis)
if (!extInfoId && imdbId) {
for (const r of searchResults) {
if (!r) continue;
const results = r.data?.results || [];
if (results.length === 1 && results[0].type === 'movie') {
extInfoId = results[0].id;
break;
}
}
}
if (!extInfoId) {
cacheSet(cacheKey, [], 60*60*1000);
return []; return [];
} }
@@ -331,4 +359,36 @@ router.put('/settings', authenticate, requireAdmin, (req, res) => {
}); });
// DEBUG nach Test entfernen
router.get('/debug-xrel3', authenticate, async (req, res) => {
const title = req.query.title || '';
const orig = req.query.orig || '';
const imdb_id = req.query.imdb_id || null;
const log = { title, orig, imdb_id };
try {
const clean = (s) => normalizeUmlauts(s||'').replace(/[*#!?:]/g,'').trim().toLowerCase();
const rawQueries = [orig, title].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i);
const normQueries = rawQueries.map(q => normalizeUmlauts(q).replace(/[*#!?:]/g,'').trim())
.filter(q => !rawQueries.map(r => r.replace(/[*#!?:]/g,'').trim()).includes(q));
log.rawQueries = rawQueries;
log.normQueries = normQueries;
log.allQueries = [...rawQueries, ...normQueries].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i);
const results = [];
for (const q of log.allQueries) {
try {
const data = await xrelFetch('/search/ext_info.json', {
q: normalizeUmlauts(q).replace(/[*#!?:]/g,''), type:'movie', limit:5
});
results.push({
query: normalizeUmlauts(q).replace(/[*#!?:]/g,''),
hits: (data.results||[]).map(h => ({ id: h.id, title: h.title, alt_title: h.alt_title, uris: h.uris }))
});
} catch(e) { results.push({ query: q, error: e.message }); }
}
log.searchResults = results;
} catch(e) { log.error = e.message; }
res.json(log);
});
module.exports = router; module.exports = router;