From 786e06b27faf3027df3565f0607186e389709267 Mon Sep 17 00:00:00 2001 From: tapframe Date: Thu, 23 Oct 2025 01:57:04 +0530 Subject: [PATCH] episodes not fetching backdrop fix tablet layout --- src/components/TabletStreamsLayout.tsx | 75 ++++++++++++++++++++------ src/hooks/useMetadata.ts | 7 +++ src/hooks/useMetadataAssets.ts | 25 ++------- src/screens/StreamsScreen.tsx | 7 +-- 4 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/components/TabletStreamsLayout.tsx b/src/components/TabletStreamsLayout.tsx index 5190492e..6bfbb508 100644 --- a/src/components/TabletStreamsLayout.tsx +++ b/src/components/TabletStreamsLayout.tsx @@ -128,6 +128,7 @@ const TabletStreamsLayout: React.FC = ({ const backdropOpacity = useSharedValue(0); const backdropScale = useSharedValue(1.05); const [backdropLoaded, setBackdropLoaded] = useState(false); + const [backdropError, setBackdropError] = useState(false); // Animation values for content panels const leftPanelOpacity = useSharedValue(0); @@ -135,10 +136,46 @@ const TabletStreamsLayout: React.FC = ({ const rightPanelOpacity = useSharedValue(0); const rightPanelTranslateX = useSharedValue(30); - // Get the backdrop source - const backdropSource = episodeImage ? { uri: episodeImage } : - bannerImage ? { uri: bannerImage } : - metadata?.poster ? { uri: metadata.poster } : undefined; + // Get the backdrop source - prioritize episode thumbnail, then show backdrop, then poster + // For episodes without thumbnails, use show's backdrop instead of poster + const backdropSource = React.useMemo(() => { + // Debug logging + if (__DEV__) { + console.log('[TabletStreamsLayout] Backdrop source selection:', { + episodeImage, + bannerImage, + metadataPoster: metadata?.poster, + episodeImageIsPoster: episodeImage === metadata?.poster, + backdropError + }); + } + + // If episodeImage failed to load, skip it and use backdrop + if (backdropError && episodeImage && episodeImage !== metadata?.poster) { + if (__DEV__) console.log('[TabletStreamsLayout] Episode thumbnail failed, falling back to backdrop'); + if (bannerImage) { + if (__DEV__) console.log('[TabletStreamsLayout] Using show backdrop (episode failed):', bannerImage); + return { uri: bannerImage }; + } + } + + // If episodeImage exists and is not the same as poster, use it (real episode thumbnail) + if (episodeImage && episodeImage !== metadata?.poster && !backdropError) { + if (__DEV__) console.log('[TabletStreamsLayout] Using episode thumbnail:', episodeImage); + return { uri: episodeImage }; + } + + // If episodeImage is the same as poster (fallback case), prioritize backdrop + if (bannerImage) { + if (__DEV__) console.log('[TabletStreamsLayout] Using show backdrop:', bannerImage); + return { uri: bannerImage }; + } + + // No fallback to poster images + + if (__DEV__) console.log('[TabletStreamsLayout] No backdrop source found'); + return undefined; + }, [episodeImage, bannerImage, metadata?.poster, backdropError]); // Animate backdrop when it loads useEffect(() => { @@ -173,18 +210,17 @@ const TabletStreamsLayout: React.FC = ({ } }, [backdropSource?.uri, backdropLoaded]); - // Reset animation when source changes + // Reset animation when episode changes useEffect(() => { - if (backdropSource?.uri) { - backdropOpacity.value = 0; - backdropScale.value = 1.05; - leftPanelOpacity.value = 0; - leftPanelTranslateX.value = -30; - rightPanelOpacity.value = 0; - rightPanelTranslateX.value = 30; - setBackdropLoaded(false); - } - }, [backdropSource?.uri]); + backdropOpacity.value = 0; + backdropScale.value = 1.05; + leftPanelOpacity.value = 0; + leftPanelTranslateX.value = -30; + rightPanelOpacity.value = 0; + rightPanelTranslateX.value = 30; + setBackdropLoaded(false); + setBackdropError(false); + }, [episodeImage]); // Animated styles for backdrop const backdropAnimatedStyle = useAnimatedStyle(() => ({ @@ -206,6 +242,12 @@ const TabletStreamsLayout: React.FC = ({ const handleBackdropLoad = () => { setBackdropLoaded(true); }; + + const handleBackdropError = () => { + if (__DEV__) console.log('[TabletStreamsLayout] Backdrop image failed to load:', backdropSource?.uri); + setBackdropError(true); + setBackdropLoaded(false); + }; const renderStreamContent = () => { if (showNoSourcesError) { @@ -284,7 +326,7 @@ const TabletStreamsLayout: React.FC = ({ theme={currentTheme} showLogos={settings.showScraperLogos} scraperLogo={(item.addonId && scraperLogos[item.addonId]) || (item as any).addon ? scraperLogos[(item.addonId || (item as any).addon) as string] || null : null} - showAlert={(t, m) => openAlert(t, m)} + showAlert={(t: string, m: string) => openAlert(t, m)} parentTitle={metadata?.name} parentType={type as 'movie' | 'series'} parentSeason={(type === 'series' || type === 'other') ? currentEpisode?.season_number : undefined} @@ -332,6 +374,7 @@ const TabletStreamsLayout: React.FC = ({ style={StyleSheet.absoluteFillObject} resizeMode={FastImage.resizeMode.cover} onLoad={handleBackdropLoad} + onError={handleBackdropError} /> item.id === id); diff --git a/src/hooks/useMetadataAssets.ts b/src/hooks/useMetadataAssets.ts index db2d8263..e9411a4e 100644 --- a/src/hooks/useMetadataAssets.ts +++ b/src/hooks/useMetadataAssets.ts @@ -251,16 +251,10 @@ export const useMetadataAssets = ( setLoadingBanner(true); - // Show fallback banner immediately to prevent blank state - const fallbackBanner = metadata?.banner || metadata?.poster || null; - if (fallbackBanner && !bannerImage) { - setBannerImage(fallbackBanner); - setBannerSource('default'); - } // If enrichment is disabled, use addon banner and don't fetch from external sources if (!settings.enrichMetadataWithTMDB) { - const addonBanner = metadata?.banner || metadata?.poster || null; + const addonBanner = metadata?.banner || null; if (addonBanner && addonBanner !== bannerImage) { setBannerImage(addonBanner); setBannerSource('default'); @@ -312,15 +306,6 @@ export const useMetadataAssets = ( finalBanner = tmdbService.getImageUrl(details.backdrop_path); bannerSourceType = 'tmdb'; - // Preload the image - if (finalBanner) { - FastImage.preload([{ uri: finalBanner }]); - } - } - else if (details?.poster_path) { - finalBanner = tmdbService.getImageUrl(details.poster_path); - bannerSourceType = 'tmdb'; - // Preload the image if (finalBanner) { FastImage.preload([{ uri: finalBanner }]); @@ -332,9 +317,9 @@ export const useMetadataAssets = ( } } - // Final fallback to metadata + // Final fallback to metadata banner only if (!finalBanner) { - finalBanner = metadata?.banner || metadata?.poster || null; + finalBanner = metadata?.banner || null; bannerSourceType = 'default'; } @@ -346,8 +331,8 @@ export const useMetadataAssets = ( forcedBannerRefreshDone.current = true; } catch (error) { - // Use default banner on error - const defaultBanner = metadata?.banner || metadata?.poster || null; + // Use default banner on error (only addon banner) + const defaultBanner = metadata?.banner || null; if (defaultBanner !== bannerImage) { setBannerImage(defaultBanner); setBannerSource('default'); diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index 2ffe5bcf..461bce28 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -227,7 +227,7 @@ export const StreamsScreen = () => { // Get backdrop from metadata assets const setMetadataStub = useCallback(() => {}, []); - const memoizedSettings = useMemo(() => settings, [settings.logoSourcePreference, settings.tmdbLanguagePreference]); + const memoizedSettings = useMemo(() => settings, [settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]); const { bannerImage } = useMetadataAssets(metadata, id, type, imdbId, memoizedSettings, setMetadataStub); // Create styles using current theme colors @@ -1532,8 +1532,9 @@ export const StreamsScreen = () => { const path = currentEpisode.still_path || hydratedStill || ''; return tmdbService.getImageUrl(path, 'original'); } - return metadata?.poster || null; - }, [currentEpisode, metadata, episodeThumbnail, tmdbEpisodeOverride?.still_path]); + // No poster fallback + return null; + }, [currentEpisode, metadata, episodeThumbnail, tmdbEpisodeOverride?.still_path, settings.enrichMetadataWithTMDB]); // Effective TMDB fields for hero (series) const effectiveEpisodeVote = useMemo(() => {