From af82eee3f18a9b074ab6f3f89f1ad903593e4aef Mon Sep 17 00:00:00 2001 From: tapframe Date: Fri, 4 Jul 2025 17:33:47 +0530 Subject: [PATCH] minor fixes --- src/hooks/useMetadataAssets.ts | 527 +++++++++++++++------------------ 1 file changed, 231 insertions(+), 296 deletions(-) diff --git a/src/hooks/useMetadataAssets.ts b/src/hooks/useMetadataAssets.ts index b52cc65..7918c51 100644 --- a/src/hooks/useMetadataAssets.ts +++ b/src/hooks/useMetadataAssets.ts @@ -1,7 +1,50 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef, useCallback } from 'react'; import { logger } from '../utils/logger'; import { TMDBService } from '../services/tmdbService'; import { isMetahubUrl, isTmdbUrl } from '../utils/logoUtils'; +import { Image } from 'expo-image'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +// Cache for image availability checks +const imageAvailabilityCache: Record = {}; + +// Helper function to check image availability with caching +const checkImageAvailability = async (url: string): Promise => { + // Check memory cache first + if (imageAvailabilityCache[url] !== undefined) { + return imageAvailabilityCache[url]; + } + + // Check AsyncStorage cache + try { + const cachedResult = await AsyncStorage.getItem(`image_available:${url}`); + if (cachedResult !== null) { + const isAvailable = cachedResult === 'true'; + imageAvailabilityCache[url] = isAvailable; + return isAvailable; + } + } catch (error) { + // Ignore AsyncStorage errors + } + + // Perform actual check + try { + const response = await fetch(url, { method: 'HEAD' }); + const isAvailable = response.ok; + + // Update caches + imageAvailabilityCache[url] = isAvailable; + try { + await AsyncStorage.setItem(`image_available:${url}`, isAvailable ? 'true' : 'false'); + } catch (error) { + // Ignore AsyncStorage errors + } + + return isAvailable; + } catch (error) { + return false; + } +}; export const useMetadataAssets = ( metadata: any, @@ -45,12 +88,10 @@ export const useMetadataAssets = ( const preferenceIsMetahub = settings.logoSourcePreference === 'metahub'; // Always clear logo on preference change to force proper refresh - setMetadata((prevMetadata: any) => ({ - ...prevMetadata!, - logo: undefined - })); - - logger.log(`[useMetadataAssets] Preference changed to ${settings.logoSourcePreference}, forcing refresh of all assets`); + setMetadata((prevMetadata: any) => ({ + ...prevMetadata!, + logo: undefined + })); } }, [settings.logoSourcePreference, setMetadata]); @@ -59,7 +100,7 @@ export const useMetadataAssets = ( setLogoLoadError(false); }, [metadata?.logo]); - // Fetch logo immediately for TMDB content - with guard against recursive updates + // Optimized logo fetching useEffect(() => { const logoPreference = settings.logoSourcePreference || 'metahub'; const currentLogoUrl = metadata?.logo; @@ -67,124 +108,89 @@ export const useMetadataAssets = ( // Determine if we need to fetch a new logo if (!currentLogoUrl) { - logger.log(`[useMetadataAssets:Logo] Condition check: No current logo exists. Proceeding with fetch.`); shouldFetchLogo = true; } else { const isCurrentLogoMetahub = isMetahubUrl(currentLogoUrl); const isCurrentLogoTmdb = isTmdbUrl(currentLogoUrl); if (logoPreference === 'tmdb' && !isCurrentLogoTmdb) { - logger.log(`[useMetadataAssets:Logo] Condition check: Preference is TMDB, but current logo is not TMDB (${currentLogoUrl}). Proceeding with fetch.`); shouldFetchLogo = true; } else if (logoPreference === 'metahub' && !isCurrentLogoMetahub) { - logger.log(`[useMetadataAssets:Logo] Condition check: Preference is Metahub, but current logo is not Metahub (${currentLogoUrl}). Proceeding with fetch.`); shouldFetchLogo = true; - } else { - logger.log(`[useMetadataAssets:Logo] Condition check: Skipping fetch. Preference (${logoPreference}) matches existing logo source. Current logo: ${currentLogoUrl}`); } } // Guard against infinite loops by checking if we're already fetching if (shouldFetchLogo && !logoFetchInProgress.current) { - logger.log(`[useMetadataAssets:Logo] Starting logo fetch. Current metadata logo: ${currentLogoUrl}`); logoFetchInProgress.current = true; const fetchLogo = async () => { // Clear existing logo before fetching new one to avoid briefly showing wrong logo - // Only do this if we decided to fetch because of a mismatch or non-existence if (shouldFetchLogo) { - logger.log(`[useMetadataAssets:Logo] Clearing existing logo in metadata state before fetch.`); - setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: undefined })); + setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: undefined })); } try { - // Get logo source preference from settings - // const logoPreference = settings.logoSourcePreference || 'metahub'; // Already defined above const preferredLanguage = settings.tmdbLanguagePreference || 'en'; - logger.log(`[useMetadataAssets:Logo] Fetching logo. Preference: ${logoPreference}, Language: ${preferredLanguage}, IMDB ID: ${imdbId}`); - if (logoPreference === 'metahub' && imdbId) { - // Metahub path - direct fetch without HEAD request for speed - logger.log(`[useMetadataAssets:Logo] Preference is Metahub. Attempting Metahub fetch for ${imdbId}.`); + // Metahub path - with cached availability check const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - try { - // Verify Metahub image exists to prevent showing broken images - logger.log(`[useMetadataAssets:Logo] Checking Metahub logo existence: ${metahubUrl}`); - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - // Update metadata with Metahub logo - logger.log(`[useMetadataAssets:Logo] Metahub logo found. Updating metadata state.`); - setMetadata((prevMetadata: any) => { - logger.log(`[useMetadataAssets:Logo] setMetadata called with Metahub logo: ${metahubUrl}`); - return { ...prevMetadata!, logo: metahubUrl }; - }); - } else { - logger.warn(`[useMetadataAssets:Logo] Metahub logo HEAD request failed with status ${response.status} for ${imdbId}`); - } - } catch (error) { - logger.error(`[useMetadataAssets:Logo] Error checking Metahub logo:`, error); + const isAvailable = await checkImageAvailability(metahubUrl); + if (isAvailable) { + // Preload the image + await Image.prefetch(metahubUrl); + + // Update metadata with Metahub logo + setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: metahubUrl })); } } else if (logoPreference === 'tmdb') { // TMDB path - optimized flow - logger.log(`[useMetadataAssets:Logo] Preference is TMDB. Attempting TMDB fetch.`); let tmdbId: string | null = null; let contentType = type === 'series' ? 'tv' : 'movie'; // Extract or find TMDB ID in one step if (id.startsWith('tmdb:')) { tmdbId = id.split(':')[1]; - logger.log(`[useMetadataAssets:Logo] Extracted TMDB ID from route ID: ${tmdbId}`); } else if (imdbId) { - logger.log(`[useMetadataAssets:Logo] Attempting to find TMDB ID from IMDB ID: ${imdbId}`); - // Only look up TMDB ID if we don't already have it try { const tmdbService = TMDBService.getInstance(); const foundId = await tmdbService.findTMDBIdByIMDB(imdbId); if (foundId) { tmdbId = String(foundId); setFoundTmdbId(tmdbId); // Save for banner fetching - logger.log(`[useMetadataAssets:Logo] Found TMDB ID: ${tmdbId}`); - } else { - logger.warn(`[useMetadataAssets:Logo] Could not find TMDB ID for IMDB ID: ${imdbId}`); } } catch (error) { - logger.error(`[useMetadataAssets:Logo] Error finding TMDB ID:`, error); + // Handle error silently } } else { - logger.warn(`[useMetadataAssets:Logo] Cannot attempt TMDB fetch: No TMDB ID in route and no IMDB ID provided.`); + const parsedId = parseInt(id, 10); + if (!isNaN(parsedId)) { + tmdbId = String(parsedId); + } } if (tmdbId) { try { // Direct fetch - avoid multiple service calls - logger.log(`[useMetadataAssets:Logo] Fetching TMDB logo for ${contentType} ID: ${tmdbId}, Language: ${preferredLanguage}`); const tmdbService = TMDBService.getInstance(); const logoUrl = await tmdbService.getContentLogo(contentType as 'tv' | 'movie', tmdbId, preferredLanguage); if (logoUrl) { - logger.log(`[useMetadataAssets:Logo] TMDB logo found. Updating metadata state.`); - setMetadata((prevMetadata: any) => { - logger.log(`[useMetadataAssets:Logo] setMetadata called with TMDB logo: ${logoUrl}`); - return { ...prevMetadata!, logo: logoUrl }; - }); - } else { - logger.warn(`[useMetadataAssets:Logo] No TMDB logo found for ${contentType}/${tmdbId}.`); + // Preload the image + await Image.prefetch(logoUrl); + + setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: logoUrl })); } } catch (error) { - logger.error(`[useMetadataAssets:Logo] Error fetching TMDB logo:`, error); + // Handle error silently } - } else { - logger.warn(`[useMetadataAssets:Logo] Skipping TMDB logo fetch as no TMDB ID was determined.`); } - } else { - logger.log(`[useMetadataAssets:Logo] Preference not Metahub and no IMDB ID, or preference not TMDB. No logo fetched.`); } } catch (error) { - logger.error(`[useMetadataAssets:Logo] Error in outer fetchLogo try block:`, error); + // Handle error silently } finally { - logger.log(`[useMetadataAssets:Logo] Finished logo fetch attempt.`); logoFetchInProgress.current = false; } }; @@ -192,58 +198,40 @@ export const useMetadataAssets = ( // Execute fetch without awaiting fetchLogo(); } - // Add logging for when fetch is skipped due to already fetching - else if (shouldFetchLogo && logoFetchInProgress.current) { - logger.log(`[useMetadataAssets:Logo] Skipping logo fetch because logoFetchInProgress is true.`); - } }, [ id, type, imdbId, - metadata?.logo, // Depend on the logo value itself, not the whole object + metadata?.logo, settings.logoSourcePreference, settings.tmdbLanguagePreference, - setMetadata // Keep setMetadata, but ensure it's memoized in parent + setMetadata ]); - // Fetch banner image based on logo source preference - optimized version - useEffect(() => { - // Skip if no metadata or already completed with the correct source - if (!metadata) { - logger.log(`[useMetadataAssets:Banner] Skipping banner fetch: No metadata.`); - return; + // Optimized banner fetching + const fetchBanner = useCallback(async () => { + if (!metadata) return; + + setLoadingBanner(true); + + // Show fallback banner immediately to prevent blank state + const fallbackBanner = metadata?.banner || metadata?.poster || null; + if (fallbackBanner && !bannerImage) { + setBannerImage(fallbackBanner); + setBannerSource('default'); } - // Check if we need to refresh the banner based on source - const currentPreference = settings.logoSourcePreference || 'metahub'; - logger.log(`[useMetadataAssets:Banner] Checking banner fetch. Preference: ${currentPreference}, Current Banner Source: ${bannerSource}, Forced Refresh Done: ${forcedBannerRefreshDone.current}`); - - if (bannerSource === currentPreference && forcedBannerRefreshDone.current) { - logger.log(`[useMetadataAssets:Banner] Skipping fetch: Banner already loaded with correct source (${currentPreference}).`); - return; // Already have the correct source, no need to refresh - } - - const fetchBanner = async () => { - logger.log(`[useMetadataAssets:Banner] Starting banner fetch.`); - setLoadingBanner(true); - - // Show fallback banner immediately to prevent blank state - const fallbackBanner = metadata?.banner || metadata?.poster || null; - if (fallbackBanner && !bannerImage) { - setBannerImage(fallbackBanner); - setBannerSource('default'); - logger.log(`[useMetadataAssets:Banner] Setting immediate fallback banner: ${fallbackBanner}`); - } + try { + const currentPreference = settings.logoSourcePreference || 'metahub'; + const preferredLanguage = settings.tmdbLanguagePreference || 'en'; + const contentType = type === 'series' ? 'tv' : 'movie'; + // Try to get a banner from the preferred source let finalBanner: string | null = null; let bannerSourceType: 'tmdb' | 'metahub' | 'default' = 'default'; - - try { - // Extract all possible IDs at once - const preferredLanguage = settings.tmdbLanguagePreference || 'en'; - const contentType = type === 'series' ? 'tv' : 'movie'; - - // Get TMDB ID once + + // TMDB path + if (currentPreference === 'tmdb') { let tmdbId = null; if (id.startsWith('tmdb:')) { tmdbId = id.split(':')[1]; @@ -252,213 +240,160 @@ export const useMetadataAssets = ( } else if ((metadata as any).tmdbId) { tmdbId = (metadata as any).tmdbId; } else if (imdbId) { - // Last attempt: Look up TMDB ID if we haven't yet - logger.log(`[useMetadataAssets:Banner] Attempting TMDB ID lookup from IMDB ID: ${imdbId} for banner fetch.`); try { const tmdbService = TMDBService.getInstance(); const foundId = await tmdbService.findTMDBIdByIMDB(imdbId); if (foundId) { - tmdbId = String(foundId); - logger.log(`[useMetadataAssets:Banner] Found TMDB ID: ${tmdbId}`); - } else { - logger.warn(`[useMetadataAssets:Banner] Could not find TMDB ID for IMDB ID: ${imdbId}`); - } - } catch (lookupError) { - logger.error(`[useMetadataAssets:Banner] Error looking up TMDB ID:`, lookupError); - } - } - - logger.log(`[useMetadataAssets:Banner] Determined TMDB ID for banner fetch: ${tmdbId}`); - - // Default fallback to use if nothing else works - - if (currentPreference === 'tmdb' && tmdbId) { - // TMDB direct path - logger.log(`[useMetadataAssets:Banner] Preference is TMDB. Attempting TMDB banner fetch for ${contentType}/${tmdbId}.`); - const endpoint = contentType === 'tv' ? 'tv' : 'movie'; - - try { - // Use TMDBService instead of direct fetch with hardcoded API key - const tmdbService = TMDBService.getInstance(); - logger.log(`[useMetadataAssets:Banner] Fetching TMDB details for ${endpoint}/${tmdbId}`); - - try { - // Get details with backdrop path using TMDBService - let details; - let images = null; - - // Step 1: Get basic details - if (endpoint === 'movie') { - details = await tmdbService.getMovieDetails(tmdbId); - logger.log(`[useMetadataAssets:Banner] TMDB getMovieDetails result:`, details ? `Found backdrop: ${!!details.backdrop_path}, Found poster: ${!!details.poster_path}` : 'null'); - - // Step 2: Get images separately if details succeeded (This call might not be needed for banner) - // if (details) { - // try { - // await tmdbService.getMovieImages(tmdbId, preferredLanguage); - // logger.log(`[useMetadataAssets:Banner] Got movie images for ${tmdbId}`); - // } catch (imageError) { - // logger.warn(`[useMetadataAssets:Banner] Could not get movie images: ${imageError}`); - // } - //} - } else { // TV Show - details = await tmdbService.getTVShowDetails(Number(tmdbId)); - logger.log(`[useMetadataAssets:Banner] TMDB getTVShowDetails result:`, details ? `Found backdrop: ${!!details.backdrop_path}, Found poster: ${!!details.poster_path}` : 'null'); - - // Step 2: Get images separately if details succeeded (This call might not be needed for banner) - // if (details) { - // try { - // await tmdbService.getTvShowImages(tmdbId, preferredLanguage); - // logger.log(`[useMetadataAssets:Banner] Got TV images for ${tmdbId}`); - // } catch (imageError) { - // logger.warn(`[useMetadataAssets:Banner] Could not get TV images: ${imageError}`); - // } - // } - } - - // Check if we have a backdrop path from details - if (details && details.backdrop_path) { - finalBanner = tmdbService.getImageUrl(details.backdrop_path); - bannerSourceType = 'tmdb'; - logger.log(`[useMetadataAssets:Banner] Using TMDB backdrop from details: ${finalBanner}`); - } - // If no backdrop, try poster as fallback - else if (details && details.poster_path) { - logger.warn(`[useMetadataAssets:Banner] No TMDB backdrop available, using poster as fallback.`); - finalBanner = tmdbService.getImageUrl(details.poster_path); - bannerSourceType = 'tmdb'; - } - else { - logger.warn(`[useMetadataAssets:Banner] No TMDB backdrop or poster found for ${endpoint}/${tmdbId}. TMDB path failed.`); - // Explicitly set finalBanner to null if TMDB fails - finalBanner = null; - } - } catch (innerErr) { - logger.error(`[useMetadataAssets:Banner] Error fetching TMDB details/images:`, innerErr); - finalBanner = null; // Ensure failure case nullifies banner - } - } catch (err) { - logger.error(`[useMetadataAssets:Banner] TMDB service initialization error:`, err); - finalBanner = null; // Ensure failure case nullifies banner - } - } else if (currentPreference === 'metahub' && imdbId) { - // Metahub path - verify it exists to prevent broken images - logger.log(`[useMetadataAssets:Banner] Preference is Metahub. Attempting Metahub banner fetch for ${imdbId}.`); - const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - - try { - logger.log(`[useMetadataAssets:Banner] Checking Metahub banner existence: ${metahubUrl}`); - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - finalBanner = metahubUrl; - bannerSourceType = 'metahub'; - logger.log(`[useMetadataAssets:Banner] Metahub banner found: ${finalBanner}`); - } else { - logger.warn(`[useMetadataAssets:Banner] Metahub banner HEAD request failed with status ${response.status}, using default.`); - finalBanner = null; // Ensure fallback if Metahub fails + tmdbId = String(foundId); } } catch (error) { - logger.error(`[useMetadataAssets:Banner] Error checking Metahub banner:`, error); - finalBanner = null; // Ensure fallback if Metahub errors + // Handle error silently } - } else { - // This case handles: - // 1. Preference is TMDB but no tmdbId could be found. - // 2. Preference is Metahub but no imdbId was provided. - logger.log(`[useMetadataAssets:Banner] Skipping direct fetch: Preference=${currentPreference}, tmdbId=${tmdbId}, imdbId=${imdbId}. Will rely on default/fallback.`); - finalBanner = null; // Explicitly nullify banner if preference conditions aren't met - } - - // Fallback logic if preferred source failed or wasn't attempted - if (!finalBanner) { - logger.log(`[useMetadataAssets:Banner] Preferred source (${currentPreference}) did not yield a banner. Checking fallbacks.`); - // Fallback 1: Try the *other* source if the preferred one failed - if (currentPreference === 'tmdb' && imdbId) { // If preferred was TMDB, try Metahub - logger.log(`[useMetadataAssets:Banner] Fallback: Trying Metahub for ${imdbId}.`); - const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - try { - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - finalBanner = metahubUrl; - bannerSourceType = 'metahub'; - logger.log(`[useMetadataAssets:Banner] Fallback Metahub banner found: ${finalBanner}`); - } else { - logger.warn(`[useMetadataAssets:Banner] Fallback Metahub HEAD failed: ${response.status}`); - } - } catch (fallbackError) { - logger.error(`[useMetadataAssets:Banner] Fallback Metahub check error:`, fallbackError); - } - } else if (currentPreference === 'metahub' && tmdbId) { // If preferred was Metahub, try TMDB - logger.log(`[useMetadataAssets:Banner] Fallback: Trying TMDB for ${contentType}/${tmdbId}.`); - const endpoint = contentType === 'tv' ? 'tv' : 'movie'; - try { - const tmdbService = TMDBService.getInstance(); - let details = endpoint === 'movie' ? await tmdbService.getMovieDetails(tmdbId) : await tmdbService.getTVShowDetails(Number(tmdbId)); - if (details?.backdrop_path) { - finalBanner = tmdbService.getImageUrl(details.backdrop_path); - bannerSourceType = 'tmdb'; - logger.log(`[useMetadataAssets:Banner] Fallback TMDB banner found (backdrop): ${finalBanner}`); - } else if (details?.poster_path) { - finalBanner = tmdbService.getImageUrl(details.poster_path); - bannerSourceType = 'tmdb'; - logger.log(`[useMetadataAssets:Banner] Fallback TMDB banner found (poster): ${finalBanner}`); - } else { - logger.warn(`[useMetadataAssets:Banner] Fallback TMDB fetch found no backdrop or poster.`); - } - } catch (fallbackError) { - logger.error(`[useMetadataAssets:Banner] Fallback TMDB check error:`, fallbackError); - } - } - - // Fallback 2: Use metadata banner/poster if other source also failed - if (!finalBanner) { - logger.log(`[useMetadataAssets:Banner] Fallback source also failed or not applicable. Using metadata.banner or metadata.poster.`); - finalBanner = metadata?.banner || metadata?.poster || null; - bannerSourceType = 'default'; - if (finalBanner) { - logger.log(`[useMetadataAssets:Banner] Using default banner from metadata: ${finalBanner}`); - } else { - logger.warn(`[useMetadataAssets:Banner] No default banner found in metadata either.`); - } - } } - // Set the final state - logger.log(`[useMetadataAssets:Banner] Final decision: Setting banner to ${finalBanner} (Source: ${bannerSourceType})`); - - // Only update if the banner actually changed to avoid unnecessary re-renders - if (finalBanner !== bannerImage || bannerSourceType !== bannerSource) { - setBannerImage(finalBanner); - setBannerSource(bannerSourceType); // Track the source of the final image - logger.log(`[useMetadataAssets:Banner] Banner updated from ${bannerImage} to ${finalBanner}`); - } else { - logger.log(`[useMetadataAssets:Banner] Banner unchanged, skipping update`); + if (tmdbId) { + try { + const tmdbService = TMDBService.getInstance(); + const endpoint = contentType === 'tv' ? 'tv' : 'movie'; + + const details = endpoint === 'movie' + ? await tmdbService.getMovieDetails(tmdbId) + : await tmdbService.getTVShowDetails(Number(tmdbId)); + + if (details?.backdrop_path) { + finalBanner = tmdbService.getImageUrl(details.backdrop_path); + bannerSourceType = 'tmdb'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } + } + else if (details?.poster_path) { + finalBanner = tmdbService.getImageUrl(details.poster_path); + bannerSourceType = 'tmdb'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } + } + } catch (error) { + // Handle error silently + } } + } + // Metahub path + else if (currentPreference === 'metahub' && imdbId) { + const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - forcedBannerRefreshDone.current = true; // Mark this cycle as complete - - } catch (error) { - logger.error(`[useMetadataAssets:Banner] Error in outer fetchBanner try block:`, error); - // Ensure fallback to default even on outer error - const defaultBanner = metadata?.banner || metadata?.poster || null; - - // Only set if it's different from current banner - if (defaultBanner !== bannerImage) { - setBannerImage(defaultBanner); - setBannerSource('default'); - logger.log(`[useMetadataAssets:Banner] Setting default banner due to outer error: ${defaultBanner}`); - } else { - logger.log(`[useMetadataAssets:Banner] Default banner already set, skipping update`); + const isAvailable = await checkImageAvailability(metahubUrl); + if (isAvailable) { + finalBanner = metahubUrl; + bannerSourceType = 'metahub'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } } - } finally { - logger.log(`[useMetadataAssets:Banner] Finished banner fetch attempt.`); - setLoadingBanner(false); } - }; - - fetchBanner(); + + // If preferred source failed, try fallback + if (!finalBanner) { + // Try the other source + if (currentPreference === 'tmdb' && imdbId) { + const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; + + const isAvailable = await checkImageAvailability(metahubUrl); + if (isAvailable) { + finalBanner = metahubUrl; + bannerSourceType = 'metahub'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } + } + } + else if (currentPreference === 'metahub') { + // Try TMDB as fallback + let tmdbId = null; + if (id.startsWith('tmdb:')) { + tmdbId = id.split(':')[1]; + } else if (foundTmdbId) { + tmdbId = foundTmdbId; + } else if ((metadata as any).tmdbId) { + tmdbId = (metadata as any).tmdbId; + } + + if (tmdbId) { + try { + const tmdbService = TMDBService.getInstance(); + const endpoint = contentType === 'tv' ? 'tv' : 'movie'; + + const details = endpoint === 'movie' + ? await tmdbService.getMovieDetails(tmdbId) + : await tmdbService.getTVShowDetails(Number(tmdbId)); + + if (details?.backdrop_path) { + finalBanner = tmdbService.getImageUrl(details.backdrop_path); + bannerSourceType = 'tmdb'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } + } + else if (details?.poster_path) { + finalBanner = tmdbService.getImageUrl(details.poster_path); + bannerSourceType = 'tmdb'; + + // Preload the image + if (finalBanner) { + await Image.prefetch(finalBanner); + } + } + } catch (error) { + // Handle error silently + } + } + } + + // Final fallback to metadata + if (!finalBanner) { + finalBanner = metadata?.banner || metadata?.poster || null; + bannerSourceType = 'default'; + } + } + + // Update state if the banner changed + if (finalBanner !== bannerImage || bannerSourceType !== bannerSource) { + setBannerImage(finalBanner); + setBannerSource(bannerSourceType); + } + + forcedBannerRefreshDone.current = true; + } catch (error) { + // Use default banner on error + const defaultBanner = metadata?.banner || metadata?.poster || null; + if (defaultBanner !== bannerImage) { + setBannerImage(defaultBanner); + setBannerSource('default'); + } + } finally { + setLoadingBanner(false); + } + }, [metadata, id, type, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference, foundTmdbId, bannerImage, bannerSource]); - }, [metadata, id, type, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference, setMetadata, foundTmdbId, bannerSource]); // Added bannerSource dependency to re-evaluate if it changes unexpectedly + // Fetch banner when needed + useEffect(() => { + const currentPreference = settings.logoSourcePreference || 'metahub'; + + if (bannerSource !== currentPreference && !forcedBannerRefreshDone.current) { + fetchBanner(); + } + }, [fetchBanner, bannerSource, settings.logoSourcePreference]); return { bannerImage, @@ -467,6 +402,6 @@ export const useMetadataAssets = ( foundTmdbId, setLogoLoadError, setBannerImage, - bannerSource, // Export banner source for debugging + bannerSource, }; }; \ No newline at end of file