aboutsummaryrefslogtreecommitdiff
path: root/pages/en/anime
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-12-24 13:03:54 +0700
committerFactiven <[email protected]>2023-12-24 13:03:54 +0700
commit50a0f0240d7fef133eb5acc1bea2b1168b08e9db (patch)
tree307e09e505580415a58d64b5fc3580e9235869f1 /pages/en/anime
parentUpdate README.md (#104) (diff)
downloadmoopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.tar.xz
moopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.zip
migrate to typescript
Diffstat (limited to 'pages/en/anime')
-rw-r--r--pages/en/anime/[...id].tsx (renamed from pages/en/anime/[...id].js)79
-rw-r--r--pages/en/anime/recent.js2
-rw-r--r--pages/en/anime/watch/[...info].js213
3 files changed, 210 insertions, 84 deletions
diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].tsx
index 25cc4d6..42cae38 100644
--- a/pages/en/anime/[...id].js
+++ b/pages/en/anime/[...id].tsx
@@ -16,18 +16,30 @@ import Footer from "@/components/shared/footer";
import { mediaInfoQuery } from "@/lib/graphql/query";
import MobileNav from "@/components/shared/MobileNav";
+import pls from "@/utils/request/index";
+
import Characters from "@/components/anime/charactersCard";
import { redis } from "@/lib/redis";
-
-export default function Info({ info, color }) {
- const { data: session } = useSession();
+import { toast } from "sonner";
+import { Navbar } from "@/components/shared/NavBar";
+import { AniListInfoTypes } from "types/info/AnilistInfoTypes";
+
+type InfoTypes = {
+ info: AniListInfoTypes;
+ color: string;
+ api: string;
+ chapterNotFound: string;
+};
+
+export default function Info({ info, color, chapterNotFound }: InfoTypes) {
+ const { data: session }: any = useSession();
const { getUserLists } = useAniList(session);
const [loading, setLoading] = useState(false);
- const [progress, setProgress] = useState(0);
- const [statuses, setStatuses] = useState(null);
+ const [progress, setProgress] = useState<number>(0);
+ const [statuses, setStatuses] = useState<any>(null);
const [domainUrl, setDomainUrl] = useState("");
- const [watch, setWatch] = useState();
+ const [watch, setWatch] = useState<string>();
const [open, setOpen] = useState(false);
const { id } = useRouter().query;
@@ -37,6 +49,14 @@ export default function Info({ info, color }) {
);
useEffect(() => {
+ if (chapterNotFound) {
+ toast.error("Source not found");
+ const cleanUrl = window.location.origin + window.location.pathname;
+ window.history.replaceState(null, "", cleanUrl);
+ }
+ }, [chapterNotFound]);
+
+ useEffect(() => {
handleClose();
async function fetchData() {
setLoading(true);
@@ -53,7 +73,9 @@ export default function Info({ info, color }) {
if (user) {
setProgress(user.progress);
- const statusMapping = {
+ const statusMapping: {
+ [key: string]: { name: string; value: string };
+ } = {
CURRENT: { name: "Watching", value: "CURRENT" },
PLANNING: { name: "Plan to watch", value: "PLANNING" },
COMPLETED: { name: "Completed", value: "COMPLETED" },
@@ -118,6 +140,7 @@ export default function Info({ info, color }) {
}&image=${info.bannerImage || info.coverImage.extraLarge}`}
/>
</Head>
+ <Navbar info={info} />
<Modal open={open} onClose={() => handleClose()}>
<div>
{!session && (
@@ -151,7 +174,7 @@ export default function Info({ info, color }) {
)}
</div>
</Modal>
- <MobileNav sessions={session} hideProfile={true} />
+ <MobileNav 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" />
@@ -169,12 +192,10 @@ export default function Info({ info, color }) {
<div className="w-full lg:max-w-screen-lg xl:max-w-screen-2xl z-30 flex flex-col gap-5">
<DetailTop
info={info}
- session={session}
handleOpen={handleOpen}
- loading={loading}
statuses={statuses}
watchUrl={watch}
- progress={progress}
+ progress={progress || 0}
color={color}
/>
@@ -188,6 +209,9 @@ export default function Info({ info, color }) {
{info?.characters?.edges && (
<div className="w-full">
+ {/* <div className="w-full h-[150px] bg-white flex-center text-black">
+ ad banner
+ </div> */}
<Characters info={info?.characters?.edges} />
</div>
)}
@@ -208,8 +232,8 @@ export default function Info({ info, color }) {
);
}
-export async function getServerSideProps(ctx) {
- const { id } = ctx.query;
+export async function getServerSideProps(ctx: any) {
+ const { id, notfound } = ctx.query;
let API_URI;
API_URI = process.env.API_URI || null || null;
@@ -217,7 +241,12 @@ export async function getServerSideProps(ctx) {
API_URI = API_URI.slice(0, -1);
}
- let cache;
+ let cache, chapterNotFound;
+
+ if (notfound) {
+ // create random id string
+ chapterNotFound = Math.random().toString(36).substring(7);
+ }
if (redis) {
cache = await redis.get(`anime:${id}`);
@@ -230,14 +259,15 @@ export async function getServerSideProps(ctx) {
info,
color,
api: API_URI,
+ chapterNotFound: chapterNotFound || null,
},
};
} else {
- const resp = await fetch("https://graphql.anilist.co/", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
+ const [resp] = await pls.post("https://graphql.anilist.co/", {
+ // method: "POST",
+ // headers: {
+ // "Content-Type": "application/json",
+ // },
body: JSON.stringify({
query: mediaInfoQuery,
variables: {
@@ -246,10 +276,10 @@ export async function getServerSideProps(ctx) {
}),
});
- const json = await resp.json();
- const data = json?.data?.Media;
+ // const json = await resp.json();
+ const data = resp?.data?.Media;
- const cacheTime = data.nextAiringEpisode?.episode
+ const cacheTime = data?.nextAiringEpisode?.episode
? 60 * 10
: 60 * 60 * 24 * 30;
@@ -283,12 +313,13 @@ export async function getServerSideProps(ctx) {
info: data,
color: color,
api: API_URI,
+ chapterNotFound: chapterNotFound || null,
},
};
}
}
-function getBrightness(hexColor) {
+function getBrightness(hexColor: { match: (arg0: RegExp) => any[] }) {
if (!hexColor) {
return 200;
}
@@ -299,7 +330,7 @@ function getBrightness(hexColor) {
return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000;
}
-function setTxtColor(hexColor) {
+function setTxtColor(hexColor: { match: (arg0: RegExp) => any[] }) {
const brightness = getBrightness(hexColor);
return brightness < 150 ? "#fff" : "#000";
}
diff --git a/pages/en/anime/recent.js b/pages/en/anime/recent.js
index 4a8111d..240ed1d 100644
--- a/pages/en/anime/recent.js
+++ b/pages/en/anime/recent.js
@@ -83,7 +83,7 @@ export default function Recent({ sessions }) {
<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">New Episodes</h1>
+ <h1 className="text-xl">Freshly Added</h1>
</Link>
</div>
<div className="grid grid-cols-2 xs:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-6 gap-5 max-w-6xl pt-20">
diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js
index beab366..dc1f412 100644
--- a/pages/en/anime/watch/[...info].js
+++ b/pages/en/anime/watch/[...info].js
@@ -1,5 +1,4 @@
-import React, { useEffect, useRef, useState } from "react";
-import PlayerComponent from "@/components/watch/player/playerComponent";
+import { useEffect, useState } from "react";
import { FlagIcon, ShareIcon } from "@heroicons/react/24/solid";
import Details from "@/components/watch/primary/details";
import EpisodeLists from "@/components/watch/secondary/episodeLists";
@@ -9,13 +8,16 @@ import { authOptions } from "../../../api/auth/[...nextauth]";
import { createList, createUser, getEpisode } from "@/prisma/user";
import Link from "next/link";
import MobileNav from "@/components/shared/MobileNav";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import Modal from "@/components/modal";
import AniList from "@/components/media/aniList";
import { signIn } from "next-auth/react";
import BugReportForm from "@/components/shared/bugReport";
import Skeleton from "react-loading-skeleton";
import Head from "next/head";
+import VidStack from "@/components/watch/new-player/player";
+import { useRouter } from "next/router";
+import { Spinner } from "@vidstack/react";
export async function getServerSideProps(context) {
let userData = null;
@@ -81,7 +83,7 @@ export async function getServerSideProps(context) {
color
}
synonyms
-
+
}
}
`,
@@ -91,6 +93,8 @@ export async function getServerSideProps(context) {
}),
});
const data = await ress.json();
+ // const variables = { id: aniId };
+ // const data = await getAnilistMediaInfo(variables, context.req);
try {
if (session) {
@@ -142,17 +146,24 @@ export default function Watch({
const [episodesList, setepisodesList] = useState();
const [mapEpisode, setMapEpisode] = useState(null);
- const [episodeSource, setEpisodeSource] = useState(null);
-
const [open, setOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false);
+ const { setAutoNext } = useWatchProvider();
+
const [onList, setOnList] = useState(false);
- const { theaterMode, setPlayerState, setAutoPlay, setMarked } =
- useWatchProvider();
+ const router = useRouter();
- const playerRef = useRef(null);
+ const {
+ theaterMode,
+ setPlayerState,
+ setAutoPlay,
+ setMarked,
+ setTrack,
+ aspectRatio,
+ setDataMedia,
+ } = useWatchProvider();
useEffect(() => {
async function getInfo() {
@@ -160,6 +171,8 @@ export default function Watch({
setOnList(true);
}
+ setDataMedia(info);
+
const response = await fetch(
`/api/v2/episode/${info.id}?releasing=${
info.status === "RELEASING" ? "true" : "false"
@@ -202,17 +215,18 @@ export default function Watch({
const previousEpisode = episodeList?.find(
(i) => i.number === parseInt(epiNumber) - 1
);
- setEpisodeNavigation({
+ const vidNav = {
prev: previousEpisode,
playing: {
id: currentEpisode.id,
- title: playingData?.title,
+ title: playingData?.title || info?.title?.romaji,
description: playingData?.description,
img: playingData?.img || playingData?.image,
number: currentEpisode.number,
},
next: nextEpisode,
- });
+ };
+ setEpisodeNavigation(vidNav);
}
}
@@ -228,12 +242,17 @@ export default function Watch({
}, [sessions?.user?.name, epiNumber, dub]);
useEffect(() => {
+ const autoNext = localStorage.getItem("autoNext"),
+ autoPlay = localStorage.getItem("autoplay");
+ if (autoNext) {
+ setAutoNext(autoNext);
+ }
+ if (autoPlay) {
+ setAutoPlay(autoPlay);
+ }
+
async function fetchData() {
if (info) {
- const autoplay =
- localStorage.getItem("autoplay_video") === "true" ? true : false;
- setAutoPlay(autoplay);
-
const anify = await fetch("/api/v2/source", {
method: "POST",
headers: {
@@ -252,6 +271,11 @@ export default function Watch({
}),
}).then((res) => res.json());
+ if (!anify?.sources?.length > 0) {
+ router.push(`/en/anime/${info.id}?notfound=true`);
+ return;
+ }
+
const skip = await fetch(
`https://api.aniskip.com/v2/skip-times/${info.idMal}/${parseInt(
epiNumber
@@ -267,31 +291,77 @@ export default function Watch({
return res.json();
});
- const op =
- skip?.results?.find((item) => item.skipType === "op") || null;
- const ed =
- skip?.results?.find((item) => item.skipType === "ed") || null;
+ let getOp =
+ skip?.results?.find((item) => item.skipType === "op") || null,
+ getEd = skip?.results?.find((item) => item.skipType === "ed") || null;
+
+ const op = getOp
+ ? {
+ startTime:
+ anify?.intro?.start ?? Math.round(getOp?.interval.startTime),
+ endTime:
+ anify?.intro?.end ?? Math.round(getOp?.interval.endTime),
+ text: "Opening",
+ }
+ : null,
+ ed = {
+ startTime:
+ anify?.outro?.start ?? Math.round(getEd?.interval.startTime),
+ endTime: anify?.outro?.end ?? Math.round(getEd?.interval.endTime),
+ text: "Ending",
+ };
+ const skipData = [op, ed].filter((i) => i !== null);
+
+ const quality =
+ anify?.sources?.find(
+ (i) => i.quality === "default" || i.quality === "auto"
+ ) || anify?.sources[0];
+
+ const reFormSubtitles = anify?.subtitles?.map((i) => {
+ return {
+ src: proxy + "/" + i.url,
+ label: i.lang,
+ kind: i.lang === "Thumbnails" ? "thumbnails" : "subtitles",
+ ...(i.lang === "English" && { default: true }),
+ };
+ });
+
+ const thumbnails = reFormSubtitles?.find(
+ (i) => i.kind === "thumbnails"
+ );
+
+ const subtitles = reFormSubtitles?.filter(
+ (i) => i.kind !== "thumbnails"
+ );
const episode = {
- epiData: anify,
- skip: {
- op,
- ed,
+ provider,
+ isDub: dub,
+ defaultQuality: {
+ // url: quality?.url,
+ url: `${proxy}/proxy/m3u8/${encodeURIComponent(
+ String(quality?.url)
+ )}/${encodeURIComponent(JSON.stringify(anify?.headers))}`,
+ headers: anify?.headers,
},
+ subtitles: subtitles,
+ thumbnails: thumbnails?.src,
+ epiData: anify,
+ skip: skipData,
};
- setEpisodeSource(episode);
+ setTrack(episode);
}
}
fetchData();
return () => {
- setEpisodeSource();
setPlayerState({
currentTime: 0,
isPlaying: false,
});
setMarked(0);
+ setTrack(null);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -424,7 +494,7 @@ export default function Watch({
</Modal>
<BugReportForm isOpen={isOpen} setIsOpen={setIsOpen} />
<main className="w-screen h-full">
- <NewNavbar
+ <Navbar
scrollP={20}
withNav={true}
shrink={true}
@@ -435,21 +505,23 @@ export default function Watch({
className={`mx-auto pt-16 ${theaterMode ? "lg:pt-16" : "lg:pt-20"}`}
>
{theaterMode && (
- <PlayerComponent
- id={"cinematic"}
- session={sessions}
- playerRef={playerRef}
- dub={dub}
- info={info}
- watchId={watchId}
- proxy={proxy}
- track={episodeNavigation}
- data={episodeSource?.epiData}
- skip={episodeSource?.skip}
- timeWatched={userData?.timeWatched}
- provider={provider}
- className="w-screen max-h-[85dvh]"
- />
+ <div
+ className={`bg-black w-full max-h-[84dvh] h-full flex-center rounded-md`}
+ style={{ aspectRatio: aspectRatio }}
+ >
+ {episodeNavigation ? (
+ <VidStack
+ id={`${watchId}-theater`}
+ navigation={episodeNavigation}
+ sessions={sessions}
+ userData={userData}
+ />
+ ) : (
+ <div className="flex-center aspect-video w-full h-full relative">
+ <SpinLoader />
+ </div>
+ )}
+ </div>
)}
<div
id="default"
@@ -459,20 +531,25 @@ export default function Watch({
>
<div id="primary" className="w-full">
{!theaterMode && (
- <PlayerComponent
- id={"default"}
- session={sessions}
- playerRef={playerRef}
- dub={dub}
- info={info}
- watchId={watchId}
- proxy={proxy}
- track={episodeNavigation}
- data={episodeSource?.epiData}
- skip={episodeSource?.skip}
- timeWatched={userData?.timeWatched}
- provider={provider}
- />
+ <div
+ className={`bg-black w-full flex-center rounded-md overflow-hidden ${
+ aspectRatio === "4/3" ? "aspect-video" : ""
+ }`}
+ // style={{ aspectRatio: aspectRatio }}
+ >
+ {episodeNavigation ? (
+ <VidStack
+ id={`${watchId}-default`}
+ navigation={episodeNavigation}
+ sessions={sessions}
+ userData={userData}
+ />
+ ) : (
+ <div className="flex-center aspect-video w-full h-full relative">
+ <SpinLoader />
+ </div>
+ )}
+ </div>
)}
<div
id="details"
@@ -506,7 +583,7 @@ export default function Watch({
className="flex items-center gap-2 px-3 py-1 ring-[1px] ring-white/20 rounded overflow-hidden"
>
<ShareIcon className="w-5 h-5" />
- share
+ <span className="hidden lg:block">share</span>
</button>
<button
type="button"
@@ -514,11 +591,10 @@ export default function Watch({
className="flex items-center gap-2 px-3 py-1 ring-[1px] ring-white/20 rounded overflow-hidden"
>
<FlagIcon className="w-5 h-5" />
- report
+ <span className="hidden lg:block">report</span>
</button>
</div>
</div>
- {/* <div>right</div> */}
</div>
<Details
@@ -538,6 +614,11 @@ export default function Watch({
id="secondary"
className={`relative ${theaterMode ? "pt-5" : "pt-4 lg:pt-0"}`}
>
+ {/* <div className="w-full h-[150px] text-black p-3">
+ <span className="bg-white w-full h-full flex-center">
+ ad banner
+ </span>
+ </div> */}
<EpisodeLists
info={info}
session={sessions}
@@ -556,3 +637,17 @@ export default function Watch({
</>
);
}
+
+function SpinLoader() {
+ return (
+ <div className="pointer-events-none absolute inset-0 z-50 flex h-full w-full items-center justify-center">
+ <Spinner.Root
+ className="text-white animate-spin opacity-100"
+ size={84}
+ >
+ <Spinner.Track className="opacity-25" width={8} />
+ <Spinner.TrackFill className="opacity-75" width={8} />
+ </Spinner.Root>
+ </div>
+ );
+}