import { useEffect, useState, Fragment } from "react"; import { ChevronDownIcon } from "@heroicons/react/20/solid"; import ViewSelector from "./viewSelector"; import ThumbnailOnly from "./viewMode/thumbnailOnly"; import ThumbnailDetail from "./viewMode/thumbnailDetail"; import ListMode from "./viewMode/listMode"; import { toast } from "sonner"; const ITEMS_PER_PAGE = 13; const DEFAULT_VIEW = 3; const fetchEpisodes = async (info, isDub, refresh = false) => { const response = await fetch( `/api/v2/episode/${info.id}?releasing=${ info.status === "RELEASING" ? "true" : "false" }${isDub ? "&dub=true" : ""}${refresh ? "&refresh=true" : ""}` ).then((res) => res.json()); const providers = filterProviders(response); return providers; }; const filterProviders = (response) => { const providersWithMap = response.find((i) => i?.map === true); let providers = response; if (providersWithMap) { providers = response.filter((i) => { if (i?.providerId === "gogoanime" && i?.map !== true) { return null; } return i; }); } return providers; }; const setDefaultProvider = (providers, setProviderId) => { if (providers.length > 0) { const defaultProvider = providers.find( (x) => x.providerId === "gogoanime" || x.providerId === "9anime" ); setProviderId(defaultProvider?.providerId || providers[0].providerId); } }; export default function AnimeEpisode({ info, session, progress, setProgress, setWatch, }) { const [providerId, setProviderId] = useState(); // default provider const [currentPage, setCurrentPage] = useState(1); // for pagination const [visible, setVisible] = useState(false); // for mobile view const itemsPerPage = 13; // choose your number of items per page const [loading, setLoading] = useState(true); const [artStorage, setArtStorage] = useState(null); const [view, setView] = useState(3); const [isDub, setIsDub] = useState(false); const [providers, setProviders] = useState(null); useEffect(() => { setLoading(true); const fetchData = async () => { const providers = await fetchEpisodes(info, isDub); setDefaultProvider(providers, setProviderId); setView(Number(localStorage.getItem("view")) || DEFAULT_VIEW); setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings"))); setProviders(providers); setLoading(false); }; fetchData(); return () => { setCurrentPage(1); setProviders(null); }; }, [info.id, isDub]); // eslint-disable-next-line react-hooks/exhaustive-deps const episodes = providers?.find((provider) => provider.providerId === providerId) ?.episodes || []; const lastEpisodeIndex = currentPage * itemsPerPage; const firstEpisodeIndex = lastEpisodeIndex - itemsPerPage; let currentEpisodes = episodes?.slice(firstEpisodeIndex, lastEpisodeIndex); const totalPages = Math.ceil(episodes.length / itemsPerPage); const handleChange = (event) => { setProviderId(event.target.value); }; const handlePageChange = (pageNumber) => { setCurrentPage(pageNumber); }; useEffect(() => { if ( !currentEpisodes || currentEpisodes?.every( (item) => // item?.img?.includes("null") || item?.img?.includes("https://s4.anilist.co/") || item?.image?.includes("https://s4.anilist.co/") || item?.img === null ) ) { setView(3); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [providerId, episodes]); useEffect(() => { if (episodes) { const getEpi = info?.nextAiringEpisode ? episodes.find((i) => i.number === progress + 1) : episodes[0]; if (getEpi) { const watchUrl = `/en/anime/watch/${ info.id }/${providerId}?id=${encodeURIComponent(getEpi.id)}&num=${ getEpi.number }${isDub ? `&dub=${isDub}` : ""}`; setWatch(watchUrl); } else { setWatch(null); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [episodes]); useEffect(() => { if (artStorage) { const currentData = JSON.parse(localStorage.getItem("artplayer_settings")) || {}; // Create a new object to store the updated data const updatedData = {}; // Iterate through the current data and copy items with different aniId to the updated object for (const key in currentData) { const item = currentData[key]; if (Number(item.aniId) === info.id && item.provider === providerId) { updatedData[key] = item; } } if (!session?.user?.name) { const maxWatchedEpisode = Object.keys(updatedData).reduce( (maxEpisode, key) => { const episodeData = updatedData[key]; if (episodeData.timeWatched >= episodeData.duration * 0.9) { return Math.max(maxEpisode, episodeData.episode); } return maxEpisode; }, 0 ); setProgress(maxWatchedEpisode); } else { return; } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [providerId, artStorage, info.id, session?.user?.name]); let debounceTimeout; const handleRefresh = async () => { try { setLoading(true); clearTimeout(debounceTimeout); debounceTimeout = setTimeout(async () => { const providers = await fetchEpisodes(info, isDub, true); setDefaultProvider(providers, setProviderId); setView(Number(localStorage.getItem("view")) || DEFAULT_VIEW); setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings"))); setProviders(providers); setLoading(false); }, 5000); } catch (err) { console.log(err); toast.error("Something went wrong"); } }; return ( <>
{info && (

Episodes

)} {info?.status !== "NOT_YET_RELEASED" && ( )}
setIsDub((prev) => !prev)} className="flex lg:hidden flex-col items-center relative rounded-md bg-secondary py-1.5 px-3 font-karla text-sm hover:ring-1 ring-action cursor-pointer group" > {isDub ? "Dub" : "Sub"} Switch to {isDub ? "Sub" : "Dub"}
setVisible(!visible)} >
{providers && (
setIsDub((prev) => !prev)} className="hidden lg:flex flex-col items-center relative rounded-[3px] bg-secondary py-1 px-3 font-karla text-sm hover:ring-1 ring-action cursor-pointer group" > {isDub ? "Dub" : "Sub"} Switch to {isDub ? "Sub" : "Dub"}
)} {providers && providers.length > 0 && ( <>
{totalPages > 1 && (
)}
)}
{/* Episodes */} {!loading ? (
{Array.isArray(providers) ? ( providers.length > 0 ? ( currentEpisodes.map((episode, index) => { // const mapData = mapProviders?.find( // (i) => i.number === episode.number // ); return ( {view === 1 && ( )} {view === 2 && ( )} {view === 3 && ( )} ); }) ) : (

Oops!

It looks like this anime is not available.

) ) : (

{providers?.message}

)}
) : (
)}
); }