From 4a09ea74c1ea43cbcd71e92444a32c2a87e3b748 Mon Sep 17 00:00:00 2001 From: Jos Vooges | STH Date: Mon, 21 Sep 2026 04:00:06 +0200 Subject: [PATCH] Fix TMDB trailers missing for Dutch locale detail pages. Request include_video_language=nl,en,null so English YouTube trailers are not filtered out when language=nl-NL. --- apps/master-api/src/metadata/service.ts | 52 +++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/apps/master-api/src/metadata/service.ts b/apps/master-api/src/metadata/service.ts index 1822631..cc2578e 100644 --- a/apps/master-api/src/metadata/service.ts +++ b/apps/master-api/src/metadata/service.ts @@ -351,6 +351,8 @@ export class MetadataService { api_key: this.tmdbApiKey, language: TMDB_LANG, append_to_response: "credits,videos,external_ids", + // Zonder dit filtert language=nl-NL YouTube-trailers weg (meestal alleen en). + include_video_language: "nl,en,null", }); const res = await fetch(`https://api.themoviedb.org/3/${path}?${params}`); if (!res.ok) { @@ -370,7 +372,13 @@ export class MetadataService { crew?: Array<{ name?: string; job?: string }>; }; videos?: { - results?: Array<{ key?: string; site?: string; type?: string; official?: boolean }>; + results?: Array<{ + key?: string; + site?: string; + type?: string; + official?: boolean; + iso_639_1?: string; + }>; }; external_ids?: { imdb_id?: string | null }; }; @@ -391,10 +399,46 @@ export class MetadataService { .filter((n, i, arr) => arr.indexOf(n) === i) .slice(0, 4); - const trailers = (data.videos?.results ?? []) - .filter((v) => v.site === "YouTube" && v.key && (v.type === "Trailer" || v.type === "Teaser")) + let videoResults = data.videos?.results ?? []; + // Fallback: aparte videos-call als append leeg blijft + if (!videoResults.length) { + try { + const vidParams = new URLSearchParams({ + api_key: this.tmdbApiKey, + include_video_language: "nl,en,null", + }); + const vidRes = await fetch( + `https://api.themoviedb.org/3/${path}/videos?${vidParams}` + ); + if (vidRes.ok) { + const vidData = (await vidRes.json()) as { results?: typeof videoResults }; + videoResults = vidData.results ?? []; + } + } catch { + // ignore + } + } + + const trailerRank = (v: { type?: string; official?: boolean; iso_639_1?: string }) => { + const typeScore = + v.type === "Trailer" ? 0 : v.type === "Teaser" ? 1 : v.type === "Clip" ? 2 : 3; + const officialScore = v.official === false ? 1 : 0; + const langScore = v.iso_639_1 === "nl" ? 0 : v.iso_639_1 === "en" ? 1 : 2; + return typeScore * 100 + officialScore * 10 + langScore; + }; + const trailers = videoResults + .filter( + (v) => + v.site === "YouTube" && + v.key && + (v.type === "Trailer" || v.type === "Teaser" || v.type === "Clip") + ) + .sort((a, b) => trailerRank(a) - trailerRank(b)) .slice(0, 3) - .map((v) => ({ source: v.key!, type: "Trailer" as const })); + .map((v) => ({ + source: v.key!, + type: (v.type === "Teaser" ? "Teaser" : "Trailer") as const, + })); const imdbFromExt = data.external_ids?.imdb_id?.trim(); const resolvedImdb =