diff options
| author | Factiven <[email protected]> | 2023-08-09 15:11:53 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-08-09 15:11:53 +0700 |
| commit | bae1f53877c3447d3ba940f823e5a8a097f5b22c (patch) | |
| tree | 98abb195bcad6f3e6c61c76c62fc238f227b0ead /pages | |
| parent | Update package-lock.json (diff) | |
| download | moopa-3.9.0.tar.xz moopa-3.9.0.zip | |
Update v3.9.0 - Merged Beta to Main (#41)v3.9.0
* initial commit
* Update_v.3.6.7-beta-v1.2
* Update_v.3.6.7-beta-v1.3
* Update_v.3.6.7-beta-v1.3
> update API
* Fixed mediaList won't update
* added .env disqus shortname
* Update_v3.6.7-beta-v1.4
>Implementing database
* Create main.yml
* Update v3.6.7-beta-v1.5
small patch
* title home page
* Update content.js
* Delete db-test.js
* Update content.js
* Update home page card
* Update v3.7.0
* Update v3.7.1-beta
> migrating backend to main code
> fixed schedule component
* Update v3.8.0
> Added dub options
> Moved schedule backend
* Update v.3.8.1
> Fixed episodes on watch page isn't dubbed
* Update v3.8.1-patch-1
* Update v3.8.1-patch-2
> Another patch for dub
* Update v3.8.2
> Removed prisma configuration for database since it's not stable yet
* Update v3.8.3
> Fixed different provider have same id
* Update v.3.8.3
> Fixed player bug where the controls won't hide after updating anilist progress
* Update v3.8.4-patch-2
* Update v3.8.5
> Update readme.md
> Update .env.example
* Update next.config.js
* small adjusment info page
* Update v3.8.6
> Minor update for Android 13 user
* Update v3.8.7
> Added prev and next button to mediaSession
* Update v3.8.7-beta-v2
* Beta v2 (#37)
* Update schema.prisma
* Update schema.prisma
* Update schema.prisma
* Update 3.9.0-beta-v2.1
> Implemented database for storing user Watch List and settings
> Added buttons to auto-play next episodes
* Update v3.9.0-beta-v2.2
* Update README.md
---------
Co-authored-by: Chitraksh Maheshwari <[email protected]>
Diffstat (limited to 'pages')
| -rw-r--r-- | pages/_document.js | 6 | ||||
| -rw-r--r-- | pages/api/anify/info/[id].js | 37 | ||||
| -rw-r--r-- | pages/api/anify/page/[...params].js | 41 | ||||
| -rw-r--r-- | pages/api/consumet/episode/[id].js | 12 | ||||
| -rw-r--r-- | pages/api/user/profile.js | 52 | ||||
| -rw-r--r-- | pages/api/user/update/episode.js | 68 | ||||
| -rw-r--r-- | pages/en/anime/[...id].js | 1 | ||||
| -rw-r--r-- | pages/en/anime/popular.js | 15 | ||||
| -rw-r--r-- | pages/en/anime/recently-watched.js | 159 | ||||
| -rw-r--r-- | pages/en/anime/trending.js | 15 | ||||
| -rw-r--r-- | pages/en/anime/watch/[...info].js | 29 | ||||
| -rw-r--r-- | pages/en/index.js | 159 | ||||
| -rw-r--r-- | pages/en/manga/[id].js | 24 | ||||
| -rw-r--r-- | pages/en/manga/read/[...params].js | 15 | ||||
| -rw-r--r-- | pages/en/search/[param].js | 7 |
15 files changed, 556 insertions, 84 deletions
diff --git a/pages/_document.js b/pages/_document.js index effc121..d79f31f 100644 --- a/pages/_document.js +++ b/pages/_document.js @@ -1,11 +1,15 @@ import { Html, Head, Main, NextScript } from "next/document"; +// import { randomBytes } from "crypto"; export default function Document() { + // const nonce = randomBytes(128).toString("base64"); + // const csp = `object-src 'none'; base-uri 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https: http: 'nonce-${nonce}' 'strict-dynamic'`; + return ( <Html lang="en"> <Head> <link rel="manifest" href="/manifest.json" /> - <link rel="apple-touch-icon" href="/icon.png"></link> + <link rel="apple-touch-icon" href="/icon.png" /> <meta name="theme-color" content="#141519" /> <link rel="stylesheet" diff --git a/pages/api/anify/info/[id].js b/pages/api/anify/info/[id].js new file mode 100644 index 0000000..c33d158 --- /dev/null +++ b/pages/api/anify/info/[id].js @@ -0,0 +1,37 @@ +import axios from "axios"; +import cacheData from "memory-cache"; + +const API_KEY = process.env.API_KEY; + +// Function to fetch new data +export async function fetchInfo(id) { + try { + const { data } = await axios.get( + `https://api.anify.tv/info/${id}?apikey=${API_KEY}` + ); + return data; + } catch (error) { + console.error("Error fetching data:", error); + return null; + } +} + +export default async function handler(req, res) { + try { + const id = req.query.id; + const cached = cacheData.get(id); + if (cached) { + return res.status(200).json(cached); + } else { + const data = await fetchInfo(id); + if (data) { + res.status(200).json(data); + cacheData.put(id, data, 1000 * 60 * 10); + } else { + res.status(404).json({ message: "Schedule not found" }); + } + } + } catch (error) { + res.status(500).json({ error }); + } +} diff --git a/pages/api/anify/page/[...params].js b/pages/api/anify/page/[...params].js new file mode 100644 index 0000000..80dda6c --- /dev/null +++ b/pages/api/anify/page/[...params].js @@ -0,0 +1,41 @@ +import axios from "axios"; +import cacheData from "memory-cache"; + +const API_KEY = process.env.API_KEY; + +// Function to fetch new data +async function fetchData(id, providerId, chapterId) { + try { + const res = await fetch( + `https://api.anify.tv/pages?id=${id}&providerId=${providerId}&readId=${chapterId}&apikey=${API_KEY}` + ); + const data = await res.json(); + return data; + // return { id, providerId, chapterId }; + } catch (error) { + console.error("Error fetching data:", error); + return null; + } +} + +export default async function handler(req, res) { + try { + const id = req.query.params; + const chapter = req.query.chapter; + // res.status(200).json({ id, chapter }); + const cached = cacheData.get(chapter); + if (cached) { + return res.status(200).json(cached); + } else { + const data = await fetchData(id[0], id[1], chapter); + if (data) { + res.status(200).json(data); + cacheData.put(id[2], data, 1000 * 60 * 10); + } else { + res.status(404).json({ message: "Manga/Novel not found :(" }); + } + } + } catch (error) { + res.status(500).json({ error }); + } +} diff --git a/pages/api/consumet/episode/[id].js b/pages/api/consumet/episode/[id].js index 737292f..e6f40ce 100644 --- a/pages/api/consumet/episode/[id].js +++ b/pages/api/consumet/episode/[id].js @@ -7,12 +7,18 @@ export default async function handler(req, res) { try { const id = req.query.id; const dub = req.query.dub || false; + const refresh = req.query.refresh || false; const providers = ["enime", "gogoanime"]; const datas = []; const cached = cacheData.get(id + dub); - if (cached) { + + if (refresh) { + cacheData.del(id + dub); + } + + if (!refresh && cached) { return res.status(200).json(cached); } else { async function fetchData(provider) { @@ -31,7 +37,7 @@ export default async function handler(req, res) { } return res.json(); }); - if (data.episodes?.length > 0) { + if (data.episodes.length > 0) { datas.push({ providerId: provider, episodes: dub ? data.episodes : data.episodes.reverse(), @@ -53,7 +59,7 @@ export default async function handler(req, res) { if (datas.length === 0) { return res.status(404).json({ message: "Anime not found" }); } else { - cacheData.put(id + dub, { data: datas }, 1000 * 60 * 60 * 15); // 15 minutes + cacheData.put(id + dub, { data: datas }, 1000 * 60 * 60 * 10); res.status(200).json({ data: datas }); } } diff --git a/pages/api/user/profile.js b/pages/api/user/profile.js new file mode 100644 index 0000000..2b44ae2 --- /dev/null +++ b/pages/api/user/profile.js @@ -0,0 +1,52 @@ +import { + createUser, + deleteUser, + getUser, + updateUser, +} from "../../../prisma/user"; + +export default async function handler(req, res) { + try { + switch (req.method) { + case "POST": { + const { name, setting } = req.body; + const new_user = await createUser(name, setting); + if (!new_user) { + return res.status(200).json({ message: "User is already created" }); + } else { + return res.status(201).json(new_user); + } + } + case "PUT": { + const { name, anime } = req.body; + const user = await updateUser(name, anime); + if (!user) { + return res.status(200).json({ message: "Title is already there" }); + } else { + return res.status(200).json(user); + } + } + case "GET": { + const { name } = req.query; + const user = await getUser(name); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } else { + return res.status(200).json(user); + } + } + case "DELETE": { + const { name } = req.body; + const user = await deleteUser(name); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } else { + return res.status(200).json(user); + } + } + } + } catch (error) { + console.log(error); + return res.status(500).json({ message: "Internal server error" }); + } +} diff --git a/pages/api/user/update/episode.js b/pages/api/user/update/episode.js new file mode 100644 index 0000000..f69bb78 --- /dev/null +++ b/pages/api/user/update/episode.js @@ -0,0 +1,68 @@ +import { + createList, + getEpisode, + updateUserEpisode, +} from "../../../../prisma/user"; + +export default async function handler(req, res) { + try { + switch (req.method) { + case "POST": { + const { name, id } = JSON.parse(req.body); + + const episode = await createList(name, id); + if (!episode) { + return res + .status(200) + .json({ message: "Episode is already created" }); + } else { + return res.status(201).json(episode); + } + } + case "PUT": { + const { + name, + id, + watchId, + title, + image, + number, + duration, + timeWatched, + aniTitle, + provider, + } = JSON.parse(req.body); + const episode = await updateUserEpisode({ + name, + id, + watchId, + title, + image, + number, + duration, + timeWatched, + aniTitle, + provider, + }); + if (!episode) { + return res.status(200).json({ message: "Episode is already there" }); + } else { + return res.status(200).json(episode); + } + } + case "GET": { + const { name, id } = req.query; + // console.log(req.query); + const episode = await getEpisode(name, id); + if (!episode) { + return res.status(404).json({ message: "Episode not found" }); + } else { + return res.status(200).json(episode); + } + } + } + } catch (error) { + console.log(error); + return res.status(500).json({ message: "Internal server error" }); + } +} diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].js index 6c78955..5e4aed8 100644 --- a/pages/en/anime/[...id].js +++ b/pages/en/anime/[...id].js @@ -154,6 +154,7 @@ export default function Info({ info, color }) { prg={progress} max={info?.episodes} image={info} + close={handleClose} /> )} </div> diff --git a/pages/en/anime/popular.js b/pages/en/anime/popular.js index b8b19ba..8cbbeab 100644 --- a/pages/en/anime/popular.js +++ b/pages/en/anime/popular.js @@ -3,7 +3,6 @@ import Image from "next/image"; import Link from "next/link"; import { useEffect, useState } from "react"; import Skeleton from "react-loading-skeleton"; -import Navbar from "../../../components/navbar"; import Footer from "../../../components/footer"; import { getServerSession } from "next-auth"; import { authOptions } from "../../api/auth/[...nextauth]"; @@ -98,7 +97,7 @@ export default function PopularAnime({ sessions }) { <> <MobileNav sessions={sessions} /> <div className="flex flex-col gap-2 items-center min-h-screen w-screen px-2 relative pb-10"> - <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed left-3"> + <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed px-3"> <Link href="/en" className="flex gap-2 items-center font-karla"> <ChevronLeftIcon className="w-5 h-5" /> <h1 className="text-xl">Popular Anime</h1> @@ -110,7 +109,11 @@ export default function PopularAnime({ sessions }) { key={index} className="flex flex-col items-center w-[150px] lg:w-[180px]" > - <Link href={`/en/anime/${i.id}`} className="p-2"> + <Link + href={`/en/anime/${i.id}`} + className="p-2" + title={i.title.romaji} + > <Image src={i.coverImage.large} alt={i.title.romaji} @@ -119,7 +122,11 @@ export default function PopularAnime({ sessions }) { className="w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] object-cover rounded hover:scale-105 scale-100 transition-all duration-200 ease-out" /> </Link> - <Link href={`/en/anime/${i.id}`} className="w-full px-2"> + <Link + href={`/en/anime/${i.id}`} + className="w-full px-2" + title={i.title.romaji} + > <h1 className="font-karla font-bold xl:text-base text-[15px] line-clamp-2"> {i.status === "RELEASING" ? ( <span className="dots bg-green-500" /> diff --git a/pages/en/anime/recently-watched.js b/pages/en/anime/recently-watched.js new file mode 100644 index 0000000..0a7fbae --- /dev/null +++ b/pages/en/anime/recently-watched.js @@ -0,0 +1,159 @@ +import { ChevronLeftIcon, PlayIcon } from "@heroicons/react/24/solid"; +import Image from "next/image"; +import Link from "next/link"; +import { useEffect, useState } from "react"; +import Skeleton from "react-loading-skeleton"; +import Footer from "../../../components/footer"; +import { getServerSession } from "next-auth"; +import { authOptions } from "../../api/auth/[...nextauth]"; +import MobileNav from "../../../components/home/mobileNav"; + +export default function PopularAnime({ sessions }) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(true); + const fetchData = async () => { + let data; + if (sessions?.user?.name) { + data = await fetch( + `/api/user/profile?name=${sessions?.user?.name}` + ).then((res) => { + if (!res.ok) { + switch (res.status) { + case 404: { + return console.log("user not found"); + } + case 500: { + return console.log("server error"); + } + } + } + return res.json(); + }); + } + if (!data) { + const dat = JSON.parse(localStorage.getItem("artplayer_settings")); + if (dat) { + const arr = Object.keys(dat).map((key) => dat[key]); + setData(arr); + setLoading(false); + } + } else { + setData(data?.WatchListEpisode); + setLoading(false); + } + }; + fetchData(); + }, []); + + return ( + <> + <MobileNav sessions={sessions} /> + <div className="flex flex-col gap-2 items-center min-h-screen w-screen px-2 relative pb-10"> + <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed left-0 px-3"> + <Link href="/en" className="flex gap-2 items-center font-karla"> + <ChevronLeftIcon className="w-5 h-5" /> + <h1 className="text-xl">Recently Watched</h1> + </Link> + </div> + <div className="grid grid-cols-1 xs:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 gap-3 md:gap-7 pt-16"> + {data + ?.filter((i) => i.title !== null) + .map((i) => { + const time = i.timeWatched; + const duration = i.duration; + let prog = (time / duration) * 100; + if (prog > 90) prog = 100; + + return ( + <Link + key={i.watchId} + className="flex flex-col gap-2 shrink-0 cursor-pointer" + href={`/en/anime/watch/${i.aniId}/${ + i.provider + }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}`} + > + <div className="relative md:w-[320px] aspect-video rounded-md overflow-hidden group"> + <div className="w-full h-full bg-gradient-to-t from-black/70 from-20% to-transparent group-hover:to-black/40 transition-all duration-300 ease-out absolute z-30" /> + <div className="absolute bottom-3 left-0 mx-2 text-white flex gap-2 items-center w-[80%] z-30"> + <PlayIcon className="w-5 h-5 shrink-0" /> + <h1 + className="font-semibold text-sm md:text-base font-karla line-clamp-1" + title={i?.title || i.anititle} + > + {i?.title || i.anititle} + </h1> + </div> + <span + className={`absolute bottom-0 left-0 h-[2px] bg-red-600 z-30`} + style={{ + width: `${prog}%`, + }} + /> + {i?.image && ( + <Image + src={i?.image} + width={200} + height={200} + alt="Episode Thumbnail" + className="w-fit group-hover:scale-[1.02] duration-300 ease-out z-10" + /> + )} + </div> + <div className="flex flex-col font-karla w-full"> + {/* <h1 className="font-semibold">{i.title}</h1> */} + <p className="flex items-center gap-1 text-sm text-gray-400 md:w-[320px]"> + <span + className="text-white max-w-[150px] md:max-w-[220px]" + style={{ + display: "inline-block", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + }} + title={i.aniTitle} + > + {i.aniTitle} + </span>{" "} + | Episode {i.episode} + </p> + </div> + </Link> + ); + })} + + {loading && ( + <> + {[1, 2, 4, 5, 6, 7, 8].map((item) => ( + <div + key={item} + className="flex flex-col gap-2 items-center md:w-[320px] rounded-md overflow-hidden" + > + <div className="w-full"> + <Skeleton className="w-fit aspect-video rounded" /> + </div> + <div className="w-full"> + <Skeleton width={80} height={20} /> + </div> + </div> + ))} + </> + )} + </div> + </div> + <Footer /> + </> + ); +} + +export async function getServerSideProps(context) { + const session = await getServerSession(context.req, context.res, authOptions); + + return { + props: { + sessions: session, + }, + }; +} diff --git a/pages/en/anime/trending.js b/pages/en/anime/trending.js index cbc30ab..9f8a187 100644 --- a/pages/en/anime/trending.js +++ b/pages/en/anime/trending.js @@ -3,7 +3,6 @@ import Image from "next/image"; import Link from "next/link"; import { useEffect, useState } from "react"; import Skeleton from "react-loading-skeleton"; -import Navbar from "../../../components/navbar"; import Footer from "../../../components/footer"; import { getServerSession } from "next-auth"; import { authOptions } from "../../api/auth/[...nextauth]"; @@ -98,7 +97,7 @@ export default function TrendingAnime({ sessions }) { <> <MobileNav sessions={sessions} /> <div className="flex flex-col gap-2 items-center min-h-screen w-screen px-2 relative pb-10"> - <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed left-3"> + <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed px-3"> <Link href="/en" className="flex gap-2 items-center font-karla"> <ChevronLeftIcon className="w-5 h-5" /> <h1 className="text-xl">Trending Now</h1> @@ -110,7 +109,11 @@ export default function TrendingAnime({ sessions }) { key={index} className="flex flex-col items-center w-[150px] lg:w-[180px]" > - <Link href={`/en/anime/${i.id}`} className="p-2"> + <Link + href={`/en/anime/${i.id}`} + className="p-2" + title={i.title.romaji} + > <Image src={i.coverImage.large} alt={i.title.romaji} @@ -119,7 +122,11 @@ export default function TrendingAnime({ sessions }) { className="w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] object-cover rounded hover:scale-105 scale-100 transition-all duration-200 ease-out" /> </Link> - <Link href={`/en/anime/${i.id}`} className="w-full px-2"> + <Link + href={`/en/anime/${i.id}`} + className="w-full px-2" + title={i.title.romaji} + > <h1 className="font-karla font-bold xl:text-base text-[15px] line-clamp-2"> {i.status === "RELEASING" ? ( <span className="dots bg-green-500" /> diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js index 67e38c2..bc8851b 100644 --- a/pages/en/anime/watch/[...info].js +++ b/pages/en/anime/watch/[...info].js @@ -9,6 +9,8 @@ import Navigasi from "../../../../components/home/staticNav"; import PrimarySide from "../../../../components/anime/watch/primarySide"; import SecondarySide from "../../../../components/anime/watch/secondarySide"; import { GET_MEDIA_USER } from "../../../../queries"; +import { createList, createUser, getEpisode } from "../../../../prisma/user"; +// import { updateUser } from "../../../../prisma/user"; export default function Info({ sessions, @@ -17,6 +19,7 @@ export default function Info({ provider, epiNumber, dub, + userData, proxy, disqus, }) { @@ -124,7 +127,7 @@ export default function Info({ } } } - + setInfo(data.data.Media); const response = await fetch( @@ -156,12 +159,16 @@ export default function Info({ setLoading(false); } } - + setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings"))); // setEpiData(episodes); setLoading(false); } getInfo(); + + return () => { + setCurrentEpisode(null); + }; }, [sessions?.user?.name, epiNumber, dub]); // console.log(proxy); @@ -190,6 +197,7 @@ export default function Info({ setOnList={setOnList} setLoading={setLoading} loading={loading} + timeWatched={userData?.timeWatched} /> <SecondarySide info={info} @@ -227,6 +235,22 @@ export async function getServerSideProps(context) { const epiNumber = query.num; const dub = query.dub; + let userData = null; + + if (session) { + await createUser(session.user.name); + await createList(session.user.name, watchId); + const data = await getEpisode(session.user.name, watchId); + userData = JSON.parse( + JSON.stringify(data, (key, value) => { + if (key === "createdDate") { + return String(value); + } + return value; + }) + ); + } + return { props: { sessions: session, @@ -235,6 +259,7 @@ export async function getServerSideProps(context) { watchId: watchId || null, epiNumber: epiNumber || null, dub: dub || null, + userData: userData?.[0] || null, proxy, disqus, }, diff --git a/pages/en/index.js b/pages/en/index.js index 35de96d..c3a98fc 100644 --- a/pages/en/index.js +++ b/pages/en/index.js @@ -21,17 +21,40 @@ import { useCountdown } from "../../utils/useCountdownSeconds"; import Navigasi from "../../components/home/staticNav"; import MobileNav from "../../components/home/mobileNav"; import axios from "axios"; +import { createUser } from "../../prisma/user"; -// Filter schedules for each day -// const filterByCountryOfOrigin = (schedule, country) => { -// const filteredSchedule = {}; -// for (const day in schedule) { -// filteredSchedule[day] = schedule[day].filter( -// (anime) => anime.countryOfOrigin === country -// ); -// } -// return filteredSchedule; -// }; +import { checkAdBlock } from "adblock-checker"; +import { ToastContainer, toast } from "react-toastify"; + +export async function getServerSideProps(context) { + const session = await getServerSession(context.req, context.res, authOptions); + + if (session) { + await createUser(session.user.name); + } + + const trendingDetail = await aniListData({ + sort: "TRENDING_DESC", + page: 1, + }); + const popularDetail = await aniListData({ + sort: "POPULARITY_DESC", + page: 1, + }); + const genreDetail = await aniListData({ sort: "TYPE", page: 1 }); + + const upComing = await getUpcomingAnime(); + + return { + props: { + genre: genreDetail.props, + detail: trendingDetail.props, + populars: popularDetail.props, + sessions: session, + upComing, + }, + }; +} export default function Home({ detail, populars, sessions, upComing }) { const { media: current } = useAniList(sessions, { stats: "CURRENT" }); @@ -42,6 +65,27 @@ export default function Home({ detail, populars, sessions, upComing }) { const [anime, setAnime] = useState([]); + useEffect(() => { + async function adBlock() { + const ad = await checkAdBlock(); + if (ad) { + toast.dark( + "Please disable your adblock for better experience, we don't have any ads on our site.", + { + position: "top-center", + autoClose: false, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + theme: "dark", + } + ); + } + } + adBlock(); + }, []); + const update = () => { setAnime((prevAnime) => prevAnime.slice(1)); }; @@ -89,6 +133,9 @@ export default function Home({ detail, populars, sessions, upComing }) { const [list, setList] = useState(null); const [planned, setPlanned] = useState(null); const [greeting, setGreeting] = useState(""); + const [user, setUser] = useState(null); + + // console.log({ user }); const [prog, setProg] = useState(null); @@ -96,6 +143,43 @@ export default function Home({ detail, populars, sessions, upComing }) { const data = detail.data[0]; useEffect(() => { + async function userData() { + let data; + if (sessions?.user?.name) { + data = await fetch( + `/api/user/profile?name=${sessions?.user?.name}` + ).then((res) => { + if (!res.ok) { + switch (res.status) { + case 404: { + return console.log("user not found"); + } + case 500: { + return console.log("server error"); + } + } + } + return res.json(); + }); + } + if (!data) { + const dat = JSON.parse(localStorage.getItem("artplayer_settings")); + if (dat) { + const arr = Object.keys(dat).map((key) => dat[key]); + const newFirst = arr?.sort((a, b) => { + return new Date(b?.createdAt) - new Date(a?.createdAt); + }); + setUser(newFirst); + } + } else { + setUser(data?.WatchListEpisode); + } + // const data = await res.json(); + } + userData(); + }, [sessions?.user?.name]); + + useEffect(() => { const time = new Date().getHours(); let greeting = ""; @@ -112,7 +196,8 @@ export default function Home({ detail, populars, sessions, upComing }) { setGreeting(greeting); async function userData() { - if (!sessions) return; + if (!sessions?.user?.name) return; + const getMedia = current.filter((item) => item.status === "CURRENT")[0] || null; const list = getMedia?.entries @@ -131,7 +216,8 @@ export default function Home({ detail, populars, sessions, upComing }) { } } userData(); - }, [sessions, current, plan]); + }, [sessions?.user?.name, current, plan]); + return ( <> <Head> @@ -158,6 +244,13 @@ export default function Home({ detail, populars, sessions, upComing }) { <div className="h-auto w-screen bg-[#141519] text-[#dbdcdd] "> <Navigasi /> <SearchBar /> + <ToastContainer + pauseOnFocusLoss={false} + style={{ + width: "400px", + }} + /> + {/* PC / TABLET */} <div className=" hidden justify-center lg:flex my-16"> <div className="relative grid grid-rows-2 items-center lg:flex lg:h-[467px] lg:w-[80%] lg:justify-between"> @@ -228,6 +321,22 @@ export default function Home({ detail, populars, sessions, upComing }) { animate={{ opacity: 1 }} transition={{ duration: 0.5, staggerChildren: 0.2 }} // Add staggerChildren prop > + {user?.length > 0 && ( + <motion.div // Add motion.div to each child component + key="recentlyWatched" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="recentlyWatched" + section="Recently Watched" + userData={user} + /> + </motion.div> + )} + {sessions && releaseData?.length > 0 && ( <motion.div // Add motion.div to each child component key="onGoing" @@ -354,29 +463,3 @@ export default function Home({ detail, populars, sessions, upComing }) { </> ); } - -export async function getServerSideProps(context) { - const session = await getServerSession(context.req, context.res, authOptions); - - const trendingDetail = await aniListData({ - sort: "TRENDING_DESC", - page: 1, - }); - const popularDetail = await aniListData({ - sort: "POPULARITY_DESC", - page: 1, - }); - const genreDetail = await aniListData({ sort: "TYPE", page: 1 }); - - const upComing = await getUpcomingAnime(); - - return { - props: { - genre: genreDetail.props, - detail: trendingDetail.props, - populars: popularDetail.props, - sessions: session, - upComing, - }, - }; -} diff --git a/pages/en/manga/[id].js b/pages/en/manga/[id].js index eb53a93..bb3cbc2 100644 --- a/pages/en/manga/[id].js +++ b/pages/en/manga/[id].js @@ -9,12 +9,12 @@ import { useEffect, useState } from "react"; import { setCookie } from "nookies"; import { getServerSession } from "next-auth"; import { authOptions } from "../../api/auth/[...nextauth]"; +import getAnifyInfo from "../../../lib/anify/info"; export default function Manga({ info, userManga, chapters }) { const [domainUrl, setDomainUrl] = useState(""); const [firstEp, setFirstEp] = useState(); - const chaptersData = - info.chapters.data.length === 0 ? chapters : info.chapters.data; + const chaptersData = info.chapters.data; useEffect(() => { setDomainUrl(window.location.origin); @@ -84,8 +84,7 @@ export async function getServerSideProps(context) { const { id } = context.query; const key = process.env.API_KEY; - const res = await fetch(`https://api.anify.tv/info/${id}?apikey=${key}`); - const data = await res.json(); + const data = await getAnifyInfo(id, key); let userManga = null; @@ -152,27 +151,10 @@ export async function getServerSideProps(context) { }; } - let chapter = null; - - if (data?.chapters?.data.length === 0) { - const res2 = await fetch( - `https://api.anify.tv/chapters/${id}?apikey=${key}` - ); - const data2 = await res2.json(); - if (data2.error) { - return { - notFound: true - } - } else { - chapter = data2; - } - } - return { props: { info: data, userManga, - chapters: chapter || null, }, }; } diff --git a/pages/en/manga/read/[...params].js b/pages/en/manga/read/[...params].js index dbabb3d..e608d16 100644 --- a/pages/en/manga/read/[...params].js +++ b/pages/en/manga/read/[...params].js @@ -15,6 +15,7 @@ import Head from "next/head"; import nookies from "nookies"; import ShortCutModal from "../../../../components/manga/modals/shortcutModal"; import ChapterModal from "../../../../components/manga/modals/chapterModal"; +import getAnifyPage from "../../../../lib/anify/page"; export default function Read({ data, currentId, sessions }) { const [info, setInfo] = useState(); @@ -228,6 +229,8 @@ export async function getServerSideProps(context) { const cookies = nookies.get(context); + const key = process.env.API_KEY; + const query = context.query; const providerId = query.params[0]; const chapterId = query.chapterId; @@ -243,18 +246,12 @@ export async function getServerSideProps(context) { const session = await getServerSession(context.req, context.res, authOptions); - const key = process.env.API_KEY; - const res = await fetch( - `https://api.anify.tv/pages?providerId=${providerId}&readId=${encodeURIComponent( - chapterId - )}&apikey=${key}` - ); + const data = await getAnifyPage(mediaId, providerId, chapterId, key); - const data = await res.json(); if (data.error) { return { - notFound: true - } + notFound: true, + }; } return { diff --git a/pages/en/search/[param].js b/pages/en/search/[param].js index cacc2b8..abd4f04 100644 --- a/pages/en/search/[param].js +++ b/pages/en/search/[param].js @@ -429,7 +429,7 @@ export default function Card() { ? `/en/manga/${anime.id}` : `/en/anime/${anime.id}` } - className="" + title={anime.title.userPreferred} > <Image className="object-cover bg-[#3B3C41] w-[146px] h-[208px] xxs:w-[115px] xxs:h-[163px] xs:w-[135px] xs:h-[192px] xl:w-[185px] xl:h-[265px] hover:scale-105 scale-100 transition-all cursor-pointer duration-200 ease-out rounded-[10px]" @@ -439,7 +439,10 @@ export default function Card() { height={500} /> </Link> - <Link href={`/en/anime/${anime.id}`}> + <Link + href={`/en/anime/${anime.id}`} + title={anime.title.userPreferred} + > <h1 className="font-outfit font-bold xl:text-base text-[15px] pt-4 line-clamp-2"> {anime.status === "RELEASING" ? ( <span className="dots bg-green-500" /> |