removed harcorded metahub

This commit is contained in:
tapframe 2025-10-08 12:50:32 +05:30
parent 14163513e3
commit 42c236e235
9 changed files with 179 additions and 365 deletions

View file

@ -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;

View file

@ -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(() => {

View file

@ -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));
}
}
}

View file

@ -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();

View file

@ -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

View file

@ -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 {

View file

@ -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,

View file

@ -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);

View file

@ -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;
};