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 MAX_OTHER_LANGUAGE_TOTAL = 2;
|
||||
|
||||
function buildParams(mediaKind, minVotes) {
|
||||
function buildParams(mediaKind, minVotes, dateFrom) {
|
||||
return {
|
||||
watch_region: 'DE',
|
||||
with_watch_providers: provider.tmdbId,
|
||||
with_watch_monetization_types: 'flatrate',
|
||||
sort_by: 'popularity.desc',
|
||||
'vote_count.gte': minVotes,
|
||||
...(mediaKind === 'movie'
|
||||
? { 'primary_release_date.gte': minDate }
|
||||
: { 'first_air_date.gte': minDate }),
|
||||
...(dateFrom
|
||||
? (mediaKind === 'movie' ? { 'primary_release_date.gte': dateFrom } : { 'first_air_date.gte': dateFrom })
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchPages(mediaKind, minVotes, pageCount) {
|
||||
const params = buildParams(mediaKind, minVotes);
|
||||
async function fetchPages(mediaKind, minVotes, pageCount, dateFrom) {
|
||||
const params = buildParams(mediaKind, minVotes, dateFrom);
|
||||
const path = mediaKind === 'movie' ? '/discover/movie' : '/discover/tv';
|
||||
const calls = [];
|
||||
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 || []);
|
||||
}
|
||||
|
||||
// Robuster Fallback in drei Stufen, garantiert genug Kandidaten:
|
||||
// 1) hohe Schwelle (bekannte Titel), 2) mittlere, 3) niedrige (kleine Kataloge wie RTL+)
|
||||
// "Ganz frisch"-Fenster: letzte 90 Tage, sehr niedrige Vote-Schwelle.
|
||||
// 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) {
|
||||
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));
|
||||
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));
|
||||
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) {
|
||||
const more = await fetchPages(mediaKind, 0, 2);
|
||||
const more = await fetchPages(mediaKind, 0, 2, minDate);
|
||||
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); }
|
||||
}
|
||||
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([
|
||||
|
||||
Reference in New Issue
Block a user