episodes not fetching backdrop fix tablet layout
This commit is contained in:
parent
ef1c34a9c0
commit
786e06b27f
4 changed files with 75 additions and 39 deletions
|
|
@ -128,6 +128,7 @@ const TabletStreamsLayout: React.FC<TabletStreamsLayoutProps> = ({
|
|||
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<TabletStreamsLayoutProps> = ({
|
|||
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<TabletStreamsLayoutProps> = ({
|
|||
}
|
||||
}, [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<TabletStreamsLayoutProps> = ({
|
|||
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<TabletStreamsLayoutProps> = ({
|
|||
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<TabletStreamsLayoutProps> = ({
|
|||
style={StyleSheet.absoluteFillObject}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
onLoad={handleBackdropLoad}
|
||||
onError={handleBackdropError}
|
||||
/>
|
||||
</Animated.View>
|
||||
<LinearGradient
|
||||
|
|
|
|||
|
|
@ -875,6 +875,13 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
}
|
||||
|
||||
// Commit final metadata once and cache it
|
||||
// Clear banner field if TMDB enrichment is enabled to prevent flash
|
||||
if (settings.enrichMetadataWithTMDB) {
|
||||
finalMetadata = {
|
||||
...finalMetadata,
|
||||
banner: undefined, // Let useMetadataAssets handle banner via TMDB
|
||||
};
|
||||
}
|
||||
setMetadata(finalMetadata);
|
||||
cacheService.setMetadata(id, type, finalMetadata);
|
||||
const isInLib = catalogService.getLibraryItems().some(item => item.id === id);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue