import React, { useEffect, useState, useRef } from 'react'; import { getMovieLinkByTitle, getMovieCard } from '../lib/api'; import { useToast } from '@/hooks/use-toast'; import VideoPlayer from './VideoPlayer'; import VideoPlayerControls from './VideoPlayerControls'; import { Loader2, Play } from 'lucide-react'; import { MovieCardData } from './ContentCard'; interface ProgressData { status: string; progress: number; downloaded: number; total: number; } interface MoviePlayerProps { movieTitle: string; videoUrl?: string; contentRatings?: any[]; poster?: string; startTime?: number; onClosePlayer?: () => void; onProgressUpdate?: (currentTime: number, duration: number) => void; onVideoEnded?: () => void; showNextButton?: boolean; } const MoviePlayer: React.FC = ({ movieTitle, videoUrl, contentRatings, poster, startTime = 0, onClosePlayer, onProgressUpdate, onVideoEnded, showNextButton = false }) => { const [videoUrlState, setVideoUrlState] = useState(videoUrl || null); const [loading, setLoading] = useState(!videoUrl); const [error, setError] = useState(null); const [progress, setProgress] = useState(null); const [videoFetched, setVideoFetched] = useState(!!videoUrl); const [cardData, setCardData] = useState(null); const [selectedImage, setSelectedImage] = useState(null); const [imageLoaded, setImageLoaded] = useState(false); const { toast } = useToast(); const pollingIntervalRef = useRef(null); const timeoutRef = useRef(null); const videoFetchedRef = useRef(!!videoUrl); const [ratingInfo, setRatingInfo] = useState<{ rating: string; description: string } | null>(null); const [currentTime, setCurrentTime] = useState(startTime); const containerRef = useRef(null); const videoRef = useRef(null); // Reset fade state when image changes useEffect(() => { setImageLoaded(false); }, [selectedImage]); // Update progress and propagate up const handleProgressUpdate = (time: number, duration: number) => { setCurrentTime(time); onProgressUpdate?.(time, duration); }; // Seek handler const handleSeek = (time: number) => { if (videoRef.current) { videoRef.current.currentTime = time; setCurrentTime(time); } }; // Random image selector const selectRandomImage = (card: MovieCardData | null) => { if (!card) return null; if (card.banner && card.banner.length > 0) { return card.banner[Math.floor(Math.random() * card.banner.length)].image; } if (card.portrait && card.portrait.length > 0) { return card.portrait[Math.floor(Math.random() * card.portrait.length)].image; } return card.image; }; // Fetch movie link or start polling const fetchMovieLink = async () => { if (videoFetchedRef.current || videoUrlState) return; try { const response = await getMovieLinkByTitle(movieTitle); if (response.url) { pollingIntervalRef.current && clearInterval(pollingIntervalRef.current); setVideoUrlState(response.url); setVideoFetched(true); videoFetchedRef.current = true; setLoading(false); } else if (response.progress_url) { if (!pollingIntervalRef.current) { pollingIntervalRef.current = setInterval(async () => { try { const res = await fetch(response.progress_url!); const data = await res.json(); setProgress(data.progress); if (data.progress.progress >= 100) { clearInterval(pollingIntervalRef.current!); timeoutRef.current = setTimeout(fetchMovieLink, 5000); } } catch (e) { console.error(e); } }, 2000); } } else { throw new Error('No URL or progress URL'); } } catch (e) { console.error('Error fetching movie link:', e); setError('Failed to load video'); toast({ title: 'Error', description: 'Could not load the video', variant: 'destructive' }); setLoading(false); } }; // Fetch card data & ratings useEffect(() => { const fetchCard = async () => { try { const movieData = await getMovieCard(movieTitle); setCardData(movieData); const img = selectRandomImage(movieData); setSelectedImage(img); // Poster fallback if (!poster) { poster = movieData.image || poster; } // Ratings const ratings = contentRatings && contentRatings.length > 0 ? contentRatings : movieData.content_ratings || []; if (ratings.length) { const us = ratings.find((r: any) => r.country === 'usa') || ratings[0]; setRatingInfo({ rating: us.name || 'NR', description: us.description || '' }); } } catch (e) { console.error('Failed to fetch movie card:', e); } }; fetchCard(); }, [movieTitle, contentRatings, poster]); // Initial link fetch / cleanup useEffect(() => { if (!videoUrlState) { fetchMovieLink(); } else { setVideoFetched(true); videoFetchedRef.current = true; setLoading(false); } return () => { pollingIntervalRef.current && clearInterval(pollingIntervalRef.current); timeoutRef.current && clearTimeout(timeoutRef.current); }; }, [movieTitle, videoUrlState]); // Sync loading state useEffect(() => { if (videoUrlState) setLoading(false); }, [videoUrlState]); // Error UI if (error) { return (
😢

Error Playing Movie

{error}

); } // Loading / preparing UI with fade‑in backdrop if (loading || !videoFetched || !videoUrlState) { return ( <>
setImageLoaded(true)} onError={(e) => { (e.target as HTMLImageElement).src = '/placeholder.svg'; }} className={`w-full h-full object-cover transition-opacity duration-700 ease-in-out ${ imageLoaded ? 'opacity-100' : 'opacity-0' }`} />
{poster ? ( {movieTitle} ) : (
)}

{progress && progress.progress < 100 ? `Preparing "${movieTitle}"` : `Loading "${movieTitle}"` }

{progress ? ( <>

{progress.progress < 5 ? 'Initializing your stream...' : progress.progress < 100 ? 'Your stream is being prepared.' : 'Almost ready! Starting playback soon...'}

{Math.round(progress.progress)}% complete

) : (
)}
); } // Playback UI return (
); }; export default MoviePlayer;