mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-10 12:01:55 +00:00
Enhance SeriesContent and useMetadata hooks for improved image handling and cast fetching
This update modifies the SeriesContent component to check if the episode still_path is a full URL before attempting to fetch it from TMDB, improving image loading efficiency. Additionally, the useMetadata hook has been enhanced to include caching for cast data, better error handling, and improved logging for debugging purposes. The logic for handling TMDB and IMDb IDs has been streamlined, ensuring a more robust data fetching process.
This commit is contained in:
parent
4b64dc64ee
commit
8cbda5c902
2 changed files with 94 additions and 62 deletions
|
|
@ -250,8 +250,13 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
const renderVerticalEpisodeCard = (episode: Episode) => {
|
const renderVerticalEpisodeCard = (episode: Episode) => {
|
||||||
let episodeImage = EPISODE_PLACEHOLDER;
|
let episodeImage = EPISODE_PLACEHOLDER;
|
||||||
if (episode.still_path) {
|
if (episode.still_path) {
|
||||||
const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500');
|
// Check if still_path is already a full URL
|
||||||
if (tmdbUrl) episodeImage = tmdbUrl;
|
if (episode.still_path.startsWith('http')) {
|
||||||
|
episodeImage = episode.still_path;
|
||||||
|
} else {
|
||||||
|
const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500');
|
||||||
|
if (tmdbUrl) episodeImage = tmdbUrl;
|
||||||
|
}
|
||||||
} else if (metadata?.poster) {
|
} else if (metadata?.poster) {
|
||||||
episodeImage = metadata.poster;
|
episodeImage = metadata.poster;
|
||||||
}
|
}
|
||||||
|
|
@ -369,8 +374,13 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
||||||
const renderHorizontalEpisodeCard = (episode: Episode) => {
|
const renderHorizontalEpisodeCard = (episode: Episode) => {
|
||||||
let episodeImage = EPISODE_PLACEHOLDER;
|
let episodeImage = EPISODE_PLACEHOLDER;
|
||||||
if (episode.still_path) {
|
if (episode.still_path) {
|
||||||
const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500');
|
// Check if still_path is already a full URL
|
||||||
if (tmdbUrl) episodeImage = tmdbUrl;
|
if (episode.still_path.startsWith('http')) {
|
||||||
|
episodeImage = episode.still_path;
|
||||||
|
} else {
|
||||||
|
const tmdbUrl = tmdbService.getImageUrl(episode.still_path, 'w500');
|
||||||
|
if (tmdbUrl) episodeImage = tmdbUrl;
|
||||||
|
}
|
||||||
} else if (metadata?.poster) {
|
} else if (metadata?.poster) {
|
||||||
episodeImage = metadata.poster;
|
episodeImage = metadata.poster;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,20 +184,23 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadCast = async () => {
|
const loadCast = async () => {
|
||||||
if (!metadata || !metadata.id) return;
|
logger.log('[loadCast] Starting cast fetch for:', id);
|
||||||
|
|
||||||
setLoadingCast(true);
|
setLoadingCast(true);
|
||||||
try {
|
try {
|
||||||
|
// Check cache first
|
||||||
|
const cachedCast = cacheService.getCast(id, type);
|
||||||
|
if (cachedCast) {
|
||||||
|
logger.log('[loadCast] Using cached cast data');
|
||||||
|
setCast(cachedCast);
|
||||||
|
setLoadingCast(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle TMDB IDs
|
// Handle TMDB IDs
|
||||||
let metadataId = id;
|
|
||||||
let metadataType = type;
|
|
||||||
|
|
||||||
if (id.startsWith('tmdb:')) {
|
if (id.startsWith('tmdb:')) {
|
||||||
const extractedTmdbId = id.split(':')[1];
|
const tmdbId = id.split(':')[1];
|
||||||
logger.log('[loadCast] Using extracted TMDB ID:', extractedTmdbId);
|
logger.log('[loadCast] Using TMDB ID directly:', tmdbId);
|
||||||
|
const castData = await tmdbService.getCredits(parseInt(tmdbId), type);
|
||||||
// For TMDB IDs, we'll use the TMDB API directly
|
|
||||||
const castData = await tmdbService.getCredits(parseInt(extractedTmdbId), type);
|
|
||||||
if (castData && castData.cast) {
|
if (castData && castData.cast) {
|
||||||
const formattedCast = castData.cast.map((actor: any) => ({
|
const formattedCast = castData.cast.map((actor: any) => ({
|
||||||
id: actor.id,
|
id: actor.id,
|
||||||
|
|
@ -205,49 +208,41 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
character: actor.character,
|
character: actor.character,
|
||||||
profile_path: actor.profile_path
|
profile_path: actor.profile_path
|
||||||
}));
|
}));
|
||||||
|
logger.log(`[loadCast] Found ${formattedCast.length} cast members from TMDB`);
|
||||||
setCast(formattedCast);
|
setCast(formattedCast);
|
||||||
|
cacheService.setCast(id, type, formattedCast);
|
||||||
setLoadingCast(false);
|
setLoadingCast(false);
|
||||||
return formattedCast;
|
return;
|
||||||
}
|
}
|
||||||
setLoadingCast(false);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue with the existing logic for non-TMDB IDs
|
|
||||||
const cachedCast = cacheService.getCast(id, type);
|
|
||||||
if (cachedCast) {
|
|
||||||
setCast(cachedCast);
|
|
||||||
setLoadingCast(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load cast in parallel with a fallback to empty array
|
// Handle IMDb IDs or convert to TMDB ID
|
||||||
const castLoadingPromise = loadWithFallback(async () => {
|
let tmdbId;
|
||||||
const tmdbId = await withTimeout(
|
if (id.startsWith('tt')) {
|
||||||
tmdbService.findTMDBIdByIMDB(id),
|
logger.log('[loadCast] Converting IMDb ID to TMDB ID');
|
||||||
API_TIMEOUT
|
tmdbId = await tmdbService.findTMDBIdByIMDB(id);
|
||||||
);
|
}
|
||||||
|
|
||||||
if (tmdbId) {
|
|
||||||
const castData = await withTimeout(
|
|
||||||
tmdbService.getCredits(tmdbId, type),
|
|
||||||
API_TIMEOUT,
|
|
||||||
{ cast: [], crew: [] }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (castData.cast && castData.cast.length > 0) {
|
|
||||||
setCast(castData.cast);
|
|
||||||
cacheService.setCast(id, type, castData.cast);
|
|
||||||
return castData.cast;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
await castLoadingPromise;
|
if (tmdbId) {
|
||||||
|
logger.log('[loadCast] Fetching cast using TMDB ID:', tmdbId);
|
||||||
|
const castData = await tmdbService.getCredits(tmdbId, type);
|
||||||
|
if (castData && castData.cast) {
|
||||||
|
const formattedCast = castData.cast.map((actor: any) => ({
|
||||||
|
id: actor.id,
|
||||||
|
name: actor.name,
|
||||||
|
character: actor.character,
|
||||||
|
profile_path: actor.profile_path
|
||||||
|
}));
|
||||||
|
logger.log(`[loadCast] Found ${formattedCast.length} cast members`);
|
||||||
|
setCast(formattedCast);
|
||||||
|
cacheService.setCast(id, type, formattedCast);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn('[loadCast] Could not find TMDB ID for cast fetch');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load cast:', error);
|
logger.error('[loadCast] Failed to load cast:', error);
|
||||||
setCast([]);
|
// Don't clear existing cast data on error
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingCast(false);
|
setLoadingCast(false);
|
||||||
}
|
}
|
||||||
|
|
@ -462,13 +457,6 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
setMetadata(content.value);
|
setMetadata(content.value);
|
||||||
// Update cache
|
// Update cache
|
||||||
cacheService.setMetadata(id, type, content.value);
|
cacheService.setMetadata(id, type, content.value);
|
||||||
|
|
||||||
if (type === 'series') {
|
|
||||||
// Load series data after the enhanced metadata is processed
|
|
||||||
setTimeout(() => {
|
|
||||||
loadSeriesData().catch(console.error);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Content not found');
|
throw new Error('Content not found');
|
||||||
}
|
}
|
||||||
|
|
@ -499,7 +487,10 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
const groupedAddonEpisodes: GroupedEpisodes = {};
|
const groupedAddonEpisodes: GroupedEpisodes = {};
|
||||||
|
|
||||||
addonVideos.forEach((video: any) => {
|
addonVideos.forEach((video: any) => {
|
||||||
const seasonNumber = video.season || 1;
|
const seasonNumber = video.season;
|
||||||
|
if (!seasonNumber || seasonNumber < 1) {
|
||||||
|
return; // Skip season 0, which often contains extras
|
||||||
|
}
|
||||||
const episodeNumber = video.episode || video.number || 1;
|
const episodeNumber = video.episode || video.number || 1;
|
||||||
|
|
||||||
if (!groupedAddonEpisodes[seasonNumber]) {
|
if (!groupedAddonEpisodes[seasonNumber]) {
|
||||||
|
|
@ -514,7 +505,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
season_number: seasonNumber,
|
season_number: seasonNumber,
|
||||||
episode_number: episodeNumber,
|
episode_number: episodeNumber,
|
||||||
air_date: video.released ? video.released.split('T')[0] : video.firstAired ? video.firstAired.split('T')[0] : '',
|
air_date: video.released ? video.released.split('T')[0] : video.firstAired ? video.firstAired.split('T')[0] : '',
|
||||||
still_path: video.thumbnail ? video.thumbnail.replace('https://image.tmdb.org/t/p/w500', '') : null,
|
still_path: video.thumbnail,
|
||||||
vote_average: parseFloat(video.rating) || 0,
|
vote_average: parseFloat(video.rating) || 0,
|
||||||
runtime: undefined,
|
runtime: undefined,
|
||||||
episodeString: `S${seasonNumber.toString().padStart(2, '0')}E${episodeNumber.toString().padStart(2, '0')}`,
|
episodeString: `S${seasonNumber.toString().padStart(2, '0')}E${episodeNumber.toString().padStart(2, '0')}`,
|
||||||
|
|
@ -531,6 +522,32 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.log(`📺 Processed addon episodes into ${Object.keys(groupedAddonEpisodes).length} seasons`);
|
logger.log(`📺 Processed addon episodes into ${Object.keys(groupedAddonEpisodes).length} seasons`);
|
||||||
|
|
||||||
|
// Fetch season posters from TMDB
|
||||||
|
try {
|
||||||
|
const tmdbIdToUse = tmdbId || (id.startsWith('tt') ? await tmdbService.findTMDBIdByIMDB(id) : null);
|
||||||
|
if (tmdbIdToUse) {
|
||||||
|
if (!tmdbId) setTmdbId(tmdbIdToUse);
|
||||||
|
const showDetails = await tmdbService.getTVShowDetails(tmdbIdToUse);
|
||||||
|
if (showDetails?.seasons) {
|
||||||
|
Object.keys(groupedAddonEpisodes).forEach(seasonStr => {
|
||||||
|
const seasonNum = parseInt(seasonStr, 10);
|
||||||
|
const seasonInfo = showDetails.seasons.find(s => s.season_number === seasonNum);
|
||||||
|
const seasonPosterPath = seasonInfo?.poster_path;
|
||||||
|
if (seasonPosterPath) {
|
||||||
|
groupedAddonEpisodes[seasonNum] = groupedAddonEpisodes[seasonNum].map(ep => ({
|
||||||
|
...ep,
|
||||||
|
season_poster_path: seasonPosterPath,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logger.log('🖼️ Successfully fetched and attached TMDB season posters to addon episodes.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to fetch TMDB season posters for addon episodes:', error);
|
||||||
|
}
|
||||||
|
|
||||||
setGroupedEpisodes(groupedAddonEpisodes);
|
setGroupedEpisodes(groupedAddonEpisodes);
|
||||||
|
|
||||||
// Set the first available season
|
// Set the first available season
|
||||||
|
|
@ -561,11 +578,16 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const transformedEpisodes: GroupedEpisodes = {};
|
const transformedEpisodes: GroupedEpisodes = {};
|
||||||
Object.entries(allEpisodes).forEach(([season, episodes]) => {
|
Object.entries(allEpisodes).forEach(([seasonStr, episodes]) => {
|
||||||
const seasonInfo = showDetails?.seasons?.find(s => s.season_number === parseInt(season));
|
const seasonNum = parseInt(seasonStr, 10);
|
||||||
|
if (seasonNum < 1) {
|
||||||
|
return; // Skip season 0, which often contains extras
|
||||||
|
}
|
||||||
|
|
||||||
|
const seasonInfo = showDetails?.seasons?.find(s => s.season_number === seasonNum);
|
||||||
const seasonPosterPath = seasonInfo?.poster_path;
|
const seasonPosterPath = seasonInfo?.poster_path;
|
||||||
|
|
||||||
transformedEpisodes[parseInt(season)] = episodes.map(episode => ({
|
transformedEpisodes[seasonNum] = episodes.map(episode => ({
|
||||||
...episode,
|
...episode,
|
||||||
episodeString: `S${episode.season_number.toString().padStart(2, '0')}E${episode.episode_number.toString().padStart(2, '0')}`,
|
episodeString: `S${episode.season_number.toString().padStart(2, '0')}E${episode.episode_number.toString().padStart(2, '0')}`,
|
||||||
season_poster_path: seasonPosterPath || null
|
season_poster_path: seasonPosterPath || null
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue