import urls from './urls'; import { requestGot } from './requestCall'; import AnimeModel, { Anime } from '../database/models/anime.model'; import crypto from 'crypto'; import util from 'util'; import { redisClient } from '../database/connection'; // @ts-ignore redisClient.get = util.promisify(redisClient.get); /* Utils fuctions - functions to get information from the pages as a parsed JSON or just scrapping the information from the page. */ interface Promo { title: string; image_url: string; video_url: string; } interface Character { id: number; name: string; image: string; role: string; } interface RelatedAnime { title: string; type: string; poster: string; } export const animeExtraInfo = async (mal_id: number) => { let data: any; let broadcast: any; const airDay: any = { mondays: 'Lunes', monday: 'Lunes', tuesdays: 'Martes', tuesday: 'Martes', wednesdays: 'Miércoles', wednesday: 'Miércoles', thursdays: 'Jueves', thursday: 'Jueves', fridays: 'Viernes', friday: 'Viernes', saturdays: 'Sábados', saturday: 'Sábados', sundays: 'Domingos', sunday: 'Domingos', default: 'Sin emisión', }; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `extraInfo_${hashStringMd5(`${mal_id}`)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } data = await requestGot(`${urls.BASE_JIKAN}anime/${mal_id}`, { parse: true, scrapy: false, }); if (data.broadcast) { broadcast = data.broadcast.split('at')[0].trim().toLowerCase() || null; } } catch (err) { return err; } if (airDay.hasOwnProperty(broadcast)) { data.broadcast = airDay[broadcast]; } else { data.broadcast = null; } const formattedObject: any = { titleJapanese: data.title_japanese, source: data.source, totalEpisodes: data.episodes, aired: { from: data.aired.from, to: data.aired.to, }, duration: data.duration.split('per')[0], rank: data.rank, broadcast: data.broadcast, producers: data.producers.map((item: any) => item.name) || null, licensors: data.licensors.map((item: any) => item.name) || null, studios: data.studios.map((item: any) => item.name) || null, openingThemes: data.opening_themes || null, endingThemes: data.ending_themes || null, }; if (formattedObject) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `extraInfo_${hashStringMd5(`${mal_id}`)}`, JSON.stringify(formattedObject), ); /* After 24hrs expire the key. */ redisClient.expireat( `extraInfo_${hashStringMd5(`${mal_id}`)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return formattedObject; } else { return null; } }; export const getAnimeVideoPromo = async (mal_id: number) => { let data: any; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `promoInfo_${hashStringMd5(`${mal_id}`)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } data = await requestGot(`${urls.BASE_JIKAN}anime/${mal_id}/videos`, { parse: true, scrapy: false, }); } catch (err) { return err; } const promo: Promo[] = data.promo.map((item: Promo) => { return { title: item.title, previewImage: item.image_url, videoURL: item.video_url, }; }); if (promo.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `promoInfo_${hashStringMd5(`${mal_id}`)}`, JSON.stringify(promo), ); /* After 24hrs expire the key. */ redisClient.expireat( `promoInfo_${hashStringMd5(`${mal_id}`)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return promo; } else { return null; } }; export const getAnimeCharacters = async (mal_id: number) => { let data: any; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `charactersInfo_${hashStringMd5(`${mal_id}`)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } data = await requestGot( `${urls.BASE_JIKAN}anime/${mal_id}/characters_staff`, { parse: true, scrapy: false }, ); } catch (err) { return err; } const characters: Character[] = data.characters.map((item: any) => { return { id: item.mal_id, name: item.name, image: item.image_url, role: item.role, }; }); if (characters.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `charactersInfo_${hashStringMd5(`${mal_id}`)}`, JSON.stringify(characters), ); /* After 24hrs expire the key. */ redisClient.expireat( `charactersInfo_${hashStringMd5(`${mal_id}`)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return characters; } else { return null; } }; const getPosterAndType = async ( id: string | undefined, mal_id: number | undefined, ) => { if (id) { const queryRes: Anime | null = await AnimeModel.findOne({ id: { $eq: id }, }); return [queryRes?.poster, queryRes?.type]; } if (mal_id) { const queryRes: Anime | null = await AnimeModel.findOne({ mal_id: { $eq: mal_id }, }); return [queryRes?.poster, queryRes?.type]; } return ''; }; export const getRelatedAnimesMAL = async (mal_id: number) => { let $: cheerio.Root; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `getRelatedMAL_${hashStringMd5(`${mal_id}`)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`https://myanimelist.net/anime/${mal_id}`, { parse: false, scrapy: true, }); } catch (err) { return err; } let listRelated: any = {}; let relatedAnimes: RelatedAnime[] = []; if ($('table.anime_detail_related_anime').length > 0) { $('table.anime_detail_related_anime') .find('tbody tr') .each((index: number, element: any) => { if ($(element).find('td').eq(0).text() !== 'Adaptation:') { listRelated[$(element).find('td').eq(1).text()] = $(element) .find('td') .children('a') .attr('href'); } }); for (const related in listRelated) { let posterUrl: any = await getPosterAndType( undefined, listRelated[related].split('/')[2], ); if (posterUrl !== '') { relatedAnimes.push({ title: related, type: posterUrl[1], poster: posterUrl[0], }); } } if (relatedAnimes.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `getRelatedMAL_${hashStringMd5(`${mal_id}`)}`, JSON.stringify(relatedAnimes), ); /* After 24hrs expire the key. */ redisClient.expireat( `getRelatedMAL_${hashStringMd5(`${mal_id}`)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return relatedAnimes; } } else { return []; } }; export const jkanimeInfo = async (id: string | undefined, mal_id: number) => { let $: cheerio.Root; let extraInfo: any; let episodesList: any[] = []; let countEpisodes: string[] = []; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `jkanimeInfo_${hashStringMd5(id!)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_JKANIME}${id}`, { scrapy: true, parse: false, }); /* Extra info of the anime */ extraInfo = (await animeExtraInfo(mal_id)) || undefined; } catch (err) { console.log(err); } if ($!) { countEpisodes = $!('div.anime__pagination a') .map((index: number, element: cheerio.Element) => { return $!(element).text(); }) .get(); const episodesCount: string = countEpisodes[countEpisodes.length - 1].split( '-', )[1]; if (extraInfo) { let broadCastDate = new Date(); let dd: number, mm: string | number, yyyy: number; const airDay: any = { Lunes: 1, Martes: 2, Miércoles: 3, Jueves: 4, Viernes: 5, Sábados: 6, Domingos: 7, 'Sin emisión': 'default', }; if (!extraInfo.aired.to) { if (airDay.hasOwnProperty(extraInfo.broadcast)) { if (broadCastDate.getDay() < airDay[extraInfo.broadcast]) { for ( let i = broadCastDate.getDay(); i < airDay[extraInfo.broadcast]; i++ ) { broadCastDate.setDate(broadCastDate.getDate() + 1); } } else { let counter = broadCastDate.getDay() + 1; /* Adding one because of the day */ broadCastDate.setDate(broadCastDate.getDate() + 1); while (counter !== airDay[extraInfo.broadcast]) { if (counter === 7) { counter = 0; } broadCastDate.setDate(broadCastDate.getDate() + 1); counter++; } } dd = broadCastDate.getDate(); mm = broadCastDate.getMonth() + 1 < 10 ? `0${broadCastDate.getMonth() + 1}` : broadCastDate.getMonth() + 1; yyyy = broadCastDate.getFullYear(); episodesList.push({ nextEpisodeDate: `${yyyy}-${mm}-${dd}`, }); } } } for (let i = 1; i <= parseInt(episodesCount); i++) { episodesList.push({ episode: i, id: `${id}/${i}`, }); } if (episodesList.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `jkanimeInfo_${hashStringMd5(id!)}`, JSON.stringify(episodesList), ); /* After 24hrs expire the key. */ redisClient.expireat( `jkanimeInfo_${hashStringMd5(id!)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return episodesList; } else { return undefined; } } else { return undefined; } }; export const monoschinosInfo = async ( id: string | undefined, mal_id: number, ) => { let $: cheerio.Root; let episodeList: any[] = []; let extraInfo: any; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `monoschinosInfo_${hashStringMd5(id!)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_MONOSCHINOS}anime/${id}`, { scrapy: true, parse: false, }); /* Extra info of the anime */ extraInfo = (await animeExtraInfo(mal_id)) || undefined; } catch (err) { console.log(err); } if ($!) { if (extraInfo) { let broadCastDate = new Date(); let dd: number, mm: string | number, yyyy: number; const airDay: any = { Lunes: 1, Martes: 2, Miércoles: 3, Jueves: 4, Viernes: 5, Sábados: 6, Domingos: 7, 'Sin emisión': 'default', }; if (!extraInfo.aired.to) { if (airDay.hasOwnProperty(extraInfo.broadcast)) { if (broadCastDate.getDay() < airDay[extraInfo.broadcast]) { for ( let i = broadCastDate.getDay(); i < airDay[extraInfo.broadcast]; i++ ) { broadCastDate.setDate(broadCastDate.getDate() + 1); } } else { let counter = broadCastDate.getDay() + 1; /* Adding one because of the day */ broadCastDate.setDate(broadCastDate.getDate() + 1); while (counter !== airDay[extraInfo.broadcast]) { if (counter === 7) { counter = 0; } broadCastDate.setDate(broadCastDate.getDate() + 1); counter++; } } dd = broadCastDate.getDate(); mm = broadCastDate.getMonth() + 1 < 10 ? `0${broadCastDate.getMonth() + 1}` : broadCastDate.getMonth() + 1; yyyy = broadCastDate.getFullYear(); episodeList.push({ nextEpisodeDate: `${yyyy}-${mm}-${dd}`, }); } } } $!('.SerieCaps a').each((index: number, element: cheerio.Element) => { let episode: number; $(element) .attr('href') ?.split('-') .forEach((item: any) => { if (!isNaN(item)) { episode = parseInt(item); } }); episodeList.push({ episode: episode!, id: `${$(element).attr('href')?.split('/')[3]}/${ $(element).attr('href')?.split('/')[4] }`, }); }); if (episodeList.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `monoschinosInfo_${hashStringMd5(id!)}`, JSON.stringify(episodeList), ); /* After 24hrs expire the key. */ redisClient.expireat( `monoschinosInfo_${hashStringMd5(id!)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return episodeList; } else { return undefined; } } else { return undefined; } }; export const tioanimeInfo = async (id: string | undefined, mal_id: number) => { let $: cheerio.Root; let episodesList: any[] = []; let anime_eps: string[] = []; let extraInfo: any; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `tioanimeInfo_${hashStringMd5(id!)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_TIOANIME}anime/${id}`, { scrapy: true, parse: false, }); /* Extra info of the anime */ extraInfo = (await animeExtraInfo(mal_id)) || undefined; } catch (err) { console.log(err); } if ($!) { if (extraInfo) { let broadCastDate = new Date(); let dd: number, mm: string | number, yyyy: number; const airDay: any = { Lunes: 1, Martes: 2, Miércoles: 3, Jueves: 4, Viernes: 5, Sábados: 6, Domingos: 7, 'Sin emisión': 'default', }; if (!extraInfo.aired.to) { if (airDay.hasOwnProperty(extraInfo.broadcast)) { if (broadCastDate.getDay() < airDay[extraInfo.broadcast]) { for ( let i = broadCastDate.getDay(); i < airDay[extraInfo.broadcast]; i++ ) { broadCastDate.setDate(broadCastDate.getDate() + 1); } } else { let counter = broadCastDate.getDay() + 1; /* Adding one because of the day */ broadCastDate.setDate(broadCastDate.getDate() + 1); while (counter !== airDay[extraInfo.broadcast]) { if (counter === 7) { counter = 0; } broadCastDate.setDate(broadCastDate.getDate() + 1); counter++; } } dd = broadCastDate.getDate(); mm = broadCastDate.getMonth() + 1 < 10 ? `0${broadCastDate.getMonth() + 1}` : broadCastDate.getMonth() + 1; yyyy = broadCastDate.getFullYear(); episodesList.push({ nextEpisodeDate: `${yyyy}-${mm}-${dd}`, }); } } } const scripts: cheerio.Element[] = $!('script').toArray(); for (const script of scripts) { if ($!(script).html()!.includes('anime_info')) { anime_eps = JSON.parse( $!(script).html()!.split('var episodes = ')[1].split(';')[0], ); } } for (const episode of anime_eps) { episodesList.push({ episode: episode, id: `ver/${id}-${episode}`, }); } if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `tioanimeInfo_${hashStringMd5(id!)}`, JSON.stringify(episodesList), ); /* After 24hrs expire the key. */ redisClient.expireat( `tioanimeInfo_${hashStringMd5(id!)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return episodesList; } else { return undefined; } }; export const videoServersMonosChinos = async (id: string) => { let $: cheerio.Root; let videoServers: any[] = []; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `videoServersMonosChinos_${hashStringMd5(id)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_MONOSCHINOS}${id}`, { scrapy: true, parse: false, }); } catch (err) { return err; } let videoNames: string[] = $('.TPlayerNv li') .map((index: number, element: cheerio.Element) => { return $(element).attr('title'); }) .get(); videoServers.push({ id: videoNames[0].toLowerCase(), url: decodeURIComponent( $('.TPlayer div iframe').attr('src')?.split('url=')[1]!, ).split('&id')[0], direct: false, }); const videoContainer: any = $('.TPlayer div').text(); $(videoContainer).each((index: number, element: cheerio.Element) => { let video: any = $(element).attr('src'); if (video) { video = video.split('url=')[1]; video = decodeURIComponent(video); video = video.split('&id')[0]; } if (video) { videoNames.forEach((value: string) => { if (video.includes(value.toLowerCase())) { videoServers.push({ id: value.toLowerCase(), url: video, direct: false, }); } }); } }); if (videoServers.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `videoServersMonosChinos_${hashStringMd5(id)}`, JSON.stringify(videoServers), ); /* After 24hrs expire the key. */ redisClient.expireat( `videoServersMonosChinos_${hashStringMd5(id)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return videoServers; } else { return null; } }; export const videoServersTioAnime = async (id: string) => { let $: cheerio.Root; let servers: any; let videoServers: any[] = []; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `videoServersTioAnime_${hashStringMd5(id)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_TIOANIME}${id}`, { scrapy: true, parse: false, }); } catch (err) { return err; } const scripts: cheerio.Element[] = $('script').toArray(); for (const script of scripts) { if ($(script).html()!.includes('var videos =')) { servers = JSON.parse( $(script).html()!.split('var videos = ')[1].split(';')[0], ); } } for (const server of servers) { videoServers.push({ id: server[0].toLowerCase(), url: server[1], direct: false, }); } if (videoServers.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `videoServersTioAnime_${hashStringMd5(id)}`, JSON.stringify(videoServers), ); /* After 24hrs expire the key. */ redisClient.expireat( `videoServersTioAnime_${hashStringMd5(id)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return videoServers; } else { return null; } }; export const videoServersJK = async (id: string) => { let $: cheerio.Root; let servers: any = {}; let script: string | null = ''; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `videoServersJK_${hashStringMd5(id)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = await requestGot(`${urls.BASE_JKANIME}${id}`, { scrapy: true, parse: false, }); } catch (err) { return err; } const serverNames: string[] = $('div.bg-servers a') .map((index: number, element: cheerio.Element) => { return $(element).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'); if (serverList.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `videoServersJK_${hashStringMd5(id!)}`, JSON.stringify(serverList), ); /* After 24hrs expire the key. */ redisClient.expireat( `videoServersJK_${hashStringMd5(id!)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return serverList; } else { return null; } }; async function desuServerUrl(url: string) { let $: cheerio.Root; try { if (redisClient.connected) { const resultQueryRedis: any = await redisClient.get( `desuServerUrl_${hashStringMd5(url)}`, ); if (resultQueryRedis) { const resultRedis: any = JSON.parse(resultQueryRedis); return resultRedis; } } $ = 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]; if (result.length > 0) { if (redisClient.connected) { /* Set the key in the redis cache. */ redisClient.set( `desuServerUrl_${hashStringMd5(url)}`, JSON.stringify(result), ); /* After 24hrs expire the key. */ redisClient.expireat( `desuServerUrl_${hashStringMd5(url)}`, parseInt(`${+new Date() / 1000}`, 10) + 7200, ); } return result; } else { return null; } } export const structureThemes = async (body: any, indv: boolean) => { let themes: any[] = []; if (indv === true) { return { title: body.title, year: body.year, themes: await getThemesData(body.themes), }; } else { for (let i = 0; i <= body.length - 1; i++) { themes.push({ title: body[i].title, year: body[i].year, themes: await getThemesData(body[i].themes), }); } return themes; } }; function getThemesData(themes: any[]): any { let items: any[] = []; for (let i = 0; i <= themes.length - 1; i++) { items.push({ title: themes[i].name.split('"')[1] || 'Remasterización', type: themes[i].type, episodes: themes[i].episodes !== '' ? themes[i].episodes : null, notes: themes[i].notes !== '' ? themes[i].notes : null, video: themes[i].link, }); } return items.filter(x => x.title !== 'Remasterización'); } export function getThemes(themes: any[]) { return themes.map((item: any) => ({ name: item.themeName, title: item.themeType, link: item.mirror.mirrorURL, })); } export const imageUrlToBase64 = async (url: string) => { let img: any = await requestGot(url); return img.rawBody.toString('base64'); }; export function hashStringMd5(string: string) { return crypto.createHash('md5').update(string).digest('hex'); }