diff --git a/backend/src/tools/media/routes.js b/backend/src/tools/media/routes.js
index a4c5c0f..e299bf9 100644
--- a/backend/src/tools/media/routes.js
+++ b/backend/src/tools/media/routes.js
@@ -93,7 +93,7 @@ function enrichMovie(m, genreMap, fsk, rank) {
}
// ── xREL ─────────────────────────────────────────────────────────────────────
-const QUALITY_BLOCKLIST = /\b(2160p|4k|uhd|uhdbluray|uhdbd)\b/i;
+const QUALITY_BLOCKLIST = /\b(2160p|4k|uhd|uhdbluray|uhdbd|complete|bluray|blu-ray|remux|hevc|x265|h265)\b/i;
async function xrelFetch(path, params = {}) {
const url = new URL(XREL_BASE + path);
@@ -118,30 +118,31 @@ async function getXrelData(tmdbId, imdbId, title, originalTitle) {
const cached = cacheGet(cacheKey);
if (cached !== undefined) return cached;
- let extInfoId = null;
+ // 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);
- // Suche ext_info_id via IMDb-URI (exakter Match)
+ const searchResults = await Promise.all(queries.map(async q => {
+ try {
+ return await xrelFetch('/search/ext_info.json', {
+ q: q.replace(/[*#!?:]/g,''), type:'movie', limit:10
+ });
+ } catch(_) { return null; }
+ }));
+
+ let extInfoId = null;
+ // IMDb-URI Match zuerst
if (imdbId) {
- 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: q.replace(/[*]/g,''), type:'movie', limit:10 });
- const hit = (data.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`));
- if (hit?.id) { extInfoId = hit.id; break; }
- } catch(_) {}
+ for (const data of searchResults) {
+ const hit = (data?.results||[]).find(h => (h.uris||[]).includes(`imdb:${imdbId}`));
+ if (hit?.id) { extInfoId = hit.id; break; }
}
}
-
- // Fallback: exakter Titel-Match
+ // Exakter Titel-Match als Fallback
if (!extInfoId) {
- const clean = (s) => (s||'').replace(/[*#!?:]/g,'').trim().toLowerCase();
- 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: q.replace(/[*#!?:]/g,''), type:'movie', limit:5 });
- const hit = (data.results||[]).find(h => clean(h.title)===clean(q) || clean(h.alt_title)===clean(q));
- if (hit?.id) { extInfoId = hit.id; break; }
- } catch(_) {}
+ 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; }
}
}
@@ -238,6 +239,7 @@ router.get('/movie/:id', authenticate, async (req, res) => {
tmdb_url: `https://www.themoviedb.org/movie/${detail.id}`,
imdb_url: imdbId ? `https://www.imdb.com/title/${imdbId}` : null,
imdb_id: imdbId,
+ countries: (detail.production_countries||[]).map(c => c.name),
});
} catch(e) { res.status(500).json({ error: e.message }); }
});
diff --git a/frontend/src/tools/media.jsx b/frontend/src/tools/media.jsx
index 6478b7a..3a22319 100644
--- a/frontend/src/tools/media.jsx
+++ b/frontend/src/tools/media.jsx
@@ -564,6 +564,7 @@ function MovieModal({ tmdbId, onClose, mobile }) {
{detail.runtime > 0 &&