/* eslint-disable react/prop-types */ import { useEffect, useRef, useState, useMemo, useCallback } from "react"; import { useLocation, useParams, Link, useNavigate } from "react-router-dom"; import { useLanguage } from "@/src/context/LanguageContext"; import { useWatch } from "@/src/hooks/useWatch"; import BouncingLoader from "@/src/components/ui/bouncingloader/Bouncingloader"; import Episodelist from "@/src/components/episodelist/Episodelist"; import website_name from "@/src/config/website"; import Sidecard from "@/src/components/sidecard/Sidecard"; import { faClosedCaptioning, faMicrophone, faPlay, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Servers from "@/src/components/servers/Servers"; import { Skeleton } from "@/src/components/ui/Skeleton/Skeleton"; import SidecardLoader from "@/src/components/Loader/Sidecard.loader"; import Watchcontrols from "@/src/components/watchcontrols/Watchcontrols"; import useWatchControl from "@/src/hooks/useWatchControl"; import Player from "@/src/components/player/Player"; import getSafeTitle from "@/src/utils/getSafetitle"; import { Helmet } from 'react-helmet-async'; import InfoTag from "@/src/components/ui/InfoTag/InfoTag"; import { generateDescription, generateKeywords, generateCanonicalUrl, generateOGImage, generateAnimeStructuredData, generateVideoStructuredData, generateBreadcrumbStructuredData, optimizeTitle, } from '@/src/utils/seo.utils'; export default function Watch() { const location = useLocation(); const navigate = useNavigate(); const { id: animeId } = useParams(); const queryParams = new URLSearchParams(location.search); let initialEpisodeId = queryParams.get("ep"); const { language } = useLanguage(); const isFirstSet = useRef(true); const [showNextEpisodeSchedule, setShowNextEpisodeSchedule] = useState(true); const { buffering, streamInfo, streamUrl, animeInfo, episodes, nextEpisodeSchedule, animeInfoLoading, totalEpisodes, isFullOverview, intro, outro, subtitles, thumbnail, setIsFullOverview, activeEpisodeNum, episodeId, setEpisodeId, activeServerId, setActiveServerId, servers, serverLoading, activeServerType, setActiveServerType, activeServerName, setActiveServerName, seasons } = useWatch(animeId, initialEpisodeId); const { autoPlay, setAutoPlay, autoSkipIntro, setAutoSkipIntro, autoNext, setAutoNext, } = useWatchControl(); const videoContainerRef = useRef(null); const playerRef = useRef(null); const episodesRef = useRef(null); // Sync URL with episodeId useEffect(() => { if (!episodes?.length) return; const currentEpNum = episodeId; const isValidEpisode = episodes.some(ep => ep.id.split('ep=')[1] === currentEpNum); if (!currentEpNum || !isValidEpisode) { const fallbackId = episodes[0].id.match(/ep=(\d+)/)?.[1]; if (fallbackId && fallbackId !== currentEpNum) setEpisodeId(fallbackId); return; } const newUrl = `/watch/${animeId}?ep=${currentEpNum}`; if (isFirstSet.current) { navigate(newUrl, { replace: true }); isFirstSet.current = false; } else { navigate(newUrl); } }, [episodeId, animeId, navigate, episodes, setEpisodeId]); // Redirect if no episodes useEffect(() => { if (totalEpisodes === 0) navigate(`/${animeId}`); }, [animeId, totalEpisodes, navigate]); // Height adjustment logic const adjustHeight = useCallback(() => { if (window.innerWidth > 1200) { if (playerRef.current && episodesRef.current) { episodesRef.current.style.height = 'auto'; episodesRef.current.style.maxHeight = `${playerRef.current.offsetHeight}px`; } } else if (episodesRef.current) { episodesRef.current.style.height = 'auto'; episodesRef.current.style.maxHeight = 'none'; } }, []); useEffect(() => { const resizeObserver = new ResizeObserver(adjustHeight); if (playerRef.current) resizeObserver.observe(playerRef.current); window.addEventListener('resize', adjustHeight); adjustHeight(); return () => { resizeObserver.disconnect(); window.removeEventListener('resize', adjustHeight); }; }, [adjustHeight, buffering, animeInfoLoading]); const seoData = useMemo(() => { if (!animeInfo) return null; const safeT = getSafeTitle(animeInfo.title, language, animeInfo.japanese_title); return { safeTitle: safeT, pageTitle: optimizeTitle(`Watch ${safeT} Episode ${activeEpisodeNum} Sub Dub Online Free`), pageDescription: generateDescription(`Stream ${safeT} Episode ${activeEpisodeNum} in HD with English Sub and Dub. ${animeInfo.animeInfo?.Overview}`), pageKeywords: `${generateKeywords(animeInfo)}, episode ${activeEpisodeNum}`, canonicalUrl: generateCanonicalUrl(`/watch/${animeId}?ep=${episodeId}`), ogImage: generateOGImage(animeInfo.poster), structured: generateAnimeStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }), videoStructured: generateVideoStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }, streamUrl), breadcrumb: generateBreadcrumbStructuredData([ { name: 'Home', url: '/' }, { name: animeInfo.title, url: `/${animeId}` }, { name: `Episode ${activeEpisodeNum}`, url: `/watch/${animeId}?ep=${episodeId}` } ]) }; }, [animeId, animeInfo, activeEpisodeNum, episodeId, language, streamUrl]); const tags = useMemo(() => { const info = animeInfo?.animeInfo?.tvInfo; if (!info) return []; return [ { condition: info.rating, text: info.rating, bgColor: "#ffffff" }, { condition: info.quality, text: info.quality, bgColor: "#FFBADE" }, { condition: info.sub, text: info.sub, icon: faClosedCaptioning, bgColor: "#B0E3AF" }, { condition: info.dub, text: info.dub, icon: faMicrophone, bgColor: "#B9E7FF" }, ]; }, [animeInfo]); return ( <> {seoData && ( {seoData.pageTitle} )}
{/* Left Column */}
{!buffering ? ( ) : (
)}

{!buffering && !streamInfo ? ( servers ? ( <> Probably this server is down, try other servers
Either reload or try again after sometime ) : ( <> Probably streaming server is down
Either reload or try again after sometime ) ) : null}

{!buffering && (
)}
{nextEpisodeSchedule?.nextEpisodeSchedule && showNextEpisodeSchedule && (
🚀
Next episode around: {new Date(nextEpisodeSchedule.nextEpisodeSchedule).toLocaleString("en-GB", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", hour12: true })}
)}
{/* Episode List (Mobile only) */}
{!episodes ? (
) : ( )}
{/* Info Section */}
{animeInfo ? ( {seoData?.safeTitle} ) : }
{animeInfo ? (

{seoData?.safeTitle}

View Details
) : }
{tags.map((tag, idx) => tag.condition && ( ))}
{animeInfo?.animeInfo?.Overview && (
{animeInfo.animeInfo.Overview.length > 270 ? ( <> {isFullOverview ? animeInfo.animeInfo.Overview : `${animeInfo.animeInfo.Overview.slice(0, 270)}...`} ) : animeInfo.animeInfo.Overview}
)}
{/* Related Anime (Mobile only) */} {!animeInfoLoading && animeInfo?.related_data?.length > 0 && (

Related Anime

)}
{/* Right Column (Desktop Only) */}
{!episodes ? (
) : ( )}
{!animeInfoLoading && animeInfo?.related_data?.length > 0 && (

Related Anime

)}
{/* Sections */}
{seasons?.length > 0 && (

More Seasons

{seasons.map((season, index) => ( {season.season}
')`, backgroundSize: '3px 3px' }} />

{season.season}

))}
)}
); }