diff --git a/src/hooks/useMetadata.ts b/src/hooks/useMetadata.ts index 17dda7b8..de7c5b12 100644 --- a/src/hooks/useMetadata.ts +++ b/src/hooks/useMetadata.ts @@ -1781,32 +1781,76 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat let productionInfo: any[] = []; if (type === 'series') { - // Fetch networks for TV shows + // Fetch networks and additional details for TV shows const showDetails = await tmdbService.getTVShowDetails(tmdbId, 'en-US'); - if (showDetails && showDetails.networks) { - productionInfo = Array.isArray(showDetails.networks) - ? showDetails.networks - .map((n: any) => ({ - id: n?.id, - name: n?.name, - logo: tmdbService.getImageUrl(n?.logo_path, 'w185'), - })) - .filter((n: any) => n && (n.logo || n.name)) - : []; + if (showDetails) { + // Fetch networks + if (showDetails.networks) { + productionInfo = Array.isArray(showDetails.networks) + ? showDetails.networks + .map((n: any) => ({ + id: n?.id, + name: n?.name, + logo: tmdbService.getImageUrl(n?.logo_path, 'w185'), + })) + .filter((n: any) => n && (n.logo || n.name)) + : []; + } + + // Fetch additional TV details + const tvDetails = { + status: showDetails.status, + firstAirDate: showDetails.first_air_date, + lastAirDate: showDetails.last_air_date, + numberOfSeasons: showDetails.number_of_seasons, + numberOfEpisodes: showDetails.number_of_episodes, + episodeRunTime: showDetails.episode_run_time, + type: showDetails.type, + originCountry: showDetails.origin_country, + originalLanguage: showDetails.original_language, + createdBy: showDetails.created_by, + }; + + // Update metadata with TV details + setMetadata((prev: any) => ({ + ...prev, + tvDetails + })); } } else if (type === 'movie') { - // Fetch production companies for movies + // Fetch production companies and additional details for movies const movieDetails = await tmdbService.getMovieDetails(String(tmdbId), 'en-US'); - if (movieDetails && movieDetails.production_companies) { - productionInfo = Array.isArray(movieDetails.production_companies) - ? movieDetails.production_companies - .map((c: any) => ({ - id: c?.id, - name: c?.name, - logo: tmdbService.getImageUrl(c?.logo_path, 'w185'), - })) - .filter((c: any) => c && (c.logo || c.name)) - : []; + if (movieDetails) { + // Fetch production companies + if (movieDetails.production_companies) { + productionInfo = Array.isArray(movieDetails.production_companies) + ? movieDetails.production_companies + .map((c: any) => ({ + id: c?.id, + name: c?.name, + logo: tmdbService.getImageUrl(c?.logo_path, 'w185'), + })) + .filter((c: any) => c && (c.logo || c.name)) + : []; + } + + // Fetch additional movie details + const movieDetailsObj = { + status: movieDetails.status, + releaseDate: movieDetails.release_date, + runtime: movieDetails.runtime, + budget: movieDetails.budget, + revenue: movieDetails.revenue, + originalLanguage: movieDetails.original_language, + originCountry: movieDetails.origin_country, + tagline: movieDetails.tagline, + }; + + // Update metadata with movie details + setMetadata((prev: any) => ({ + ...prev, + movieDetails: movieDetailsObj + })); } } diff --git a/src/screens/MetadataScreen.tsx b/src/screens/MetadataScreen.tsx index 30b00339..ab0fafe2 100644 --- a/src/screens/MetadataScreen.tsx +++ b/src/screens/MetadataScreen.tsx @@ -918,6 +918,83 @@ const MetadataScreen: React.FC = () => { /> )} + {/* Movie Details section - shown above recommendations for movies when TMDB enrichment is ON */} + {shouldLoadSecondaryData && Object.keys(groupedEpisodes).length === 0 && metadata?.movieDetails && ( + + Movie Details + + {metadata.movieDetails.tagline && ( + + Tagline + + "{metadata.movieDetails.tagline}" + + + )} + + {metadata.movieDetails.status && ( + + Status + {metadata.movieDetails.status} + + )} + + {metadata.movieDetails.releaseDate && ( + + Release Date + + {new Date(metadata.movieDetails.releaseDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} + + + )} + + {metadata.movieDetails.runtime && ( + + Runtime + + {Math.floor(metadata.movieDetails.runtime / 60)}h {metadata.movieDetails.runtime % 60}m + + + )} + + {metadata.movieDetails.budget && metadata.movieDetails.budget > 0 && ( + + Budget + + ${metadata.movieDetails.budget.toLocaleString()} + + + )} + + {metadata.movieDetails.revenue && metadata.movieDetails.revenue > 0 && ( + + Revenue + + ${metadata.movieDetails.revenue.toLocaleString()} + + + )} + + {metadata.movieDetails.originCountry && metadata.movieDetails.originCountry.length > 0 && ( + + Origin Country + {metadata.movieDetails.originCountry.join(', ')} + + )} + + {metadata.movieDetails.originalLanguage && ( + + Original Language + {metadata.movieDetails.originalLanguage.toUpperCase()} + + )} + + )} + {/* Recommendations Section with skeleton when loading - Lazy loaded */} {type === 'movie' && shouldLoadSecondaryData && ( { ) : ( metadata && )} + + {/* TV Details section - shown after episodes for series when TMDB enrichment is ON */} + {shouldLoadSecondaryData && Object.keys(groupedEpisodes).length > 0 && metadata?.tvDetails && ( + + Show Details + + {metadata.tvDetails.status && ( + + Status + {metadata.tvDetails.status} + + )} + + {metadata.tvDetails.firstAirDate && ( + + First Air Date + + {new Date(metadata.tvDetails.firstAirDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} + + + )} + + {metadata.tvDetails.lastAirDate && ( + + Last Air Date + + {new Date(metadata.tvDetails.lastAirDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} + + + )} + + {metadata.tvDetails.numberOfSeasons && ( + + Seasons + {metadata.tvDetails.numberOfSeasons} + + )} + + {metadata.tvDetails.numberOfEpisodes && ( + + Total Episodes + {metadata.tvDetails.numberOfEpisodes} + + )} + + {metadata.tvDetails.episodeRunTime && metadata.tvDetails.episodeRunTime.length > 0 && ( + + Episode Runtime + + {metadata.tvDetails.episodeRunTime.join(' - ')} min + + + )} + + {metadata.tvDetails.originCountry && metadata.tvDetails.originCountry.length > 0 && ( + + Origin Country + {metadata.tvDetails.originCountry.join(', ')} + + )} + + {metadata.tvDetails.originalLanguage && ( + + Original Language + {metadata.tvDetails.originalLanguage.toUpperCase()} + + )} + + {metadata.tvDetails.createdBy && metadata.tvDetails.createdBy.length > 0 && ( + + Created By + + {metadata.tvDetails.createdBy.map(creator => creator.name).join(', ')} + + + )} + + )} @@ -1114,14 +1277,50 @@ const styles = StyleSheet.create({ opacity: 0.9, }, productionHeader: { - fontSize: 14, + fontSize: 16, fontWeight: '700', color: '#fff', - marginBottom: 8, + marginBottom: 12, textTransform: 'uppercase', letterSpacing: 1, + opacity: 0.9, + }, + tvDetailsContainer: { + paddingHorizontal: 16, + marginTop: 20, + marginBottom: 16, + }, + tvDetailsHeader: { + fontSize: 16, + fontWeight: '700', + color: '#fff', + marginBottom: 12, + textTransform: 'uppercase', + letterSpacing: 1, + opacity: 0.9, + }, + tvDetailRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 8, + borderBottomWidth: 1, + borderBottomColor: 'rgba(255,255,255,0.1)', + }, + tvDetailLabel: { + fontSize: 14, + fontWeight: '600', + color: '#fff', opacity: 0.8, }, + tvDetailValue: { + fontSize: 14, + fontWeight: '500', + color: '#fff', + opacity: 0.9, + textAlign: 'right', + flex: 1, + }, }); diff --git a/src/services/catalogService.ts b/src/services/catalogService.ts index 3781b53d..c00397ec 100644 --- a/src/services/catalogService.ts +++ b/src/services/catalogService.ts @@ -98,6 +98,32 @@ export interface StreamingContent { name: string; logo?: string; }>; + tvDetails?: { + status?: string; + firstAirDate?: string; + lastAirDate?: string; + numberOfSeasons?: number; + numberOfEpisodes?: number; + episodeRunTime?: number[]; + type?: string; + originCountry?: string[]; + originalLanguage?: string; + createdBy?: Array<{ + id: number; + name: string; + profile_path?: string; + }>; + }; + movieDetails?: { + status?: string; + releaseDate?: string; + runtime?: number; + budget?: number; + revenue?: number; + originalLanguage?: string; + originCountry?: string[]; + tagline?: string; + }; } export interface CatalogContent {