From 3e3190db86419f85cd3dab4a53b156dbfb4d1601 Mon Sep 17 00:00:00 2001 From: Gauvain Date: Wed, 15 Jul 2026 18:54:30 +0200 Subject: [PATCH] fix(home): build episode thumb URLs from the matched ParentThumbItemId pair --- components/ContinueWatchingPoster.tsx | 9 ++++++--- components/home/TVHeroCarousel.tsx | 7 ++++--- components/tv/TVPosterCard.tsx | 7 ++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/components/ContinueWatchingPoster.tsx b/components/ContinueWatchingPoster.tsx index c0f4a2650..0f85f56b0 100644 --- a/components/ContinueWatchingPoster.tsx +++ b/components/ContinueWatchingPoster.tsx @@ -35,8 +35,10 @@ const ContinueWatchingPoster: React.FC = ({ return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; } if (item.Type === "Episode") { - if (item.ParentBackdropItemId && item.ParentThumbImageTag) { - return `${api?.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=389&quality=80&tag=${item.ParentThumbImageTag}`; + // Matched pair: the parent that owns the Thumb (ParentThumbItemId), not the + // backdrop owner — otherwise the Thumb tag is requested on the wrong item → black. + if (item.ParentThumbItemId && item.ParentThumbImageTag) { + return `${api?.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=389&quality=80&tag=${item.ParentThumbImageTag}`; } return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; @@ -61,7 +63,8 @@ const ContinueWatchingPoster: React.FC = ({ } return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; - }, [item]); + // useEpisodePoster in deps so flipping the prop re-computes the URL live. + }, [item, useEpisodePoster]); if (!url) return ; diff --git a/components/home/TVHeroCarousel.tsx b/components/home/TVHeroCarousel.tsx index 596f2d369..a62538013 100644 --- a/components/home/TVHeroCarousel.tsx +++ b/components/home/TVHeroCarousel.tsx @@ -65,10 +65,11 @@ const HeroCard: React.FC = React.memo( const posterUrl = useMemo(() => { if (!api) return null; - // For episodes, always use series thumb + // For episodes, always use series thumb. + // Matched pair: ParentThumbItemId owns the Thumb tag, not ParentBackdropItemId. if (item.Type === "Episode") { - if (item.ParentThumbImageTag) { - return `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=400&quality=80&tag=${item.ParentThumbImageTag}`; + if (item.ParentThumbItemId && item.ParentThumbImageTag) { + return `${api.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=400&quality=80&tag=${item.ParentThumbImageTag}`; } if (item.SeriesId) { return `${api.basePath}/Items/${item.SeriesId}/Images/Thumb?fillHeight=400&quality=80`; diff --git a/components/tv/TVPosterCard.tsx b/components/tv/TVPosterCard.tsx index 9a24a5034..3c80c2291 100644 --- a/components/tv/TVPosterCard.tsx +++ b/components/tv/TVPosterCard.tsx @@ -139,9 +139,10 @@ export const TVPosterCard: React.FC = ({ if (orientation === "horizontal") { // Episode: prefer series thumb image for consistent look (like hero section) if (item.Type === "Episode") { - // First try parent/series thumb (horizontal series artwork) - if (item.ParentBackdropItemId && item.ParentThumbImageTag) { - return `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=700&quality=80&tag=${item.ParentThumbImageTag}`; + // First try parent/series thumb (horizontal series artwork). + // Matched pair: ParentThumbItemId owns the Thumb tag, not ParentBackdropItemId. + if (item.ParentThumbItemId && item.ParentThumbImageTag) { + return `${api.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=700&quality=80&tag=${item.ParentThumbImageTag}`; } // Fall back to episode's own primary image if (item.ImageTags?.Primary) {