import React, { useEffect, useState } from 'react'; import { useParams, Link } from 'react-router-dom'; import { Play, Plus, ThumbsUp, Share2, ChevronDown } from 'lucide-react'; import { getTvShowMetadata, getGenresItems } from '../lib/api'; import ContentRow from '../components/ContentRow'; import { useToast } from '@/hooks/use-toast'; interface Episode { episode_number: number; name: string; overview: string; still_path: string; air_date: string; runtime: number; fileName?: string; // The actual file name with extension } interface Season { season_number: number; name: string; overview: string; poster_path: string; air_date: string; episodes: Episode[]; } interface FileStructureItem { type: string; path: string; contents?: FileStructureItem[]; size?: number; } const TvShowDetailPage = () => { const { title } = useParams<{ title: string }>(); const [tvShow, setTvShow] = useState(null); const [seasons, setSeasons] = useState([]); const [selectedSeason, setSelectedSeason] = useState(1); const [episodes, setEpisodes] = useState([]); const [loading, setLoading] = useState(true); const [seasonsLoading, setSeasonsLoading] = useState(false); const [similarShows, setSimilarShows] = useState([]); const [expandedSeasons, setExpandedSeasons] = useState(false); const { toast } = useToast(); // Helper function to extract episode info from file path const extractEpisodeInfoFromPath = (filePath: string): Episode | null => { // Get the actual file name (with extension) from the full file path const fileName = filePath.split('/').pop() || filePath; // For file names like "Nanbaka - S01E02 - The Inmates Are Stupid! The Guards Are Kind of Stupid, Too! SDTV.mp4" const episodeRegex = /S(\d+)E(\d+)\s*-\s*(.+?)(?=\.\w+$)/i; const match = fileName.match(episodeRegex); if (match) { const episodeNumber = parseInt(match[2], 10); const episodeName = match[3].trim(); // Determine quality from the file name const isHD = fileName.toLowerCase().includes('720p') || fileName.toLowerCase().includes('1080p') || fileName.toLowerCase().includes('hdtv'); return { episode_number: episodeNumber, name: episodeName, overview: '', // No overview available from file path still_path: '/placeholder.svg', // Use placeholder image air_date: '', // No air date available runtime: isHD ? 24 : 22, // Approximate runtime based on quality fileName: fileName // Store only the file name with extension }; } return null; }; // Helper function to extract season number and name from directory path const getSeasonInfoFromPath = (path: string): { number: number, name: string } => { const seasonRegex = /Season\s*(\d+)/i; const specialsRegex = /Specials/i; if (specialsRegex.test(path)) { return { number: 0, name: 'Specials' }; } const match = path.match(seasonRegex); if (match) { return { number: parseInt(match[1], 10), name: `Season ${match[1]}` }; } return { number: 1, name: 'Season 1' }; // Default if no match }; // Process the file structure to extract seasons and episodes const processTvShowFileStructure = (fileStructure: any): Season[] => { if (!fileStructure || !fileStructure.contents) { return []; } const extractedSeasons: Season[] = []; // Find season directories const seasonDirectories = fileStructure.contents.filter( (item: FileStructureItem) => item.type === 'directory' ); seasonDirectories.forEach((seasonDir: FileStructureItem) => { if (!seasonDir.contents) return; const seasonInfo = getSeasonInfoFromPath(seasonDir.path); const episodesArr: Episode[] = []; // Process files in this season directory seasonDir.contents.forEach((item: FileStructureItem) => { if (item.type === 'file') { const episode = extractEpisodeInfoFromPath(item.path); if (episode) { episodesArr.push(episode); } } }); // Sort episodes by episode number episodesArr.sort((a, b) => a.episode_number - b.episode_number); if (episodesArr.length > 0) { extractedSeasons.push({ season_number: seasonInfo.number, name: seasonInfo.name, overview: '', // No overview available poster_path: tvShow?.data?.image || '/placeholder.svg', air_date: tvShow?.data?.year || '', episodes: episodesArr }); } }); // Sort seasons by season number extractedSeasons.sort((a, b) => a.season_number - b.season_number); return extractedSeasons; }; useEffect(() => { const fetchTvShowData = async () => { if (!title) return; try { setLoading(true); const data = await getTvShowMetadata(title); setTvShow(data); if (data && data.file_structure) { const processedSeasons = processTvShowFileStructure(data.file_structure); setSeasons(processedSeasons); // Select the first season by default (Specials = 0, Season 1 = 1) if (processedSeasons.length > 0) { setSelectedSeason(processedSeasons[0].season_number); } } // Fetch similar shows based on individual genres if (data.data && data.data.genres && data.data.genres.length > 0) { const currentShowName = data.data.name; const showsByGenre = await Promise.all( data.data.genres.map(async (genre: any) => { // Pass a single genre name for each call const genreResult = await getGenresItems([genre.name], 'series', 10, 1); console.log('Genre result:', genreResult); if (genreResult.series && Array.isArray(genreResult.series)) { return genreResult.series.map((showItem: any) => { const { title: similarTitle } = showItem; console.log('Similar show:', showItem); // Skip current show if (similarTitle === currentShowName) return null; return { type: 'tvshow', title: similarTitle, }; }); } return []; }) ); // Flatten the array of arrays and remove null results const flattenedShows = showsByGenre.flat().filter(Boolean); // Remove duplicates based on the title const uniqueShows = Array.from( new Map(flattenedShows.map(show => [show.title, show])).values() ); setSimilarShows(uniqueShows); } } catch (error) { console.error(`Error fetching TV show details for ${title}:`, error); toast({ title: "Error loading TV show details", description: "Please try again later", variant: "destructive" }); } finally { setLoading(false); } }; fetchTvShowData(); }, [title, toast]); // Update episodes when selectedSeason or seasons change useEffect(() => { if (seasons.length > 0) { const season = seasons.find(s => s.season_number === selectedSeason); if (season) { setEpisodes(season.episodes); } else { setEpisodes([]); } } }, [selectedSeason, seasons]); const toggleExpandSeasons = () => { setExpandedSeasons(!expandedSeasons); }; if (loading) { return (
); } if (!tvShow) { return (

TV Show Not Found

We couldn't find the TV show you're looking for.

Back to TV Shows
); } const tvShowData = tvShow.data; const airYears = tvShowData.year; const language = tvShowData.originalLanguage; const showName = (tvShowData.translations?.nameTranslations?.find((t: any) => t.language === 'eng')?.name || tvShowData.name || ''); const overview = tvShowData.translations?.overviewTranslations?.find((t: any) => t.language === 'eng')?.overview || tvShowData.translations?.overviewTranslations?.[0]?.overview || tvShowData.overview || 'No overview available.'; // Get the current season details const currentSeason = seasons.find(s => s.season_number === selectedSeason); const currentSeasonName = currentSeason?.name || `Season ${selectedSeason}`; return (
{/* Hero backdrop */}
{showName} { const target = e.target as HTMLImageElement; target.src = '/placeholder.svg'; }} />
{/* TV Show details */}
{/* Poster */}
{showName} { const target = e.target as HTMLImageElement; target.src = '/placeholder.svg'; }} />
{/* Details */}

{showName}

{airYears && {airYears}} {tvShowData.vote_average && ( {tvShowData.vote_average.toFixed(1)} )} {seasons.length > 0 && ( {seasons.length} Season{seasons.length !== 1 ? 's' : ''} )}
{tvShowData.genres && tvShowData.genres.map((genre: any, index: number) => ( {genre.name || genre} ))}

{overview}

Play
{/* Additional details */}
{language && (

Language

{language}

)} {tvShowData.translations?.nameTranslations?.find((t: any) => t.isPrimary) && (

Tagline

"{tvShowData.translations.nameTranslations.find((t: any) => t.isPrimary).tagline || ''}"

)}
{/* Episodes */}

Episodes

{expandedSeasons && (
{seasons.map((season) => ( ))}
)}
{seasonsLoading ? (
) : episodes.length === 0 ? (
No episodes available for this season.
) : ( episodes.map((episode) => (
{episode.name} { const target = e.target as HTMLImageElement; target.src = '/placeholder.svg'; }} />
{episode.runtime ? `${episode.runtime} min` : '--'}

{episode.episode_number}. {episode.name}

{episode.air_date ? new Date(episode.air_date).toLocaleDateString() : ''}

{episode.overview || 'No description available.'}

)) )}
{/* Similar Shows */} {similarShows.length > 0 && (
)}
); }; export default TvShowDetailPage;