From afa20d36df1a912ab923f554a1cc444f0960f3ec Mon Sep 17 00:00:00 2001 From: capitanwesler Date: Sat, 13 Mar 2021 20:25:53 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=BE=20Migrating=20getAnimeServers=20an?= =?UTF-8?q?d=20getEpisodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/controllers/AnimeController.ts | 98 +++++++++----- src/routes.ts | 5 + src/utils/util.ts | 203 +++++++++++++++++++++++++++-- yarn.lock | 46 ++----- 5 files changed, 278 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index a54a132..2bcc140 100644 --- a/package.json +++ b/package.json @@ -59,9 +59,9 @@ "ts-node-dev": "^1.1.1" }, "devDependencies": { + "@types/cheerio": "^0.22.24", "@types/cors": "^2.8.10", "@types/express": "^4.17.11", - "@types/got": "^9.6.11", "@types/node": "^14.14.31", "@types/tough-cookie": "^4.0.0", "@typescript-eslint/eslint-plugin": "^4.15.2", diff --git a/src/controllers/AnimeController.ts b/src/controllers/AnimeController.ts index a048a62..1468511 100644 --- a/src/controllers/AnimeController.ts +++ b/src/controllers/AnimeController.ts @@ -1,6 +1,8 @@ import { NextFunction, Request, Response } from 'express'; import { requestGot } from '../utils/requestCall'; +import { animeFlvInfo, jkanimeInfo, videoServersJK } from '../utils/util'; import { transformUrlServer } from '../utils/transformerUrl'; +import AnimeModel, { Anime as ModelA } from '../database/models/anime.model'; import urls from '../utils/urls'; /* @@ -189,42 +191,80 @@ export default class AnimeController { const { type, page, url } = req.params; let data: any; + if (['movie', 'ova', 'tv', 'special'].indexOf(type) !== -1) { + try { + data = await requestGot( + `${urls.BASE_ANIMEFLV_JELU}${ + url.charAt(0).toUpperCase() + url.slice(1) + }/${type}/${page}`, + { + parse: true, + scrapy: false, + }, + ); + } catch (err) { + return next(err); + } + + const animes: Movie[] = data[url.toLowerCase()].map((item: any) => { + return { + id: item.id, + title: item.title, + type: url.toLowerCase(), + page: page, + banner: item.banner, + image: item.poster, + synopsis: item.synopsis, + status: item.debut, + rate: item.rating, + genres: item.genres.map((genre: any) => genre), + episodes: item.episodes.map((episode: any) => episode), + }; + }); + + if (animes.length > 0) { + res.status(200).json({ + animes, + }); + } else { + res.status(500).json({ message: 'Aruppi lost in the shell' }); + } + } else { + next(); + } + } + + async getEpisodes(req: Request, res: Response, next: NextFunction) { + const { title } = req.params; + let searchAnime: ModelA | null; + try { - data = await requestGot( - `${urls.BASE_ANIMEFLV_JELU}${ - url.charAt(0).toUpperCase() + url.slice(1) - }/${type}/${page}`, - { - parse: true, - scrapy: false, - }, - ); + searchAnime = await AnimeModel.findOne({ title: { $eq: title } }); } catch (err) { return next(err); } - const animes: Movie[] = data[url.toLowerCase()].map((item: any) => { - return { - id: item.id, - title: item.title, - type: url.toLowerCase(), - page: page, - banner: item.banner, - image: item.poster, - synopsis: item.synopsis, - status: item.debut, - rate: item.rating, - genres: item.genres.map((genre: any) => genre), - episodes: item.episodes.map((episode: any) => episode), - }; - }); + if (!searchAnime?.jkanime) { + res.status(200).json({ episodes: await animeFlvInfo(searchAnime?.id) }); + } else { + res.status(200).json({ episodes: await jkanimeInfo(searchAnime?.id) }); + } + } - if (animes.length > 0) { - res.status(200).json({ - animes, - }); + async getServers(req: Request, res: Response, next: NextFunction) { + const { id } = req.params; + + if (isNaN(parseInt(id.split('/')[0]))) { + res.status(200).json({ servers: await videoServersJK(id) }); } else { - res.status(500).json({ message: 'Aruppi lost in the shell' }); + const data: any = await requestGot( + `${urls.BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`, + { parse: true, scrapy: false }, + ); + + console.log(data); + + res.status(200).json({ servers: await transformUrlServer(data.servers) }); } } } diff --git a/src/routes.ts b/src/routes.ts index cd16a60..48be5ea 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -74,6 +74,11 @@ routes.get('/api/v4/top/:type/:page/:subtype?/', animeController.top); routes.get('/api/v4/allAnimes', animeController.getAllAnimes); routes.get('/api/v4/lastEpisodes', animeController.getLastEpisodes); routes.get('/api/v4/:url/:type/:page', animeController.getContent); +routes.get('/api/v4/getEpisodes/:title', animeController.getEpisodes); +routes.get( + '/api/v4/getAnimeServers/:id([^/]+/[^/]+)', + animeController.getServers, +); /* Directory Controller */ routes.get( diff --git a/src/utils/util.ts b/src/utils/util.ts index cbe1cd2..d6b105e 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -150,10 +150,13 @@ const getPosterAndType = async ( }; export const getRelatedAnimesFLV = async (id: string) => { - const $ = await requestGot(`${urls.BASE_ANIMEFLV}/anime/${id}`, { - parse: false, - scrapy: true, - }); + const $: cheerio.Root = await requestGot( + `${urls.BASE_ANIMEFLV}/anime/${id}`, + { + parse: false, + scrapy: true, + }, + ); let listRelated: any = {}; let relatedAnimes: RelatedAnime[] = []; @@ -184,10 +187,13 @@ export const getRelatedAnimesFLV = async (id: string) => { }; export const getRelatedAnimesMAL = async (mal_id: number) => { - const $ = await requestGot(`https://myanimelist.net/anime/${mal_id}`, { - parse: false, - scrapy: true, - }); + const $: cheerio.Root = await requestGot( + `https://myanimelist.net/anime/${mal_id}`, + { + parse: false, + scrapy: true, + }, + ); let listRelated: any = {}; let relatedAnimes: RelatedAnime[] = []; @@ -196,7 +202,7 @@ export const getRelatedAnimesMAL = async (mal_id: number) => { $('table.anime_detail_related_anime') .find('tbody tr') .each((index: number, element: any) => { - if ($(element).find('td').eq(0) !== 'Adaptation:') { + if ($(element).find('td').eq(0).text() !== 'Adaptation:') { listRelated[$(element).find('td').eq(1).text()] = $(element) .find('td') .children('a') @@ -224,3 +230,182 @@ export const getRelatedAnimesMAL = async (mal_id: number) => { return []; } }; + +export const animeFlvInfo = async (id: string | undefined) => { + let $: cheerio.Root; + let anime_info: string[] = []; + let anime_eps: string[] = []; + let nextEpisodeDate: string | null; + let episodes: any[] = []; + + try { + $ = await requestGot(`${urls.BASE_ANIMEFLV}/anime/${id}`, { + scrapy: true, + parse: false, + }); + } catch (err) { + return err; + } + + const scripts: cheerio.Element[] = $('script').toArray(); + + for (const script of scripts) { + if ($(script).html()!.includes('anime_info')) { + anime_info = JSON.parse( + $(script).html()!.split('var anime_info = ')[1].split(';\n')[0], + ); + + anime_eps = JSON.parse( + $(script).html()!.split('var episodes = ')[1].split(';')[0], + ); + } + } + + if (anime_info.length === 4) { + nextEpisodeDate = anime_info[3]; + } else { + nextEpisodeDate = null; + } + + episodes.push({ nextEpisodeDate }); + + for (const episode of anime_eps) { + episodes.push({ + episode: episode[0], + id: `${episode[1]}/${id}-${episode[0]}`, + }); + } + + return episodes; +}; + +export const jkanimeInfo = async (id: string | undefined) => { + let $: cheerio.Root; + let nextEpisodeDate: string | null; + let imageLink: string | undefined; + let episodesList: any[] = []; + let countEpisodes: string[] = []; + + try { + $ = await requestGot(`${urls.BASE_JKANIME}${id}`, { + scrapy: true, + parse: false, + }); + } catch (err) { + return err; + } + + countEpisodes = $('div.navigation a') + .map((index: number, element: cheerio.Element) => { + return $(element).text(); + }) + .get(); + + const episodesCount: string = countEpisodes[countEpisodes.length - 1].split( + '-', + )[1]; + + nextEpisodeDate = $('div.proxep p').text() || null; + + episodesList.push({ nextEpisodeDate }); + + for (let i = 1; i <= parseInt(episodesCount); i++) { + episodesList.push({ + episode: i, + id: `${id}/${i}`, + }); + } + + return episodesList; +}; + +export const videoServersJK = async (id: string) => { + let $: cheerio.Root; + let servers: any = {}; + let script: string | null = ''; + + try { + $ = await requestGot(`${urls.BASE_JKANIME}${id}`, { + scrapy: true, + parse: false, + }); + } catch (err) { + return err; + } + + const serverNames: string[] = $('div#reproductor-box li') + .map((index: number, element: cheerio.Element) => { + return $(element).find('a').text(); + }) + .get(); + + $('script').each((index: number, element: cheerio.Element) => { + if ($(element).html()!.includes('var video = [];')) { + script = $(element).html(); + } + }); + + try { + let videoUrls = script.match(/(?<=src=").*?(?=[\*"])/gi); + + for (let i = 0; i < serverNames.length; i++) { + servers[serverNames[i]] = videoUrls![i]; + } + } catch (err) { + return null; + } + + let serverList = []; + + for (let server in servers) { + if (serverNames[serverNames.indexOf(server)].toLowerCase() === 'desu') { + serverList.push({ + id: serverNames[serverNames.indexOf(server)].toLowerCase(), + url: + (await desuServerUrl(servers[server])) !== null + ? await desuServerUrl(servers[server]) + : servers[server], + direct: false, + }); + } else { + serverList.push({ + id: serverNames[serverNames.indexOf(server)].toLowerCase(), + url: servers[server], + direct: false, + }); + } + } + + serverList = serverList.filter(x => x.id !== 'xtreme s' && x.id !== 'desuka'); + + return serverList; +}; + +async function desuServerUrl(url: string) { + let $: cheerio.Root; + + try { + $ = await requestGot(url, { scrapy: true, parse: false }); + } catch (err) { + return err; + } + + let script: string | null = ''; + + $('script').each((index: number, element: cheerio.Element) => { + if ($(element).html()!.includes('var parts = {')) { + if ($(element).html()) { + script = $(element).html(); + } else { + return null; + } + } + }); + + let result = script + .match(/swarmId: '(https:\/\/\S+)'/gi)! + .toString() + .split("'")[1]; + + return result; +} diff --git a/yarn.lock b/yarn.lock index 869c6b0..bfec2d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,6 +81,13 @@ "@types/node" "*" "@types/responselike" "*" +"@types/cheerio@^0.22.24": + version "0.22.24" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.24.tgz#fcee47074aa221ac0f31ede0c72c0800bf3bf0aa" + integrity sha512-iKXt/cwltGvN06Dd6zwQG1U35edPwId9lmcSeYfcxSNvvNg4vysnFB+iBQNjj06tSVV7MBj0GWMQ7dwb4Z+p8Q== + dependencies: + "@types/node" "*" + "@types/connect@*": version "3.4.34" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" @@ -117,15 +124,6 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/got@^9.6.11": - version "9.6.11" - resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.11.tgz#482b402cc5ee459481aeeadb08142ebb1a9afb26" - integrity sha512-dr3IiDNg5TDesGyuwTrN77E1Cd7DCdmCFtEfSGqr83jMMtcwhf/SGPbN2goY4JUWQfvxwY56+e5tjfi+oXeSdA== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - "@types/http-cache-semantics@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" @@ -201,7 +199,7 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== -"@types/tough-cookie@*", "@types/tough-cookie@^4.0.0": +"@types/tough-cookie@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== @@ -391,11 +389,6 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -598,13 +591,6 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -combined-stream@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - compose-middleware@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/compose-middleware/-/compose-middleware-5.0.1.tgz#4c0adb751afdde45d637a7a0b361095e510fafff" @@ -761,11 +747,6 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - denque@^1.4.1: version "1.5.0" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" @@ -1272,15 +1253,6 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -1810,7 +1782,7 @@ mime-db@1.46.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== -mime-types@^2.1.12, mime-types@~2.1.24: +mime-types@~2.1.24: version "2.1.29" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==