Ios #14
5 changed files with 132 additions and 199 deletions
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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() || '??'}
|
||||
|
|
|
|||
Loading…
Reference in a new issue