fix: modal X-Button fixed, xREL parallel (Scene+P2P), Badge sequential batches

This commit is contained in:
2026-06-10 19:19:28 +02:00
parent e53e8e4331
commit 102ab274c7
2 changed files with 84 additions and 100 deletions

View File

@@ -101,78 +101,69 @@ async function xrelFetch(path, params = {}, timeoutMs = 7000) {
return res.json();
}
// Findet ext_info_id: nimmt einfach den ersten movie-Treffer
// (xREL's eigene Datenbank der erste Treffer bei Filmtitel ist fast immer korrekt)
// Findet ext_info_id: sucht original + deutsch parallel, nimmt ersten Treffer
async function findExtInfoId(title, originalTitle) {
const queries = [originalTitle, title]
.filter(Boolean)
.filter((v, i, a) => a.indexOf(v) === i);
for (const q of queries) {
// Alle Queries parallel starten
const results = await Promise.all(queries.map(async q => {
try {
const data = await xrelFetch('/search/ext_info.json', { q, type: 'movie', limit: 5 });
const data = await xrelFetch('/search/ext_info.json', { q, type: 'movie', limit: 5 }, 5000);
const hits = Array.isArray(data.results) ? data.results : [];
// Ersten movie-Treffer nehmen
const hit = hits.find(h => h.type === 'movie');
if (hit?.id) return hit.id;
} catch (_) {}
}
return null;
return hits.find(h => h.type === 'movie')?.id || null;
} catch (_) { return null; }
}));
return results.find(id => id != null) || null;
}
// Holt alle deutschen Releases für eine ext_info_id (alle Seiten)
// Holt alle deutschen Releases für eine ext_info_id Scene + P2P parallel
async function getGermanReleasesById(extInfoId) {
const [sceneData, p2pData] = await Promise.all([
xrelFetch('/release/ext_info.json', { id: extInfoId, per_page: 100 }, 6000).catch(() => ({})),
xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 100 }, 6000).catch(() => ({})),
]);
const results = [];
// Scene Releases (normalerweise 1 Seite reicht, max 3)
try {
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,
date: r.time ? new Date(r.time * 1000).toISOString().slice(0, 10) : null,
p2p: false,
});
}
} catch (_) {}
// Scene
for (const r of sceneData.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,
date: r.time ? new Date(r.time * 1000).toISOString().slice(0, 10) : null,
p2p: false,
});
}
// P2P Releases alle Seiten holen
try {
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 (_) {}
// P2P alle Seiten parallel holen
const p2pTotalPages = Math.min(p2pData.pagination?.total_pages || 1, 4);
const allP2P = [...(p2pData.list || [])];
if (p2pTotalPages > 1) {
const extra = await Promise.all(
Array.from({ length: p2pTotalPages - 1 }, (_, i) =>
xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 100, page: i + 2 }, 6000)
.then(d => d.list || []).catch(() => [])
)
);
for (const list of extra) allP2P.push(...list);
}
for (const r of allP2P) {
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,
});
}
results.sort((a, b) => (b.date || '').localeCompare(a.date || ''));
return results;
@@ -224,6 +215,7 @@ router.get('/movie/:id', authenticate, async (req, res) => {
const fsk = extractFSK(detail.release_dates);
const imdbId = extIds.imdb_id || detail.imdb_id || null;
// xREL: findExtInfoId + getGermanReleasesById sequenziell aber mit kürzerem Timeout
let xrel = [];
try {
const extInfoId = await findExtInfoId(detail.title, detail.original_title);
@@ -309,27 +301,4 @@ router.put('/settings', authenticate, requireAdmin, (req, res) => {
res.json({ ok: true });
});
// DEBUG nach Test entfernen
router.get('/debug-xrel2', authenticate, async (req, res) => {
const title = req.query.title || 'Thunderbolts';
const original = req.query.original || title;
const log = {};
try {
log.step1_query = title;
const searchData = await xrelFetch('/search/ext_info.json', { q: title, type: 'movie', limit: 5 });
log.step1_results = (searchData.results || []).map(r => ({ id: r.id, title: r.title, type: r.type }));
const extInfoId = (searchData.results || []).find(h => h.type === 'movie')?.id;
log.ext_info_id = extInfoId || null;
if (extInfoId) {
const p2p = await xrelFetch('/p2p/releases.json', { ext_info_id: extInfoId, per_page: 5 });
log.p2p_total = p2p.total_count;
log.p2p_sample = (p2p.list || []).slice(0, 3).map(r => ({ dirname: r.dirname, main_lang: r.main_lang, pub_time: r.pub_time }));
const scene = await xrelFetch('/release/ext_info.json', { id: extInfoId, per_page: 5 });
log.scene_total = scene.total_count;
log.scene_sample = (scene.list || []).slice(0, 3).map(r => ({ dirname: r.dirname, time: r.time }));
}
} catch (e) { log.error = e.message; }
res.json(log);
});
module.exports = router;