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'; interface ProgressData { status: string; progress: number; downloaded: number; total: number; } interface MoviePlayerProps { movieTitle: string; videoUrl?: string; contentRatings?: any[]; thumbnail?: string; poster?: string; startTime?: number; onClosePlayer?: () => void; onProgressUpdate?: (currentTime: number, duration: number) => void; onVideoEnded?: () => void; showNextButton?: boolean; } const MoviePlayer: React.FC = ({ movieTitle, videoUrl, contentRatings, thumbnail, 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 { 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); // Update the onProgressUpdate handler to also update currentTime const handleProgressUpdate = (time: number, duration: number) => { setCurrentTime(time); onProgressUpdate?.(time, duration); }; // Handler for seeking from WatchTogether const handleSeek = (time: number) => { if (videoRef.current) { videoRef.current.currentTime = time; setCurrentTime(time); } }; // --- Link Fetching & Polling --- const fetchMovieLink = async () => { if (videoFetchedRef.current || videoUrlState) return; try { const response = await getMovieLinkByTitle(movieTitle); if (response && response.url) { // Stop any polling if running if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); pollingIntervalRef.current = null; } setVideoUrlState(response.url); setVideoFetched(true); videoFetchedRef.current = true; setLoading(false); // Ensure loading is set to false when URL is fetched console.log('Video URL fetched:', response.url); } else if (response && response.progress_url) { startPolling(response.progress_url); } else { console.error('No video URL or progress URL found in response:', response); setError('Video URL not available'); } } catch (error) { console.error('Error fetching movie link:', error); setError('Failed to load video'); toast({ title: "Error", description: "Could not load the video", variant: "destructive" }); } finally { // Only set loading to false if we don't have a video yet if (!videoFetchedRef.current && !videoUrlState) { setLoading(false); } } }; // Fetch content ratings if not provided useEffect(() => { const fetchRatingInfo = async () => { if (contentRatings && contentRatings.length > 0) { const usRating = contentRatings.find(r => r.country === 'usa') || contentRatings[0]; setRatingInfo({ rating: usRating.name || 'NR', description: usRating.description || '' }); return; } try { const movieData = await getMovieCard(movieTitle); if (movieData && movieData.content_ratings) { const ratings = movieData.content_ratings; const usRating = ratings.find((r: any) => r.country === 'US') || ratings[0]; setRatingInfo({ rating: usRating?.name || 'NR', description: usRating?.description || '' }); } } catch (error) { console.error('Failed to fetch movie ratings:', error); } }; fetchRatingInfo(); }, [movieTitle, contentRatings]); const pollProgress = async (progressUrl: string) => { try { const res = await fetch(progressUrl); const data = await res.json(); setProgress(data.progress); if (data.progress.progress >= 100) { if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); pollingIntervalRef.current = null; } if (!videoFetchedRef.current) { timeoutRef.current = setTimeout(fetchMovieLink, 5000); } } } catch (error) { console.error('Error polling progress:', error); } }; const startPolling = (progressUrl: string) => { if (!pollingIntervalRef.current) { const interval = setInterval(() => pollProgress(progressUrl), 2000); pollingIntervalRef.current = interval; } }; // Cleanup on unmount and when dependencies change useEffect(() => { if (!videoUrlState) { fetchMovieLink(); } else { setVideoFetched(true); videoFetchedRef.current = true; setLoading(false); // Make sure loading is false when we have a URL } return () => { if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); if (timeoutRef.current) clearTimeout(timeoutRef.current); }; }, [movieTitle, videoUrl]); // Add effect to update loading state when videoUrlState changes useEffect(() => { if (videoUrlState) { setLoading(false); } }, [videoUrlState]); if (error) { return (
😢

Error Playing Movie

{error}

); } if (loading || !videoFetched || !videoUrlState) { return (
{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

) : (
)}
); } return (
); }; export default MoviePlayer;