removed harcorded metahub
This commit is contained in:
parent
14163513e3
commit
42c236e235
9 changed files with 179 additions and 365 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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<StreamingContent> => {
|
||||
|
|
@ -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<StreamingContent> => {
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<boolean> => {
|
||||
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<string | null> => {
|
||||
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;
|
||||
};
|
||||
Loading…
Reference in a new issue