aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-08-12 22:54:26 +0700
committerGitHub <[email protected]>2023-08-12 22:54:26 +0700
commit3e78826658c7d2a4e9b3c1d73e63dacc1d39c361 (patch)
treed580d03670692c6c5d361ec8559e7a2352354f3a /pages
parentUpdate v3.9.1 - Merged Beta to Main (#44) (diff)
downloadmoopa-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].js5
-rw-r--r--pages/api/user/profile.js16
-rw-r--r--pages/api/user/update/episode.js30
-rw-r--r--pages/en/anime/[...id].js6
-rw-r--r--pages/en/anime/recently-watched.js98
-rw-r--r--pages/en/anime/watch/[...info].js6
-rw-r--r--pages/en/dmca.js5
-rw-r--r--pages/en/index.js36
-rw-r--r--pages/en/manga/read/[...params].js1
-rw-r--r--pages/index.js21
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) {