diff --git a/.gitignore b/.gitignore
index 8c3fe02..dd34640 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,4 +120,7 @@ dist
### VisualStudioCode Patch ###
# Ignore all local history of files
-.history
\ No newline at end of file
+.history
+
+generated/
+.vercel
diff --git a/README.md b/README.md
index 10f6a30..ac579d7 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,6 @@
-
-
-
-
diff --git a/graphql/index.ts b/graphql/index.ts
deleted file mode 100644
index a69e659..0000000
--- a/graphql/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ApolloServer } from "apollo-server-micro";
-import typeDefs from "./type-defs";
-import resolvers from "./resolvers";
-
-const server = new ApolloServer({
- typeDefs,
- resolvers,
- introspection: true,
- playground: true
-});
-
-module.exports = server.createHandler({ path: "/graphql" });
diff --git a/graphql/resolvers/index.ts b/graphql/resolvers/index.ts
deleted file mode 100644
index c6c7baa..0000000
--- a/graphql/resolvers/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import queries from "./queries";
-import scalarsAndEnums from "./scalars-enums";
-
-export default {
- ...queries,
- ...scalarsAndEnums
-};
diff --git a/graphql/resolvers/queries.ts b/graphql/resolvers/queries.ts
deleted file mode 100644
index bf08346..0000000
--- a/graphql/resolvers/queries.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import cheerio from "cheerio";
-import {
- get,
- getCover,
- getBanner,
- getThumbnail,
- imageUrlToBase64,
- getAnime,
- explore,
-} from "../utils";
-
-export default {
- Query: {
- anime: async (_, { slug }, ctx) => {
- const body = await getAnime({ slug }, ctx);
-
- const rawInfo = body
- .split("var anime_info = ", 2)[1]
- .replace(/'/g, "'")
- .split(";", 2)[0];
- const info = JSON.parse(rawInfo);
-
- const $ = cheerio.load(body);
-
- return {
- id: parseInt(info[0]),
- name: info[1],
- slug,
- type: $("span.Type").attr("class").split(" ")[1],
- synopsis: $("div.Description p").text(),
- rating: parseFloat($("#votes_prmd").text()),
- };
- },
- search: async (_, args) => explore(args),
- explore: async (_, args) => explore(args),
- episodeSources: async (_, { episodeId, episodeN, animeSlug, type }) => {
- const body = await get(`/ver/${episodeId}/${animeSlug}-${episodeN}`);
-
- const rawSources = body.split("var videos = ", 2)[1].split(";", 2)[0];
- const sources = JSON.parse(rawSources);
-
- return sources[type || "SUB"];
- },
- },
- Anime: {
- cover: async ({ id }) => imageUrlToBase64(getCover(id)),
- banner: async ({ id }) => imageUrlToBase64(getBanner(id)),
- status: async (args, _, ctx) => {
- const body = await getAnime(args, ctx);
- const $ = cheerio.load(body);
-
- let status = 1;
- if ($("p.AnmStts").hasClass("A")) status = 2;
- else if ($("p.AnmStts").hasClass("B")) status = 3;
-
- return status;
- },
- genres: async (args, _, ctx) => {
- const body = await getAnime(args, ctx);
- const $ = cheerio.load(body);
-
- return $("nav.Nvgnrs a").map((_, a) => {
- const $a = $(a);
- return $a.attr("href").split("=")[1];
- });
- },
- episodes: async (args, _, ctx) => {
- const body = await getAnime(args, ctx);
-
- const rawEpisodes = body.split("var episodes = ", 2)[1].split(";", 2)[0];
-
- return JSON.parse(rawEpisodes).map((arr) => ({
- id: arr[1],
- n: arr[0],
- thumbnail: getThumbnail(args.id, arr[0]),
- }));
- },
- nextEpisode: async (args, _, ctx) => {
- const body = await getAnime(args, ctx);
-
- const rawInfo = body
- .split("var anime_info = ", 2)[1]
- .replace(/'/g, "'")
- .split(";", 2)[0];
-
- return JSON.parse(rawInfo)[3] || null;
- },
- },
- Episode: {
- thumbnail: async ({ thumbnail }) => imageUrlToBase64(thumbnail),
- },
-};
diff --git a/graphql/type-defs.ts b/graphql/type-defs.ts
deleted file mode 100644
index 216a237..0000000
--- a/graphql/type-defs.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-const { gql } = require("apollo-server-micro");
-
-export default gql`
- scalar Date
-
- enum AnimeType {
- TV
- MOVIE
- SPECIAL
- OVA
- }
-
- enum AnimeStatus {
- RUNNING
- ENDED
- SOON
- }
-
- enum AnimeGenre {
- ACCION
- ARTES_MARCIALES
- AVENTURA
- CARRERAS
- CIENCIA_FICCION
- COMEDIA
- DEMENCIA
- DEMONIOS
- DEPORTES
- DRAMA
- ECCHI
- ESCOLARES
- ESPACIAL
- FANTASIA
- HAREM
- HISTORICO
- INFANTIL
- JOSEI
- JUEGOS
- MAGIA
- MECHA
- MILITAR
- MISTERIO
- MUSICA
- PARODIA
- POLICIA
- PSICOLOGICO
- RECUENTOS_DE_LA_VIDA
- ROMANCE
- SAMURAI
- SEINEN
- SHOUJO
- SHOUNEN
- SOBRENATURAL
- SUPERPODERES
- SUSPENSO
- TERROR
- VAMPIROS
- YAOI
- YURI
- }
-
- enum SourcesType {
- SUB
- LAT
- }
-
- type Anime {
- id: Int!
- name: String!
- slug: String!
- type: AnimeType!
- status: AnimeStatus!
- cover: String!
- banner: String!
- synopsis: String!
- rating: Float!
- genres: [AnimeGenre!]!
- episodes: [Episode!]!
- nextEpisode: Date
- }
-
- type Episode {
- id: Int!
- n: Float!
- thumbnail: String!
- }
-
- type EpisodeSource {
- server: String!
- title: String!
- code: String!
- }
-
- type Query {
- anime(id: Int, slug: String!): Anime
- search(query: String!): [Anime!]!
- explore(
- type: [AnimeType!]
- status: [AnimeStatus!]
- genre: [AnimeGenre!]
- ): [Anime!]!
- episodeSources(
- episodeId: Int!
- episodeN: Int!
- animeSlug: String!
- type: SourcesType
- ): [EpisodeSource!]
- }
-`;
-
-/**
- * *AnimeGenre
- *
- * Acción accion
- * Artes Marciales artes-marciales
- * Aventuras aventura
- * Carreras carreras
- * Ciencia Ficción ciencia-ficcion
- * Comedia comedia
- * Demencia demencia
- * Demonios demonios
- * Deportes deportes
- * Drama drama
- * Ecchi ecchi
- * Escolares escolares
- * Espacial espacial
- * Fantasía fantasia
- * Harem harem
- * Historico historico
- * Infantil infantil
- * Josei josei
- * Juegos juegos
- * Magia magia
- * Mecha mecha
- * Militar militar
- * Misterio misterio
- * Música musica
- * Parodia parodia
- * Policía policia
- * Psicológico psicologico
- * Recuentos de la vida recuentos-de-la-vida
- * Romance romance
- * Samurai samurai
- * Seinen seinen
- * Shoujo shoujo
- * Shounen shounen
- * Sobrenatural sobrenatural
- * Superpoderes superpoderes
- * Suspenso suspenso
- * Terror terror
- * Vampiros vampiros
- * Yaoi yaoi
- * Yuri yuri
- */
diff --git a/graphql/utils.ts b/graphql/utils.ts
deleted file mode 100644
index 5400bb8..0000000
--- a/graphql/utils.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import cloudscraper from "cloudscraper";
-import cheerio from "cheerio";
-import { GraphQLError } from "graphql";
-
-export const get = async (url: string, query?: string): Promise => {
- try {
- let res = await cloudscraper({
- url: `https://animeflv.net${url}` + (query ? `?${query}` : ""),
- method: "GET"
- });
- return res;
- } catch (error) {
- throw new GraphQLError("Anime do not exist");
- }
-};
-
-export const getCover = (animeId: number) =>
- `https://animeflv.net/uploads/animes/covers/${animeId}.jpg`;
-
-export const getBanner = (animeId: number) =>
- `https://animeflv.net/uploads/animes/banners/${animeId}.jpg`;
-
-export const getThumbnail = (animeId: number, episodeN: number) =>
- `https://cdn.animeflv.net/screenshots/${animeId}/${episodeN}/th_3.jpg`;
-
-export const imageUrlToBase64 = async url => {
- let res = await cloudscraper({
- url,
- method: "GET",
- encoding: null
- });
-
- return Buffer.from(res).toString("base64");
-};
-
-export const getAnime = async ({ slug }, ctx) => {
- if (ctx.animeBody) {
- return ctx.animeBody;
- } else {
- return await get(`/anime/${slug}`);
- }
-};
-
-export const explore = async ({ query, type, status, genre }) => {
- const body: string = await get(
- `/browse`,
- query ? `q=${query}` : generateQuery({ type, status, genre })
- );
-
- const $ = cheerio.load(body);
-
- return $("article.Anime").map((_, anime) => {
- const $anime = $(anime);
- const url = $anime
- .find("a.Button")
- .attr("href")
- .split("/");
- const image = $anime.find("div.Image img");
- const imageUrl = (image.attr("src") || image.attr("data-cfsrc")).split("/");
- const id = parseInt(imageUrl[imageUrl.length - 1].split(".")[0]);
-
- return {
- id,
- name: $anime.find("div.Title").text(),
- slug: url[url.length - 1],
- type: $anime
- .find("span.Type")
- .attr("class")
- .split(" ")[1],
- synopsis: $anime
- .find("div.Description p")
- .last()
- .text(),
- rating: parseFloat($anime.find("span.fa-star").text())
- };
- });
-};
-
-function generateQuery(object: object) {
- let accum = "";
- for (const key in object) {
- if (object[key] == null || object[key] == []) continue;
-
- for (const item of object[key]) {
- accum += `&${key}[]=${item}`;
- }
- }
- return accum;
-}
diff --git a/index.ts b/index.ts
new file mode 100644
index 0000000..f00a44b
--- /dev/null
+++ b/index.ts
@@ -0,0 +1,11 @@
+import { ApolloServer } from "apollo-server-micro";
+import schema from "./schema";
+
+const server = new ApolloServer({
+ schema,
+ introspection: true,
+ playground: true,
+ tracing: process.env.NODE_ENV === "development",
+});
+
+export default server.createHandler({ path: "/graphql" });
diff --git a/package.json b/package.json
index 187f9ca..57dd45d 100644
--- a/package.json
+++ b/package.json
@@ -1,23 +1,32 @@
{
"name": "animeflv-graphql",
- "version": "1.2.3",
+ "version": "2.0.0",
"description": "GraphQL API for AnimeFLV",
"main": "index.js",
"repository": "https://github.com/JuanM04/animeflv-graphql.git",
"author": "Juan Martín Seery ",
- "license": "MIT",
+ "license": "GPL-3.0",
+ "scripts": {
+ "dev": "vercel dev",
+ "generate:nexus": "ts-node --transpile-only schema",
+ "vercel-build": "yarn -s generate:nexus"
+ },
"dependencies": {
+ "@nexus/schema": "^0.17.0",
"apollo-server-micro": "^2.19.0",
"cheerio": "^1.0.0-rc.3",
"cloudscraper": "^4.5.0",
"graphql": "^15.4.0",
+ "graphql-iso-date": "^3.6.1",
"request": "^2.88.0"
},
"devDependencies": {
"@types/cheerio": "^0.22.22",
+ "@types/graphql-iso-date": "^3.4.0",
"@types/node": "^14.14.6",
"@types/request": "^2.48.5",
"@types/request-promise": "^4.1.46",
+ "ts-node": "^9.0.0",
"typescript": "^4.0.5"
}
}
diff --git a/schema/Anime.ts b/schema/Anime.ts
new file mode 100644
index 0000000..af25dd1
--- /dev/null
+++ b/schema/Anime.ts
@@ -0,0 +1,98 @@
+import { objectType } from "@nexus/schema";
+import cheerio from "cheerio";
+
+import { AnimeType, AnimeGenre, AnimeStatus } from "./enums";
+import Episode from "./Episode";
+
+import { imageUrlToBase64, get, ANIMEFLV_URL } from "../utils/helpers";
+import { NexusGenEnums } from "../generated/nexus";
+
+const getAnime = async (slug: string | null | undefined, ctx: any) => {
+ if (ctx.animeBody) {
+ return ctx.animeBody;
+ } else {
+ return await get(`anime/${slug}`);
+ }
+};
+
+const Anime = objectType({
+ name: "Anime",
+ definition(t) {
+ t.int("id", { deprecation: "Better use `slug`" });
+ t.string("slug");
+ t.string("name");
+ t.field("type", { type: AnimeType });
+ t.field("status", {
+ type: AnimeStatus,
+ resolve: async ({ slug }, _, ctx) => {
+ const body = await getAnime(slug, ctx);
+ const $ = cheerio.load(body);
+
+ let status = 1;
+ if ($("p.AnmStts").hasClass("A")) status = 2;
+ else if ($("p.AnmStts").hasClass("B")) status = 3;
+
+ return status as NexusGenEnums["AnimeStatus"];
+ },
+ });
+ t.string("cover", {
+ resolve: (anime) =>
+ imageUrlToBase64(
+ `${ANIMEFLV_URL}/uploads/animes/covers/${anime.id}.jpg`
+ ),
+ });
+ t.string("banner", {
+ resolve: (anime) =>
+ imageUrlToBase64(
+ `${ANIMEFLV_URL}/uploads/animes/banners/${anime.id}.jpg`
+ ),
+ });
+ t.string("synopsis");
+ t.float("rating");
+ t.list.field("genres", {
+ type: AnimeGenre,
+ resolve: async ({ slug }, _, ctx) => {
+ const body = await getAnime(slug, ctx);
+ const $ = cheerio.load(body);
+
+ return $("nav.Nvgnrs a")
+ .toArray()
+ .map((_, a) => {
+ const $a = $(a);
+ return $a.attr("href")?.split("=")[1];
+ }) as NexusGenEnums["AnimeGenre"][];
+ },
+ });
+ t.list.field("episodes", {
+ type: Episode,
+ resolve: async ({ id, slug }, _, ctx) => {
+ const body = await getAnime(slug, ctx);
+
+ const rawEpisodes = body
+ .split("var episodes = ", 2)[1]
+ .split(";", 2)[0];
+
+ return JSON.parse(rawEpisodes).map((arr: number[]) => ({
+ id: arr[1],
+ n: arr[0],
+ anime: { id, slug },
+ }));
+ },
+ });
+ t.date("nextEpisode", {
+ nullable: true,
+ resolve: async ({ slug }, _, ctx) => {
+ const body = await getAnime(slug, ctx);
+
+ const rawInfo = body
+ .split("var anime_info = ", 2)[1]
+ .replace(/'/g, "'")
+ .split(";", 2)[0];
+
+ return JSON.parse(rawInfo)[3] || null;
+ },
+ });
+ },
+});
+
+export default Anime;
diff --git a/schema/Episode.ts b/schema/Episode.ts
new file mode 100644
index 0000000..4446a7a
--- /dev/null
+++ b/schema/Episode.ts
@@ -0,0 +1,22 @@
+import { objectType } from "@nexus/schema";
+
+import EpisodeAnime from "./EpisodeAnime";
+
+import { imageUrlToBase64 } from "../utils/helpers";
+
+const Episode = objectType({
+ name: "Episode",
+ definition(t) {
+ t.int("id", { deprecation: "Better use `n`" });
+ t.float("n");
+ t.field("anime", { type: EpisodeAnime });
+ t.string("thumbnail", {
+ resolve: async (episode) =>
+ imageUrlToBase64(
+ `https://cdn.animeflv.net/screenshots/${episode.id}/${episode.id}/th_3.jpg`
+ ),
+ });
+ },
+});
+
+export default Episode;
diff --git a/schema/EpisodeAnime.ts b/schema/EpisodeAnime.ts
new file mode 100644
index 0000000..9e61fde
--- /dev/null
+++ b/schema/EpisodeAnime.ts
@@ -0,0 +1,11 @@
+import { objectType } from "@nexus/schema";
+
+const EpisodeAnime = objectType({
+ name: "EpisodeAnime",
+ definition(t) {
+ t.int("id", { deprecation: "Better use `slug`" });
+ t.string("slug");
+ },
+});
+
+export default EpisodeAnime;
diff --git a/schema/EpisodeSource.ts b/schema/EpisodeSource.ts
new file mode 100644
index 0000000..61df7ee
--- /dev/null
+++ b/schema/EpisodeSource.ts
@@ -0,0 +1,12 @@
+import { objectType } from "@nexus/schema";
+
+const EpisodeSource = objectType({
+ name: "EpisodeSource",
+ definition(t) {
+ t.string("server");
+ t.string("title");
+ t.string("code");
+ },
+});
+
+export default EpisodeSource;
diff --git a/schema/Query.ts b/schema/Query.ts
new file mode 100644
index 0000000..90165e9
--- /dev/null
+++ b/schema/Query.ts
@@ -0,0 +1,141 @@
+import { objectType, stringArg, arg, floatArg } from "@nexus/schema";
+import cheerio from "cheerio";
+
+import { AnimeGenre, AnimeStatus, AnimeType, SourcesType } from "./enums";
+import Anime from "./Anime";
+import EpisodeSource from "./EpisodeSource";
+
+import { get } from "../utils/helpers";
+
+export const explore = async ({
+ query,
+ type,
+ status,
+ genre,
+}: {
+ query?: string;
+ type?: any;
+ status?: any;
+ genre?: any;
+}) => {
+ const body: string = await get(
+ `browse`,
+ query ? `q=${query}` : generateQuery({ type, status, genre })
+ );
+
+ const $ = cheerio.load(body);
+
+ return $("article.Anime")
+ .toArray()
+ .map((anime) => {
+ const $anime = $(anime);
+ const url = $anime.find("a.Button").attr("href")?.split("/") as string[];
+ const image = $anime.find("div.Image img");
+ const imageUrl = (image.attr("src") || image.attr("data-cfsrc"))?.split(
+ "/"
+ ) as string[];
+ const id = parseInt(imageUrl[imageUrl.length - 1].split(".")[0]);
+
+ return {
+ id,
+ name: $anime.find("div.Title").text(),
+ slug: url[url.length - 1],
+ type: $anime.find("span.Type").attr("class")?.split(" ")[1],
+ synopsis: $anime.find("div.Description p").last().text(),
+ rating: parseFloat($anime.find("span.fa-star").text()),
+ };
+ });
+};
+
+function generateQuery(object: any) {
+ let accum = "";
+ for (const key in object) {
+ if (object[key] == null || object[key] == []) continue;
+
+ for (const item of object[key]) {
+ accum += `&${key}[]=${item}`;
+ }
+ }
+ return accum;
+}
+
+const Query = objectType({
+ name: "Query",
+ definition(t) {
+ t.field("anime", {
+ type: Anime,
+ nullable: true,
+ args: {
+ slug: stringArg({ required: true }),
+ },
+ // @ts-ignore
+ resolve: async (_, args, ctx) => {
+ const body = await get(`anime/${args.slug}`);
+ ctx.animeBody = body;
+
+ const rawInfo = body
+ .split("var anime_info = ", 2)[1]
+ .replace(/'/g, "'")
+ .split(";", 2)[0];
+ const info = JSON.parse(rawInfo);
+
+ const $ = cheerio.load(body);
+
+ return {
+ id: parseInt(info[0]),
+ name: info[1],
+ slug: args.slug,
+ type: $("span.Type")?.attr("class")?.split(" ")?.[1],
+ synopsis: $("div.Description p").text(),
+ rating: parseFloat($("#votes_prmd").text()),
+ };
+ },
+ });
+ t.list.field("search", {
+ type: Anime,
+ args: {
+ query: stringArg({ required: true }),
+ },
+ // @ts-ignore
+ resolve: (_, args) => explore(args),
+ });
+ t.list.field("explore", {
+ type: Anime,
+ args: {
+ type: arg({
+ type: AnimeType,
+ list: true,
+ }),
+ status: arg({
+ type: AnimeStatus,
+ list: true,
+ }),
+ genre: arg({
+ type: AnimeGenre,
+ list: true,
+ }),
+ },
+ // @ts-ignore
+ resolve: (_, args) => explore(args),
+ });
+ t.list.field("episodeSources", {
+ type: EpisodeSource,
+ nullable: true,
+ args: {
+ animeSlug: stringArg({ required: true }),
+ episodeN: floatArg({ required: true }),
+ type: arg({ type: SourcesType }),
+ },
+ resolve: async (_, { episodeN, animeSlug, type }) => {
+ const body = await get(`ver/${animeSlug}-${episodeN}`);
+
+ const rawSources = body.split("var videos = ", 2)[1].split(";", 2)[0];
+ const sources = JSON.parse(rawSources);
+
+ return sources[type || "SUB"];
+ },
+ });
+ },
+});
+
+export default Query;
diff --git a/graphql/resolvers/scalars-enums.ts b/schema/enums.ts
similarity index 73%
rename from graphql/resolvers/scalars-enums.ts
rename to schema/enums.ts
index c355e9a..38c6a29 100644
--- a/graphql/resolvers/scalars-enums.ts
+++ b/schema/enums.ts
@@ -1,18 +1,8 @@
-import { GraphQLScalarType } from "graphql";
+import { enumType } from "@nexus/schema";
-export default {
- AnimeType: {
- TV: "tv",
- MOVIE: "movie",
- SPECIAL: "special",
- OVA: "ova"
- },
- AnimeStatus: {
- RUNNING: 1,
- ENDED: 2,
- SOON: 3
- },
- AnimeGenre: {
+export const AnimeGenre = enumType({
+ name: "AnimeGenre",
+ members: {
ACCION: "accion",
ARTES_MARCIALES: "artes-marciales",
AVENTURA: "aventura",
@@ -52,16 +42,30 @@ export default {
TERROR: "terror",
VAMPIROS: "vampiros",
YAOI: "yaoi",
- YURI: "yuri"
+ YURI: "yuri",
},
- Date: new GraphQLScalarType({
- name: "Date",
- description: "Date scalar type YYYY-MM-DD",
- parseValue(string) {
- return string;
- },
- serialize(string) {
- return string;
- }
- })
-};
+});
+
+export const AnimeStatus = enumType({
+ name: "AnimeStatus",
+ members: {
+ RUNNING: 1,
+ ENDED: 2,
+ SOON: 3,
+ },
+});
+
+export const AnimeType = enumType({
+ name: "AnimeType",
+ members: {
+ TV: "tv",
+ MOVIE: "movie",
+ SPECIAL: "special",
+ OVA: "ova",
+ },
+});
+
+export const SourcesType = enumType({
+ name: "SourcesType",
+ members: ["SUB", "LAT"],
+});
diff --git a/schema/index.ts b/schema/index.ts
new file mode 100644
index 0000000..81f86a6
--- /dev/null
+++ b/schema/index.ts
@@ -0,0 +1,17 @@
+import { makeSchema } from "@nexus/schema";
+import { join } from "path";
+
+import * as scalars from "./scalars";
+import * as enums from "./enums";
+import Anime from "./Anime";
+import Episode from "./Episode";
+import EpisodeSource from "./EpisodeSource";
+import Query from "./Query";
+
+export default makeSchema({
+ types: { ...scalars, ...enums, Anime, Episode, EpisodeSource, Query },
+ outputs: {
+ schema: join(process.cwd(), "generated", "schema.graphql"),
+ typegen: join(process.cwd(), "generated", "nexus.d.ts"),
+ },
+});
diff --git a/schema/scalars.ts b/schema/scalars.ts
new file mode 100644
index 0000000..b19c979
--- /dev/null
+++ b/schema/scalars.ts
@@ -0,0 +1,4 @@
+import { asNexusMethod } from "@nexus/schema";
+import { GraphQLDate } from "graphql-iso-date";
+
+export const Date = asNexusMethod(GraphQLDate, "date", "Date");
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1eef507
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2018",
+ "module": "commonjs",
+ "lib": ["esnext"],
+ "strict": true,
+ "rootDir": ".",
+ "outDir": "dist",
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "baseUrl": "."
+ }
+}
\ No newline at end of file
diff --git a/utils/helpers.ts b/utils/helpers.ts
new file mode 100644
index 0000000..2a4ea84
--- /dev/null
+++ b/utils/helpers.ts
@@ -0,0 +1,26 @@
+import cloudscraper from "cloudscraper";
+import { GraphQLError } from "graphql";
+
+export const ANIMEFLV_URL = "https://www3.animeflv.net/";
+
+export const get = async (url: string, query?: string): Promise => {
+ try {
+ let res = await cloudscraper({
+ url: ANIMEFLV_URL + url + (query ? `?${query}` : ""),
+ method: "GET",
+ });
+ return res;
+ } catch (error) {
+ throw new GraphQLError("Anime do not exist");
+ }
+};
+
+export const imageUrlToBase64 = async (url: string) => {
+ let res = await cloudscraper({
+ url,
+ method: "GET",
+ encoding: null,
+ });
+
+ return Buffer.from(res).toString("base64");
+};
diff --git a/vercel.json b/vercel.json
index bb1bf93..1722f9a 100644
--- a/vercel.json
+++ b/vercel.json
@@ -2,7 +2,17 @@
"version": 2,
"name": "animeflv",
"builds": [
- { "src": "graphql/index.ts", "use": "@now/node" },
+ { "src": "index.ts", "use": "@now/node" },
{ "src": "index.html", "use": "@now/static" }
+ ],
+ "routes": [
+ {
+ "src": "/graphql",
+ "dest": "/index.ts"
+ },
+ {
+ "src": "/",
+ "dest": "/index.html"
+ }
]
}
diff --git a/yarn.lock b/yarn.lock
index cdc6e23..a4f27b5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -35,6 +35,14 @@
dependencies:
xss "^1.0.6"
+"@nexus/schema@^0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@nexus/schema/-/schema-0.17.0.tgz#5b6ec9630a9cd17f64a7ffc999378fa9835df3a9"
+ integrity sha512-X/DdvXGDNw2VMi1F2un7JRY17yFbuxXHM+U4ViejAf9jCkhlasdljK3TLTqA5wSFfriFIx0eWtYHkUeOvIjLkg==
+ dependencies:
+ iterall "^1.2.2"
+ tslib "^1.9.3"
+
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@@ -161,6 +169,13 @@
dependencies:
"@types/node" "*"
+"@types/graphql-iso-date@^3.4.0":
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/@types/graphql-iso-date/-/graphql-iso-date-3.4.0.tgz#b6710b21e3b0bfdb1a0529b285148d98eac18b1f"
+ integrity sha512-V3jITHTsoI2E8TGt9+/HPDz6LWt3z9/HYnPJYWI6WwiLRexsngg7KzaQlCgQkA4jkEbGPROUD0hJFc9F02W9WA==
+ dependencies:
+ graphql "^15.1.0"
+
"@types/graphql-upload@^8.0.0":
version "8.0.3"
resolved "https://registry.yarnpkg.com/@types/graphql-upload/-/graphql-upload-8.0.3.tgz#b371edb5f305a2a1f7b7843a890a2a7adc55c3ec"
@@ -481,6 +496,11 @@ arg@4.1.0:
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
@@ -549,6 +569,11 @@ boom@7.x.x:
dependencies:
hoek "6.x.x"
+buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
busboy@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b"
@@ -673,6 +698,11 @@ dicer@0.3.0:
dependencies:
streamsearch "0.1.2"
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
dom-serializer@0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -854,6 +884,11 @@ graphql-extensions@^0.12.6:
apollo-server-env "^2.4.5"
apollo-server-types "^0.6.1"
+graphql-iso-date@^3.6.1:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz#bd2d0dc886e0f954cbbbc496bbf1d480b57ffa96"
+ integrity sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q==
+
graphql-tag@^2.9.2:
version "2.10.3"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03"
@@ -887,7 +922,7 @@ graphql@^14.5.3:
dependencies:
iterall "^1.2.2"
-graphql@^15.4.0:
+graphql@^15.1.0, graphql@^15.4.0:
version "15.4.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.4.0.tgz#e459dea1150da5a106486ba7276518b5295a4347"
integrity sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA==
@@ -1080,6 +1115,11 @@ lru-cache@^5.0.0:
dependencies:
yallist "^3.0.2"
+make-error@^1.1.1:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
micro@^9.3.2:
version "9.3.4"
resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.4.tgz#745a494e53c8916f64fb6a729f8cbf2a506b35ad"
@@ -1274,6 +1314,19 @@ sha.js@^2.4.11:
inherits "^2.0.1"
safe-buffer "^5.0.1"
+source-map-support@^0.5.17:
+ version "0.5.19"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
+ integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@@ -1363,6 +1416,17 @@ ts-invariant@^0.4.0:
dependencies:
tslib "^1.9.3"
+ts-node@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3"
+ integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==
+ dependencies:
+ arg "^4.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ source-map-support "^0.5.17"
+ yn "3.1.1"
+
tslib@^1.10.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
@@ -1458,6 +1522,11 @@ yallist@^3.0.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
zen-observable-ts@^0.8.20:
version "0.8.20"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz#44091e335d3fcbc97f6497e63e7f57d5b516b163"