Refactor FeaturedContent logo fetching logic for clarity and efficiency

This update enhances the FeaturedContent component by improving the logo fetching logic. Key changes include clearer variable naming, streamlined ID extraction, and optimized error handling. The logic now better handles logo source preferences and ensures that existing logos are used as fallbacks when necessary. Additionally, the dependency array for the fetch effect has been refined for better performance. Overall, these modifications enhance code readability and maintainability.
This commit is contained in:
tapframe 2025-05-04 18:35:04 +05:30
parent 86d1492573
commit bd1d8e30ec
5 changed files with 132 additions and 199 deletions

View file

@ -122,153 +122,132 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
if (!featuredContent || logoFetchInProgress.current) return; if (!featuredContent || logoFetchInProgress.current) return;
const fetchLogo = async () => { const fetchLogo = async () => {
// Set fetch in progress flag
logoFetchInProgress.current = true; logoFetchInProgress.current = true;
try { try {
const contentId = featuredContent.id; const contentId = featuredContent.id;
const contentData = featuredContent; // Use a clearer variable name
const currentLogo = contentData.logo;
// Get logo source preference from settings // Get preferences
const logoPreference = settings.logoSourcePreference || 'metahub'; // Default to metahub if not set const logoPreference = settings.logoSourcePreference || 'metahub';
const preferredLanguage = settings.tmdbLanguagePreference || 'en'; // Get preferred language const preferredLanguage = settings.tmdbLanguagePreference || 'en';
// Check if current logo matches preferences // Reset state for new fetch
const currentLogo = featuredContent.logo; setLogoUrl(null);
if (currentLogo) { setLogoLoadError(false);
const isCurrentMetahub = isMetahubUrl(currentLogo);
const isCurrentTmdb = isTmdbUrl(currentLogo); // Extract IDs
let imdbId: string | null = null;
// If logo already matches preference, use it if (contentData.id.startsWith('tt')) {
if ((logoPreference === 'metahub' && isCurrentMetahub) || imdbId = contentData.id;
(logoPreference === 'tmdb' && isCurrentTmdb)) { } else if ((contentData as any).imdbId) {
setLogoUrl(currentLogo); imdbId = (contentData as any).imdbId;
logoFetchInProgress.current = false; } else if ((contentData as any).externalIds?.imdb_id) {
return; imdbId = (contentData as any).externalIds.imdb_id;
}
} }
// Extract IMDB ID if available let tmdbId: string | null = null;
let imdbId = null; if (contentData.id.startsWith('tmdb:')) {
if (featuredContent.id.startsWith('tt')) { tmdbId = contentData.id.split(':')[1];
// If the ID itself is an IMDB ID } else if ((contentData as any).tmdb_id) {
imdbId = featuredContent.id; tmdbId = String((contentData as any).tmdb_id);
} else if ((featuredContent as any).imdbId) {
// Try to get IMDB ID from the content object if available
imdbId = (featuredContent as any).imdbId;
} }
// Extract TMDB ID if available // If we only have IMDB ID, try to find TMDB ID proactively
let tmdbId = null; if (imdbId && !tmdbId) {
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`;
try { try {
const response = await fetch(metahubUrl, { method: 'HEAD' }); const tmdbService = TMDBService.getInstance();
if (response.ok) { const foundData = await tmdbService.findTMDBIdByIMDB(imdbId);
setLogoUrl(metahubUrl); if (foundData) {
logoFetchInProgress.current = false; tmdbId = String(foundData);
return; // Exit if Metahub logo was found
} }
} catch (error) { } catch (findError) {
// Removed logger.warn // 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 = contentData.type === 'series' ? 'tv' : 'movie';
const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie'; let finalLogoUrl: string | null = null;
try { let primaryAttempted = false;
const tmdbService = TMDBService.getInstance(); let fallbackAttempted = false;
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
// --- Logo Fetching Logic ---
if (logoUrl) {
setLogoUrl(logoUrl); if (logoPreference === 'metahub') {
} else if (currentLogo) { // Primary: Metahub (needs imdbId)
// 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
if (imdbId) { if (imdbId) {
primaryAttempted = true;
const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`;
try { try {
const response = await fetch(metahubUrl, { method: 'HEAD' }); const response = await fetch(metahubUrl, { method: 'HEAD' });
if (response.ok) { if (response.ok) {
setLogoUrl(metahubUrl); finalLogoUrl = metahubUrl;
} else if (currentLogo) {
// If Metahub fails too, use existing logo if any
setLogoUrl(currentLogo);
} }
} catch (error) { } catch (error) { /* Log if needed */ }
// Removed logger.warn }
if (currentLogo) setLogoUrl(currentLogo);
} // Fallback: TMDB (needs tmdbId)
} else if (currentLogo) { if (!finalLogoUrl && tmdbId) {
// Use existing logo if we don't have IMDB ID fallbackAttempted = true;
setLogoUrl(currentLogo); 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) { } catch (error) {
// Removed logger.error // logger.error('[FeaturedContent] Error in fetchLogo:', error);
// Optionally set a fallback logo or handle the error state setLogoLoadError(true);
setLogoUrl(featuredContent.logo ?? null); // Fallback to initial logo or null
} finally { } finally {
logoFetchInProgress.current = false; logoFetchInProgress.current = false;
} }
}; };
// Trigger fetch when content changes
fetchLogo(); fetchLogo();
}, [featuredContent?.id, settings.logoSourcePreference, settings.tmdbLanguagePreference]); }, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference]);
// Load poster and logo // Load poster and logo
useEffect(() => { useEffect(() => {

View file

@ -286,9 +286,10 @@ const HeroSection: React.FC<HeroSectionProps> = ({
})); }));
const parallaxImageStyle = useAnimatedStyle(() => ({ const parallaxImageStyle = useAnimatedStyle(() => ({
width: '100%', width: '120%',
height: '120%', height: '100%',
top: '-10%', top: '-10%',
left: '-10%',
transform: [ transform: [
{ {
translateY: interpolate( translateY: interpolate(
@ -302,7 +303,7 @@ const HeroSection: React.FC<HeroSectionProps> = ({
scale: interpolate( scale: interpolate(
dampedScrollY.value, dampedScrollY.value,
[0, 150, 300], [0, 150, 300],
[1.1, 1.02, 0.95], [1.05, 1.02, 0.99],
Extrapolate.CLAMP Extrapolate.CLAMP
) )
} }

View file

@ -196,7 +196,15 @@ export const useMetadataAssets = (
else if (shouldFetchLogo && logoFetchInProgress.current) { else if (shouldFetchLogo && logoFetchInProgress.current) {
logger.log(`[useMetadataAssets:Logo] Skipping logo fetch because logoFetchInProgress is true.`); 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 // Fetch banner image based on logo source preference - optimized version
useEffect(() => { useEffect(() => {

View file

@ -686,7 +686,8 @@ const AppNavigator = () => {
/> />
<Stack.Screen <Stack.Screen
name="Metadata" name="Metadata"
component={MetadataScreen as any} component={MetadataScreen}
options={{ headerShown: false, animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="Streams" name="Streams"
@ -700,22 +701,27 @@ const AppNavigator = () => {
<Stack.Screen <Stack.Screen
name="Player" name="Player"
component={VideoPlayer as any} component={VideoPlayer as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="Catalog" name="Catalog"
component={CatalogScreen as any} component={CatalogScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="Addons" name="Addons"
component={AddonsScreen as any} component={AddonsScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="Search" name="Search"
component={SearchScreen as any} component={SearchScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="CatalogSettings" name="CatalogSettings"
component={CatalogSettingsScreen as any} component={CatalogSettingsScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="HomeScreenSettings" name="HomeScreenSettings"
@ -765,10 +771,12 @@ const AppNavigator = () => {
<Stack.Screen <Stack.Screen
name="Calendar" name="Calendar"
component={CalendarScreen as any} component={CalendarScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="NotificationSettings" name="NotificationSettings"
component={NotificationSettingsScreen as any} component={NotificationSettingsScreen as any}
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
/> />
<Stack.Screen <Stack.Screen
name="MDBListSettings" name="MDBListSettings"

View file

@ -348,48 +348,11 @@ const LogoSourceSettings = () => {
settings.logoSourcePreference || 'metahub' settings.logoSourcePreference || 'metahub'
); );
// TMDB Language Preference
const [selectedTmdbLanguage, setSelectedTmdbLanguage] = useState<string>(
settings.tmdbLanguagePreference || 'en'
);
// Make sure logoSource stays in sync with settings // Make sure logoSource stays in sync with settings
useEffect(() => { useEffect(() => {
setLogoSource(settings.logoSourcePreference || 'metahub'); setLogoSource(settings.logoSourcePreference || 'metahub');
}, [settings.logoSourcePreference]); }, [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 // Selected example show
const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]); 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}`); 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 // Get TMDB logo and banner
try { try {
const apiKey = TMDB_API_KEY; const apiKey = TMDB_API_KEY;
@ -451,15 +417,15 @@ const LogoSourceSettings = () => {
// Find initial logo (prefer selectedTmdbLanguage, then 'en') // Find initial logo (prefer selectedTmdbLanguage, then 'en')
let initialLogoPath: string | null = null; let initialLogoPath: string | null = null;
let initialLanguage = selectedTmdbLanguage; let initialLanguage = preferredTmdbLanguage;
// First try to find a logo in the user's preferred language // 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) { if (preferredLogo) {
initialLogoPath = preferredLogo.file_path; initialLogoPath = preferredLogo.file_path;
initialLanguage = selectedTmdbLanguage; initialLanguage = preferredTmdbLanguage;
logger.log(`[LogoSourceSettings] Found initial ${selectedTmdbLanguage} TMDB logo for ${show.name}`); logger.log(`[LogoSourceSettings] Found initial ${preferredTmdbLanguage} TMDB logo for ${show.name}`);
} else { } else {
// Fallback to English logo // Fallback to English logo
const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en'); 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) { if (initialLogoPath) {
setTmdbLogo(`https://image.tmdb.org/t/p/original${initialLogoPath}`); setTmdbLogo(`https://image.tmdb.org/t/p/original${initialLogoPath}`);
setSelectedTmdbLanguage(initialLanguage); // Set selected language based on found logo
} else { } else {
logger.warn(`[LogoSourceSettings] No valid initial TMDB logo found for ${show.name}`); logger.warn(`[LogoSourceSettings] No valid initial TMDB logo found for ${show.name}`);
} }
@ -588,9 +553,6 @@ const LogoSourceSettings = () => {
// Handle TMDB language selection // Handle TMDB language selection
const handleTmdbLanguageSelect = (languageCode: string) => { const handleTmdbLanguageSelect = (languageCode: string) => {
// First set local state for immediate UI updates
setSelectedTmdbLanguage(languageCode);
// Update the preview logo if possible // Update the preview logo if possible
if (tmdbLogosData) { if (tmdbLogosData) {
const selectedLogoData = tmdbLogosData.find(logo => logo.iso_639_1 === languageCode); const selectedLogoData = tmdbLogosData.find(logo => logo.iso_639_1 === languageCode);
@ -606,6 +568,9 @@ const LogoSourceSettings = () => {
saveLanguagePreference(languageCode); saveLanguagePreference(languageCode);
}; };
// Get preferred language directly from settings for UI rendering
const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en';
// Save language preference with proper persistence // Save language preference with proper persistence
const saveLanguagePreference = async (languageCode: string) => { const saveLanguagePreference = async (languageCode: string) => {
logger.log(`[LogoSourceSettings] Saving TMDB language preference: ${languageCode}`); 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 // First use the settings hook to update the setting - this is crucial
updateSetting('tmdbLanguagePreference', languageCode); 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 // Clear any cached logo data
await AsyncStorage.removeItem('_last_logos_'); await AsyncStorage.removeItem('_last_logos_');
@ -875,7 +812,7 @@ const LogoSourceSettings = () => {
key={langCode} // Use the unique code as key key={langCode} // Use the unique code as key
style={[ style={[
styles.languageItem, styles.languageItem,
selectedTmdbLanguage === langCode && styles.selectedLanguageItem preferredTmdbLanguage === langCode && styles.selectedLanguageItem
]} ]}
onPress={() => handleTmdbLanguageSelect(langCode)} onPress={() => handleTmdbLanguageSelect(langCode)}
activeOpacity={0.7} activeOpacity={0.7}
@ -884,7 +821,7 @@ const LogoSourceSettings = () => {
<Text <Text
style={[ style={[
styles.languageItemText, styles.languageItemText,
selectedTmdbLanguage === langCode && styles.selectedLanguageItemText preferredTmdbLanguage === langCode && styles.selectedLanguageItemText
]} ]}
> >
{(langCode || '').toUpperCase() || '??'} {(langCode || '').toUpperCase() || '??'}