mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
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 we've had an error, try metahub fallback
|
||||||
if (retryCount > 0 && !item.poster.includes('metahub.space')) {
|
// Use addon poster if available, otherwise use placeholder
|
||||||
return `https://images.metahub.space/poster/small/${item.id}/img`;
|
if (retryCount > 0 && !item.poster) {
|
||||||
|
return 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image';
|
||||||
}
|
}
|
||||||
|
|
||||||
// For TMDB images, use smaller sizes
|
// For TMDB images, use smaller sizes
|
||||||
|
|
@ -204,7 +205,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
|
||||||
}
|
}
|
||||||
|
|
||||||
// For metahub images, use smaller sizes
|
// For metahub images, use smaller sizes
|
||||||
if (item.poster.includes('images.metahub.space')) {
|
if (item.poster.includes('placeholder')) {
|
||||||
return item.poster.replace('/medium/', '/small/');
|
return item.poster.replace('/medium/', '/small/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,7 +269,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
|
||||||
onError={(error) => {
|
onError={(error) => {
|
||||||
if (__DEV__) console.warn('Image load error for:', item.poster, error);
|
if (__DEV__) console.warn('Image load error for:', item.poster, error);
|
||||||
// Try fallback URL on first 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);
|
setRetryCount(1);
|
||||||
// Don't set error state yet, let it try the fallback
|
// Don't set error state yet, let it try the fallback
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import Animated, {
|
||||||
} from 'react-native-reanimated';
|
} from 'react-native-reanimated';
|
||||||
import { StreamingContent } from '../../services/catalogService';
|
import { StreamingContent } from '../../services/catalogService';
|
||||||
import { SkeletonFeatured } from './SkeletonLoaders';
|
import { SkeletonFeatured } from './SkeletonLoaders';
|
||||||
import { isValidMetahubLogo, hasValidLogoFormat, isMetahubUrl, isTmdbUrl } from '../../utils/logoUtils';
|
import { hasValidLogoFormat, isTmdbUrl } from '../../utils/logoUtils';
|
||||||
import { useSettings } from '../../hooks/useSettings';
|
import { useSettings } from '../../hooks/useSettings';
|
||||||
import { TMDBService } from '../../services/tmdbService';
|
import { TMDBService } from '../../services/tmdbService';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
|
|
@ -268,16 +268,38 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
||||||
const currentLogo = contentData.logo;
|
const currentLogo = contentData.logo;
|
||||||
|
|
||||||
// Get preferences
|
// Get preferences
|
||||||
const logoPreference = settings.logoSourcePreference || 'metahub';
|
const logoPreference = settings.logoSourcePreference || 'tmdb';
|
||||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
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
|
// Reset state for new fetch only if switching to a different item
|
||||||
if (prevContentIdRef.current !== contentId) {
|
if (prevContentIdRef.current !== contentId) {
|
||||||
setLogoUrl(null);
|
setLogoUrl(null);
|
||||||
}
|
}
|
||||||
setLogoLoadError(false);
|
setLogoLoadError(false);
|
||||||
|
|
||||||
// Extract IDs
|
// Extract IDs (only when enrichment is enabled)
|
||||||
let imdbId: string | null = null;
|
let imdbId: string | null = null;
|
||||||
if (contentData.id.startsWith('tt')) {
|
if (contentData.id.startsWith('tt')) {
|
||||||
imdbId = contentData.id;
|
imdbId = contentData.id;
|
||||||
|
|
@ -294,7 +316,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
||||||
tmdbId = String((contentData as any).tmdb_id);
|
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) {
|
if (imdbId && !tmdbId) {
|
||||||
try {
|
try {
|
||||||
const tmdbService = TMDBService.getInstance();
|
const tmdbService = TMDBService.getInstance();
|
||||||
|
|
@ -315,63 +337,18 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
||||||
// --- Logo Fetching Logic ---
|
// --- Logo Fetching Logic ---
|
||||||
logger.debug('[FeaturedContent] fetchLogo:ids', { imdbId, tmdbId, preference: logoPreference, lang: preferredLanguage });
|
logger.debug('[FeaturedContent] fetchLogo:ids', { imdbId, tmdbId, preference: logoPreference, lang: preferredLanguage });
|
||||||
|
|
||||||
if (logoPreference === 'metahub') {
|
// Only try TMDB if preference is 'tmdb' and we have tmdbId
|
||||||
// Primary: Metahub (needs imdbId)
|
if (logoPreference === 'tmdb' && tmdbId) {
|
||||||
if (imdbId) {
|
primaryAttempted = true;
|
||||||
primaryAttempted = true;
|
try {
|
||||||
const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`;
|
const tmdbService = TMDBService.getInstance();
|
||||||
try {
|
const tTmdb = nowMs();
|
||||||
const tHead = nowMs();
|
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
|
||||||
const response = await fetch(metahubUrl, { method: 'HEAD' });
|
if (logoUrl) {
|
||||||
if (response.ok) {
|
finalLogoUrl = logoUrl;
|
||||||
finalLogoUrl = metahubUrl;
|
logger.debug('[FeaturedContent] fetchLogo:tmdb:ok', { url: logoUrl, duration: since(tTmdb) });
|
||||||
logger.debug('[FeaturedContent] fetchLogo:metahub:ok', { url: metahubUrl, duration: since(tHead) });
|
}
|
||||||
}
|
} catch (error) { /* Log if needed */ }
|
||||||
} 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 */ }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Set Final Logo ---
|
// --- Set Final Logo ---
|
||||||
|
|
@ -400,7 +377,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
||||||
|
|
||||||
// Trigger fetch when content changes
|
// Trigger fetch when content changes
|
||||||
fetchLogo();
|
fetchLogo();
|
||||||
}, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference]);
|
}, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]);
|
||||||
|
|
||||||
// Load poster and logo
|
// Load poster and logo
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { logger } from '../utils/logger';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import { useGenres } from '../contexts/GenreContext';
|
import { useGenres } from '../contexts/GenreContext';
|
||||||
import { useSettings, settingsEmitter } from './useSettings';
|
import { useSettings, settingsEmitter } from './useSettings';
|
||||||
|
import { isTmdbUrl } from '../utils/logoUtils';
|
||||||
|
|
||||||
// Create a persistent store outside of the hook to maintain state between navigation
|
// Create a persistent store outside of the hook to maintain state between navigation
|
||||||
const persistentStore = {
|
const persistentStore = {
|
||||||
|
|
@ -19,7 +20,7 @@ const persistentStore = {
|
||||||
showHeroSection: true,
|
showHeroSection: true,
|
||||||
featuredContentSource: 'tmdb' as 'tmdb' | 'catalogs',
|
featuredContentSource: 'tmdb' as 'tmdb' | 'catalogs',
|
||||||
selectedHeroCatalogs: [] as string[],
|
selectedHeroCatalogs: [] as string[],
|
||||||
logoSourcePreference: 'metahub' as 'metahub' | 'tmdb',
|
logoSourcePreference: 'tmdb' as 'metahub' | 'tmdb',
|
||||||
tmdbLanguagePreference: 'en'
|
tmdbLanguagePreference: 'en'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -132,7 +133,7 @@ export function useFeaturedContent() {
|
||||||
|
|
||||||
// Then fetch logos for each item based on preference
|
// Then fetch logos for each item based on preference
|
||||||
const tLogos = Date.now();
|
const tLogos = Date.now();
|
||||||
const preference = settings.logoSourcePreference || 'metahub';
|
const preference = settings.logoSourcePreference || 'tmdb';
|
||||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
||||||
|
|
||||||
const fetchLogoForItem = async (item: StreamingContent): Promise<StreamingContent> => {
|
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 });
|
logger.info('[useFeaturedContent] logos:resolved', { count: formattedContent.length, duration: `${Date.now() - tLogos}ms`, preference });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -247,7 +257,7 @@ export function useFeaturedContent() {
|
||||||
const topItems = allItems.sort(() => Math.random() - 0.5).slice(0, 10);
|
const topItems = allItems.sort(() => Math.random() - 0.5).slice(0, 10);
|
||||||
|
|
||||||
// Optionally enrich with logos based on preference for tmdb-sourced IDs
|
// 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 preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
||||||
|
|
||||||
const enrichLogo = async (item: any): Promise<StreamingContent> => {
|
const enrichLogo = async (item: any): Promise<StreamingContent> => {
|
||||||
|
|
@ -264,6 +274,17 @@ export function useFeaturedContent() {
|
||||||
inLibrary: Boolean((item as any).inLibrary),
|
inLibrary: Boolean((item as any).inLibrary),
|
||||||
};
|
};
|
||||||
try {
|
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 rawId = String(item.id);
|
||||||
const isTmdb = rawId.startsWith('tmdb:');
|
const isTmdb = rawId.startsWith('tmdb:');
|
||||||
const isImdb = rawId.startsWith('tt');
|
const isImdb = rawId.startsWith('tt');
|
||||||
|
|
@ -277,52 +298,44 @@ export function useFeaturedContent() {
|
||||||
tmdbId = found ? String(found) : null;
|
tmdbId = found ? String(found) : null;
|
||||||
}
|
}
|
||||||
if (!tmdbId && !imdbId) return base;
|
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 });
|
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);
|
const logoUrl = await tmdbService.getContentLogo(item.type === 'series' ? 'tv' : 'movie', tmdbId as string, preferredLanguage);
|
||||||
if (logoUrl) {
|
if (logoUrl) {
|
||||||
logger.debug('[useFeaturedContent] logo:tmdb:ok', { name: item.name, id: item.id, url: logoUrl, lang: preferredLanguage });
|
logger.debug('[useFeaturedContent] logo:tmdb:ok', { name: item.name, id: item.id, url: logoUrl, lang: preferredLanguage });
|
||||||
return { ...base, logo: logoUrl };
|
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) {
|
} catch (error) {
|
||||||
logger.error('[useFeaturedContent] logo:error', { name: item.name, id: item.id, error: String(error) });
|
logger.error('[useFeaturedContent] logo:error', { name: item.name, id: item.id, error: String(error) });
|
||||||
return base;
|
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 { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { logger } from '../utils/logger';
|
import { logger } from '../utils/logger';
|
||||||
import { TMDBService } from '../services/tmdbService';
|
import { TMDBService } from '../services/tmdbService';
|
||||||
import { isMetahubUrl, isTmdbUrl } from '../utils/logoUtils';
|
import { isTmdbUrl } from '../utils/logoUtils';
|
||||||
import { Image } from 'expo-image';
|
import { Image } from 'expo-image';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ export const useMetadataAssets = (
|
||||||
|
|
||||||
// Force logo refresh on preference change
|
// Force logo refresh on preference change
|
||||||
if (metadata?.logo) {
|
if (metadata?.logo) {
|
||||||
const currentLogoIsMetahub = isMetahubUrl(metadata.logo);
|
const currentLogoIsExternal = isTmdbUrl(metadata.logo);
|
||||||
const currentLogoIsTmdb = isTmdbUrl(metadata.logo);
|
const currentLogoIsTmdb = isTmdbUrl(metadata.logo);
|
||||||
const preferenceIsMetahub = settings.logoSourcePreference === 'metahub';
|
const preferenceIsMetahub = settings.logoSourcePreference === 'metahub';
|
||||||
|
|
||||||
|
|
@ -102,21 +102,30 @@ export const useMetadataAssets = (
|
||||||
|
|
||||||
// Optimized logo fetching
|
// Optimized logo fetching
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const logoPreference = settings.logoSourcePreference || 'metahub';
|
const logoPreference = settings.logoSourcePreference || 'tmdb';
|
||||||
const currentLogoUrl = metadata?.logo;
|
const currentLogoUrl = metadata?.logo;
|
||||||
let shouldFetchLogo = false;
|
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) {
|
if (!currentLogoUrl) {
|
||||||
shouldFetchLogo = true;
|
shouldFetchLogo = true;
|
||||||
} else {
|
} else {
|
||||||
const isCurrentLogoMetahub = isMetahubUrl(currentLogoUrl);
|
const isCurrentLogoExternal = isTmdbUrl(currentLogoUrl);
|
||||||
const isCurrentLogoTmdb = isTmdbUrl(currentLogoUrl);
|
const isCurrentLogoTmdb = isTmdbUrl(currentLogoUrl);
|
||||||
|
|
||||||
if (logoPreference === 'tmdb' && !isCurrentLogoTmdb) {
|
if (logoPreference === 'tmdb' && !isCurrentLogoTmdb) {
|
||||||
shouldFetchLogo = true;
|
shouldFetchLogo = true;
|
||||||
} else if (logoPreference === 'metahub' && !isCurrentLogoMetahub) {
|
|
||||||
shouldFetchLogo = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,19 +142,7 @@ export const useMetadataAssets = (
|
||||||
try {
|
try {
|
||||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
||||||
|
|
||||||
if (logoPreference === 'metahub' && imdbId) {
|
if (logoPreference === 'tmdb') {
|
||||||
// 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') {
|
|
||||||
// TMDB path - optimized flow
|
// TMDB path - optimized flow
|
||||||
let tmdbId: string | null = null;
|
let tmdbId: string | null = null;
|
||||||
let contentType = type === 'series' ? 'tv' : 'movie';
|
let contentType = type === 'series' ? 'tv' : 'movie';
|
||||||
|
|
@ -205,6 +202,7 @@ export const useMetadataAssets = (
|
||||||
metadata?.logo,
|
metadata?.logo,
|
||||||
settings.logoSourcePreference,
|
settings.logoSourcePreference,
|
||||||
settings.tmdbLanguagePreference,
|
settings.tmdbLanguagePreference,
|
||||||
|
settings.enrichMetadataWithTMDB,
|
||||||
setMetadata
|
setMetadata
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -221,16 +219,27 @@ export const useMetadataAssets = (
|
||||||
setBannerSource('default');
|
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 {
|
try {
|
||||||
const currentPreference = settings.logoSourcePreference || 'metahub';
|
const currentPreference = settings.logoSourcePreference || 'tmdb';
|
||||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
||||||
const contentType = type === 'series' ? 'tv' : 'movie';
|
const contentType = type === 'series' ? 'tv' : 'movie';
|
||||||
|
|
||||||
// Try to get a banner from the preferred source
|
// Try to get a banner from the preferred source
|
||||||
let finalBanner: string | null = null;
|
let finalBanner: string | null = null;
|
||||||
let bannerSourceType: 'tmdb' | 'metahub' | 'default' = 'default';
|
let bannerSourceType: 'tmdb' | 'default' = 'default';
|
||||||
|
|
||||||
// TMDB path
|
// TMDB path only
|
||||||
if (currentPreference === 'tmdb') {
|
if (currentPreference === 'tmdb') {
|
||||||
let tmdbId = null;
|
let tmdbId = null;
|
||||||
if (id.startsWith('tmdb:')) {
|
if (id.startsWith('tmdb:')) {
|
||||||
|
|
@ -282,89 +291,12 @@ export const useMetadataAssets = (
|
||||||
// Handle error silently
|
// 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) {
|
if (!finalBanner) {
|
||||||
// Try the other source
|
finalBanner = metadata?.banner || metadata?.poster || null;
|
||||||
if (currentPreference === 'tmdb' && imdbId) {
|
bannerSourceType = 'default';
|
||||||
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';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state if the banner changed
|
// Update state if the banner changed
|
||||||
|
|
@ -384,11 +316,11 @@ export const useMetadataAssets = (
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingBanner(false);
|
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
|
// Fetch banner when needed
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentPreference = settings.logoSourcePreference || 'metahub';
|
const currentPreference = settings.logoSourcePreference || 'tmdb';
|
||||||
|
|
||||||
if (bannerSource !== currentPreference && !forcedBannerRefreshDone.current) {
|
if (bannerSource !== currentPreference && !forcedBannerRefreshDone.current) {
|
||||||
fetchBanner();
|
fetchBanner();
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||||
featuredContentSource: 'catalogs',
|
featuredContentSource: 'catalogs',
|
||||||
heroStyle: 'carousel',
|
heroStyle: 'carousel',
|
||||||
selectedHeroCatalogs: [], // Empty array means all catalogs are selected
|
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
|
tmdbLanguagePreference: 'en', // Default to English
|
||||||
episodeLayoutStyle: 'vertical', // Default to vertical layout for new installs
|
episodeLayoutStyle: 'vertical', // Default to vertical layout for new installs
|
||||||
autoplayBestStream: false, // Disabled by default for user choice
|
autoplayBestStream: false, // Disabled by default for user choice
|
||||||
|
|
|
||||||
|
|
@ -530,31 +530,6 @@ const LogoSourceSettings = () => {
|
||||||
logger.error(`[LogoSourceSettings] Error fetching TMDB images:`, tmdbError);
|
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) {
|
} catch (err) {
|
||||||
logger.error(`[LogoSourceSettings] Error fetching ${show.name} logos:`, err);
|
logger.error(`[LogoSourceSettings] Error fetching ${show.name} logos:`, err);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -577,10 +577,16 @@ class CatalogService {
|
||||||
|
|
||||||
private convertMetaToStreamingContent(meta: Meta): StreamingContent {
|
private convertMetaToStreamingContent(meta: Meta): StreamingContent {
|
||||||
// Basic conversion for catalog display - no enhanced metadata processing
|
// 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;
|
let posterUrl = meta.poster;
|
||||||
if (!posterUrl || posterUrl.trim() === '' || posterUrl === 'null' || posterUrl === 'undefined') {
|
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 {
|
return {
|
||||||
|
|
@ -590,7 +596,7 @@ class CatalogService {
|
||||||
poster: posterUrl,
|
poster: posterUrl,
|
||||||
posterShape: 'poster',
|
posterShape: 'poster',
|
||||||
banner: meta.background,
|
banner: meta.background,
|
||||||
logo: `https://images.metahub.space/logo/medium/${meta.id}/img`, // Use metahub for catalog display
|
logo: logoUrl,
|
||||||
imdbRating: meta.imdbRating,
|
imdbRating: meta.imdbRating,
|
||||||
year: meta.year,
|
year: meta.year,
|
||||||
genres: meta.genres,
|
genres: meta.genres,
|
||||||
|
|
@ -612,8 +618,8 @@ class CatalogService {
|
||||||
poster: meta.poster || 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image',
|
poster: meta.poster || 'https://via.placeholder.com/300x450/cccccc/666666?text=No+Image',
|
||||||
posterShape: 'poster',
|
posterShape: 'poster',
|
||||||
banner: meta.background,
|
banner: meta.background,
|
||||||
// Use addon's logo if available, fallback to metahub
|
// Use addon's logo if available, otherwise undefined
|
||||||
logo: (meta as any).logo || `https://images.metahub.space/logo/medium/${meta.id}/img`,
|
logo: (meta as any).logo || undefined,
|
||||||
imdbRating: meta.imdbRating,
|
imdbRating: meta.imdbRating,
|
||||||
year: meta.year,
|
year: meta.year,
|
||||||
genres: meta.genres,
|
genres: meta.genres,
|
||||||
|
|
|
||||||
|
|
@ -710,7 +710,6 @@ class StremioService {
|
||||||
cleanBaseUrl = `https://${cleanBaseUrl}`;
|
cleanBaseUrl = `https://${cleanBaseUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('Addon base URL:', cleanBaseUrl, queryString ? `with query: ${queryString}` : '');
|
|
||||||
return { baseUrl: cleanBaseUrl, queryParams: queryString };
|
return { baseUrl: cleanBaseUrl, queryParams: queryString };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -760,16 +759,13 @@ class StremioService {
|
||||||
|
|
||||||
// Add filters
|
// Add filters
|
||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
logger.log(`Adding ${filters.length} filters to ${manifest.name} request`);
|
|
||||||
filters.forEach(filter => {
|
filters.forEach(filter => {
|
||||||
if (filter.value) {
|
if (filter.value) {
|
||||||
logger.log(`Adding filter ${filter.title}=${filter.value}`);
|
|
||||||
url += `&${encodeURIComponent(filter.title)}=${encodeURIComponent(filter.value)}`;
|
url += `&${encodeURIComponent(filter.title)}=${encodeURIComponent(filter.value)}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(`🔗 [${manifest.name}] Requesting catalog: ${url}`);
|
|
||||||
|
|
||||||
const response = await this.retryRequest(async () => {
|
const response = await this.retryRequest(async () => {
|
||||||
return await axios.get(url);
|
return await axios.get(url);
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,6 @@
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { TMDBService } from '../services/tmdbService';
|
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
|
* 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
|
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
|
* Checks if a URL is from TMDB
|
||||||
* @param url The URL to check
|
* @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
|
* Fetches a banner image from TMDB
|
||||||
* @param imdbId The IMDB ID of the content
|
* @param tmdbId The TMDB ID of the content
|
||||||
* @param tmdbId The TMDB ID of the content (if available)
|
|
||||||
* @param type The content type ('movie' or 'series')
|
* @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
|
* @returns The URL of the banner image, or null if none found
|
||||||
*/
|
*/
|
||||||
export const fetchBannerWithPreference = async (
|
export const fetchBannerFromTMDB = async (
|
||||||
imdbId: string | null,
|
|
||||||
tmdbId: number | string | null,
|
tmdbId: number | string | null,
|
||||||
type: 'movie' | 'series',
|
type: 'movie' | 'series'
|
||||||
preference: 'metahub' | 'tmdb'
|
|
||||||
): Promise<string | null> => {
|
): 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 (!tmdbId) {
|
||||||
if (preference === 'tmdb') {
|
logger.warn(`[logoUtils] Cannot fetch from TMDB - no TMDB ID provided`);
|
||||||
// Try TMDB first if it's the preferred source
|
return null;
|
||||||
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`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try Metahub if it's preferred or TMDB failed
|
try {
|
||||||
if (imdbId) {
|
const tmdbService = TMDBService.getInstance();
|
||||||
const metahubUrl = `https://images.metahub.space/background/large/${imdbId}/img`;
|
|
||||||
|
|
||||||
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 {
|
let bannerUrl = null;
|
||||||
const response = await fetch(metahubUrl, { method: 'HEAD' });
|
if (tmdbType === 'movie') {
|
||||||
if (response.ok) {
|
const movieDetails = await tmdbService.getMovieDetails(tmdbId.toString());
|
||||||
logger.log(`[logoUtils] Successfully fetched banner from Metahub: ${metahubUrl}`);
|
if (movieDetails && movieDetails.backdrop_path) {
|
||||||
return metahubUrl;
|
bannerUrl = tmdbService.getImageUrl(movieDetails.backdrop_path, 'original');
|
||||||
|
logger.log(`[logoUtils] Found backdrop_path: ${movieDetails.backdrop_path}`);
|
||||||
} else {
|
} 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 TMDB for ${type} (ID: ${tmdbId})`);
|
||||||
logger.warn(`[logoUtils] No banner found from any source for ${type} (IMDB: ${imdbId}, TMDB: ${tmdbId})`);
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
Loading…
Reference in a new issue