aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/_app.js4
-rw-r--r--pages/admin/index.js261
-rw-r--r--pages/api/v2/admin/meta/index.js6
-rw-r--r--pages/en/anime/[...id].js4
-rw-r--r--pages/en/anime/watch/[...info].js38
5 files changed, 80 insertions, 233 deletions
diff --git a/pages/_app.js b/pages/_app.js
index 9e07d22..f553a98 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -7,9 +7,9 @@ import "react-toastify/dist/ReactToastify.css";
import "react-loading-skeleton/dist/skeleton.css";
import { SkeletonTheme } from "react-loading-skeleton";
import SearchPalette from "@/components/searchPalette";
-import { SearchProvider } from "@/lib/hooks/isOpenState";
+import { SearchProvider } from "@/lib/context/isOpenState";
import Head from "next/head";
-import { WatchPageProvider } from "@/lib/hooks/watchPageProvider";
+import { WatchPageProvider } from "@/lib/context/watchPageProvider";
import { ToastContainer, toast } from "react-toastify";
import { useEffect } from "react";
import { unixTimestampToRelativeTime } from "@/utils/getTimes";
diff --git a/pages/admin/index.js b/pages/admin/index.js
index 4fdc8c2..cbb5086 100644
--- a/pages/admin/index.js
+++ b/pages/admin/index.js
@@ -1,19 +1,14 @@
+import AdminDashboard from "@/components/admin/dashboard";
+import AdminLayout from "@/components/admin/layout";
+import AppendMeta from "@/components/admin/meta/AppendMeta";
+import {
+ countKeysWithPrefix,
+ countNumericKeys,
+ getValuesWithPrefix,
+} from "@/utils/getRedisWithPrefix";
import { getServerSession } from "next-auth";
import { authOptions } from "pages/api/auth/[...nextauth]";
-import { useState } from "react";
-import { toast } from "react-toastify";
-
-// Define a function to convert the data
-function convertData(episodes) {
- const convertedData = episodes.map((episode) => ({
- episode: episode.episode,
- title: episode?.title,
- description: episode?.description || null,
- img: episode?.img?.hd || episode?.img?.mobile || null, // Use hd if available, otherwise use mobile
- }));
-
- return convertedData;
-}
+import React, { useState } from "react";
export async function getServerSideProps(context) {
const sessions = await getServerSession(
@@ -43,221 +38,49 @@ export async function getServerSideProps(context) {
};
}
+ const [anime, info, meta, report] = await Promise.all([
+ countNumericKeys(),
+ countKeysWithPrefix("anime:"),
+ countKeysWithPrefix("meta:"),
+ getValuesWithPrefix("report:"),
+ ]);
+
return {
props: {
session: sessions,
+ animeCount: anime || 0,
+ infoCount: info || 0,
+ metaCount: meta || 0,
+ report: report || [],
api,
},
};
}
-export default function Admin({ api }) {
- const [id, setId] = useState();
- const [resultData, setResultData] = useState(null);
-
- const [query, setQuery] = useState("");
- const [tmdbId, setTmdbId] = useState();
- const [hasilQuery, setHasilQuery] = useState([]);
- const [season, setSeason] = useState();
-
- const [override, setOverride] = useState();
-
- const [loading, setLoading] = useState(false);
-
- const handleSearch = async () => {
- try {
- setLoading(true);
- setResultData(null);
- const res = await fetch(`${api}/meta/tmdb/${query}`);
- const json = await res.json();
- const data = json.results;
- setHasilQuery(data);
- setLoading(false);
- } catch (err) {
- console.log(err);
- }
- };
-
- const handleDetail = async () => {
- try {
- setLoading(true);
- const res = await fetch(`${api}/meta/tmdb/info/${tmdbId}?type=TV%20Series
-`);
- const json = await res.json();
- const data = json.seasons;
- setHasilQuery(data);
- setLoading(false);
- } catch (err) {
- console.log(err);
- }
- };
-
- const handleStore = async () => {
- try {
- setLoading(true);
- if (!resultData && !id) {
- console.log("No data to store");
- setLoading(false);
- return;
- }
- const data = await fetch("/api/v2/admin/meta", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- id: id,
- data: resultData,
- }),
- });
- if (data.status === 200) {
- const json = await data.json();
- toast.success(json.message);
- setLoading(false);
- }
- } catch (err) {
- console.log(err);
- }
- };
-
- const handleOverride = async () => {
- setResultData(JSON.parse(override));
- };
+export default function Admin({
+ animeCount,
+ infoCount,
+ metaCount,
+ report,
+ api,
+}) {
+ const [page, setPage] = useState(1);
return (
- <>
- <div className="container mx-auto p-4">
- <h1 className="text-3xl font-semibold mb-4">Append Data Page</h1>
- <div>
- <div className="space-y-3 mb-4">
- <label>Search Anime:</label>
- <input
- type="text"
- className="w-full px-3 py-2 border rounded-md text-black"
- value={query}
- onChange={(e) => setQuery(e.target.value)}
- />
- <button
- type="button"
- className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
- onClick={handleSearch}
- >
- Find Anime{" "}
- {loading && <span className="animate-spin ml-2">🔄</span>}
- </button>
- </div>
- <div className="space-y-3 mb-4">
- <label>Get Episodes:</label>
- <input
- type="number"
- className="w-full px-3 py-2 border rounded-md text-black"
- value={tmdbId}
- onChange={(e) => setTmdbId(e.target.value)}
- />
- <button
- type="button"
- className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
- onClick={handleDetail}
- >
- Get Details{" "}
- {loading && <span className="animate-spin ml-2">🔄</span>}
- </button>
- </div>
-
- <div className="space-y-3 mb-4">
- <label>Override Result:</label>
- <textarea
- rows="5"
- className="w-full px-3 py-2 border rounded-md text-black"
- value={override}
- onChange={(e) => setOverride(e.target.value)}
- />
- <button
- type="button"
- className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
- onClick={handleOverride}
- >
- Override{" "}
- {loading && <span className="animate-spin ml-2">🔄</span>}
- </button>
- </div>
-
- <div className="space-y-3 mb-4">
- <label className="block text-sm font-medium text-gray-300">
- Anime ID:
- </label>
- <input
- type="number"
- className="w-full px-3 py-2 border rounded-md text-black"
- value={id}
- onChange={(e) => setId(e.target.value)}
- />
- </div>
- <div className="mb-4">
- <button
- className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
- onClick={handleStore}
- >
- Store Data {season && `Season ${season}`}
- </button>
- </div>
-
- {hasilQuery?.some((i) => i?.season) && (
- <div className="border rounded-md p-4 mt-4">
- <h2 className="text-lg font-semibold mb-2">
- Which season do you want to format?
- </h2>
- <div className="w-full flex gap-2">
- {hasilQuery?.map((season, index) => (
- <button
- type="button"
- className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
- key={index}
- onClick={() => {
- setLoading(true);
- const data = hasilQuery[index].episodes;
- const convertedData = convertData(data);
- setSeason(index + 1);
- setResultData(convertedData);
- console.log(convertedData);
- setLoading(false);
- }}
- >
- <p>
- {season.season}{" "}
- {loading && <span className="animate-spin ml-2">🔄</span>}
- </p>
- </button>
- ))}
- </div>
- </div>
- )}
-
- {resultData && (
- <div className="border rounded-md p-4 mt-4">
- <h2 className="text-lg font-semibold mb-2">Season {season}</h2>
- <pre>{JSON.stringify(resultData, null, 2)}</pre>
- </div>
- )}
- {hasilQuery && (
- <div className="border rounded-md p-4 mt-4">
- <h2 className="text-lg font-semibold mb-2">
- Result Data,{" "}
- {hasilQuery.length > 0 && `${hasilQuery.length} Seasons`}:
- </h2>
- <pre>{JSON.stringify(hasilQuery, null, 2)}</pre>
- </div>
- )}
- </div>
- <div>
- {/* {resultData && (
- <div className="border rounded-md p-4 mt-4">
- <h2 className="text-lg font-semibold mb-2">Result Data:</h2>
- <pre>{JSON.stringify(resultData, null, 2)}</pre>
- </div>
- )} */}
- </div>
+ <AdminLayout page={page} setPage={setPage}>
+ <div className="h-full">
+ {page == 1 && (
+ <AdminDashboard
+ animeCount={animeCount}
+ infoCount={infoCount}
+ metaCount={metaCount}
+ report={report}
+ />
+ )}
+ {page == 2 && <AppendMeta api={api} />}
+ {page == 3 && <p className="flex-center h-full">Coming Soon!</p>}
+ {page == 4 && <p className="flex-center h-full">Coming Soon!</p>}
</div>
- </>
+ </AdminLayout>
);
}
diff --git a/pages/api/v2/admin/meta/index.js b/pages/api/v2/admin/meta/index.js
index 5f51b7f..600a3ef 100644
--- a/pages/api/v2/admin/meta/index.js
+++ b/pages/api/v2/admin/meta/index.js
@@ -27,12 +27,6 @@ export default async function handler(req, res) {
});
}
- const getId = await redis.get(`meta:${id}`);
- if (getId) {
- return res
- .status(200)
- .json({ message: `Data already exist for id: ${id}` });
- }
await redis.set(`meta:${id}`, JSON.stringify(data));
return res
.status(200)
diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].js
index 4809ce5..910bbc6 100644
--- a/pages/en/anime/[...id].js
+++ b/pages/en/anime/[...id].js
@@ -152,7 +152,7 @@ export default function Info({ info, color }) {
<MobileNav sessions={session} hideProfile={true} />
<main className="w-screen min-h-screen relative flex flex-col items-center bg-primary gap-5">
<div className="w-screen absolute">
- <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[280px] w-screen z-10 inset-0 backdrop-blur-[2px]" />
+ <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[280px] w-screen z-10 inset-0" />
{info?.bannerImage && (
<Image
src={info?.bannerImage}
@@ -160,7 +160,7 @@ export default function Info({ info, color }) {
height={1000}
width={1000}
blurDataURL={info?.bannerImage}
- className="object-cover bg-image w-screen absolute top-0 left-0 h-[250px] brightness-[55%] z-0"
+ className="object-cover bg-image blur-[2px] w-screen absolute top-0 left-0 h-[250px] brightness-[55%] z-0"
/>
)}
</div>
diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js
index f5b4fce..b74a3f2 100644
--- a/pages/en/anime/watch/[...info].js
+++ b/pages/en/anime/watch/[...info].js
@@ -4,7 +4,7 @@ import { FlagIcon, ShareIcon } from "@heroicons/react/24/solid";
import Details from "@/components/watch/primary/details";
import EpisodeLists from "@/components/watch/secondary/episodeLists";
import { getServerSession } from "next-auth";
-import { useWatchProvider } from "@/lib/hooks/watchPageProvider";
+import { useWatchProvider } from "@/lib/context/watchPageProvider";
import { authOptions } from "../../../api/auth/[...nextauth]";
import { createList, createUser, getEpisode } from "@/prisma/user";
import Link from "next/link";
@@ -289,6 +289,29 @@ export default function Watch({
};
}, [provider, watchId, info?.id]);
+ useEffect(() => {
+ const mediaSession = navigator.mediaSession;
+ if (!mediaSession) return;
+
+ const now = episodeNavigation?.playing;
+ const poster = now?.img || info?.bannerImage;
+ const title = now?.title || info?.title?.romaji;
+
+ const artwork = poster
+ ? [{ src: poster, sizes: "512x512", type: "image/jpeg" }]
+ : undefined;
+
+ mediaSession.metadata = new MediaMetadata({
+ title: title,
+ artist: `Moopa ${
+ title === info?.title?.romaji
+ ? "- Episode " + epiNumber
+ : `- ${info?.title?.romaji || info?.title?.english}`
+ }`,
+ artwork,
+ });
+ }, [episodeNavigation, info, epiNumber]);
+
const handleShareClick = async () => {
try {
if (navigator.share) {
@@ -338,7 +361,6 @@ export default function Watch({
<meta name="robots" content="index, follow" />
<meta property="og:type" content="website" />
- <meta property="og:url" content="https://moopa.live/" />
<meta
property="og:title"
content={`Watch - ${
@@ -347,12 +369,19 @@ export default function Watch({
/>
<meta
property="og: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!"
+ content={episodeNavigation?.playing?.description || info?.description}
+ />
+ <meta
+ property="og:image"
+ content={episodeNavigation?.playing?.img || info?.bannerImage}
/>
- <meta property="og:image" content="/preview.png" />
<meta property="og:site_name" content="Moopa" />
<meta name="twitter:card" content="summary_large_image" />
<meta
+ name="twitter:image"
+ content={episodeNavigation?.playing?.img || info?.bannerImage}
+ />
+ <meta
name="twitter:title"
content={`Watch - ${
episodeNavigation?.playing?.title || info?.title?.english
@@ -499,6 +528,7 @@ export default function Watch({
>
<EpisodeLists
info={info}
+ session={sessions}
map={mapEpisode}
providerId={provider}
watchId={watchId}