diff --git a/src/components/home/ContentItem.tsx b/src/components/home/ContentItem.tsx index 71bac4f9..62c645a5 100644 --- a/src/components/home/ContentItem.tsx +++ b/src/components/home/ContentItem.tsx @@ -193,8 +193,9 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe } // If we've had an error, try metahub fallback - if (retryCount > 0 && !item.poster.includes('metahub.space')) { - return `https://images.metahub.space/poster/small/${item.id}/img`; + // Use addon poster if available, otherwise use placeholder + if (retryCount > 0 && !item.poster) { + return 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image'; } // For TMDB images, use smaller sizes @@ -204,7 +205,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe } // For metahub images, use smaller sizes - if (item.poster.includes('images.metahub.space')) { + if (item.poster.includes('placeholder')) { return item.poster.replace('/medium/', '/small/'); } @@ -268,7 +269,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe onError={(error) => { if (__DEV__) console.warn('Image load error for:', item.poster, error); // Try fallback URL on first error - if (retryCount === 0 && item.poster && !item.poster.includes('metahub.space')) { + if (retryCount === 0 && item.poster && !item.poster.includes('placeholder')) { setRetryCount(1); // Don't set error state yet, let it try the fallback return; diff --git a/src/components/home/FeaturedContent.tsx b/src/components/home/FeaturedContent.tsx index 79953914..75ef2165 100644 --- a/src/components/home/FeaturedContent.tsx +++ b/src/components/home/FeaturedContent.tsx @@ -27,7 +27,7 @@ import Animated, { } from 'react-native-reanimated'; import { StreamingContent } from '../../services/catalogService'; import { SkeletonFeatured } from './SkeletonLoaders'; -import { isValidMetahubLogo, hasValidLogoFormat, isMetahubUrl, isTmdbUrl } from '../../utils/logoUtils'; +import { hasValidLogoFormat, isTmdbUrl } from '../../utils/logoUtils'; import { useSettings } from '../../hooks/useSettings'; import { TMDBService } from '../../services/tmdbService'; import { logger } from '../../utils/logger'; @@ -268,16 +268,38 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin const currentLogo = contentData.logo; // Get preferences - const logoPreference = settings.logoSourcePreference || 'metahub'; + const logoPreference = settings.logoSourcePreference || 'tmdb'; 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 && !isTmdbUrl(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 + // Extract IDs (only when enrichment is enabled) let imdbId: string | null = null; if (contentData.id.startsWith('tt')) { imdbId = contentData.id; @@ -294,7 +316,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin tmdbId = String((contentData as any).tmdb_id); } - // If we only have IMDB ID, try to find TMDB ID proactively + // 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(); @@ -315,63 +337,18 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin // --- Logo Fetching Logic --- logger.debug('[FeaturedContent] fetchLogo:ids', { imdbId, tmdbId, preference: logoPreference, lang: preferredLanguage }); - if (logoPreference === 'metahub') { - // Primary: Metahub (needs imdbId) - if (imdbId) { - primaryAttempted = true; - const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - try { - const tHead = nowMs(); - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - finalLogoUrl = metahubUrl; - logger.debug('[FeaturedContent] fetchLogo:metahub:ok', { url: metahubUrl, duration: since(tHead) }); - } - } catch (error) { /* Log if needed */ } - } - - // Fallback: TMDB (needs tmdbId) - if (!finalLogoUrl && tmdbId) { - fallbackAttempted = 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:fallback:ok', { url: logoUrl, duration: since(tTmdb) }); - } - } catch (error) { /* Log if needed */ } - } - - } else { // logoPreference === 'tmdb' - // Primary: TMDB (needs tmdbId) - 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 */ } - } - - // Fallback: Metahub (needs imdbId) - if (!finalLogoUrl && imdbId) { - fallbackAttempted = true; - const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - try { - const tHead = nowMs(); - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - finalLogoUrl = metahubUrl; - logger.debug('[FeaturedContent] fetchLogo:metahub:fallback:ok', { url: metahubUrl, duration: since(tHead) }); - } - } catch (error) { /* Log if needed */ } - } + // Only try TMDB if preference is 'tmdb' and we have tmdbId + if (logoPreference === 'tmdb' && 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 --- @@ -400,7 +377,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin // Trigger fetch when content changes fetchLogo(); - }, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference]); + }, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]); // Load poster and logo useEffect(() => { diff --git a/src/hooks/useFeaturedContent.ts b/src/hooks/useFeaturedContent.ts index ace04ec2..d156d6b7 100644 --- a/src/hooks/useFeaturedContent.ts +++ b/src/hooks/useFeaturedContent.ts @@ -7,6 +7,7 @@ import { logger } from '../utils/logger'; import * as Haptics from 'expo-haptics'; import { useGenres } from '../contexts/GenreContext'; import { useSettings, settingsEmitter } from './useSettings'; +import { isTmdbUrl } from '../utils/logoUtils'; // Create a persistent store outside of the hook to maintain state between navigation const persistentStore = { @@ -19,7 +20,7 @@ const persistentStore = { showHeroSection: true, featuredContentSource: 'tmdb' as 'tmdb' | 'catalogs', selectedHeroCatalogs: [] as string[], - logoSourcePreference: 'metahub' as 'metahub' | 'tmdb', + logoSourcePreference: 'tmdb' as 'metahub' | 'tmdb', tmdbLanguagePreference: 'en' } }; @@ -132,7 +133,7 @@ export function useFeaturedContent() { // Then fetch logos for each item based on preference const tLogos = Date.now(); - const preference = settings.logoSourcePreference || 'metahub'; + const preference = settings.logoSourcePreference || 'tmdb'; const preferredLanguage = settings.tmdbLanguagePreference || 'en'; const fetchLogoForItem = async (item: StreamingContent): Promise => { @@ -209,7 +210,16 @@ export function useFeaturedContent() { } }; - formattedContent = await Promise.all(preFormattedContent.map(fetchLogoForItem)); + // Only fetch logos if enrichment is enabled + if (settings.enrichMetadataWithTMDB) { + formattedContent = await Promise.all(preFormattedContent.map(fetchLogoForItem)); + } else { + // When enrichment is disabled, just use the pre-formatted content with addon logos + formattedContent = preFormattedContent.map((item: StreamingContent) => ({ + ...item, + logo: item.logo && !isTmdbUrl(item.logo) ? item.logo : undefined + })); + } logger.info('[useFeaturedContent] logos:resolved', { count: formattedContent.length, duration: `${Date.now() - tLogos}ms`, preference }); } } else { @@ -247,7 +257,7 @@ export function useFeaturedContent() { const topItems = allItems.sort(() => Math.random() - 0.5).slice(0, 10); // Optionally enrich with logos based on preference for tmdb-sourced IDs - const preference = settings.logoSourcePreference || 'metahub'; + const preference = settings.logoSourcePreference || 'tmdb'; const preferredLanguage = settings.tmdbLanguagePreference || 'en'; const enrichLogo = async (item: any): Promise => { @@ -264,6 +274,17 @@ export function useFeaturedContent() { inLibrary: Boolean((item as any).inLibrary), }; try { + // If enrichment is disabled, use addon logo if available + if (!settings.enrichMetadataWithTMDB) { + if (base.logo && !isTmdbUrl(base.logo)) { + logger.debug('[useFeaturedContent] enrichment disabled, using addon logo', { name: item.name, logo: base.logo }); + return base; + } + logger.debug('[useFeaturedContent] enrichment disabled, no addon logo available', { name: item.name }); + return { ...base, logo: undefined }; + } + + // Only proceed with TMDB enrichment if enrichment is enabled const rawId = String(item.id); const isTmdb = rawId.startsWith('tmdb:'); const isImdb = rawId.startsWith('tt'); @@ -277,52 +298,44 @@ export function useFeaturedContent() { tmdbId = found ? String(found) : null; } if (!tmdbId && !imdbId) return base; - if (preference === 'tmdb') { + // Only try TMDB if preference is 'tmdb' and we have tmdbId + if (preference === 'tmdb' && tmdbId) { logger.debug('[useFeaturedContent] logo:try:tmdb', { name: item.name, id: item.id, tmdbId, lang: preferredLanguage }); - if (!tmdbId) return base; const logoUrl = await tmdbService.getContentLogo(item.type === 'series' ? 'tv' : 'movie', tmdbId as string, preferredLanguage); if (logoUrl) { logger.debug('[useFeaturedContent] logo:tmdb:ok', { name: item.name, id: item.id, url: logoUrl, lang: preferredLanguage }); return { ...base, logo: logoUrl }; } - // fallback metahub - if (!imdbId && tmdbId) { - const details: any = item.type === 'series' ? await tmdbService.getShowExternalIds(parseInt(tmdbId)) : await tmdbService.getMovieDetails(tmdbId); - imdbId = details?.imdb_id; - } - if (imdbId) { - const url = `https://images.metahub.space/logo/medium/${imdbId}/img`; - logger.debug('[useFeaturedContent] logo:fallback:metahub', { name: item.name, id: item.id, url }); - return { ...base, logo: url }; - } - return base; - } else { - // metahub first - if (!imdbId && tmdbId) { - const details: any = item.type === 'series' ? await tmdbService.getShowExternalIds(parseInt(tmdbId)) : await tmdbService.getMovieDetails(tmdbId); - imdbId = details?.imdb_id; - } - if (imdbId) { - const url = `https://images.metahub.space/logo/medium/${imdbId}/img`; - logger.debug('[useFeaturedContent] logo:metahub:ok', { name: item.name, id: item.id, url }); - return { ...base, logo: url }; - } - logger.debug('[useFeaturedContent] logo:metahub:miss → fallback:tmdb', { name: item.name, id: item.id, lang: preferredLanguage }); - if (!tmdbId) return base; - const logoUrl = await tmdbService.getContentLogo(item.type === 'series' ? 'tv' : 'movie', tmdbId as string, preferredLanguage); - if (logoUrl) { - logger.debug('[useFeaturedContent] logo:tmdb:fallback:ok', { name: item.name, id: item.id, url: logoUrl, lang: preferredLanguage }); - return { ...base, logo: logoUrl }; - } - return base; } + return base; } catch (error) { logger.error('[useFeaturedContent] logo:error', { name: item.name, id: item.id, error: String(error) }); return base; } }; - formattedContent = await Promise.all(topItems.map(enrichLogo)); + // When enrichment is disabled, only use addon logos and never fetch external logos + if (!settings.enrichMetadataWithTMDB) { + logger.debug('[useFeaturedContent] enrichment disabled, using only addon logos'); + formattedContent = topItems.map((item: any) => { + const base: StreamingContent = { + id: item.id, + type: item.type, + name: item.name, + poster: item.poster, + banner: (item as any).banner, + logo: (item as any).logo && !isTmdbUrl((item as any).logo) ? (item as any).logo : undefined, + description: (item as any).description, + year: (item as any).year, + genres: (item as any).genres, + inLibrary: Boolean((item as any).inLibrary), + }; + return base; + }); + } else { + // Only enrich with logos if enrichment is enabled + formattedContent = await Promise.all(topItems.map(enrichLogo)); + } } } diff --git a/src/hooks/useMetadataAssets.ts b/src/hooks/useMetadataAssets.ts index 7918c51a..76f6cc64 100644 --- a/src/hooks/useMetadataAssets.ts +++ b/src/hooks/useMetadataAssets.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { logger } from '../utils/logger'; import { TMDBService } from '../services/tmdbService'; -import { isMetahubUrl, isTmdbUrl } from '../utils/logoUtils'; +import { isTmdbUrl } from '../utils/logoUtils'; import { Image } from 'expo-image'; import AsyncStorage from '@react-native-async-storage/async-storage'; @@ -83,7 +83,7 @@ export const useMetadataAssets = ( // Force logo refresh on preference change if (metadata?.logo) { - const currentLogoIsMetahub = isMetahubUrl(metadata.logo); + const currentLogoIsExternal = isTmdbUrl(metadata.logo); const currentLogoIsTmdb = isTmdbUrl(metadata.logo); const preferenceIsMetahub = settings.logoSourcePreference === 'metahub'; @@ -102,21 +102,30 @@ export const useMetadataAssets = ( // Optimized logo fetching useEffect(() => { - const logoPreference = settings.logoSourcePreference || 'metahub'; + const logoPreference = settings.logoSourcePreference || 'tmdb'; const currentLogoUrl = metadata?.logo; let shouldFetchLogo = false; - // Determine if we need to fetch a new logo + // If enrichment is disabled, use addon logo and don't fetch from external sources + if (!settings.enrichMetadataWithTMDB) { + // If we have an addon logo, use it and don't fetch external logos + if (metadata?.logo && !isTmdbUrl(metadata.logo)) { + // This is an addon logo, keep it + return; + } + // If no addon logo, don't fetch external logos when enrichment is disabled + return; + } + + // Determine if we need to fetch a new logo (only when enrichment is enabled) if (!currentLogoUrl) { shouldFetchLogo = true; } else { - const isCurrentLogoMetahub = isMetahubUrl(currentLogoUrl); + const isCurrentLogoExternal = isTmdbUrl(currentLogoUrl); const isCurrentLogoTmdb = isTmdbUrl(currentLogoUrl); if (logoPreference === 'tmdb' && !isCurrentLogoTmdb) { shouldFetchLogo = true; - } else if (logoPreference === 'metahub' && !isCurrentLogoMetahub) { - shouldFetchLogo = true; } } @@ -133,19 +142,7 @@ export const useMetadataAssets = ( try { const preferredLanguage = settings.tmdbLanguagePreference || 'en'; - if (logoPreference === 'metahub' && imdbId) { - // Metahub path - with cached availability check - const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - - const isAvailable = await checkImageAvailability(metahubUrl); - if (isAvailable) { - // Preload the image - await Image.prefetch(metahubUrl); - - // Update metadata with Metahub logo - setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: metahubUrl })); - } - } else if (logoPreference === 'tmdb') { + if (logoPreference === 'tmdb') { // TMDB path - optimized flow let tmdbId: string | null = null; let contentType = type === 'series' ? 'tv' : 'movie'; @@ -205,6 +202,7 @@ export const useMetadataAssets = ( metadata?.logo, settings.logoSourcePreference, settings.tmdbLanguagePreference, + settings.enrichMetadataWithTMDB, setMetadata ]); @@ -221,16 +219,27 @@ export const useMetadataAssets = ( setBannerSource('default'); } + // If enrichment is disabled, use addon banner and don't fetch from external sources + if (!settings.enrichMetadataWithTMDB) { + const addonBanner = metadata?.banner || metadata?.poster || null; + if (addonBanner && addonBanner !== bannerImage) { + setBannerImage(addonBanner); + setBannerSource('default'); + } + setLoadingBanner(false); + return; + } + try { - const currentPreference = settings.logoSourcePreference || 'metahub'; + const currentPreference = settings.logoSourcePreference || 'tmdb'; const preferredLanguage = settings.tmdbLanguagePreference || 'en'; const contentType = type === 'series' ? 'tv' : 'movie'; // Try to get a banner from the preferred source let finalBanner: string | null = null; - let bannerSourceType: 'tmdb' | 'metahub' | 'default' = 'default'; + let bannerSourceType: 'tmdb' | 'default' = 'default'; - // TMDB path + // TMDB path only if (currentPreference === 'tmdb') { let tmdbId = null; if (id.startsWith('tmdb:')) { @@ -282,89 +291,12 @@ export const useMetadataAssets = ( // Handle error silently } } - } - // Metahub path - else if (currentPreference === 'metahub' && imdbId) { - const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - - const isAvailable = await checkImageAvailability(metahubUrl); - if (isAvailable) { - finalBanner = metahubUrl; - bannerSourceType = 'metahub'; - - // Preload the image - if (finalBanner) { - await Image.prefetch(finalBanner); - } - } } - // If preferred source failed, try fallback + // Final fallback to metadata if (!finalBanner) { - // Try the other source - if (currentPreference === 'tmdb' && imdbId) { - const metahubUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - - const isAvailable = await checkImageAvailability(metahubUrl); - if (isAvailable) { - finalBanner = metahubUrl; - bannerSourceType = 'metahub'; - - // Preload the image - if (finalBanner) { - await Image.prefetch(finalBanner); - } - } - } - else if (currentPreference === 'metahub') { - // Try TMDB as fallback - let tmdbId = null; - if (id.startsWith('tmdb:')) { - tmdbId = id.split(':')[1]; - } else if (foundTmdbId) { - tmdbId = foundTmdbId; - } else if ((metadata as any).tmdbId) { - tmdbId = (metadata as any).tmdbId; - } - - if (tmdbId) { - try { - const tmdbService = TMDBService.getInstance(); - const endpoint = contentType === 'tv' ? 'tv' : 'movie'; - - const details = endpoint === 'movie' - ? await tmdbService.getMovieDetails(tmdbId) - : await tmdbService.getTVShowDetails(Number(tmdbId)); - - if (details?.backdrop_path) { - finalBanner = tmdbService.getImageUrl(details.backdrop_path); - bannerSourceType = 'tmdb'; - - // Preload the image - if (finalBanner) { - await Image.prefetch(finalBanner); - } - } - else if (details?.poster_path) { - finalBanner = tmdbService.getImageUrl(details.poster_path); - bannerSourceType = 'tmdb'; - - // Preload the image - if (finalBanner) { - await Image.prefetch(finalBanner); - } - } - } catch (error) { - // Handle error silently - } - } - } - - // Final fallback to metadata - if (!finalBanner) { - finalBanner = metadata?.banner || metadata?.poster || null; - bannerSourceType = 'default'; - } + finalBanner = metadata?.banner || metadata?.poster || null; + bannerSourceType = 'default'; } // Update state if the banner changed @@ -384,11 +316,11 @@ export const useMetadataAssets = ( } finally { setLoadingBanner(false); } - }, [metadata, id, type, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference, foundTmdbId, bannerImage, bannerSource]); + }, [metadata, id, type, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB, foundTmdbId, bannerImage, bannerSource]); // Fetch banner when needed useEffect(() => { - const currentPreference = settings.logoSourcePreference || 'metahub'; + const currentPreference = settings.logoSourcePreference || 'tmdb'; if (bannerSource !== currentPreference && !forcedBannerRefreshDone.current) { fetchBanner(); diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 17235894..dbddb01f 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -96,7 +96,7 @@ export const DEFAULT_SETTINGS: AppSettings = { featuredContentSource: 'catalogs', heroStyle: 'carousel', selectedHeroCatalogs: [], // Empty array means all catalogs are selected - logoSourcePreference: 'metahub', // Default to Metahub as first source + logoSourcePreference: 'tmdb', // Default to TMDB as first source tmdbLanguagePreference: 'en', // Default to English episodeLayoutStyle: 'vertical', // Default to vertical layout for new installs autoplayBestStream: false, // Disabled by default for user choice diff --git a/src/screens/LogoSourceSettings.tsx b/src/screens/LogoSourceSettings.tsx index 70c1a3e7..92b3a88f 100644 --- a/src/screens/LogoSourceSettings.tsx +++ b/src/screens/LogoSourceSettings.tsx @@ -530,31 +530,6 @@ const LogoSourceSettings = () => { logger.error(`[LogoSourceSettings] Error fetching TMDB images:`, tmdbError); } - // Get Metahub logo and banner - try { - // Metahub logo - const metahubLogoUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`; - const logoResponse = await fetch(metahubLogoUrl, { method: 'HEAD' }); - - if (logoResponse.ok) { - setMetahubLogo(metahubLogoUrl); - logger.log(`[LogoSourceSettings] Got ${show.name} Metahub logo: ${metahubLogoUrl}`); - } - - // Metahub banner - const metahubBannerUrl = `https://images.metahub.space/background/medium/${imdbId}/img`; - const bannerResponse = await fetch(metahubBannerUrl, { method: 'HEAD' }); - - if (bannerResponse.ok) { - setMetahubBanner(metahubBannerUrl); - logger.log(`[LogoSourceSettings] Got ${show.name} Metahub banner: ${metahubBannerUrl}`); - } else if (tmdbBanner) { - // If Metahub banner doesn't exist, use TMDB banner - setMetahubBanner(tmdbBanner); - } - } catch (metahubErr) { - logger.error(`[LogoSourceSettings] Error checking Metahub images:`, metahubErr); - } } catch (err) { logger.error(`[LogoSourceSettings] Error fetching ${show.name} logos:`, err); } finally { diff --git a/src/services/catalogService.ts b/src/services/catalogService.ts index cb8ce851..323e8a4c 100644 --- a/src/services/catalogService.ts +++ b/src/services/catalogService.ts @@ -577,10 +577,16 @@ class CatalogService { private convertMetaToStreamingContent(meta: Meta): StreamingContent { // Basic conversion for catalog display - no enhanced metadata processing - // Validate poster URL and provide better fallback + // Use addon's poster if available, otherwise use placeholder let posterUrl = meta.poster; if (!posterUrl || posterUrl.trim() === '' || posterUrl === 'null' || posterUrl === 'undefined') { - posterUrl = `https://images.metahub.space/poster/medium/${meta.id}/img`; + posterUrl = 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image'; + } + + // Use addon's logo if available, otherwise undefined + let logoUrl = (meta as any).logo; + if (!logoUrl || logoUrl.trim() === '' || logoUrl === 'null' || logoUrl === 'undefined') { + logoUrl = undefined; } return { @@ -590,7 +596,7 @@ class CatalogService { poster: posterUrl, posterShape: 'poster', banner: meta.background, - logo: `https://images.metahub.space/logo/medium/${meta.id}/img`, // Use metahub for catalog display + logo: logoUrl, imdbRating: meta.imdbRating, year: meta.year, genres: meta.genres, @@ -612,8 +618,8 @@ class CatalogService { poster: meta.poster || 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image', posterShape: 'poster', banner: meta.background, - // Use addon's logo if available, fallback to metahub - logo: (meta as any).logo || `https://images.metahub.space/logo/medium/${meta.id}/img`, + // Use addon's logo if available, otherwise undefined + logo: (meta as any).logo || undefined, imdbRating: meta.imdbRating, year: meta.year, genres: meta.genres, diff --git a/src/services/stremioService.ts b/src/services/stremioService.ts index 7c399c3c..71c8c2ef 100644 --- a/src/services/stremioService.ts +++ b/src/services/stremioService.ts @@ -710,7 +710,6 @@ class StremioService { cleanBaseUrl = `https://${cleanBaseUrl}`; } - logger.log('Addon base URL:', cleanBaseUrl, queryString ? `with query: ${queryString}` : ''); return { baseUrl: cleanBaseUrl, queryParams: queryString }; } @@ -760,16 +759,13 @@ class StremioService { // Add filters if (filters.length > 0) { - logger.log(`Adding ${filters.length} filters to ${manifest.name} request`); filters.forEach(filter => { if (filter.value) { - logger.log(`Adding filter ${filter.title}=${filter.value}`); url += `&${encodeURIComponent(filter.title)}=${encodeURIComponent(filter.value)}`; } }); } - logger.log(`🔗 [${manifest.name}] Requesting catalog: ${url}`); const response = await this.retryRequest(async () => { return await axios.get(url); diff --git a/src/utils/logoUtils.ts b/src/utils/logoUtils.ts index fcbdc050..f2b469db 100644 --- a/src/utils/logoUtils.ts +++ b/src/utils/logoUtils.ts @@ -1,51 +1,6 @@ import { logger } from './logger'; import { TMDBService } from '../services/tmdbService'; -/** - * Checks if a URL is a valid Metahub logo by performing a HEAD request - * @param url The Metahub logo URL to check - * @returns True if the logo is valid, false otherwise - */ -export const isValidMetahubLogo = async (url: string): Promise => { - if (!url || !url.includes('metahub.space')) { - return false; - } - - try { - const response = await fetch(url, { method: 'HEAD' }); - - // Check if request was successful - if (!response.ok) { - logger.warn(`[logoUtils] Logo URL returned status ${response.status}: ${url}`); - return false; - } - - // Check file size to detect "Missing Image" placeholders - const contentLength = response.headers.get('content-length'); - const fileSize = contentLength ? parseInt(contentLength, 10) : 0; - - // If content-length header is missing, we can't check file size, so assume it's valid - if (!contentLength) { - logger.warn(`[logoUtils] No content-length header for URL: ${url}`); - return true; // Give it the benefit of the doubt - } - - // If file size is suspiciously small, it might be a "Missing Image" placeholder - // Check for extremely small files (less than 100 bytes) which are definitely placeholders - if (fileSize < 100) { - logger.warn(`[logoUtils] Logo URL returned extremely small file (${fileSize} bytes), likely a placeholder: ${url}`); - return false; - } - - // For file sizes between 100-500 bytes, they might be small legitimate SVG files - // So we'll allow them through - return true; - } catch (error) { - logger.error(`[logoUtils] Error checking logo URL: ${url}`, error); - // Don't fail hard on network errors, let the image component try to load it - return true; - } -}; /** * Utility to determine if a URL is likely to be a valid logo @@ -63,16 +18,6 @@ export const hasValidLogoFormat = (url: string | null): boolean => { return true; // Allow most URLs to pass through }; -/** - * Checks if a URL is from Metahub - * @param url The URL to check - * @returns True if the URL is from Metahub - */ -export const isMetahubUrl = (url: string | null): boolean => { - if (!url) return false; - return url.includes('metahub.space'); -}; - /** * Checks if a URL is from TMDB * @param url The URL to check @@ -84,87 +29,56 @@ export const isTmdbUrl = (url: string | null): boolean => { }; /** - * Fetches a banner image based on logo source preference - * @param imdbId The IMDB ID of the content - * @param tmdbId The TMDB ID of the content (if available) + * Fetches a banner image from TMDB + * @param tmdbId The TMDB ID of the content * @param type The content type ('movie' or 'series') - * @param preference The logo source preference ('metahub' or 'tmdb') * @returns The URL of the banner image, or null if none found */ -export const fetchBannerWithPreference = async ( - imdbId: string | null, +export const fetchBannerFromTMDB = async ( tmdbId: number | string | null, - type: 'movie' | 'series', - preference: 'metahub' | 'tmdb' + type: 'movie' | 'series' ): Promise => { - logger.log(`[logoUtils] Fetching banner with preference ${preference} for ${type} (IMDB: ${imdbId}, TMDB: ${tmdbId})`); + logger.log(`[logoUtils] Fetching banner from TMDB for ${type} (ID: ${tmdbId})`); - // Determine which source to try first based on preference - if (preference === 'tmdb') { - // Try TMDB first if it's the preferred source - if (tmdbId) { - try { - const tmdbService = TMDBService.getInstance(); - - // Get backdrop from TMDB - const tmdbType = type === 'series' ? 'tv' : 'movie'; - logger.log(`[logoUtils] Attempting to fetch banner from TMDB for ${tmdbType} (ID: ${tmdbId})`); - - let bannerUrl = null; - if (tmdbType === 'movie') { - const movieDetails = await tmdbService.getMovieDetails(tmdbId.toString()); - if (movieDetails && movieDetails.backdrop_path) { - bannerUrl = tmdbService.getImageUrl(movieDetails.backdrop_path, 'original'); - logger.log(`[logoUtils] Found backdrop_path: ${movieDetails.backdrop_path}`); - } else { - logger.warn(`[logoUtils] No backdrop_path found in movie details for ID ${tmdbId}`); - } - } else { - const showDetails = await tmdbService.getTVShowDetails(Number(tmdbId)); - if (showDetails && showDetails.backdrop_path) { - bannerUrl = tmdbService.getImageUrl(showDetails.backdrop_path, 'original'); - logger.log(`[logoUtils] Found backdrop_path: ${showDetails.backdrop_path}`); - } else { - logger.warn(`[logoUtils] No backdrop_path found in TV show details for ID ${tmdbId}`); - } - } - - if (bannerUrl) { - logger.log(`[logoUtils] Successfully fetched ${tmdbType} banner from TMDB: ${bannerUrl}`); - return bannerUrl; - } - } catch (error) { - logger.error(`[logoUtils] Error fetching banner from TMDB for ID ${tmdbId}:`, error); - } - - logger.warn(`[logoUtils] No banner found from TMDB for ${type} (ID: ${tmdbId}), falling back to Metahub`); - } else { - logger.warn(`[logoUtils] Cannot fetch from TMDB - no TMDB ID provided, falling back to Metahub`); - } + if (!tmdbId) { + logger.warn(`[logoUtils] Cannot fetch from TMDB - no TMDB ID provided`); + return null; } - // Try Metahub if it's preferred or TMDB failed - if (imdbId) { - const metahubUrl = `https://images.metahub.space/background/large/${imdbId}/img`; + try { + const tmdbService = TMDBService.getInstance(); - logger.log(`[logoUtils] Attempting to fetch banner from Metahub for ${imdbId}`); + // Get backdrop from TMDB + const tmdbType = type === 'series' ? 'tv' : 'movie'; + logger.log(`[logoUtils] Attempting to fetch banner from TMDB for ${tmdbType} (ID: ${tmdbId})`); - try { - const response = await fetch(metahubUrl, { method: 'HEAD' }); - if (response.ok) { - logger.log(`[logoUtils] Successfully fetched banner from Metahub: ${metahubUrl}`); - return metahubUrl; + let bannerUrl = null; + if (tmdbType === 'movie') { + const movieDetails = await tmdbService.getMovieDetails(tmdbId.toString()); + if (movieDetails && movieDetails.backdrop_path) { + bannerUrl = tmdbService.getImageUrl(movieDetails.backdrop_path, 'original'); + logger.log(`[logoUtils] Found backdrop_path: ${movieDetails.backdrop_path}`); } else { - logger.warn(`[logoUtils] Metahub banner request failed with status ${response.status}`); + logger.warn(`[logoUtils] No backdrop_path found in movie details for ID ${tmdbId}`); + } + } else { + const showDetails = await tmdbService.getTVShowDetails(Number(tmdbId)); + if (showDetails && showDetails.backdrop_path) { + bannerUrl = tmdbService.getImageUrl(showDetails.backdrop_path, 'original'); + logger.log(`[logoUtils] Found backdrop_path: ${showDetails.backdrop_path}`); + } else { + logger.warn(`[logoUtils] No backdrop_path found in TV show details for ID ${tmdbId}`); } - } catch (error) { - logger.warn(`[logoUtils] Failed to fetch banner from Metahub:`, error); } - } else { - logger.warn(`[logoUtils] Cannot fetch from Metahub - no IMDB ID provided`); + + if (bannerUrl) { + logger.log(`[logoUtils] Successfully fetched ${tmdbType} banner from TMDB: ${bannerUrl}`); + return bannerUrl; + } + } catch (error) { + logger.error(`[logoUtils] Error fetching banner from TMDB for ID ${tmdbId}:`, error); } - // If both sources fail or aren't available, return null - logger.warn(`[logoUtils] No banner found from any source for ${type} (IMDB: ${imdbId}, TMDB: ${tmdbId})`); + logger.warn(`[logoUtils] No banner found from TMDB for ${type} (ID: ${tmdbId})`); return null; }; \ No newline at end of file