aboutsummaryrefslogtreecommitdiff
path: root/pages/api
diff options
context:
space:
mode:
Diffstat (limited to 'pages/api')
-rw-r--r--pages/api/auth/[...nextauth].ts (renamed from pages/api/auth/[...nextauth].js)65
-rw-r--r--pages/api/og.tsx (renamed from pages/api/og.jsx)2
-rw-r--r--pages/api/v2/episode/[id].tsx (renamed from pages/api/v2/episode/[id].js)128
-rw-r--r--pages/api/v2/etc/recent/[page].js57
-rw-r--r--pages/api/v2/etc/recent/[page].tsx81
-rw-r--r--pages/api/v2/etc/schedule/index.tsx (renamed from pages/api/v2/etc/schedule/index.js)35
6 files changed, 217 insertions, 151 deletions
diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].ts
index da78d07..70b2e3d 100644
--- a/pages/api/auth/[...nextauth].js
+++ b/pages/api/auth/[...nextauth].ts
@@ -1,29 +1,6 @@
-import NextAuth from "next-auth";
-import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
+import NextAuth, { NextAuthOptions } from "next-auth";
-const defaultOptions = {
- watchQuery: {
- fetchPolicy: "no-cache",
- errorPolicy: "ignore",
- },
- query: {
- fetchPolicy: "no-cache",
- errorPolicy: "all",
- },
-};
-
-const client = new ApolloClient({
- uri: "https://graphql.anilist.co",
- cache: new InMemoryCache(),
- defaultOptions: defaultOptions,
-});
-
-// import clientPromise from "../../../lib/mongodb";
-// import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
-
-export const authOptions = {
- // Configure one or more authentication providers
- // adapter: MongoDBAdapter(clientPromise),
+export const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET,
providers: [
{
@@ -38,8 +15,17 @@ export const authOptions = {
userinfo: {
url: process.env.GRAPHQL_ENDPOINT,
async request(context) {
- const { data } = await client.query({
- query: gql`
+ // console.log(context.tokens.access_token);
+ const { data } = await fetch("https://graphql.anilist.co", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ // ...(context.tokens.access_token && {
+ Authorization: `Bearer ${context.tokens.access_token}`,
+ // }),
+ },
+ body: JSON.stringify({
+ query: `
query {
Viewer {
id
@@ -57,34 +43,33 @@ export const authOptions = {
}
}
`,
- context: {
- headers: {
- Authorization: "Bearer " + context.tokens.access_token,
- },
- },
- });
+ }),
+ }).then((res) => res.json());
- const userLists = data.Viewer.mediaListOptions.animeList.customLists;
+ const userLists = data.Viewer?.mediaListOptions.animeList.customLists;
let custLists = userLists || [];
if (!userLists?.includes("Watched using Moopa")) {
custLists.push("Watched using Moopa");
- const fetchGraphQL = async (query, variables) => {
+ const fetchGraphQL = async (
+ query: string,
+ variables: { lists: any }
+ ) => {
const response = await fetch("https://graphql.anilist.co/", {
method: "POST",
headers: {
"Content-Type": "application/json",
- Authorization: context.tokens.access_token
- ? `Bearer ${context.tokens.access_token}`
- : undefined,
+ ...(context.tokens.access_token && {
+ Authorization: `Bearer ${context.tokens.access_token}`,
+ }),
},
body: JSON.stringify({ query, variables }),
});
return response.json();
};
- const customLists = async (lists) => {
+ const customLists = async (lists: any) => {
const setList = `
mutation($lists: [String]){
UpdateUser(animeListOptions: { customLists: $lists }){
@@ -104,7 +89,7 @@ export const authOptions = {
name: data.Viewer.name,
sub: data.Viewer.id,
image: data.Viewer.avatar,
- list: data.Viewer.mediaListOptions.animeList.customLists,
+ list: data.Viewer?.mediaListOptions.animeList.customLists,
};
},
},
diff --git a/pages/api/og.jsx b/pages/api/og.tsx
index d52f90e..47619bc 100644
--- a/pages/api/og.jsx
+++ b/pages/api/og.tsx
@@ -11,7 +11,7 @@ const outfit = fetch(
new URL("../../assets/Outfit-Regular.ttf", import.meta.url)
).then((res) => res.arrayBuffer());
-export default async function handler(request) {
+export default async function handler(request: any) {
const Karla = await karla;
const Outfit = await outfit;
diff --git a/pages/api/v2/episode/[id].js b/pages/api/v2/episode/[id].tsx
index b601f62..b646126 100644
--- a/pages/api/v2/episode/[id].js
+++ b/pages/api/v2/episode/[id].tsx
@@ -1,17 +1,21 @@
+// @ts-nocheck
+
import axios from "axios";
import { rateLimiterRedis, rateSuperStrict, redis } from "@/lib/redis";
import appendMetaToEpisodes from "@/utils/appendMetaToEpisodes";
+import { NextApiRequest, NextApiResponse } from "next";
+import { AnifyEpisode, ConsumetInfo, EpisodeData } from "types";
+import { Episode } from "@/types/api/Episode";
+import { getProviderWithMostEpisodesAndImage } from "@/utils/parseMetaData";
-let CONSUMET_URI;
+let CONSUMET_URI: string | null;
CONSUMET_URI = process.env.API_URI || null;
if (CONSUMET_URI && CONSUMET_URI.endsWith("/")) {
CONSUMET_URI = CONSUMET_URI.slice(0, -1);
}
-const API_KEY = process.env.API_KEY;
-
-const isAscending = (data) => {
+const isAscending = (data: Episode[]) => {
for (let i = 1; i < data.length; i++) {
if (data[i].number < data[i - 1].number) {
return false;
@@ -20,7 +24,16 @@ const isAscending = (data) => {
return true;
};
-function filterData(data, type) {
+export interface RawEpisodeData {
+ map?: boolean;
+ providerId: string;
+ episodes: {
+ sub: Episode[];
+ dub: Episode[];
+ };
+}
+
+function filterData(data: RawEpisodeData[], type: "sub" | "dub") {
// Filter the data based on the type (sub or dub) and providerId
const filteredData = data.map((item) => {
if (item?.map === true) {
@@ -44,10 +57,10 @@ function filterData(data, type) {
return noEmpty;
}
-async function fetchConsumet(id) {
+async function fetchConsumet(id?: string | string[] | undefined) {
try {
- async function fetchData(dub) {
- const { data } = await axios.get(
+ const fetchData = async (dub?: any) => {
+ const { data } = await axios.get<ConsumetInfo>(
`${CONSUMET_URI}/meta/anilist/info/${id}${dub ? "?dub=true" : ""}`
);
if (data?.message === "Anime not found" && data?.length < 1) {
@@ -59,23 +72,32 @@ async function fetchConsumet(id) {
}
const reformatted = data.episodes?.map((item) => ({
- id: item?.id || null,
+ id: item.id,
title: item?.title || null,
img: item?.image || null,
number: item?.number || null,
- createdAt: item?.createdAt || null,
+ createdAt: item?.airDate || null,
description: item?.description || null,
- url: item?.url || null,
}));
return reformatted;
- }
+ };
const [subData, dubData] = await Promise.all([
fetchData(),
fetchData(true),
]);
+ if (subData.every((i) => i.id?.includes("dub"))) {
+ // replace dub in title with sub
+ subData.forEach((item) => {
+ if (item.id?.includes("dub")) {
+ item.id = item.id?.replace("dub", "anime");
+ }
+ });
+ console.log("replaced dub with sub");
+ }
+
const array = [
{
map: true,
@@ -88,38 +110,34 @@ async function fetchConsumet(id) {
];
return array;
- } catch (error) {
+ } catch (error: any) {
console.error("Error fetching and processing data:", error.message);
return [];
}
}
-async function fetchAnify(id) {
+async function fetchAnify(id?: string) {
try {
- const { data } = await axios.get(`https://api.anify.tv/episodes/${id}`);
+ const { data } = await axios.get<AnifyEpisode[]>(
+ `https://api.anify.tv/episodes/${id}`
+ );
if (!data) {
return [];
}
- const filtered = data.filter((item) => item.providerId !== "kass");
- // const modifiedData = filtered.map((provider) => {
- // if (provider.providerId === "gogoanime") {
- // const reversedEpisodes = [...provider.episodes].reverse();
- // return { ...provider, episodes: reversedEpisodes };
- // }
- // return provider;
- // });
+ const filtered = data.filter(
+ (item) => item.providerId !== "9anime" && item.providerId !== "kass"
+ );
- // return modifiedData;
return filtered;
- } catch (error) {
+ } catch (error: any) {
console.error("Error fetching and processing data:", error.message);
return [];
}
}
-async function fetchCoverImage(id, available = false) {
+async function fetchCoverImage(id: string, available = false) {
try {
if (!process.env.API_KEY) {
return [];
@@ -137,16 +155,20 @@ async function fetchCoverImage(id, available = false) {
return [];
}
- const getData = data[0].data;
+ const getData = getProviderWithMostEpisodesAndImage(data);
+ // const getData = data?.[0]?.data;
- return getData;
- } catch (error) {
+ return getData.data;
+ } catch (error: any) {
console.error("Error fetching and processing data:", error.message);
return [];
}
}
-export default async function handler(req, res) {
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
const { id, releasing = "false", dub = false, refresh = null } = req.query;
// if releasing is true then cache for 1 hour, if it false cache for 1 month;
@@ -159,11 +181,11 @@ export default async function handler(req, res) {
let cached;
let meta;
- let headers;
+ let headers: any = {};
if (redis) {
try {
- const ipAddress = req.socket.remoteAddress;
+ const ipAddress: any = req.socket.remoteAddress;
refresh
? await rateSuperStrict.consume(ipAddress)
: await rateLimiterRedis.consume(ipAddress);
@@ -171,9 +193,7 @@ export default async function handler(req, res) {
headers = refresh
? await rateSuperStrict.get(ipAddress)
: await rateLimiterRedis.get(ipAddress);
-
- console.log(headers);
- } catch (error) {
+ } catch (error: any) {
return res.status(429).json({
error: `Too Many Requests, retry after ${getTimeFromMs(
error.msBeforeNext
@@ -182,20 +202,28 @@ export default async function handler(req, res) {
});
}
+ meta = await redis.get(`meta:${id}`);
+ const parsedMeta = JSON.parse(meta);
+ if (parsedMeta?.length === 0) {
+ await redis.del(`meta:${id}`);
+ console.log("deleted meta cache");
+ meta = null;
+ }
+
if (refresh) {
await redis.del(`episode:${id}`);
- console.log("deleted cache");
} else {
cached = await redis.get(`episode:${id}`);
- console.log("using redis");
+ if (cached?.length === 0) {
+ await redis.del(`episode:${id}`);
+ cached = null;
+ }
}
-
- meta = await redis.get(`meta:${id}`);
}
if (cached && !refresh) {
if (dub) {
- const filteredData = filterData(JSON.parse(cached), "dub");
+ const filteredData: EpisodeData[] = filterData(JSON.parse(cached), "dub");
let filtered = filteredData.filter((item) =>
item?.episodes?.some((epi) => epi.hasDub !== false)
@@ -208,7 +236,9 @@ export default async function handler(req, res) {
res.setHeader("X-RateLimit-Remaining", headers.remainingPoints);
res.setHeader("X-RateLimit-BeforeReset", headers.msBeforeNext);
- return res.status(200).json(filtered);
+ return res
+ .status(200)
+ .json(filtered?.filter((i) => i?.providerId !== "9anime"));
} else {
const filteredData = filterData(JSON.parse(cached), "sub");
@@ -221,11 +251,13 @@ export default async function handler(req, res) {
res.setHeader("X-RateLimit-Remaining", headers.remainingPoints);
res.setHeader("X-RateLimit-BeforeReset", headers.msBeforeNext);
- return res.status(200).send(filtered);
+ return res
+ .status(200)
+ .send(filtered?.filter((i) => i?.providerId !== "9anime"));
}
} else {
const [consumet, anify, cover] = await Promise.all([
- fetchConsumet(id, dub),
+ fetchConsumet(id),
fetchAnify(id),
fetchCoverImage(id, meta),
]);
@@ -249,12 +281,16 @@ export default async function handler(req, res) {
if (meta) {
data = await appendMetaToEpisodes(filteredData, JSON.parse(meta));
- } else if (cover && !cover.some((e) => e.img === null)) {
+ } else if (
+ cover &&
+ // !cover?.some((item: { img: null }) => item.img === null) &&
+ cover?.length > 0
+ ) {
if (redis) await redis.set(`meta:${id}`, JSON.stringify(cover));
data = await appendMetaToEpisodes(filteredData, cover);
}
- if (redis && cacheTime !== null) {
+ if (redis && cacheTime !== null && rawData?.length > 0) {
await redis.set(
`episode:${id}`,
JSON.stringify(rawData),
@@ -282,7 +318,7 @@ export default async function handler(req, res) {
}
}
-function getTimeFromMs(time) {
+function getTimeFromMs(time: number) {
const timeInSeconds = time / 1000;
if (timeInSeconds >= 3600) {
diff --git a/pages/api/v2/etc/recent/[page].js b/pages/api/v2/etc/recent/[page].js
deleted file mode 100644
index 2ff22ea..0000000
--- a/pages/api/v2/etc/recent/[page].js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { rateLimitStrict, redis } from "@/lib/redis";
-
-let API_URL;
-API_URL = process.env.API_URI || null;
-if (API_URL && API_URL.endsWith("/")) {
- API_URL = API_URL.slice(0, -1);
-}
-
-export default async function handler(req, res) {
- try {
- if (redis) {
- try {
- const ipAddress = req.socket.remoteAddress;
- await rateLimitStrict.consume(ipAddress);
- } catch (error) {
- return res.status(429).json({
- error: `Too Many Requests, retry after ${error.msBeforeNext / 1000}`,
- });
- }
- }
-
- let cache;
-
- if (redis) {
- cache = await redis.get(`recent-episode`);
- }
-
- if (cache) {
- return res.status(200).json({ results: JSON.parse(cache) });
- } else {
- const page = req.query.page || 1;
-
- var hasNextPage = true;
- var datas = [];
-
- async function fetchData(page) {
- const data = await fetch(
- `https://api.anify.tv/recent?type=anime&page=${page}&perPage=45`
- ).then((res) => res.json());
-
- // const filtered = data?.results?.filter((i) => i.type !== "ONA");
- // hasNextPage = data?.hasNextPage;
- datas = data;
- }
-
- await fetchData(page);
-
- if (redis) {
- await redis.set(`recent-episode`, JSON.stringify(datas), "EX", 60 * 60);
- }
-
- return res.status(200).json({ results: datas });
- }
- } catch (error) {
- res.status(500).json({ error });
- }
-}
diff --git a/pages/api/v2/etc/recent/[page].tsx b/pages/api/v2/etc/recent/[page].tsx
new file mode 100644
index 0000000..e49591c
--- /dev/null
+++ b/pages/api/v2/etc/recent/[page].tsx
@@ -0,0 +1,81 @@
+import { rateLimitStrict, redis } from "@/lib/redis";
+import { AnifyRecentEpisode } from "@/utils/types";
+import axios from "axios";
+import { NextApiRequest, NextApiResponse } from "next";
+
+let API_URL: string | null;
+API_URL = process.env.API_URI || null;
+if (API_URL && API_URL.endsWith("/")) {
+ API_URL = API_URL.slice(0, -1);
+}
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ try {
+ if (redis) {
+ try {
+ const ipAddress: any = req.socket.remoteAddress;
+ await rateLimitStrict?.consume(ipAddress);
+ } catch (error: any) {
+ return res.status(429).json({
+ error: `Too Many Requests, retry after ${error.msBeforeNext / 1000}`,
+ });
+ }
+ }
+
+ let cache;
+
+ if (redis) {
+ cache = await redis.get(`recent-episode`);
+ }
+
+ if (cache) {
+ return res.status(200).json({ results: JSON.parse(cache) });
+ } else {
+ const page = req.query.page || 1;
+
+ var hasNextPage = true;
+ let datas: AnifyRecentEpisode[] = [];
+
+ const fetchData = async (page: any) => {
+ const { data } = await axios.get(
+ `https://api.anify.tv/recent?type=anime&page=${page}&perPage=45&fields=[id,slug,title,currentEpisode,coverImage,episodes]`
+ );
+
+ // const filtered = data?.results?.filter((i) => i.type !== "ONA");
+ // hasNextPage = data?.hasNextPage;
+
+ const newData = data.map((i: AnifyRecentEpisode) => {
+ const getGogo = i.episodes?.data?.find(
+ (x) => x.providerId === "gogoanime"
+ );
+ const getGogoEpisode = getGogo?.episodes?.find(
+ (x) => x.number === i.currentEpisode
+ );
+
+ return {
+ id: i.id,
+ slug: getGogoEpisode?.id,
+ title: i.title,
+ currentEpisode: i.currentEpisode,
+ coverImage: i.coverImage,
+ };
+ });
+
+ datas = newData;
+ };
+
+ await fetchData(page);
+
+ if (redis) {
+ await redis.set(`recent-episode`, JSON.stringify(datas), "EX", 60 * 60);
+ }
+
+ return res.status(200).json({ results: datas });
+ }
+ } catch (error) {
+ res.status(500).json({ error });
+ }
+}
diff --git a/pages/api/v2/etc/schedule/index.js b/pages/api/v2/etc/schedule/index.tsx
index 2ddc82a..e6f0b26 100644
--- a/pages/api/v2/etc/schedule/index.js
+++ b/pages/api/v2/etc/schedule/index.tsx
@@ -1,6 +1,7 @@
import axios from "axios";
import cron from "cron";
import { rateLimiterRedis, redis } from "@/lib/redis";
+import { NextApiRequest, NextApiResponse } from "next";
// Function to fetch new data
async function fetchData() {
@@ -37,22 +38,42 @@ const job = new cron.CronJob("0 0 * * 1", () => {
});
job.start();
-export default async function handler(req, res) {
+interface Title {
+ romaji: string;
+ english: string;
+ native: string;
+}
+
+type CachedData = {
+ id: string;
+ title: Title;
+ coverImage: string;
+ bannerImage: string;
+ airingAt: number;
+ airingEpisode: number;
+};
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
try {
- let cached;
+ let cached: CachedData | null = null;
if (redis) {
try {
- const ipAddress = req.socket.remoteAddress;
- await rateLimiterRedis.consume(ipAddress);
- } catch (error) {
+ const ipAddress: any = req.socket.remoteAddress;
+ await rateLimiterRedis?.consume(ipAddress);
+ } catch (error: any) {
return res.status(429).json({
error: `Too Many Requests, retry after ${error.msBeforeNext / 1000}`,
});
}
- cached = await redis.get("schedule");
+ const cachedData = await redis.get("schedule");
+ cached = cachedData ? JSON.parse(cachedData) : null;
}
+
if (cached) {
- return res.status(200).json(JSON.parse(cached));
+ return res.status(200).json(cached);
} else {
const data = await fetchData();