diff --git a/src/components/home/FeaturedContent.tsx b/src/components/home/FeaturedContent.tsx index eb4467f9c..2f88399df 100644 --- a/src/components/home/FeaturedContent.tsx +++ b/src/components/home/FeaturedContent.tsx @@ -122,153 +122,132 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat if (!featuredContent || logoFetchInProgress.current) return; const fetchLogo = async () => { - // Set fetch in progress flag logoFetchInProgress.current = true; try { const contentId = featuredContent.id; + const contentData = featuredContent; // Use a clearer variable name + const currentLogo = contentData.logo; - // Get logo source preference from settings - const logoPreference = settings.logoSourcePreference || 'metahub'; // Default to metahub if not set - const preferredLanguage = settings.tmdbLanguagePreference || 'en'; // Get preferred language + // Get preferences + const logoPreference = settings.logoSourcePreference || 'metahub'; + const preferredLanguage = settings.tmdbLanguagePreference || 'en'; - // Check if current logo matches preferences - const currentLogo = featuredContent.logo; - if (currentLogo) { - const isCurrentMetahub = isMetahubUrl(currentLogo); - const isCurrentTmdb = isTmdbUrl(currentLogo); - - // If logo already matches preference, use it - if ((logoPreference === 'metahub' && isCurrentMetahub) || - (logoPreference === 'tmdb' && isCurrentTmdb)) { - setLogoUrl(currentLogo); - logoFetchInProgress.current = false; - return; - } + // Reset state for new fetch + setLogoUrl(null); + setLogoLoadError(false); + + // Extract IDs + let imdbId: string | null = null; + if (contentData.id.startsWith('tt')) { + imdbId = contentData.id; + } else if ((contentData as any).imdbId) { + imdbId = (contentData as any).imdbId; + } else if ((contentData as any).externalIds?.imdb_id) { + imdbId = (contentData as any).externalIds.imdb_id; } - // Extract IMDB ID if available - let imdbId = null; - if (featuredContent.id.startsWith('tt')) { - // If the ID itself is an IMDB ID - imdbId = featuredContent.id; - } else if ((featuredContent as any).imdbId) { - // Try to get IMDB ID from the content object if available - imdbId = (featuredContent as any).imdbId; + let tmdbId: string | null = null; + if (contentData.id.startsWith('tmdb:')) { + tmdbId = contentData.id.split(':')[1]; + } else if ((contentData as any).tmdb_id) { + tmdbId = String((contentData as any).tmdb_id); } - // Extract TMDB ID if available - let tmdbId = null; - if (contentId.startsWith('tmdb:')) { - tmdbId = contentId.split(':')[1]; - } - - // First source based on preference - if (logoPreference === 'metahub' && imdbId) { - // Try to get logo from Metahub first - const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - + // If we only have IMDB ID, try to find TMDB ID proactively + if (imdbId && !tmdbId) { try { - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - setLogoUrl(metahubUrl); - logoFetchInProgress.current = false; - return; // Exit if Metahub logo was found + const tmdbService = TMDBService.getInstance(); + const foundData = await tmdbService.findTMDBIdByIMDB(imdbId); + if (foundData) { + tmdbId = String(foundData); } - } catch (error) { - // Removed logger.warn + } catch (findError) { + // logger.warn(`[FeaturedContent] Failed to find TMDB ID for ${imdbId}:`, findError); } - - // Fall back to TMDB if Metahub fails and we have a TMDB ID - if (tmdbId) { - const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie'; - try { - const tmdbService = TMDBService.getInstance(); - const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage); - - if (logoUrl) { - setLogoUrl(logoUrl); - } else if (currentLogo) { - // If TMDB fails too, use existing logo if any - setLogoUrl(currentLogo); - } - } catch (error) { - // Removed logger.error - if (currentLogo) setLogoUrl(currentLogo); - } - } else if (currentLogo) { - // Use existing logo if we don't have TMDB ID - setLogoUrl(currentLogo); - } - } else if (logoPreference === 'tmdb') { - // Try to get logo from TMDB first - if (tmdbId) { - const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie'; - try { - const tmdbService = TMDBService.getInstance(); - const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage); - - if (logoUrl) { - setLogoUrl(logoUrl); - logoFetchInProgress.current = false; - return; // Exit if TMDB logo was found - } - } catch (error) { - // Removed logger.error - } - } else if (imdbId) { - // If we have IMDB ID but no TMDB ID, try to find TMDB ID - try { - const tmdbService = TMDBService.getInstance(); - const foundTmdbId = await tmdbService.findTMDBIdByIMDB(imdbId); - - if (foundTmdbId) { - const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie'; - const logoUrl = await tmdbService.getContentLogo(tmdbType, foundTmdbId.toString(), preferredLanguage); - - if (logoUrl) { - setLogoUrl(logoUrl); - logoFetchInProgress.current = false; - return; // Exit if TMDB logo was found - } - } - } catch (error) { - // Removed logger.error - } - } - - // Fall back to Metahub if TMDB fails and we have an IMDB ID + } + + const tmdbType = contentData.type === 'series' ? 'tv' : 'movie'; + let finalLogoUrl: string | null = null; + let primaryAttempted = false; + let fallbackAttempted = false; + + // --- Logo Fetching Logic --- + + if (logoPreference === 'metahub') { + // Primary: Metahub (needs imdbId) if (imdbId) { + primaryAttempted = true; const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - try { const response = await fetch(metahubUrl, { method: 'HEAD' }); if (response.ok) { - setLogoUrl(metahubUrl); - } else if (currentLogo) { - // If Metahub fails too, use existing logo if any - setLogoUrl(currentLogo); + finalLogoUrl = metahubUrl; } - } catch (error) { - // Removed logger.warn - if (currentLogo) setLogoUrl(currentLogo); - } - } else if (currentLogo) { - // Use existing logo if we don't have IMDB ID - setLogoUrl(currentLogo); + } catch (error) { /* Log if needed */ } + } + + // Fallback: TMDB (needs tmdbId) + if (!finalLogoUrl && tmdbId) { + fallbackAttempted = true; + try { + const tmdbService = TMDBService.getInstance(); + const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage); + if (logoUrl) { + finalLogoUrl = logoUrl; + } + } catch (error) { /* Log if needed */ } + } + + } else { // logoPreference === 'tmdb' + // Primary: TMDB (needs tmdbId) + if (tmdbId) { + primaryAttempted = true; + try { + const tmdbService = TMDBService.getInstance(); + const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage); + if (logoUrl) { + finalLogoUrl = logoUrl; + } + } catch (error) { /* Log if needed */ } + } + + // Fallback: Metahub (needs imdbId) + if (!finalLogoUrl && imdbId) { + fallbackAttempted = true; + const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; + try { + const response = await fetch(metahubUrl, { method: 'HEAD' }); + if (response.ok) { + finalLogoUrl = metahubUrl; + } + } catch (error) { /* Log if needed */ } } } + + // --- Set Final Logo --- + if (finalLogoUrl) { + setLogoUrl(finalLogoUrl); + } else if (currentLogo) { + // Use existing logo only if primary and fallback failed or weren't applicable + setLogoUrl(currentLogo); + } else { + // No logo found from any source + setLogoLoadError(true); + // logger.warn(`[FeaturedContent] No logo found for ${contentData.name} (${contentId}) with preference ${logoPreference}. Primary attempted: ${primaryAttempted}, Fallback attempted: ${fallbackAttempted}`); + } + } catch (error) { - // Removed logger.error - // Optionally set a fallback logo or handle the error state - setLogoUrl(featuredContent.logo ?? null); // Fallback to initial logo or null + // logger.error('[FeaturedContent] Error in fetchLogo:', error); + setLogoLoadError(true); } finally { logoFetchInProgress.current = false; } }; + // Trigger fetch when content changes fetchLogo(); - }, [featuredContent?.id, settings.logoSourcePreference, settings.tmdbLanguagePreference]); + }, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference]); // Load poster and logo useEffect(() => { diff --git a/src/components/metadata/HeroSection.tsx b/src/components/metadata/HeroSection.tsx index cef9ec49d..be3c2119d 100644 --- a/src/components/metadata/HeroSection.tsx +++ b/src/components/metadata/HeroSection.tsx @@ -286,9 +286,10 @@ const HeroSection: React.FC = ({ })); const parallaxImageStyle = useAnimatedStyle(() => ({ - width: '100%', - height: '120%', + width: '120%', + height: '100%', top: '-10%', + left: '-10%', transform: [ { translateY: interpolate( @@ -302,7 +303,7 @@ const HeroSection: React.FC = ({ scale: interpolate( dampedScrollY.value, [0, 150, 300], - [1.1, 1.02, 0.95], + [1.05, 1.02, 0.99], Extrapolate.CLAMP ) } diff --git a/src/hooks/useMetadataAssets.ts b/src/hooks/useMetadataAssets.ts index 0b44bf094..5bd17f423 100644 --- a/src/hooks/useMetadataAssets.ts +++ b/src/hooks/useMetadataAssets.ts @@ -196,7 +196,15 @@ export const useMetadataAssets = ( else if (shouldFetchLogo && logoFetchInProgress.current) { logger.log(`[useMetadataAssets:Logo] Skipping logo fetch because logoFetchInProgress is true.`); } - }, [id, type, metadata, setMetadata, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference]); // Added tmdbLanguagePreference dependency + }, [ + id, + type, + imdbId, + metadata?.logo, // Depend on the logo value itself, not the whole object + settings.logoSourcePreference, + settings.tmdbLanguagePreference, + setMetadata // Keep setMetadata, but ensure it's memoized in parent + ]); // Fetch banner image based on logo source preference - optimized version useEffect(() => { diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 6720cda31..8d8c4b8c6 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -686,7 +686,8 @@ const AppNavigator = () => { /> { { { settings.logoSourcePreference || 'metahub' ); - // TMDB Language Preference - const [selectedTmdbLanguage, setSelectedTmdbLanguage] = useState( - settings.tmdbLanguagePreference || 'en' - ); - // Make sure logoSource stays in sync with settings useEffect(() => { setLogoSource(settings.logoSourcePreference || 'metahub'); }, [settings.logoSourcePreference]); - // Keep selectedTmdbLanguage in sync with settings - useEffect(() => { - setSelectedTmdbLanguage(settings.tmdbLanguagePreference || 'en'); - }, [settings.tmdbLanguagePreference]); - - // Force reload settings from AsyncStorage when component mounts - useEffect(() => { - const loadSettingsFromStorage = async () => { - try { - const settingsJson = await AsyncStorage.getItem('app_settings'); - if (settingsJson) { - const storedSettings = JSON.parse(settingsJson); - - // Update local state to match stored settings - if (storedSettings.logoSourcePreference) { - setLogoSource(storedSettings.logoSourcePreference); - } - - if (storedSettings.tmdbLanguagePreference) { - setSelectedTmdbLanguage(storedSettings.tmdbLanguagePreference); - } - - logger.log('[LogoSourceSettings] Successfully loaded settings from AsyncStorage'); - } - } catch (error) { - logger.error('[LogoSourceSettings] Error loading settings from AsyncStorage:', error); - } - }; - - loadSettingsFromStorage(); - }, []); - // Selected example show const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]); @@ -429,6 +392,9 @@ const LogoSourceSettings = () => { logger.log(`[LogoSourceSettings] Fetching ${show.name} with TMDB ID: ${tmdbId}, IMDB ID: ${imdbId}`); + // Get preferred language directly from settings + const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en'; + // Get TMDB logo and banner try { const apiKey = TMDB_API_KEY; @@ -451,15 +417,15 @@ const LogoSourceSettings = () => { // Find initial logo (prefer selectedTmdbLanguage, then 'en') let initialLogoPath: string | null = null; - let initialLanguage = selectedTmdbLanguage; + let initialLanguage = preferredTmdbLanguage; // First try to find a logo in the user's preferred language - const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === selectedTmdbLanguage); + const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === preferredTmdbLanguage); if (preferredLogo) { initialLogoPath = preferredLogo.file_path; - initialLanguage = selectedTmdbLanguage; - logger.log(`[LogoSourceSettings] Found initial ${selectedTmdbLanguage} TMDB logo for ${show.name}`); + initialLanguage = preferredTmdbLanguage; + logger.log(`[LogoSourceSettings] Found initial ${preferredTmdbLanguage} TMDB logo for ${show.name}`); } else { // Fallback to English logo const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en'); @@ -478,7 +444,6 @@ const LogoSourceSettings = () => { if (initialLogoPath) { setTmdbLogo(`https://image.tmdb.org/t/p/original${initialLogoPath}`); - setSelectedTmdbLanguage(initialLanguage); // Set selected language based on found logo } else { logger.warn(`[LogoSourceSettings] No valid initial TMDB logo found for ${show.name}`); } @@ -588,9 +553,6 @@ const LogoSourceSettings = () => { // Handle TMDB language selection const handleTmdbLanguageSelect = (languageCode: string) => { - // First set local state for immediate UI updates - setSelectedTmdbLanguage(languageCode); - // Update the preview logo if possible if (tmdbLogosData) { const selectedLogoData = tmdbLogosData.find(logo => logo.iso_639_1 === languageCode); @@ -606,6 +568,9 @@ const LogoSourceSettings = () => { saveLanguagePreference(languageCode); }; + // Get preferred language directly from settings for UI rendering + const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en'; + // Save language preference with proper persistence const saveLanguagePreference = async (languageCode: string) => { logger.log(`[LogoSourceSettings] Saving TMDB language preference: ${languageCode}`); @@ -614,34 +579,6 @@ const LogoSourceSettings = () => { // First use the settings hook to update the setting - this is crucial updateSetting('tmdbLanguagePreference', languageCode); - // For extra assurance, also save directly to AsyncStorage - // Get current settings from AsyncStorage - const settingsJson = await AsyncStorage.getItem('app_settings'); - - if (settingsJson) { - const currentSettings = JSON.parse(settingsJson); - - // Update the language preference - const updatedSettings = { - ...currentSettings, - tmdbLanguagePreference: languageCode - }; - - // Save back to AsyncStorage using await to ensure it completes - await AsyncStorage.setItem('app_settings', JSON.stringify(updatedSettings)); - logger.log(`[LogoSourceSettings] Successfully saved TMDB language preference '${languageCode}' to AsyncStorage`); - } else { - // If no settings exist yet, create new settings object with this preference - const newSettings = { - ...DEFAULT_SETTINGS, - tmdbLanguagePreference: languageCode - }; - - // Save to AsyncStorage - await AsyncStorage.setItem('app_settings', JSON.stringify(newSettings)); - logger.log(`[LogoSourceSettings] Created new settings with TMDB language preference '${languageCode}'`); - } - // Clear any cached logo data await AsyncStorage.removeItem('_last_logos_'); @@ -875,7 +812,7 @@ const LogoSourceSettings = () => { key={langCode} // Use the unique code as key style={[ styles.languageItem, - selectedTmdbLanguage === langCode && styles.selectedLanguageItem + preferredTmdbLanguage === langCode && styles.selectedLanguageItem ]} onPress={() => handleTmdbLanguageSelect(langCode)} activeOpacity={0.7} @@ -884,7 +821,7 @@ const LogoSourceSettings = () => { {(langCode || '').toUpperCase() || '??'}