From d691189973f8c2208f8bac4ed4c59b773595ff37 Mon Sep 17 00:00:00 2001 From: tapframe Date: Mon, 13 Oct 2025 12:15:26 +0530 Subject: [PATCH] fixed backdrop zoom in laoding overlay. --- react-native-vlc-media-player | 1 + src/components/home/FeaturedContent.tsx | 166 ++++--------------- src/components/player/AndroidVideoPlayer.tsx | 6 +- src/hooks/useFeaturedContent.ts | 12 +- 4 files changed, 44 insertions(+), 141 deletions(-) create mode 160000 react-native-vlc-media-player diff --git a/react-native-vlc-media-player b/react-native-vlc-media-player new file mode 160000 index 00000000..22fae0eb --- /dev/null +++ b/react-native-vlc-media-player @@ -0,0 +1 @@ +Subproject commit 22fae0eb0964990c030889e2be3ed7a3d5c92a45 diff --git a/src/components/home/FeaturedContent.tsx b/src/components/home/FeaturedContent.tsx index 9157cdca..8437f6af 100644 --- a/src/components/home/FeaturedContent.tsx +++ b/src/components/home/FeaturedContent.tsx @@ -28,8 +28,6 @@ import Animated, { import { StreamingContent } from '../../services/catalogService'; import { SkeletonFeatured } from './SkeletonLoaders'; import { hasValidLogoFormat, isTmdbUrl } from '../../utils/logoUtils'; -import { useSettings } from '../../hooks/useSettings'; -import { TMDBService } from '../../services/tmdbService'; import { logger } from '../../utils/logger'; import { useTheme } from '../../contexts/ThemeContext'; @@ -150,15 +148,11 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin const [showSkeleton, setShowSkeleton] = useState(true); const [logoError, setLogoError] = useState(false); const [bannerError, setBannerError] = useState(false); - const { settings } = useSettings(); const logoOpacity = useSharedValue(0); const bannerOpacity = useSharedValue(0); const posterOpacity = useSharedValue(0); const prevContentIdRef = useRef(null); - // Add state for tracking logo load errors const [logoLoadError, setLogoLoadError] = useState(false); - // Add a ref to track logo fetch in progress - const logoFetchInProgress = useRef(false); const firstRenderTsRef = useRef(nowMs()); const lastContentChangeTsRef = useRef(0); @@ -251,130 +245,29 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin setLogoLoadError(false); }, [featuredContent?.id]); - // Fetch logo when enrichment is enabled; otherwise only use addon logo + // Use logo from featuredContent data (already processed by useFeaturedContent hook) useEffect(() => { - if (!featuredContent || logoFetchInProgress.current) return; + if (!featuredContent) { + setLogoUrl(null); + setLogoLoadError(false); + return; + } - const fetchLogo = async () => { - logoFetchInProgress.current = true; - const t0 = nowMs(); - logger.info('[FeaturedContent] fetchLogo:start', { id: featuredContent?.id, type: featuredContent?.type }); + // Simply use the logo that's already been processed by the useFeaturedContent hook + const logo = featuredContent.logo; + logger.info('[FeaturedContent] using logo from data', { + id: featuredContent.id, + name: featuredContent.name, + hasLogo: Boolean(logo), + logo: logo, + logoSource: logo ? (isTmdbUrl(logo) ? 'tmdb' : 'addon') : 'none', + type: featuredContent.type + }); - try { - const contentId = featuredContent.id; - const contentData = featuredContent; // Use a clearer variable name - const currentLogo = contentData.logo; - - // Get language preference (only relevant when enrichment is enabled) - const preferredLanguage = settings.tmdbLanguagePreference || 'en'; - - // If enrichment is disabled, use addon logo and don't fetch from external sources - if (!settings.enrichMetadataWithTMDB) { - logger.info('[FeaturedContent] enrichment disabled, checking for addon logo', { - hasLogo: !!contentData.logo, - logo: contentData.logo, - isExternal: contentData.logo ? isTmdbUrl(contentData.logo) : false, - isTmdb: contentData.logo ? isTmdbUrl(contentData.logo) : false - }); - - // If we have an addon logo, use it and don't fetch external logos - if (contentData.logo) { - logger.info('[FeaturedContent] enrichment disabled, using addon logo', { logo: contentData.logo }); - setLogoUrl(contentData.logo); - logoFetchInProgress.current = false; - return; - } - // If no addon logo, don't fetch external logos when enrichment is disabled - logger.info('[FeaturedContent] enrichment disabled, no addon logo available'); - logoFetchInProgress.current = false; - return; - } - - // Reset state for new fetch only if switching to a different item - if (prevContentIdRef.current !== contentId) { - setLogoUrl(null); - } - setLogoLoadError(false); - - // Extract IDs (only when enrichment is enabled) - 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; - } - - 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); - } - - // If we only have IMDB ID, try to find TMDB ID proactively (only when enrichment is enabled) - if (imdbId && !tmdbId) { - try { - const tmdbService = TMDBService.getInstance(); - const foundData = await tmdbService.findTMDBIdByIMDB(imdbId); - if (foundData) { - tmdbId = String(foundData); - } - } catch (findError) { - // logger.warn(`[FeaturedContent] Failed to find TMDB ID for ${imdbId}:`, findError); - } - } - - const tmdbType = contentData.type === 'series' ? 'tv' : 'movie'; - let finalLogoUrl: string | null = null; - let primaryAttempted = false; - let fallbackAttempted = false; - - // --- Logo Fetching Logic (TMDB only when enrichment is enabled) --- - logger.debug('[FeaturedContent] fetchLogo:ids', { imdbId, tmdbId, lang: preferredLanguage }); - - // Try TMDB if we have a TMDB id - if (tmdbId) { - primaryAttempted = true; - try { - const tmdbService = TMDBService.getInstance(); - const tTmdb = nowMs(); - const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage); - if (logoUrl) { - finalLogoUrl = logoUrl; - logger.debug('[FeaturedContent] fetchLogo:tmdb:ok', { url: logoUrl, duration: since(tTmdb) }); - } - } catch (error) { /* Log if needed */ } - } - - // --- Set Final Logo --- - if (finalLogoUrl) { - setLogoUrl(finalLogoUrl); - logger.info('[FeaturedContent] fetchLogo:done', { id: contentId, result: 'tmdb', url: finalLogoUrl, duration: since(t0) }); - } else if (currentLogo) { - // Use existing logo only if primary and fallback failed or weren't applicable - setLogoUrl(currentLogo); - logger.info('[FeaturedContent] fetchLogo:done', { id: contentId, result: 'addon', url: currentLogo, duration: since(t0) }); - } else { - // No logo found from any source - setLogoLoadError(true); - logger.warn('[FeaturedContent] fetchLogo:none', { id: contentId, primaryAttempted, fallbackAttempted, duration: since(t0) }); - // logger.warn(`[FeaturedContent] No logo found for ${contentData.name} (${contentId}) with preference ${logoPreference}. Primary attempted: ${primaryAttempted}, Fallback attempted: ${fallbackAttempted}`); - } - - } catch (error) { - // logger.error('[FeaturedContent] Error in fetchLogo:', error); - setLogoLoadError(true); - logger.error('[FeaturedContent] fetchLogo:error', { error: String(error), duration: since(t0) }); - } finally { - logoFetchInProgress.current = false; - } - }; - - // Trigger fetch when content changes - fetchLogo(); - }, [featuredContent, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]); + setLogoUrl(logo || null); + setLogoLoadError(!logo); + setLogoError(false); // Reset any previous errors + }, [featuredContent]); // Load poster and logo useEffect(() => { @@ -475,17 +368,20 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin // Load logo if available with enhanced timing if (logoUrl) { const tLogo = nowMs(); + // Try to preload but don't fail if it times out - still show the logo const logoSuccess = await preloadImage(logoUrl); if (logoSuccess) { - logoOpacity.value = withDelay(500, withTiming(1, { - duration: 600, - easing: Easing.out(Easing.cubic) - })); - logger.debug('[FeaturedContent] logo:ready', { id: contentId, duration: since(tLogo) }); + logger.debug('[FeaturedContent] logo:preload:success', { id: contentId, duration: since(tLogo) }); } else { - setLogoLoadError(true); - logger.warn('[FeaturedContent] logo:failed', { id: contentId, duration: since(tLogo) }); + logger.debug('[FeaturedContent] logo:preload:failed', { id: contentId, duration: since(tLogo) }); } + + // Always animate in the logo since we have the URL + logoOpacity.value = withDelay(500, withTiming(1, { + duration: 600, + easing: Easing.out(Easing.cubic) + })); + logger.debug('[FeaturedContent] logo:animated', { id: contentId }); } logger.info('[FeaturedContent] images:load:done', { id: contentId, total: since(t0) }); }; @@ -495,7 +391,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin const onLogoLoadError = () => { setLogoLoaded(true); // Treat error as "loaded" to stop spinner - setLogoError(true); + setLogoLoadError(true); logger.warn('[FeaturedContent] logo:onError', { id: featuredContent?.id, url: logoUrl }); }; diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index c166ab20..d0215b48 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react'; -import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState } from 'react-native'; +import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState, Image } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Video, { VideoRef, SelectedTrack, SelectedTrackType, BufferingStrategyType, ViewType } from 'react-native-video'; import FastImage from '@d11/react-native-fast-image'; @@ -3129,10 +3129,10 @@ const AndroidVideoPlayer: React.FC = () => { opacity: backdropImageOpacityAnim } ]}> - )} diff --git a/src/hooks/useFeaturedContent.ts b/src/hooks/useFeaturedContent.ts index 6cbe5d09..ca2ecdf4 100644 --- a/src/hooks/useFeaturedContent.ts +++ b/src/hooks/useFeaturedContent.ts @@ -197,7 +197,7 @@ export function useFeaturedContent() { logoSource: c.logo ? (isTmdbUrl(String(c.logo)) ? 'tmdb' : 'addon') : 'none', logo: c.logo || undefined, })); - logger.debug('[useFeaturedContent] catalogs:logos:details', { items: details }); + logger.info('[useFeaturedContent] catalogs:logos:details (enrich=true)', { items: details }); } catch {} } else { // When enrichment is disabled, prefer addon-provided logos; if missing, fetch basic meta to pull logo (like HeroSection) @@ -257,7 +257,7 @@ export function useFeaturedContent() { logoSource: c.logo ? (isTmdbUrl(String(c.logo)) ? 'tmdb' : 'addon') : 'none', logo: c.logo || undefined, })); - logger.debug('[useFeaturedContent] catalogs:logos:details (no-enrich)', { items: details }); + logger.info('[useFeaturedContent] catalogs:logos:details (no-enrich)', { items: details }); } catch {} } } @@ -294,7 +294,13 @@ export function useFeaturedContent() { if (formattedContent.length > 0) { persistentStore.featuredContent = formattedContent[0]; - setFeaturedContent(formattedContent[0]); + setFeaturedContent(formattedContent[0]); + logger.info('[useFeaturedContent] setting featuredContent', { + id: formattedContent[0].id, + name: formattedContent[0].name, + hasLogo: Boolean(formattedContent[0].logo), + logo: formattedContent[0].logo + }); currentIndexRef.current = 0; // Persist cache for fast startup (skipped when cache disabled) if (!DISABLE_CACHE) {