import getAnimeInfo from "@/src/utils/getAnimeInfo.utils"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlay, faClosedCaptioning, faMicrophone, } from "@fortawesome/free-solid-svg-icons"; import { useEffect, useState, useMemo } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import CategoryCard from "@/src/components/categorycard/CategoryCard"; import Loader from "@/src/components/Loader/Loader"; import Error from "@/src/components/error/Error"; import { useLanguage } from "@/src/context/LanguageContext"; import Voiceactor from "@/src/components/voiceactor/Voiceactor"; 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, generateBreadcrumbStructuredData, optimizeTitle, } from '@/src/utils/seo.utils'; const InfoItem = ({ label, value, isProducer = true }) => { if (!value) return null; const renderValue = () => { if (Array.isArray(value)) { return value.map((item, index) => ( {isProducer ? ( :;,.?/\\|{}[\]`~*_]/g, "").split(" ").join("-").replace(/-+/g, "-")}`} className="cursor-pointer transition-colors duration-300 hover:text-gray-300" > {item} ) : ( item )} {index < value.length - 1 && ", "} )); } if (isProducer) { return ( :;,.?/\\|{}[\]`~*_]/g, "").split(" ").join("-").replace(/-+/g, "-")}`} className="cursor-pointer transition-colors duration-300 hover:text-gray-300" > {value} ); } return value; }; return (
{`${label}: `} {renderValue()}
); }; const Synopsis = ({ text, isFull, onToggle, isMobile = false }) => { if (!text) return null; const limit = isMobile ? 150 : 270; const isTooLong = text.length > limit; return (
{isTooLong ? ( <> {isFull ? text : (isMobile ?
{text}
: `${text.slice(0, limit)}...`)} ) : text}
); }; const TagsList = ({ tags }) => (
{tags.map((tag, index) => tag.condition && ( ))}
); const DetailGrid = ({ info, isMobile = false }) => { const items = [ { label: "Japanese", value: info?.Japanese }, { label: "Synonyms", value: info?.Synonyms }, { label: "Aired", value: info?.Aired }, { label: "Premiered", value: info?.Premiered }, { label: "Duration", value: info?.Duration }, { label: "Status", value: info?.Status }, { label: "MAL Score", value: info?.["MAL Score"] }, ]; return (
{items.map((item, index) => ( ))} {info?.Genres && (

Genres

{info.Genres.map((genre, index) => ( {genre} ))}
)}
); }; function AnimeInfo({ random = false }) { const { language } = useLanguage(); const { id: paramId } = useParams(); const id = random ? null : paramId; const { id: currentId } = useParams(); const navigate = useNavigate(); const [isFull, setIsFull] = useState(false); const [animeInfo, setAnimeInfo] = useState(null); const [seasons, setSeasons] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (id === "404-not-found-page") return; const fetchAnimeInfo = async () => { setLoading(true); try { const data = await getAnimeInfo(id, random); if (!data?.data) throw new Error("Anime not found"); setSeasons(data?.seasons); setAnimeInfo(data.data); } catch (err) { console.error("Error fetching anime info:", err); setError(err); } finally { setLoading(false); } }; fetchAnimeInfo(); window.scrollTo({ top: 0, behavior: "smooth" }); }, [id, random]); const seoData = useMemo(() => { if (!animeInfo) return null; const { title, japanese_title, poster, animeInfo: info } = animeInfo; const safeTitle = getSafeTitle(title, language, japanese_title); return { safeTitle, title: optimizeTitle(`Watch ${safeTitle} Sub Dub Online Free`), description: generateDescription(info?.Overview), keywords: generateKeywords(animeInfo), canonical: generateCanonicalUrl(`/${animeInfo.id}`), ogImage: generateOGImage(poster), structured: generateAnimeStructuredData(animeInfo), breadcrumb: generateBreadcrumbStructuredData([ { name: 'Home', url: '/' }, { name: animeInfo.title, url: `/${animeInfo.id}` } ]) }; }, [animeInfo, language]); const tags = useMemo(() => { if (!animeInfo?.animeInfo?.tvInfo) return []; const info = animeInfo.animeInfo; return [ { condition: info.tvInfo.rating, text: info.tvInfo.rating, bgColor: "#ffffff" }, { condition: info.tvInfo.quality, text: info.tvInfo.quality, bgColor: "#FFBADE" }, { condition: info.tvInfo.sub, text: info.tvInfo.sub, icon: faClosedCaptioning, bgColor: "#B0E3AF" }, { condition: info.tvInfo.dub, text: info.tvInfo.dub, icon: faMicrophone, bgColor: "#B9E7FF" }, ]; }, [animeInfo]); if (loading) return ; if (error || (!animeInfo && !loading)) return ; if (!animeInfo) { navigate("/404-not-found-page"); return null; } const { poster, japanese_title, animeInfo: info } = animeInfo; const isAiring = animeInfo?.animeInfo?.Status?.toLowerCase() !== "not-yet-aired"; return ( <> {seoData.title}
{/* Mobile Layout */}
{seoData.safeTitle} {animeInfo.adultContent && (
18+
)}

{seoData.safeTitle}

{language === "EN" && japanese_title && (

JP: {japanese_title}

)}
setIsFull(!isFull)} isMobile />
{isAiring ? ( Watch Now ) : (
Not yet released
)}
{/* Desktop Layout */}
{seoData.safeTitle} {animeInfo.adultContent && (
18+
)}

{seoData.safeTitle}

{language === "EN" && japanese_title && (

JP Title: {japanese_title}

)}
setIsFull(!isFull)} /> {isAiring ? ( Watch Now ) : (
Not yet released
)}
{/* Sections */}
{seasons?.length > 0 && (

More Seasons

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

{season.season}

))}
)} {animeInfo?.charactersVoiceActors?.length > 0 && (
)} {animeInfo?.recommended_data?.length > 0 && (
)}
); } export default AnimeInfo;