diff --git a/.eslintrc.json b/.eslintrc.json index d0c38f8d..5e3ef36b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,22 +1,16 @@ { - "env": { - "browser": true, - "es2021": true - }, - "parser": "@typescript-eslint/parser", - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module", - "project": ["./tsconfig.json"] - }, - "plugins": [ - "@typescript-eslint" - ], - "root": true, - "rules": { - } + "env": { + "browser": true, + "es2021": true + }, + "parser": "@typescript-eslint/parser", + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": ["./tsconfig.json"] + }, + "plugins": ["@typescript-eslint"], + "root": true, + "rules": {} } diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 00000000..2abee68d Binary files /dev/null and b/bun.lockb differ diff --git a/jest.config.js b/jest.config.js index 260c66e6..f590a7c3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,10 +1,8 @@ module.exports = { - roots: [ - "/src", - ], + roots: ["/src"], testMatch: [ "**/__tests__/**/*.+(ts|tsx|js)", - "**/?(*.)+(spec|test).+(ts|tsx|js)" + "**/?(*.)+(spec|test).+(ts|tsx|js)", ], transform: { "^.+\\.(ts|tsx)$": [ @@ -19,4 +17,4 @@ module.exports = { "!**/*.d.ts", "!**/node_modules/**", ], -} +}; diff --git a/package-lock.json b/package-lock.json index 1e76b13c..13d5724a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "axios": "^1.3.4", "cheerio": "^1.0.0-rc.12", "cors": "^2.8.5", + "crypto-js": "^4.2.0", "express": "^4.18.2", "helmet": "^6.0.1", "morgan": "^1.10.0", @@ -4651,6 +4652,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "node_modules/crypto-random-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", diff --git a/package.json b/package.json index 5745beb0..8d06d548 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "src/index.ts", "scripts": { "start": "node ./build/src/index.js", + "start:pm2": "pm2 start ./build/src/index.js", "server": "nodemon src/index.ts", "start:vercel": "node build/index.js", "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"", @@ -47,6 +48,7 @@ "axios": "^1.3.4", "cheerio": "^1.0.0-rc.12", "cors": "^2.8.5", + "crypto-js": "^4.2.0", "express": "^4.18.2", "helmet": "^6.0.1", "morgan": "^1.10.0", diff --git a/puppeteer.config.js b/puppeteer.config.js index 3996fd8b..bf95408c 100644 --- a/puppeteer.config.js +++ b/puppeteer.config.js @@ -4,5 +4,5 @@ const { Configuration } = require("puppeteer"); /** @type {Configuration} */ module.exports = { - cacheDirectory: join(os.homedir(), ".cache", "puppeteer") -} + cacheDirectory: join(os.homedir(), ".cache", "puppeteer"), +}; diff --git a/src/index.ts b/src/index.ts index 2992c175..f341031b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,23 +3,22 @@ import morgan from "morgan"; import index from "../src/routes/app"; import providersList from "../src/routes/providers"; import helmet from "helmet"; -import cors from 'cors' +import cors from "cors"; /* Anime */ import flv from "../src/routes/v1/anime/animeflv/AnimeflvRoutes"; import latinhd from "../src/routes/v1/anime/animelatinohd/AnimeLatinoHDRoutes"; -import gogoanime from "../src/routes/v1/anime/gogoanime/GogoAnimeRoute"; -import zoro from "../src/routes/v1/anime/zoro/ZoroRoutes"; import monoschinos from "../src/routes/v1/anime/monoschinos/MonosChinosRoute"; -import tioanime from '../src/routes/v1/anime/tioanime/TioAnimeRoute' +import tioanime from "../src/routes/v1/anime/tioanime/TioAnimeRoute"; import WcoStream from "../src/routes/v1/anime/wcostream/wcostreamRoutes"; import AnimeBlix from "../src/routes/v1/anime/animeblix/AnimeBlixRoutes"; import Animevostfr from "../src/routes/v1/anime/animevostfr/AnimevostfrRoutes"; +import hentaila from "../src/routes/v1/anime/hentaila/HentaiLaRoutes"; +import HentaiHaven from "../src/routes/v1/anime/hentaihaven/HentaiHavenRoutes"; /* Manga */ import comick from "../src/routes/v1/manga/comick/ComickRoutes"; import inmanga from "../src/routes/v1/manga/inmanga/InmangaRoutes"; -import nhentai from "../src/routes/v1/manga/nhentai/NhentaiRoutes" import mangareader from "../src/routes/v1/manga/mangareader/MangaReaderRoutes"; import manganelo from "../src/routes/v1/manga/manganelo/ManganeloRoutes"; @@ -33,34 +32,30 @@ app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(morgan("dev")); app.use(helmet()); -app.use(cors()) +app.use(cors()); //routes /*anime*/ app.use(flv); app.use(latinhd); -app.use(gogoanime); app.use(monoschinos); -app.use(zoro); -app.use(tioanime) +app.use(tioanime); app.use(WcoStream); app.use(AnimeBlix); app.use(Animevostfr); +app.use(hentaila); +app.use(HentaiHaven); /* anime */ - /*Manga*/ app.use(comick); app.use(inmanga); -app.use(nhentai) app.use(mangareader); app.use(manganelo); /*Manga*/ - - /*error */ interface ErrorResponse { @@ -102,7 +97,6 @@ app.use((err, res, _next) => { res.status(response.error.status).send(response); }); - app.listen(port, () => { console.log(`Servidor iniciado en el puerto ${port} listo para trabajar :)`); }); diff --git a/src/models/AnimeScraperModel.ts b/src/models/AnimeScraperModel.ts new file mode 100644 index 00000000..f545bf7e --- /dev/null +++ b/src/models/AnimeScraperModel.ts @@ -0,0 +1,8 @@ +import type { AnimeMedia } from "@animetypes/anime"; +import type { Episode } from "@animetypes/episode"; +import type { IAnimeResult } from "@animetypes/search"; +import { BaseScraperModel } from "./BaseScraperModel"; + +export abstract class AnimeScraperModel extends BaseScraperModel { + public abstract GetEpisodeServers(...args: unknown[]): Promise; +} diff --git a/src/models/BaseScraperModel.ts b/src/models/BaseScraperModel.ts new file mode 100644 index 00000000..4c8378ad --- /dev/null +++ b/src/models/BaseScraperModel.ts @@ -0,0 +1,9 @@ +import type { BaseMedia, BaseResult } from "@animetypes/base"; +import type { IResultSearch } from "@animetypes/search"; + +export abstract class BaseScraperModel { + public abstract readonly url: string; + + public abstract GetItemInfo(item: string): Promise; + public abstract GetItemByFilter(...args: unknown[]): Promise>; +} diff --git a/src/models/MangaScraperModel.ts b/src/models/MangaScraperModel.ts new file mode 100644 index 00000000..c648b5be --- /dev/null +++ b/src/models/MangaScraperModel.ts @@ -0,0 +1,6 @@ +import type { MangaMedia, MangaChapter, MangaVolume, IMangaResult } from "@animetypes/manga"; +import { BaseScraperModel } from "./BaseScraperModel"; + +export abstract class MangaScraperModel extends BaseScraperModel { + public abstract GetMangaChapters(...args: unknown[]): Promise +} diff --git a/src/routes/providers.ts b/src/routes/providers.ts index 610416c2..be419ce5 100644 --- a/src/routes/providers.ts +++ b/src/routes/providers.ts @@ -11,7 +11,7 @@ interface ProviderScraper { status: number | string; icon: string; url: string; - apiID: string + apiID: string; favicon: string | string[]; } diff --git a/src/routes/v1/anime/9anime/9animeRoute.js b/src/routes/v1/anime/9anime/9animeRoute.js index ab752711..91dded82 100644 --- a/src/routes/v1/anime/9anime/9animeRoute.js +++ b/src/routes/v1/anime/9anime/9animeRoute.js @@ -10,4 +10,4 @@ r.get("/anime/9anime/name/:name", (req, res) => { }); }); -export default r +export default r; diff --git a/src/routes/v1/anime/animeblix/AnimeBlixRoutes.ts b/src/routes/v1/anime/animeblix/AnimeBlixRoutes.ts index ed6dcb59..e7172a56 100644 --- a/src/routes/v1/anime/animeblix/AnimeBlixRoutes.ts +++ b/src/routes/v1/anime/animeblix/AnimeBlixRoutes.ts @@ -6,27 +6,32 @@ const router = Router(); // Filter router.get("/anime/animeblix/filter", async (req, res) => { - const { search, type, page, year, genre } = req.query - - const data = await Anime.GetAnimeByFilter(search as string, type as unknown as number, page as unknown as number, year as string, genre as string) - res.send(data) + const { search, type, page, year, genre } = req.query; + + const data = await Anime.GetItemByFilter( + search as string, + type as unknown as number, + page as unknown as number, + year as string, + genre as string, + ); + res.send(data); }); // Anime Info +(Episodes list) router.get("/anime/animeblix/name/:name", async (req, res) => { - - const { name } = req.params - const data = await Anime.GetAnimeInfo(name.includes("ver-")? name.replace("ver-","") : name) - res.send(data) - + const { name } = req.params; + const data = await Anime.GetItemInfo( + name.includes("ver-") ? name.replace("ver-", "") : name, + ); + res.send(data); }); // Episode Info +(Video Servers) router.get("/anime/animeblix/episode/:episode", async (req, res) => { - const { episode } = req.params - const data = await Anime.GetEpisodeServers(episode) - res.send(data) - + const { episode } = req.params; + const data = await Anime.GetEpisodeServers(episode); + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/anime/animeflv/AnimeflvRoutes.ts b/src/routes/v1/anime/animeflv/AnimeflvRoutes.ts index cee1e705..139eb54a 100644 --- a/src/routes/v1/anime/animeflv/AnimeflvRoutes.ts +++ b/src/routes/v1/anime/animeflv/AnimeflvRoutes.ts @@ -6,19 +6,20 @@ import { StatusAnimeflv, OrderAnimeflv, } from "../../../../scraper/sites/anime/animeflv/animeflv_helper"; +import { ScraperErrorResponse } from "utils/ScraperError"; const r = Router(); - //anime info r.get("/anime/flv/name/:name", async (req, res) => { try { const { name } = req.params; const flv = new AnimeFlv(); - const animeInfo = await flv.GetAnimeInfo(name); + const animeInfo = await flv.GetItemInfo(name); res.send(animeInfo); } catch (error) { - console.log(error); - res.status(500).send(error); + if (error instanceof ScraperErrorResponse) { + res.status(404).send(error); + } } }); @@ -30,16 +31,17 @@ r.get("/anime/flv/episode/:episode", async (req, res) => { const animeInfo = await flv.GetEpisodeServers(episode); res.send(animeInfo); } catch (error) { - console.log(error); - res.status(500).send(error); + if (error instanceof ScraperErrorResponse) { + res.status(404).send(error); + } } }); - + //filter r.get("/anime/flv/filter", async (req, res) => { try { const gen = req.query.gen as Genres; - const date = req.query.date as string; + const year = req.query.year as string; const type = req.query.type as TypeAnimeflv; const status = req.query.status as StatusAnimeflv; const ord = req.query.ord as OrderAnimeflv; @@ -47,11 +49,20 @@ r.get("/anime/flv/filter", async (req, res) => { const title = req.query.title as string; const flv = new AnimeFlv(); - const animeInfo = await flv.Filter(gen, date, type, status, ord, page, title); + const animeInfo = await flv.GetItemByFilter( + gen, + year, + type, + status, + ord, + page, + title + ); res.send(animeInfo); } catch (error) { - console.log(error); - res.status(500).send(error); + if (error instanceof ScraperErrorResponse) { + res.status(404).send(error); + } } }); diff --git a/src/routes/v1/anime/animelatinohd/AnimeLatinoHDRoutes.ts b/src/routes/v1/anime/animelatinohd/AnimeLatinoHDRoutes.ts index 9632b116..35836c61 100644 --- a/src/routes/v1/anime/animelatinohd/AnimeLatinoHDRoutes.ts +++ b/src/routes/v1/anime/animelatinohd/AnimeLatinoHDRoutes.ts @@ -5,28 +5,31 @@ const router = Router(); // Filter router.get("/anime/animelatinohd/filter", async (req, res) => { - const { search, type, page, year, genre } = req.query - - const data = await Anime.GetAnimeByFilter(search as string, type as unknown as number, page as unknown as number, year as string, genre as string) - res.send(data) + const { search, type, page, year, genre } = req.query; + + const data = await Anime.GetItemByFilter( + search as string, + type as unknown as number, + page as unknown as number, + year as string, + genre as string, + ); + res.send(data); }); // Anime Info +(Episodes list) router.get("/anime/animelatinohd/name/:name", async (req, res) => { - - const { name } = req.params - const data = await Anime.GetAnimeInfo(name) - res.send(data) - + const { name } = req.params; + const data = await Anime.GetItemInfo(name); + res.send(data); }); // Episode Info +(Video Servers) router.get("/anime/animelatinohd/episode/:episode", async (req, res) => { - const { lang } = req.query - const { episode } = req.params - const data = await Anime.GetEpisodeServers(episode, lang as string) - res.send(data) - + const { lang } = req.query; + const { episode } = req.params; + const data = await Anime.GetEpisodeServers(episode, lang as string); + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/anime/animevostfr/AnimevostfrRoutes.ts b/src/routes/v1/anime/animevostfr/AnimevostfrRoutes.ts index 3bdd76f2..987885aa 100644 --- a/src/routes/v1/anime/animevostfr/AnimevostfrRoutes.ts +++ b/src/routes/v1/anime/animevostfr/AnimevostfrRoutes.ts @@ -5,27 +5,27 @@ const router = Router(); // Filter router.get("/anime/animevostfr/filter", async (req, res) => { - const { search, type, page, year, genre } = req.query + const { search, page } = req.query; - const data = await Anime.GetAnimeByFilter(search as string, type as unknown as number, page as unknown as number, year as string, genre as string) - res.send(data) + const data = await Anime.GetItemByFilter( + search as string, + page as unknown as number + ); + res.send(data); }); // Anime Info +(Episodes list) router.get("/anime/animevostfr/name/:name", async (req, res) => { - - const { name } = req.params - const data = await Anime.GetAnimeInfo(name) - res.send(data) - + const { name } = req.params; + const data = await Anime.GetItemInfo(name); + res.send(data); }); // Episode Info +(Video Servers) router.get("/anime/animevostfr/episode/:episode", async (req, res) => { - const { episode } = req.params - const data = await Anime.GetEpisodeServers(episode) - res.send(data) - + const { episode } = req.params; + const data = await Anime.GetEpisodeServers(episode); + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/anime/gogoanime/GogoAnimeRoute.ts b/src/routes/v1/anime/gogoanime/GogoAnimeRoute.ts index a745d1e7..df6e1657 100644 --- a/src/routes/v1/anime/gogoanime/GogoAnimeRoute.ts +++ b/src/routes/v1/anime/gogoanime/GogoAnimeRoute.ts @@ -1,4 +1,4 @@ -import { Router } from "express"; +/* import { Router } from "express"; import { GogoanimeInfo, GogoanimeServer, @@ -63,3 +63,4 @@ r.get("/anime/gogoanime/filter", async (req, res) => { }); export default r; + */ diff --git a/src/routes/v1/anime/hentaihaven/HentaiHavenRoutes.ts b/src/routes/v1/anime/hentaihaven/HentaiHavenRoutes.ts new file mode 100644 index 00000000..8bc25d2d --- /dev/null +++ b/src/routes/v1/anime/hentaihaven/HentaiHavenRoutes.ts @@ -0,0 +1,31 @@ +import { Router } from "express"; +import { HentaiHaven } from "../../../../scraper/sites/anime/hentaihaven/HentaiHaven"; +const Anime = new HentaiHaven(); +const router = Router(); + +// Filter +router.get("/anime/hentaihaven/filter", async (req, res) => { + const { search, page } = req.query; + + const data = await Anime.GetItemByFilter( + search as string, + page as unknown as number + ); + res.send(data); +}); + +// Anime Info +(Episodes list) +router.get("/anime/hentaihaven/name/:name", async (req, res) => { + const { name } = req.params; + const data = await Anime.GetItemInfo(name); + res.send(data); +}); + +// Episode Info +(Video Servers) +router.get("/anime/hentaihaven/episode/:episode", async (req, res) => { + const { episode } = req.params; + const data = await Anime.GetEpisodeServers(episode); + res.send(data); +}); + +export default router; diff --git a/src/routes/v1/anime/hentaila/HentaiLaRoutes.ts b/src/routes/v1/anime/hentaila/HentaiLaRoutes.ts new file mode 100644 index 00000000..4cf36bf8 --- /dev/null +++ b/src/routes/v1/anime/hentaila/HentaiLaRoutes.ts @@ -0,0 +1,30 @@ +import { Router } from "express"; +import { HentaiLa } from "../../../../scraper/sites/anime/hentaila/HentaiLa"; +const Anime = new HentaiLa(); +const router = Router(); + +// Filter +router.get("/anime/hentaila/filter", async (req, res) => { + const { search } = req.query; + + const data = await Anime.GetItemByFilter( + search as string + ); + res.send(data); +}); + +// Anime Info +(Episodes list) +router.get("/anime/hentaila/name/:name", async (req, res) => { + const { name } = req.params; + const data = await Anime.GetItemInfo(name); + res.send(data); +}); + +// Episode Info +(Video Servers) +router.get("/anime/hentaila/episode/:episode", async (req, res) => { + const { episode } = req.params; + const data = await Anime.GetEpisodeServers(episode); + res.send(data); +}); + +export default router; diff --git a/src/routes/v1/anime/monoschinos/MonosChinosRoute.ts b/src/routes/v1/anime/monoschinos/MonosChinosRoute.ts index eb10e7fb..4e9ede99 100644 --- a/src/routes/v1/anime/monoschinos/MonosChinosRoute.ts +++ b/src/routes/v1/anime/monoschinos/MonosChinosRoute.ts @@ -9,7 +9,7 @@ r.get("/anime/monoschinos/name/:name", async (req, res) => { const { name } = req.params; const monos = new Monoschinos(); const animeInfo = await monos.getAnime( - `https://monoschinos2.com/anime/${name}` + `https://monoschinos2.com/anime/${name}`, ); res.send(animeInfo); } catch (error) { @@ -24,7 +24,7 @@ r.get("/anime/monoschinos/episode/:episode", async (req, res) => { const { episode } = req.params; const monos = new Monoschinos(); const animeInfo = await monos.getEpisodeServers( - `https://monoschinos2.com/ver/${episode}` + `https://monoschinos2.com/ver/${episode}`, ); res.send(animeInfo); } catch (error) { @@ -36,14 +36,13 @@ r.get("/anime/monoschinos/episode/:episode", async (req, res) => { //filter r.get("/anime/monoschinos/filter", async (req, res) => { try { - const title = req.query.title as string + const title = req.query.title as string; const cat = req.query.category as string; const gen = req.query.gen as string; const year = req.query.year as string; - const letter = req.query.letter as string; const monos = new Monoschinos(); - const animeInfo = await monos.filter(title, cat, gen, year, letter); + const animeInfo = await monos.filter(title, cat, gen, year); res.send(animeInfo); } catch (error) { console.log(error); @@ -51,4 +50,23 @@ r.get("/anime/monoschinos/filter", async (req, res) => { } }); +//last episodes +r.get("/anime/monoschinos/last/:option", async (req, res) => { + try { + const { option } = req.params; + + const monos = new Monoschinos(); + if ("episodes" === option) { + res.send(await monos.getLastEpisodes()); + } else if ("animes" === option) { + res.send(await monos.getLastAnimes()); + } else { + throw "Invalid option in the URL"; + } + } catch (error) { + console.log(error); + res.status(500).send(error); + } +}); + export default r; diff --git a/src/routes/v1/anime/otakutv/otakutvRoute.js b/src/routes/v1/anime/otakutv/otakutvRoute.js index 106aae33..8914dfa7 100644 --- a/src/routes/v1/anime/otakutv/otakutvRoute.js +++ b/src/routes/v1/anime/otakutv/otakutvRoute.js @@ -1,53 +1,53 @@ import { Router } from "express"; import g from "../../../../scraper/sites/anime/otakuTV/getAnime.js"; import c from "../../../../scraper/sites/anime/otakuTV/getAnimeComingSoon.js"; -import l from '../../../../scraper/sites/anime/otakuTV/getAnimeLatino.js' +import l from "../../../../scraper/sites/anime/otakuTV/getAnimeLatino.js"; import n from "../../../../scraper/sites/anime/otakuTV/getAnimeNew.js"; -import ra from '../../../../scraper/sites/anime/otakuTV/getAnimeRanking.js' +import ra from "../../../../scraper/sites/anime/otakuTV/getAnimeRanking.js"; import u from "../../../../scraper/sites/anime/otakuTV/getUsersActive.js"; const r = Router(); //coming soon -r.get("/anime/otakuTV/comingsoon", (req, res)=>{ - c.getComingSoon().then(f =>{ - res.send(f) - }) -}) +r.get("/anime/otakuTV/comingsoon", (req, res) => { + c.getComingSoon().then((f) => { + res.send(f); + }); +}); //latino anime r.get("/anime/otakuTV/animelatin", (req, res) => { - l.getAnimeLatino().then(f => { - res.send(f) - }) -}) + l.getAnimeLatino().then((f) => { + res.send(f); + }); +}); //news r.get("/anime/otakuTV/animenew", (req, res) => { - n.getAnimeNew().then(f => { - res.send(f) - }) -}) + n.getAnimeNew().then((f) => { + res.send(f); + }); +}); -//anime ranking +//anime ranking r.get("/anime/otakuTV/animeranking", (req, res) => { - ra.getAnimeRanking().then(f => { - res.send(f) - }) -}) + ra.getAnimeRanking().then((f) => { + res.send(f); + }); +}); //user-ranking r.get("/anime/otakuTV/usertop", (req, res) => { - u.getUsersActive().then(f => { - res.send(f) - }) -}) + u.getUsersActive().then((f) => { + res.send(f); + }); +}); //name anime -r.get("/anime/otakuTV/:name", (req, res)=>{ - const {name} = req.params - g.getAnime(name).then(f => { - res.send(f) - }) -}) +r.get("/anime/otakuTV/:name", (req, res) => { + const { name } = req.params; + g.getAnime(name).then((f) => { + res.send(f); + }); +}); -export default r +export default r; diff --git a/src/routes/v1/anime/tioanime/TioAnimeRoute.ts b/src/routes/v1/anime/tioanime/TioAnimeRoute.ts index e4fbfcbe..46a170bb 100644 --- a/src/routes/v1/anime/tioanime/TioAnimeRoute.ts +++ b/src/routes/v1/anime/tioanime/TioAnimeRoute.ts @@ -8,7 +8,7 @@ r.get("/anime/tioanime/name/:name", async (req, res) => { const { name } = req.params; const tioanime = new TioAnime(); const animeInfo = await tioanime.getAnime( - `https://tioanime.com/anime/${name}` + `https://tioanime.com/anime/${name}`, ); res.send(animeInfo); } catch (error) { @@ -23,7 +23,7 @@ r.get("/anime/tioanime/episode/:episode", async (req, res) => { const { episode } = req.params; const tioanime = new TioAnime(); const animeInfo = await tioanime.getEpisodeServers( - `https://tioanime.com/ver/${episode}` + `https://tioanime.com/ver/${episode}`, ); res.send(animeInfo); } catch (error) { @@ -38,18 +38,18 @@ r.get("/anime/tioanime/last/:option", async (req, res) => { const { option } = req.params; const tioanime = new TioAnime(); - if ('episodes' === option) { + if ("episodes" === option) { res.send(await tioanime.getLastEpisodes()); - } else if ('animes' === option) { + } else if ("animes" === option) { res.send(await tioanime.getLastAnimes(null)); - } else if ('movies' === option) { + } else if ("movies" === option) { res.send(await tioanime.getLastMovies()); - } else if ('ovas' === option) { + } else if ("ovas" === option) { res.send(await tioanime.getLastOvas()); - } else if ('onas' === option) { + } else if ("onas" === option) { res.send(await tioanime.getLastOnas()); } else { - throw 'Invalid option in the URL'; + throw "Invalid option in the URL"; } } catch (error) { console.log(error); @@ -59,21 +59,29 @@ r.get("/anime/tioanime/last/:option", async (req, res) => { //filter r.get("/anime/tioanime/filter", async (req, res) => { - try { + try { const title = req.query.title as string; const types = (req.query.type as string[]) ?? []; const genres = (req.query.gen as string[]) ?? []; const begin = (req.query.begin_year as unknown as number) ?? 1950; - const end = (req.query.end_year as unknown as number) ?? new Date().getFullYear(); + const end = + (req.query.end_year as unknown as number) ?? new Date().getFullYear(); const status = (req.query.status as unknown as number) ?? 2; const sort = (req.query.sort as string) ?? "recent"; const tioanime = new TioAnime(); - const animeInfo = await tioanime.filter(title, types, genres, {begin, end}, status, sort); + const animeInfo = await tioanime.filter( + title, + types, + genres, + { begin, end }, + status, + sort, + ); res.send(animeInfo); - //console.log(tioanime.filter(types, genres, { begin: begin, end: end }, status, sort).then(result => { console.log(result) } )); + //console.log(tioanime.filter(types, genres, { begin: begin, end: end }, status, sort).then(result => { console.log(result) } )); } catch (error) { console.log(error); res.status(500).send(error); diff --git a/src/routes/v1/anime/wcostream/wcostreamRoutes.ts b/src/routes/v1/anime/wcostream/wcostreamRoutes.ts index 63a8bf4a..0e5637eb 100644 --- a/src/routes/v1/anime/wcostream/wcostreamRoutes.ts +++ b/src/routes/v1/anime/wcostream/wcostreamRoutes.ts @@ -5,35 +5,41 @@ const Anime = new WcoStream(); const router = Router(); router.get("/anime/wcostream/name/:name", async (req, res) => { - const { name } = req.params - const data = await Anime.GetAnimeInfo(name) + const { name } = req.params; + const data = await Anime.GetItemInfo(name); - res.send(data) -}) + res.send(data); +}); router.get("/anime/wcostream/episode/:episode", async (req, res) => { - const { episode } = req.params - const { season } = req.query - const data = await Anime.GetEpisodeServers(episode, season as unknown as number) + const { episode } = req.params; + const { season } = req.query; + const data = await Anime.GetEpisodeServers( + episode, + season as unknown as number, + ); - res.send(data) -}) + res.send(data); +}); router.get("/anime/wcostream/filter", async (req, res) => { - const { search, page } = req.query - const data = await Anime.GetAnimeByFilter(search as string, page as unknown as number) + const { search, page } = req.query; + const data = await Anime.GetItemByFilter( + search as string, + page as unknown as number, + ); - res.send(data) -}) + res.send(data); +}); /* Global API */ -router.post("/runtime/unpacked", async (req,res) => { - const {base64} = req.body - const data = await Anime.RuntimeUnpacked(base64) - return res.send(data) -}) +router.post("/runtime/unpacked", async (req, res) => { + const { base64 } = req.body; + const data = await Anime.RuntimeUnpacked(base64); + return res.send(data); +}); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/anime/zoro/ZoroRoutes.ts b/src/routes/v1/anime/zoro/ZoroRoutes.ts index 931deede..cdcc825b 100644 --- a/src/routes/v1/anime/zoro/ZoroRoutes.ts +++ b/src/routes/v1/anime/zoro/ZoroRoutes.ts @@ -7,7 +7,7 @@ r.get("/anime/zoro/name/:name", async (req, res) => { try { const { name } = req.params; const zoro = new Zoro(); - const animeInfo = await zoro.GetAnimeInfo(name); + const animeInfo = await zoro.GetItemInfo(name); res.send(animeInfo); } catch (error) { console.log(error); @@ -20,7 +20,7 @@ r.get("/anime/zoro/episode/:episode/:ep", async (req, res) => { try { const { episode, ep } = req.params; const zoro = new Zoro(); - const animeInfo = await zoro.GetEpisodeServer(episode, ep); + const animeInfo = await zoro.GetEpisodeServers(episode, ep); res.send(animeInfo); } catch (error) { console.log(error); @@ -41,7 +41,16 @@ r.get("/anime/zoro/filter", async (req, res) => { const page = req.query.page as string; const zoro = new Zoro(); - const animeInfo = await zoro.Filter(type, rated, score, season, language, sort, gen, page); + const animeInfo = await zoro.GetItemByFilter( + type, + rated, + score, + season, + language, + sort, + gen, + page, + ); res.send(animeInfo); } catch (error) { console.log(error); diff --git a/src/routes/v1/doramas/dramanice/DramaniceRoutes.ts b/src/routes/v1/doramas/dramanice/DramaniceRoutes.ts index f4df7f51..eedaa2bd 100644 --- a/src/routes/v1/doramas/dramanice/DramaniceRoutes.ts +++ b/src/routes/v1/doramas/dramanice/DramaniceRoutes.ts @@ -5,28 +5,31 @@ const router = Router(); // Filter router.get("/anime/animelatinohd/filter", async (req, res) => { - const { search, type, page, year, genre } = req.query - - const data = await Dorama.GetAnimeByFilter(search as string, type as unknown as number, page as unknown as number, year as string, genre as string) - res.send(data) + const { search, type, page, year, genre } = req.query; + + const data = await Dorama.GetAnimeByFilter( + search as string, + type as unknown as number, + page as unknown as number, + year as string, + genre as string, + ); + res.send(data); }); // Anime Info +(Episodes list) router.get("/anime/animelatinohd/name/:name", async (req, res) => { - - const { name } = req.params - const data = await Dorama.GetAnimeInfo(name) - res.send(data) - + const { name } = req.params; + const data = await Dorama.GetAnimeInfo(name); + res.send(data); }); // Episode Info +(Video Servers) router.get("/anime/animelatinohd/episode/:episode", async (req, res) => { - const { lang } = req.query - const { episode } = req.params - const data = await Dorama.GetEpisodeServers(episode, lang as string) - res.send(data) - + const { lang } = req.query; + const { episode } = req.params; + const data = await Dorama.GetEpisodeServers(episode, lang as string); + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/manga/comick/ComickRoutes.ts b/src/routes/v1/manga/comick/ComickRoutes.ts index 357db755..13f6d337 100644 --- a/src/routes/v1/manga/comick/ComickRoutes.ts +++ b/src/routes/v1/manga/comick/ComickRoutes.ts @@ -3,31 +3,36 @@ import { Comick } from "../../../../scraper/sites/manga/comick/Comick"; const Manga = new Comick(); const router = Router(); - router.get("/manga/comick/filter", async (req, res) => { - const { search, type, year, genre } = req.query; - - const data = await Manga.GetMangaByFilter(search as string, type as unknown as number, year as string, genre as string) - - res.send(data) + const { search, type, year, genre, page,status } = req.query; + + const data = await Manga.GetMangaByFilter( + search as string, + type as unknown as number, + year as unknown as number, + status as unknown as number, + genre as string, + page as unknown as number + ); + + res.send(data); }); +router.get("/manga/comick/name/:manga", async (req, res) => { + const { manga } = req.params; + const { lang } = req.query; -router.get("/manga/comick/title/:manga", async (req, res) => { - const { manga } = req.params; - const { lang } = req.query; - - const data = await Manga.GetMangaInfo(manga, lang as string) + const data = await Manga.GetMangaInfo(manga, lang as string); - res.send(data) + res.send(data); }); router.get("/manga/comick/chapter/:chapter", async (req, res) => { - const { chapter } = req.params - const { lang } = req.query; + const { chapter } = req.params; + const { lang } = req.query; - const data = await Manga.GetChapterInfo(chapter, lang as string) + const data = await Manga.GetChapterInfo(chapter, lang as string); - res.send(data) + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/manga/inmanga/InmangaRoutes.ts b/src/routes/v1/manga/inmanga/InmangaRoutes.ts index 74ec504d..dab41292 100644 --- a/src/routes/v1/manga/inmanga/InmangaRoutes.ts +++ b/src/routes/v1/manga/inmanga/InmangaRoutes.ts @@ -3,30 +3,31 @@ import { Inmanga } from "../../../../scraper/sites/manga/inmanga/Inmanga"; const Manga = new Inmanga(); const router = Router(); - router.get("/manga/inmanga/filter", async (req, res) => { - const { search, type, genre } = req.query; - const data = await Manga.GetMangaByFilter(search as string, type as unknown as number, genre as string[]); - - res.send(data) + const { search, type, genre } = req.query; + const data = await Manga.GetMangaByFilter( + search as string, + type as unknown as number, + genre as string[], + ); + + res.send(data); }); +router.get("/manga/inmanga/name/:manga", async (req, res) => { + const { manga } = req.params; + const { cid } = req.query; -router.get("/manga/inmanga/title/:manga", async (req, res) => { - const { manga } = req.params; - const {cid} = req.query; + const data = await Manga.GetMangaInfo(manga, cid as string); - const data = await Manga.GetMangaInfo(manga, cid as string); - - res.send(data) + res.send(data); }); router.get("/manga/inmanga/chapter/:chapter", async (req, res) => { + const { chapter } = req.params; + const { cid } = req.query; + const data = await Manga.GetChapterInfo(chapter, cid as string); - const { chapter } = req.params - const { cid } = req.query - const data = await Manga.GetChapterInfo(chapter, cid as string); - - res.send(data) + res.send(data); }); -export default router \ No newline at end of file +export default router; diff --git a/src/routes/v1/manga/manganelo/ManganeloRoutes.ts b/src/routes/v1/manga/manganelo/ManganeloRoutes.ts index d494d99b..8f80e403 100644 --- a/src/routes/v1/manga/manganelo/ManganeloRoutes.ts +++ b/src/routes/v1/manga/manganelo/ManganeloRoutes.ts @@ -5,8 +5,8 @@ import { Manganelo } from "../../../../scraper/sites/manga/manganelo/Manganelo"; const router = Router(); const manganelo = new Manganelo(); -router.get(`/manga/${manganelo.name}/title/:id`, async (req, res) => { - const result = await manganelo.GetMangaInfo( +router.get(`/manga/${manganelo.name}/name/:id`, async (req, res) => { + const result = await manganelo.GetItemInfo( req.params.id as unknown as string, ); @@ -14,18 +14,22 @@ router.get(`/manga/${manganelo.name}/title/:id`, async (req, res) => { }); router.get(`/manga/${manganelo.name}/filter`, async (req, res) => { - const result = await manganelo.Filter({ + const result = await manganelo.GetItemByFilter({ sts: req.query.status as unknown as "ongoing" | "completed", genres: req.query.genres as unknown as string, - orby: req.query.order as unknown as typeof manganatoOrderByOptionsList[number], - page: req.query.page as unknown as number + orby: req.query + .order as unknown as (typeof manganatoOrderByOptionsList)[number], + page: req.query.page as unknown as number, }); return res.status(200).send(result); -}) +}); router.get(`/manga/${manganelo.name}/chapter/:id`, async (req, res) => { - const result = await manganelo.GetMangaChapters(req.params.id as unknown as string, req.query.num as unknown as number); + const result = await manganelo.GetMangaChapters( + req.params.id as unknown as string, + req.query.num as unknown as number, + ); return res.status(200).send(result); }); diff --git a/src/routes/v1/manga/mangareader/MangaReaderRoutes.ts b/src/routes/v1/manga/mangareader/MangaReaderRoutes.ts index 11e11988..b4b93ca1 100644 --- a/src/routes/v1/manga/mangareader/MangaReaderRoutes.ts +++ b/src/routes/v1/manga/mangareader/MangaReaderRoutes.ts @@ -6,17 +6,17 @@ import { MangaReaderFilterScore, MangaReaderFilterSort, MangaReaderFilterStatus, - MangaReaderFilterType + MangaReaderFilterType, } from "../../../../scraper/sites/manga/MangaReader/MangaReaderTypes"; const mangaReader = new MangaReader(); const router = Router(); -router.get("/manga/mangareader/title/:id", async (req, res) => { +router.get("/manga/mangareader/name/:id", async (req, res) => { try { - const id = req.params.id as unknown as number; + const id = req.params.id as unknown as string; - const data = await mangaReader.GetMangaInfo(id); + const data = await mangaReader.GetItemInfo(id); return res.status(200).send(data); } catch (e) { @@ -31,7 +31,8 @@ router.get("/manga/mangareader/filter", async (req, res) => { const status = req.query.status as MangaReaderFilterStatus; const ratingType = req.query.rating as MangaReaderFilterRatingType; const score = req.query.score as MangaReaderFilterScore; - const language = req.query.language as typeof MangaReaderFilterLanguage[number]; + const language = req.query + .language as (typeof MangaReaderFilterLanguage)[number]; const startYear = req.query.startyear as unknown as number; const startMonth = req.query.startmonth as unknown as number; const startDay = req.query.startday as unknown as number; @@ -41,7 +42,7 @@ router.get("/manga/mangareader/filter", async (req, res) => { const sort = req.query.sort as MangaReaderFilterSort; const numPage = req.query.page as unknown as number; - const data = await mangaReader.Filter({ + const data = await mangaReader.GetItemByFilter({ type, status, ratingType, @@ -54,7 +55,7 @@ router.get("/manga/mangareader/filter", async (req, res) => { endMonth, endDay, sort, - numPage + numPage, }); return res.status(200).send(data); @@ -66,16 +67,16 @@ router.get("/manga/mangareader/filter", async (req, res) => { router.get("/manga/mangareader/chapter/:id", async (req, res) => { try { - const id = req.params.id as unknown as number; + const id = req.params.id as unknown as string; const chapterNumber = req.query.number as unknown as number; - const language = req.query.lang as typeof MangaReaderFilterLanguage[number]; - + const language = req.query + .lang as (typeof MangaReaderFilterLanguage)[number]; const data = await mangaReader.GetMangaChapters( id, chapterNumber, language, - "chapter" + "chapter", ); return res.status(200).send(data); @@ -87,16 +88,16 @@ router.get("/manga/mangareader/chapter/:id", async (req, res) => { router.get("/manga/mangareader/volume/:id", async (req, res) => { try { - const id = req.params.id as unknown as number; + const id = req.params.id as unknown as string; const chapterNumber = req.query.number as unknown as number; - const language = req.query.lang as typeof MangaReaderFilterLanguage[number]; - + const language = req.query + .lang as (typeof MangaReaderFilterLanguage)[number]; const data = await mangaReader.GetMangaChapters( id, chapterNumber, language, - "volume" + "volume", ); return res.status(200).send(data); diff --git a/src/scraper/sites/anime/9Anime/9Anime.js b/src/scraper/sites/anime/9Anime/9Anime.js index 6a3a27e2..abe1b5cd 100644 --- a/src/scraper/sites/anime/9Anime/9Anime.js +++ b/src/scraper/sites/anime/9Anime/9Anime.js @@ -45,7 +45,7 @@ async function NineAnimeInfo(animeName) { $("div.binfo div.info div.bmeta").each((i, e) => { const info = parseAnimeInfo( $(e).find("div.meta:first").text().trim(), - $(e).find("div.meta").next().text().trim() + $(e).find("div.meta").next().text().trim(), ); animeData.year = info.dateAired.trim(); animeData.genres = info.genre; diff --git a/src/scraper/sites/anime/animeBlixs/AnimeBlix.ts b/src/scraper/sites/anime/animeBlixs/AnimeBlix.ts index ac0d4ba7..84763c0b 100644 --- a/src/scraper/sites/anime/animeBlixs/AnimeBlix.ts +++ b/src/scraper/sites/anime/animeBlixs/AnimeBlix.ts @@ -1,163 +1,213 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Anime } from "../../../../types/anime"; +import { AnimeMedia } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { AnimeSearch, ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; +import { + ResultSearch, + AnimeResult +} from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; //import { Calendar } from "@animetypes/date"; /** List of Domains * https://vwv.animeblix.org - * + * * https://animeblix.xyz - * + * * https://animeblix.com - * -*/ - -export class AnimeBlix { - readonly url = "https://vwv.animeblix.org"; - readonly api = "https://api.animelatinohd.com"; - - async GetAnimeInfo(anime: string): Promise { - try { - const { data } = await axios.get(`${this.url}/animes/${anime.includes("ver-") ? anime : "ver-"+anime}`); - const $ = cheerio.load(data); - - const AnimeTypes = $(".cn .info .r .u li span:contains('Tipo:')").next() - const AnimeStatus = $(".cn .info .r .u li span[class='em']").length ? $(".cn .info .r .u li span[class='em']").text() : $(".cn .info .r .u li span[class='fi']").length ? $(".cn .info .r .u li span[class='fi']").text() : $(".cn .info .r .u li span[class='es']").text() - const AnimeDate = $(".cn .info .r .u li span:contains('Fecha de emisión:')").next().text().trim().replace(" -", "").split(" ") - - const Dates = AnimeDate[0] ? new Date(String(AnimeDate[0])) : null - const DateFormat = new Intl.DateTimeFormat("en", { day: "numeric", month: "numeric", year: "numeric" }).format(Dates).split("/") - - - const AcceptAlts = $(".cn .info .r .u").next().find("li").text().replace("Nombre original: ", "").replace("Nombre en inglés: ", "---").replace("Nombre en japones: ", "---") - - let AltsSlice: number = 0 - - if (AcceptAlts.includes("Támbien conocido como:")) { - AltsSlice = AcceptAlts.indexOf("Támbien conocido como:") - } else if (AcceptAlts.includes("Estudio(s):")) { - AltsSlice = AcceptAlts.indexOf("Estudio(s):") - } else if (AcceptAlts.includes("Producido por:")) { - AltsSlice = AcceptAlts.indexOf("Producido por:") - } else if (AcceptAlts.includes("Licenciada por:")) { - AltsSlice = AcceptAlts.indexOf("Licenciada por:") - } - - const AltNames = AcceptAlts.slice(0, AltsSlice) - const AnimeInfo: Anime = { - name: $(".cn .ti h1 strong").text(), - url: `/anime/animeblix/name/${anime}`, - synopsis: $(".cn .info .r .tx .content p").first().text(), - alt_name: [...AltNames.split("---")], - image: { - url: $(".cn .info .l .i img").attr("data-src") - }, - genres: [...$(".cn .info .r .gn li").text().split(",")], - type: AnimeTypes.length ? AnimeTypes.text() == "TV" ? "Anime" : AnimeTypes.text() == "Pelicula" ? "Movie" : AnimeTypes.text() == "Ova" ? "OVA" : "Null" : "Null", //tv,pelicula,especial,ova - status: AnimeStatus, - date: AnimeDate[0] ? { year: DateFormat[2], month: DateFormat[1], day: DateFormat[0] } : null, - episodes: [] - } - - const ListEpisodeIndex = $(".sc .cn #l").html() - const RemoveSymbols: RegExp = /[^0-9,]+/g; - const ReplaceSymbols: RegExp = /(,)+/g; - const ListEpisode = ListEpisodeIndex.slice(ListEpisodeIndex.indexOf("var eps = "), ListEpisodeIndex.indexOf("; { - const AnimeEpisode: Episode = { - name: "Episode " +e, - number: e, - image: "", - url: `/anime/animeblix/episode/${anime+"-"+e}` - } - - AnimeInfo.episodes.push(AnimeEpisode); - }) - - return AnimeInfo; - - } catch (error) { - console.log(error) - } + * + */ + +export class AnimeBlix extends AnimeScraperModel { + readonly url = "https://vwv.animeblix.org"; + readonly api = "https://api.animelatinohd.com"; + + async GetItemInfo(anime: string): Promise { + try { + const { data } = await axios.get( + `${this.url}/animes/${anime.includes("ver-") ? anime : "ver-" + anime}` + ); + const $ = cheerio.load(data); + + const AnimeTypes = $(".cn .info .r .u li span:contains('Tipo:')").next(); + const AnimeStatus = $(".cn .info .r .u li span[class='em']").length + ? $(".cn .info .r .u li span[class='em']").text() + : $(".cn .info .r .u li span[class='fi']").length + ? $(".cn .info .r .u li span[class='fi']").text() + : $(".cn .info .r .u li span[class='es']").text(); + const AnimeDate = $( + ".cn .info .r .u li span:contains('Fecha de emisión:')" + ) + .next() + .text() + .trim() + .replace(" -", "") + .split(" "); + + const Dates = AnimeDate[0] ? new Date(String(AnimeDate[0])) : null; + const DateFormat = new Intl.DateTimeFormat("en", { + day: "numeric", + month: "numeric", + year: "numeric", + }) + .format(Dates) + .split("/"); + + const AcceptAlts = $(".cn .info .r .u") + .next() + .find("li") + .text() + .replace("Nombre original: ", "") + .replace("Nombre en inglés: ", "---") + .replace("Nombre en japones: ", "---"); + + let AltsSlice: number = 0; + + if (AcceptAlts.includes("Támbien conocido como:")) { + AltsSlice = AcceptAlts.indexOf("Támbien conocido como:"); + } else if (AcceptAlts.includes("Estudio(s):")) { + AltsSlice = AcceptAlts.indexOf("Estudio(s):"); + } else if (AcceptAlts.includes("Producido por:")) { + AltsSlice = AcceptAlts.indexOf("Producido por:"); + } else if (AcceptAlts.includes("Licenciada por:")) { + AltsSlice = AcceptAlts.indexOf("Licenciada por:"); + } + + const AltNames = AcceptAlts.slice(0, AltsSlice); + const AnimeInfo: AnimeMedia = { + name: $(".cn .ti h1 strong").text(), + url: `/anime/animeblix/name/${anime}`, + synopsis: $(".cn .info .r .tx .content p").first().text(), + alt_names: [...AltNames.split("---")], + image: { + url: $(".cn .info .l .i img").attr("data-src"), + }, + genres: [...$(".cn .info .r .gn li").text().split(",")], + type: AnimeTypes.length + ? AnimeTypes.text() == "TV" + ? "Anime" + : AnimeTypes.text() == "Pelicula" + ? "Movie" + : AnimeTypes.text() == "Ova" + ? "OVA" + : "Null" + : "Null", //tv,pelicula,especial,ova + status: AnimeStatus, + date: AnimeDate[0] + ? { year: DateFormat[2], month: DateFormat[1], day: DateFormat[0] } + : null, + episodes: [], + }; + + const ListEpisodeIndex = $(".sc .cn #l").html(); + const RemoveSymbols: RegExp = /[^0-9,]+/g; + const ReplaceSymbols: RegExp = /(,)+/g; + const ListEpisode = ListEpisodeIndex.slice( + ListEpisodeIndex.indexOf("var eps = "), + ListEpisodeIndex.indexOf("; { + const AnimeEpisode: Episode = { + name: "Episode " + e, + num: Number(e), + url: `/anime/animeblix/episode/${anime + "-" + e}`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.log(error); } - async GetEpisodeServers(episode: string): Promise { - try { - - const number = episode.substring(episode.lastIndexOf("-") + 1) - const anime = episode.substring(0, episode.lastIndexOf("-")) - - const { data } = await axios.get(`${this.url}/${anime.replace("ver-","")}-${number}`); - const $ = cheerio.load(data); - - const AnimeEpisodeInfo: Episode = { - name: number, - url: `/anime/animeblix/episode/${episode}`, - number: number, - image: "", - servers: [] - } - - - $("").map((e) => { - - const Server: EpisodeServer = { - name: "e.server.title", - url: "", - } - - Server.url = "https://api.animelatinohd.com/stream/" - Server.name = String(e) - - AnimeEpisodeInfo.servers.push(Server) - }) - - return AnimeEpisodeInfo; - } catch (error) { - console.log(error) - } + } + async GetEpisodeServers(episode: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + + const { data } = await axios.get( + `${this.url}/${anime.replace("ver-", "")}-${number}` + ); + const $ = cheerio.load(data); + + const AnimeEpisodeInfo: Episode = { + name: number, + url: `/anime/animeblix/episode/${episode}`, + num: Number(number), + servers: [], + }; + + $("").map((e) => { + const Server: EpisodeServer = { + name: "e.server.title", + url: "", + }; + + Server.url = "https://api.animelatinohd.com/stream/"; + Server.name = String(e); + + AnimeEpisodeInfo.servers.push(Server); + }); + + return AnimeEpisodeInfo; + } catch (error) { + console.log(error); } - - async GetAnimeByFilter(search?: string, type?: number, page?: number, year?: string, genre?: string): Promise> { - try { - const { data } = await axios.get(`${this.api}/api/anime/list`, { - params: { - search: search, - type: type, - year: year, - genre: genre, - page: page - } - }); - - const animeSearchParseObj = data - - const animeSearch: ResultSearch = { - nav: { - count: animeSearchParseObj.data.length, - current: animeSearchParseObj.current_page, - next: animeSearchParseObj.data.length < 28 ? 0 : animeSearchParseObj.current_page + 1, - hasNext: animeSearchParseObj.data.length < 28 ? false : true - }, - results: [] - } - animeSearchParseObj.data.map(e => { - const animeSearchData: AnimeSearch = { - name: e.name, - image: "https://www.themoviedb.org/t/p/original" + e.poster + "?&w=53&q=95", - url: `/anime/animelatinohd/name/${e.slug}`, - type: "" - } - animeSearch.results.push(animeSearchData) - }) - return animeSearch; - } catch (error) { - console.log(error) - } + } + + async GetItemByFilter( + search?: string, + type?: number, + page?: number, + year?: string, + genre?: string + ): Promise> { + try { + const { data } = await axios.get(`${this.api}/api/anime/list`, { + params: { + search: search, + type: type, + year: year, + genre: genre, + page: page, + }, + }); + + const animeSearchParseObj = data; + + const animeSearch: ResultSearch = { + nav: { + count: animeSearchParseObj.data.length, + current: animeSearchParseObj.current_page, + next: + animeSearchParseObj.data.length < 28 + ? 0 + : animeSearchParseObj.current_page + 1, + hasNext: animeSearchParseObj.data.length < 28 ? false : true, + }, + results: [], + }; + animeSearchParseObj.data.map((e) => { + const animeSearchData: AnimeResult = { + name: e.name, + image: + "https://www.themoviedb.org/t/p/original" + + e.poster + + "?&w=53&q=95", + url: `/anime/animelatinohd/name/${e.slug}`, + type: "", + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.log(error); } - + } } - - diff --git a/src/scraper/sites/anime/animeflv/AnimeFlv.ts b/src/scraper/sites/anime/animeflv/AnimeFlv.ts index 31ee53d3..c89df662 100644 --- a/src/scraper/sites/anime/animeflv/AnimeFlv.ts +++ b/src/scraper/sites/anime/animeflv/AnimeFlv.ts @@ -1,7 +1,7 @@ -import axios from "axios"; +import axios, { AxiosResponse } from "axios"; import { load } from "cheerio"; -import { Anime, Chronology } from "../../../../types/anime"; -import { Episode, EpisodeServer } from "../../../../types/episode"; +import { AnimeMedia, Chronology } from "../../../../types/anime"; +import { Episode } from "../../../../types/episode"; import { Genres, OrderAnimeflv, @@ -9,34 +9,46 @@ import { TypeAnimeflv, } from "./animeflv_helper"; import { - AnimeSearch, ResultSearch, - IResultSearch, - IAnimeSearch, + type IResultSearch, + type IAnimeResult, + AnimeResult, } from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; +import { ScraperErrorResponse } from "utils/ScraperError"; -export class AnimeFlv { - readonly url = "https://animeflv.ws"; +export class AnimeFlv extends AnimeScraperModel { + readonly url = "https://m.animeflv.net"; - async GetAnimeInfo(anime: string): Promise { + async GetItemInfo(anime: string): Promise { try { const { data } = await axios.get(`${this.url}/anime/${anime}`); const $ = load(data); - const title = $("h2.Title").text().trim(); - const title_alt = $("span.TxtAlt").text().trim(); - const img = $("div.AnimeCover .Image figure img").attr("src"); - const status = $("p.AnmStts span").text().trim(); - const synopsis = $("div.Description").text().trim(); - const episodes = $(".ListCaps li a"); - const AnimeReturn = new Anime(); + + //relevant information + const title = $("h1.Title").text().trim(); + const title_alt = title; + const img = $("meta[property='og:image']").attr("content"); + const status = $("p strong.Anm-On").text(); + const synopsis = $("header p:contains(Sinopsis)") + .text() + .replace("Sinopsis:", "") + .trim(); + + //container of episodes + const episodesContainer = $("div.List-Episodes div.AACrdn"); + + const AnimeReturn = new AnimeMedia(); AnimeReturn.name = title; - AnimeReturn.alt_name = [...title_alt.split(",")]; + AnimeReturn.alt_names = [...title_alt.split(",")]; AnimeReturn.image = { url: img, }; AnimeReturn.status = status; AnimeReturn.synopsis = synopsis; AnimeReturn.chronology = []; + AnimeReturn.genres = []; + AnimeReturn.episodes = []; //getRelated $("ul.ListAnmRel li a").each((_i, e) => { @@ -45,23 +57,31 @@ export class AnimeFlv { cro.url = `/anime/flv/name/${$(e).attr("href").replace("/anime/", "")}`; AnimeReturn.chronology.push(cro); }); + //get genres - $("nav.Nvgnrs a").each((_i, e) => { - const gen = $(e).text().trim(); + $("footer a").each((_i, e) => { + const gen = $(e).text().trim() as string; + AnimeReturn.genres.push(gen); }); + //get episodes - episodes.each((_i_, e) => { - const l = $(e).attr("href").replace("/", ""); - const episode = new Episode(); - episode.name = $(e).children(".Title").text().trim(); - episode.url = `/anime/flv/episode/${`${l}`.replace( - "/anime", - "/anime/flv" - )}`; - episode.number = $(e).children("p").last().text().trim(); - episode.image = $(e).children("figure").find(".lazy").attr("src"); - AnimeReturn.episodes.push(episode); + episodesContainer.each((_i_, e) => { + $(e) + .find("ul li") + .each((_i, e) => { + const link = $(e).find("a"); + const name = link.text().trim(); + const numberEpisode = Number(name.split(" ").slice(-1)); + const episode = new Episode(); + episode.name = name; + episode.url = `/anime/flv/episode/${link + .attr("href") + .replace("/ver/", "")}`; + episode.num = numberEpisode; + + AnimeReturn.episodes.push(episode); + }); }); return AnimeReturn; } catch (error) { @@ -69,45 +89,51 @@ export class AnimeFlv { "An error occurred while getting the anime info: invalid name", error ); - throw new Error( + throw new ScraperErrorResponse( "An error occurred while getting the anime info: invalid name" ); } } - async Filter( + async GetItemByFilter( gen?: Genres | string, - date?: string, + year?: string, type?: TypeAnimeflv, status?: StatusAnimeflv, ord?: OrderAnimeflv, page?: number, title?: string - ): Promise> { + ): Promise> { try { - const { data } = await axios.get(`${this.url}/browse`, { - params: { - genres: gen || "all", - year: date || "all", - status: status || "all", - Tipo: type || "all", - order: ord || 1, - page: page || 1, - q: title, - }, - }); + const { data, request }: AxiosResponse = await axios.get( + `${this.url}/browse`, + { + params: { + page: page, + genre: gen, + year: year, + status: status, + type: type, + order: ord, + q: title, + }, + } + ); + console.log(request); const $ = load(data); - const infoList = $("ul.ListAnimes li"); - const data_filter = new ResultSearch(); + const infoList = $("ul.List-Animes li"); + const data_filter = new ResultSearch(); data_filter.results = []; infoList.each((_i, e) => { - const info = new AnimeSearch(); - info.name = $(e).find("h3").text().trim(); - info.image = - $(e) - .find("a") - .attr("href") - .replace("/anime/", "https://img.animeflv.ws/cover/") + ".jpg"; + const info = new AnimeResult(); + info.name = $(e).find("h2").text().trim(); + info.image = $(e) + .find("img") + .attr("src") + .replace( + "/uploads/animes/", + "https://m.animeflv.net/uploads/animes/" + ); info.url = `/anime/flv/name/${$(e) .find("a") .attr("href") @@ -118,32 +144,68 @@ export class AnimeFlv { return data_filter; } catch (error) { console.log("An error occurred while getting the filter values", error); - throw new Error("An error occurred while getting the filter values"); + throw new ScraperErrorResponse("An error occurred while getting the filter values"); } } async GetEpisodeServers(episode: string): Promise { try { - const { data } = await axios.get(`${this.url}/${episode}`); + const { data } = await axios.get(`${this.url}/ver/${episode}`); + /* const test: AxiosResponse = await axios.get( + "https://streamtape.com/e/ybVywBZRXMheQ2/" + ); + const $t = load(test.data); */ const $ = load(data); - const title = $(".CapiTop").children("h1").text().trim(); - const getLinks = $(".CpCnA .anime_muti_link li"); - const numberEpisode = episode.substring(episode.lastIndexOf("-") + 1) + const title = $("h1").text().trim(); + const getLinks = $("script"); + const numberEpisode = episode.substring(episode.lastIndexOf("-") + 1); const episodeReturn = new Episode(); episodeReturn.name = title; episodeReturn.url = `/anime/flv/episode/${episode}`; - episodeReturn.number = numberEpisode as unknown as string; + episodeReturn.num = Number(numberEpisode); episodeReturn.servers = []; - const promises = getLinks.map(async(_i, e) => { - const servers = new EpisodeServer(); - const title = $(e).attr("title"); + /* const player = $t("div.plyr__video-wrapper").html(); + + console.log(player); */ + + getLinks.each((_i, e) => { + interface VideoObject { + title: string; + code: string; + } + + const scriptContent = $(e).html(); + const regexVideoObject = /var videos = (\{.*?\});/; + + const matchObject = scriptContent.match(regexVideoObject); + + if (matchObject) { + const videoObject: VideoObject[] = JSON.parse(matchObject[1]).SUB; + + for (let index = 0; index < videoObject.length; index++) { + const element = videoObject[index]; + + episodeReturn.servers.push({ + name: element.title, + url: element.code, + }); + } + } + }); + /*const promises = getLinks.map(async (_i, e) => { + /* const servers = new EpisodeServer(); + const title = $(e).find("a").text().trim(); const videoData = $(e).attr("data-video"); servers.name = title; servers.url = videoData; - if(videoData.includes("streaming.php")){ - await this.getM3U(`${videoData.replace("streaming.php", "ajax.php")}&refer=none`).then((g) => { - if(g.source.length){ + console.log(title); */ + + /* if (videoData.includes("streaming.php")) { + await this.getM3U( + `${videoData.replace("streaming.php", "ajax.php")}&refer=none` + ).then((g) => { + if (g.source.length) { servers.file_url = g.source[0].file; } }); @@ -169,23 +231,23 @@ export class AnimeFlv { default: break; } - episodeReturn.servers.push(servers); - }); - await Promise.all(promises); + episodeReturn.servers.push(servers); + })*/ + //await Promise.all(promises); return episodeReturn; } catch (error) { console.log("An error occurred while getting the episode servers", error); - throw new Error("An error occurred while getting the episode servers"); + throw new ScraperErrorResponse("An error occurred while getting the episode servers"); } } - private async getM3U(vidurl: string) { + /* private async getM3U(vidurl: string) { try { const res = await axios.get(vidurl); return res.data; } catch (error) { - console.log(error) + console.log(error); } - } + } */ } diff --git a/src/scraper/sites/anime/animeflv/animeflv_helper.ts b/src/scraper/sites/anime/animeflv/animeflv_helper.ts index f5be52c1..4ea4c5bd 100644 --- a/src/scraper/sites/anime/animeflv/animeflv_helper.ts +++ b/src/scraper/sites/anime/animeflv/animeflv_helper.ts @@ -1,52 +1,63 @@ //genres animeflf export enum Genres { - Action = "Acción", - MartialArts = "Artes Marciales", - Adventure = "Aventuras", - Racing = "Carreras", - ScienceFiction = "Ciencia Ficción", - Comedy = "Comedia", - Dementia = "Demencia", - Demons = "Demonios", - Sports = "Deportes", - Drama = "Drama", - Ecchi = "Ecchi", - School = "Escolares", - Space = "Espacial", - Fantasy = "Fantasía", - Harem = "Harem", - Historical = "Histórico", - Kids = "Infantil", - Josei = "Josei", - Games = "Juegos", - Magic = "Magia", - Mecha = "Mecha", - Military = "Militar", - Mystery = "Misterio", - Music = "Música", - Parody = "Parodia", - Police = "Policía", - Psychological = "Psicológico", - SliceOfLife = "Recuentos de la vida", - Romance = "Romance", - Samurai = "Samurai", - Seinen = "Seinen", - Shoujo = "Shoujo", - Shounen = "Shounen", - Supernatural = "Sobrenatural", - Superpowers = "Superpoderes", - Suspense = "Suspenso", - Horror = "Terror", - Vampires = "Vampiros", - Yaoi = "Yaoi", - Yuri = "Yuri", - } + Action = "accion", + MartialArts = "artes-marciales", + Adventure = "aventura", + Racing = "carreras", + ScienceFiction = "ciencia-ficcion", + Comedy = "comedia", + Dementia = "demencia", + Demons = "demonios", + Sports = "deportes", + Drama = "drama", + Ecchi = "ecchi", + School = "escolares", + Space = "espacial", + Fantasy = "fantasia", + Harem = "harem", + Historical = "historico", + Kids = "infantil", + Josei = "josei", + Games = "juegos", + Magic = "magia", + Mecha = "mecha", + Military = "militar", + Mystery = "misterio", + Music = "musica", + Parody = "parodia", + Police = "policia", + Psychological = "psicologico", + SliceOfLife = "recuentos-de-la-vida", + Romance = "romance", + Samurai = "samurai", + Seinen = "seinen", + Shoujo = "shoujo", + Shounen = "shounen", + Supernatural = "sobrenatural", + Superpowers = "superpoderes", + Suspense = "suspenso", + Horror = "terror", + Vampires = "vampiros", + Yaoi = "yaoi", + Yuri = "yuri", +} - export enum StatusAnimeflv { - OnGoing = "En emision", - Finished = "Finalizado", - Upcoming = "Próximamente", - } +export enum StatusAnimeflv { + OnGoing = "1", + Finished = "2", + Upcoming = "3", +} -export type TypeAnimeflv = "all" | 1 | 2 | 3 | 4; -export type OrderAnimeflv = "all" | 1 | 2 | 3 | 4 | 5; \ No newline at end of file +export enum TypeAnimeflv { + TV = "tv", + MOVIE = "movie", + SPECIAL = "special", + OVA = "ova", +} +export enum OrderAnimeflv { + DEFAULT = "default", + UPDATED = "updated", + ADDED = "added", + TITLE = "title", + RATING = "rating", +} diff --git a/src/scraper/sites/anime/animelatinohd/AnimeLatinoHD.ts b/src/scraper/sites/anime/animelatinohd/AnimeLatinoHD.ts index eee60bdc..c199150a 100644 --- a/src/scraper/sites/anime/animelatinohd/AnimeLatinoHD.ts +++ b/src/scraper/sites/anime/animelatinohd/AnimeLatinoHD.ts @@ -1,165 +1,203 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Anime } from "../../../../types/anime"; +import { AnimeMedia } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { AnimeSearch, ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; - -export class AnimeLatinoHD { - readonly url = "https://www.animelatinohd.com"; - readonly api = "https://api.animelatinohd.com"; - - async GetAnimeInfo(anime: string): Promise { - try { - const { data } = await axios.get(`${this.url}/anime/${anime}`); - const $ = cheerio.load(data); - - const animeInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data - - const AnimeInfo: Anime = { - name: animeInfoParseObj.name, - url: `/anime/animelatinohd/name/${anime}`, - synopsis: animeInfoParseObj.overview, - alt_name: [...animeInfoParseObj.name_alternative.split(",")], - image: { - url: "https://www.themoviedb.org/t/p/original" + animeInfoParseObj.poster + "?&w=53&q=95" - }, - genres: [...animeInfoParseObj.genres.split(",")], - type: animeInfoParseObj.type, - status: animeInfoParseObj.status == 1 ? "En emisión" : "Finalizado", - date: animeInfoParseObj.aired, - episodes: [] - } - - animeInfoParseObj.episodes.map(e => { - const AnimeEpisode: Episode = { - name: animeInfoParseObj.name, - number: e.number + "", - image: "https://www.themoviedb.org/t/p/original" + animeInfoParseObj.banner + "?&w=280&q=95", - url: `/anime/animelatinohd/episode/${animeInfoParseObj.slug + "-" + e.number}` - } - - AnimeInfo.episodes.push(AnimeEpisode); - }) - - return AnimeInfo; - - } catch (error) { - console.log(error) - } +import CryptoJS from 'crypto-js' + +import { + ResultSearch, + AnimeResult +} from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; + +export class AnimeLatinoHD extends AnimeScraperModel { + + readonly url = "https://www.animelatinohd.com"; + readonly api = "https://web.animelatinohd.com"; + readonly key = "l7z8rIhQDXIH6pl66ZEQgPkNwkDlilgdOHMMWkxkzzE=" + + async GetItemInfo(anime: string): Promise { + try { + const { data } = await axios.get(`${this.url}/anime/${anime}`); + const $ = cheerio.load(data); + + const animeInfoParseObj = JSON.parse(this.decrypt(JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data)); + const AnimeInfo: AnimeMedia = { + name: animeInfoParseObj.name, + url: `/anime/animelatinohd/name/${anime}`, + synopsis: animeInfoParseObj.overview, + alt_names: [...animeInfoParseObj.name_alternative.split(",")], + image: { + url: + "https://www.themoviedb.org/t/p/original" + + animeInfoParseObj.poster + + "?&w=53&q=95", + }, + genres: [...animeInfoParseObj.genres.split(",")], + type: animeInfoParseObj.type, + status: animeInfoParseObj.status == 1 ? "En emisión" : "Finalizado", + date: animeInfoParseObj.aired, + episodes: [], + }; + + animeInfoParseObj.episodes.map((e) => { + const AnimeEpisode: Episode = { + name: animeInfoParseObj.name, + num: Number(e.number), + thumbnail:{ + url :"https://www.themoviedb.org/t/p/original" + animeInfoParseObj.banner + "?&w=280&q=95" + }, + url: `/anime/animelatinohd/episode/${ + animeInfoParseObj.slug + "-" + e.number + }`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.log(error); } - async GetEpisodeServers(episode: string, lang: string): Promise { - try { - - const number = episode.substring(episode.lastIndexOf("-") + 1) - const anime = episode.substring(0, episode.lastIndexOf("-")) - const langType = [{ lang: "es", type: "Latino" }, { lang: "jp", type: "Subtitulado" }] - - const { data } = await axios.get(`${this.url}/ver/${anime}/${number}`); - const $ = cheerio.load(data); - - const animeEpisodeParseObj = JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data - - const AnimeEpisodeInfo: Episode = { - name: animeEpisodeParseObj.anime.name, - url: `/anime/animelatinohd/episode/${episode}`, - number: number, - image: "", - servers: [] - } - - const sel_lang = langType.filter((e) => e.lang == lang) - let f_index = 0 - - if (sel_lang.length) { - $("#languaje option").each((_i, e) => { - if ($(e).text() == sel_lang[0].type) { - f_index = Number($(e).val()) - } - }) - } else { - $("#languaje option").each((_i, e) => { - f_index = Number($(e).val()) - }) - } - - await Promise.all(animeEpisodeParseObj.players[f_index].map(async (e: { server: { title: string; }; id: string; }) => { - //let min = await axios.get("https://api.animelatinohd.com/stream/" + e.id, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62", "Referer": "https://www.animelatinohd.com/" } }) - // let dat = cheerio.load(min.data) - - const Server: EpisodeServer = { - name: e.server.title, - url: "", - } - Server.url = "https://api.animelatinohd.com/stream/" + e.id - Server.name = e.server.title - - //state 1 - /*if (e.server.title == "Beta") { - let sel = dat("script:contains('var foo_ui = function (event) {')") - let sort = String(sel.html()) - let domain = eval(sort.slice(sort.search("const url"), sort.search("const langDef")).replace("const url =", "").trim()) - - let sortMORE = sort.slice(sort.search('ajax'), sort.search("method: 'post',")) - let obj_sort = sortMORE.replace("ajax({", "").trim().replace("url:", "").replace(",", "").replace('"', "").replace('"', "").trim() - let id_file = obj_sort.slice(obj_sort.lastIndexOf("/"), obj_sort.length) - Server.url = domain + "/v" + id_file - - } else if (e.server.title == "Gamma") { - Server.url = dat('meta[name="og:url"]').attr("content") - } else { - let sel = dat("script[data-cfasync='false']") - let sort = String(sel.html()) - let sortMORE = sort.slice(sort.lastIndexOf("master") + 7, sort.lastIndexOf("hls2") - 11) - let id_file = sortMORE.replace("_x", "") - Server.url = "https://filemoon.sx" + "/e/" + id_file - }*/ - AnimeEpisodeInfo.servers.push(Server) - })) - - return AnimeEpisodeInfo; - } catch (error) { - console.log(error) + } + async GetEpisodeServers(episode: string, lang: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + const langType = [ + { lang: "es", type: "Latino" }, + { lang: "jp", type: "Subtitulado" }, + ]; + + const { data } = await axios.get(`${this.url}/ver/${anime}/${number}`); + const $ = cheerio.load(data); + + const animeEpisodeParseObj = JSON.parse(this.decrypt(JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data)); + + const AnimeEpisodeInfo: Episode = { + name: animeEpisodeParseObj.anime.name, + url: `/anime/animelatinohd/episode/${episode}`, + num: Number(number), + servers: [], + }; + + const sel_lang = langType.filter((e) => e.lang == lang); + let f_index = 0; + + if (sel_lang.length) { + $("#languaje option").each((_i, e) => { + if ($(e).text() == sel_lang[0].type) { + f_index = Number($(e).val()); + } + }); + } else { + $("#languaje option").each((_i, e) => { + f_index = Number($(e).val()); + }); + } + + + for (let index = 0; index < animeEpisodeParseObj.players[f_index].length; index++) { + //const warpVideo = await axios.get(this.api +'/video/'+this.encrypt(JSON.stringify(animeEpisodeParseObj.players[f_index][index].id))) + const Server: EpisodeServer = { + name: animeEpisodeParseObj.players[f_index][index].server.title, + url: "", + }; + Server.url = `${this.api}/video/${this.encrypt(JSON.stringify(animeEpisodeParseObj.players[f_index][index].id))}`; + Server.name = animeEpisodeParseObj.players[f_index][index].server.title; + + AnimeEpisodeInfo.servers.push(Server); } + + + AnimeEpisodeInfo.servers.sort((a,b) => a.name.localeCompare(b.name)) + return AnimeEpisodeInfo; + } catch (error) { + console.log(error); } - - async GetAnimeByFilter(search?: string, type?: number, page?: number, year?: string, genre?: string): Promise> { - try { - const { data } = await axios.get(`${this.api}/api/anime/list`, { - params: { - search: search, - type: type, - year: year, - genre: genre, - page: page - } - }); - - const animeSearchParseObj = data - - const animeSearch: ResultSearch = { - nav: { - count: animeSearchParseObj.data.length, - current: animeSearchParseObj.current_page, - next: animeSearchParseObj.data.length < 28 ? 0 : animeSearchParseObj.current_page + 1, - hasNext: animeSearchParseObj.data.length < 28 ? false : true - }, - results: [] - } - animeSearchParseObj.data.map(e => { - const animeSearchData: AnimeSearch = { - name: e.name, - image: "https://www.themoviedb.org/t/p/original" + e.poster + "?&w=53&q=95", - url: `/anime/animelatinohd/name/${e.slug}`, - type: "" - } - animeSearch.results.push(animeSearchData) - }) - return animeSearch; - } catch (error) { - console.log(error) - } + } + + decrypt(data: string){ + let t = CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8) + t = JSON.parse(t) + const a = CryptoJS.enc.Base64.parse(t.iv) + const n = CryptoJS.AES.decrypt(t.value, CryptoJS.enc.Base64.parse(this.key), { + iv:a, + mode:CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7 + }) + return CryptoJS.enc.Utf8.stringify(n) + } + encrypt(data:string | number){ + let t = CryptoJS.lib.WordArray.random(16) + let r + const a = CryptoJS.enc.Base64.parse(this.key) + const n = { + iv: t, + mode:CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7 + } + + const s = CryptoJS.AES.encrypt(data, a, n).toString(); + + r = { + iv: t = CryptoJS.enc.Base64.stringify(t), + value: s, + mac: CryptoJS.HmacSHA256(t + s, a).toString() + }; + r = JSON.stringify(r) + r = CryptoJS.enc.Utf8.parse(r) + + return CryptoJS.enc.Base64.stringify(r) + } + async GetItemByFilter( + search?: string, + type?: number, + page?: number, + year?: string, + genre?: string + ): Promise> { + try { + + const { data } = await axios.get(`${this.api}/api/anime/list`, { + params: { + search: search, + type: type, + year: year, + genre: genre, + page: page, + }, + }); + + const animeSearchParseObj = JSON.parse(this.decrypt(data.data)); + + const animeSearch: ResultSearch = { + nav: { + count: animeSearchParseObj.data.length, + current: animeSearchParseObj.current_page, + next: + animeSearchParseObj.data.length < 28 + ? 0 + : animeSearchParseObj.current_page + 1, + hasNext: animeSearchParseObj.data.length < 28 ? false : true, + }, + results: [], + }; + animeSearchParseObj.data.map((e) => { + const animeSearchData: AnimeResult = { + name: e.name, + image: + "https://www.themoviedb.org/t/p/original" + + e.poster + + "?&w=53&q=95", + url: `/anime/animelatinohd/name/${e.slug}`, + type: "", + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.log(error); } - + } } - - diff --git a/src/scraper/sites/anime/animevostfr/Animevostfr.ts b/src/scraper/sites/anime/animevostfr/Animevostfr.ts index 68c899d4..8901921e 100644 --- a/src/scraper/sites/anime/animevostfr/Animevostfr.ts +++ b/src/scraper/sites/anime/animevostfr/Animevostfr.ts @@ -1,166 +1,197 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Anime } from "../../../../types/anime"; +import { AnimeMedia } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { AnimeSearch, ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; -//import { Calendar } from "@animetypes/date"; +import { + ResultSearch, + AnimeResult, +} from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; /** List of Domains - * + * * https://animevostfr.tv - * -*/ - -export class Animevostfr { - readonly url = "https://animevostfr.tv"; - readonly api = "https://api.animelatinohd.com"; - - async GetAnimeInfo(anime: string): Promise { - try { - const { data } = await axios.get(`${this.url}/${anime}`); - const $ = cheerio.load(data); - - - const AnimeTypes = $(".mvic-info .mvici-right p strong:contains(' Type:')").nextAll().text() - const AnimeStatus = $(".mvic-info .mvici-right p a[rel='tag']").first().text() - const AnimeDate = $(".mvic-info .mvici-right p strong:contains(' An:')").nextAll().text() - const AnimeDescription = $(".mvi-content .mvic-desc .desc p").html() - - - - - const AnimeInfo: Anime = { - name: $(".mvi-content .mvic-desc h1").text(), - url: `/anime/animevostfr/name/${anime}`, - synopsis: AnimeDescription.slice(AnimeDescription.indexOf("Synopsis:") + "Synopsis:".length, -1).trim(), - alt_name: [...AnimeDescription.slice(AnimeDescription.indexOf("Titre alternatif:") + "Titre alternatif:".length, AnimeDescription.indexOf("Synopsis:")).replace("
\n", "").split("/").map((n) => n.replace(/^\s+|\s+$|\s+(?=\s)/g, ""))], - image: { - url: $(".mvi-content .mvic-thumb img").attr("data-lazy-src") - }, - genres: [...$(".mvic-info .mvici-left p").first().text().replace("\n Genres:\n ", "").split(",").map((n) => n.replace(/^\s+|\s+$|\s+(?=\s)/g, ""))], - type: AnimeTypes == "Anime" ? "Anime" : AnimeTypes == "MOVIE" ? "Movie" : "Null", //tv,pelicula,especial,ova - status: AnimeStatus == "En cours" ? true : false, - date: AnimeDate ? { year: AnimeDate } : null, - episodes: [] - } - - $("#seasonss .les-title").each((_i, e) => { - - const number = $(e).find("a").attr("href").substring($(e).find("a").attr("href").lastIndexOf("-") + 1).replace("/", "") - const AnimeEpisode: Episode = { - name: "Episode " + number, - number: number, - image: "", - url: `/anime/animevostfr/episode/${anime + "-" + number}` - } - - AnimeInfo.episodes.push(AnimeEpisode); - }) - - return AnimeInfo; - - } catch (error) { - console.log(error) - } + * + */ + +export class Animevostfr extends AnimeScraperModel { + readonly url = "https://animevostfr.tv"; + + async GetItemInfo(anime: string): Promise { + try { + const { data } = await axios.get(`${this.url}/${anime}`); + const $ = cheerio.load(data); + + const AnimeTypes = $( + ".mvic-info .mvici-right p strong:contains(' Type:')" + ) + .nextAll() + .text(); + const AnimeStatus = $(".mvic-info .mvici-right p a[rel='tag']") + .first() + .text(); + const AnimeDate = $(".mvic-info .mvici-right p strong:contains(' An:')") + .nextAll() + .text(); + const AnimeDescription = $(".mvi-content .mvic-desc .desc p").html(); + + const AnimeInfo: AnimeMedia = { + name: $(".mvi-content .mvic-desc h1").text(), + url: `/anime/animevostfr/name/${anime}`, + synopsis: AnimeDescription.slice( + AnimeDescription.indexOf("Synopsis:") + "Synopsis:".length, + -1 + ).trim(), + alt_names: [ + ...AnimeDescription.slice( + AnimeDescription.indexOf("Titre alternatif:") + + "Titre alternatif:".length, + AnimeDescription.indexOf("Synopsis:") + ) + .replace("
\n", "") + .split("/") + .map((n) => n.replace(/^\s+|\s+$|\s+(?=\s)/g, "")), + ], + image: { + url: $(".mvi-content .mvic-thumb img").attr("data-lazy-src"), + }, + genres: [ + ...$(".mvic-info .mvici-left p") + .first() + .text() + .replace("\n Genres:\n ", "") + .split(",") + .map((n) => n.replace(/^\s+|\s+$|\s+(?=\s)/g, "")), + ], + type: + AnimeTypes == "Anime" + ? "Anime" + : AnimeTypes == "MOVIE" + ? "Movie" + : "Null", //tv,pelicula,especial,ova + status: AnimeStatus == "En cours" ? true : false, + date: AnimeDate ? { year: AnimeDate } : null, + episodes: [], + }; + + $("#seasonss .les-title").each((_i, e) => { + const number = $(e) + .find("a") + .attr("href") + .substring($(e).find("a").attr("href").lastIndexOf("-") + 1) + .replace("/", ""); + const AnimeEpisode: Episode = { + name: "Episode " + number, + num: Number(number), + url: `/anime/animevostfr/episode/${anime + "-" + number}`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.log(error); } - async GetEpisodeServers(episode: string): Promise { - try { - - const number = episode.substring(episode.lastIndexOf("-") + 1) - const anime = episode.substring(0, episode.lastIndexOf("-")) - - const { data } = await axios.get(`${this.url}/episode/${anime}-episode-${number}`); - const $ = cheerio.load(data); - const s = $('.form-group.list-server select option') - const e = $('.list-episodes select option') - const ListFilmId = [] - const ListServer = [] - - s.map((_i, e) => ListServer.push($(e).val())) - e.map((_i, e) => ListFilmId.push($(e).attr("episodeid"))) - - /* - "SERVER_VIP" - "SERVER_HYDRAX" - "SERVER_PHOTOSS" - "SERVER_DOWNLOAD" - "SERVER_PHOTOS" - "SERVER_OPEN_LOAD" - "SERVER_OPEN_LOADS" - "SERVER_OPEN_CDN" - "SERVER_OPEN_CDNO" - "SERVER_PHOTO" - "SERVER_STREAM_MANGO" - "SERVER_RAPID_VIDEO" - */ - - const AnimeEpisodeInfo: Episode = { - name: "Episode " + number, - url: `/anime/animevostfr/episode/${episode}`, - number: number, - image: "", - servers: [] - } - - await Promise.all(ListServer.map(async (n) => { - const servers = await axios.get(`${this.url}/ajax-get-link-stream/?server=${n}&filmId=${ListFilmId[0]}`) - let currentData = servers.data - if (n == "opencdn" || n == "photo") { - currentData = currentData.replace("?logo=https://animevostfr.tv/1234.png", "").replace("short.ink/", "abysscdn.com/?v=") - } - const Servers: EpisodeServer = { - name: n, - url: currentData, - } - AnimeEpisodeInfo.servers.push(Servers) - })) - - - - AnimeEpisodeInfo.servers.sort((a: EpisodeServer, b: EpisodeServer) => a.name.length - b.name.length) - return AnimeEpisodeInfo; - } catch (error) { - console.log(error) - } + } + async GetEpisodeServers(episode: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + + const { data } = await axios.get( + `${this.url}/episode/${anime}-episode-${number}` + ); + const $ = cheerio.load(data); + const s = $(".form-group.list-server select option"); + const e = $(`.list-episodes select option[value='${number}']`); + const ListFilmId = $(e).attr("episodeid"); + const ListServer = []; + s.map((_i, e) => ListServer.push($(e).val())); + /* + "SERVER_VIP" + "SERVER_HYDRAX" + "SERVER_PHOTOSS" + "SERVER_DOWNLOAD" + "SERVER_PHOTOS" + "SERVER_OPEN_LOAD" + "SERVER_OPEN_LOADS" + "SERVER_OPEN_CDN" + "SERVER_OPEN_CDNO" + "SERVER_PHOTO" + "SERVER_STREAM_MANGO" + "SERVER_RAPID_VIDEO" + */ + const AnimeEpisodeInfo: Episode = { + name: "Episode " + number, + url: `/anime/animevostfr/episode/${episode}`, + num: Number(number), + servers: [], + }; + await Promise.all( + ListServer.map(async (n: string) => { + if (n == "opencdn" || n == "photo" || n == "vip") { + const sservers = await axios.get( + `${this.url}/ajax-get-link-stream/?server=${n}&filmId=${ListFilmId}` + ); + let currentData = sservers.data; + currentData = currentData + .replace(`?logo=${this.url}/1234.png`, "") + .replace("hydrax.net/watch", "abysscdn.com/") + .replace("short.ink/", "abysscdn.com/?v="); + const Servers: EpisodeServer = { + name: n, + url: currentData, + }; + AnimeEpisodeInfo.servers.push(Servers); + } + return AnimeEpisodeInfo + }) + ) + return AnimeEpisodeInfo; + } catch (error) { + console.log(error) } - - async GetAnimeByFilter(search?: string, type?: number, page?: number, year?: string, genre?: string): Promise> { - try { - const { data } = await axios.get(`${this.api}/api/anime/list`, { - params: { - search: search, - type: type, - year: year, - genre: genre, - page: page - } - }); - - const animeSearchParseObj = data - - const animeSearch: ResultSearch = { - nav: { - count: animeSearchParseObj.data.length, - current: animeSearchParseObj.current_page, - next: animeSearchParseObj.data.length < 28 ? 0 : animeSearchParseObj.current_page + 1, - hasNext: animeSearchParseObj.data.length < 28 ? false : true - }, - results: [] - } - animeSearchParseObj.data.map(e => { - const animeSearchData: AnimeSearch = { - name: e.name, - image: "https://www.themoviedb.org/t/p/original" + e.poster + "?&w=53&q=95", - url: `/anime/animelatinohd/name/${e.slug}`, - type: "" - } - animeSearch.results.push(animeSearchData) - }) - return animeSearch; - } catch (error) { - console.log(error) - } + } + + async GetItemByFilter( + search?: string, + page?: number + ): Promise> { + try { + const { data } = await axios.get(`${this.url}/page/${page ? page : 1}`, { + params: { + s: search + }, + }); + + const $ = cheerio.load(data); + + const animeSearch: ResultSearch = { + nav: { + count: $(".movies-list .ml-item").length, + current: page ? Number(page) : 1, + next: + $(".movies-list .ml-item").length < 32 + ? 0 + : Number(page) + 1, + hasNext: $(".movies-list .ml-item").length < 32 ? false : true, + }, + results: [], + }; + + $(".movies-list .ml-item").each((_i, e) => { + const animeSearchData: AnimeResult = { + name: $(e).find(".mli-info").text(), + image: $(e).find(".mli-thumb").attr("data-original"), + url: `/anime/animevostfr/name/${$(e).find(".ml-mask").attr("href").replace(this.url, "").replace("/", "").replace("/", "")}`, + type: $(e).find(".mli-quality").text().includes("Movie") ? "movie" : "anime", + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.log(error); } - + } } - diff --git a/src/scraper/sites/anime/gogoanime/Gogoanime.ts b/src/scraper/sites/anime/gogoanime/Gogoanime.ts index 05b711a7..70cae333 100644 --- a/src/scraper/sites/anime/gogoanime/Gogoanime.ts +++ b/src/scraper/sites/anime/gogoanime/Gogoanime.ts @@ -3,178 +3,146 @@ import { Anime } from "../../../../types/anime"; import { getAllAnimes } from "./assets/getAllAnimesHTML"; import { Episode } from "../../../../types/episode"; - //This is a class export class GogoanimeInfo { - async getAnimeInfo(animeName: string) { - - try { - - const $ = await getHTML(`https://www3.gogoanimes.fi/category/${animeName}`); - - const anime = new Anime; - - anime.genres = []; - - anime.name = $("div.anime_info_body_bg h1").text(); - - anime.image = { - url: $("div.anime_info_body_bg ").find("img").attr("src") - } - - anime.alt_name = $("div.anime_info_body_bg"). - find("p"). - last(). - text(). - replace("Other name: ", ""). - trim(); - - - - $("div.anime_info_body_bg p.type a").each((iterator, elementHTML) => { - - if (iterator) - - anime.genres.push($(elementHTML).html()); - - }) - - - $('div.anime_info_body_bg p.type').each((index, element) => { - //Skips for first p.type - if (index) - - if (index == 1) { - anime.synopsis = $(element).text().replace('Plot Summary: ', '').trim(); - } - - if (index == 4 && $(element).text().trim() != 'Status: ') { + try { + const $ = await getHTML( + `https://www3.gogoanimes.fi/category/${animeName}` + ); + + const anime = new Anime(); + + anime.genres = []; + + anime.name = $("div.anime_info_body_bg h1").text(); + + anime.image = { + url: $("div.anime_info_body_bg ").find("img").attr("src"), + }; + + anime.alt_name = $("div.anime_info_body_bg") + .find("p") + .last() + .text() + .replace("Other name: ", "") + .trim(); + + $("div.anime_info_body_bg p.type a").each((iterator, elementHTML) => { + if (iterator) anime.genres.push($(elementHTML).html()); + }); + + $("div.anime_info_body_bg p.type").each((index, element) => { + //Skips for first p.type + if (index) + if (index == 1) { + anime.synopsis = $(element) + .text() + .replace("Plot Summary: ", "") + .trim(); + } + + if (index == 4 && $(element).text().trim() != "Status: ") { anime.status = true; - } - - if (index == 5) { - anime.alt_name = $(element).text().trim() - .replace('Other name:', '') - .replace(/\s/g, '') } - }) - + if (index == 5) { + anime.alt_name = $(element) + .text() + .trim() + .replace("Other name:", "") + .replace(/\s/g, ""); + } + }); - let getNumberEpisodes: any = $('#episode_page li').last().text().trim().split("-")[1]; - getNumberEpisodes = parseInt(getNumberEpisodes); + let getNumberEpisodes: any = $("#episode_page li") + .last() + .text() + .trim() + .split("-")[1]; + getNumberEpisodes = parseInt(getNumberEpisodes); - for (let index = 1; index <= getNumberEpisodes; index++) { - anime.episodes.push({ + for (let index = 1; index <= getNumberEpisodes; index++) { + anime.episodes.push({ name: `${animeName}-cap-${index}`, - url: `/anime/gogoanime/episode/${animeName}/${index}`,//sorry for the change + url: `/anime/gogoanime/episode/${animeName}/${index}`, //sorry for the change number: `${index}`, - image: "That isn't image" - }) - } - - return anime + image: "That isn't image", + }); + } - } catch(error) { + return anime; + } catch (error) { return error; } - - } - } export class GogoanimeFilter { + async getAnimesfilterByGenre(genre: string, numPage: number) { + let animesByGenre = await getAllAnimes( + `https://www3.gogoanimes.fi/genre/${genre}`, + numPage + ); - async getAnimesfilterByGenre(genre: string, numPage: number) { - - - let animesByGenre = await getAllAnimes( - - `https://www3.gogoanimes.fi/genre/${genre}`, numPage - - ) - - - return animesByGenre; - - } - - - async filterBySeasons( season: string, year: string, numPage: number ) { + return animesByGenre; + } + async filterBySeasons(season: string, year: string, numPage: number) { let animes = await getAllAnimes( - `https://www3.gogoanimes.fi/sub-category/${season}-${year}-anime`, numPage + `https://www3.gogoanimes.fi/sub-category/${season}-${year}-anime`, + numPage ); - - return animes - + return animes; } - } - - - export class GogoanimeServer { - - async getAnimeServerEpisode(animeName: string, episodeNumber: number) { - - + async getAnimeServerEpisode(animeName: string, episodeNumber: number) { let serverUrl: string; let serverName: string; const $ = await getHTML( - `https://www3.gogoanimes.fi/${animeName}-episode-${episodeNumber}` + `https://www3.gogoanimes.fi/${animeName}-episode-${episodeNumber}` ); const episode = new Episode(); - - episode.name = "This isn't name"; - episode.servers = []; - - - - $(".anime_muti_link ul li ").each((iterator, element) => { + episode.name = "This isn't name"; + episode.servers = []; - if (iterator == 0 || iterator == 1){ - - serverName = $(element).find("a").text(). - replace(" this server", "").trim(); + $(".anime_muti_link ul li ").each((iterator, element) => { + if (iterator == 0 || iterator == 1) { + serverName = $(element) + .find("a") + .text() + .replace(" this server", "") + .trim(); serverUrl = `http:${$(element).find("a").attr("data-video")}`; - - episode.servers.push({ + episode.servers.push({ name: serverName, - url: serverUrl + url: serverUrl, }); - - }if(iterator > 2) { - - - serverName = $(element).find("a").text(). - replace(" this server", "").trim(); - - serverUrl = $(element).find("a").attr("data-video"); + } + if (iterator > 2) { + serverName = $(element) + .find("a") + .text() + .replace(" this server", "") + .trim(); + serverUrl = $(element).find("a").attr("data-video"); - episode.servers.push({ + episode.servers.push({ name: serverName, - url: serverUrl + url: serverUrl, }); - } - - }) + }); return episode; } - } - - - diff --git a/src/scraper/sites/anime/gogoanime/assets/getAllAnimesHTML.ts b/src/scraper/sites/anime/gogoanime/assets/getAllAnimesHTML.ts index a89ebfe4..5735a252 100644 --- a/src/scraper/sites/anime/gogoanime/assets/getAllAnimesHTML.ts +++ b/src/scraper/sites/anime/gogoanime/assets/getAllAnimesHTML.ts @@ -1,77 +1,57 @@ import { IAnime } from "@animetypes/anime"; import { getHTML } from "./getHTML"; - - export async function getAllAnimes(url: string, numPage: number) { - - try { - - + try { let animes: IAnime[] = []; - - let $ = await getHTML( - `${url}?page=${numPage}` - ); - - let pageState = $(".anime_name h2").text(). - replace("ADVERTISEMENTSRECENT RELEASESeason", ""). - trim(); + let $ = await getHTML(`${url}?page=${numPage}`); - if ( pageState != "404 Not found" ) { + let pageState = $(".anime_name h2") + .text() + .replace("ADVERTISEMENTSRECENT RELEASESeason", "") + .trim(); + if (pageState != "404 Not found") { $(".last_episodes ul li").each((_, element) => { + let animeName = $(element).find("p.name").find("a").text().trim(); + let animeImage = $(element) + .find(".img") + .find("a") + .find("img") + .attr("src"); - let animeName = $(element).find("p.name").find("a"). - text(). - trim(); - - - let animeImage = $(element).find(".img").find("a"). - find("img"). - attr("src"); + let animeNameUrl = $(element) + .find(".img") + .find("a") + .attr("href") + .replace("/category/", ""); - let animeNameUrl= $(element).find(".img").find("a"). - attr("href"). - replace("/category/", ""); - - let year: string | number = $(element).find("p.released"). - text(). - replace("Released: ", ""); + let year: string | number = $(element) + .find("p.released") + .text() + .replace("Released: ", ""); year = parseInt(year); - animes.push({ - name: animeName, - image: { - url: animeImage + animes.push({ + name: animeName, + image: { + url: animeImage, + }, + url: `/anime/gogoanime/name/${animeNameUrl}`, + date: { + begin: { + year: year, }, - url: `/anime/gogoanime/name/${animeNameUrl}`, - date: { - begin: { - year: year, - } - } - - }) - - - }) - + }, + }); + }); } - - - - return animes - - } catch( error ){ - - return error - - } - - + return animes; + } catch (error) { + return error; + } } diff --git a/src/scraper/sites/anime/gogoanime/assets/getHTML.ts b/src/scraper/sites/anime/gogoanime/assets/getHTML.ts index 5d2b6e96..81670458 100644 --- a/src/scraper/sites/anime/gogoanime/assets/getHTML.ts +++ b/src/scraper/sites/anime/gogoanime/assets/getHTML.ts @@ -2,15 +2,8 @@ import { load } from "cheerio"; import axios from "axios"; export async function getHTML(url: string) { - const { data } = await axios.get(`${url}`); const pageHTML = load(data); return pageHTML; - } - - - - - diff --git a/src/scraper/sites/anime/hentaihaven/HentaiHaven.ts b/src/scraper/sites/anime/hentaihaven/HentaiHaven.ts new file mode 100644 index 00000000..8ca5dd1b --- /dev/null +++ b/src/scraper/sites/anime/hentaihaven/HentaiHaven.ts @@ -0,0 +1,131 @@ +import * as cheerio from "cheerio"; +import axios from "axios"; +import { AnimeMedia } from "../../../../types/anime"; +import { Episode, EpisodeServer } from "../../../../types/episode"; + +import { ResultSearch, AnimeResult } from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; + +export class HentaiHaven extends AnimeScraperModel { + readonly url = "https://hentaihaven.com"; + + async GetItemInfo(anime: string): Promise { + try { + const { data } = await axios.get(`${this.url}/video/${anime}`); + const $ = cheerio.load(data); + const genres = []; + + $(".single_data .list") + .first() + .find("a") + .each((_i, e) => genres.push($(e).text())); + + const AnimeInfo: AnimeMedia = { + name: $("h1.htitle").text().trim(), + url: `/anime/hentaihaven/name/${anime}`, + synopsis: $(".vraven_expand .vraven_text.single p") + .last() + .text() + .replace(/\n/g, ""), + alt_names: [ + ...$(".col-12.col-xl-9 h3.h4") + .text() + .split(",") + .map((v) => v.trim()), + ], + image: { + url: $(".hentai_cover .shadow img").attr("src"), + }, + nsfw: true, + genres: [...genres], + episodes: [], + }; + + $(".hentai__episodes ul li").each((_i, e) => { + const number = Number( + $(e).find("a .chapter_info .title").text().replace("Episode ", "") + ); + const AnimeEpisode: Episode = { + name: $(e).find("a .chapter_info .title").text(), + num: number, + thumbnail: { + url: $(e).find("a img").attr("src"), + }, + url: `/anime/hentaihaven/episode/${anime}-${number}`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.error(error); + } + } + async GetEpisodeServers(episode: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + + const { data } = await axios.get( + `${this.url}/video/${anime}/episode-${number}` + ); + const $ = cheerio.load(data); + + const AnimeEpisodeInfo: Episode = { + name: $(".video_footer #chapter-heading").text(), + url: `/anime/hentaihaven/episode/${episode}`, + num: Number(number), + servers: [], + }; + + const Server: EpisodeServer = { + name: "Main Server", + url: $(".player_logic_item iframe").attr("src"), + }; + AnimeEpisodeInfo.servers.push(Server); + + return AnimeEpisodeInfo; + } catch (error) { + console.error(error); + } + } + + async GetItemByFilter( + search?: string, + page: number = 1 + ): Promise> { + try { + const { data } = await axios.get(`${this.url}/page/${page}`, { + params: { + s: search, + }, + }); + const $ = cheerio.load(data); + const count = $(".row_items .item").length; + const animeSearch: ResultSearch = { + nav: { + count: count, + current: Number(page), + next: count < 40 ? 1 : Number(page) + 1, + hasNext: count < 40 ? false : true, + }, + results: [], + }; + $(".row_items .item").each((_i, e) => { + const animeSearchData: AnimeResult = { + name: $(e).find(".title").text(), + image: $(e).find(".cover img").attr("src"), + url: `/anime/hentaiheven/name/${$(e) + .find(".title") + .text() + .replace(this.url + "/video", "")}`, + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.error(error); + } + } +} diff --git a/src/scraper/sites/anime/hentaila/HentaiLa.ts b/src/scraper/sites/anime/hentaila/HentaiLa.ts new file mode 100644 index 00000000..505c1b22 --- /dev/null +++ b/src/scraper/sites/anime/hentaila/HentaiLa.ts @@ -0,0 +1,139 @@ +import * as cheerio from "cheerio"; +import axios from "axios"; +import { AnimeMedia } from "../../../../types/anime"; +import { Episode, EpisodeServer } from "../../../../types/episode"; + +import { ResultSearch, AnimeResult } from "../../../../types/search"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; + +export class HentaiLa extends AnimeScraperModel { + readonly url = "https://www4.hentaila.com"; + + async GetItemInfo(anime: string): Promise { + try { + const formatUrl = !anime.includes("hentai-") ? "hentai-" + anime : anime; + const { data } = await axios.get(`${this.url}/${formatUrl}`); + const $ = cheerio.load(data); + const genres = []; + $(".genres a").each((_i, e) => genres.push($(e).text())); + + const AnimeInfo: AnimeMedia = { + name: $(".hentai-single .h-header h1.h-title").text().trim(), + url: `/anime/hentaila/name/${anime}`, + synopsis: $(".hentai-single .h-content p").text(), + image: { + url: `${this.url}${$(".hentai-single .h-thumb figure img").attr( + "src" + )}`, + }, + genres: [...genres], + nsfw: true, + type: "Anime", + status: $(".h-meta .status-on").text().includes("En Emision") + ? "En emisión" + : "Finalizado", + episodes: [], + }; + + $(".episodes-list article").each((_i, e) => { + const number = Number( + $(e) + .find("h2.h-title") + .text() + .replace( + $(".hentai-single .h-header h1.h-title").text() + " Episodio", + "" + ) + ); + const AnimeEpisode: Episode = { + name: $(e).find("h2.h-title").text(), + num: number, + thumbnail: { + url: `${this.url}${$(e).find(".h-thumb figure img").attr("src")}`, + }, + url: `/anime/hentaila/episode/${formatUrl}-${number}`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.log(error); + } + } + async GetEpisodeServers(episode: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + const formaturl = anime.includes("hentai-") + ? anime.replace("hentai-", "") + : anime; + const { data } = await axios.get( + `${this.url}/ver/${formaturl}-${number}` + ); + const $ = cheerio.load(data); + + const AnimeEpisodeInfo: Episode = { + name: $(".section-header h1.section-title").text(), + url: `/anime/hentaila/episode/${episode}`, + num: Number(number), + servers: [], + }; + + const video_script = $("script").get().at(-3).children[0].data; + const video_format = eval( + video_script + .slice( + video_script.indexOf("var videos ="), + video_script.lastIndexOf("$(document)") + ) + .replace("var videos =", "") + ); + for (let index = 0; index < video_format.length; index++) { + const Server: EpisodeServer = { + name: video_format[index][0].includes("Yupi") ? "YourUpload" : video_format[index][0], + url: video_format[index][1], + }; + AnimeEpisodeInfo.servers.push(Server); + } + + AnimeEpisodeInfo.servers.sort((a, b) => a.name.localeCompare(b.name)); + return AnimeEpisodeInfo; + } catch (error) { + console.log(error); + } + } + + async GetItemByFilter( + search?: string + ): Promise> { + try { + const content = new FormData(); + content.append("value", search); + const { data } = await axios.post(`${this.url}/api/search`, content); + + const animeSearch: ResultSearch = { + nav: { + count: data.length, + current: 1, + next: 0, + hasNext: false, + }, + results: [], + }; + data.map((e) => { + const animeSearchData: AnimeResult = { + name: e.title, + image: `${this.url}/uploads/portadas/${e.id}.jpg`, + url: `/anime/hentaila/name/${e.slug}`, + type: "Anime", + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.log(error); + } + } +} diff --git a/src/scraper/sites/anime/monoschinos/Monoschinos.ts b/src/scraper/sites/anime/monoschinos/Monoschinos.ts index 30f28b50..0cf5686a 100644 --- a/src/scraper/sites/anime/monoschinos/Monoschinos.ts +++ b/src/scraper/sites/anime/monoschinos/Monoschinos.ts @@ -1,8 +1,8 @@ -import axios from "axios"; +import axios, { AxiosResponse } from "axios"; import * as cheerio from "cheerio"; import { api, utils } from "../../../../types/utils"; import * as types from "../../../../types/."; -import { ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; +import { ResultSearch, IResultSearch, AnimeResult } from "../../../../types/search"; const PageInfo = { name: 'monoschinos', @@ -21,11 +21,19 @@ const PageInfo = { async function getEpisodeServers(url: string): Promise { let servers: types.EpisodeServer[] = []; const $ = cheerio.load((await axios.get(url)).data); - $('div.playother').children().each((_i, element) => { - servers.push(new types.EpisodeServer($(element).text().trim(), - Buffer.from($(element).attr('data-player'), 'base64').toString('binary')) - ); + const referenceElement = $('p.fs-5.text-light.my-4'); + const divElement = referenceElement.next('div.d-flex'); + + if (divElement.length === 0) { + throw new Error('The div following the reference element was not found'); + } + + divElement.find('a').each((_i, element) => { + servers.push(new types.EpisodeServer($(element).text().trim(), + $(element).attr('href') + )); }); + return servers; } @@ -35,12 +43,12 @@ async function getEpisodeServers(url: string): Promise { * @param element * @returns */ -async function getEpisodeByElement($, element): Promise { - const episode = new types.Episode(); - episode.number = parseInt($(element).find('div.positioning p').text().trim()); - episode.image = $(element).find('div.animeimgdiv img.animeimghv').attr('data-src'); - episode.name = $(element).find('h2.animetitles').text().trim(); - episode.url = api.getEpisodeURL(PageInfo, $(element).find('a').attr('href')); +function getEpisodeByElement($: cheerio.Root, element: cheerio.Element): types.Episode { + const episode = new types.Episode(); + episode.num = parseInt($(element).find('span.episode').text().trim()); + episode.thumbnail = new types.Image($(element).find('img').attr('data-src')); + episode.name = $(element).find('h2').text(); + episode.url = api.getEpisodeURL(PageInfo, $(element).find('a').attr('href')); return episode; } @@ -52,12 +60,9 @@ async function getEpisodeByElement($, element): Promise { async function getLastEpisodes(): Promise { let episodes: types.Episode[] = []; const $ = cheerio.load((await axios.get(PageInfo.url)).data); - const elements = $('div.heroarea div.heroarea1 div.row').children(); - for (let i = 0; i < elements.length; i++) { - if ($(elements[i]).children().length != 0) { - episodes.push(await getEpisodeByElement($, elements[i])); - } - } + $('ul.row.row-cols-xl-4.row-cols-lg-4.row-cols-md-3.row-cols-2').find('li').each((_i, element) => { + episodes.push(getEpisodeByElement($, element)); + }); return episodes; } @@ -66,10 +71,10 @@ async function getLastEpisodes(): Promise { * @param $ * @returns */ -function getGenres($): string[] { +function getGenres($: cheerio.Root): string[] { let genres: string[] = []; - $('div.chapterdetls2 table tbody a').each((_i, element) => { - genres.push($(element).text().trim()) + $('div.tab-content div.tab-pane div.lh-lg a').each((_i, element) => { + genres.push($(element).find('span').text().trim()) }); return genres; } @@ -77,18 +82,33 @@ function getGenres($): string[] { /** * * @param $ + * @param pageData + * @param animeName * @returns */ -function getAnimeEpisodes($): types.Episode[] { +async function getAnimeEpisodes($: cheerio.Root, animeName: string, pageData: AxiosResponse, animePath: string): Promise { + const response = await fetch($('.caplist').attr('data-ajax'), { + "headers": { + "accept": "application/json, text/javascript, */*; q=0.01", + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + "cookie": pageData.headers['set-cookie'].join(";"), + }, + "body": `_token=${$('meta[name="csrf-token"]').attr('content')}`, + "method": "POST" + }) + let episodes: types.Episode[] = []; - $('div.heromain2 div.allanimes div.row').children().each((_i, element) => { - const episode = new types.Episode(); - episode.number = parseInt($(element).attr('data-episode').trim()); - episode.image = $(element).find('img.animeimghv').attr('data-src'); - episode.name = $(element).find('img.animeimghv').attr('alt'); - episode.url = api.getEpisodeURL(PageInfo, $(element).find('a').attr('href')); + + const length = (await response.json()).eps.length + const image = $('div img.lazy.w-100').attr('data-src') + for (let i = 1; i <= length; i++) { + const episode = new types.Episode(); + episode.num = i; + episode.thumbnail = new types.Image(image); + episode.name = `${animeName} Episodio ${i}`; + episode.url = api.getEpisodeURL(PageInfo, `https://monoschinos2.com/ver/${animePath}-episodio-${i}`); episodes.push(episode); - }); + } return episodes; } @@ -111,8 +131,8 @@ interface ClimaticCalendar { * @param element * @returns the calendar of anime */ -function getAnimeCalendar(element): ClimaticCalendar { - const date = element.find('ol.breadcrumb li.breadcrumb-item').text().trim().split(' '); +function getAnimeCalendar(strDate: string): ClimaticCalendar { + const date = strDate.split(' '); if (date.length != 5) return { year: 0, station: null }; else { @@ -129,47 +149,59 @@ function getAnimeCalendar(element): ClimaticCalendar { * @param url * @returns */ -async function getAnime(url: string): Promise { +async function getAnime(url: string): Promise { // The anime page in monoschinos does not define the chronology and type - const $ = cheerio.load((await axios.get(url)).data); - const calendar = getAnimeCalendar($($('div.chapterdetails nav').children()[1])); - const anime = new types.Anime(); - anime.name = $('div.chapterdetails').find('h1').text(); - anime.alt_name = $('div.chapterdetails').find('span.alterno').text(); - anime.url = api.getAnimeURL(PageInfo, url); - anime.synopsis = $('div.chapterdetls2 p').text().trim(); - anime.genres = getGenres($); - anime.image = new types.Image($('div.chapterpic img').attr('src'), $('div.herobg img').attr('src')); - anime.status = 'estreno' === $('div.butns button.btn1').text().toLowerCase().trim(); - anime.episodes = getAnimeEpisodes($); - anime.date = new types.Calendar(calendar.year); - anime.station = calendar.station; + const pageData = await axios.get(url); + const $ = cheerio.load(pageData.data); + + const info = $('div.tab-content div.bg-transparent dl').children(); + + const calendar = getAnimeCalendar($(info[3]).text().trim()); + const anime = new types.AnimeMedia(); + anime.name = $(info[5]).text() + anime.alt_names = $(info[7]).text() + anime.url = api.getAnimeURL(PageInfo, url); + anime.synopsis = $('section.d-sm-none div.mt-3 p').text() + anime.image = new types.Image($('div img.bg-secondary').attr('data-src')) + anime.status = $($('div.tab-content div.col-12.col-md-9 div.ms-2').children()[1]).text(); + anime.genres = getGenres($); + anime.date = new types.Calendar(calendar.year); + anime.station = calendar.station; + anime.episodes = await getAnimeEpisodes($, anime.name, pageData, url.split('/').pop()); return anime; } /** + * If url is null then the function will return a list of the last anime + * that were published, otherwise it refers to a url that contains a + * search filtering that among them can be search by name or search by + * category, genre and date * - * @throws {Error} - * @param url - * @returns + * @param url web address with results filtering + * @returns anime list */ -async function getLastAnimes(url?: string): Promise { - let animes: types.Anime[] = []; - const $ = cheerio.load((await axios.get(url ?? `${PageInfo.url}/emision`)).data); - const elements = $('div.heroarea div.heromain div.row').children(); - for (let i = 0; i < elements.length; i++) { - const href = $(elements[i]).find('a').attr('href'); - if (utils.isUsableValue(href) && href !== 'https://monoschinos2.com/emision?p=2') { - animes.push(await getAnime(href)); - } +async function getLastAnimes(url?: string): Promise { + let animes: types.AnimeMedia[] = []; + const $ = cheerio.load((await axios.get(url ?? PageInfo.url)).data); + + const addElement = (element: cheerio.Element) => { + let anime = new types.AnimeMedia(); + anime.url = api.getAnimeURL(PageInfo, $(element).find('a').attr('href')) + anime.image = new types.Image($(element).find('img').attr('data-src')); + anime.name = $(element).find('h3').text().trim(); + animes.push(anime); + } + + if (url === null) { + $('ul.row.row-cols-2.row-cols-sm-3').find('li') + .each((_i, element) => addElement(element)); + } else { + $('ul.row').find('li') + .each((_i, element) => addElement(element)); } return animes; } -//console.log(await getLastAnimes('https://monoschinos2.com/animes?categoria=anime&genero=accion&fecha=2023&letra=A')); - -//console.log(await getLastAnimes()) - /** * * @@ -177,15 +209,15 @@ async function getLastAnimes(url?: string): Promise { */ export class Monoschinos { - getLastEpisodes = getLastEpisodes; - getLastAnimes = getLastEpisodes; + getLastEpisodes = getLastEpisodes; + getLastAnimes = (() => getLastAnimes(null)); getEpisodeServers = getEpisodeServers; - getAnime = getAnime; + getAnime = getAnime; - async filter(name: (string | null), category?: string, genre?: string, year?: string, letter?: string): Promise> { - const animes = new ResultSearch(); + async filter(name: (string | null), category?: string, genre?: string, year?: string): Promise> { + const animes = new ResultSearch(); const link = utils.isUsableValue(name) ? `${PageInfo.url}/buscar?q=${name}` : - `${PageInfo.url}/animes?categoria=${category ?? false}&genero=${genre ?? false}&fecha=${year ?? false}&letra=${letter ?? false}`; + `${PageInfo.url}/animes?categoria=${category ?? false}&genero=${genre ?? false}&fecha=${year ?? false}`; (await getLastAnimes(link)) .forEach(element => { if (utils.isUsableValue(element)) { @@ -198,5 +230,10 @@ export class Monoschinos } }; - -//console.log(await getAnime("https://monoschinos2.com/anime/world-dai-star-sub-espanol")); \ No newline at end of file +/****************************** Test API ******************************/ +/*new Monoschinos().filter(null, null, null, '2022').then(data => { + console.log(data) +}).catch(error => console.log(error))*/ +/*getAnime("https://monoschinos2.com/anime/one-room-hiatari-futsuu-tenshi-tsuki-sub-espanol").then(data => { + console.log(data) +}).catch(error => console.log(error))*/ \ No newline at end of file diff --git a/src/scraper/sites/anime/otakuTV/getAnime.js b/src/scraper/sites/anime/otakuTV/getAnime.js index f78ebc95..5086ee54 100644 --- a/src/scraper/sites/anime/otakuTV/getAnime.js +++ b/src/scraper/sites/anime/otakuTV/getAnime.js @@ -1,6 +1,6 @@ import axios from "axios"; import * as ch from "cheerio"; -import { Anime } from '../../../../utils/schemaProviders.js'; +import { Anime } from "../../../../utils/schemaProviders.js"; async function getAnime(anime) { //aplica minuscula y reemplaza espacios con - @@ -8,7 +8,7 @@ async function getAnime(anime) { try { const { data } = await axios.get( - `https://www1.otakustv.com/anime/${animename}` + `https://www1.otakustv.com/anime/${animename}`, ); console.log(data); @@ -17,20 +17,16 @@ async function getAnime(anime) { const anime = new Anime(); - anime.name = $("div.inn-text h1.text-white").text(); - if ($("span.btn-anime-info").text().trim() == 'Finalizado') { + if ($("span.btn-anime-info").text().trim() == "Finalizado") { anime.active = false; } else { anime.active = true; } - anime.synopsis = $("div.modal-body").first().text().trim(); - anime.year = $("span.date") - .text() - .replace(" Estreno: ", "Se estreno: "); + anime.year = $("span.date").text().replace(" Estreno: ", "Se estreno: "); // anime.rate = $("div.none-otakus-a span.ml-1").text().replace("-", " -"); //Aqui literalmente tuve que usar un each no mas para sacar una cosa, @@ -41,13 +37,11 @@ async function getAnime(anime) { }); const getEpisodes = $( - "div.tabs div.tab-content div.tab-pane div.pl-lg-4 div.container-fluid div.row div.col-6 " + "div.tabs div.tab-content div.tab-pane div.pl-lg-4 div.container-fluid div.row div.col-6 ", ).each((i, j) => { anime.episodes.push({ title: $(j).find("p").find("span").html(), - url: $(j) - .find("a") - .attr("href") + url: $(j).find("a").attr("href"), }); }); @@ -59,6 +53,6 @@ async function getAnime(anime) { } } -getAnime('bocchi the rock'); +getAnime("bocchi the rock"); export default { getAnime }; diff --git a/src/scraper/sites/anime/otakuTV/getAnimeComingSoon.js b/src/scraper/sites/anime/otakuTV/getAnimeComingSoon.js index 1574aac4..29c54342 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimeComingSoon.js +++ b/src/scraper/sites/anime/otakuTV/getAnimeComingSoon.js @@ -9,7 +9,7 @@ export async function getComingSoon() { const animes = []; const getAnimes = $( - "div.pronto div.base-carusel div.carusel_pronto div.item " + "div.pronto div.base-carusel div.carusel_pronto div.item ", ).each((i, element) => { animes.push({ name: $(element).find("h2").text().trim(), diff --git a/src/scraper/sites/anime/otakuTV/getAnimeInfo.js b/src/scraper/sites/anime/otakuTV/getAnimeInfo.js index a5a89193..cdd0ecf5 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimeInfo.js +++ b/src/scraper/sites/anime/otakuTV/getAnimeInfo.js @@ -1,6 +1,6 @@ import axios from "axios"; import * as ch from "cheerio"; -import { Anime } from '../../../../utils/schemaProviders.js'; +import { Anime } from "../../../../utils/schemaProviders.js"; async function getAnime(anime) { //Transform to minus and change spaces to - @@ -8,7 +8,7 @@ async function getAnime(anime) { try { const { data } = await axios.get( - `https://www1.otakustv.com/anime/${animename}` + `https://www1.otakustv.com/anime/${animename}`, ); const $ = ch.load(data); @@ -19,7 +19,7 @@ async function getAnime(anime) { anime.name = $("div.inn-text h1.text-white").text(); //Test its state - if ($("span.btn-anime-info").text().trim() == 'Finalizado') { + if ($("span.btn-anime-info").text().trim() == "Finalizado") { anime.active = false; } else { anime.active = true; @@ -29,10 +29,7 @@ async function getAnime(anime) { anime.synopsis = $("div.modal-body").first().text().trim(); //gets year - anime.year = $("span.date") - .text() - .replace(" Estreno: ", "Se estreno: "); - + anime.year = $("span.date").text().replace(" Estreno: ", "Se estreno: "); //omits first thing and return its image const stuff = $("div.img-in img ").each((i, j) => { @@ -41,22 +38,18 @@ async function getAnime(anime) { //pushing episodes on its array const getEpisodes = $( - "div.tabs div.tab-content div.tab-pane div.pl-lg-4 div.container-fluid div.row div.col-6 " + "div.tabs div.tab-content div.tab-pane div.pl-lg-4 div.container-fluid div.row div.col-6 ", ).each((i, j) => { anime.episodes.push({ title: $(j).find("p").find("span").html(), - url: $(j) - .find("a") - .attr("href") + url: $(j).find("a").attr("href"), }); }); - return anime; } catch (error) { return error; } } - export default { getAnime }; diff --git a/src/scraper/sites/anime/otakuTV/getAnimeNew.js b/src/scraper/sites/anime/otakuTV/getAnimeNew.js index 10cfe247..efaea6ee 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimeNew.js +++ b/src/scraper/sites/anime/otakuTV/getAnimeNew.js @@ -23,7 +23,7 @@ async function getAnimeNew() { .replace("video(s)", "") .trim(), }); - } + }, ); return animes; diff --git a/src/scraper/sites/anime/otakuTV/getAnimePremiere.js b/src/scraper/sites/anime/otakuTV/getAnimePremiere.js index 70927f9c..9f1b8bbc 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimePremiere.js +++ b/src/scraper/sites/anime/otakuTV/getAnimePremiere.js @@ -1,2 +1,2 @@ import axios from "axios"; -import * as ch from 'cheerio' +import * as ch from "cheerio"; diff --git a/src/scraper/sites/anime/otakuTV/getAnimeRanking.js b/src/scraper/sites/anime/otakuTV/getAnimeRanking.js index 7fa758c6..5df72dce 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimeRanking.js +++ b/src/scraper/sites/anime/otakuTV/getAnimeRanking.js @@ -9,12 +9,15 @@ async function getAnimeRanking() { const animeRanking = []; const title = $( - "div.ranking div.base-carusel div.carusel_ranking div.item " + "div.ranking div.base-carusel div.carusel_ranking div.item ", ).each((i, j) => { animeRanking.push({ title: $(j).find("a").find("h2").text(), coverImg: $(j).find("a").find("img").attr("src"), - linkTo: $(j).find("a").attr("href").replace("https://www1.otakustv.com/anime/", "/anime/otakuTV/"), + linkTo: $(j) + .find("a") + .attr("href") + .replace("https://www1.otakustv.com/anime/", "/anime/otakuTV/"), }); }); diff --git a/src/scraper/sites/anime/otakuTV/getAnimeServer.js b/src/scraper/sites/anime/otakuTV/getAnimeServer.js index 6d1167b7..74ef39cb 100644 --- a/src/scraper/sites/anime/otakuTV/getAnimeServer.js +++ b/src/scraper/sites/anime/otakuTV/getAnimeServer.js @@ -1,41 +1,26 @@ import { load } from "cheerio"; import puppeteer from "puppeteer"; - async function getAnimeServer(name) { - - const animeName = name?.toLowerCase().replace(/\s/g, "-"); try { - - - const browser = await puppeteer.launch(); const page = await browser.newPage(); - await page.goto("https://www1.otakustv.com/anime/bocchi-the-rock/episodio-1"); + await page.goto( + "https://www1.otakustv.com/anime/bocchi-the-rock/episodio-1", + ); const html = await page.content(); - const $ = load(html); console.log($.html()); - - - - - - - - - } catch (error) { - return error + return error; } } -getAnimeServer() - +getAnimeServer(); -export default { getAnimeServer } +export default { getAnimeServer }; diff --git a/src/scraper/sites/anime/otakuTV/getUsersActive.js b/src/scraper/sites/anime/otakuTV/getUsersActive.js index 87d139fa..c28307f7 100644 --- a/src/scraper/sites/anime/otakuTV/getUsersActive.js +++ b/src/scraper/sites/anime/otakuTV/getUsersActive.js @@ -18,7 +18,7 @@ async function getUsersActive() { .attr("href") .replace( "https://www1.otakustv.com/perfil/", - "/anime/otakuTV/profile/" + "/anime/otakuTV/profile/", ), name: $(j).find("h2").text(), ranking: $(j).find("p").text(), diff --git a/src/scraper/sites/anime/otakuTV/search.js b/src/scraper/sites/anime/otakuTV/search.js index 1bb40ec8..bbf3f2c6 100644 --- a/src/scraper/sites/anime/otakuTV/search.js +++ b/src/scraper/sites/anime/otakuTV/search.js @@ -1,14 +1,15 @@ -import { load } from 'cheerio'; -import axios from 'axios'; -import { AnimeSearch, Image, SearchArray } from '../../../../utils/schemaProviders.js'; - +import { load } from "cheerio"; +import axios from "axios"; +import { + AnimeSearch, + Image, + SearchArray, +} from "../../../../utils/schemaProviders.js"; async function Search(name) { - let url = `https://www1.otakustv.com/buscador?q=${name}`; try { - const { data } = await axios.get(`${url}`); const $ = load(data); @@ -16,21 +17,20 @@ async function Search(name) { const animes = new SearchArray(1); //Get all animes - $('.animes_lista .row .col-6').each((i, element) => { - animes.data.push(new AnimeSearch( - $(element).find('p.font-GDSherpa-Bold').text(), - new Image($(element).find('img').attr('src')), - $(element).find('a').attr('href') - )) - }) - - return animes - + $(".animes_lista .row .col-6").each((i, element) => { + animes.data.push( + new AnimeSearch( + $(element).find("p.font-GDSherpa-Bold").text(), + new Image($(element).find("img").attr("src")), + $(element).find("a").attr("href"), + ), + ); + }); + + return animes; } catch (error) { - return error + return error; } - } -export default { Search } - +export default { Search }; diff --git a/src/scraper/sites/anime/tioanime/TioAnime.ts b/src/scraper/sites/anime/tioanime/TioAnime.ts index d9392b89..7961b980 100644 --- a/src/scraper/sites/anime/tioanime/TioAnime.ts +++ b/src/scraper/sites/anime/tioanime/TioAnime.ts @@ -2,268 +2,318 @@ import axios from "axios"; import * as cheerio from "cheerio"; import { utils } from "../../../../types/utils"; import * as types from "../../../../types/."; -import { ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; +import { + ResultSearch, + type IResultSearch, + type AnimeResult, +} from "../../../../types/search"; const PageInfo = { - url: 'https://tioanime.com' // url page -} - + url: "https://tioanime.com", // url page +}; function getAnimeChronology($) { - let chrono_list: types.IChronology[] = []; - $('section.w-history ul.list-unstyled li').each((_i, element) => { - // The chronological anime has to access its year and type as extra - // information that is not included in the Chronology class - chrono_list.push(new types.Chronology( - $(element).find('h3.title').text(), - PageInfo.url + $(element).find('div.media-body a').attr('href'), - PageInfo.url + $(element).find('figure.fa-play-circle img').attr('src') - )); - }); - return chrono_list; + let chrono_list: types.IChronology[] = []; + $("section.w-history ul.list-unstyled li").each((_i, element) => { + // The chronological anime has to access its year and type as extra + // information that is not included in the Chronology class + chrono_list.push( + new types.Chronology( + $(element).find("h3.title").text(), + PageInfo.url + $(element).find("div.media-body a").attr("href"), + PageInfo.url + $(element).find("figure.fa-play-circle img").attr("src") + ) + ); + }); + return chrono_list; } async function getEpisodeServers(url) { - 'use strict' - let servers: types.IEpisodeServer[] = []; - const $ = cheerio.load((await axios.get(url)).data); - const script = $($('script').get().pop()).text().trim(); - try { - const videos = new Function(script.substring(0, script.indexOf('$(document)')) - .replace("var videos =", "return"))(); - for (let i = 0; i < videos.length; i++) { - servers.push(new types.EpisodeServer(videos[i][0], - videos[i][1].replace('\\', '') - )); - } + "use strict"; + let servers: types.IEpisodeServer[] = []; + const $ = cheerio.load((await axios.get(url)).data); + const script = $($("script").get().pop()).text().trim(); + try { + const videos = new Function( + script + .substring(0, script.indexOf("$(document)")) + .replace("var videos =", "return") + )(); + for (let i = 0; i < videos.length; i++) { + servers.push( + new types.EpisodeServer(videos[i][0], videos[i][1].replace("\\", "")) + ); + } - const table_downloads = $($('table.table-downloads tbody')).children(); - for (let i = 0; i < table_downloads.length; i++) { - const server = $($(table_downloads[i]).find('td')[0]).text().trim(); - const episode_server = servers.find((episode) => { - return episode.name.toLowerCase() === server.toLocaleLowerCase(); - }); - if (!(episode_server == undefined || episode_server == null)) { - episode_server.file_url = $(table_downloads[i]).find("a").attr('href'); - } else { - servers.push({ - name: server, url: null, file_url: $(table_downloads[i]).find("a").attr('href') - }); - } - } - } catch (error) { - console.log(error) + const table_downloads = $($("table.table-downloads tbody")).children(); + for (let i = 0; i < table_downloads.length; i++) { + const server = $($(table_downloads[i]).find("td")[0]).text().trim(); + const episode_server = servers.find((episode) => { + return episode.name.toLowerCase() === server.toLocaleLowerCase(); + }); + if (!(episode_server == undefined || episode_server == null)) { + episode_server.file_url = $(table_downloads[i]).find("a").attr("href"); + } else { + servers.push({ + name: server, + url: null, + file_url: $(table_downloads[i]).find("a").attr("href"), + }); + } } - return servers; + } catch (error) { + console.log(error); + } + return servers; } async function getAnimeEpisodes(data) { - let __episodes: types.IEpisode[] = []; - data.episodes.forEach(episode_number => { - let episode = new types.Episode(); - episode.name = `${data.info[2]} Capitulo ${episode_number}`; - episode.image = PageInfo.url +`/uploads/thumbs/${data.info[0]}.jpg`; - episode.url = `/anime/tioanime/episode/${data.info[1]}-${episode_number}`; - episode.number = episode_number; - __episodes.push(episode); - }); - return __episodes; + let __episodes: types.IEpisode[] = []; + data.episodes.forEach((episode_number) => { + let episode = new types.Episode(); + episode.name = `${data.info[2]} Capitulo ${episode_number}`; + episode.thumbnail = new types.Image(PageInfo.url + `/uploads/thumbs/${data.info[0]}.jpg`); + episode.url = `/anime/tioanime/episode/${data.info[1]}-${episode_number}`; + episode.num = episode_number; + __episodes.push(episode); + }); + return __episodes; } function getEpisode($, element) { - const title = $(element).find('h3.title').text().trim(); - const episode = new types.Episode(); - episode.image = PageInfo.url + $(element).find('figure.fa-play-circle img').attr('src'); - episode.url = $(element).find('article.episode a').attr('href').replace('/ver/', '/anime/tioanime/servers/') - - for (let i = title.length - 1; i >= 0; i--) { - if (title[i] == ' ') { - episode.name = title.substring(0, i).trim(); - episode.number = parseInt(title.substring(i + 1, title.length)); - break; - } + const title = $(element).find("h3.title").text().trim(); + const episode = new types.Episode(); + episode.thumbnail = new types.Image(PageInfo.url + $(element).find("figure.fa-play-circle img").attr("src")) + episode.url = $(element) + .find("article.episode a") + .attr("href") + .replace("/ver/", "/anime/tioanime/servers/"); + + for (let i = title.length - 1; i >= 0; i--) { + if (title[i] == " ") { + episode.name = title.substring(0, i).trim(); + episode.num = parseInt(title.substring(i + 1, title.length)); + break; } - return episode; + } + return episode; } async function getLastEpisodes() { - let episodes: types.IEpisode[] = []; - try { - const $ = cheerio.load((await axios.get(PageInfo.url)).data); - const elements = $('div.container section ul.episodes li').children(); - for (let i = 0; i < elements.length; i++) { - episodes.push(getEpisode($, elements[i])); - } - } catch (error) { - console.log(error); + let episodes: types.IEpisode[] = []; + try { + const $ = cheerio.load((await axios.get(PageInfo.url)).data); + const elements = $("div.container section ul.episodes li").children(); + for (let i = 0; i < elements.length; i++) { + episodes.push(getEpisode($, elements[i])); } - return episodes; + } catch (error) { + console.log(error); + } + return episodes; } function getGenres($, elements) { - let genres: string[] = []; - elements.each((_i, element) => { - genres.push($(element).find('a').text().trim()); - }); - return genres; + let genres: string[] = []; + elements.each((_i, element) => { + genres.push($(element).find("a").text().trim()); + }); + return genres; } function getScriptAnimeInfo($) { - let script = $($('script').get().pop()).text().trim(); - try { - script = script.substring(0, script.indexOf('$(document)')); - script = script.substring(0, script.indexOf("var episodes_details")) - + "return { info: anime_info, episodes: episodes };"; - const variables = new Function(script)() - return { info: variables.info, episodes: variables.episodes }; - } catch (error) { - console.log(error + "\n Script code: " + script); - } - return null; + let script = $($("script").get().pop()).text().trim(); + try { + script = script.substring(0, script.indexOf("$(document)")); + script = + script.substring(0, script.indexOf("var episodes_details")) + + "return { info: anime_info, episodes: episodes };"; + const variables = new Function(script)(); + return { info: variables.info, episodes: variables.episodes }; + } catch (error) { + console.log(error + "\n Script code: " + script); + } + return null; } async function getAnime(url) { - // ignore property alt_name - const $ = cheerio.load((await axios.get(url)).data); - const data = getScriptAnimeInfo($); - // It is possible that the object returned by the getScriptAnimeInfo function is null. - if (data == null) - throw new Error('The getScriptAnimeInfo() function returns a null value.'); - const anime = new types.Anime(); - anime.name = $('div.container h1.title').text(); - //anime.url = url; - anime.url = url.replace('https://tioanime.com/anime/', '/anime/tioanime/name/'); - //anime.type = $('div.meta span.anime-type-peli').text(); - anime.type = (() => { - switch ($('div.meta span.anime-type-peli').text().toLowerCase()) { - case "anime": - return "Anime"; - case "movie": - return "Movie"; - case "one": - return "ONA"; - case "ona": - return "OVA"; - } - return "Null"; - })(); - - //anime.year = parseInt($('div.meta span.year').text().trim()); - anime.date = new types.DatePeriod(new types.Calendar(data.info.length < 4 ? - parseInt($('div.meta span.year').text().trim().substring(0, 4)) : - new Date(data.info[3]).getFullYear()) - ); - anime.synopsis = $('p.sinopsis').text().trim(); - anime.genres = getGenres($, $('div.container p.genres span')); - anime.image = new types.Image(PageInfo.url + $('div.container div.thumb figure img').attr('src'), - $('figure.backdrop img').attr('src') == undefined ? "" : - PageInfo.url + $('figure.backdrop img').attr('src') - ); - anime.status = $('div.thumb a.status').text().trim() === 'En emision'; - anime.station = $('div.meta span.fa-snowflake').text().trim().split('\n')[0]; - anime.episodes = await getAnimeEpisodes(data); - anime.chronology = getAnimeChronology($); - return anime; + // ignore property alt_name + const $ = cheerio.load((await axios.get(url)).data); + const data = getScriptAnimeInfo($); + // It is possible that the object returned by the getScriptAnimeInfo function is null. + if (data == null) + throw new Error("The getScriptAnimeInfo() function returns a null value."); + const anime = new types.AnimeMedia(); + anime.name = $("div.container h1.title").text(); + //anime.url = url; + anime.url = url.replace( + "https://tioanime.com/anime/", + "/anime/tioanime/name/" + ); + //anime.type = $('div.meta span.anime-type-peli').text(); + anime.type = (() => { + switch ($("div.meta span.anime-type-peli").text().toLowerCase()) { + case "anime": + return "Anime"; + case "movie": + return "Movie"; + case "one": + return "ONA"; + case "ona": + return "OVA"; + } + return "Null"; + })(); + + //anime.year = parseInt($('div.meta span.year').text().trim()); + anime.date = new types.DatePeriod( + new types.Calendar( + data.info.length < 4 + ? parseInt($("div.meta span.year").text().trim().substring(0, 4)) + : new Date(data.info[3]).getFullYear() + ) + ); + anime.synopsis = $("p.sinopsis").text().trim(); + anime.genres = getGenres($, $("div.container p.genres span")); + anime.image = new types.Image( + PageInfo.url + $("div.container div.thumb figure img").attr("src"), + $("figure.backdrop img").attr("src") == undefined + ? "" + : PageInfo.url + $("figure.backdrop img").attr("src") + ); + anime.status = $("div.thumb a.status").text().trim() === "En emision"; + anime.station = $("div.meta span.fa-snowflake").text().trim().split("\n")[0]; + anime.episodes = await getAnimeEpisodes(data); + anime.chronology = getAnimeChronology($); + return anime; } -async function getLastAnimes(url: string) { - console.log(url) - try { - let animes: types.IAnime[] = []; - const $ = cheerio.load((await axios.get(url ?? PageInfo.url)).data); - const elements = $(utils.isUsableValue(url) ? 'ul.animes' : 'div.container section ul.list-unstyled.row li').children(); - for (let i = 0; i < elements.length; i++) { - const anime_url = $(elements[i]).find('article.anime a').attr('href'); - if (utils.isUsableValue(anime_url)) { - animes.push(await getAnime(PageInfo.url + anime_url)); - } - } - return animes; - } catch (error) { - console.log(error); +async function getLastAnimes(url: string | null) { + try { + let animes: types.AnimeMedia[] = []; + const $ = cheerio.load((await axios.get(url ?? PageInfo.url)).data); + const elements = $( + utils.isUsableValue(url) + ? "ul.animes" + : "div.container section ul.list-unstyled.row li" + ).children(); + for (let i = 0; i < elements.length; i++) { + const anime_url = $(elements[i]).find("article.anime a").attr("href"); + if (utils.isUsableValue(anime_url)) { + animes.push(await getAnime(PageInfo.url + anime_url)); + } } - return []; + return animes; + } catch (error) { + console.log(error); + } + return []; } async function getSectionContents(section: number) { - let animes: types.IAnime[] = []; - try { - const $ = cheerio.load((await axios.get(`${PageInfo.url}/directorio?type%5B%5D=${section}`)).data); - const elements = $(`ul.animes`).children(); - for (let i = 0; i < elements.length; i++) { - animes.push(await getAnime(PageInfo.url + $(elements[i]) - .find('article.anime a').attr('href'))); - } - } catch (error) { - console.log(error); + let animes: types.AnimeMedia[] = []; + try { + const $ = cheerio.load( + (await axios.get(`${PageInfo.url}/directorio?type%5B%5D=${section}`)).data + ); + const elements = $(`ul.animes`).children(); + for (let i = 0; i < elements.length; i++) { + animes.push( + await getAnime( + PageInfo.url + $(elements[i]).find("article.anime a").attr("href") + ) + ); } - return animes; + } catch (error) { + console.log(error); + } + return animes; } async function getLastMovies() { - return await getSectionContents(1); + return await getSectionContents(1); } async function getLastOvas() { - return await getSectionContents(2); + return await getSectionContents(2); } async function getLastOnas() { - return await getSectionContents(3); + return await getSectionContents(3); } - - export interface IYearRange { - begin: number; - end: number; + begin: number; + end: number; } -export class TioAnime -{ - getLastEpisodes = getLastEpisodes; - getLastAnimes = getLastAnimes; - getLastMovies = getLastMovies; - getLastOvas = getLastOvas; - getLastOnas = getLastOnas; - getEpisodeServers = getEpisodeServers; - getAnime = getAnime; +export class TioAnime { + getLastEpisodes = getLastEpisodes; + getLastAnimes = getLastAnimes; + getLastMovies = getLastMovies; + getLastOvas = getLastOvas; + getLastOnas = getLastOnas; + getEpisodeServers = getEpisodeServers; + getAnime = getAnime; - private arrayToURLParams(param: string, array: string[]): string { - let elements = ''; - if (utils.isUsableValue(array)) { - for (let i = 0; i < array.length; i++) { - elements += `${param}%5B%5D=${array[i]}`; - if (i + 1 < array.length) { - elements += '&'; - } - } - } - return elements.length !== 0 ? elements + '&' : ''; - } + private arrayToURLParams(param: string, array: string[]): string { + let elements = ""; + if (utils.isUsableValue(array)) { + for (let i = 0; i < array.length; i++) { + elements += `${param}%5B%5D=${array[i]}`; + if (i + 1 < array.length) { + elements += "&"; + } + } + } + return elements.length !== 0 ? elements + "&" : ""; + } - // 0: Anime (TV), 1 Movie, 2: OVA, 3: ONA - // all genres - // year_range { begin, end } - // 2: Finalizado, 1: En emision, 3: Proximamente - // recent, -recent - - async filter(name: (string | null), types?: string[], genres?: string[], year_range?: IYearRange, status?: number, sort?: string): - Promise> { - const animes = new ResultSearch(); - let usable; - if (!(usable = utils.isUsableValue(name) && name.trim().length != 0)) - year_range ?? (year_range = { begin: 1950, end: new Date().getFullYear() }); - (await getLastAnimes(`${PageInfo.url}/directorio?${(usable ? `q=${name}` : `${this.arrayToURLParams('type', types)}${this.arrayToURLParams('genero', genres)}year=${year_range.begin}%2C${year_range.end}&status=${status ?? 2}&sort=${sort ?? 'recent'}`)}`)) - .forEach(element => { - if (utils.isUsableValue(element)) { - animes.results.push({ name: element.name, image: element.image.url, - url: element.url, type: element.type }) - } - }); - return animes; - } -}; + // 0: Anime (TV), 1 Movie, 2: OVA, 3: ONA + // all genres + // year_range { begin, end } + // 2: Finalizado, 1: En emision, 3: Proximamente + // recent, -recent + + async filter( + name: string | null, + types?: string[], + genres?: string[], + year_range?: IYearRange, + status?: number, + sort?: string + ): Promise> { + const animes = new ResultSearch(); + let usable; + if (!(usable = utils.isUsableValue(name) && name.trim().length != 0)) + year_range ?? + (year_range = { begin: 1950, end: new Date().getFullYear() }); + ( + await getLastAnimes( + `${PageInfo.url}/directorio?${ + usable + ? `q=${name}` + : `${this.arrayToURLParams("type", types)}${this.arrayToURLParams( + "genero", + genres + )}year=${year_range.begin}%2C${year_range.end}&status=${ + status ?? 2 + }&sort=${sort ?? "recent"}` + }` + ) + ).forEach((element) => { + if (utils.isUsableValue(element)) { + animes.results.push({ + name: element.name, + image: element.image.url, + url: element.url, + type: element.type, + }); + } + }); + return animes; + } +} /*getEpisodeServers('https://tioanime.com/ver/oniichan-wa-oshimai-3').then(result => { console.log(result) @@ -271,4 +321,3 @@ export class TioAnime //new TioAnime().filter(null, null, ["demencia"], null, null, null).then(result => { console.log(result) } ) /* */ - diff --git a/src/scraper/sites/anime/wcostream/WcoStream.ts b/src/scraper/sites/anime/wcostream/WcoStream.ts index babe58c7..9f2056b0 100644 --- a/src/scraper/sites/anime/wcostream/WcoStream.ts +++ b/src/scraper/sites/anime/wcostream/WcoStream.ts @@ -1,184 +1,284 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Anime } from "../../../../types/anime"; +import { AnimeMedia } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { IResultSearch, IAnimeSearch, ResultSearch, AnimeSearch } from "../../../../types/search"; -import { UnPacked } from"../../../../types/utils"; +import { + ResultSearch, + AnimeResult +} from "../../../../types/search"; +import { UnPacked } from "../../../../types/utils"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; /** List of Domains * https://wcostream.tv - * + * * https://m.wcostream.org (phone) - * + * * https://wcopanel.cizgifilmlerizle.com * https://neptun.cizgifilmlerizle.com - * + * * https://ndisk[>1].cizgifilmlerizle.com * https://neptun[>1].cizgifilmlerizle.com - * + * * https://cdn.animationexplore.com * https://animationexplore.com - * + * * https://watchanimesub.net * https://lb.watchanimesub.net - * + * * https://www.wcopremium.tv -*/ + */ //Default Set Axios Cookie -axios.defaults.withCredentials = true -axios.defaults.headers.common["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"; - -export class WcoStream { - readonly url = "https://www.wcostream.tv"; - - async GetAnimeInfo(anime: string): Promise { - try { - const { data } = await axios.get(`${this.url}/anime/${anime}`, { headers: { "User-Agent": "Mozilla/5.0 (Linux; Android 10; LM-K920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36" } }); - const $ = cheerio.load(data); - - const image = $("#category_description .ui-grid-solo .ui-block-a img").attr("src") - const name = $(".main .ui-grid-solo.center .ui-block-a > .ui-bar.ui-bar-x").text().replace("Share On", "") - const genre = $(".ui-grid-solo.left .ui-block-a").text().replace("Genre;", "").replace("Language; ", "") - - const AnimeInfo: Anime = { - name: name, - url: `/anime/wcostream/name/${anime}`, - synopsis: $("#category_description .ui-grid-solo .ui-block-a div p").text().replace("Watch ", "").replace(/\n/g, ""), - image: { - url: !image.includes("https://") ? image.replace("//", "https://") : image - }, - genres: [...genre.replace(genre.includes("Dubbed") ? "Dubbed" : "Subbed", "").trim().replace(/\n/g, "").replace(/\s+/g, "").replace("-", "").split(",").map(v => v.trim())], - episodes: [] - } - - $("ul.ui-listview-z li").map((_i, e) => { - const data = $(e).find("a").text() - const episode = data.slice(data.search(" Episode ")).replace(data.includes("English Dubbed") ? "English Dubbed" : "English Subbed", "").replace("Episode", "").trim().replace(/[^0-9-.]/g, "") - const season = data.includes("Season") ? data.slice(data.search(" Season "), data.search(" Episode ")).replace("Season", "").trim() : "" - - if (data && !data.includes("Movie") && !data.includes("OVA")) { - const AnimeEpisode: Episode = { - name: data, - number: episode, - image: `https://cdn.animationexplore.com/thumbs/${$(e).find("a").attr("href").replace("https://www.wcostream.tv/", "").replace("/", "").replace(/[^a-zA-Z0-9 ]/g, " ").replace(/\s+/g, "-")}.jpg`, - url: `/anime/wcostream/episode/${anime.replace(/[^a-zA-Z0-9 ]/g, ' ').replace(/\s+/g, "-") + "-" + episode}${season ? "?season=" + season : ""}` - } - - AnimeInfo.episodes.push(AnimeEpisode); - } - }) - - return AnimeInfo; - - } catch (error) { - console.log(error) - } - } +axios.defaults.withCredentials = true; +axios.defaults.headers.common["User-Agent"] = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"; + +export class WcoStream extends AnimeScraperModel { + readonly url = "https://www.wcostream.tv"; + + async GetItemInfo(anime: string): Promise { + try { + const { data } = await axios.get(`${this.url}/anime/${anime}`, { + headers: { + "User-Agent": + "Mozilla/5.0 (Linux; Android 10; LM-K920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36", + }, + }); + const $ = cheerio.load(data); - // Global Apis https://www.wcostream.org/wp-json - // https://www.wcostream.org/wp-json/wp/v2/pages + const image = $( + "#category_description .ui-grid-solo .ui-block-a img" + ).attr("src"); + const name = $( + ".main .ui-grid-solo.center .ui-block-a > .ui-bar.ui-bar-x" + ) + .text() + .replace("Share On", ""); + const genre = $(".ui-grid-solo.left .ui-block-a") + .text() + .replace("Genre;", "") + .replace("Language; ", ""); - async GetEpisodeServers(episode: string, season: number | string) { - try { + const AnimeInfo: AnimeMedia = { + name: name, + url: `/anime/wcostream/name/${anime}`, + synopsis: $("#category_description .ui-grid-solo .ui-block-a div p") + .text() + .replace("Watch ", "") + .replace(/\n/g, ""), + image: { + url: !image.includes("https://") + ? image.replace("//", "https://") + : image, + }, + genres: [ + ...genre + .replace(genre.includes("Dubbed") ? "Dubbed" : "Subbed", "") + .trim() + .replace(/\n/g, "") + .replace(/\s+/g, "") + .replace("-", "") + .split(",") + .map((v) => v.trim()), + ], + episodes: [], + }; - const NumEpisode = episode.substring(episode.lastIndexOf("-") + 1) - const anime = episode.substring(0, episode.lastIndexOf("-")) + $("ul.ui-listview-z li").map((_i, e) => { + const data = $(e).find("a").text(); + const episode = data + .slice(data.search(" Episode ")) + .replace( + data.includes("English Dubbed") + ? "English Dubbed" + : "English Subbed", + "" + ) + .replace("Episode", "") + .trim() + .replace(/[^0-9-.]/g, ""); + const season = data.includes("Season") + ? data + .slice(data.search(" Season "), data.search(" Episode ")) + .replace("Season", "") + .trim() + : ""; - const { data } = await axios.get(`https://www.wcostream.tv/playlist-cat/${anime}`) - const $ = cheerio.load(data); + if (data && !data.includes("Movie") && !data.includes("OVA")) { + const AnimeEpisode: Episode = { + name: data, + num: Number(episode), + thumbnail: {url:`https://cdn.animationexplore.com/thumbs/${$(e) + .find("a") + .attr("href") + .replace("https://www.wcostream.tv/", "") + .replace("/", "") + .replace(/[^a-zA-Z0-9 ]/g, " ") + .replace(/\s+/g, "-")}.jpg`}, + url: `/anime/wcostream/episode/${ + anime.replace(/[^a-zA-Z0-9 ]/g, " ").replace(/\s+/g, "-") + + "-" + + episode + }${season ? "?season=" + season : ""}`, + }; - const mainUrl = $("script").get()[3].children[0].data - const mainOrigin = eval(mainUrl.trim().slice(mainUrl.search("playlist:") + 6, mainUrl.search('image: ') - 4).trim().replace(",", "")) + AnimeInfo.episodes.push(AnimeEpisode); + } + }); - const mainData = await axios.get(this.url + mainOrigin) - const $$ = cheerio.load(mainData.data.replaceAll(":image", " type='image'").replaceAll(":source", " type='video'").trim()) + return AnimeInfo; + } catch (error) { + console.log(error); + } + } - const AnimeEpisodeInfo: Episode = { - name: "", - url: `/anime/wcostream/episode/${episode}${season ? "?season=" + season : ""}`, - number: NumEpisode, - image: "", - servers: [] - } + // Global Apis https://www.wcostream.org/wp-json + // https://www.wcostream.org/wp-json/wp/v2/pages + async GetEpisodeServers(episode: string, season: number | string) { + try { + const NumEpisode = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); - $$("item").each(async (_i, e) => { - const title = $$(e).find("title").text() + const { data } = await axios.get( + `https://www.wcostream.tv/playlist-cat/${anime}` + ); + const $ = cheerio.load(data); - if (title.includes("Episode " + NumEpisode + " ") && !season && !title.includes("Season")) { - AnimeEpisodeInfo.name = title.replace("", "").trim() - AnimeEpisodeInfo.image = $$(e).find("jwplayer[type='image']").text() - const Server: EpisodeServer = { - name: "JWplayer - " + $$(e).find("jwplayer[type='video']").attr("label"), - url: $$(e).find("jwplayer[type='video']").attr("file"), - } - AnimeEpisodeInfo.servers.push(Server); + const mainUrl = $("script").get()[3].children[0].data; + const mainOrigin = eval( + mainUrl + .trim() + .slice(mainUrl.search("playlist:") + 6, mainUrl.search("image: ") - 4) + .trim() + .replace(",", "") + ); - } else if (title.includes("Episode " + NumEpisode + " ") && season && title.includes("Season " + season)) { - AnimeEpisodeInfo.name = title.replace("", "").trim() - AnimeEpisodeInfo.image = $$(e).find("jwplayer[type='image']").text() + const mainData = await axios.get(this.url + mainOrigin); + const $$ = cheerio.load( + mainData.data + .replaceAll(":image", " type='image'") + .replaceAll(":source", " type='video'") + .trim() + ); - const Server: EpisodeServer = { - name: "JWplayer - " + $$(e).find("jwplayer[type='video']").attr("label"), - url: $$(e).find("jwplayer[type='video']").attr("file"), - } + const AnimeEpisodeInfo: Episode = { + name: "", + url: `/anime/wcostream/episode/${episode}${ + season ? "?season=" + season : "" + }`, + num: Number(NumEpisode), + servers: [], + }; - AnimeEpisodeInfo.servers.push(Server); - } - }) - return AnimeEpisodeInfo; - } catch (error) { - console.log(error) - } - } + $$("item").each(async (_i, e) => { + const title = $$(e).find("title").text(); - async GetAnimeByFilter(search?: string, page?: number): Promise> { - try { - const formdata = new FormData(); - formdata.append("catara", search); - formdata.append("konuara", "series"); - - const { data } = await axios.post(`${this.url}/search`, formdata); - - const $ = cheerio.load(data) - const animeSearch: ResultSearch = { - nav: { - count: $("#blog .cerceve").length, - current: Number(page ? page : 1), - next: $("#blog .cerceve").length < 28 ? 0 : page ? Number(page) + 1 : 2, - hasNext: $("#blog .cerceve").length < (28 * page) ? false : true - }, - results: [] - } - - $("#blog .cerceve").each((i, e) => { - if ((animeSearch.nav.current > 1 ? i - 1 : i) >= 28 * (animeSearch.nav.current - 1) && (animeSearch.nav.current > 1 ? i + 1 : i) <= 28 * animeSearch.nav.current) { - const animeSearchData: AnimeSearch = { - name: $(e).find(".iccerceve a").attr("title"), - image: $(e).find(".iccerceve a img").attr("src"), - url: `/anime/wcostream/name/${$(e).find(".iccerceve a").attr("href").replace("/anime/", "")}`, - type: "anime" - } - animeSearch.results.push(animeSearchData) - } - }) - return animeSearch; - } catch (error) { - console.log(error) + if ( + title.includes("Episode " + NumEpisode + " ") && + !season && + !title.includes("Season") + ) { + AnimeEpisodeInfo.name = title + .replace("", "") + .trim(); + AnimeEpisodeInfo.thumbnail.url = $$(e).find("jwplayer[type='image']").text(); + const Server: EpisodeServer = { + name: + "JWplayer - " + + $$(e).find("jwplayer[type='video']").attr("label"), + url: $$(e).find("jwplayer[type='video']").attr("file"), + }; + AnimeEpisodeInfo.servers.push(Server); + } else if ( + title.includes("Episode " + NumEpisode + " ") && + season && + title.includes("Season " + season) + ) { + AnimeEpisodeInfo.name = title + .replace("", "") + .trim(); + AnimeEpisodeInfo.thumbnail.url = $$(e).find("jwplayer[type='image']").text(); + + const Server: EpisodeServer = { + name: + "JWplayer - " + + $$(e).find("jwplayer[type='video']").attr("label"), + url: $$(e).find("jwplayer[type='video']").attr("file"), + }; + + AnimeEpisodeInfo.servers.push(Server); } + }); + return AnimeEpisodeInfo; + } catch (error) { + console.log(error); } + } + + async GetItemByFilter( + search?: string, + page?: number + ): Promise> { + try { + const formdata = new FormData(); + formdata.append("catara", search); + formdata.append("konuara", "series"); + + const { data } = await axios.post(`${this.url}/search`, formdata); - async RuntimeUnpacked(data:string) { - - const $ = cheerio.load(decodeURI(data)) - - const Buffer = btoa($("script").get().at(-1).children[0].data) - const UnBuffer = UnPacked(Buffer) - const RequestBR = await eval(UnBuffer.slice(UnBuffer.indexOf("{sources:[{file:") + "{sources:[{file:".length, UnBuffer.indexOf("}],image:", 1))); + const $ = cheerio.load(data); + const animeSearch: ResultSearch = { + nav: { + count: $("#blog .cerceve").length, + current: Number(page ? page : 1), + next: + $("#blog .cerceve").length < 28 ? 0 : page ? Number(page) + 1 : 2, + hasNext: $("#blog .cerceve").length < 28 * page ? false : true, + }, + results: [], + }; - return RequestBR + $("#blog .cerceve").each((i, e) => { + if ( + (animeSearch.nav.current > 1 ? i - 1 : i) >= + 28 * (animeSearch.nav.current - 1) && + (animeSearch.nav.current > 1 ? i + 1 : i) <= + 28 * animeSearch.nav.current + ) { + const animeSearchData: AnimeResult = { + name: $(e).find(".iccerceve a").attr("title"), + image: $(e).find(".iccerceve a img").attr("src"), + url: `/anime/wcostream/name/${$(e) + .find(".iccerceve a") + .attr("href") + .replace("/anime/", "")}`, + type: "anime", + }; + animeSearch.results.push(animeSearchData); + } + }); + return animeSearch; + } catch (error) { + console.log(error); } -} + } + async RuntimeUnpacked(data: string) { + const $ = cheerio.load(decodeURI(data)); + const Buffer = btoa($("script").get().at(-1).children[0].data); + const UnBuffer = UnPacked(Buffer); + const RequestBR = await eval( + UnBuffer.slice( + UnBuffer.indexOf("{sources:[{file:") + "{sources:[{file:".length, + UnBuffer.indexOf("}],image:", 1) + ) + ); + + return RequestBR; + } +} diff --git a/src/scraper/sites/anime/zoro/Zoro.ts b/src/scraper/sites/anime/zoro/Zoro.ts index fed0d91c..36aa3c8c 100644 --- a/src/scraper/sites/anime/zoro/Zoro.ts +++ b/src/scraper/sites/anime/zoro/Zoro.ts @@ -1,13 +1,18 @@ import axios from "axios"; import { load } from "cheerio"; +import { AnimeScraperModel } from "../../../../models/AnimeScraperModel"; import { Anime, Chronology } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { AnimeSearch, ResultSearch, IAnimeSearch } from "../../../../types/search"; +import { + AnimeSearch, + ResultSearch, + type IAnimeSearch, +} from "../../../../types/search"; -export class Zoro { +export class Zoro extends AnimeScraperModel { readonly url = "https://aniwatch.to"; - async GetAnimeInfo(animeName: string): Promise { + async GetItemInfo(animeName: string): Promise { try { const response = await axios.get(`${this.url}/${animeName}`); const $ = load(response.data); @@ -58,7 +63,7 @@ export class Zoro { } } //filter - async Filter( + async GetItemByFilter( type?: string, rated?: string, score?: string, @@ -66,10 +71,9 @@ export class Zoro { language?: string, sort?: string, genres?: string, - page_anime?: string, + page_anime?: string ) { try { - const { data } = await axios.get(`${this.url}/filter`, { params: { type: type, @@ -82,7 +86,7 @@ export class Zoro { page: page_anime || 1, }, }); - + const $ = load(data); const most_cards = $("div.film_list div.film_list-wrap div.flw-item"); //const page_index = $("div.pre-pagination nav ul li.active"); @@ -110,7 +114,7 @@ export class Zoro { } //episode server - async GetEpisodeServer(episode: string, ep: string) { + async GetEpisodeServers(episode: string, ep: string) { try { const animename = episode.toLowerCase().replace(/\s/g, "-"); const { data } = await axios.get( diff --git a/src/scraper/sites/doramas/dramanice/Dramanice.ts b/src/scraper/sites/doramas/dramanice/Dramanice.ts index 35fef897..a84b5b42 100644 --- a/src/scraper/sites/doramas/dramanice/Dramanice.ts +++ b/src/scraper/sites/doramas/dramanice/Dramanice.ts @@ -2,99 +2,117 @@ import * as cheerio from "cheerio"; import axios from "axios"; import { Anime } from "../../../../types/anime"; import { Episode, EpisodeServer } from "../../../../types/episode"; -import { AnimeSearch, ResultSearch, IResultSearch, IAnimeSearch } from "../../../../types/search"; +import { + AnimeSearch, + ResultSearch, + type IResultSearch, + type IAnimeSearch, +} from "../../../../types/search"; export class Dramanice { - readonly url = "https://www.animelatinohd.com"; - readonly api = "https://api.animelatinohd.com"; - - async GetAnimeInfo(anime: string): Promise { - try { - const { data } = await axios.get(`${this.url}/anime/${anime}`); - const $ = cheerio.load(data); - - const animeInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data - - const AnimeInfo: Anime = { - name: animeInfoParseObj.name, - url: `/anime/animelatinohd/name/${anime}`, - synopsis: animeInfoParseObj.overview, - alt_name: [...animeInfoParseObj.name_alternative.split(",")], - image: { - url: "https://www.themoviedb.org/t/p/original" + animeInfoParseObj.poster + "?&w=53&q=95" - }, - genres: [...animeInfoParseObj.genres.split(",")], - type: animeInfoParseObj.type, - status: animeInfoParseObj.status == 1 ? "En emisión" : "Finalizado", - date: animeInfoParseObj.aired, - episodes: [] - } - - animeInfoParseObj.episodes.map(e => { - const AnimeEpisode: Episode = { - name: animeInfoParseObj.name, - number: e.number + "", - image: "https://www.themoviedb.org/t/p/original" + animeInfoParseObj.banner + "?&w=280&q=95", - url: `/anime/animelatinohd/episode/${animeInfoParseObj.slug + "-" + e.number}` - } - - AnimeInfo.episodes.push(AnimeEpisode); - }) - - return AnimeInfo; - - } catch (error) { - console.log(error) - } + readonly url = "https://www.animelatinohd.com"; + readonly api = "https://api.animelatinohd.com"; + + async GetAnimeInfo(anime: string): Promise { + try { + const { data } = await axios.get(`${this.url}/anime/${anime}`); + const $ = cheerio.load(data); + + const animeInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()).props + .pageProps.data; + + const AnimeInfo: Anime = { + name: animeInfoParseObj.name, + url: `/anime/animelatinohd/name/${anime}`, + synopsis: animeInfoParseObj.overview, + alt_name: [...animeInfoParseObj.name_alternative.split(",")], + image: { + url: + "https://www.themoviedb.org/t/p/original" + + animeInfoParseObj.poster + + "?&w=53&q=95", + }, + genres: [...animeInfoParseObj.genres.split(",")], + type: animeInfoParseObj.type, + status: animeInfoParseObj.status == 1 ? "En emisión" : "Finalizado", + date: animeInfoParseObj.aired, + episodes: [], + }; + + animeInfoParseObj.episodes.map((e) => { + const AnimeEpisode: Episode = { + name: animeInfoParseObj.name, + number: e.number + "", + image: + "https://www.themoviedb.org/t/p/original" + + animeInfoParseObj.banner + + "?&w=280&q=95", + url: `/anime/animelatinohd/episode/${ + animeInfoParseObj.slug + "-" + e.number + }`, + }; + + AnimeInfo.episodes.push(AnimeEpisode); + }); + + return AnimeInfo; + } catch (error) { + console.log(error); } - async GetEpisodeServers(episode: string, lang: string): Promise { - try { - - const number = episode.substring(episode.lastIndexOf("-") + 1) - const anime = episode.substring(0, episode.lastIndexOf("-")) - const langType = [{ lang: "es", type: "Latino" }, { lang: "jp", type: "Subtitulado" }] - - const { data } = await axios.get(`${this.url}/ver/${anime}/${number}`); - const $ = cheerio.load(data); - - const animeEpisodeParseObj = JSON.parse($("#__NEXT_DATA__").html()).props.pageProps.data - - const AnimeEpisodeInfo: Episode = { - name: animeEpisodeParseObj.anime.name, - url: `/anime/animelatinohd/episode/${episode}`, - number: number, - image: "", - servers: [] - } - - const sel_lang = langType.filter((e) => e.lang == lang) - let f_index = 0 - - if (sel_lang.length) { - $("#languaje option").each((_i, e) => { - if ($(e).text() == sel_lang[0].type) { - f_index = Number($(e).val()) - } - }) - } else { - $("#languaje option").each((_i, e) => { - f_index = Number($(e).val()) - }) - } - - await Promise.all(animeEpisodeParseObj.players[f_index].map(async (e: { server: { title: string; }; id: string; }) => { - //let min = await axios.get("https://api.animelatinohd.com/stream/" + e.id, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62", "Referer": "https://www.animelatinohd.com/" } }) - // let dat = cheerio.load(min.data) - - const Server: EpisodeServer = { - name: e.server.title, - url: "", - } - Server.url = "https://api.animelatinohd.com/stream/" + e.id - Server.name = e.server.title - - //state 1 - /*if (e.server.title == "Beta") { + } + async GetEpisodeServers(episode: string, lang: string): Promise { + try { + const number = episode.substring(episode.lastIndexOf("-") + 1); + const anime = episode.substring(0, episode.lastIndexOf("-")); + const langType = [ + { lang: "es", type: "Latino" }, + { lang: "jp", type: "Subtitulado" }, + ]; + + const { data } = await axios.get(`${this.url}/ver/${anime}/${number}`); + const $ = cheerio.load(data); + + const animeEpisodeParseObj = JSON.parse($("#__NEXT_DATA__").html()).props + .pageProps.data; + + const AnimeEpisodeInfo: Episode = { + name: animeEpisodeParseObj.anime.name, + url: `/anime/animelatinohd/episode/${episode}`, + number: number, + image: "", + servers: [], + }; + + const sel_lang = langType.filter((e) => e.lang == lang); + let f_index = 0; + + if (sel_lang.length) { + $("#languaje option").each((_i, e) => { + if ($(e).text() == sel_lang[0].type) { + f_index = Number($(e).val()); + } + }); + } else { + $("#languaje option").each((_i, e) => { + f_index = Number($(e).val()); + }); + } + + await Promise.all( + animeEpisodeParseObj.players[f_index].map( + async (e: { server: { title: string }; id: string }) => { + //let min = await axios.get("https://api.animelatinohd.com/stream/" + e.id, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62", "Referer": "https://www.animelatinohd.com/" } }) + // let dat = cheerio.load(min.data) + + const Server: EpisodeServer = { + name: e.server.title, + url: "", + }; + Server.url = "https://api.animelatinohd.com/stream/" + e.id; + Server.name = e.server.title; + + //state 1 + /*if (e.server.title == "Beta") { let sel = dat("script:contains('var foo_ui = function (event) {')") let sort = String(sel.html()) let domain = eval(sort.slice(sort.search("const url"), sort.search("const langDef")).replace("const url =", "").trim()) @@ -113,53 +131,64 @@ export class Dramanice { let id_file = sortMORE.replace("_x", "") Server.url = "https://filemoon.sx" + "/e/" + id_file }*/ - AnimeEpisodeInfo.servers.push(Server) - })) - - return AnimeEpisodeInfo; - } catch (error) { - console.log(error) - } + AnimeEpisodeInfo.servers.push(Server); + } + ) + ); + + return AnimeEpisodeInfo; + } catch (error) { + console.log(error); } - - async GetAnimeByFilter(search?: string, type?: number, page?: number, year?: string, genre?: string): Promise> { - try { - const { data } = await axios.get(`${this.api}/api/anime/list`, { - params: { - search: search, - type: type, - year: year, - genre: genre, - page: page - } - }); - - const animeSearchParseObj = data - - const animeSearch: ResultSearch = { - nav: { - count: animeSearchParseObj.data.length, - current: animeSearchParseObj.current_page, - next: animeSearchParseObj.data.length < 28 ? 0 : animeSearchParseObj.current_page + 1, - hasNext: animeSearchParseObj.data.length < 28 ? false : true - }, - results: [] - } - animeSearchParseObj.data.map(e => { - const animeSearchData: AnimeSearch = { - name: e.name, - image: "https://www.themoviedb.org/t/p/original" + e.poster + "?&w=53&q=95", - url: `/anime/animelatinohd/name/${e.slug}`, - type: "" - } - animeSearch.results.push(animeSearchData) - }) - return animeSearch; - } catch (error) { - console.log(error) - } + } + + async GetAnimeByFilter( + search?: string, + type?: number, + page?: number, + year?: string, + genre?: string + ): Promise> { + try { + const { data } = await axios.get(`${this.api}/api/anime/list`, { + params: { + search: search, + type: type, + year: year, + genre: genre, + page: page, + }, + }); + + const animeSearchParseObj = data; + + const animeSearch: ResultSearch = { + nav: { + count: animeSearchParseObj.data.length, + current: animeSearchParseObj.current_page, + next: + animeSearchParseObj.data.length < 28 + ? 0 + : animeSearchParseObj.current_page + 1, + hasNext: animeSearchParseObj.data.length < 28 ? false : true, + }, + results: [], + }; + animeSearchParseObj.data.map((e) => { + const animeSearchData: AnimeSearch = { + name: e.name, + image: + "https://www.themoviedb.org/t/p/original" + + e.poster + + "?&w=53&q=95", + url: `/anime/animelatinohd/name/${e.slug}`, + type: "", + }; + animeSearch.results.push(animeSearchData); + }); + return animeSearch; + } catch (error) { + console.log(error); } - + } } - - diff --git a/src/scraper/sites/manga/MangaBuddy/MangaBuddy.ts b/src/scraper/sites/manga/MangaBuddy/MangaBuddy.ts index b2b5a73f..feede7df 100644 --- a/src/scraper/sites/manga/MangaBuddy/MangaBuddy.ts +++ b/src/scraper/sites/manga/MangaBuddy/MangaBuddy.ts @@ -1,7 +1,11 @@ import axios from "axios"; import { load } from "cheerio"; -import { Manga, IMangaChapter, IMangaResult } from "../../../../types/manga"; -import { IResultSearch } from "@animetypes/search"; +import { + Manga, + type IMangaChapter, + type IMangaResult, +} from "../../../../types/manga"; +import { type IResultSearch } from "@animetypes/search"; export class MangaBuddy { readonly url = "https://mangabuddy.com"; diff --git a/src/scraper/sites/manga/MangaReader/MangaReader.ts b/src/scraper/sites/manga/MangaReader/MangaReader.ts index 552d61a2..584219db 100644 --- a/src/scraper/sites/manga/MangaReader/MangaReader.ts +++ b/src/scraper/sites/manga/MangaReader/MangaReader.ts @@ -1,27 +1,30 @@ import { Image } from "../../../../types/image"; import { - IMangaResult, - Manga, + type IMangaResult, + MangaMedia, MangaChapter, - MangaVolume + MangaVolume, } from "../../../../types/manga"; import axios from "axios"; import { load } from "cheerio"; import { MangaReaderFilterLanguage, MangaReaderChapterType, - MangaReaderFilterData + MangaReaderFilterData, } from "./MangaReaderTypes"; -import { IResultSearch, ResultSearch } from "../../../../types/search"; +import { type IResultSearch, ResultSearch } from "../../../../types/search"; +import { MangaScraperModel } from "../../../../models/MangaScraperModel"; -export class MangaReader { +export class MangaReader extends MangaScraperModel { readonly url = "https://mangareader.to"; - private async GetMangaVolumeRange(mangaId: number) { + private async GetMangaVolumeRange(mangaId: string) { const { data } = await axios.get(`${this.url}/a-${mangaId}`); const $ = load(data); - const rangeResult: number[] = $("div.volume-list-ul div.manga_list div.manga_list-wrap") + const rangeResult: number[] = $( + "div.volume-list-ul div.manga_list div.manga_list-wrap" + ) .find("div.item") .map((_, element) => { const mangaVolumeTitle = $(element) @@ -29,21 +32,24 @@ export class MangaReader { .text() .trim(); return Number(mangaVolumeTitle.split(" ").at(-1)); - }).get(); + }) + .get(); return rangeResult; } private async GetSpecificMangaChapterName( - mangaId: number, + mangaId: string, chapterNumber: number, - language: typeof MangaReaderFilterLanguage[number], + language: (typeof MangaReaderFilterLanguage)[number], type: MangaReaderChapterType ): Promise { const { data } = await axios.get(`${this.url}/a-${mangaId}`); const $ = load(data); - let langCode: typeof MangaReaderFilterLanguage[number] = MangaReaderFilterLanguage[MangaReaderFilterLanguage.indexOf(language)] || ""; + let langCode: (typeof MangaReaderFilterLanguage)[number] = + MangaReaderFilterLanguage[MangaReaderFilterLanguage.indexOf(language)] || + ""; let result = ``; let chapterItemHtmlTag = ``; @@ -64,7 +70,10 @@ export class MangaReader { if (!chapters.length) throw new Error("Chapters doesn't found."); - const chaptersTitle: string[] = chapters.find(chapterTitleHtmlTag).map((_, element) => $(element).text().trim()).get(); + const chaptersTitle: string[] = chapters + .find(chapterTitleHtmlTag) + .map((_, element) => $(element).text().trim()) + .get(); for (let title of chaptersTitle) { if (title.includes(chapterTitleMatch)) { @@ -82,27 +91,39 @@ export class MangaReader { if (type === "chapter") idType = "chap"; else if (type === "volume") idType = "vol"; - const { data: pagesAjaxData } = await axios.get(`${this.url}/ajax/image/list/${idType}/${chapterId}?mode=horizontal&quality=high`); + const { data: pagesAjaxData } = await axios.get( + `${this.url}/ajax/image/list/${idType}/${chapterId}?mode=horizontal&quality=high` + ); const $pagesAjaxData = load(pagesAjaxData.html); - const pagesSection = $pagesAjaxData("div#main-wrapper div.container-reader-hoz div#divslide div.divslide-wrapper div.ds-item").find("div.ds-image") + const pagesSection = $pagesAjaxData( + "div#main-wrapper div.container-reader-hoz div#divslide div.divslide-wrapper div.ds-item" + ).find("div.ds-image"); - let pages = pagesSection.map((_, element) => $pagesAjaxData(element).attr("data-url")).get(); + let pages = pagesSection + .map((_, element) => $pagesAjaxData(element).attr("data-url")) + .get(); return pages; } - async GetMangaInfo(mangaId: number): Promise { + async GetItemInfo(mangaId: string): Promise { try { const { data } = await axios.get(`${this.url}/a-${mangaId}`); - const { data: charactersAjaxList } = await axios.get(`${this.url}/ajax/character/list/${mangaId}`); + const { data: charactersAjaxList } = await axios.get( + `${this.url}/ajax/character/list/${mangaId}` + ); const $ = load(data); const $characterListAjaxResult = load(charactersAjaxList.html); - const charactersSection = $characterListAjaxResult("div.character-list div.cl-item div.cli-info"); + const charactersSection = $characterListAjaxResult( + "div.character-list div.cl-item div.cli-info" + ); const title = $("h2.manga-name").text().trim(); - const altTitle = $("div.manga-name-or").text().trim() ? Array.of($("div.manga-name-or").text().trim()) : null; + const altTitle = $("div.manga-name-or").text().trim() + ? Array.of($("div.manga-name-or").text().trim()) + : null; const thumbnailUrl = $("div.manga-poster img.manga-poster-img").attr( "src" ); @@ -114,15 +135,18 @@ export class MangaReader { .trim(); // Manga genres - const mangaGenres: Array = $("div.genres").find("a").map((_, element) => $(element).text().trim()).get(); + const mangaGenres: Array = $("div.genres") + .find("a") + .map((_, element) => $(element).text().trim()) + .get(); - const manga = new Manga(); + const manga = new MangaMedia(); manga.id = mangaId.toString(); - manga.title = title; - manga.altTitles = altTitle; + manga.name = title; + manga.alt_names = altTitle; manga.thumbnail = new Image(thumbnailUrl); - manga.description = description || null; + manga.synopsis = description || null; if (status === "Finished") manga.status = "completed"; else manga.status = "ongoing"; @@ -130,36 +154,43 @@ export class MangaReader { manga.genres = mangaGenres; if (charactersSection.html()) { - const characters = charactersSection.find("h4.cl-name a").map((_, element) => $characterListAjaxResult(element).text().trim()).get(); + const characters = charactersSection + .find("h4.cl-name a") + .map((_, element) => $characterListAjaxResult(element).text().trim()) + .get(); manga.characters = characters; } else manga.characters = null; // Get manga chapters manga.chapters = []; - const mangaChapterItemSection = $( - "div.chapters-list-ul ul.ulclear" - ); + const mangaChapterItemSection = $("div.chapters-list-ul ul.ulclear"); let langCode: string = ``; if (mangaChapterItemSection?.first().attr("id")) langCode = mangaChapterItemSection.first().attr("id").split("-")[0]; - mangaChapterItemSection.first().find("li.chapter-item").each((_, element) => { - const mangaChapter = new MangaChapter(); - - const mangaTitle = $(element) - .find("a.item-link span.name") - .text() - .trim(); - const mangaChapterNumber = mangaTitle.split(" ").at(1).replace(":", ""); - - mangaChapter.title = mangaTitle; - mangaChapter.id = mangaId.toString(); - mangaChapter.url = `/manga/mangareader/chapter/${mangaId.toString()}?number=${mangaChapterNumber}&lang=${langCode}`; - mangaChapter.images = null; - - manga.chapters.push(mangaChapter); - }); + mangaChapterItemSection + .first() + .find("li.chapter-item") + .each((_, element) => { + const mangaChapter = new MangaChapter(); + + const mangaTitle = $(element) + .find("a.item-link span.name") + .text() + .trim(); + const mangaChapterNumber = mangaTitle + .split(" ") + .at(1) + .replace(":", ""); + + mangaChapter.name = mangaTitle; + mangaChapter.id = mangaId.toString(); + mangaChapter.url = `/manga/mangareader/chapter/${mangaId.toString()}?number=${mangaChapterNumber}&lang=${langCode}`; + mangaChapter.images = null; + + manga.chapters.push(mangaChapter); + }); // Get manga volumes const mangaVolumeRange = await this.GetMangaVolumeRange(mangaId); @@ -177,34 +208,37 @@ export class MangaReader { .attr("id") .split("-")[0]; - mangaVolumeItemSection.first().find("div.item").each((_, element) => { - const mangaVolume = new MangaVolume(); - - const mangaVolumeTitle = $(element) - .find("div.manga-poster span.tick-item") - .text() - .trim(); - const mangaVolumeNumber = mangaVolumeTitle.split(" ").at(-1); - const mangaVolumeThumbnail = $(element) - .find("div.manga-poster img.manga-poster-img") - .attr("src"); - - mangaVolume.range = [mangaVolumeRange.at(-1), mangaVolumeRange.at(0)]; - mangaVolume.id = mangaId.toString(); - mangaVolume.title = mangaVolumeTitle; - mangaVolume.number = Number(mangaVolumeNumber); - mangaVolume.thumbnail = mangaVolumeThumbnail; - mangaVolume.url = `/manga/mangareader/volume/${mangaId.toString()}?number=${mangaVolumeNumber}&lang=${langVolumeCode}`; - - manga.volumes.push(mangaVolume); - }); + mangaVolumeItemSection + .first() + .find("div.item") + .each((_, element) => { + const mangaVolume = new MangaVolume(); + + const mangaVolumeTitle = $(element) + .find("div.manga-poster span.tick-item") + .text() + .trim(); + const mangaVolumeNumber = mangaVolumeTitle.split(" ").at(-1); + const mangaVolumeThumbnail = $(element) + .find("div.manga-poster img.manga-poster-img") + .attr("src"); + + mangaVolume.range = [mangaVolumeRange.at(-1), mangaVolumeRange.at(0)]; + mangaVolume.id = mangaId.toString(); + mangaVolume.name = mangaVolumeTitle; + mangaVolume.num = Number(mangaVolumeNumber); + mangaVolume.thumbnail = new Image(mangaVolumeThumbnail); + mangaVolume.url = `/manga/mangareader/volume/${mangaId.toString()}?number=${mangaVolumeNumber}&lang=${langVolumeCode}`; + + manga.volumes.push(mangaVolume); + }); if ( - mangaGenres.some(genre => genre === "Hentai" || genre === "Ecchi") === + mangaGenres.some((genre) => genre === "Hentai" || genre === "Ecchi") === true ) - manga.isNSFW = true; - else manga.isNSFW = false; + manga.nsfw = true; + else manga.nsfw = false; return manga; } catch (error) { @@ -215,7 +249,7 @@ export class MangaReader { } } - async Filter( + async GetItemByFilter( options: MangaReaderFilterData ): Promise> { const { @@ -231,7 +265,7 @@ export class MangaReader { endMonth, endDay, sort, - numPage + numPage, } = options; if ( startYear <= 0 || @@ -258,8 +292,8 @@ export class MangaReader { em: endMonth ?? "", ed: endDay ?? "", sort: sort ?? "", - page: numPage ?? 1 - } + page: numPage ?? 1, + }, }); const $ = load(data); @@ -291,9 +325,9 @@ export class MangaReader { mangaFilterResults.results.push({ id: mangaResultsID, - title: mangaResultsTitle, + name: mangaResultsTitle, thumbnail: new Image(mangaResultsThumbnail), - url: `/manga/mangareader/title/${mangaResultsID}` + url: `/manga/mangareader/name/${mangaResultsID}`, }); }); @@ -301,16 +335,18 @@ export class MangaReader { } async GetMangaChapters( - mangaId: number, + mangaId: string, chapterNumber: number, - language: typeof MangaReaderFilterLanguage[number], + language: (typeof MangaReaderFilterLanguage)[number], type: MangaReaderChapterType ) { try { - const { data } = await axios.get(`${this.url}/read/a-${mangaId}/${language}/${type}-${chapterNumber}`); + const { data } = await axios.get( + `${this.url}/read/a-${mangaId}/${language}/${type}-${chapterNumber}` + ); const $ = load(data); - const chapterId = $('div#wrapper').attr('data-reading-id'); + const chapterId = $("div#wrapper").attr("data-reading-id"); if (!chapterId) throw new Error("Chapter pages doesn't found."); @@ -325,10 +361,10 @@ export class MangaReader { const mangaChapter = new MangaChapter(); const chapterPages = await this.GetMangaPages(chapterId, "chapter"); - mangaChapter.title = mangaChapterName; + mangaChapter.name = mangaChapterName; mangaChapter.id = mangaId; mangaChapter.images = chapterPages; - mangaChapter.number = chapterNumber; + mangaChapter.num = chapterNumber; mangaChapter.url = `/manga/mangareader/chapter/${mangaId.toString()}?number=${chapterNumber}&lang=${language}`; return mangaChapter; @@ -337,11 +373,11 @@ export class MangaReader { const mangaVolumeRange = await this.GetMangaVolumeRange(mangaId); const volumePages = await this.GetMangaPages(chapterId, "volume"); - mangaVolume.title = mangaChapterName; + mangaVolume.name = mangaChapterName; mangaVolume.id = mangaId; mangaVolume.range = [mangaVolumeRange.at(-1), mangaVolumeRange.at(0)]; mangaVolume.images = volumePages; - mangaVolume.number = chapterNumber; + mangaVolume.num = chapterNumber; mangaVolume.url = `/manga/mangareader/volume/${mangaId.toString()}?number=${chapterNumber}&lang=${language}`; return mangaVolume; diff --git a/src/scraper/sites/manga/MangaReader/MangaReaderTypes.ts b/src/scraper/sites/manga/MangaReader/MangaReaderTypes.ts index 9115c54d..8fd53b8f 100644 --- a/src/scraper/sites/manga/MangaReader/MangaReaderTypes.ts +++ b/src/scraper/sites/manga/MangaReader/MangaReaderTypes.ts @@ -6,7 +6,7 @@ export enum MangaReaderFilterType { LightNovel, Manhwa, Manhua, - Comic = 8 + Comic = 8, } export enum MangaReaderFilterStatus { @@ -15,7 +15,7 @@ export enum MangaReaderFilterStatus { Publishing, OnHiatus, Discontinued, - NotYetPublished + NotYetPublished, } export enum MangaReaderFilterRatingType { @@ -25,7 +25,7 @@ export enum MangaReaderFilterRatingType { Teens, Mature, MildNudity, - Adults + Adults, } export enum MangaReaderFilterScore { @@ -39,10 +39,17 @@ export enum MangaReaderFilterScore { Good, VeryGood, Great, - Masterpiece + Masterpiece, } -export const MangaReaderFilterLanguage = ["", "en", "ja", "ko", "zh", "fr"] as const; +export const MangaReaderFilterLanguage = [ + "", + "en", + "ja", + "ko", + "zh", + "fr", +] as const; export enum MangaReaderFilterSort { All = "", @@ -50,7 +57,7 @@ export enum MangaReaderFilterSort { Score = "score", NameAZ = "name-az", ReleaseDate = "release-date", - MostViewed = "most-viewed" + MostViewed = "most-viewed", } export type MangaReaderChapterType = "chapter" | "volume"; @@ -60,7 +67,7 @@ export interface MangaReaderFilterData { status?: MangaReaderFilterStatus; ratingType?: MangaReaderFilterRatingType; score?: MangaReaderFilterScore; - language?: typeof MangaReaderFilterLanguage[number]; + language?: (typeof MangaReaderFilterLanguage)[number]; startYear?: number; startMonth?: number; startDay?: number; diff --git a/src/scraper/sites/manga/comick/Comick.ts b/src/scraper/sites/manga/comick/Comick.ts index fc3fdda9..505462ba 100644 --- a/src/scraper/sites/manga/comick/Comick.ts +++ b/src/scraper/sites/manga/comick/Comick.ts @@ -1,196 +1,248 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Manga, MangaChapter, IMangaResult } from "../../../../types/manga"; -import { IResultSearch } from "../../../../types/search"; +import { + MangaMedia, + MangaChapter, + type IMangaResult, +} from "../../../../types/manga"; +import { type IResultSearch } from "../../../../types/search"; //Default Set Axios Cookie -axios.defaults.withCredentials = true -axios.defaults.headers.common["User-Agent"] = "Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G532G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Mobile Safari/537.36"; +axios.defaults.withCredentials = true; +axios.defaults.headers.common["User-Agent"] = + "Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G532G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Mobile Safari/537.36"; /** List of Domains * https://comick.app - * + * * https://api.comick.app - * + * * https://api.comick.io - * + * * https://comick.cc - * + * * https://meo.comick.pictures -*/ - + */ export class Comick { - readonly url = "https://comick.app"; - readonly api = "https://api.comick.io" - - async GetMangaByFilter(search?: string, type?: number, year?: string, genre?: string) { - try { - const { data } = await axios.get(`${this.api}/v1.0/search`, { - params: { - q: search, - status: type, - year: year, - genre: genre, - } - }); - - const ResultList: IResultSearch = { - results: [] - } - data.map((e: { id: number; title: string; md_covers: { b2key: string; }[]; slug: string; }) => { - const ListMangaResult: IMangaResult = { - id: e.id, - title: e.title, - thumbnail: { - url: "https://meo.comick.pictures/" + e.md_covers[0].b2key - }, - url: `/manga/comick/title/${e.slug}` - } - ResultList.results.push(ListMangaResult) - }) - - return ResultList - } catch (error) { - console.log(error) + readonly url = "https://comick.app"; + readonly api = "https://api.comick.io"; + + async GetMangaByFilter( + search?: string, + status?: number, + type?: number, + year?: number, + genre?: string, + page?: number + ): Promise> { + try { + const { data } = await axios.get(`${this.api}/v1.0/search`, { + params: { + q: search, + status: status, + type:type, + year: year, + page: page, + genre: genre, + }, + }); + const ResultList: IResultSearch = { + nav: { count: data.length, + current: page ? page : 1, + next: + data.length < 49 + ? 0 + : page + 1, + hasNext: data.length < 49 ? false : true, }, + results: [], + }; + data.map( + (e: { + id: number; + title: string; + md_covers: { b2key: string }[]; + slug: string; + }) => { + const ListMangaResult: IMangaResult = { + id: e.id, + name: e.title, + thumbnail: { + url: "https://meo.comick.pictures/" + e.md_covers[0].b2key, + }, + url: `/manga/comick/name/${e.slug}`, + }; + ResultList.results.push(ListMangaResult); } - } - - async GetMangaInfo(manga: string, lang: string): Promise { - try { - const { data } = await axios.get(`${this.api}/comic/${manga}`); - // build static - ///_next/data/S1XqseNRmzozm3TaUH1lU/comic/00-solo-leveling.json - const currentLang = lang ? `?lang=${lang}` : `?lang=en` - const mangaInfoParseObj = data - - const dataApi = await axios.get(`${this.api}/comic/${mangaInfoParseObj.comic.hid}/chapters${currentLang}`); - - const MangaInfo: Manga = { - id: mangaInfoParseObj.comic.id, - title: mangaInfoParseObj.comic.title, - altTitles: mangaInfoParseObj.comic.md_titles.map((e: { title: string; }) => e.title), - url: `/manga/comick/title/${mangaInfoParseObj.comic.slug}`, - description: mangaInfoParseObj.comic.desc, - isNSFW: mangaInfoParseObj.comic.hentai, - langlist: mangaInfoParseObj.langList, - status: mangaInfoParseObj.comic.status == "1" ? "ongoing" : "completed", - authors: mangaInfoParseObj.authors.map((e: { name: string; }) => e.name), - genres: mangaInfoParseObj.comic.md_comic_md_genres.map((e: { md_genres: {name:string;} }) => e.md_genres.name), - chapters: [], - thumbnail: { - url: "https://meo.comick.pictures/" + mangaInfoParseObj.comic.md_covers[0].b2key - } - } + ); - dataApi.data.chapters.map((e: { id: number; title: string; hid: string; chap: number; created_at: string; lang: string; }) => { - const mindate = new Date(e.created_at); - const langChapter = currentLang ? currentLang : "?lang=" + e.lang - - const MangaInfoChapter: MangaChapter = { - id: e.id, - title: e.title, - url: `/manga/comick/chapter/${e.hid}-${mangaInfoParseObj.comic.slug}-${e.chap ? e.chap : "err"}${langChapter}`, - number: e.chap, - images: null, - cover: null, - date: { - year: mindate.getFullYear() ? mindate.getFullYear() : null, - month: mindate.getMonth() ? mindate.getMonth() : null, - day: mindate.getDay() ? mindate.getDay() : null - } - } - return MangaInfo.chapters.push(!langChapter.includes("?lang=id") ? MangaInfoChapter : null) - }) - - return MangaInfo - } catch (error) { - console.log(error) - } + return ResultList; + } catch (error) { } - - async GetChapterInfo(manga: string, lang: string) { - try { - - const currentLang = lang ? "-" + lang : "-en"; - const hid = manga.substring(0, manga.indexOf("-")); - const idTitle = manga.substring(manga.indexOf("-") + 1); - const idNumber = idTitle.substring(idTitle.lastIndexOf("-") + 1); - const title = idTitle.substring(0, idTitle.lastIndexOf("-")); - - let urlchange = "" - - if (idNumber != "err") { - urlchange = `${hid}-chapter-${idNumber}${currentLang}` - } else { - urlchange = hid + } + + async GetMangaInfo(manga: string, lang: string): Promise { + try { + const { data } = await axios.get( + `${this.url}/comic/${manga}` + ); + const $ = cheerio.load(data); + const mangaInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()) + .props.pageProps; + const buildId = JSON.parse($("#__NEXT_DATA__").html()).buildId; + const currentLang = lang ? `?lang=${lang}` : `?lang=en`; + let dataApi = null + if (mangaInfoParseObj.firstChap) { + dataApi = await axios.get( + `${this.url}/_next/data/${buildId}/comic/${manga}/${mangaInfoParseObj.firstChap.hid + "-chapter-" + mangaInfoParseObj.firstChap.chap + "-" + mangaInfoParseObj.firstChap.lang}.json` + ); + } + const MangaInfo: MangaMedia = { + id: mangaInfoParseObj.comic.id, + name: mangaInfoParseObj.comic.title, + alt_names: mangaInfoParseObj.comic.md_titles.map( + (e: { title: string }) => e.title + ), + url: `/manga/comick/name/${mangaInfoParseObj.comic.slug}`, + synopsis: mangaInfoParseObj.comic.desc, + nsfw: mangaInfoParseObj.comic.hentai, + langlist: mangaInfoParseObj.langList, + status: mangaInfoParseObj.comic.status == "1" ? "ongoing" : "completed", + authors: mangaInfoParseObj.authors.map((e: { name: string }) => e.name), + genres: mangaInfoParseObj.comic.md_comic_md_genres.map( + (e: { md_genres: { name: string } }) => e.md_genres.name + ), + chapters: [], + thumbnail: { + url: + "https://meo.comick.pictures/" + + mangaInfoParseObj.comic.md_covers[0].b2key, + }, + }; + if (mangaInfoParseObj.firstChap) { + dataApi.data.pageProps.chapters.map( + (e: { + id: number; + title: string; + hid: string; + chap: number; + created_at: string; + lang: string; + }) => { + const mindate = new Date(e.created_at); + const langChapter = currentLang ? currentLang : "?lang=" + e.lang; + + const MangaInfoChapter: MangaChapter = { + id: e.id, + name: e.title, + url: `/manga/comick/chapter/${e.hid}-${mangaInfoParseObj.comic.slug + }-${e.chap ? e.chap : "err"}${langChapter}`, + num: Number(e.chap), + images: null, + date: { + year: mindate.getFullYear() ? mindate.getFullYear() : null, + month: mindate.getMonth() ? mindate.getMonth() : null, + day: mindate.getDay() ? mindate.getDay() : null, + }, + }; + return MangaInfo.chapters.push( + !langChapter.includes("?lang=id") ? MangaInfoChapter : null + ); + } + ); + } + return MangaInfo; + } catch (error) { + } + } + + async GetChapterInfo(manga: string, lang: string) { + try { + const currentLang = lang ? "-" + lang : "-en"; + const hid = manga.substring(0, manga.indexOf("-")); + const idTitle = manga.substring(manga.indexOf("-") + 1); + const idNumber = idTitle.substring(idTitle.lastIndexOf("-") + 1); + const title = idTitle.substring(0, idTitle.lastIndexOf("-")); + + let urlchange = ""; + + if (idNumber != "err") { + urlchange = `${hid}-chapter-${idNumber}${currentLang}`; + } else { + urlchange = hid; + } + + const { data } = await axios.get( + `${this.url}/comic/${title}/${urlchange}` + ); + const $ = cheerio.load(data); + + if (JSON.parse($("#__NEXT_DATA__").html()).isFallback == false) { + const mangaChapterInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()) + .props.pageProps; + const mindate = new Date(mangaChapterInfoParseObj.chapter.created_at); + + const MangaChapterInfoChapter: MangaChapter = { + id: mangaChapterInfoParseObj.chapter.id, + name: mangaChapterInfoParseObj.seoTitle, + url: `/manga/comick/chapter/${manga}`, + num: mangaChapterInfoParseObj.chapter.chap, + images: mangaChapterInfoParseObj.chapter.md_images.map( + (e: { w: number; h: number; name: string; b2key: string }) => { + return { + width: e.w, + height: e.h, + name: e.name, + image: "https://meo.comick.pictures/" + e.b2key, + }; } - - const { data } = await axios.get(`${this.url}/comic/${title}/${urlchange}`); - const $ = cheerio.load(data); - - if (JSON.parse($("#__NEXT_DATA__").html()).isFallback == false) { - const mangaChapterInfoParseObj = JSON.parse($("#__NEXT_DATA__").html()).props.pageProps - const mindate = new Date(mangaChapterInfoParseObj.chapter.created_at); - - const MangaChapterInfoChapter: MangaChapter = { - id: mangaChapterInfoParseObj.chapter.id, - title: mangaChapterInfoParseObj.seoTitle, - url: `/manga/comick/chapter/${manga}`, - number: mangaChapterInfoParseObj.chapter.chap, - images: mangaChapterInfoParseObj.chapter.md_images.map((e: { w: number; h: number; name: string; b2key: string; }) => { - return { - width: e.w, - height: e.h, - name: e.name, - image: "https://meo.comick.pictures/" + e.b2key - } - }), - cover: "https://meo.comick.pictures/" + mangaChapterInfoParseObj.chapter.md_comics.md_covers[0].b2key, - date: { - year: mindate.getFullYear() ? mindate.getFullYear() : null, - month: mindate.getMonth() ? mindate.getMonth() : null, - day: mindate.getDay() ? mindate.getDay() : null - } - } - return MangaChapterInfoChapter; - - } else { - const buildid = JSON.parse($("#__NEXT_DATA__").html()).buildId - const currentUrl = idNumber == "err" ? `${title}/${hid}.json?slug=${title}&chapter=${hid}` : `${title}/${hid}-chapter-${idNumber}${currentLang}.json?slug=${title}&chapter=${hid}-chapter-${idNumber}${currentLang}` - const dataBuild = await axios.get(`${this.url}/_next/data/${buildid}/comic/${currentUrl}`); - - const mindate = new Date(dataBuild.data.pageProps.chapter.created_at); - - const MangaChapterInfoChapter: MangaChapter = { - id: dataBuild.data.pageProps.chapter.id, - title: dataBuild.data.pageProps.seoTitle, - url: `/manga/comick/chapter/${manga}`, - number: dataBuild.data.pageProps.chapter.chap, - images: dataBuild.data.pageProps.chapter.md_images.map((s: { w: number; h: number; name: string; b2key: string; }) => { - return { - width: s.w, - height: s.h, - name: s.name, - image: "https://meo.comick.pictures/" + s.b2key - } - }), - cover: "https://meo.comick.pictures/" + dataBuild.data.pageProps.chapter.md_comics.md_covers[0].b2key, - date: { - year: mindate.getFullYear() ? mindate.getFullYear() : null, - month: mindate.getMonth() ? mindate.getMonth() : null, - day: mindate.getDay() ? mindate.getDay() : null - } - } - - return MangaChapterInfoChapter; - + ), + thumbnail:{url:null,banner:"https://meo.comick.pictures/" +mangaChapterInfoParseObj.chapter.md_comics.md_covers[0].b2key}, + date: { + year: mindate.getFullYear() ? mindate.getFullYear() : null, + month: mindate.getMonth() ? mindate.getMonth() : null, + day: mindate.getDay() ? mindate.getDay() : null, + }, + }; + return MangaChapterInfoChapter; + } else { + const buildid = JSON.parse($("#__NEXT_DATA__").html()).buildId; + const currentUrl = + idNumber == "err" + ? `${title}/${hid}.json?slug=${title}&chapter=${hid}` + : `${title}/${hid}-chapter-${idNumber}${currentLang}.json?slug=${title}&chapter=${hid}-chapter-${idNumber}${currentLang}`; + const dataBuild = await axios.get( + `${this.url}/_next/data/${buildid}/comic/${currentUrl}` + ); + + const mindate = new Date(dataBuild.data.pageProps.chapter.created_at); + + const MangaChapterInfoChapter: MangaChapter = { + id: dataBuild.data.pageProps.chapter.id, + name: dataBuild.data.pageProps.seoTitle, + url: `/manga/comick/chapter/${manga}`, + num: dataBuild.data.pageProps.chapter.chap, + images: dataBuild.data.pageProps.chapter.md_images.map( + (s: { w: number; h: number; name: string; b2key: string }) => { + return { + width: s.w, + height: s.h, + name: s.name, + image: "https://meo.comick.pictures/" + s.b2key, + }; } - } catch (error) { - console.log(error) - } + ), + thumbnail:{url:null,banner:"https://meo.comick.pictures/" +dataBuild.data.pageProps.chapter.md_comics.md_covers[0].b2key}, + date: { + year: mindate.getFullYear() ? mindate.getFullYear() : null, + month: mindate.getMonth() ? mindate.getMonth() : null, + day: mindate.getDay() ? mindate.getDay() : null, + }, + }; + + return MangaChapterInfoChapter; + } + } catch (error) { } - + } } - - diff --git a/src/scraper/sites/manga/inmanga/Inmanga.ts b/src/scraper/sites/manga/inmanga/Inmanga.ts index dcaad05a..a2729793 100644 --- a/src/scraper/sites/manga/inmanga/Inmanga.ts +++ b/src/scraper/sites/manga/inmanga/Inmanga.ts @@ -1,162 +1,255 @@ import * as cheerio from "cheerio"; import axios from "axios"; -import { Manga, MangaChapter, IMangaResult } from "../../../../types/manga" -import { IResultSearch } from "../../../../types/search"; +import { + MangaMedia, + MangaChapter, + type IMangaResult, +} from "../../../../types/manga"; +import { type IResultSearch } from "../../../../types/search"; //Default Set Axios Cookie -axios.defaults.withCredentials = true -axios.defaults.headers.common["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"; +axios.defaults.withCredentials = true; +axios.defaults.headers.common["User-Agent"] = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"; export class Inmanga { - readonly url = "https://inmanga.com"; - - async GetMangaByFilter(search?: string, type?: number, genre?: string[]) { - try { - const formdata = new FormData(); - formdata.append("filter[queryString]", search); - formdata.append("filter[broadcastStatus]", String(type)) - formdata.append("filter[skip]", "0"); - formdata.append("filter[take]", "10"); - const genreList = ['33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '-1'] - - if (genre) { - genre.map((e) => { - if (genreList.includes(e)) { - formdata.append("filter[generes][]", genreList[genreList.indexOf(e)]); - } - }) - } else { - formdata.append("filter[generes][]", "-1"); - } - - const bodyContent = formdata; - const { data } = await axios.post(`${this.url}/manga/getMangasConsultResult`, bodyContent); - const $ = cheerio.load(data); - - const ResultList: IResultSearch = { - results: [] - } - - $("a").each((_i, e) => { - const idtd = $(e).attr("href").split("/") - const name = idtd[3] - const cid = idtd[4] - const title = $(e).find(".list-group.col-xs-12 .m0.list-group-item.ellipsed-text").text().trim() - - const ListMangaResult: IMangaResult = { - id: null, - title: title, - thumbnail: { - url: `https://inmanga.com/thumbnails/manga/${name}/${cid}` - }, - - // old version `/manga/inmanga/title/${title.replace(/[^a-zA-Z:]/g, "-")}` - url: `/manga/inmanga/title/${name}?cid=${cid}` - } - ResultList.results.push(ListMangaResult) - }) - - return ResultList - } catch (error) { - console.log(error) - } + readonly url = "https://inmanga.com"; + + async GetMangaByFilter(search?: string, type?: number, genre?: string[]): Promise> { + try { + const formdata = new FormData(); + formdata.append("filter[queryString]", search); + formdata.append("filter[broadcastStatus]", String(type)); + formdata.append("filter[skip]", "0"); + formdata.append("filter[take]", "10"); + const genreList = [ + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59", + "60", + "61", + "62", + "63", + "64", + "65", + "66", + "67", + "68", + "69", + "70", + "71", + "72", + "73", + "74", + "75", + "76", + "77", + "78", + "79", + "80", + "81", + "82", + "83", + "84", + "85", + "86", + "87", + "88", + "-1", + ]; + + if (genre) { + genre.map((e) => { + if (genreList.includes(e)) { + formdata.append( + "filter[generes][]", + genreList[genreList.indexOf(e)] + ); + } + }); + } else { + formdata.append("filter[generes][]", "-1"); + } + + const bodyContent = formdata; + const { data } = await axios.post( + `${this.url}/manga/getMangasConsultResult`, + bodyContent + ); + const $ = cheerio.load(data); + + const ResultList: IResultSearch = { + results: [], + }; + + $("a").each((_i, e) => { + const idtd = $(e).attr("href").split("/"); + const name = idtd[3]; + const cid = idtd[4]; + const title = $(e) + .find(".list-group.col-xs-12 .m0.list-group-item.ellipsed-text") + .text() + .trim(); + + const ListMangaResult: IMangaResult = { + id: null, + name: title, + thumbnail: { + url: `https://inmanga.com/thumbnails/manga/${name}/${cid}`, + }, + + // old version `/manga/inmanga/title/${title.replace(/[^a-zA-Z:]/g, "-")}` + url: `/manga/inmanga/name/${name}?cid=${cid}`, + }; + ResultList.results.push(ListMangaResult); + }); + + return ResultList; + } catch (error) { + console.log(error); } - - async GetMangaInfo(manga: string,cid: string): Promise { - try { - const dataPost = await axios.get(`${this.url}/ver/manga/${manga}/${cid}`); - const $_ = cheerio.load(dataPost.data); - - const MangaInfo: Manga = { - id: cid, - title: $_("div.col-md-3.col-sm-4 div.panel-heading.visible-xs").text(), - altTitles: [], - url: `/manga/inmanga/title/${manga}`, - description: $_("body > div > section > div > div > div:nth-child(6) > div > div.panel-body").text().trim(), - isNSFW: false, - status: $_(".col-md-3.col-sm-4 .list-group > a:nth-child(1) > span").text() == "En emisión" ? "ongoing" : "completed", - authors: [], - genres: [], - chapters: [], - thumbnail: { - url: `https://inmanga.com/thumbnails/manga/${manga}/${cid}` - } - } - - $_(".col-md-9.col-sm-8.col-xs-12 .panel.widget .panel-heading .text-muted span").each((_i, e) => MangaInfo.altTitles.push($_(e).text().replace(";", ""))) - $_(".col-md-9.col-sm-8.col-xs-12 .panel.widget .panel-heading .label.ml-sm").each((_i, e) => MangaInfo.genres.push($_(e).text().trim())) - - MangaInfo.altTitles.slice(MangaInfo.altTitles.indexOf('""'), 0) - MangaInfo.genres.slice(MangaInfo.genres.indexOf('""'), 0) - - const dataChPost = await axios.get(`${this.url}/chapter/getall?mangaIdentification=${cid}`); - const dataCh = JSON.parse(dataChPost.data.data); - dataCh.result.map((e: { Id: number; MangaName: string; Number: number; Identification: string; }) => { - const MangaInfoChapter: MangaChapter = { - id: e.Id, - title: e.MangaName, - url: `/manga/inmanga/chapter/${manga}-${e.Number}?cid=${e.Identification}`, // Change url (: = title ) manga.replace(/[^a-zA-Z:]/g," ") - number: e.Number, - images: null, - cover: null, - date: { - year: null, - month: null, - day: null - } - } - MangaInfo.chapters.push(MangaInfoChapter); - }) - - return MangaInfo - } catch (error) { - console.log(error) + } + + async GetMangaInfo(manga: string, cid: string): Promise { + try { + const dataPost = await axios.get(`${this.url}/ver/manga/${manga}/${cid}`); + const $_ = cheerio.load(dataPost.data); + const AltNames = [] + + const MangaInfo: MangaMedia = { + id: cid, + name: $_("div.col-md-3.col-sm-4 div.panel-heading.visible-xs").text(), + alt_names: AltNames, + url: `/manga/inmanga/name/${manga}`, + synopsis: $_( + "body > div > section > div > div > div:nth-child(6) > div > div.panel-body" + ) + .text() + .trim(), + nsfw: false, + status: + $_(".col-md-3.col-sm-4 .list-group > a:nth-child(1) > span").text() == + "En emisión" + ? "ongoing" + : "completed", + authors: [], + genres: [], + chapters: [], + thumbnail: { + url: `https://inmanga.com/thumbnails/manga/${manga}/${cid}`, + }, + }; + $_( + ".col-md-9.col-sm-8.col-xs-12 .panel.widget .panel-heading .text-muted span" + ).each((_i, e) => + AltNames.push($_(e).text().replace(";", "")) + ); + + $_( + ".col-md-9.col-sm-8.col-xs-12 .panel.widget .panel-heading .label.ml-sm" + ).each((_i, e) => MangaInfo.genres.push($_(e).text().trim())); + + MangaInfo.alt_names.slice(MangaInfo.alt_names.indexOf('""'), 0); + MangaInfo.genres.slice(MangaInfo.genres.indexOf('""'), 0); + + const dataChPost = await axios.get( + `${this.url}/chapter/getall?mangaIdentification=${cid}` + ); + const dataCh = JSON.parse(dataChPost.data.data); + dataCh.result.map( + (e: { + Id: number; + MangaName: string; + Number: number; + Identification: string; + }) => { + const MangaInfoChapter: MangaChapter = { + id: e.Id, + name: e.MangaName, + url: `/manga/inmanga/chapter/${manga}-${e.Number}?cid=${e.Identification}`, // Change url (: = title ) manga.replace(/[^a-zA-Z:]/g," ") + num: e.Number, + images: null, + date: { + year: null, + month: null, + day: null, + }, + }; + MangaInfo.chapters.push(MangaInfoChapter); } - } + ); - async GetChapterInfo(manga: string, cid: string) { - try { - const title = manga.substring(0, manga.lastIndexOf("-")); - const idNumber = Number(manga.substring(manga.lastIndexOf("-") + 1)); - - const { data } = await axios.get(`${this.url}/chapter/chapterIndexControls?identification=${cid}`) - const $ = cheerio.load(data); - - const allimages = [] - - const MangaChapterInfoChapter: MangaChapter = { - id: 1, - title: "", - url: `/manga/inmanga/chapter/`, - number: idNumber, - images: allimages, - cover: null, - date: { - year: null, - month: null, - day: null - } - } - - $(".p0.col-sm-12.col-xs-12.PagesContainer a").each((_i, e) => { - const id = $(e).find("img").attr("id") - const alt = $(e).find("img").attr("alt") - const page = $(e).find("img").attr("data-pagenumber") - - allimages.push({ - width: "", - height: "", - name: alt, - url: `https://pack-yak.intomanga.com/images/manga/${title}/chapter/${idNumber}/page/${page}/${id}` - }) - }) - - return MangaChapterInfoChapter; - } catch (error) { - console.log(error) - } + return MangaInfo; + } catch (error) { + console.log(error); } - + } + + async GetChapterInfo(manga: string, cid: string) { + try { + const title = manga.substring(0, manga.lastIndexOf("-")); + const idNumber = Number(manga.substring(manga.lastIndexOf("-") + 1)); + + const { data } = await axios.get( + `${this.url}/chapter/chapterIndexControls?identification=${cid}` + ); + const $ = cheerio.load(data); + + const allimages = []; + + const MangaChapterInfoChapter: MangaChapter = { + id: 1, + name: "", + url: `/manga/inmanga/chapter/`, + num: idNumber, + images: allimages, + date: { + year: null, + month: null, + day: null, + }, + }; + + $(".p0.col-sm-12.col-xs-12.PagesContainer a").each((_i, e) => { + const id = $(e).find("img").attr("id"); + const alt = $(e).find("img").attr("alt"); + const page = $(e).find("img").attr("data-pagenumber"); + + allimages.push({ + width: "", + height: "", + name: alt, + url: `https://pack-yak.intomanga.com/images/manga/${title}/chapter/${idNumber}/page/${page}/${id}`, + }); + }); + + return MangaChapterInfoChapter; + } catch (error) { + console.log(error); + } + } } - - diff --git a/src/scraper/sites/manga/mangaStructure.ts b/src/scraper/sites/manga/mangaStructure.ts deleted file mode 100644 index d3f45106..00000000 --- a/src/scraper/sites/manga/mangaStructure.ts +++ /dev/null @@ -1,11 +0,0 @@ -//example - -export class MangaProvider { - readonly url = "link"; - - async GetMangaInfo() {} - - async Filter() {} - - async GetMangaChapters() {} -} diff --git a/src/scraper/sites/manga/manganelo/Manganelo.ts b/src/scraper/sites/manga/manganelo/Manganelo.ts index 99c6b919..d271338b 100644 --- a/src/scraper/sites/manga/manganelo/Manganelo.ts +++ b/src/scraper/sites/manga/manganelo/Manganelo.ts @@ -1,13 +1,14 @@ -import { IMangaResult, Manga, MangaChapter } from "../../../../types/manga"; +import { IMangaResult, MangaMedia, MangaChapter } from "../../../../types/manga"; import axios from "axios"; import { load } from "cheerio"; import { Image } from "../../../../types/image"; import { ManganatoManagerUtils } from "./ManganatoManagerUtils"; -import { IManganatoFilterParams } from "./ManganatoTypes"; +import { type IManganatoFilterParams } from "./ManganatoTypes"; import { ResultSearch } from "../../../../types/search"; +import { MangaScraperModel } from "../../../../models/MangaScraperModel"; -export class Manganelo { - private readonly url = "https://manganelo.tv"; //chapmanganelo.com //mangakakalot.tv; +export class Manganelo extends MangaScraperModel { + readonly url = "https://manganelo.tv"; //chapmanganelo.com //mangakakalot.tv; readonly name = "manganelo"; private readonly manager = ManganatoManagerUtils.Instance; @@ -24,115 +25,150 @@ export class Manganelo { } private GetMangaStatus(data: cheerio.Root) { - const selector = data("div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(3) > td.table-value"); + const selector = data( + "div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(3) > td.table-value" + ); - if (selector.length == 0) - return null; + if (selector.length == 0) return null; - if (selector.text().trim() == "Ongoing") - return "ongoing"; - else - return "completed"; + if (selector.text().trim() == "Ongoing") return "ongoing"; + else return "completed"; } private GetMangaAuthors(data: cheerio.Root): string[] | null { - const selector = data("div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(2) > td.table-value"); - - if (selector.length == 0 && selector.find("a.a-h").length == 0) - return null; - - return selector.find("a.a-h").map((_, element) => { - return data(element).text().trim(); - }).get(); + const selector = data( + "div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(2) > td.table-value" + ); + + if (selector.length == 0 && selector.find("a.a-h").length == 0) return null; + + return selector + .find("a.a-h") + .map((_, element) => { + return data(element).text().trim(); + }) + .get(); } private GetMangaGenres(data: cheerio.Root): string[] | null { - const selector = data("div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(4) > td.table-value"); - - if (selector.length == 0 && selector.find("a.a-h").length == 0) - return null; - - return selector.find("a.a-h").map((_, element) => { - return data(element).text().trim(); - }).get(); + const selector = data( + "div.panel-story-info > div.story-info-right > table > tbody > tr:nth-child(4) > td.table-value" + ); + + if (selector.length == 0 && selector.find("a.a-h").length == 0) return null; + + return selector + .find("a.a-h") + .map((_, element) => { + return data(element).text().trim(); + }) + .get(); } private isNsfw(genres: string[]) { - return genres.some(genre => genre === "Pornographic" || genre === "Mature" || genre === "Erotica"); + return genres.some( + (genre) => + genre === "Pornographic" || genre === "Mature" || genre === "Erotica" + ); } private GetMangaPages(data: cheerio.Root) { - if (data("div.container-chapter-reader").length == 0 && data("div.container-chapter-reader > img").length == 0) + if ( + data("div.container-chapter-reader").length == 0 && + data("div.container-chapter-reader > img").length == 0 + ) return null; - return data("div.container-chapter-reader > img").map((_, element) => data(element).attr("data-src")).get(); + return data("div.container-chapter-reader > img") + .map((_, element) => data(element).attr("data-src")) + .get(); } private GetMangaSearchResults(data: cheerio.Root): IMangaResult[] | null { const section = data("div.panel-content-genres"); - if (section.length === 0) - return null; - - return section.find("div.content-genres-item").map((_, element) => { - const mangaResultId = data(element).find("a.genres-item-img").attr("href").split("-").at(-1); - const name = data(element).find("a.genres-item-img").attr("title").trim(); - - const mangaInfoResults: IMangaResult = { - id: mangaResultId, - title: name, - url: `/manga/${this.name}/title/${mangaResultId}` - } - - return mangaInfoResults; - }).get(); + if (section.length === 0) return null; + + return section + .find("div.content-genres-item") + .map((_, element) => { + const mangaResultId = data(element) + .find("a.genres-item-img") + .attr("href") + .split("-") + .at(-1); + const name = data(element) + .find("a.genres-item-img") + .attr("title") + .trim(); + + const mangaInfoResults: IMangaResult = { + id: mangaResultId, + name: name, + url: `/manga/${this.name}/name/${mangaResultId}`, + }; + + return mangaInfoResults; + }) + .get(); } - async GetMangaInfo(mangaId: string) { + async GetItemInfo(mangaId: string) { const { data } = await axios.get(`${this.url}/manga/manga-${mangaId}`); const $ = load(data); - const manga = new Manga; + const manga = new MangaMedia(); - const title = $("div.panel-story-info > div.story-info-right > h1").text().trim(); + const title = $("div.panel-story-info > div.story-info-right > h1") + .text() + .trim(); const description = this.GetMangaDescription($); - const thumbnail = this.url + $("div.panel-story-info > div.story-info-left > span.info-image > img").attr("src"); - const altTitle = $("table > tbody > tr:nth-child(1) > td.table-value > h2").text().trim(); + const thumbnail = + this.url + + $( + "div.panel-story-info > div.story-info-left > span.info-image > img" + ).attr("src"); + const altTitle = $("table > tbody > tr:nth-child(1) > td.table-value > h2") + .text() + .trim(); const status = this.GetMangaStatus($); const authors = this.GetMangaAuthors($); const genres = this.GetMangaGenres($); - const chapters = $("div.panel-story-chapter-list").find("ul > li.a-h").map((_, element) => { - const chapter = new MangaChapter; - const url = $(element).find("a.chapter-name").attr("href"); + const chapters = $("div.panel-story-chapter-list") + .find("ul > li.a-h") + .map((_, element) => { + const chapter = new MangaChapter(); + const url = $(element).find("a.chapter-name").attr("href"); - const chapterId = url.substring(url.lastIndexOf("-") + 1); + const chapterId = url.substring(url.lastIndexOf("-") + 1); - chapter.id = Number(chapterId); - chapter.title = $(element).find("a.chapter-name").text().trim(); - chapter.url = `/manga/${this.name}/chapter/${mangaId}?num=${chapterId}`; - chapter.number = Number(chapterId); - chapter.images = null; + chapter.id = Number(chapterId); + chapter.name = $(element).find("a.chapter-name").text().trim(); + chapter.url = `/manga/${this.name}/chapter/${mangaId}?num=${chapterId}`; + chapter.num = Number(chapterId); + chapter.images = null; - return chapter; - }).get(); + return chapter; + }) + .get(); manga.id = mangaId; - manga.url = `/manga/${this.name}/title/${mangaId}`; - manga.title = title; - manga.altTitles = Array.of(altTitle); + manga.url = `/manga/${this.name}/name/${mangaId}`; + manga.name = title; + manga.alt_names = Array.of(altTitle); manga.thumbnail = new Image(thumbnail); - manga.description = description; + manga.synopsis = description; manga.status = status; manga.authors = authors; manga.genres = genres; manga.characters = null; manga.chapters = chapters; manga.volumes = null; - manga.isNSFW = this.isNsfw(genres); + manga.nsfw = this.isNsfw(genres); return manga; } - async Filter(params: IManganatoFilterParams) { + async GetItemByFilter(params: IManganatoFilterParams) { const url = this.manager.url.generate(params); const { data } = await axios.get(url); @@ -145,17 +181,22 @@ export class Manganelo { } async GetMangaChapters(mangaId: string, chapterNumber: number) { - const { data } = await axios.get(`${this.url}/chapter/manga-${mangaId}/chapter-${chapterNumber}`); + const { data } = await axios.get( + `${this.url}/chapter/manga-${mangaId}/chapter-${chapterNumber}` + ); const $ = load(data); const images = this.GetMangaPages($); - const name = $("body > div.body-site > div:nth-child(1) > div.panel-breadcrumb > a").eq(-1).attr("title") || null; - const chapter = new MangaChapter; + const name = + $("body > div.body-site > div:nth-child(1) > div.panel-breadcrumb > a") + .eq(-1) + .attr("title") || null; + const chapter = new MangaChapter(); chapter.id = Number(chapterNumber); - chapter.title = name; + chapter.name = name; chapter.url = `/manga/${this.name}/chapter/${mangaId}?num=${chapterNumber}`; - chapter.number = Number(chapterNumber); + chapter.num = Number(chapterNumber); chapter.images = images; return chapter; diff --git a/src/scraper/sites/manga/manganelo/managers/ManganatoURLManager.ts b/src/scraper/sites/manga/manganelo/managers/ManganatoURLManager.ts index cf1e266a..abccecfa 100644 --- a/src/scraper/sites/manga/manganelo/managers/ManganatoURLManager.ts +++ b/src/scraper/sites/manga/manganelo/managers/ManganatoURLManager.ts @@ -1,6 +1,6 @@ import { URLSearchParams } from "url"; import { - IManganatoFilterParams, + type IManganatoFilterParams, ManganatoFilterURLParams, manganatoGenreList, manganatoOrderByOptions, @@ -43,7 +43,7 @@ export class ManganatoAdvancedSearchURLManager extends ManganatoManager { private processOrderBy(order: unknown) { return typeof order === "string" && manganatoOrderByOptionsList.includes( - order.toLowerCase() as manganatoOrderByOptions, + order.toLowerCase() as manganatoOrderByOptions ) ? order : ""; diff --git a/src/scraper/sites/manga/nhentai/Nhentai.ts b/src/scraper/sites/manga/nhentai/Nhentai.ts index 2901e62e..61496c22 100644 --- a/src/scraper/sites/manga/nhentai/Nhentai.ts +++ b/src/scraper/sites/manga/nhentai/Nhentai.ts @@ -1,7 +1,7 @@ import axios from "axios"; import { load } from "cheerio"; import { getFilterByPages } from "./assets/getFilterByPage"; -import { IMangaChapter, Manga } from "../../../../types/manga"; +import { type IMangaChapter, Manga } from "../../../../types/manga"; export class Nhentai { async filter(mangaName: string) { @@ -47,7 +47,7 @@ class NhentaiMangaInfo { async getMangaInfoById(mangaId: string) { try { const { data } = await axios.get(`https://nhentai.to/g/${mangaId}`); - + const $ = load(data); const manga = new Manga(); @@ -56,7 +56,6 @@ class NhentaiMangaInfo { manga.authors = []; manga.chapters = []; - manga.title = $("div#info h1").text(); manga.thumbnail = { url: $("div#cover a img").attr("src"), @@ -103,9 +102,9 @@ class NhentaiGetMangaChapters { mangaImagesPages.push( $(chapterImage) .attr("data-src") - .replace("cdn.dogehls.xyz", "t7.nhentai.net"), + .replace("cdn.dogehls.xyz", "t7.nhentai.net") ); - }, + } ); mangaChapters.push({ diff --git a/src/scraper/sites/manga/tmomanga/Page.js b/src/scraper/sites/manga/tmomanga/Page.js index ab1adb48..e1b72cb8 100644 --- a/src/scraper/sites/manga/tmomanga/Page.js +++ b/src/scraper/sites/manga/tmomanga/Page.js @@ -1,65 +1,84 @@ import axios from "axios"; import * as cheerio from "cheerio"; -import { Chapter, ChapterView, Manga } from "../../../../utils/manga/schemaProviders.js"; +import { + Chapter, + ChapterView, + Manga, +} from "../../../../utils/manga/schemaProviders.js"; import { Image } from "../../../../utils/schemaProviders.js"; export const TMOManga = { - url: 'https://tmomanga.com' + url: "https://tmomanga.com", }; /** - * - * @param {string} url + * + * @param {string} url * @returns {Chapter} */ async function getChapter(url) { - const $ = cheerio.load((await axios.get(url)).data); - let chapter = new Chapter(); + const $ = cheerio.load((await axios.get(url)).data); + let chapter = new Chapter(); - chapter.title = $('h1#chapter-heading').text().trim(); - chapter.url = url; - chapter.number = parseInt($('li.active').text().trim().split(' ').pop()); - chapter.images = $('div#images_chapter img').map((i, el) => { - if (i === 0) - chapter.cover = $(el).attr('data-src'); - return $(el).attr('data-src'); - }).toArray(); - return chapter; + chapter.title = $("h1#chapter-heading").text().trim(); + chapter.url = url; + chapter.number = parseInt($("li.active").text().trim().split(" ").pop()); + chapter.images = $("div#images_chapter img") + .map((i, el) => { + if (i === 0) chapter.cover = $(el).attr("data-src"); + return $(el).attr("data-src"); + }) + .toArray(); + return chapter; } /** - * - * @param {string} url + * + * @param {string} url * @returns {Manga} */ async function getManga(url) { - const $ = cheerio.load((await axios.get(url)).data); - const data = $('div.post-content-data div.post-content_item'); + const $ = cheerio.load((await axios.get(url)).data); + const data = $("div.post-content-data div.post-content_item"); - let manga = new Manga(); - manga.title = $('div.post-title h1').text(); - manga.url = url; - manga.image = new Image($('div.summary_image img').attr('src'), null); - manga.synopsis = $('div.description-summary p').text().trim(); - manga.year = $(data.get(0)).find('div.summary-content').text().trim(); - manga.genres = $(data.get(1)).find('a').map((i, el) => { return $(el).text().trim() }).toArray(); - manga.chapters = $('ul.sub-chap li').children().map((i, el) => { return $(el).attr('href') }).toArray(); - return manga; + let manga = new Manga(); + manga.title = $("div.post-title h1").text(); + manga.url = url; + manga.image = new Image($("div.summary_image img").attr("src"), null); + manga.synopsis = $("div.description-summary p").text().trim(); + manga.year = $(data.get(0)).find("div.summary-content").text().trim(); + manga.genres = $(data.get(1)) + .find("a") + .map((i, el) => { + return $(el).text().trim(); + }) + .toArray(); + manga.chapters = $("ul.sub-chap li") + .children() + .map((i, el) => { + return $(el).attr("href"); + }) + .toArray(); + return manga; } /** - * - * @param {*} element + * + * @param {*} element * @returns {ChapterView} */ function getChapterView(element) { - let view = new ChapterView(); - view.title = element.find('span.manga-title-updated').text().trim() + ' - ' + - element.find('span.manga-episode-title').text().trim(); - view.url = element.find('a').attr('href'); - view.image = element.find('img').attr('src'); - view.manga = view.url.substring(0, view.url.lastIndexOf('-')).replace('capitulo', 'manga'); - return view; + let view = new ChapterView(); + view.title = + element.find("span.manga-title-updated").text().trim() + + " - " + + element.find("span.manga-episode-title").text().trim(); + view.url = element.find("a").attr("href"); + view.image = element.find("img").attr("src"); + view.manga = view.url + .substring(0, view.url.lastIndexOf("-")) + .replace("capitulo", "manga"); + return view; } /** @@ -67,43 +86,47 @@ function getChapterView(element) { * @returns {(Manga[] | ChapterView[])} */ async function getSectionContent(number) { - try { - let content = []; - const $ = cheerio.load((await axios.get(TMOManga.url)).data); - const elements = $($('div.main-col-inner').get(number)).find('div.row').children(); - for (let i = 0; i < elements.length; i++) { - content.push(number == 1 ? await getManga($(elements[i]).find('h3 a').attr('href')) : - getChapterView($(elements[i]))); - } - return content; - } catch (error) { - console.log(error); + try { + let content = []; + const $ = cheerio.load((await axios.get(TMOManga.url)).data); + const elements = $($("div.main-col-inner").get(number)) + .find("div.row") + .children(); + for (let i = 0; i < elements.length; i++) { + content.push( + number == 1 + ? await getManga($(elements[i]).find("h3 a").attr("href")) + : getChapterView($(elements[i])), + ); } - return []; + return content; + } catch (error) { + console.log(error); + } + return []; } /** - * + * * @returns {Manga[]} */ async function getLastMangas() { - return await getSectionContent(1); + return await getSectionContent(1); } /** - * + * * @returns {ChapterView[]} */ async function getLastChapters() { - return await getSectionContent(0); + return await getSectionContent(0); } //console.log(await getChapter('https://tmomanga.com/capitulo/soredemo-ayumu-wa-yosetekuru-187.00')); -export default -{ - getLastMangas, - getLastChapters, - getManga, - getChapter -} \ No newline at end of file +export default { + getLastMangas, + getLastChapters, + getManga, + getChapter, +}; diff --git a/src/scraper/sites/manga/tmomanga/filter.js b/src/scraper/sites/manga/tmomanga/filter.js index fae4f6c7..9d82a4a1 100644 --- a/src/scraper/sites/manga/tmomanga/filter.js +++ b/src/scraper/sites/manga/tmomanga/filter.js @@ -3,39 +3,44 @@ import * as cheerio from "cheerio"; import utils from "../../../../utils/utilities.js"; /** - * - * @param {string} name + * + * @param {string} name * @param {string} genre * @return */ function getSearchURL(name, genre) { - if (utils.isUsableValue(genre)) - url = `https://tmomanga.com/genero/${genre.toLowerCase().replace(' ', '-')}`; - else if (utils.isUsableValue(name)) - url = `https://tmomanga.com/biblioteca?search=${encodeURI(name).split('%20').join('+')}`; - return null; + if (utils.isUsableValue(genre)) + url = `https://tmomanga.com/genero/${genre + .toLowerCase() + .replace(" ", "-")}`; + else if (utils.isUsableValue(name)) + url = `https://tmomanga.com/biblioteca?search=${encodeURI(name) + .split("%20") + .join("+")}`; + return null; } /** - * - * @param {string} name - * @param {string} genre - * @param {number} page + * + * @param {string} name + * @param {string} genre + * @param {number} page */ export async function filter(name, genre, page) { - try { - let search_url = getSearchURL(name, genre); - if (search_url != null) { - let mangas = []; - if (utils.isUsableValue(page)) - search_url += `?page=${page}`; - const $ = cheerio.load((await axios.get(search_url)).data); - const elements = $($('div.main-col-inner').get(0)).find('div.row').children(); - for (let i = 0; i < elements.length; i++) - mangas.push($(elements[i]).find('h3 a').attr('href')); - return mangas; - } - } catch (error) { - console.log(error); + try { + let search_url = getSearchURL(name, genre); + if (search_url != null) { + let mangas = []; + if (utils.isUsableValue(page)) search_url += `?page=${page}`; + const $ = cheerio.load((await axios.get(search_url)).data); + const elements = $($("div.main-col-inner").get(0)) + .find("div.row") + .children(); + for (let i = 0; i < elements.length; i++) + mangas.push($(elements[i]).find("h3 a").attr("href")); + return mangas; } + } catch (error) { + console.log(error); + } } diff --git a/src/scraper/sites/news/kudasai/kudasai.js b/src/scraper/sites/news/kudasai/kudasai.js index 79d0ecda..f18cc1b3 100644 --- a/src/scraper/sites/news/kudasai/kudasai.js +++ b/src/scraper/sites/news/kudasai/kudasai.js @@ -2,71 +2,71 @@ import { load } from "cheerio"; import axios from "axios"; import { - Post, - NewsShema, - NewsInfo, + Post, + NewsShema, + NewsInfo, } from "../../../../utils/shemaNewsProviders.js"; //url const kudasaiUrl = { - main: "https://somoskudasai.com/", - posts: "https://somoskudasai.com/noticias", + main: "https://somoskudasai.com/", + posts: "https://somoskudasai.com/noticias", }; //get post async function PostsNews() { - try { - const { data } = await axios.get(kudasaiUrl.posts); - const $ = load(data); - const ShemaDataArray = new NewsShema(); + try { + const { data } = await axios.get(kudasaiUrl.posts); + const $ = load(data); + const ShemaDataArray = new NewsShema(); - //get data post - $("main section div.nwslst article").each((i, e) => { - const cards = new Post(); - cards.title = $(e).find("h2").text().trim(); - cards.topics.push($(e).find("header > span").text().trim().split(" / ")); - cards.image = $("img.attachment-post-thumbnail").attr("src"); - cards.date = $(e).find("header div.ar-mt > span.db").text().trim(); - cards.url = $(e) - .find("a") - .attr("href") - .replace("https://somoskudasai.com/noticias/", "/news/kudasai/"); - ShemaDataArray.data.push(cards); - }); + //get data post + $("main section div.nwslst article").each((i, e) => { + const cards = new Post(); + cards.title = $(e).find("h2").text().trim(); + cards.topics.push($(e).find("header > span").text().trim().split(" / ")); + cards.image = $("img.attachment-post-thumbnail").attr("src"); + cards.date = $(e).find("header div.ar-mt > span.db").text().trim(); + cards.url = $(e) + .find("a") + .attr("href") + .replace("https://somoskudasai.com/noticias/", "/news/kudasai/"); + ShemaDataArray.data.push(cards); + }); - return ShemaDataArray; - } catch (error) { - return error; - } + return ShemaDataArray; + } catch (error) { + return error; + } } //get new info async function New(param) { - try { - const { data } = await axios.get(`${kudasaiUrl.posts}/${param}`); - const $ = load(data); - const ShemaDataArray = new NewsShema(); + try { + const { data } = await axios.get(`${kudasaiUrl.posts}/${param}`); + const $ = load(data); + const ShemaDataArray = new NewsShema(); - //get general info - $("section.single article").each((i, e) => { - const news = new NewsInfo(); - news.title = $(e).find("h1").text().trim(); - news.topics.push($(e).find("span.typ").text().trim().split(" / ")); - news.banner = $(e).find("img").attr("src"); - news.uploadedBy = $(e).find('div.ar-mt span.fwb').text().trim(); - news.uploadedAt = $(e).find('div.ar-mt span.op5').text().trim(); + //get general info + $("section.single article").each((i, e) => { + const news = new NewsInfo(); + news.title = $(e).find("h1").text().trim(); + news.topics.push($(e).find("span.typ").text().trim().split(" / ")); + news.banner = $(e).find("img").attr("src"); + news.uploadedBy = $(e).find("div.ar-mt span.fwb").text().trim(); + news.uploadedAt = $(e).find("div.ar-mt span.op5").text().trim(); - //get info - $("main section div.entry").each((i, e) => { - news.preview.full = $(e).find("p").text().trim(); - news.preview.images.push($(e).find('img').attr('src')); - }); - ShemaDataArray.data.push(news); - }); + //get info + $("main section div.entry").each((i, e) => { + news.preview.full = $(e).find("p").text().trim(); + news.preview.images.push($(e).find("img").attr("src")); + }); + ShemaDataArray.data.push(news); + }); - return ShemaDataArray; - } catch (error) { - return error - } + return ShemaDataArray; + } catch (error) { + return error; + } } /* New( @@ -74,4 +74,4 @@ async function New(param) { ).then((f) => { console.log(f); }); */ -export default {PostsNews, New}; +export default { PostsNews, New }; diff --git a/src/test/Animeflv.spec.ts b/src/test/Animeflv.spec.ts index d75b892a..d3fea662 100644 --- a/src/test/Animeflv.spec.ts +++ b/src/test/Animeflv.spec.ts @@ -1,26 +1,72 @@ -import {AnimeFlv} from '../scraper/sites/anime/animeflv/AnimeFlv'; -import { StatusAnimeflv, Genres } from '../scraper/sites/anime/animeflv/animeflv_helper'; -describe('AnimeFlv', () => { - let animeFlv: AnimeFlv; - - beforeEach(() => { - animeFlv = new AnimeFlv(); - }); - - it('should get anime info successfully', async () => { - const animeInfo = await animeFlv.GetAnimeInfo('wonder-egg-priority'); - expect(animeInfo.name).toBe('Wonder Egg Priority'); - expect(animeInfo.alt_name).toContain('ワンダーエッグ・プライオリティ'); - expect(animeInfo.image.url).toContain('.jpg'); - expect(animeInfo.status).toBe('En emision'); - expect(animeInfo.synopsis.length).toBeGreaterThan(0); - expect(animeInfo.chronology?.length).toBeGreaterThan(0); - expect(animeInfo.genres.length).toBeGreaterThan(0); - expect(animeInfo.episodes.length).toBeGreaterThan(0); - }); - - it('should filter anime successfully', async () => { - const result = await animeFlv.Filter(Genres.Action, 'all', 'all', StatusAnimeflv.OnGoing, 1, 1); - expect(result.results.length).toBeGreaterThan(0); - }); - }); \ No newline at end of file +import { AnimeFlv } from "../scraper/sites/anime/animeflv/AnimeFlv"; +import { AnimeMedia } from "../types/anime"; +/* import { Episode } from "../types/episode"; +import { + Genres, + StatusAnimeflv, +} from "../scraper/sites/anime/animeflv/animeflv_helper"; */ +import { + Genres, + StatusAnimeflv, +} from "../scraper/sites/anime/animeflv/animeflv_helper"; + +describe("AnimeFlv test", () => { + let animeFlv: AnimeFlv; + + beforeEach(() => { + animeFlv = new AnimeFlv(); + }); + + test("should get anime info successfully", async () => { + const animeInfo: AnimeMedia = await animeFlv.GetItemInfo("horimiya-piece"); + + const alt_names_expected: string[] = ["Horimiya: Piece"]; + const genres_expected: string[] = ["Escolares", "Romance", "Shounen"]; + + expect(animeInfo.name).toBe("Horimiya: Piece"); + expect(animeInfo.alt_names).toEqual( + expect.arrayContaining(alt_names_expected) + ); + expect(animeInfo.image.url).toContain(".jp"); + expect(animeInfo.synopsis).toBe( + "Historias del manga no adaptadas en el anime principal." + ); + expect(animeInfo.chronology?.length).toBeGreaterThanOrEqual(0); + expect(animeInfo.genres).toEqual(expect.arrayContaining(genres_expected)); + expect(animeInfo.episodes?.length).toBeGreaterThanOrEqual(12); + }); + + it("should filter anime successfully", async () => { + const result = await animeFlv.GetItemByFilter( + Genres.Action, + "all", + "all", + StatusAnimeflv.OnGoing, + 1, + 1 + ); + expect(result.results.length).toBeGreaterThan(0); + }); + + /* it("should filter anime successfully", async () => { + const result = await animeFlv.GetItemByFilter( + Genres.Action, + "all", + "all", + StatusAnimeflv.OnGoing, + 1, + 1 + ); + expect(result.results.length).toBeGreaterThan(0); + }); + + it("should get episode servers successfully", async () => { + const episode: Episode = await animeFlv.GetEpisodeServers( + "wonder-egg-priority-01" + ); + expect(episode.name).toBeTruthy(); + expect(episode.url).toContain("/anime/flv/episode/wonder-egg-priority-01"); + expect(episode.num).toBe(1); + expect(episode?.servers?.length).toBeGreaterThan(0); + }); */ +}); diff --git a/src/test/Animelatinohd.spec.ts b/src/test/Animelatinohd.spec.ts index fda6ae6b..9776efc1 100644 --- a/src/test/Animelatinohd.spec.ts +++ b/src/test/Animelatinohd.spec.ts @@ -8,18 +8,18 @@ describe("AnimeLatinohd", () => { }); it("should get anime info successfully", async () => { - const animeInfo = await animelatinohd.GetAnimeInfo("wonder-egg-priority"); + const animeInfo = await animelatinohd.GetItemInfo("wonder-egg-priority"); expect(animeInfo.name).toBe("Wonder Egg Priority"); - expect(animeInfo.alt_name).toContain("ワンダーエッグ・プライオリティ"); + expect(animeInfo.alt_names).toContain("ワンダーエッグ・プライオリティ"); expect(animeInfo.image.url).toContain(".jpg"); expect(animeInfo.status).toBe("Finalizado"); - expect(animeInfo.synopsis.length).toBeGreaterThan(0); - expect(animeInfo.genres.length).toBeGreaterThan(0); - expect(animeInfo.episodes.length).toBeGreaterThan(0); + expect(animeInfo.synopsis?.length).toBeGreaterThan(0); + expect(animeInfo.genres?.length).toBeGreaterThan(0); + expect(animeInfo.episodes?.length).toBeGreaterThan(0); }); it("should filter anime successfully", async () => { - const result = await animelatinohd.GetAnimeByFilter(); + const result = await animelatinohd.GetItemByFilter(); expect(result.results.length).toBeGreaterThan(0); }, 10000); -}); \ No newline at end of file +}); diff --git a/src/test/Comick.spec.ts b/src/test/Comick.spec.ts index 559ef27e..b4ac97ea 100644 --- a/src/test/Comick.spec.ts +++ b/src/test/Comick.spec.ts @@ -9,15 +9,14 @@ describe("Comick", () => { it("should get anime info successfully", async () => { const mangaInfo = await comick.GetMangaInfo("00-solo-leveling", "en"); - - expect(mangaInfo.title).toBe("Solo Leveling"); - expect(mangaInfo.altTitles).toContain("我独自升级"); - expect(mangaInfo.status).toBe("completed"); + expect(mangaInfo.name).toBe("Solo Leveling"); + expect(mangaInfo.alt_names).toContain("我独自升级"); + expect(mangaInfo.status).toBe("completed"); }); it("should filter anime successfully", async () => { const result = await comick.GetMangaByFilter(); expect(result.results.length).toBeGreaterThan(0); }, 10000); -}); \ No newline at end of file +}); diff --git a/src/test/HentaiHaven.spec.ts b/src/test/HentaiHaven.spec.ts new file mode 100644 index 00000000..59c7d848 --- /dev/null +++ b/src/test/HentaiHaven.spec.ts @@ -0,0 +1,23 @@ +import { HentaiHaven } from "../scraper/sites/anime/hentaihaven/HentaiHaven"; + +describe("HentaiHaven", () => { + let hentaihaven: HentaiHaven; + + beforeEach(() => { + hentaihaven = new HentaiHaven(); + }); + + it("should get anime info successfully", async () => { + const animeInfo = await hentaihaven.GetItemInfo("tokubetsu-jugyou-2"); + expect(animeInfo.name).toBe("Tokubetsu Jugyou 2"); + expect(animeInfo.image.url).toContain(".jpg"); + expect(animeInfo.synopsis?.length).toBeGreaterThan(0); + expect(animeInfo.genres?.length).toBeGreaterThan(0); + expect(animeInfo.episodes?.length).toBeGreaterThan(0); + }); + + it("should filter anime successfully", async () => { + const result = await hentaihaven.GetItemByFilter("animation"); + expect(result.results.length).toBeGreaterThan(0); + }, 10000); +}); diff --git a/src/test/Hentaila.spec.ts b/src/test/Hentaila.spec.ts new file mode 100644 index 00000000..6b3749b1 --- /dev/null +++ b/src/test/Hentaila.spec.ts @@ -0,0 +1,24 @@ +import { HentaiLa } from "../scraper/sites/anime/hentaila/HentaiLa"; + +describe("HentaiLa", () => { + let hentaila: HentaiLa; + + beforeEach(() => { + hentaila = new HentaiLa(); + }); + + it("should get anime info successfully", async () => { + const animeInfo = await hentaila.GetItemInfo("hentai-korashime-2"); + expect(animeInfo.name).toBe("Korashime 2"); + expect(animeInfo.image.url).toContain(".jpg"); + expect(animeInfo.status).toBe("Finalizado"); + expect(animeInfo.synopsis?.length).toBeGreaterThan(0); + expect(animeInfo.genres?.length).toBeGreaterThan(0); + expect(animeInfo.episodes?.length).toBeGreaterThan(0); + }); + + it("should filter anime successfully", async () => { + const result = await hentaila.GetItemByFilter("na"); + expect(result.results.length).toBeGreaterThan(0); + }, 10000); +}); diff --git a/src/test/Inmanga.spec.ts b/src/test/Inmanga.spec.ts index c3b80172..ff3fc84d 100644 --- a/src/test/Inmanga.spec.ts +++ b/src/test/Inmanga.spec.ts @@ -8,17 +8,16 @@ describe("Inmanga", () => { }); it("should get anime info successfully", async () => { - const mangaInfo = await inmanga.GetMangaInfo("Kimetsu-no-Yaiba"); - - expect(mangaInfo.title).toBe("Kimetsu no Yaiba"); - expect(mangaInfo.altTitles).toContain("Blade of Demon Destruction"); + const mangaInfo = await inmanga.GetMangaInfo("Kimetsu-no-Yaiba","78352626-0e2c-4b10-9610-28abf57c6881"); - expect(mangaInfo.status).toBe("ongoing"); + expect(mangaInfo.name).toBe("Kimetsu no Yaiba"); + expect(mangaInfo.alt_names).toContain("Blade of Demon Destruction"); + expect(mangaInfo.status).toBe("ongoing"); }); it("should filter anime successfully", async () => { const result = await inmanga.GetMangaByFilter(); expect(result.results.length).toBeGreaterThan(0); }, 10000); -}); \ No newline at end of file +}); diff --git a/src/test/MangaReader.spec.ts b/src/test/MangaReader.spec.ts index 93ce63b9..d5f7ca9f 100644 --- a/src/test/MangaReader.spec.ts +++ b/src/test/MangaReader.spec.ts @@ -6,7 +6,7 @@ import { MangaReaderFilterScore, MangaReaderFilterStatus, MangaReaderFilterLanguage, - MangaReaderFilterRatingType + MangaReaderFilterRatingType, } from "../scraper/sites/manga/MangaReader/MangaReaderTypes"; describe("MangaReader", () => { @@ -18,7 +18,7 @@ describe("MangaReader", () => { it("should return manga info successfully", async () => { const testsList: Array<{ - id: number; + id: string; mangaName: string; altName: string[]; mangaGenres: string[]; @@ -27,60 +27,60 @@ describe("MangaReader", () => { hasVolumes: boolean; hasChapters: boolean; }> = [ - { - id: 65961, - mangaName: "Zashisu", - altName: ["ザシス"], - mangaGenres: ["Horror", "Mystery", "Psychological", "School", "Seinen"], - isNsfw: false, - status: "ongoing", - hasVolumes: false, - hasChapters: true - }, - { - id: 65941, - mangaName: "Mitsuba no Monogatari", - altName: ["みつばものがたり 呪いの少女と死の輪舞《ロンド》"], - mangaGenres: ["Fantasy"], - isNsfw: false, - status: "ongoing", - hasVolumes: false, - hasChapters: true - }, - { - id: 65795, - mangaName: - "Akuyaku Reijou ni Tensei suru no Mahou ni Muchuu de Itara Ouji ni Dekiaisaremashita", - altName: ["悪役令嬢に転生するも魔法に夢中でいたら王子に溺愛されました"], - mangaGenres: ["Fantasy", "Romance", "School", "Shoujo"], - isNsfw: false, - status: "ongoing", - hasVolumes: false, - hasChapters: true - }, - { - id: 65879, - mangaName: "My Star Is the Lewdest", - altName: ["俺の女優が一番淫ら"], - mangaGenres: ["Comedy", "Ecchi"], - isNsfw: true, - status: "ongoing", - hasVolumes: false, - hasChapters: true - }, - { - id: 65789, - mangaName: "Hoop Days", - altName: ["ディアボーイズ"], - mangaGenres: ["Drama", "Slice of Life", "Sports"], - isNsfw: false, - status: "completed", - hasVolumes: false, - hasChapters: true - } - ]; + { + id: "65961", + mangaName: "Zashisu", + altName: ["ザシス"], + mangaGenres: ["Horror", "Mystery", "Psychological", "School", "Seinen"], + isNsfw: false, + status: "ongoing", + hasVolumes: false, + hasChapters: true, + }, + { + id: "65941", + mangaName: "Mitsuba no Monogatari", + altName: ["みつばものがたり 呪いの少女と死の輪舞《ロンド》"], + mangaGenres: ["Fantasy"], + isNsfw: false, + status: "ongoing", + hasVolumes: false, + hasChapters: true, + }, + { + id: "65795", + mangaName: + "Akuyaku Reijou ni Tensei suru no Mahou ni Muchuu de Itara Ouji ni Dekiaisaremashita", + altName: ["悪役令嬢に転生するも魔法に夢中でいたら王子に溺愛されました"], + mangaGenres: ["Fantasy", "Romance", "School", "Shoujo"], + isNsfw: false, + status: "ongoing", + hasVolumes: false, + hasChapters: true, + }, + { + id: "65879", + mangaName: "My Star Is the Lewdest", + altName: ["俺の女優が一番淫ら"], + mangaGenres: ["Comedy", "Ecchi"], + isNsfw: true, + status: "ongoing", + hasVolumes: false, + hasChapters: true, + }, + { + id: "65789", + mangaName: "Hoop Days", + altName: ["ディアボーイズ"], + mangaGenres: ["Drama", "Slice of Life", "Sports"], + isNsfw: false, + status: "completed", + hasVolumes: false, + hasChapters: true, + }, + ]; - testsList.forEach(async fields => { + testsList.forEach(async (fields) => { const { id, mangaName, @@ -89,13 +89,13 @@ describe("MangaReader", () => { isNsfw, status, hasVolumes, - hasChapters + hasChapters, } = fields; - const mangaInfo = await mangareader.GetMangaInfo(id); + const mangaInfo = await mangareader.GetItemInfo(id); - expect(mangaInfo.title).toStrictEqual(mangaName); - expect(mangaInfo.altTitles).toStrictEqual(altName); - expect(mangaInfo.isNSFW).toStrictEqual(isNsfw); + expect(mangaInfo.name).toStrictEqual(mangaName); + expect(mangaInfo.alt_names).toStrictEqual(altName); + expect(mangaInfo.nsfw).toStrictEqual(isNsfw); expect(mangaInfo.genres).toStrictEqual(mangaGenres); expect(mangaInfo.status).toStrictEqual(status); @@ -116,7 +116,7 @@ describe("MangaReader", () => { status?: MangaReaderFilterStatus; ratingType?: MangaReaderFilterRatingType; score?: MangaReaderFilterScore; - language?: typeof MangaReaderFilterLanguage[number]; + language?: (typeof MangaReaderFilterLanguage)[number]; startYear?: number; startMonth?: number; startDay?: number; @@ -126,63 +126,63 @@ describe("MangaReader", () => { sort?: MangaReaderFilterSort; numPage?: number; }> = [ - { - hasResults: true, - type: MangaReaderFilterType.Manhwa, - status: MangaReaderFilterStatus.Finished, - ratingType: MangaReaderFilterRatingType.MildNudity, - numPage: 1 - }, - { - hasResults: true, - type: MangaReaderFilterType.Doujinshi, - status: MangaReaderFilterStatus.All, - ratingType: MangaReaderFilterRatingType.Teens, - score: MangaReaderFilterScore.Horrible, - language: "ja" - }, - { - hasResults: true, - type: MangaReaderFilterType.Manga, - status: MangaReaderFilterStatus.Finished, - ratingType: MangaReaderFilterRatingType.Teens, - score: MangaReaderFilterScore.All, - language: "ja", - startYear: 2021, - startMonth: 3, - startDay: 5, - endYear: 2023, - endMonth: 3, - endDay: 6 - }, - { - hasResults: true, - type: MangaReaderFilterType.OneShot, - status: MangaReaderFilterStatus.All, - ratingType: MangaReaderFilterRatingType.Teens, - numPage: 2 - }, - { - hasResults: false, - type: MangaReaderFilterType.LightNovel, - status: MangaReaderFilterStatus.Finished, - ratingType: MangaReaderFilterRatingType.Children, - score: MangaReaderFilterScore.All, - language: "en", - numPage: 3 - }, - { - hasResults: false, - type: MangaReaderFilterType.OneShot, - status: MangaReaderFilterStatus.All, - ratingType: MangaReaderFilterRatingType.Teens, - score: MangaReaderFilterScore.VeryGood, - language: "en", - numPage: 1 - } - ]; + { + hasResults: true, + type: MangaReaderFilterType.Manhwa, + status: MangaReaderFilterStatus.Finished, + ratingType: MangaReaderFilterRatingType.MildNudity, + numPage: 1, + }, + { + hasResults: true, + type: MangaReaderFilterType.Doujinshi, + status: MangaReaderFilterStatus.All, + ratingType: MangaReaderFilterRatingType.Teens, + score: MangaReaderFilterScore.Horrible, + language: "ja", + }, + { + hasResults: true, + type: MangaReaderFilterType.Manga, + status: MangaReaderFilterStatus.Finished, + ratingType: MangaReaderFilterRatingType.Teens, + score: MangaReaderFilterScore.All, + language: "ja", + startYear: 2021, + startMonth: 3, + startDay: 5, + endYear: 2023, + endMonth: 3, + endDay: 6, + }, + { + hasResults: true, + type: MangaReaderFilterType.OneShot, + status: MangaReaderFilterStatus.All, + ratingType: MangaReaderFilterRatingType.Teens, + numPage: 2, + }, + { + hasResults: false, + type: MangaReaderFilterType.LightNovel, + status: MangaReaderFilterStatus.Finished, + ratingType: MangaReaderFilterRatingType.Children, + score: MangaReaderFilterScore.All, + language: "en", + numPage: 3, + }, + { + hasResults: false, + type: MangaReaderFilterType.OneShot, + status: MangaReaderFilterStatus.All, + ratingType: MangaReaderFilterRatingType.Teens, + score: MangaReaderFilterScore.VeryGood, + language: "en", + numPage: 1, + }, + ]; - testsList.forEach(async fields => { + testsList.forEach(async (fields) => { const { hasResults, type, @@ -197,10 +197,10 @@ describe("MangaReader", () => { endMonth, endDay, sort, - numPage + numPage, } = fields; - const filter = await mangareader.Filter({ + const filter = await mangareader.GetItemByFilter({ type: type, status: status, ratingType: ratingType, @@ -213,7 +213,7 @@ describe("MangaReader", () => { endMonth: endMonth, endDay: endDay, sort: sort, - numPage: numPage + numPage: numPage, }); if (hasResults === true) @@ -225,40 +225,40 @@ describe("MangaReader", () => { it("should return manga chapter pages successfully", async () => { const testsList: Array<{ chapterTitle: string; - id: number; + id: string; chapterNumber: number; - language: typeof MangaReaderFilterLanguage[number]; + language: (typeof MangaReaderFilterLanguage)[number]; type: MangaReaderChapterType; }> = [ - { - chapterTitle: "Chapter 3: 第 3 話", - id: 65953, - chapterNumber: 3, - language: "ja", - type: "chapter" - }, - { - chapterTitle: "VOL 2", - id: 65781, - chapterNumber: 2, - language: "en", - type: "volume" - } - ]; + { + chapterTitle: "Chapter 3: 第 3 話", + id: "65953", + chapterNumber: 3, + language: "ja", + type: "chapter", + }, + { + chapterTitle: "VOL 2", + id: "65781", + chapterNumber: 2, + language: "en", + type: "volume", + }, + ]; - testsList.forEach(async fields => { + testsList.forEach(async (fields) => { const { chapterTitle, id, chapterNumber, language, type } = fields; const mangaChapters = await mangareader.GetMangaChapters( id, chapterNumber, language, - type + type, ); expect(mangaChapters?.images.length).toBeGreaterThanOrEqual(1); - expect(mangaChapters?.title).toStrictEqual(chapterTitle); + expect(mangaChapters?.name).toStrictEqual(chapterTitle); expect(mangaChapters?.id).toStrictEqual(id); - expect(mangaChapters?.number).toStrictEqual(chapterNumber); + expect(mangaChapters?.num).toStrictEqual(chapterNumber); }); }, 5000); }); diff --git a/src/test/Manganelo.spec.ts b/src/test/Manganelo.spec.ts index 6353a9f6..789d6dc3 100644 --- a/src/test/Manganelo.spec.ts +++ b/src/test/Manganelo.spec.ts @@ -1,5 +1,8 @@ -import { Manganelo } from '../scraper/sites/manga/manganelo/Manganelo'; -import { IManganatoFilterParams, manganatoGenreList } from '../scraper/sites/manga/manganelo/ManganatoTypes'; +import { Manganelo } from "../scraper/sites/manga/manganelo/Manganelo"; +import { + IManganatoFilterParams, + manganatoGenreList, +} from "../scraper/sites/manga/manganelo/ManganatoTypes"; type ManganeloTestTemplate = { id: string; @@ -10,63 +13,63 @@ type ManganeloTestTemplate = { type ManganeloGenresOptions = keyof typeof manganatoGenreList; -interface ManganeloFilterTestTemplate extends Omit { +interface ManganeloFilterTestTemplate + extends Omit { genres: ManganeloGenresOptions[]; -}; +} type ManganeloChapterTestTemplate = { id: string; num: number; }; -describe('Manganelo', () => { +describe("Manganelo", () => { let manganelo: Manganelo; beforeEach(() => { manganelo = new Manganelo(); }); - it('should get manga info successfully', async () => { - + it("should get manga info successfully", async () => { const testsSuites: ManganeloTestTemplate[] = [ { - id: 'md990312', + id: "md990312", nsfw: false, - status: 'ongoing', - title: 'Your Eternal Lies' + status: "ongoing", + title: "Your Eternal Lies", }, { - id: 'he984887', + id: "he984887", nsfw: false, - status: 'ongoing', - title: 'The Peerless Sword God' + status: "ongoing", + title: "The Peerless Sword God", }, { - id: 'go983949', + id: "go983949", nsfw: false, - status: 'ongoing', - title: 'Bite Into Me' + status: "ongoing", + title: "Bite Into Me", }, { - id: 'oj992266', + id: "oj992266", nsfw: true, - status: 'ongoing', - title: 'Dekiai Osananajimi Ha Watashi No Otto De Stalker!?' - } + status: "ongoing", + title: "Dekiai Osananajimi Ha Watashi No Otto De Stalker!?", + }, ]; testsSuites.forEach(async (options) => { - const mangaInfo = await manganelo.GetMangaInfo(options.id); - expect(mangaInfo.title).toStrictEqual(options.title); + const mangaInfo = await manganelo.GetItemInfo(options.id); + expect(mangaInfo.name).toStrictEqual(options.title); - if (mangaInfo.altTitles) - expect(mangaInfo.altTitles.length).toBeGreaterThanOrEqual(1); + if (mangaInfo.alt_names) + expect(mangaInfo.alt_names.length).toBeGreaterThanOrEqual(1); if (mangaInfo.thumbnail && mangaInfo.thumbnail.url) - expect(mangaInfo.thumbnail.url).toContain('.jpg'); + expect(mangaInfo.thumbnail.url).toContain(".jpg"); expect(mangaInfo.status).toStrictEqual(options.status); - expect(mangaInfo.isNSFW).toStrictEqual(options.nsfw); + expect(mangaInfo.nsfw).toStrictEqual(options.nsfw); if (mangaInfo.genres) expect(mangaInfo.genres.length).toBeGreaterThanOrEqual(1); @@ -76,52 +79,52 @@ describe('Manganelo', () => { }); }); - it('should filter manga successfully', async () => { + it("should filter manga successfully", async () => { const filterTestsSuites: ManganeloFilterTestTemplate[] = [ { - genres: ['action'], - orby: 'az', + genres: ["action"], + orby: "az", page: 3, - sts: 'completed' + sts: "completed", }, { - genres: ['drama', 'romance'], - orby: 'newest', + genres: ["drama", "romance"], + orby: "newest", page: 1, - sts: 'ongoing' - } + sts: "ongoing", + }, ]; filterTestsSuites.forEach(async (options) => { - const result = await manganelo.Filter({ - genres: options.genres.join(' '), + const result = await manganelo.GetItemByFilter({ + genres: options.genres.join(" "), orby: options.orby, page: options.page, - sts: options.sts + sts: options.sts, }); expect(result.results.length).toBeGreaterThanOrEqual(1); }); }); - it('should return manga chapters successfully', async () => { + it("should return manga chapters successfully", async () => { const chapterTestsSuites: ManganeloChapterTestTemplate[] = [ { - id: 'he984887', - num: 221 + id: "he984887", + num: 221, }, { - id: 'oj992266', - num: 1 + id: "oj992266", + num: 1, }, { - id: 'md990312', - num: 79 + id: "md990312", + num: 79, }, { - id: 'go983949', - num: 2 - } + id: "go983949", + num: 2, + }, ]; chapterTestsSuites.forEach(async (options) => { diff --git a/src/test/Monoschinos.spec.ts b/src/test/Monoschinos.spec.ts new file mode 100644 index 00000000..70ae8274 --- /dev/null +++ b/src/test/Monoschinos.spec.ts @@ -0,0 +1,57 @@ +import { Monoschinos } from "../scraper/sites/anime/monoschinos/Monoschinos"; + +describe('Monoschinos', () => { + let monos: Monoschinos; + + beforeEach(() => { + monos = new Monoschinos(); + }); + it("should get anime info successfully", async () => { + const animeInfo = await monos.getAnime( + 'https://monoschinos2.com/anime/one-room-hiatari-futsuu-tenshi-tsuki-sub-espanol', + ); + + expect(animeInfo.name).toBe('One Room, Hiatari Futsuu, Tenshi-tsuki.'); + expect(animeInfo.image.url).toContain('.jpg'); + expect(animeInfo.synopsis?.length).toBeGreaterThan(0); + // The chronology function does not exist in Monoschinos + //expect(animeInfo.chronology?.length).toBeGreaterThan(0); + expect(animeInfo.genres?.length).toBeGreaterThan(0); + expect(animeInfo.episodes?.length).toBeGreaterThan(0); + expect((await monos.getLastAnimes()).length).toBeGreaterThan(0); + expect((await monos.getLastEpisodes()).length).toBeGreaterThan(0); + expect((await monos.getEpisodeServers('https://monoschinos2.com/ver/one-room-hiatari-futsuu-tenshi-tsuki-episodio-1')).length).toBeGreaterThan(0); + }); +}); + + +/*async function test() { + let monos: Monoschinos; + monos = new Monoschinos(); + + const animeInfo = await monos.getAnime( + "https://monoschinos2.com/anime/date-a-live-i-sub-espanol", + ); + + console.log("> Name: ") + console.log(animeInfo.name) + console.log("> Image url: ") + console.log(animeInfo.image.url) + console.log("> Synopsis length: ") + console.log(animeInfo.synopsis?.length) + //console.log("> Chronology length: ") + //console.log(animeInfo.chronology?.length) + console.log("> Genres length: ") + console.log(animeInfo.genres?.length) + console.log("> Episodes count: ") + console.log(animeInfo.episodes?.length) + + console.log("> function getLastAnimes(): ") + console.log((await monos.getLastAnimes()).length) + console.log("> function getLastAnimes(): ") + console.log((await monos.getLastEpisodes()).length) + console.log("> function getEpisodeServers(): ") + console.log((await monos.getEpisodeServers('https://monoschinos2.com/ver/date-a-live-i-episodio-1')).length) +} + +test();*/ diff --git a/src/test/Nhentai.spec.ts b/src/test/Nhentai.spec.ts deleted file mode 100644 index b405de9d..00000000 --- a/src/test/Nhentai.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Nhentai } from "../../src/scraper/sites/manga/nhentai/Nhentai"; -import { IMangaChapter, IMangaResult, Manga } from "../types/manga"; - -describe("It returns a list of animes related that name filter", () => { - it("it should match that fields", async () => { - const mangasHentai: IMangaResult[] = await new Nhentai().filter( - "Evangelion", - ); - - expect(mangasHentai[0].id).toBe("403447"); - expect(mangasHentai[0].title).toBe( - "[Cassino (Magarikouji Lily)] Playboys (2) – Neon Genesis Evangelion dj [Eng]", - ); - }); -}); - -describe("Manga info tests", () => { - it("it should return a manga information from manga id", async () => { - const getMangaInfo: Manga = await new Nhentai().getMangaInfo("403447"); - - expect(getMangaInfo.title).toEqual( - "[Cassino (Magarikouji Lily)] Playboys (2) – Neon Genesis Evangelion dj [Eng]", - ); - expect(getMangaInfo.id).toEqual("403447"); - }); -}); - -describe("Manga chapters", () => { - it("it should return a manga chapters from manga id", async () => { - const getMangaChapters: IMangaChapter[] = - await new Nhentai().getMangaChapters("403447"); - expect(getMangaChapters[0].images[0]).toEqual( - "https://t7.nhentai.net/galleries/2223278/1t.jpg", - ); - }); -}); diff --git a/src/test/TioAnime.spec.ts b/src/test/TioAnime.spec.ts index 739520ca..74ddaa2e 100644 --- a/src/test/TioAnime.spec.ts +++ b/src/test/TioAnime.spec.ts @@ -1,36 +1,63 @@ -import { TioAnime } from '../scraper/sites/anime/tioanime/TioAnime' +import { TioAnime } from "../scraper/sites/anime/tioanime/TioAnime"; describe("TioAnime", () => { let tioanime: TioAnime; beforeEach(() => { tioanime = new TioAnime(); - }) - it('should get anime info successfully', async () => { - const animeInfo = await tioanime.getAnime('https://tioanime.com/anime/date-a-live'); + }); + it("should get anime info successfully", async () => { + const animeInfo = await tioanime.getAnime( + "https://tioanime.com/anime/date-a-live", + ); - expect(animeInfo.name).toBe('Date A Live'); - expect(animeInfo.image.url).toContain('.jpg'); - expect(animeInfo.synopsis.length).toBeGreaterThan(0); + expect(animeInfo.name).toBe("Date A Live"); + expect(animeInfo.image.url).toMatch(/\.jpg$/); + expect(animeInfo.synopsis?.length).toBeGreaterThan(0); expect(animeInfo.chronology?.length).toBeGreaterThan(0); - expect(animeInfo.genres.length).toBeGreaterThan(0); - expect(animeInfo.episodes.length).toBeGreaterThan(0); - }); + expect(animeInfo.genres?.length).toBeGreaterThan(0); + expect(animeInfo.episodes?.length).toBeGreaterThan(0); + expect((await tioanime.getLastAnimes(null)).length).toBeGreaterThan(0); + expect((await tioanime.getLastEpisodes()).length).toBeGreaterThan(0); + expect((await tioanime.getLastMovies()).length).toBeGreaterThan(0); + expect((await tioanime.getLastOnas()).length).toBeGreaterThan(0); + expect((await tioanime.getLastOvas()).length).toBeGreaterThan(0); + expect((await tioanime.getEpisodeServers('https://tioanime.com/ver/date-a-live-1')).length).toBeGreaterThan(0); + }, 40000 /*ms*/); +}); - /*it('should get episode servers successfully', async () => { - const episodeServers = await tioanime.getEpisodeServers('https://tioanime.com/ver/isekai-nonbiri-nouka-9'); - expect(episodeServers.length).toBeGreaterThan(0); - for (let i = 0; i < episodeServers.length; i++) { - const server = episodeServers[i]; - expect(server.name.length).toBeGreaterThan(0); - if (server.file_url != undefined && server.file_url != null) { - expect(server.file_url.length).toBeGreaterThan(0); - } - } - }); - it('should filter anime successfully', async () => { - const result = await tioanime.filter("", ["1"], ["accion"], { begin: 1950, end: 2023 }, 2, "recent"); - expect(result.results.length).toBeGreaterThan(0); - });*/ -}) +/*async function test() { + let tioanime: TioAnime; + tioanime = new TioAnime(); + const animeInfo = await tioanime.getAnime( + "https://tioanime.com/anime/date-a-live", + ); + + console.log('> Name: ') + console.log(animeInfo.name); + console.log('> Image url: ') + console.log(animeInfo.image.url); + console.log('> Synopsis length: ') + console.log(animeInfo.synopsis?.length); + console.log('> Chronology length: ') + console.log(animeInfo.chronology?.length); + console.log('> Genres length: ') + console.log(animeInfo.genres?.length); + console.log('> Episodes length: ') + console.log(animeInfo.episodes?.length); + console.log('> function getLastAnimes(): ') + console.log((await tioanime.getLastAnimes(null)).length); + console.log('> function getLastEpisodes(): ') + console.log((await tioanime.getLastEpisodes()).length); + console.log('> function getLastMovies(): ') + console.log((await tioanime.getLastMovies()).length); + console.log('> function getLastOnas(): ') + console.log((await tioanime.getLastOnas()).length); + console.log('> function getLastOvas(): ') + console.log((await tioanime.getLastOvas()).length); + console.log('> function getEpisodeServers(): ') + console.log((await tioanime.getEpisodeServers('https://tioanime.com/ver/isekai-nonbiri-nouka-9')).length); +} + +test();*/ diff --git a/src/test/Zoro.spec.ts b/src/test/Zoro.spec.ts deleted file mode 100644 index 1e94db83..00000000 --- a/src/test/Zoro.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {Zoro} from '../scraper/sites/anime/zoro/Zoro' - -describe("Zoro", () => { - let zoro: Zoro; - - beforeEach(()=> { - zoro = new Zoro(); - }) - it('should get anime info successfully', async () => { - const animeInfo = await zoro.GetAnimeInfo('tokyo-ghoul-790'); - expect(animeInfo.name).toBe('Tokyo Ghoul'); - expect(animeInfo.alt_name).toContain('東京喰種-トーキョーグール-'); - expect(animeInfo.image.url).toContain('.jpg'); - expect(animeInfo.synopsis.length).toBeGreaterThan(0); - expect(animeInfo.chronology?.length).toBeGreaterThan(0); - expect(animeInfo.genres.length).toBeGreaterThan(0); - }); - it('should filter anime successfully', async () => { - const result = await zoro.Filter("2"); - expect(result.results.length).toBeGreaterThan(0); - }); -}) diff --git a/src/types/anime.ts b/src/types/anime.ts index 1fb02fde..8e82ed80 100644 --- a/src/types/anime.ts +++ b/src/types/anime.ts @@ -1,154 +1,129 @@ -//anime data return standard - -import { ICalendar, IDatePeriod } from './date'; -import { IEpisode } from './episode'; -import { IImage } from './image'; - -//spanish providers - TypeScript version - -/** Specifies the type of anime to which its content refers. */ -export type AnimeType = "Anime" | "Movie" | "OVA" | "ONA" | "Null"; -/** Specify the climatic season in which the anime was published. */ -export type ClimaticStation = "Summer" | "Autumn" | "Winter" | "Spring"; - -/** - * Spectify the rating and stats in the anime - * @author Zukaritasu - */ -export interface IAnimeStats { - score?: string | number; - views?: string | number; - rating?: string | number; // stars -} - -/** - * Spectify chronology to that anime, in some pages puts what anime - * should you see before to that anime - * - * @author Mawfyy - */ -export interface IChronology { - name: string; - url: `/anime/${string}/name/${string}`| string; - image?: string; -} - -/** - * Spectify the anime structure that you scrapped - * @author Zukaritasu - */ -export interface IAnime { - /** Name of the anime */ - name: string; - /** Alternative names describing the name of the anime in another language */ - alt_name?: string | string[]; - /** Anime identifier that can be used when the anime name is not used in the URL. */ - id?: number; - /** The URL or location of the anime in the API */ - url: `/anime/${string}/name/${string}` | string; - /** The anime synopsis */ - synopsis?: string; - /** - * An IImage interface object representing the anime - * image and its banner. */ - image: IImage; - /** - * The date from when the anime started until it ended. The end date may be - * auxiliary in case the anime has not ended. */ - date?: IDatePeriod | ICalendar; - /** The type of anime that indicates whether it is a movie, a special, TV, etc.. */ - type?: AnimeType; - /** Genres that apply to anime */ - genres?: string[]; - /** Climatic station of which the anime was released */ - station?: ClimaticStation | string; - /** - * Most anime websites have an anime statistics section including ratings and - * number of views, etc... */ - stats?: IAnimeStats; - /** Chronology of the anime. It is an array that contains the anime related to it. */ - chronology?: IChronology[]; - /** - * A list of the episodes of this anime. This property must be null or not used - * if an IAnime object is used in IChronology. */ - episodes?: IEpisode[]; - /** - * The status of the anime indicating whether it is on air, finished - * or still on hold. */ - status?: string | boolean; - /** Indicates whether the anime is adult content. */ - nsfw?: boolean; -} - -/**---------------- Interfaces implementation ---------------- **/ - -/** - * Spectify the rating and stats in the anime - * @author Zukaritasu - */ -export class AnimeStats implements IAnimeStats { - /** Anime score */ - score?: string | number; - /** The number of views of the anime */ - views?: string | number; - /** */ - rating?: string | number; -} - -/** - * Spectify chronology to that anime, in some pages puts what anime - * should you see before to that anime - * - * @author Mawfyy - */ -export class Chronology implements IChronology { - /** @inheritdoc */ - name: string; - /** @inheritdoc */ - url: `/anime/${string}/name/${string}` | string; - /** @inheritdoc */ - image?: string; - - constructor(name?: string, url?: string, image?: string) { - this.name = name; - this.url = url; - this.image = image; - } -} - -/** - * Spectify the anime structure that you scrapped - * @author Zukaritasu - */ -export class Anime implements IAnime { - /** @inheritdoc */ - name: string; - /** @inheritdoc */ - alt_name?: string | string[]; - /** @inheritdoc */ - id?: number; - /** @inheritdoc */ - url: `/anime/${string}/name/${string}` | string; - /** @inheritdoc */ - synopsis: string; - /** @inheritdoc */ - image: IImage; - /** @inheritdoc */ - date?: IDatePeriod | ICalendar; - /** @inheritdoc */ - type?: AnimeType; - /** @inheritdoc */ - genres: string[] = []; - /** @inheritdoc */ - stats?: IAnimeStats; - /** @inheritdoc */ - station?: ClimaticStation | string; - /** @inheritdoc */ - chronology?: IChronology[]; - /** @inheritdoc */ - episodes: IEpisode[] = []; - /** @inheritdoc */ - status?: string | boolean; - /** @inheritdoc */ - nsfw?: boolean; -} +//anime data return standard + +import { BaseMedia, type IBaseMedia } from "./base"; +import { ICalendar, IDatePeriod } from "./date"; +import { IEpisode } from "./episode"; +import { IImage } from "./image"; + +//spanish providers - TypeScript version + +/** Specifies the type of anime to which its content refers. */ +export type AnimeType = "Anime" | "Movie" | "OVA" | "ONA" | "Null"; +/** Specify the climatic season in which the anime was published. */ +export type ClimaticStation = "Summer" | "Autumn" | "Winter" | "Spring"; + +/** + * Spectify the rating and stats in the anime + * @author Zukaritasu + */ +export interface IAnimeStats { + score?: string | number; + views?: string | number; + rating?: string | number; // stars +} + +/** + * Spectify chronology to that anime, in some pages puts what anime + * should you see before to that anime + * + * @author Mawfyy + */ +export interface IChronology { + name: string; + url: `/anime/${string}/name/${string}` | string; + image?: string; +} + +/** + * Spectify the anime structure that you scrapped + * @author Zukaritasu + */ +export interface IAnimeMedia extends IBaseMedia { + /** + * An IImage interface object representing the anime + * image and its banner. */ + image: IImage; + /** URL or location of the anime in the API. */ + url: `/anime/${string}/name/${string}` | string; + /** Anime identifier that can be used when the anime name is not used in the URL. */ + id?: number; + /** + * The date from when the anime started until it ended. The end date may be + * auxiliary in case the anime has not ended. */ + date?: IDatePeriod | ICalendar; + /** The type of anime that indicates whether it is a movie, a special, TV, etc.. */ + type?: AnimeType; + /** Climatic station of which the anime was released */ + station?: ClimaticStation | string; + /** + * Most anime websites have an anime statistics section including ratings and + * number of views, etc... */ + stats?: IAnimeStats; + /** Chronology of the anime. It is an array that contains the anime related to it. */ + chronology?: IChronology[]; + /** + * A list of the episodes of this anime. This property must be null or not used + * if an IAnime object is used in IChronology. */ + episodes?: IEpisode[]; +} + +/**---------------- Interfaces implementation ---------------- **/ + +/** + * Spectify the rating and stats in the anime + * @author Zukaritasu + */ +export class AnimeStats implements IAnimeStats { + /** Anime score */ + score?: string | number; + /** The number of views of the anime */ + views?: string | number; + /** */ + rating?: string | number; +} + +/** + * Spectify chronology to that anime, in some pages puts what anime + * should you see before to that anime + * + * @author Mawfyy + */ +export class Chronology implements IChronology { + /** @inheritdoc */ + name: string; + /** @inheritdoc */ + url: `/anime/${string}/name/${string}` | string; + /** @inheritdoc */ + image?: string; + + constructor(name?: string, url?: string, image?: string) { + this.name = name; + this.url = url; + this.image = image; + } +} + +/** + * Spectify the anime structure that you scrapped + * @author Zukaritasu + */ +export class AnimeMedia extends BaseMedia implements IAnimeMedia { + /** @inheritdoc */ + image: IImage; + /** @inheritdoc */ + url: `/anime/${string}/name/${string}` | string; + /** @inheritdoc */ + id?: number; + /** @inheritdoc */ + date?: IDatePeriod | ICalendar; + /** @inheritdoc */ + type?: AnimeType; + /** @inheritdoc */ + station?: ClimaticStation | string; + /** @inheritdoc */ + stats?: IAnimeStats; + /** @inheritdoc */ + chronology?: IChronology[]; + /** @inheritdoc */ + episodes?: IEpisode[]; +} diff --git a/src/types/base.ts b/src/types/base.ts new file mode 100644 index 00000000..240a05bb --- /dev/null +++ b/src/types/base.ts @@ -0,0 +1,92 @@ +import type { IImage } from "./image"; + +/** + * The base interface to create new types of providers. + * + * @author Victor + */ +export interface IBaseMedia { + /** Name or title of the media */ + name: string; + /** Alternative names or titles in different languages. */ + alt_names?: string[] | string; + /** Description or synopsis of the media. */ + synopsis?: string; + /** Genres that apply to the media. */ + genres?: string[]; + /** Indicates the status of the media, such as ongoing, completed, on air, finished, or on hold. */ + status?: string | boolean | "ongoing" | "completed"; + /** Indicates if the content is intended for adult audiences. */ + nsfw?: boolean; +} + +/** + * The base interface to create new chapter classes for new types of providers. + * + * @author Victor + */ +export interface IBaseChapter { + /** + * Name of the media chapter. May contain the chapter number concatenated + * with the media name. + */ + name: string; + /** The chapter number. */ + num: number; + /** The image of the chapter shown as thumbnail */ + thumbnail?: IImage; +} + +/** + * The base interface to create new search results of any new type of provider. + * + * @author Victor + */ +export interface IBaseResult { + /** Name of the media that was the result of your search */ + name: string; +} + +/** + * Create the structure of the base media. + * + * @author Victor + */ +export abstract class BaseMedia implements IBaseMedia { + /** @inheritdoc */ + name: string; + /** @inheritdoc */ + alt_names?: string[] | string; + /** @inheritdoc */ + synopsis?: string; + /** @inheritdoc */ + genres?: string[]; + /** @inheritdoc */ + status?: string | boolean | "ongoing" | "completed"; + /** @inheritdoc */ + nsfw?: boolean; +} + +/** + * Create the result search of the base media. + * + * @author Victor + */ +export abstract class BaseResult implements IBaseResult { + /** @inheritdoc */ + name: string; +} + +/** + * Extends the properties of a base chapter to create a new one. + * + * @author Victor + */ +export abstract class BaseChapter implements IBaseChapter { + /** @inheritdoc */ + name: string; + /** @inheritdoc */ + num: number; + /** @inheritdoc */ + thumbnail?: IImage; +} diff --git a/src/types/date.ts b/src/types/date.ts index 2f635189..0829f895 100644 --- a/src/types/date.ts +++ b/src/types/date.ts @@ -1,90 +1,94 @@ -//Spanish Providers - TypeScript version - -/** - * In most anime websites in the anime information part may be available - * the year in which the content was published but without specifying the - * month and day so the year property is not optional, although from the - * HTML in some cases you can extract the exact month and day of publication, - * in that case the month and day properties are optional. - * - * @author Zukaritasu - */ -export interface ICalendar { - /** - * The year of publication. This property is not optional - * because in many anime pages they only say the year of publication but - * not the month and day. */ - year: number | string; - month?: number | string; - day?: number | string; -} - -/** - * If the anime was completed it has a start and end date of - * publication but if it is still on air only the begin property will contain - * the information of when it was published, the end property is optional. - * - * @author Zukaritasu - */ -export interface IDatePeriod { - /** The exact date on which it was published the anime */ - begin: ICalendar; - /** - * The exact date the anime ended. Ownership is optional because it - * may not be finished yet. */ - end?: ICalendar; -} - -/** - * Specifies the year, month and day in which the anime or other content - * related to the anime was released in the form of a movie, OVA or ONA. - * Implementation of the {@link ICalendar} interface - * - * @author Zukaritasu - * @extends ICalendar - */ -export class Calendar implements ICalendar { - /** @inheritdoc */ - year: number; - /** @inheritdoc */ - month?: number; - /** @inheritdoc */ - day?: number; - - constructor(year: number) { - this.year = year; - } - - /** - * Returns an instance of the ICalendar interface. The - * function takes as parameter the date in a string; the format - * must be compatible with the constructor of the Date class. - * @example - * Calendar.getCalendar("01/05/2022") - * - * @param date - * @returns ICalendar - */ - static getCalendar(date: string): ICalendar { - const obj = new Date(date); - return { year: obj.getFullYear(), day: obj.getDay(), month: obj.getMonth() }; - } -} - -/** - * Implementation of the {@link IDatePeriod} interface - * - * @author Zukaritasu - * @extends IDatePeriod - */ -export class DatePeriod implements IDatePeriod { - /** @inheritdoc */ - begin: ICalendar; - /** @inheritdoc */ - end?: ICalendar; - - constructor(begin: ICalendar, end?: ICalendar) { - this.begin = begin; - this.end = end; - } -} +//Spanish Providers - TypeScript version + +/** + * In most anime websites in the anime information part may be available + * the year in which the content was published but without specifying the + * month and day so the year property is not optional, although from the + * HTML in some cases you can extract the exact month and day of publication, + * in that case the month and day properties are optional. + * + * @author Zukaritasu + */ +export interface ICalendar { + /** + * The year of publication. This property is not optional + * because in many anime pages they only say the year of publication but + * not the month and day. */ + year: number | string; + month?: number | string; + day?: number | string; +} + +/** + * If the anime was completed it has a start and end date of + * publication but if it is still on air only the begin property will contain + * the information of when it was published, the end property is optional. + * + * @author Zukaritasu + */ +export interface IDatePeriod { + /** The exact date on which it was published the anime */ + begin: ICalendar; + /** + * The exact date the anime ended. Ownership is optional because it + * may not be finished yet. */ + end?: ICalendar; +} + +/** + * Specifies the year, month and day in which the anime or other content + * related to the anime was released in the form of a movie, OVA or ONA. + * Implementation of the {@link ICalendar} interface + * + * @author Zukaritasu + * @extends ICalendar + */ +export class Calendar implements ICalendar { + /** @inheritdoc */ + year: number; + /** @inheritdoc */ + month?: number; + /** @inheritdoc */ + day?: number; + + constructor(year: number) { + this.year = year; + } + + /** + * Returns an instance of the ICalendar interface. The + * function takes as parameter the date in a string; the format + * must be compatible with the constructor of the Date class. + * @example + * Calendar.getCalendar("01/05/2022") + * + * @param date + * @returns ICalendar + */ + static getCalendar(date: string): ICalendar { + const obj = new Date(date); + return { + year: obj.getFullYear(), + day: obj.getDay(), + month: obj.getMonth(), + }; + } +} + +/** + * Implementation of the {@link IDatePeriod} interface + * + * @author Zukaritasu + * @extends IDatePeriod + */ +export class DatePeriod implements IDatePeriod { + /** @inheritdoc */ + begin: ICalendar; + /** @inheritdoc */ + end?: ICalendar; + + constructor(begin: ICalendar, end?: ICalendar) { + this.begin = begin; + this.end = end; + } +} diff --git a/src/types/episode.ts b/src/types/episode.ts index 53690fe9..ca7c6190 100644 --- a/src/types/episode.ts +++ b/src/types/episode.ts @@ -1,84 +1,72 @@ -//Spanish Providers - TypeScript version - -/** - * This interface only puts the server name where host episode, - * and url to that episode - * - * @author Mawfyy - * @author Zukaritasu - */ -export interface IEpisodeServer { - /** Name of the server where the episode is hosted */ - name: string; - /** - * The URL of the chapter. This URL leads to the video player of the - * server where the episode is hosted. */ - url: string; - /** Direct video file url for download */ - file_url?: string; -} - -/** - * This interface of the episode contains the basic properties necessary - * for the scraper. The *servers* property contains a list of servers - * although this is optional for performance reasons when using the API. - * - * @author Zukaritasu - */ -export interface IEpisode { - /** - * Name of anime episode. May contain the chapter number concatenated - * with the anime name. */ - name: string; - /** The episode URL in the API query */ - url: `/anime/${string}/episode/${string | number}` | string; - /** The episode number. By default the value can be 0 in string or integer. */ - number: number | string; - /** - * List of available servers where the episode is located. Remember that - * this is not the download link of the episode but of the video player. */ - servers?: IEpisodeServer[]; - /** The image of the episode shown as thumbnail */ - image: string; -} - -/** - * This interface only puts the server name where host episode, - * and url to that episode - * - * @author Mawfyy - * @author Zukaritasu - */ -export class EpisodeServer implements IEpisodeServer { - /** @inheritdoc */ - name: string; - /** @inheritdoc */ - url: string; - /** @inheritdoc */ - file_url?: string; - - constructor(name?: string, url?: string) { - this.name = name; - this.url = url; - } -} - -/** - * This interface of the episode contains the basic properties necessary - * for the scraper. The *servers* property contains a list of servers - * although this is optional for performance reasons when using the API. - * - * @author Zukaritasu - */ -export class Episode implements IEpisode { - /** @inheritdoc */ - name: string; - /** @inheritdoc */ - url: `/anime/${string}/episode/${string | number}` | string; - /** @inheritdoc */ - number: number | string; - /** @inheritdoc */ - servers?: IEpisodeServer[] = []; - /** @inheritdoc */ - image: string; -} +//Spanish Providers - TypeScript version + +import { BaseChapter, type IBaseChapter } from "./base"; + +/** + * This interface only puts the server name where host episode, + * and url to that episode + * + * @author Mawfyy + * @author Zukaritasu + */ +export interface IEpisodeServer { + /** Name of the server where the episode is hosted */ + name: string; + /** + * The URL of the chapter. This URL leads to the video player of the + * server where the episode is hosted. */ + url: string; + /** Direct video file url for download */ + file_url?: string; +} + +/** + * This interface of the episode contains the basic properties necessary + * for the scraper. The *servers* property contains a list of servers + * although this is optional for performance reasons when using the API. + * + * @author Zukaritasu + */ +export interface IEpisode extends IBaseChapter { + /** The episode URL in the API query */ + url: `/anime/${string}/episode/${string | number}` | string; + /** + * List of available servers where the episode is located. Remember that + * this is not the download link of the episode but of the video player. */ + servers?: IEpisodeServer[]; +} + +/** + * This interface only puts the server name where host episode, + * and url to that episode + * + * @author Mawfyy + * @author Zukaritasu + */ +export class EpisodeServer implements IEpisodeServer { + /** @inheritdoc */ + name: string; + /** @inheritdoc */ + url: string; + /** @inheritdoc */ + file_url?: string; + + constructor(name?: string, url?: string) { + this.name = name; + this.url = url; + } +} + +/** + * This interface of the episode contains the basic properties necessary + * for the scraper. The *servers* property contains a list of servers + * although this is optional for performance reasons when using the API. + * + * @author Zukaritasu + */ +export class Episode extends BaseChapter implements IEpisode { + /** @inheritdoc */ + url: `/anime/${string}/episode/${string | number}` | string; + /** @inheritdoc */ + servers?: IEpisodeServer[] = []; +} diff --git a/src/types/extractors.ts b/src/types/extractors.ts index 56548a4e..efaca982 100644 --- a/src/types/extractors.ts +++ b/src/types/extractors.ts @@ -3,41 +3,38 @@ import axios from "axios"; //import { UnPacked } from "./utils"; /** - * + * * @param url URL to Request data * @example await filemoon("https://filemoon.sx/e/5ehdd8cohg8r") * @description List of domains [filemoon.sx] - * + * * RequestBR Preload url needed by the request with cookies */ - -axios.defaults.withCredentials = true - +axios.defaults.withCredentials = true; export const filemoon = async (_url: string) => { - - //const Request = await axios.get("https://filemoon.sx/e/5ehdd8cohg8r") - let headersList = { - "User-Agent":"Mozilla/5.0 (Linux; Android 10; LM-K920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36" - } - - let response = await fetch("https://filemoon.sx/e/397bb6qxbwvh", { - method: "GET", - credentials:"include", - headers: headersList - }); - - let data = await response.text(); - - console.log(btoa(data)) - // const $ = cheerio.load(data) - - //const Buffer = btoa($("script").get().at(-1).children[0].data) - //const UnBuffer = UnPacked(Buffer) - - //const RequestBR = await eval(UnBuffer.slice(UnBuffer.indexOf("{sources:[{file:") + "{sources:[{file:".length, UnBuffer.indexOf("}],image:", 1))); - //await axios.get(RequestBR,{headers:{"Accept":"*/*","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"}}) - return data - -} + //const Request = await axios.get("https://filemoon.sx/e/5ehdd8cohg8r") + let headersList = { + "User-Agent": + "Mozilla/5.0 (Linux; Android 10; LM-K920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36", + }; + + let response = await fetch("https://filemoon.sx/e/397bb6qxbwvh", { + method: "GET", + credentials: "include", + headers: headersList, + }); + + let data = await response.text(); + + console.log(btoa(data)); + // const $ = cheerio.load(data) + + //const Buffer = btoa($("script").get().at(-1).children[0].data) + //const UnBuffer = UnPacked(Buffer) + + //const RequestBR = await eval(UnBuffer.slice(UnBuffer.indexOf("{sources:[{file:") + "{sources:[{file:".length, UnBuffer.indexOf("}],image:", 1))); + //await axios.get(RequestBR,{headers:{"Accept":"*/*","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.55"}}) + return data; +}; diff --git a/src/types/filter.ts b/src/types/filter.ts index 9c118a00..e646abf9 100644 --- a/src/types/filter.ts +++ b/src/types/filter.ts @@ -2,44 +2,44 @@ * @author Yako */ export enum Genres { - Action = "Acción", - MartialArts = "Artes Marciales", - Adventure = "Aventuras", - Racing = "Carreras", - ScienceFiction = "Ciencia Ficción", - Comedy = "Comedia", - Dementia = "Demencia", - Demons = "Demonios", - Sports = "Deportes", - Drama = "Drama", - Ecchi = "Ecchi", - School = "Escolares", - Space = "Espacial", - Fantasy = "Fantasía", - Harem = "Harem", - Historical = "Histórico", - Kids = "Infantil", - Josei = "Josei", - Games = "Juegos", - Magic = "Magia", - Mecha = "Mecha", - Military = "Militar", - Mystery = "Misterio", - Music = "Música", - Parody = "Parodia", - Police = "Policía", - Psychological = "Psicológico", - SliceOfLife = "Recuentos de la vida", - Romance = "Romance", - Samurai = "Samurai", - Seinen = "Seinen", - Shoujo = "Shoujo", - Shounen = "Shounen", - Supernatural = "Sobrenatural", - Superpowers = "Superpoderes", - Suspense = "Suspenso", - Horror = "Terror", - Vampires = "Vampiros", - Yaoi = "Yaoi", - Yuri = "Yuri", - } \ No newline at end of file + Action = "Acción", + MartialArts = "Artes Marciales", + Adventure = "Aventuras", + Racing = "Carreras", + ScienceFiction = "Ciencia Ficción", + Comedy = "Comedia", + Dementia = "Demencia", + Demons = "Demonios", + Sports = "Deportes", + Drama = "Drama", + Ecchi = "Ecchi", + School = "Escolares", + Space = "Espacial", + Fantasy = "Fantasía", + Harem = "Harem", + Historical = "Histórico", + Kids = "Infantil", + Josei = "Josei", + Games = "Juegos", + Magic = "Magia", + Mecha = "Mecha", + Military = "Militar", + Mystery = "Misterio", + Music = "Música", + Parody = "Parodia", + Police = "Policía", + Psychological = "Psicológico", + SliceOfLife = "Recuentos de la vida", + Romance = "Romance", + Samurai = "Samurai", + Seinen = "Seinen", + Shoujo = "Shoujo", + Shounen = "Shounen", + Supernatural = "Sobrenatural", + Superpowers = "Superpoderes", + Suspense = "Suspenso", + Horror = "Terror", + Vampires = "Vampiros", + Yaoi = "Yaoi", + Yuri = "Yuri", +} diff --git a/src/types/image.ts b/src/types/image.ts index 4e1e0897..6fe53d17 100644 --- a/src/types/image.ts +++ b/src/types/image.ts @@ -1,36 +1,36 @@ -//Spanish Providers - TypeScript version - -/** - * In most animes include a image and banner that these anime - * banner probably it isn't common into pages, so banner property - * is opcional, the anime's image or cover always display in the pages. - * - * @author Mawfyy - */ -export interface IImage { - /** The URL of the content image */ - url: string; - /** - * The URL of the content banner. It is optional because it is not available - * in all sites. */ - banner?: string; -} - -/** - * In most animes include a image and banner that these anime - * banner probably it isn't common into pages, so banner property - * is opcional, the anime's image or cover always display in the pages. - * - * @author Mawfyy - */ -export class Image implements IImage { - /** @inheritdoc */ - url: string; - /** @inheritdoc */ - banner?: string; - - constructor(url: string, banner?: string) { - this.url = url; - this.banner = banner; - } -} +//Spanish Providers - TypeScript version + +/** + * In most animes include a image and banner that these anime + * banner probably it isn't common into pages, so banner property + * is opcional, the anime's image or cover always display in the pages. + * + * @author Mawfyy + */ +export interface IImage { + /** The URL of the content image */ + url: string; + /** + * The URL of the content banner. It is optional because it is not available + * in all sites. */ + banner?: string; +} + +/** + * In most animes include a image and banner that these anime + * banner probably it isn't common into pages, so banner property + * is opcional, the anime's image or cover always display in the pages. + * + * @author Mawfyy + */ +export class Image implements IImage { + /** @inheritdoc */ + url: string; + /** @inheritdoc */ + banner?: string; + + constructor(url: string, banner?: string) { + this.url = url; + this.banner = banner; + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 5059f75d..16a4aa6c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -export * from "./anime" -export * from "./episode" -export * from "./image" -export * from "./date" \ No newline at end of file +export * from "./anime"; +export * from "./episode"; +export * from "./image"; +export * from "./date"; diff --git a/src/types/manga.ts b/src/types/manga.ts index 6b9b0d1e..d4405722 100644 --- a/src/types/manga.ts +++ b/src/types/manga.ts @@ -1,6 +1,7 @@ import { IImage } from "./image"; import { ICalendar } from "./date"; import { IResultSearch } from "./search"; +import { BaseChapter, BaseMedia, type IBaseChapter, type IBaseMedia, type IBaseResult } from "./base"; /** * The chapter is part of the manga and is also part of a volume. It is made @@ -8,25 +9,19 @@ import { IResultSearch } from "./search"; * * @author Zukaritasu */ -export interface IMangaChapter { +export interface IMangaChapter extends IBaseChapter { /** ID or chapter identifier of the chapter that is part of the manga */ id: number | string; - /** Chapter title. May contain the manga chapter number. */ - title: string; - /** - * A brief description of what the new chapter brings. This property - * is optional because not all websites have it available. */ - description?: string; /** URL of the chapter in the API location */ url: `/manga/${string}/chapter/${string}`; - /** Chapter number */ - number: number; /** * Images of the manga chapter. * The first image may contain the cover of the chapter. */ images: string[]; - /** The cover page of the chapter. Refers to the first page of the chapter. */ - cover?: string; + /** + * A brief description of what the new chapter brings. This property + * is optional because not all websites have it available. */ + description?: string; /** * The date on which the chapter was published. This is optional because * in some cases it is not specified. */ @@ -43,7 +38,7 @@ export interface IMangaChapter { * * @author Zukaritasu */ -export interface IMangaVolume { +export interface IMangaVolume extends IBaseChapter { /** Manga volume ID */ id: number | string; /** @@ -51,24 +46,14 @@ export interface IMangaVolume { * the number of the first chapter of the volume is the beginning and * the last chapter is the end. */ range: [number, number]; - /** - * The title of the volume. The title may contain a short explanation - * of what the volume contains. */ - title?: string; /** * Description or introduction that explains a little of what is to * come in the next chapters that make up the volume. */ - description?: string; - /** Manga volume number */ - number?: number; //number + synopsis?: string; /** Images of the manga volume. */ images: string[]; /** The date on which the first chapter of the volume was published. */ date?: ICalendar; - /** - * The image or cover of the volume. - * This property contains the URL of the image */ - thumbnail?: string; /** URL of the volume in the API location */ url?: `/manga/${string}/volume/${string}`; // title or number } @@ -79,27 +64,17 @@ export interface IMangaVolume { * * @author Zukaritasu */ -export interface IManga { +export interface IMangaMedia extends IBaseMedia { /** Manga ID */ id: number | string; - /** The URL of the manga in the API location */ - url: `/manga/${string}/title/${string}`; - /** The title of the manga. */ - title: string; - /** The title of the manga in other languages (alternative names) */ - altTitles?: string[]; + /** URL or location of the manga in the API. */ + url: `/manga/${string}/name/${string}` | string; /** * Manga cover or miniature. Some manga pages show the cover and the * banner, hence the use of the IImage interface. */ thumbnail?: IImage; - /** Synopsis or description of the manga */ - description?: string; - /** Indicates the status of the manga, in progress or completed. */ - status?: "ongoing" | "completed"; /** A list with the name of the authors of the manga */ authors?: string[]; - /** Genres manga */ - genres?: string[]; /** A list of the characters that are part of the history of manga */ characters?: string[]; @@ -113,8 +88,6 @@ export interface IManga { chapters?: IMangaChapter[]; /** A list of manga volumes */ volumes?: IMangaVolume[]; - /** Indicates if the content of the manga is for +18 */ - isNSFW: boolean; } /** @@ -126,16 +99,16 @@ export interface IManga { * * @author Zukaritasu */ -export interface IMangaResult { - /** {@inheritdoc IManga.id} */ +export interface IMangaResult extends IBaseResult { + /** Manga ID */ id: number | string; - /** {@inheritdoc IManga.title} */ - title: string; - /** {@inheritdoc IManga.thumbnail} */ + /** The manga URL from the API */ + url: `/manga/${string}/name/${string}` | string; + /** + * Manga cover or miniature. Some manga pages show the cover and the + * banner, hence the use of the IImage interface. */ thumbnail?: IImage; - /** {@inheritdoc IManga.url} */ - url: `/manga/${string}/title/${string}`; -}//filter +} //filter /** * This class defines the basic properties that a manga website can @@ -143,35 +116,23 @@ export interface IMangaResult { * * @author Zukaritasu */ -export class Manga implements IManga { - /** @inheritdoc */ - id: string; - /** @inheritdoc */ - url: `/manga/${string}/title/${string}`; +export class MangaMedia extends BaseMedia implements IMangaMedia { /** @inheritdoc */ - title: string; + id: string | number; /** @inheritdoc */ - altTitles?: string[]; + url: `/manga/${string}/name/${string}` | string; /** @inheritdoc */ thumbnail?: IImage; /** @inheritdoc */ - description?: string; - /** @inheritdoc */ - status?: "ongoing" | "completed"; - /** @inheritdoc */ authors?: string[]; /** @inheritdoc */ - langlist?: string[]; - /** @inheritdoc */ - genres?: string[]; - /** @inheritdoc */ characters?: string[]; /** @inheritdoc */ + langlist?: string[]; + /** @inheritdoc */ chapters?: IMangaChapter[]; /** @inheritdoc */ volumes?: IMangaVolume[]; - /** @inheritdoc */ - isNSFW: boolean; } //////////////////// @@ -182,22 +143,16 @@ export class Manga implements IManga { * * @author Zukaritasu */ -export class MangaChapter implements IMangaChapter { +export class MangaChapter extends BaseChapter implements IMangaChapter { /** @inheritdoc */ id: number | string; /** @inheritdoc */ - title: string; - /** @inheritdoc */ description?: string; /** @inheritdoc */ url: `/manga/${string}/chapter/${string}`; /** @inheritdoc */ - number: number; - /** @inheritdoc */ images: string[] = []; /** @inheritdoc */ - cover?: string; - /** @inheritdoc */ date?: ICalendar; } @@ -211,23 +166,17 @@ export class MangaChapter implements IMangaChapter { * * @author Zukaritasu */ -export class MangaVolume implements IMangaVolume { +export class MangaVolume extends BaseChapter implements IMangaVolume { /** @inheritdoc */ id: number | string; /** @inheritdoc */ range: [number, number] = [0, 0]; /** @inheritdoc */ - title?: string; - /** @inheritdoc */ - description?: string; - /** @inheritdoc */ - number?: number; //number + synopsis?: string; /** @inheritdoc */ images: string[]; /** @inheritdoc */ date?: ICalendar; /** @inheritdoc */ - thumbnail?: string; - /** @inheritdoc */ - url?:`/manga/${string}/volume/${string}`; // title or number + url?: `/manga/${string}/volume/${string}`; // title or number } diff --git a/src/types/movie.d.ts b/src/types/movie.d.ts index 78baee26..021e79a1 100644 --- a/src/types/movie.d.ts +++ b/src/types/movie.d.ts @@ -1,36 +1,31 @@ - /* * - *Specifies fields that has a movie, as rating or genres + *Specifies fields that has a movie, as rating or genres * to that movie. - * + * * @author Mawfyy */ export interface IMovie { - title?: String, - originalTitle: String, - dateReleased: String, - durationInSeconds: number | String, - description?: String, - url: String, - urlTrailer?: String - genre?: String[] - rating?: number, - animeNameRelated?: String + title?: String; + originalTitle: String; + dateReleased: String; + durationInSeconds: number | String; + description?: String; + url: String; + urlTrailer?: String; + genre?: String[]; + rating?: number; + animeNameRelated?: String; cast?: { - directors?: String[] - writes?: String[] - actors?: String[] - } + directors?: String[]; + writes?: String[]; + actors?: String[]; + }; } - export class Movie { - movie: IMovie; - getMovie(): IMovie - + getMovie(): IMovie; } - diff --git a/src/types/search.ts b/src/types/search.ts index 59ed7258..c98d7278 100644 --- a/src/types/search.ts +++ b/src/types/search.ts @@ -1,87 +1,85 @@ -//Spanish Providers - TypeScript version - -/** - * Anime search helpers, use them with you scrapping by filter (searching..), - * this format help you how you can return - * theses results - * - * @author Mawfyy - * @author Zukaritasu - */ -export interface IAnimeSearch { - /** Name of the anime that was the result of your search */ - name: string; - /** The URL of the anime image */ - image: string; - /** The anime URL from the API */ - url: `/anime/${string}/name/${string}` | string; // API url - /** - * Defines the type of content to which the anime is directed, which - * can be a movie, OVA, ONA, etc... */ - type?: string; -} - -/** - * To navigate more easily among the infinite number of results that the - * API can return, we use this interface in which there is information - * about which page is being searched and how many pages are still available. - * - * @author Zukaritasu - */ -export interface IPageNavigation { - /** number of pages available to search */ - count?: number; - /** page number where you are currently located */ - current?: number; - /** the next page number */ - next?: number; - /** Indicates if there is a next page available */ - hasNext?: boolean -} - -/** - * Search results including information on the page number of the - * searched web site - * - * @author Zukaritasu - */ -export interface IResultSearch { - /** Search by navigation */ - nav?: IPageNavigation; - /** A list of the results obtained */ - results: T[]; -} - -/** - * Anime search helpers, use them with you scrapping by filter (searching..), - * this format help you how you can return - * theses results - * - * @author Mawfyy - * @author Zukaritasu - */ -export class AnimeSearch implements IAnimeSearch { - /** @inheritdoc */ - name: string; - /** @inheritdoc */ - image: string; - /** @inheritdoc */ - url: `/anime/${string}/name/${string}` | string; // API url - /** @inheritdoc */ - type?: string; -} - -/** - * Search results including information on the page number of the - * searched web site - * - * @author Zukaritasu - */ -export class ResultSearch implements IResultSearch { - /** @inheritdoc */ - nav?: IPageNavigation; - /** @inheritdoc */ - results: T[] = []; -} - -/** end */ +//Spanish Providers - TypeScript version + +import { BaseResult, type IBaseResult } from "./base"; + +/** + * Anime search helpers, use them with you scrapping by filter (searching..), + * this format help you how you can return + * theses results + * + * @author Mawfyy + * @author Zukaritasu + */ +export interface IAnimeResult extends IBaseResult { + /** The anime URL from the API */ + url: `/anime/${string}/name/${string}` | string; + /** The URL of the anime image */ + image: string; + /** + * Defines the type of content to which the anime is directed, which + * can be a movie, OVA, ONA, etc... */ + type?: string; +} + +/** + * To navigate more easily among the infinite number of results that the + * API can return, we use this interface in which there is information + * about which page is being searched and how many pages are still available. + * + * @author Zukaritasu + */ +export interface IPageNavigation { + /** number of pages available to search */ + count?: number; + /** page number where you are currently located */ + current?: number; + /** the next page number */ + next?: number; + /** Indicates if there is a next page available */ + hasNext?: boolean; +} + +/** + * Search results including information on the page number of the + * searched web site + * + * @author Zukaritasu + */ +export interface IResultSearch { + /** Search by navigation */ + nav?: IPageNavigation; + /** A list of the results obtained */ + results: T[]; +} + +/** + * Anime search helpers, use them with you scrapping by filter (searching..), + * this format help you how you can return + * theses results + * + * @author Mawfyy + * @author Zukaritasu + */ +export class AnimeResult extends BaseResult implements IAnimeResult { + /** @inheritdoc */ + url: `/anime/${string}/name/${string}` | string; + /** @inheritdoc */ + image: string; + /** @inheritdoc */ + type?: string; +} + +/** + * Search results including information on the page number of the + * searched web site + * + * @author Zukaritasu + */ +export class ResultSearch implements IResultSearch { + /** @inheritdoc */ + nav?: IPageNavigation; + /** @inheritdoc */ + results: T[] = []; +} + +/** end */ diff --git a/src/types/utils.ts b/src/types/utils.ts index a9a47a90..3b9d5047 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -3,66 +3,71 @@ import { unpack } from "unpacker"; //Spanish Providers - TypeScript version interface IPageInfo { - name: string - url: string, // url page - server: string, - domain: string + name: string; + url: string; // url page + server: string; + domain: string; } //================== API functions ================== export const api = { - /** - * Replaces the original URL with the API URL - * - * @param info - * @param url - * @returns - */ - getEpisodeURL(info: IPageInfo, url: string): string { - return url.replace(`https://${info.domain}/ver/`, `/anime/${info.name}/episode/`); - }, + /** + * Replaces the original URL with the API URL + * + * @param info + * @param url + * @returns + */ + getEpisodeURL(info: IPageInfo, url: string): string { + return url.replace( + `https://${info.domain}/ver/`, + `/anime/${info.name}/episode/`, + ); + }, - /** - * Replaces the original URL with the API URL - * - * @param info - * @param url - * @returns - */ - getAnimeURL(info: IPageInfo, url: string): string { - return url.replace(`https://${info.domain}/anime/`, `/anime/${info.name}/name/`); - } -} + /** + * Replaces the original URL with the API URL + * + * @param info + * @param url + * @returns + */ + getAnimeURL(info: IPageInfo, url: string): string { + return url.replace( + `https://${info.domain}/anime/`, + `/anime/${info.name}/name/`, + ); + }, +}; //=================================================== export const utils = { - /** - * - * @param object - * @returns - */ - isUsableValue(object: any): boolean { - return object != null && object != undefined; - } -} - + /** + * + * @param object + * @returns + */ + isUsableValue(object: any): boolean { + return object != null && object != undefined; + }, +}; /** - * + * * @param packedString in Base64 - * + * */ export const UnPacked = (packedString: string) => { - let valuePacked: string; + let valuePacked: string; - if (typeof atob === "undefined") { - valuePacked = Buffer.from(packedString, "base64").toString("binary"); - } else { - valuePacked = atob(packedString); - } - console.log(unpack(valuePacked)) - return unpack(valuePacked); -} + if (typeof atob === "undefined") { + valuePacked = Buffer.from(packedString, "base64").toString("binary"); + } else { + valuePacked = atob(packedString); + } + console.log(unpack(valuePacked)); + return unpack(valuePacked); +}; diff --git a/src/utils/ScraperError.ts b/src/utils/ScraperError.ts new file mode 100644 index 00000000..86c0083b --- /dev/null +++ b/src/utils/ScraperError.ts @@ -0,0 +1,9 @@ +import { AxiosError } from "axios"; + +export class ScraperErrorResponse extends AxiosError { + constructor(message: string, name?: string) { + super(message); + this.name = name || "Server error"; + this.stack = null; + } +} diff --git a/src/utils/manga/schemaProviders.js b/src/utils/manga/schemaProviders.js index 25bd2734..5b9914ac 100644 --- a/src/utils/manga/schemaProviders.js +++ b/src/utils/manga/schemaProviders.js @@ -1,53 +1,53 @@ -import { Image } from '../schemaProviders.js'; +import { Image } from "../schemaProviders.js"; /** - * + * */ export class ChapterView { - title; - url; - image; - manga; + title; + url; + image; + manga; } /** * Describes a manga chapter with its basic information including the * title and an array containing the images or pages of the chapter. - * + * * @author Zukaritasu * @see {Manga} */ export class Chapter { - /** - * The title of the manga chapter. This may contain the chapter number. - * @type {Image} - */ - title; - /** - * The manga chapter number. The value can be expressed in string or - * number if possible. - * @type {(string | number)} - * @default 0 - */ - number = 0; - /** - * The URL of the manga chapter - * @type {string} - */ - url; - /** - * Array containing the URLs of the images or pages of the manga chapter. - * It can also contain the cover - * @type {string[]} - */ - images = []; - /** - * The cover of the manga chapter. All chapters contain a cover being - * the first page or image. It can be extracted from the first element - * of the array images - * @type {string} - */ - cover; + /** + * The title of the manga chapter. This may contain the chapter number. + * @type {Image} + */ + title; + /** + * The manga chapter number. The value can be expressed in string or + * number if possible. + * @type {(string | number)} + * @default 0 + */ + number = 0; + /** + * The URL of the manga chapter + * @type {string} + */ + url; + /** + * Array containing the URLs of the images or pages of the manga chapter. + * It can also contain the cover + * @type {string[]} + */ + images = []; + /** + * The cover of the manga chapter. All chapters contain a cover being + * the first page or image. It can be extracted from the first element + * of the array images + * @type {string} + */ + cover; } /** @@ -55,48 +55,48 @@ export class Chapter { * most of the manga provider pages do not describe volumes or arcs, so * the 'chapters' property contains all the chapters without the need to * separate them by volumes or arcs. - * + * * @author Zukaritasu * @see {Chapter} * @see '../schemaProviders.js#Image' */ export class Manga { - /** - * The title of the manga - * @type {string} - */ - title; - /** - * The URL of the manga - * @type {string} - */ - url; - /** - * The cover of the manga. It can also be the banner and if that is the - * case use the Image class of the anime providers schema - * @type {Image} - * @see '../schemaProviders.js#Image' - */ - image; - /** - * The year of publication of the manga - * @type {number} - */ - year; - /** - * Description or synopsis of the manga - * @type {string} - */ - synopsis; - /** - * Manga genres - * @type {string[]} - */ - genres = []; - /** - * Manga chapters. Most of the pages of manga do not define volumes - * and arcs, so all the chapters are in a single array - * @type {(Chapter[] | string[])} - */ - chapters = []; -} \ No newline at end of file + /** + * The title of the manga + * @type {string} + */ + title; + /** + * The URL of the manga + * @type {string} + */ + url; + /** + * The cover of the manga. It can also be the banner and if that is the + * case use the Image class of the anime providers schema + * @type {Image} + * @see '../schemaProviders.js#Image' + */ + image; + /** + * The year of publication of the manga + * @type {number} + */ + year; + /** + * Description or synopsis of the manga + * @type {string} + */ + synopsis; + /** + * Manga genres + * @type {string[]} + */ + genres = []; + /** + * Manga chapters. Most of the pages of manga do not define volumes + * and arcs, so all the chapters are in a single array + * @type {(Chapter[] | string[])} + */ + chapters = []; +} diff --git a/src/utils/schemaProviders.js b/src/utils/schemaProviders.js index 84b08a4c..bfd3a12f 100644 --- a/src/utils/schemaProviders.js +++ b/src/utils/schemaProviders.js @@ -56,7 +56,7 @@ export class EpisodeServer { */ url; /** - * + * * @param {string} name server name * @param {string} url server url */ @@ -108,9 +108,9 @@ export class Episode { * @returns Episode number */ static getEpisodeNumber(name) { - if (typeof name === 'string') { + if (typeof name === "string") { for (let i = name.length - 1; i >= 0; i--) { - if (name[i] === ' ') { + if (name[i] === " ") { return parseInt(name.substring(i, name.length).trim()); } } @@ -129,11 +129,11 @@ export class Episode { * @enum {String} */ export const ClimaticStation = { - Summer: Symbol('summer'), - Autumn: Symbol('autumn'), - Winter: Symbol('winter'), - Spring: Symbol('spring'), -} + Summer: Symbol("summer"), + Autumn: Symbol("autumn"), + Winter: Symbol("winter"), + Spring: Symbol("spring"), +}; /** * Anime chronology @@ -224,7 +224,7 @@ export class Anime { * Spanish depending on the location * @type {string[]} */ - genres = [] + genres = []; /** * Climatic station from the anime. If the station is not defined then * the default value is null @@ -286,10 +286,10 @@ export class AnimeSearch { * @param {string} type anime type */ constructor(name, image, url, type = null) { - this.name = name; - this.image = image; - this.url = url; - this.type = type; + this.name = name; + this.image = image; + this.url = url; + this.type = type; } } @@ -314,7 +314,7 @@ export class SearchArray { * @type {(string | number)} page the search page number */ constructor(page) { - this.data = new Array(); - this.page = page + this.data = new Array(); + this.page = page; } } diff --git a/src/utils/shemaNewsProviders.js b/src/utils/shemaNewsProviders.js index 55eb4779..0ba6e698 100644 --- a/src/utils/shemaNewsProviders.js +++ b/src/utils/shemaNewsProviders.js @@ -1,39 +1,39 @@ -export class NewsShema{ - constructor(){ - this.data = new Array() - } +export class NewsShema { + constructor() { + this.data = new Array(); + } } -export class NewsInfo{ - /** - * - * @param {*} title - * @param {*} url - * @param {*} uploadedAt - * @param {*} uploadedBy - * @param {*} banner - * @param {*} intro - * @param {*} full - */ - constructor(title, url, uploadedAt, uploadedBy, banner, intro, full){ - this.title = title; - //this.url = url; - this.uploadedAt = uploadedAt; - this.uploadedBy = uploadedBy; - this.topics = new Array(); - this.banner = banner; - this.preview = { - images: new Array(), - full: full, - } - } +export class NewsInfo { + /** + * + * @param {*} title + * @param {*} url + * @param {*} uploadedAt + * @param {*} uploadedBy + * @param {*} banner + * @param {*} intro + * @param {*} full + */ + constructor(title, url, uploadedAt, uploadedBy, banner, intro, full) { + this.title = title; + //this.url = url; + this.uploadedAt = uploadedAt; + this.uploadedBy = uploadedBy; + this.topics = new Array(); + this.banner = banner; + this.preview = { + images: new Array(), + full: full, + }; + } } -export class Post{ - constructor(title, image, date, url){ - this.title = title; - this.image = image; - this.date = date; - this.url = url - this.topics = new Array(); - } -} \ No newline at end of file +export class Post { + constructor(title, image, date, url) { + this.title = title; + this.image = image; + this.date = date; + this.url = url; + this.topics = new Array(); + } +} diff --git a/src/utils/shemaProvidersExperimental.js b/src/utils/shemaProvidersExperimental.js index 15d673cf..973957a6 100644 --- a/src/utils/shemaProvidersExperimental.js +++ b/src/utils/shemaProvidersExperimental.js @@ -5,26 +5,26 @@ /* Search */ export class AnimeSearch { - /** - * - * @param {*} anime_title - * @param {*} anime_image - * @param {*} link_anime - * @param {*} type - * @param {*} current_page - */ - constructor(anime_title, anime_image, anime_link, type) { - this.anime_title = anime_title; - this.anime_image = anime_image; - this.anime_link = anime_link; - this.type = type; - } + /** + * + * @param {*} anime_title + * @param {*} anime_image + * @param {*} link_anime + * @param {*} type + * @param {*} current_page + */ + constructor(anime_title, anime_image, anime_link, type) { + this.anime_title = anime_title; + this.anime_image = anime_image; + this.anime_link = anime_link; + this.type = type; + } } export class SearchArray { - constructor(page) { - this.data = new Array(); - this.page = page; - } + constructor(page) { + this.data = new Array(); + this.page = page; + } } /* Search */ @@ -32,54 +32,54 @@ export class SearchArray { /* Anime Info */ export class GetAnimeEpisodeList { - /** - * @param {*} episode_title String() - * @param {*} episode_number String() - * @param {*} episode_image String() - * @param {*} episode_link String() - */ - constructor(episode_title, episode_number, episode_image, episode_link) { - this.episode_title = episode_title; - this.episode_number = episode_number; - this.episode_image = episode_image; - this.episode_link = episode_link; - } + /** + * @param {*} episode_title String() + * @param {*} episode_number String() + * @param {*} episode_image String() + * @param {*} episode_link String() + */ + constructor(episode_title, episode_number, episode_image, episode_link) { + this.episode_title = episode_title; + this.episode_number = episode_number; + this.episode_image = episode_image; + this.episode_link = episode_link; + } } export class GetAnimeInfo { - /** - * @param {*} anime_title String() - * @param {*} alternative_title String() - * @param {*} description String() - * @param {*} keywords new Array() - * @param {*} status String() - * @param {*} link String() /anime/provider/ - * @param {*} episode_title String() - * @param {*} episode_number String() - * @param {*} image_espisode String() - * @param {*} type String() - * @param {*} anime_image String() - * @param {*} premiere String() - * @author yako - * @description please use: episode_title: String(), episode_number: String(), image_episode: String(), link_episode: String() - */ - constructor(type = null, anime_image = null, premiere = null) { - this.title = String(); - this.alternative_title = new Array(); - this.type = type; - this.image = new String(); - this.synopsis = [ - { - description: String(), - keywords: new Array(), - status: String(), - premiere: premiere, - chronology: [], - }, - ]; - this.anime_similar = new Array(); - this.episode_list = new Array(); - } + /** + * @param {*} anime_title String() + * @param {*} alternative_title String() + * @param {*} description String() + * @param {*} keywords new Array() + * @param {*} status String() + * @param {*} link String() /anime/provider/ + * @param {*} episode_title String() + * @param {*} episode_number String() + * @param {*} image_espisode String() + * @param {*} type String() + * @param {*} anime_image String() + * @param {*} premiere String() + * @author yako + * @description please use: episode_title: String(), episode_number: String(), image_episode: String(), link_episode: String() + */ + constructor(type = null, anime_image = null, premiere = null) { + this.title = String(); + this.alternative_title = new Array(); + this.type = type; + this.image = new String(); + this.synopsis = [ + { + description: String(), + keywords: new Array(), + status: String(), + premiere: premiere, + chronology: [], + }, + ]; + this.anime_similar = new Array(); + this.episode_list = new Array(); + } } /* Anime Info */ @@ -87,32 +87,32 @@ export class GetAnimeInfo { /* Anime Servers */ export class GetAnimeEpisode { - /** - * @param {*} title episode - * @param {*} next episode - * @param {*} previous episode - * @param {*} list episode - */ - constructor(episode_title,episode_next, episode_prev,episode_list) { - this.episode_title = episode_title; - this.episode_next = episode_next; - this.episode_prev = episode_prev; - this.episode_list = episode_list; - this.servers = new Array(); - } + /** + * @param {*} title episode + * @param {*} next episode + * @param {*} previous episode + * @param {*} list episode + */ + constructor(episode_title, episode_next, episode_prev, episode_list) { + this.episode_title = episode_title; + this.episode_next = episode_next; + this.episode_prev = episode_prev; + this.episode_list = episode_list; + this.servers = new Array(); + } } export class GetAnimeServers { - /** - * @param {*} name server - * @param {*} url server - * @param {*} optional is additional information from some providers - */ - constructor(name, url) { - this.name = name; - this.url = url; - this.optional = new Array(); - } + /** + * @param {*} name server + * @param {*} url server + * @param {*} optional is additional information from some providers + */ + constructor(name, url) { + this.name = name; + this.url = url; + this.optional = new Array(); + } } -/* Anime Servers*/ \ No newline at end of file +/* Anime Servers*/ diff --git a/src/utils/utilities.js b/src/utils/utilities.js index 8e9f2f86..3635bcae 100644 --- a/src/utils/utilities.js +++ b/src/utils/utilities.js @@ -1,6 +1,6 @@ export default { - /** Returns true if argument is different from null and undefined */ - isUsableValue: function(value) { - return value != null && value != undefined; - } -} \ No newline at end of file + /** Returns true if argument is different from null and undefined */ + isUsableValue: function (value) { + return value != null && value != undefined; + }, +}; diff --git a/tsconfig.json b/tsconfig.json index 7be0c2ba..32ab9199 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,29 +25,16 @@ "outDir": "./build", "baseUrl": "./src", "paths": { - "@animetypes/*": [ - "./types/*", - ], - "@providers/*":[ + "@animetypes/*": ["./types/*"], + "@providers/*": [ "./scraper/sites/anime/*", "./scraper/sites/manga/*", "./scraper/sites/doramas/*" - - ], - "@routes/manga/*": [ - "./routes/v1/manga/*" - ], - "@routes/anime/*": [ - "./routes/v1/anime/*" - ], - "@routes/doramas/*": [ - "./routes/v1/doramas/*" ], + "@routes/manga/*": ["./routes/v1/manga/*"], + "@routes/anime/*": ["./routes/v1/anime/*"], + "@routes/doramas/*": ["./routes/v1/doramas/*"] } }, - "exclude": [ - "node_modules", - "build", - "**/*.spec.ts" - ] + "exclude": ["node_modules", "build", "**/*.spec.ts"] } diff --git a/vercel.json b/vercel.json index d7de4abd..ba547441 100644 --- a/vercel.json +++ b/vercel.json @@ -12,4 +12,4 @@ "dest": "src/index.ts" } ] -} \ No newline at end of file +}