Refactor logo fetching logic in LogoSourceSettings and MetadataScreen components

Update the LogoSourceSettings component to improve logo source selection and enhance the MetadataScreen's banner fetching mechanism. Implement better error handling and loading states for a smoother user experience during logo and banner retrieval. Ensure that user preferences are effectively utilized in the fetching process.
This commit is contained in:
tapframe 2025-05-03 19:32:19 +05:30
parent ba834ed3a8
commit 7aba05f384
2 changed files with 552 additions and 445 deletions

1
package-lock.json generated
View file

@ -7,6 +7,7 @@
"": {
"name": "nuvio",
"version": "1.0.0",
"hasInstallScript": true,
"dependencies": {
"@expo/metro-runtime": "~4.0.1",
"@expo/vector-icons": "^14.1.0",

View file

@ -96,6 +96,12 @@ const LogoSourceSettings = () => {
const [metahubBanner, setMetahubBanner] = useState<string | null>(null);
const [loadingLogos, setLoadingLogos] = useState(true);
// State for TMDB language selection
const [selectedTmdbLanguage, setSelectedTmdbLanguage] = useState<string>('en');
// Store unique language codes as strings
const [uniqueTmdbLanguages, setUniqueTmdbLanguages] = useState<string[]>([]);
const [tmdbLogosData, setTmdbLogosData] = useState<Array<{ iso_639_1: string; file_path: string }> | null>(null);
// Load example logos for selected show
useEffect(() => {
fetchExampleLogos(selectedShow);
@ -108,6 +114,10 @@ const LogoSourceSettings = () => {
setMetahubLogo(null);
setTmdbBanner(null);
setMetahubBanner(null);
// Reset unique languages and logos data
setUniqueTmdbLanguages([]);
setTmdbLogosData(null);
setSelectedTmdbLanguage('en'); // Reset to default language
try {
const tmdbService = TMDBService.getInstance();
@ -119,33 +129,50 @@ const LogoSourceSettings = () => {
// Get TMDB logo and banner
try {
// Manually fetch images from TMDB API
const apiKey = TMDB_API_KEY;
const endpoint = contentType === 'tv' ? 'tv' : 'movie';
const response = await fetch(`https://api.themoviedb.org/3/${endpoint}/${tmdbId}/images?api_key=${apiKey}`);
const imagesData = await response.json();
// Get TMDB logo
// Store all TMDB logos data and extract unique languages
if (imagesData.logos && imagesData.logos.length > 0) {
// Look for English logo first
let logoPath = null;
setTmdbLogosData(imagesData.logos);
// Filter for logos with valid language codes and get unique codes
const validLogoLanguages = imagesData.logos
.map((logo: { iso_639_1: string | null }) => logo.iso_639_1)
.filter((lang: string | null): lang is string => lang !== null && typeof lang === 'string');
// Explicitly type the Set and resulting array
const uniqueCodes: string[] = [...new Set<string>(validLogoLanguages)];
setUniqueTmdbLanguages(uniqueCodes);
// Find initial logo (prefer 'en')
let initialLogoPath: string | null = null;
let initialLanguage = 'en';
const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en');
// First try to find an English logo
const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) =>
logo.iso_639_1 === 'en'
);
if (englishLogo) {
logoPath = englishLogo.file_path;
initialLogoPath = englishLogo.file_path;
initialLanguage = 'en';
logger.log(`[LogoSourceSettings] Found initial English TMDB logo for ${show.name}`);
} else if (imagesData.logos[0]) {
// Fallback to the first logo
logoPath = imagesData.logos[0].file_path;
// Fallback to the first available logo
initialLogoPath = imagesData.logos[0].file_path;
initialLanguage = imagesData.logos[0].iso_639_1;
logger.log(`[LogoSourceSettings] No English logo, using first available (${initialLanguage}) TMDB logo for ${show.name}`);
}
if (logoPath) {
const tmdbLogoUrl = `https://image.tmdb.org/t/p/original${logoPath}`;
setTmdbLogo(tmdbLogoUrl);
logger.log(`[LogoSourceSettings] Got ${show.name} TMDB logo: ${tmdbLogoUrl}`);
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}`);
}
} else {
logger.warn(`[LogoSourceSettings] No TMDB logos found in response for ${show.name}`);
setUniqueTmdbLanguages([]); // Ensure it's empty if no logos
}
// Get TMDB banner (backdrop)
@ -294,6 +321,20 @@ const LogoSourceSettings = () => {
);
};
// Handle TMDB language selection
const handleTmdbLanguageSelect = (languageCode: string) => {
setSelectedTmdbLanguage(languageCode);
if (tmdbLogosData) {
const selectedLogoData = tmdbLogosData.find(logo => logo.iso_639_1 === languageCode);
if (selectedLogoData) {
setTmdbLogo(`https://image.tmdb.org/t/p/original${selectedLogoData.file_path}`);
logger.log(`[LogoSourceSettings] Switched TMDB logo to language: ${languageCode}`);
} else {
logger.warn(`[LogoSourceSettings] Could not find logo data for selected language: ${languageCode}`);
}
}
};
return (
<SafeAreaView style={[styles.container]}>
<StatusBar barStyle="light-content" />
@ -402,6 +443,39 @@ const LogoSourceSettings = () => {
{renderLogoExample(tmdbLogo, tmdbBanner, loadingLogos)}
<Text style={styles.logoSourceLabel}>{selectedShow.name} logo from TMDB</Text>
</View>
{/* TMDB Language Selector */}
{uniqueTmdbLanguages.length > 1 && (
<View style={styles.languageSelectorContainer}>
<Text style={styles.languageSelectorLabel}>Available logo languages:</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.languageScrollContent}
>
{/* Iterate over unique language codes */}
{uniqueTmdbLanguages.map((langCode) => (
<TouchableOpacity
key={langCode} // Use the unique code as key
style={[
styles.languageItem,
selectedTmdbLanguage === langCode && styles.selectedLanguageItem
]}
onPress={() => handleTmdbLanguageSelect(langCode)}
>
<Text
style={[
styles.languageItemText,
selectedTmdbLanguage === langCode && styles.selectedLanguageItemText
]}
>
{(langCode || '').toUpperCase() || '??'}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}
</TouchableOpacity>
</View>
@ -415,9 +489,9 @@ const LogoSourceSettings = () => {
</ScrollView>
</SafeAreaView>
);
};
};
const styles = StyleSheet.create({
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.darkBackground,
@ -557,6 +631,38 @@ const styles = StyleSheet.create({
fontSize: 12,
marginTop: 4,
},
languageSelectorContainer: {
marginTop: 16,
},
languageSelectorLabel: {
color: colors.mediumEmphasis,
fontSize: 13,
marginBottom: 8,
},
languageScrollContent: {
paddingRight: 16, // Match container padding
},
languageItem: {
paddingHorizontal: 12,
paddingVertical: 6,
backgroundColor: colors.elevation1,
borderRadius: 16,
marginRight: 8,
borderWidth: 1,
borderColor: colors.elevation3,
},
selectedLanguageItem: {
backgroundColor: colors.primary,
borderColor: colors.primary,
},
languageItemText: {
color: colors.mediumEmphasis,
fontSize: 13,
fontWeight: '600',
},
selectedLanguageItemText: {
color: colors.white,
},
bannerContainer: {
height: 120,
width: '100%',
@ -595,6 +701,6 @@ const styles = StyleSheet.create({
paddingVertical: 6,
borderRadius: 4,
},
});
});
export default LogoSourceSettings;
export default LogoSourceSettings;