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;
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(() => {

View file

@ -286,9 +286,10 @@ const HeroSection: React.FC<HeroSectionProps> = ({
}));
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<HeroSectionProps> = ({
scale: interpolate(
dampedScrollY.value,
[0, 150, 300],
[1.1, 1.02, 0.95],
[1.05, 1.02, 0.99],
Extrapolate.CLAMP
)
}

View file

@ -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(() => {

View file

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

View file

@ -348,48 +348,11 @@ const LogoSourceSettings = () => {
settings.logoSourcePreference || 'metahub'
);
// TMDB Language Preference
const [selectedTmdbLanguage, setSelectedTmdbLanguage] = useState<string>(
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 = () => {
<Text
style={[
styles.languageItemText,
selectedTmdbLanguage === langCode && styles.selectedLanguageItemText
preferredTmdbLanguage === langCode && styles.selectedLanguageItemText
]}
>
{(langCode || '').toUpperCase() || '??'}