Nexorav2 / frontend /components /sections /EpisodesSection.tsx
ChandimaPrabath's picture
v0.2.4 beta - TvShow Player Updated
5a8b2b7
import Link from 'next/link';
import { useEffect, useState } from 'react';
interface FileStructure {
contents?: any[];
}
export default function EpisodesSection({
fileStructure,
tvshow,
}: {
fileStructure: FileStructure;
tvshow: any;
}) {
const [activeSeasonName, setActiveSeasonName] = useState('');
useEffect(() => {
if (!activeSeasonName && fileStructure.contents && fileStructure.contents.length > 0) {
setActiveSeasonName(fileStructure.contents[0].path.split('/').pop() || '');
}
}, [fileStructure.contents, activeSeasonName]);
const activeSeasonContent = fileStructure.contents?.find(
(season) => season.path.split('/').pop() === activeSeasonName,
);
return (
<div className="bg-gray-800/60 backdrop-blur-md rounded-2xl p-6 border border-gray-700/50">
<div className="flex flex-col space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-200">Episodes</h3>
</div>
{/* Season Buttons */}
<div className="overflow-x-auto whitespace-nowrap flex gap-2 scrollbar-hide snap-x snap-mandatory pb-2">
{fileStructure.contents?.map((season, idx) => {
const seasonName = season.path.split('/').pop();
const isSpecials = seasonName === 'Specials';
return (
<button
key={idx}
onClick={() => setActiveSeasonName(seasonName)}
className={`px-4 py-2 min-w-fit rounded-full text-sm font-medium transition-colors snap-start ${
activeSeasonName === seasonName
? 'bg-purple-500 text-white'
: isSpecials
? 'bg-amber-500/20 text-amber-300 hover:bg-amber-500/30'
: 'bg-purple-500/20 text-purple-300 hover:bg-purple-500/30'
}`}
>
{seasonName}
</button>
);
})}
</div>
{/* Episodes List */}
<div className="space-y-8 mt-4">
{activeSeasonContent && (
<div key={activeSeasonName} className="space-y-4">
<h4
className={`text-base font-medium ${
activeSeasonContent.path.includes('Specials')
? 'text-amber-300'
: 'text-purple-300'
}`}
>
{activeSeasonName}
</h4>
<div className="space-y-2">
{activeSeasonContent.contents?.map(
(episode: any, episodeIdx: number) => {
// Skip subtitle files
if (episode.path.endsWith('.srt') || episode.path.endsWith('.vtt')) {
return null;
}
const match = episode.path.match(
/[S](\d+)[E](\d+) - (.+?)\./,
);
if (!match) return null;
const [, , episodeNum, episodeTitle] = match;
const isSpecials =
activeSeasonContent.path.includes('Specials');
// Use the actual file name from the file structure for the Link href
const fileName = episode.path.split('/').pop();
return (
<div
key={episodeIdx}
className="group flex items-center gap-4 p-3 rounded-xl transition-colors hover:bg-gray-700/50 cursor-pointer"
>
{/* Episode Number */}
<div
className={`flex-shrink-0 w-12 h-12 flex items-center justify-center rounded-xl bg-gray-700/50 ${
isSpecials
? 'group-hover:bg-amber-500/20'
: 'group-hover:bg-purple-500/20'
}`}
>
<span
className={`text-lg font-semibold text-gray-300 ${
isSpecials
? 'group-hover:text-amber-300'
: 'group-hover:text-purple-300'
}`}
>
{episodeNum}
</span>
</div>
{/* Episode Info */}
<div className="flex-grow">
<h4 className="text-gray-200 font-medium">
{episodeTitle.replace(/_/g, ' ')}
</h4>
<div className="flex items-center gap-3 text-sm text-gray-400">
<span>
{Math.round(episode.size / 1024 / 1024)}{' '}
MB
</span>
<span></span>
<span>
{episode.path.includes('720p')
? 'HD'
: 'SD'}
</span>
</div>
</div>
{/* Play Button */}
<Link
href={`/watch/tvshow/${tvshow}/${activeSeasonName}/${fileName}`}
className={`flex-shrink-0 p-2 rounded-full text-gray-300 opacity-0 group-hover:opacity-100 transition-opacity ${
isSpecials
? 'bg-amber-500/20 hover:bg-amber-500/30'
: 'bg-purple-500/20 hover:bg-purple-500/30'
}`}
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M4 4l12 6-12 6V4z" />
</svg>
</Link>
</div>
);
},
)}
</div>
</div>
)}
</div>
</div>
</div>
);
}