diff --git a/backend/src/tools/media/routes.js b/backend/src/tools/media/routes.js index b968021..60ec314 100644 --- a/backend/src/tools/media/routes.js +++ b/backend/src/tools/media/routes.js @@ -126,36 +126,64 @@ async function getXrelData(tmdbId, imdbId, title, originalTitle) { const cached = cacheGet(cacheKey); if (cached !== undefined) return cached; - // Alle Suchen parallel – originaltitel und deutsch gleichzeitig - const clean = (s) => (s||'').replace(/[*#!?:]/g,'').trim().toLowerCase(); - const queries = [originalTitle, title].filter(Boolean).filter((v,i,a) => a.indexOf(v)===i); + const clean = (s) => normalizeUmlauts(s||'').replace(/[*#!?:]/g,'').trim().toLowerCase(); - 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 { - return await xrelFetch('/search/ext_info.json', { - q: normalizeUmlauts(q).replace(/[*#!?:]/g,''), type:'movie', limit:10 - }); + return { + q, + data: await xrelFetch('/search/ext_info.json', { + q: normalizeUmlauts(q).replace(/[*#!?:]/g,''), type:'movie', limit:10 + }) + }; } catch(_) { return null; } })); let extInfoId = null; - // IMDb-URI Match zuerst + + // Strategie 1: IMDb-URI exakter Match (zuverlässigste Methode) if (imdbId) { - for (const data of searchResults) { - const hit = (data?.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`)); - if (hit?.id) { extInfoId = hit.id; break; } - } - } - // 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])); + for (const r of searchResults) { + if (!r) continue; + const hit = (r.data?.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`)); if (hit?.id) { extInfoId = hit.id; break; } } } + // Strategie 2: Exakter Titel-Match 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 []; } @@ -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;