From cbaf06a8de28504c6be9ea70357adb50f986bad0 Mon Sep 17 00:00:00 2001 From: JuanM04 Date: Sat, 7 Nov 2020 18:16:14 -0300 Subject: [PATCH] =?UTF-8?q?v2=20=E2=80=94=20Nexus=20Schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- README.md | 4 - graphql/index.ts | 12 -- graphql/resolvers/index.ts | 7 - graphql/resolvers/queries.ts | 92 ----------- graphql/type-defs.ts | 154 ------------------ graphql/utils.ts | 89 ---------- index.ts | 11 ++ package.json | 13 +- schema/Anime.ts | 98 +++++++++++ schema/Episode.ts | 22 +++ schema/EpisodeAnime.ts | 11 ++ schema/EpisodeSource.ts | 12 ++ schema/Query.ts | 141 ++++++++++++++++ .../scalars-enums.ts => schema/enums.ts | 56 ++++--- schema/index.ts | 17 ++ schema/scalars.ts | 4 + tsconfig.json | 13 ++ utils/helpers.ts | 26 +++ vercel.json | 12 +- yarn.lock | 71 +++++++- 21 files changed, 481 insertions(+), 389 deletions(-) delete mode 100644 graphql/index.ts delete mode 100644 graphql/resolvers/index.ts delete mode 100644 graphql/resolvers/queries.ts delete mode 100644 graphql/type-defs.ts delete mode 100644 graphql/utils.ts create mode 100644 index.ts create mode 100644 schema/Anime.ts create mode 100644 schema/Episode.ts create mode 100644 schema/EpisodeAnime.ts create mode 100644 schema/EpisodeSource.ts create mode 100644 schema/Query.ts rename graphql/resolvers/scalars-enums.ts => schema/enums.ts (73%) create mode 100644 schema/index.ts create mode 100644 schema/scalars.ts create mode 100644 tsconfig.json create mode 100644 utils/helpers.ts 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 @@ GitHub - - - GitHub Workflow Status -
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"