fix: RTL+ Fallback ohne Jahresfilter bei kleinen Katalogen, frisches 90-Tage-Fenster für aktuelle Hits vorgezogen
This commit is contained in:
@@ -549,21 +549,21 @@ router.get('/streaming/:providerId', authenticate, async (req, res) => {
|
|||||||
const UNLIMITED_LANGS = new Set(['de', 'en']);
|
const UNLIMITED_LANGS = new Set(['de', 'en']);
|
||||||
const MAX_OTHER_LANGUAGE_TOTAL = 2;
|
const MAX_OTHER_LANGUAGE_TOTAL = 2;
|
||||||
|
|
||||||
function buildParams(mediaKind, minVotes) {
|
function buildParams(mediaKind, minVotes, dateFrom) {
|
||||||
return {
|
return {
|
||||||
watch_region: 'DE',
|
watch_region: 'DE',
|
||||||
with_watch_providers: provider.tmdbId,
|
with_watch_providers: provider.tmdbId,
|
||||||
with_watch_monetization_types: 'flatrate',
|
with_watch_monetization_types: 'flatrate',
|
||||||
sort_by: 'popularity.desc',
|
sort_by: 'popularity.desc',
|
||||||
'vote_count.gte': minVotes,
|
'vote_count.gte': minVotes,
|
||||||
...(mediaKind === 'movie'
|
...(dateFrom
|
||||||
? { 'primary_release_date.gte': minDate }
|
? (mediaKind === 'movie' ? { 'primary_release_date.gte': dateFrom } : { 'first_air_date.gte': dateFrom })
|
||||||
: { 'first_air_date.gte': minDate }),
|
: {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchPages(mediaKind, minVotes, pageCount) {
|
async function fetchPages(mediaKind, minVotes, pageCount, dateFrom) {
|
||||||
const params = buildParams(mediaKind, minVotes);
|
const params = buildParams(mediaKind, minVotes, dateFrom);
|
||||||
const path = mediaKind === 'movie' ? '/discover/movie' : '/discover/tv';
|
const path = mediaKind === 'movie' ? '/discover/movie' : '/discover/tv';
|
||||||
const calls = [];
|
const calls = [];
|
||||||
for (let p = 1; p <= pageCount; p++) calls.push(tmdb(path, { ...params, page: p }));
|
for (let p = 1; p <= pageCount; p++) calls.push(tmdb(path, { ...params, page: p }));
|
||||||
@@ -571,22 +571,58 @@ router.get('/streaming/:providerId', authenticate, async (req, res) => {
|
|||||||
return pages.flatMap(p => p.results || []);
|
return pages.flatMap(p => p.results || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Robuster Fallback in drei Stufen, garantiert genug Kandidaten:
|
// "Ganz frisch"-Fenster: letzte 90 Tage, sehr niedrige Vote-Schwelle.
|
||||||
// 1) hohe Schwelle (bekannte Titel), 2) mittlere, 3) niedrige (kleine Kataloge wie RTL+)
|
// TMDb's popularity-Wert reagiert auf kurzfristige Trend-Spitzen (Suchanfragen,
|
||||||
|
// Watchlist-Einträge etc.), das geht in der riesigen 2-Jahres-Gesamtliste aber
|
||||||
|
// unter alten Dauerbrennern unter. Durch die enge Zeitspanne kommen aktuell
|
||||||
|
// virale Titel (z.B. ein Release dieser Woche) automatisch nach vorne –
|
||||||
|
// ganz ohne zusätzliche Einzel-Abfragen pro Titel.
|
||||||
|
const freshDate = (() => {
|
||||||
|
const d = new Date();
|
||||||
|
d.setDate(d.getDate() - 90);
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Robuster Fallback in vier Stufen, garantiert genug Kandidaten:
|
||||||
|
// 1) hohe Schwelle (letzte 2 Jahre, bekannte Titel)
|
||||||
|
// 2) mittlere Schwelle (letzte 2 Jahre)
|
||||||
|
// 3) niedrige Schwelle (letzte 2 Jahre)
|
||||||
|
// 4) OHNE Jahresfilter (für kleinere Kataloge wie RTL+, die laut TMDb in den
|
||||||
|
// letzten 2 Jahren kaum neue Lizenzen bekommen haben – die zeigen wir dann
|
||||||
|
// mit ihren ältesten verfügbaren beliebten Titeln statt einer leeren Liste)
|
||||||
async function fetchCandidates(mediaKind) {
|
async function fetchCandidates(mediaKind) {
|
||||||
let raw = await fetchPages(mediaKind, 300, 2);
|
// Schritt 1: ganz frische Titel (letzte 90 Tage, niedrige Schwelle) –
|
||||||
|
// werden später vorne in die Liste gemischt, damit aktuelle Hits Vorrang haben
|
||||||
|
const freshRaw = await fetchPages(mediaKind, 5, 1, freshDate);
|
||||||
|
const fresh = freshRaw.filter(m => !BLOCKED_LANGS.has(m.original_language));
|
||||||
|
|
||||||
|
// Schritt 2: normaler Pool der letzten 2 Jahre, gestaffelte Vote-Schwelle
|
||||||
|
let raw = await fetchPages(mediaKind, 300, 2, minDate);
|
||||||
let filtered = raw.filter(m => !BLOCKED_LANGS.has(m.original_language));
|
let filtered = raw.filter(m => !BLOCKED_LANGS.has(m.original_language));
|
||||||
if (filtered.length < 15) {
|
if (filtered.length < 15) {
|
||||||
const more = await fetchPages(mediaKind, 50, 2);
|
const more = await fetchPages(mediaKind, 50, 2, minDate);
|
||||||
const seen = new Set(filtered.map(m => m.id));
|
const seen = new Set(filtered.map(m => m.id));
|
||||||
for (const m of more) if (!seen.has(m.id) && !BLOCKED_LANGS.has(m.original_language)) { filtered.push(m); seen.add(m.id); }
|
for (const m of more) if (!seen.has(m.id) && !BLOCKED_LANGS.has(m.original_language)) { filtered.push(m); seen.add(m.id); }
|
||||||
}
|
}
|
||||||
if (filtered.length < 10) {
|
if (filtered.length < 10) {
|
||||||
const more = await fetchPages(mediaKind, 0, 2);
|
const more = await fetchPages(mediaKind, 0, 2, minDate);
|
||||||
const seen = new Set(filtered.map(m => m.id));
|
const seen = new Set(filtered.map(m => m.id));
|
||||||
for (const m of more) if (!seen.has(m.id) && !BLOCKED_LANGS.has(m.original_language)) { filtered.push(m); seen.add(m.id); }
|
for (const m of more) if (!seen.has(m.id) && !BLOCKED_LANGS.has(m.original_language)) { filtered.push(m); seen.add(m.id); }
|
||||||
}
|
}
|
||||||
return filtered;
|
// Schritt 3: kleine Kataloge (z.B. RTL+) – TMDb hat dort oft kaum Titel
|
||||||
|
// der letzten 2 Jahre korrekt eingetragen, dann ganz ohne Jahresfilter
|
||||||
|
if (filtered.length < 10) {
|
||||||
|
const more = await fetchPages(mediaKind, 20, 2, null);
|
||||||
|
const seen = new Set(filtered.map(m => m.id));
|
||||||
|
for (const m of more) if (!seen.has(m.id) && !BLOCKED_LANGS.has(m.original_language)) { filtered.push(m); seen.add(m.id); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frische Titel vorne einmischen (Duplikate raus)
|
||||||
|
const seenIds = new Set();
|
||||||
|
const merged = [];
|
||||||
|
for (const m of fresh) { if (!seenIds.has(m.id)) { merged.push(m); seenIds.add(m.id); } }
|
||||||
|
for (const m of filtered) { if (!seenIds.has(m.id)) { merged.push(m); seenIds.add(m.id); } }
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [movieRaw, tvRaw, genreMap] = await Promise.all([
|
const [movieRaw, tvRaw, genreMap] = await Promise.all([
|
||||||
|
|||||||
Reference in New Issue
Block a user