diff options
| author | Factiven <[email protected]> | 2023-08-12 22:54:26 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-08-12 22:54:26 +0700 |
| commit | 3e78826658c7d2a4e9b3c1d73e63dacc1d39c361 (patch) | |
| tree | d580d03670692c6c5d361ec8559e7a2352354f3a /pages | |
| parent | Update v3.9.1 - Merged Beta to Main (#44) (diff) | |
| download | moopa-3e78826658c7d2a4e9b3c1d73e63dacc1d39c361.tar.xz moopa-3e78826658c7d2a4e9b3c1d73e63dacc1d39c361.zip | |
Update v3.9.3 - Merged Beta to Main (#51)v3.9.3
* commit
* update db
* Update v3.9.1-beta-v3.1
* Update v3.9.1
* Fix watched progress not showing
* Secure headers
* Fix recently watched image
* Update v3.9.2
> Added custom lists for AniList
> Fixed episode listMode progress
* Update db route
* Fixed AniList
* Fix next button on dub anime
> video is playing sub anime instead dub
* small adjusment for premid
* fix eslint
* small updates
> added ability to remove episode from recently watched
* Update v3.9.3
Diffstat (limited to 'pages')
| -rw-r--r-- | pages/api/consumet/episode/[id].js | 5 | ||||
| -rw-r--r-- | pages/api/user/profile.js | 16 | ||||
| -rw-r--r-- | pages/api/user/update/episode.js | 30 | ||||
| -rw-r--r-- | pages/en/anime/[...id].js | 6 | ||||
| -rw-r--r-- | pages/en/anime/recently-watched.js | 98 | ||||
| -rw-r--r-- | pages/en/anime/watch/[...info].js | 6 | ||||
| -rw-r--r-- | pages/en/dmca.js | 5 | ||||
| -rw-r--r-- | pages/en/index.js | 36 | ||||
| -rw-r--r-- | pages/en/manga/read/[...params].js | 1 | ||||
| -rw-r--r-- | pages/index.js | 21 |
10 files changed, 177 insertions, 47 deletions
diff --git a/pages/api/consumet/episode/[id].js b/pages/api/consumet/episode/[id].js index e6f40ce..6e7f318 100644 --- a/pages/api/consumet/episode/[id].js +++ b/pages/api/consumet/episode/[id].js @@ -1,4 +1,3 @@ -import axios from "axios"; import cacheData from "memory-cache"; const API_URL = process.env.API_URI; @@ -9,7 +8,7 @@ export default async function handler(req, res) { const dub = req.query.dub || false; const refresh = req.query.refresh || false; - const providers = ["enime", "gogoanime"]; + const providers = ["enime", "gogoanime", "zoro"]; const datas = []; const cached = cacheData.get(id + dub); @@ -59,7 +58,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 * 10); + 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 index dd22bd8..e20aaca 100644 --- a/pages/api/user/profile.js +++ b/pages/api/user/profile.js @@ -43,13 +43,21 @@ export default async function handler(req, res) { } case "DELETE": { const { name } = req.body; - const user = await deleteUser(name); - if (!user) { - return res.status(404).json({ message: "User not found" }); + // return res.status(200).json({ name }); + if (session.user.name !== name) { + return res.status(401).json({ message: "Unauthorized" }); } else { - return res.status(200).json(user); + const user = await deleteUser(name); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } else { + return res.status(200).json(user); + } } } + default: { + return res.status(405).json({ message: "Method not allowed" }); + } } } catch (error) { console.log(error); diff --git a/pages/api/user/update/episode.js b/pages/api/user/update/episode.js index 7974446..52c9494 100644 --- a/pages/api/user/update/episode.js +++ b/pages/api/user/update/episode.js @@ -3,6 +3,7 @@ import { authOptions } from "../../auth/[...nextauth]"; import { createList, + deleteEpisode, getEpisode, updateUserEpisode, } from "../../../../prisma/user"; @@ -16,13 +17,17 @@ export default async function handler(req, res) { 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" }); + if (session.user.name !== name) { + return res.status(401).json({ message: "Unauthorized" }); } else { - return res.status(201).json(episode); + 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": { @@ -68,6 +73,19 @@ export default async function handler(req, res) { return res.status(200).json(episode); } } + case "DELETE": { + const { name, id } = req.body; + if (session.user.name !== name) { + return res.status(401).json({ message: "Unauthorized" }); + } else { + const episode = await deleteEpisode(name, id); + if (!episode) { + return res.status(404).json({ message: "Episode not found" }); + } else { + return res.status(200).json({ message: "Episode deleted" }); + } + } + } } } catch (error) { console.log(error); diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].js index 5e4aed8..534aa17 100644 --- a/pages/en/anime/[...id].js +++ b/pages/en/anime/[...id].js @@ -125,14 +125,14 @@ export default function Info({ info, color }) { }&image=${info.bannerImage || info.coverImage.extraLarge}`} /> </Head> - <ToastContainer pauseOnFocusLoss={false} /> + <ToastContainer pauseOnHover={false} /> <Modal open={open} onClose={() => handleClose()}> <div> {!session && ( <div className="flex-center flex-col gap-5 px-10 py-5 bg-secondary rounded-md"> - <h1 className="text-md font-extrabold font-karla"> + <div className="text-md font-extrabold font-karla"> Edit your list - </h1> + </div> <button className="flex items-center bg-[#363642] rounded-md text-white p-1" onClick={() => signIn("AniListProvider")} diff --git a/pages/en/anime/recently-watched.js b/pages/en/anime/recently-watched.js index 0a7fbae..1cc713a 100644 --- a/pages/en/anime/recently-watched.js +++ b/pages/en/anime/recently-watched.js @@ -7,10 +7,13 @@ import Footer from "../../../components/footer"; import { getServerSession } from "next-auth"; import { authOptions } from "../../api/auth/[...nextauth]"; import MobileNav from "../../../components/home/mobileNav"; +import { ToastContainer, toast } from "react-toastify"; +import { XMarkIcon } from "@heroicons/react/24/outline"; export default function PopularAnime({ sessions }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); + const [remove, setRemoved] = useState(); useEffect(() => { setLoading(true); @@ -46,11 +49,66 @@ export default function PopularAnime({ sessions }) { } }; fetchData(); - }, []); + }, [remove]); + + const removeItem = async (id) => { + if (sessions?.user?.name) { + // remove from database + const res = await fetch(`/api/user/update/episode`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + name: sessions?.user?.name, + id: id, + }), + }); + const data = await res.json(); + + // remove from local storage + const artplayerSettings = + JSON.parse(localStorage.getItem("artplayer_settings")) || {}; + if (artplayerSettings[id]) { + delete artplayerSettings[id]; + localStorage.setItem( + "artplayer_settings", + JSON.stringify(artplayerSettings) + ); + } + + // update client + setRemoved(id); + + if (data?.message === "Episode deleted") { + toast.success("Episode removed from history", { + position: "bottom-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + draggable: true, + theme: "dark", + }); + } + } else { + const artplayerSettings = + JSON.parse(localStorage.getItem("artplayer_settings")) || {}; + if (artplayerSettings[id]) { + delete artplayerSettings[id]; + localStorage.setItem( + "artplayer_settings", + JSON.stringify(artplayerSettings) + ); + } + + setRemoved(id); + } + }; return ( <> <MobileNav sessions={sessions} /> + <ToastContainer pauseOnHover={false} /> <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"> @@ -68,14 +126,27 @@ export default function PopularAnime({ sessions }) { if (prog > 90) prog = 100; return ( - <Link + <div 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}`} + className="flex flex-col gap-2 shrink-0 cursor-pointer relative group/item" > - <div className="relative md:w-[320px] aspect-video rounded-md overflow-hidden group"> + <div className="absolute z-40 top-1 right-1 group-hover/item:visible invisible hover:text-action"> + <div + className="flex flex-col items-center group/delete" + onClick={() => removeItem(i.watchId)} + > + <XMarkIcon className="w-6 h-6 shrink-0 bg-primary p-1 rounded-full" /> + <span className="absolute font-karla bg-secondary shadow-black shadow-2xl py-1 px-2 whitespace-nowrap text-white text-sm rounded-md right-7 -bottom-[2px] z-40 duration-300 transition-all ease-out group-hover/delete:visible group-hover/delete:scale-100 group-hover/delete:translate-x-0 group-hover/delete:opacity-100 opacity-0 translate-x-10 scale-50 invisible"> + Remove from history + </span> + </div> + </div> + <Link + className="relative md:w-[320px] aspect-video rounded-md overflow-hidden group" + href={`/en/anime/watch/${i.aniId}/${ + i.provider + }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}`} + > <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" /> @@ -101,8 +172,13 @@ export default function PopularAnime({ sessions }) { className="w-fit group-hover:scale-[1.02] duration-300 ease-out z-10" /> )} - </div> - <div className="flex flex-col font-karla w-full"> + </Link> + <Link + className="flex flex-col font-karla w-full" + href={`/en/anime/watch/${i.aniId}/${ + i.provider + }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}`} + > {/* <h1 className="font-semibold">{i.title}</h1> */} <p className="flex items-center gap-1 text-sm text-gray-400 md:w-[320px]"> <span @@ -119,8 +195,8 @@ export default function PopularAnime({ sessions }) { </span>{" "} | Episode {i.episode} </p> - </div> - </Link> + </Link> + </div> ); })} diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js index e013c6b..c17d9c5 100644 --- a/pages/en/anime/watch/[...info].js +++ b/pages/en/anime/watch/[...info].js @@ -172,8 +172,6 @@ export default function Info({ }; }, [sessions?.user?.name, epiNumber, dub]); - // console.log(proxy); - return ( <> <Head> @@ -199,6 +197,7 @@ export default function Info({ setLoading={setLoading} loading={loading} timeWatched={userData?.timeWatched} + dub={dub} /> <SecondarySide info={info} @@ -230,8 +229,7 @@ export async function getServerSideProps(context) { const proxy = process.env.PROXY_URI; const disqus = process.env.DISQUS_SHORTNAME; - const aniId = query.info[0]; - const provider = query.info[1]; + const [aniId, provider] = query.info; const watchId = query.id; const epiNumber = query.num; const dub = query.dub; diff --git a/pages/en/dmca.js b/pages/en/dmca.js index 8dad7d7..fd93811 100644 --- a/pages/en/dmca.js +++ b/pages/en/dmca.js @@ -14,10 +14,7 @@ export default function DMCA() { property rights of others and complying with the Digital Millennium Copyright Act (DMCA)." /> - <meta - property="og:image" - content="https://cdn.discordapp.com/attachments/1068758633464201268/1081591948705546330/logo.png" - /> + <meta property="og:image" content="/icon-512x512.png" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/c.svg" /> </Head> diff --git a/pages/en/index.js b/pages/en/index.js index 159d257..73b4e94 100644 --- a/pages/en/index.js +++ b/pages/en/index.js @@ -9,7 +9,6 @@ import Content from "../../components/home/content"; import { motion } from "framer-motion"; import { signOut } from "next-auth/react"; -import { useAniList } from "../../lib/anilist/useAnilist"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../api/auth/[...nextauth]"; import SearchBar from "../../components/searchBar"; @@ -25,6 +24,7 @@ import { createUser } from "../../prisma/user"; import { checkAdBlock } from "adblock-checker"; import { ToastContainer, toast } from "react-toastify"; +import { useAniList } from "../../lib/anilist/useAnilist"; export async function getServerSideProps(context) { const session = await getServerSession(context.req, context.res, authOptions); @@ -35,7 +35,6 @@ export async function getServerSideProps(context) { } } catch (error) { console.error(error); - // Handle the error here } const trendingDetail = await aniListData({ @@ -108,8 +107,14 @@ export default function Home({ detail, populars, sessions, upComing }) { useEffect(() => { const getSchedule = async () => { - const { data } = await axios.get(`/api/anify/schedule`); - setSchedules(data); + const res = await fetch(`/api/anify/schedule`); + const data = await res.json(); + + if (!res.ok) { + setSchedules(null); + } else { + setSchedules(data); + } }; getSchedule(); }, []); @@ -120,12 +125,16 @@ export default function Home({ detail, populars, sessions, upComing }) { function getRelease() { let releasingAnime = []; let progress = []; + let seenIds = new Set(); // Create a Set to store the IDs of seen anime release.map((list) => { list.entries.map((entry) => { - if (entry.media.status === "RELEASING") { + if ( + entry.media.status === "RELEASING" && + !seenIds.has(entry.media.id) + ) { releasingAnime.push(entry.media); + seenIds.add(entry.media.id); // Add the ID to the Set } - progress.push(entry); }); }); @@ -139,8 +148,7 @@ export default function Home({ detail, populars, sessions, upComing }) { const [planned, setPlanned] = useState(null); const [greeting, setGreeting] = useState(""); const [user, setUser] = useState(null); - - // console.log({ user }); + const [removed, setRemoved] = useState(); const [prog, setProg] = useState(null); @@ -194,7 +202,7 @@ export default function Home({ detail, populars, sessions, upComing }) { // const data = await res.json(); } userData(); - }, [sessions?.user?.name]); + }, [sessions?.user?.name, removed]); useEffect(() => { const time = new Date().getHours(); @@ -251,7 +259,11 @@ export default function Home({ detail, populars, sessions, upComing }) { /> <meta name="twitter:image" - content="https://cdn.discordapp.com/attachments/1084446049986420786/1093300833422168094/image.png" + content="https://beta.moopa.live/preview.png" + /> + <meta + name="description" + content="Discover your new favorite anime or manga title! Moopa offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Moopa today!" /> <link rel="icon" href="/c.svg" /> </Head> @@ -262,7 +274,7 @@ export default function Home({ detail, populars, sessions, upComing }) { <Navigasi /> <SearchBar /> <ToastContainer - pauseOnFocusLoss={false} + pauseOnHover={false} style={{ width: "400px", }} @@ -350,6 +362,8 @@ export default function Home({ detail, populars, sessions, upComing }) { ids="recentlyWatched" section="Recently Watched" userData={user} + userName={sessions?.user?.name} + setRemoved={setRemoved} /> </motion.div> )} diff --git a/pages/en/manga/read/[...params].js b/pages/en/manga/read/[...params].js index e608d16..301b646 100644 --- a/pages/en/manga/read/[...params].js +++ b/pages/en/manga/read/[...params].js @@ -115,6 +115,7 @@ export default function Read({ data, currentId, sessions }) { }` : "Getting Info..."} </title> + <meta id="CoverImage" data-manga-cover={info?.coverImage} /> </Head> <div className="w-screen flex justify-evenly relative"> <ToastContainer pauseOnFocusLoss={false} /> diff --git a/pages/index.js b/pages/index.js index 6f020fb..56b2c1f 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,7 +1,26 @@ +import Head from "next/head"; import { parseCookies } from "nookies"; export default function Home() { - return <></>; + return ( + <> + <Head> + <meta + name="twitter:title" + content="Moopa - Free Anime and Manga Streaming" + /> + <meta + name="twitter:description" + content="Discover your new favorite anime or manga title! Moopa offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Moopa today!" + /> + <meta name="twitter:image" content="/preview.png" /> + <meta + name="description" + content="Discover your new favorite anime or manga title! Moopa offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Moopa today!" + /> + </Head> + </> + ); } export async function getServerSideProps(context) { |