v3.0.0 - Full Directory - Supports v2.6.8

This commit is contained in:
Jéluchu 2020-08-18 09:44:04 +02:00
parent cfa6b05917
commit 1e357ad206
14 changed files with 2538 additions and 2849 deletions

View File

@ -1,4 +1,4 @@
# **Aruppi API** (v2.7.1)
# **Aruppi API** (v3.0.0)
> This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture
>
@ -69,6 +69,12 @@ But if you need to see the code or the operation of the old version you can do i
- [Aruppi API GitHub (v1) [Deprecated]](https://github.com/aruppi/aruppi-api-v1)
- [Aruppi API Custom Link(v1) [Deprecated]](https://aruppi.herokuapp.com/api/Aruppi/)
## Countdown to deprecation of v2 API
Aruppi has grown since it was launched and we need to continue improving the application along with the services to be able to give new features.
At this time version 2.6.8 will remain functional until Aruppi App users fully migrate to version 1.5.0 of the app
## **:handshake: Contributing**
- Fork it!

2820
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "aruppi",
"version": "2.7.1",
"version": "3.0.0",
"description": "Aruppi is a custom API to obtain data from the Japanese culture for the mobile app",
"main": "./src/api/api.js",
"scripts": {

View File

@ -13,32 +13,32 @@ router.get('/', (req, res) => {
credits: 'The bitch loves APIs that offers data to Aruppi App',
entries: [
{
'Schedule': '/api/v2/schedule/:day',
'Top': '/api/v2/top/:type/:subtype/:page',
'AllAnimes': '/api/v2/allAnimes',
'Anitakume': '/api/v2/anitakume',
'News': '/api/v2/news',
'Season': '/api/v2/season/:year/:type',
'All Seasons': '/api/v2/allSeasons',
'All Directory': '/api/v2/allDirectory',
'Genres': '/api/v2/getByGenres/:genre/:order/:page?',
'Futures Seasons': '/api/v2/laterSeasons',
'LastEpisodes': '/api/v2/lastEpisodes',
'Movies': '/api/v2/movies/:type/:page',
'Ovas': '/api/v2/ovas/:type/:page',
'Specials': '/api/v2/specials/:type/:page',
'Tv': '/api/v2/tv/:type/:page',
'MoreInfo': '/api/v2/moreInfo/:title',
'GetAnimeServers': '/api/v2/getAnimeServers/:id',
'Search': '/api/v2/search/:title',
'Images': '/api/v2/images/:query',
'Videos': '/api/v2/videos/:channelId',
'Radios': '/api/v2/radio',
'All Themes': '/api/v2/allThemes',
'Themes': '/api/v2/themes/:title',
'Year Themes': '/api/v2/themesYear/:year?',
'Random Theme': '/api/v2/randomTheme',
'Artists Theme': '/api/v2/artists/:id?'
'Schedule': '/api/v3/schedule/:day',
'Top': '/api/v3/top/:type/:subtype/:page',
'AllAnimes': '/api/v3/allAnimes',
'Anitakume': '/api/v3/anitakume',
'News': '/api/v3/news',
'Season': '/api/v3/season/:year/:type',
'All Seasons': '/api/v3/allSeasons',
'All Directory': '/api/v3/allDirectory',
'Genres': '/api/v3/getByGenres/:genre/:order/:page?',
'Futures Seasons': '/api/v3/laterSeasons',
'LastEpisodes': '/api/v3/lastEpisodes',
'Movies': '/api/v3/movies/:type/:page',
'Ovas': '/api/v3/ovas/:type/:page',
'Specials': '/api/v3/specials/:type/:page',
'Tv': '/api/v3/tv/:type/:page',
'MoreInfo': '/api/v3/moreInfo/:title',
'GetAnimeServers': '/api/v3/getAnimeServers/:id',
'Search': '/api/v3/search/:title',
'Images': '/api/v3/images/:query',
'Videos': '/api/v3/videos/:channelId',
'Radios': '/api/v3/radio',
'All Themes': '/api/v3/allThemes',
'Themes': '/api/v3/themes/:title',
'Year Themes': '/api/v3/themesYear/:year?',
'Random Theme': '/api/v3/randomTheme',
'Artists Theme': '/api/v3/artists/:id?'
}
]
});

View File

@ -7,6 +7,8 @@ const version = require('./../package.json').version;
const middlewares = require('./middlewares/index').middleware;
const api = require('./api');
const api_legacy = require('./v2/api');
const app = express();
app.use(helmet());
@ -37,7 +39,8 @@ app.get('/api/v1', (req, res) => {
});
});
app.use('/api/v2', api);
app.use('/api/v2', api_legacy);
app.use('/api/v3', api);
app.use(middlewares);

View File

@ -429,7 +429,6 @@ const getThemes = async (themes) => {
name: doc.themeName,
type: doc.themeType,
video: doc.mirror.mirrorURL
`${BASE_ANIMEFLV}anime/${id}`
}));
};

606
src/v2/api/api.js Normal file
View File

@ -0,0 +1,606 @@
const rss = require('rss-to-json');
const {
homgot
} = require('./apiCall');
const {
jkanimeInfo,
animeflvGenres,
animeflvInfo,
imageUrlToBase64,
getAnimeCharacters,
getAnimeVideoPromo,
animeExtraInfo,
searchAnime,
transformUrlServer,
obtainPreviewNews,
structureThemes,
getAnimes,
getDirectory,
helper,
videoServersJK,
getThemes
} = require('../utils');
const ThemeParser = require('../utils/animetheme');
const parserThemes = new ThemeParser()
const {
BASE_ANIMEFLV_JELU, BASE_JIKAN, BASE_IVOOX, BASE_QWANT, BASE_YOUTUBE, GENRES_URL, BASE_THEMEMOE
} = require('./urls');
const schedule = async (day) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}schedule/${day.current}`, options);
const body = data[day.current];
const promises = []
body.map(doc =>{
promises.push({
title: doc.title,
malid: doc.mal_id,
image: doc.image_url
});
});
return promises;
};
const top = async (type, subtype, page) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}top/${type}/${page}/${subtype}`, options);
return data.top;
};
const getAllAnimes = async () =>{
let data = await getAnimes()
return data.map(item => ({
index: item[0],
animeId: item[3],
title: item[1],
id: item[2],
type: item[4]
}));
};
const getAllDirectory = async () =>{ return await getDirectory(); };
const getAnitakume = async () =>{
const promises = []
await rss.load(BASE_IVOOX).then(rss => {
const body = JSON.parse(JSON.stringify(rss, null, 3)).items
body.map(doc =>{
let time = new Date(doc.created)
const monthNames = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
let day = time.getDate()
let month = monthNames[time.getMonth()]
let year = time.getFullYear()
let date
if(month < 10){
date = `${day} de 0${month} de ${year}`
}else{
date = `${day} de ${month} de ${year}`
}
promises.push({
title: doc.title,
duration: doc.itunes_duration,
created: date,
mp3: doc.enclosures.map(x => x.url)
});
});
});
return promises;
};
const getNews = async (pageRss) =>{
const promises = []
for(let i = 0; i <= pageRss.length -1; i++) {
await rss.load(pageRss[i].url).then(rss => {
const body = JSON.parse(JSON.stringify(rss, null, 3)).items
body.map(doc => {
promises.push({
title: doc.title,
url: doc.link,
author: pageRss[i].author,
thumbnail: obtainPreviewNews(doc[pageRss[i].content]),
content: doc[pageRss[i].content]
});
});
});
}
return promises;
};
const season = async (year, type) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/${year}/${type}`, options);
let body = data.anime;
const promises = []
body.map(doc =>{
promises.push({
title: doc.title,
malid: doc.mal_id,
image: doc.image_url,
genres: doc.genres.map(x => x.name)
});
});
return promises;
};
const allSeasons = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/archive`, options);
let body = data.archive;
const promises = []
body.map(doc =>{
promises.push({
year: doc.year,
seasons: doc.seasons,
});
});
return promises;
};
const laterSeasons = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/later`, options);
let body = data.anime;
const promises = []
body.map(doc =>{
promises.push({
malid: doc.mal_id,
title: doc.title,
image: doc.image_url
});
});
return promises;
};
const getLastEpisodes = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}LatestEpisodesAdded`, options);
let body = data.episodes;
const promises = []
body.map(doc => {
promises.push(helper().then(async () => ({
id: doc.id,
title: doc.title,
image: doc.poster,
episode: doc.episode,
servers: await transformUrlServer(JSON.parse(JSON.stringify(doc.servers)))
})));
});
return Promise.all(promises);
};
const getSpecials = async (type, subType, page) =>{
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}${type.url}/${subType}/${page}`, options);
let body = data[type.prop];
const promises = []
body.map(doc =>{
promises.push({
id: doc.id,
title: doc.title,
type: doc.type,
banner: doc.banner,
image: doc.poster,
synopsis: doc.synopsis,
status: doc.debut,
rate: doc.rating,
genres: doc.genres.map(x => x),
episodes: doc.episodes.map(x => x)
});
});
return promises;
};
const getMoreInfo = async (title) =>{
const promises = []
let animeTitle = ''
let animeId = ''
let animeType = ''
let animeIndex = ''
let seriesTitle
let position
const jkAnimeTitles = [
{ title: 'The God of High School', id: 'the-god-of-high-school' },
{ title: 'Kami no Tou', id: 'kami-no-tou' },
{ title: 'BNA', id: 'bna' },
{ title: 'Ansatsu Kyoushitsu (TV)', id: 'ansatsu-kyoushitsu-tv' },
{ title: 'Ansatsu Kyoushitsu (TV) 2nd Season', id: 'ansatsu-kyoushitsu-tv-2nd-season' }
];
const jkMyAnimetitles = [
{ jkanime: 'Ansatsu Kyoushitsu (TV)', myanimelist: 'Ansatsu Kyoushitsu'},
{ jkanime: 'Ansatsu Kyoushitsu (TV) 2nd Season', myanimelist: 'Ansatsu Kyoushitsu 2nd Season' }
];
let jkanime = false
let jkanimeID
let jkanimeName
for (let name in jkAnimeTitles) {
if (title === jkAnimeTitles[name].title) {
jkanime = true
jkanimeID = jkAnimeTitles[name].id
for (let name in jkMyAnimetitles) {
if (title === jkMyAnimetitles[name].jkanime || title === jkMyAnimetitles[name].myanimelist) {
jkanimeName = jkMyAnimetitles[name].myanimelist
position = name
}
}
if (jkanimeName === undefined) {
jkanimeName = jkAnimeTitles[name].title
}
}
}
if (jkanime === false) {
const titles = [
{ animeflv: 'Kaguya-sama wa Kokurasetai: Tensai-tachi no Renai Zunousen 2nd Season', myanimelist: 'Kaguya-sama wa Kokurasetai?: Tensai-tachi no Renai Zunousen', alternative: 'Kaguya-sama wa Kokurasetai'},
{ animeflv: 'Naruto Shippuden', myanimelist: 'Naruto: Shippuuden' },
{ animeflv: 'Rock Lee no Seishun Full-Power Ninden', myanimelist: 'Naruto SD: Rock Lee no Seishun Full-Power Ninden' },
{ animeflv: 'BAKI: dai reitaisai-hen', myanimelist: 'Baki 2nd Season' },
{ animeflv: 'Hitoribocchi no ○○ Seikatsu', myanimelist: 'Hitoribocchi no Marumaru Seikatsu' },
{ animeflv: 'Nekopara (TV)', myanimelist: 'Nekopara' },
{ animeflv: 'Black Clover (TV)', myanimelist: 'Black Clover' }
];
for (let name in titles) {
if (title === titles[name].animeflv || title === titles[name].myanimelist || title === titles[name].alternative) {
seriesTitle = titles[name].animeflv
position = name
}
}
if (seriesTitle === undefined) {
seriesTitle = title
}
await getAllAnimes().then(animes => {
for (const i in animes) {
if (animes[i].title.split('\t')[0] === seriesTitle.split('\t')[0] ||
animes[i].title === `${seriesTitle} (TV)` ||
animes[i].title.includes(seriesTitle.split('○')[0])
) {
if (animes[i].title.includes('(TV)', 0)) { animeTitle = animes[i].title.split('\t')[0].replace(' (TV)', '') }
else { animeTitle = animes[i].title.split('\t')[0] }
animeId = animes[i].id
animeIndex = animes[i].index
animeType = animes[i].type.toLowerCase()
if (position !== undefined) {
seriesTitle = titles[position].myanimelist
}
break;
}
}
});
try{
if (animeType === 'tv') {
promises.push(await animeflvInfo(animeId, animeIndex).then(async extra => ({
title: animeTitle || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
moreInfo: await animeExtraInfo(seriesTitle).then(info =>{
return info || null
}),
promo: await getAnimeVideoPromo(seriesTitle).then(promo =>{
return promo || null
}),
characters: await getAnimeCharacters(seriesTitle).then(characters =>{
return characters || null
})
})));
} else {
promises.push(await animeflvInfo(animeId).then(async extra => ({
title: animeTitle || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
})));
}
}catch(err){
console.log(err)
}
} else {
promises.push(await jkanimeInfo(jkanimeID).then(async extra => ({
title: jkanimeName || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
moreInfo: await animeExtraInfo(jkanimeName).then(info =>{
return info || null
}),
promo: await getAnimeVideoPromo(jkanimeName).then(promo =>{
return promo || null
}),
characters: await getAnimeCharacters(jkanimeName).then(characters =>{
return characters || null
})
})));
}
return promises;
};
const getAnimeServers = async (id) => {
const jkAnimeIDs = [
{ id: 'the-god-of-high-school' },
{ id: 'kami-no-tou' },
{ id: 'bna' },
{ id: 'ansatsu-kyoushitsu-tv' },
{ id: 'ansatsu-kyoushitsu-tv-2nd-season' }
];
let jkanime = false
let jkanimeID
for (let name in jkAnimeIDs) {
if (id.includes(jkAnimeIDs[name].id)) {
jkanime = true
jkanimeID = id
}
}
if (jkanime === false) {
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`, options);
let body = data.servers;
return await transformUrlServer(body);
} else {
return await videoServersJK(jkanimeID)
}
};
const search = async (title) =>{ return await searchAnime(title); };
const getImages = async (query) => {
let options = { parse: true }
const data = await homgot(`${BASE_QWANT}count=${query.count}&q=${query.title}&t=${query.type}&safesearch=${query.safesearch}&locale=${query.country}&uiv=4`, options);
const body = data.data.result.items;
const promises = []
body.map(doc =>{
promises.push({
type: doc.thumb_type,
thumbnail: `https:${doc.thumbnail}`,
fullsize: `https:${doc.media_fullsize}`
});
});
return promises;
};
const getYoutubeVideos = async (channelId) => {
let options = { parse: true }
const data = await homgot(`${BASE_YOUTUBE}${channelId.id}&part=${channelId.part}&order=${channelId.order}&maxResults=${channelId.maxResults}`, options);
const body = data[channelId.prop];
const promises = []
body.map(doc =>{
promises.push({
title: doc.snippet.title,
videoId: doc.id.videoId,
thumbDefault: doc.snippet.thumbnails.default.url,
thumbMedium: doc.snippet.thumbnails.medium.url,
thumbHigh: doc.snippet.thumbnails.high.url
});
});
return promises;
};
const getRadioStations = async () => {
return require('../assets/radiostations.json');
}
const getOpAndEd = async (title) => {
let data = await parserThemes.serie(title)
return await structureThemes(data, true)
};
const getThemesYear = async (year) => {
let data = []
if (year === undefined) {
return await parserThemes.allYears();
} else {
data = await parserThemes.year(year)
return await structureThemes(data, false)
}
};
const getRandomTheme = async () => {
let promise = []
let options = { parse: true }
const data = await homgot(`${BASE_THEMEMOE}roulette`, options);
let themes = await getThemes(data.themes)
promise.push({
name: data.name,
title: themes[0].name,
link: themes[0].video
})
return promise;
};
const getArtist = async (id) => {
let data
if (id === undefined) {
return await parserThemes.artists();
} else {
data = await parserThemes.artist(id)
return await structureThemes(data, false)
}
};
const getAnimeGenres = async(genre, order, page) => {
let $
let promises = []
let options = { scrapy: true }
if (page !== undefined) {
$ = await homgot(`${GENRES_URL}genre%5B%5D=${genre}&order=${order}&page=${page}`,options)
} else {
$ = await homgot(`${GENRES_URL}genre%5B%5D=${genre}&order=${order}`,options)
}
$('div.Container ul.ListAnimes li article').each((index , element) =>{
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
const poster = $element.find('a div.Image figure img').attr('src');
const banner = poster.replace('covers' , 'banners').trim();
const type = $element.find('div.Description p span.Type').text();
const synopsis = $element.find('div.Description p').eq(1).text().trim();
const rating = $element.find('div.Description p span.Vts').text();
promises.push(animeflvGenres(id).then(async genres => ({
id: id || null,
title: title || null,
poster: await imageUrlToBase64(poster) || null,
banner: banner || null,
synopsis: synopsis || null,
type: type || null,
rating: rating || null,
genres: genres || null
})))
})
return Promise.all(promises);
};
const getAllThemes = async () => {
let data = await parserThemes.all()
return await structureThemes(data, false)
};
module.exports = {
schedule,
top,
getAllAnimes,
getAllDirectory,
getAnitakume,
getNews,
season,
allSeasons,
laterSeasons,
getLastEpisodes,
getSpecials,
getMoreInfo,
getAnimeServers,
search,
getImages,
getYoutubeVideos,
getRadioStations,
getOpAndEd,
getThemesYear,
getRandomTheme,
getArtist,
getAnimeGenres,
getAllThemes
};

30
src/v2/api/apiCall.js Normal file
View File

@ -0,0 +1,30 @@
const hooman = require('hooman');
const { CookieJar } = require('tough-cookie');
const cookieJar = new CookieJar();
const cheerio = require('cheerio');
let response
let data
const homgot = async (url, options) => {
response = await hooman.get(url, cookieJar);
if (options !== undefined) {
if (options.scrapy) {
data = await cheerio.load(response.body)
}
if (options.parse) {
data = JSON.parse(response.body)
}
} else {
data = response
}
return data
}
module.exports = {
homgot
}

49
src/v2/api/index.js Normal file
View File

@ -0,0 +1,49 @@
const express = require('express');
const routes = require('./routes/index');
const router = express.Router();
router.get('/', (req, res) => {
res.set('Cache-Control', 'no-store');
res.json({
message: 'Aruppi API - 🎏',
author: 'Jéluchu',
version: '2.6.8',
credits: 'The bitch loves APIs that offers data to Aruppi App',
deprecated: 'This version will be available until users migrate to Aruppi App v1.5.0',
entries: [
{
'Schedule': '/api/v2/schedule/:day',
'Top': '/api/v2/top/:type/:subtype/:page',
'AllAnimes': '/api/v2/allAnimes',
'Anitakume': '/api/v2/anitakume',
'News': '/api/v2/news',
'Season': '/api/v2/season/:year/:type',
'All Seasons': '/api/v2/allSeasons',
'All Directory': '/api/v2/allDirectory',
'Genres': '/api/v2/getByGenres/:genre/:order/:page?',
'Futures Seasons': '/api/v2/laterSeasons',
'LastEpisodes': '/api/v2/lastEpisodes',
'Movies': '/api/v2/movies/:type/:page',
'Ovas': '/api/v2/ovas/:type/:page',
'Specials': '/api/v2/specials/:type/:page',
'Tv': '/api/v2/tv/:type/:page',
'MoreInfo': '/api/v2/moreInfo/:title',
'GetAnimeServers': '/api/v2/getAnimeServers/:id',
'Search': '/api/v2/search/:title',
'Images': '/api/v2/images/:query',
'Videos': '/api/v2/videos/:channelId',
'Radios': '/api/v2/radio',
'All Themes': '/api/v2/allThemes',
'Themes': '/api/v2/themes/:title',
'Year Themes': '/api/v2/themesYear/:year?',
'Random Theme': '/api/v2/randomTheme',
'Artists Theme': '/api/v2/artists/:id?'
}
]
});
});
router.use('/', routes);
module.exports = router;

501
src/v2/api/routes/index.js Normal file
View File

@ -0,0 +1,501 @@
const express = require('express');
const router = express.Router();
const api = require('../api');
const { BASE_KUDASAI, BASE_PALOMITRON, BASE_RAMENPARADOS, BASE_CRUNCHYROLL } = require('../urls');
router.get('/schedule/:day' , (req, res) =>{
let day = {current: req.params.day}
api.schedule(day)
.then(day =>{
if (day.length > 0) {
res.status(200).json({
day
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/top/:type/:subtype/:page' , (req, res) =>{
let type = req.params.type;
let subtype = req.params.subtype;
let page = req.params.page;
api.top(type, subtype, page)
.then(top =>{
if (top.length > 0) {
res.status(200).json({
top
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allAnimes' , (req, res) =>{
api.getAllAnimes()
.then(animes =>{
if (animes.length > 0) {
res.status(200).json({
animes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allDirectory' , (req, res) =>{
api.getAllDirectory()
.then(directory =>{
if (directory.length > 0) {
res.status(200).json({
directory
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/anitakume' , (req, res) =>{
api.getAnitakume()
.then(podcast =>{
if (podcast.length > 0) {
res.status(200).json({
podcast
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/news' , (req, res) =>{
let pagesRss = [
{ url: BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
{ url: BASE_PALOMITRON, author: 'Palomitron', content: 'description' },
{ url: BASE_RAMENPARADOS, author: 'Ramen para dos', content: 'content' },
{ url: BASE_CRUNCHYROLL, author: 'Crunchyroll', content: 'content_encoded' }
];
api.getNews(pagesRss)
.then(news =>{
if (news.length > 0) {
res.status(200).json({
news
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/season/:year/:type' , (req, res) =>{
let year = req.params.year;
let type = req.params.type;
api.season(year, type)
.then(season =>{
if (season.length > 0) {
res.status(200).json({
season
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allSeasons' , (req, res) =>{
api.allSeasons()
.then(archive =>{
if (archive.length > 0) {
res.status(200).json({
archive
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/laterSeasons' , (req, res) =>{
api.laterSeasons()
.then(future =>{
if (future.length > 0) {
res.status(200).json({
future
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/lastEpisodes' , (req, res) =>{
api.getLastEpisodes()
.then(episodes =>{
if (episodes.length > 0) {
res.status(200).json({
episodes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/movies/:type/:page' , (req, res) =>{
let type = {url: 'Movies', prop: 'movies'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(movies =>{
if (movies.length > 0) {
res.status(200).json({
movies
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/ovas/:type/:page' , (req, res) =>{
let type = {url: 'Ova', prop: 'ova'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(ovas =>{
if (ovas.length > 0) {
res.status(200).json({
ovas
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/specials/:type/:page' , (req, res) =>{
let type = {url: 'Special', prop: 'special'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(specials =>{
if (specials.length > 0) {
res.status(200).json({
specials
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/tv/:type/:page' , (req, res) =>{
let type = {url: 'Tv', prop: 'tv'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(tv =>{
if (tv.length > 0) {
res.status(200).json({
tv
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/moreInfo/:title' , (req, res) =>{
let title = req.params.title;
api.getMoreInfo(title)
.then(info =>{
if (info.length > 0) {
res.status(200).json({
info
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/getAnimeServers/:id([^/]+/[^/]+)' , (req, res) =>{
let id = req.params.id;
api.getAnimeServers(id)
.then(servers =>{
if (servers.length > 0) {
res.status(200).json({
servers
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/search/:title' , (req, res) =>{
let title = req.params.title;
api.search(title)
.then(search =>{
if (search.length > 0) {
res.status(200).json({
search
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/images/:query' , (req, res) =>{
let query = { title: req.params.query, count: '51', type: 'images', safesearch: '1', country: 'es_ES', uiv: '4' };
api.getImages(query)
.then(images =>{
if (images.length > 0) {
res.status(200).json({
images
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/videos/:channelId' , (req, res) =>{
let channelId = { id: req.params.channelId, part: 'snippet,id', order: 'date', maxResults: '50', prop: 'items' };
api.getYoutubeVideos(channelId)
.then(videos =>{
if (videos.length > 0) {
res.status(200).json({
videos
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/radio' , (req, res) =>{
api.getRadioStations()
.then(stations =>{
if (stations.length > 0) {
res.status(200).json({
stations
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allThemes', (req, res) =>{
api.getAllThemes()
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/themes/:title' , (req, res) =>{
let title = req.params.title;
api.getOpAndEd(title)
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/themesYear/:year?', (req, res) =>{
let year = req.params.year;
let season = req.params.season
api.getThemesYear(year, season)
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/randomTheme', (req, res) =>{
api.getRandomTheme()
.then(random =>{
if (random.length > 0) {
res.set('Cache-Control', 'no-store');
res.status(200).json({
random
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/artists/:id?', (req, res) =>{
let id = req.params.id;
api.getArtist(id)
.then(artists =>{
if (artists.length > 0) {
res.status(200).json({
artists
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/getByGenres/:genre/:order/:page?' , (req , res) =>{
let genre = req.params.genre;
let order = req.params.order;
let page = req.params.page;
api.getAnimeGenres(genre, order , page)
.then(animes =>{
if (animes.length > 0) {
res.status(200).json({
animes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
module.exports = router;

21
src/v2/api/urls.js Normal file
View File

@ -0,0 +1,21 @@
module.exports = {
BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/',
BASE_ANIMEFLV: 'https://animeflv.net/',
BASE_JKANIME: 'https://jkanime.net/',
BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/',
BASE_YOUTUBE: 'https://aruppi.jeluchu.xyz/apis/youtube/v3/search?channelId=',
BASE_JIKAN: 'https://aruppi.jeluchu.xyz/apis/jikan/v3/',
BASE_IVOOX: 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml',
BASE_KUDASAI: 'https://somoskudasai.com/feed/',
BASE_PALOMITRON: 'https://elpalomitron.com/category/animemanga/feed/',
BASE_RAMENPARADOS: 'https://ramenparados.com/category/noticias/anime/feed/',
BASE_CRUNCHYROLL: 'https://www.crunchyroll.com/newsrss?lang=esES',
SEARCH_URL: 'https://animeflv.net/browse?q=',
JKANIME_URL: 'https://jkanime.net/buscar/',
GENRES_URL: 'https://animeflv.net/browse?',
SEARCH_DIRECTORY: 'https://animeflv.net/browse?order=title&page=',
BASE_EPISODE_IMG_URL: 'https://cdn.animeflv.net/screenshots/',
BASE_QWANT: 'https://api.qwant.com/search/images?',
REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/',
BASE_THEMEMOE: 'https://themes.moe/api/'
};

View File

@ -0,0 +1,306 @@
[
{
"name": "Ghost Anime Radio",
"url": "http://animeradio.su:8000/"
},
{
"name": "Vocaloid Radio",
"url": "http://curiosity.shoutca.st:8019/stream"
},
{
"name": "NyanServer (J-Trance)",
"url": "http://radio.nyan.pw/station/stream"
},
{
"name": "Listen Radio (J-Pop)",
"url": "https://listen.moe/stream"
},
{
"name": "Listen Radio (K-Pop)",
"url": "https://listen.moe/kpop/stream"
},
{
"name": "Anison FM",
"url": "http://pool.anison.fm:9000/AniSonFM(128)"
},
{
"name": "Radio Nami",
"url": "https://radionami.com/play_radio.m3u"
},
{
"name": "R/a/dio",
"url": "http://relay0.r-a-d.io/main.mp3"
},
{
"name": "Chiru.no",
"url": "http://chiru.no:8000/stream.mp3"
},
{
"name": "Vocaloid Radio VRX",
"url": "http://vrx.piro.moe:8000/stream-192"
},
{
"name": "Asian Wave Japan",
"url": "https://listen1.myradio24.com/7934"
},
{
"name": "Radio Vocaloid",
"url": "http://142.4.217.133:9848/stream"
},
{
"name": "Final Fantasy Radio",
"url": "http://finalfantasystation.com:8000/stream"
},
{
"name": "Shinsen Radio",
"url": "http://shinsen-radio.org:8000/shinsen-radio.128.mp3"
},
{
"name": "Anime Nexus",
"url": "http://radio.animenexus.mx:8000/animenexus"
},
{
"name": "Yggdrasil Radio",
"url": "http://shirayuki.org:9100/"
},
{
"name": "Eden Radio",
"url": "http://edenofthewest.com:8080/eden.mp3"
},
{
"name": "Gensokyo Radio",
"url": "http://stream.gensokyoradio.net:8000/"
},
{
"name": "Radio J-Hero",
"url": "http://stm1.radiojhero.com:8008/;"
},
{
"name": "Phate Radio",
"url": "http://stream.phate.io/phatecc"
},
{
"name": "91.8 The fan",
"url": "http://198.27.80.154:8800/live"
},
{
"name": "Radio AOI",
"url": "http://radioaoi.pl/stream.m3u"
},
{
"name": "Radio Touhou",
"url": "http://www.touhouradio.com/touhouradio.m3u"
},
{
"name": "Radio MultiAnime",
"url": "http://67.20.61.70:8301"
},
{
"name": "Radio Fan World Anime",
"url": "http://stream.miradio.in:2199/tunein/fanworld.pls"
},
{
"name": "Radio Japan-A",
"url": "http://www.japanaradio.com/free/48kaacp.pls"
},
{
"name": "Radio JPopsuki",
"url": "http://jpopsuki.fm:2199/tunein/jpopsuki-stream.pls"
},
{
"name": "Radio Hot Mix",
"url": "http://hotmixradio-japan.ice.infomaniak.ch/hotmixradio-japan-128.mp3"
},
{
"name": "Dada more Radio",
"url": "http://dadamore2.ddo.jp:8000/listen.pls"
},
{
"name": "Initial D World",
"url": "http://69.163.186.124:9001/listen.aac"
},
{
"name": "Radio Blast",
"url": "http://192.99.150.31:8315/"
},
{
"name": "Kibo FM",
"url": "http://listen.kibo.fm:8000/kibofm"
},
{
"name": "Power 945",
"url": "http://38.96.148.28:8754/stream"
},
{
"name": "Japan Fans",
"url": "http://159.253.37.137:9984/listen.pls"
},
{
"name": "Radio Aniterasu",
"url": "http://aniterasu.com:8000/;?1442956789440.mp3"
},
{
"name": "Big B Radio's J-Pop",
"url": "http://64.71.79.181:6059/stream"
},
{
"name": "Radio Blue Heron",
"url": "http://cp3.digistream.info:8170"
},
{
"name": "Friends Forever",
"url": "http://23.29.71.154:8066/"
},
{
"name": "Radio Greek Otaku",
"url": "http://192.99.4.210:3684/stream"
},
{
"name": "Radio UR",
"url": "http://listen.ur-radio.de/anime.mp3"
},
{
"name": "Radio Anime",
"url": "http://stream.animeradio.de/animeradio.mp3"
},
{
"name": "PowerPlay J-Pop",
"url": "http://agnes.torontocast.com:8102"
},
{
"name": "Radio Asia Dream",
"url": "http://bluford.torontocast.com:8526"
},
{
"name": "J-Pop Kawaii",
"url": "http://bb31.sonixcast.com:20002/stream/1/"
},
{
"name": "J-Club HipHop",
"url": "http://agnes.torontocast.com:8051"
},
{
"name": "J-Rock",
"url": "http://cristina.torontocast.com:8057"
},
{
"name": "J-Pop Sakura",
"url": "http://bb31.sonixcast.com:20278/stream/1/"
},
{
"name": "J-Pop Haru Sakura",
"url": "http://184.75.223.178:8087/"
},
{
"name": "Radio Ronin",
"url": "https://s3.radio.co/sff133d65b/listen"
},
{
"name": "Radio Shinka",
"url": "http://5.9.65.9:8171/live"
},
{
"name": "Radio Naihatsu",
"url": "http://108.163.223.242:8305/"
},
{
"name": "J-Pop Project",
"url": "http://agnes.torontocast.com:8083/"
},
{
"name": "J-idols Project",
"url": "http://agnes.torontocast.com:8011/"
},
{
"name": "Radio J1",
"url": "https://jenny.torontocast.com:2000/stream/J1HITS"
},
{
"name": "J1 XTRA",
"url": "https://jenny.torontocast.com:2000/stream/J1XTRA"
},
{
"name": "J1 GOLD",
"url": "https://jenny.torontocast.com:2000/stream/J1GOLD"
},
{
"name": "Animu FM",
"url": "http://cast.animu.com.br:9021/stream"
},
{
"name": "Radio Wave Anime",
"url": "http://s04.radio-tochka.com:5470/mount"
},
{
"name": "Radio Anime Stream",
"url": "https://radioanime.radioca.st/stream"
},
{
"name": "Radio Baka",
"url": "http://144.217.203.184:8398/;"
},
{
"name": "Radio Animecol",
"url": "http://node-15.zeno.fm/6bfysacxc6quv"
},
{
"name": "JMusic Anime",
"url": "http://ample-zeno-24.radiojar.com/ddetxwuhkpeuv"
},
{
"name": "Radio Japanese Music",
"url": "http://live.japanesemusicid.com:8000/japanesemusic"
},
{
"name": "Radio Japannext",
"url": "https://perseus.shoutca.st/tunein/japannex.pls"
},
{
"name": "Radio Akari",
"url": "http://ample-zeno-22.radiojar.com/0t952vqukfeuv"
},
{
"name": "Anime Universe",
"url": "http://176.31.241.17:8147/;"
},
{
"name": "Geek Radio Music",
"url": "http://stream.zenolive.com/8d0xskxsxxquv"
},
{
"name": "Radio Aniterasu",
"url": "http://aniterasuradio.com:8000/;"
},
{
"name": "Radio Akiba",
"url": "http://stm24.srvstm.com:9526/;"
},
{
"name": "Radio Caprice",
"url": "http://79.111.119.111:8002/anime"
},
{
"name": "Radio Caprice J-Rock",
"url": "http://79.111.119.111:8002/jpop"
},
{
"name": "Nihonara!",
"url": "http://79.111.119.111:8002/jrock"
},
{
"name": "Radio Opening",
"url": "http://5.39.86.120:8000/nihonara_128.mp3"
},
{
"name": "Radio Aewen K-J-Pop",
"url": "http://stream.zeno.fm/tza2ayy47qruv"
},
{
"name": "Radio Wkend",
"url": "http://209.58.145.135:8031/stream"
},
{
"name": "Nihongo FM",
"url": "http://199.180.72.2:9004/stream"
}
]

288
src/v2/utils/animetheme.js Normal file
View File

@ -0,0 +1,288 @@
const cheerio = require('cheerio');
const {
homgot
} = require('../api/apiCall');
const {
REDDIT_ANIMETHEMES
} = require('../api/urls');
class ThemeParser {
constructor() {}
async all() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseLinks();
}
catch(err) {
throw err;
}
}
async allYears() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseYears();
}
catch(err) {
throw err;
}
}
async serie(query) {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseSerie(query);
}
catch(err) {
throw err;
}
}
async artists() {
try {
this.animes = [];
this.$ = await redditocall('artist');
return await this.parseArtists();
}
catch(err) {
throw err;
}
}
async artist(id) {
try {
this.animes = [];
this.$ = await redditocall(`artist/${id}`);
return await this.parseArtist();
}
catch(err) {
throw err;
}
}
async random(query) {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseRandom(query);
}
catch(err) {
throw err;
}
}
async year(date) {
let animes = [];
this.$ = await redditocall(date)
this.$('h3').each((i, el) => {
let parsed = this.parseAnime(el);
parsed.year = date;
animes.push(parsed);
})
return animes;
}
parseRandom() {
return new Promise(async resolve => {
let data = this.$('p a');
const origin = '1'
let randomize = Math.round(Math.random()*((data.length-1)-origin)+parseInt(origin));
this.$ = await redditocall(this.$('p a')[randomize].attribs.href.split('/r/AnimeThemes/wiki/')[1].split('#wiki')[0]);
let rand = Math.round(Math.random()*this.$('h3').length - 1);
let parsed = this.parseAnime(this.$('h3')[rand]);
resolve(parsed);
})
}
parseYears(){
return new Promise(async resolve => {
let promises = []
let data = this.$('h3 a');
for (let i = 0; i < data.length; i++) {
promises.push({
id: data[i].children[0].parent.attribs.href.split('/')[4],
name: data[i].children[0].data
})
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseArtists(){
return new Promise(async resolve => {
let promises = []
let data = this.$('p a').filter(x => x > 0);
for (let i = 0; i < data.length; i++) {
promises.push({
id: data[i].children[0].parent.attribs.href.split('/')[5],
name: data[i].children[0].data
})
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseArtist(){
return new Promise(async resolve => {
let promises = []
let data = this.$('h3');
for (let i = 0; i < data.length; i++) {
let parsed = await this.parseAnime(data[i])
promises.push(parsed)
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseSerie(query){
return new Promise(async resolve => {
let data = this.$('p a');
for (let i = 0; i < data.length; i++) {
let serieElement = data[i].children[0].data
if (serieElement.split(" (")[0] === query) {
this.$ = await redditocall(this.$('p a')[i].attribs.href.split('/r/AnimeThemes/wiki/')[1].split('#wiki')[0]);
for (let i = 0; i < this.$('h3').length; i++) {
if (this.$('h3')[i].children[0].children[0].data === query) {
let parsed = this.parseAnime(this.$('h3')[i]);
resolve(parsed);
}
}
}
}
})
}
parseLinks() {
return new Promise(async resolve => {
let years = this.$('h3 a');
this.$('h3 a')[0].children[0].data
for (let i = 0; i < years.length; i++) {
let yearElement = years[i];
await this.year(this.$(yearElement).attr('href').split('/')[4])
.then(async animes => {
this.animes = this.animes.concat(animes);
if(i === years.length - 1) {
resolve(this.animes);
}
})
}
})
}
parseAnime(dat) {
let el = this.$(dat).children('a');
let title = el.text();
let malId = el.attr('href').split('/')[4];
let next = this.$(dat).next();
let theme = {
id: malId,
title
}
if (next.prop("tagName") === "P") {
theme.themes = this.parseTable(next.next());
} else if (next.prop("tagName") === "TABLE") {
theme.themes = this.parseTable(next);
}
return theme;
}
parseTable(table) {
if (table.prop('tagName') !== "TABLE") {
return this.parseTable(table.next());
}
let themes = [];
table.children('tbody').children('tr').each(function () {
const $ = cheerio.load(this);
const td = $('td'); // Theme row
let name = replaceAll(td.first().text(), "&quot;", "\"")
let linkEl = td.eq(1).children().first();
let link = linkEl.attr('href');
let linkDesc = linkEl.text();
let episodes = td.eq(2).text();
let notes = td.eq(3).text();
themes.push({
name,
link,
desc: linkDesc,
type: (name.startsWith('OP') ? 'opening' : 'ending'),
episodes,
notes
})
})
return themes;
}
}
async function redditocall(href) {
let options = { parse: true }
let resp = await homgot(REDDIT_ANIMETHEMES + href + ".json", options)
return cheerio.load(getHTML(resp.data.content_html));
}
function getHTML(str) {
let html = replaceAll(str, "&lt;", "<")
html = replaceAll(html, "&gt;", ">")
return html;
}
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
}
module.exports = ThemeParser;

698
src/v2/utils/index.js Normal file
View File

@ -0,0 +1,698 @@
const {
BASE_ANIMEFLV, BASE_JIKAN, BASE_EPISODE_IMG_URL, SEARCH_URL, SEARCH_DIRECTORY, BASE_ARUPPI, BASE_JKANIME, JKANIME_URL
} = require('../api/urls.js');
const {
homgot
} = require('../api/apiCall.js');
function btoa(str) {
let buffer;
if (str instanceof Buffer) {
buffer = str;
}
else {
buffer = Buffer.from(str.toString(), 'binary');
}
return buffer.toString('base64');
}
global.btoa = btoa;
async function videoServersJK(id) {
let options = { scrapy: true }
const $ = await homgot(`${BASE_JKANIME}${id}`, options);
const scripts = $('script');
const episodes = $('div#reproductor-box li');
const serverNames = [];
let servers = [];
episodes.each((index , element) =>{
const $element = $(element);
const serverName = $element.find('a').text();
serverNames.push(serverName);
})
for(let i = 0; i < scripts.length; i++){
const $script = $(scripts[i]);
const contents = $script.html();
try{
if ((contents || '').includes('var video = [];')) {
Array.from({length: episodes.length} , (v , k) =>{
let index = Number(k + 1);
let videoPageURL = contents.split(`video[${index}] = \'<iframe class="player_conte" src="`)[1].split('"')[0];
servers.push({iframe: videoPageURL});
});
}
}catch(err) {
return null;
}
}
let serverList = [];
for(let server in servers) {
serverList.push({
id: serverNames[server].toLowerCase(),
url: await getVideoURL(servers[server].iframe),
direct: true
});
}
serverList = serverList.filter(x => x.id !== 'xtreme s' && x.id !== 'desuka' );
return await Promise.all(serverList);
}
async function getVideoURL(url) {
let options = { scrapy: true }
const $ = await homgot(url, options);
const video = $('video');
if(video.length){
const src = $(video).find('source').attr('src');
return src || null;
}
else{
const scripts = $('script');
const l = global;
const ll = String;
const $script2 = $(scripts[1]).html();
eval($script2);
return l.ss || null;
}
}
const jkanimeInfo = async (id) => {
let poster = ""
let banner = ""
let synopsis = ""
let rating = ""
let debut = ""
let type = ""
let $
try {
let options = { scrapy: true }
$ = await homgot(`${BASE_JKANIME}${id}`, options);
const animeExtraInfo = [];
const genres = [];
let listByEps;
poster = $('div[id="container"] div.serie-info div.cap-portada')[0].children[1].attribs.src;
banner = $('div[id="container"] div.serie-info div.cap-portada')[0].children[1].attribs.src;
synopsis = $('div[id="container"] div.serie-info div.sinopsis-box p')[0].children[1].data;
rating = "Sin calificación"
debut = $('div[id="container"] div.serie-info div.info-content div')[6].children[3].children[0].children[0].data;
type = $('div[id="container"] div.serie-info div.info-content div')[0].children[3].children[0].data
animeExtraInfo.push({
poster: poster,
banner: banner,
synopsis: synopsis,
rating: rating,
debut: debut,
type: type,
})
let rawGenres = $('div[id="container"] div.serie-info div.info-content div')[1].children[3].children
for (let i = 0; i <= rawGenres.length -1; i++) {
if (rawGenres[i].name === 'a') {
const genre = rawGenres[i].children[0].data
genres.push(genre)
}
}
let nextEpisodeDate
let rawNextEpisode = $('div[id="container"] div.left-container div[id="proxep"] p')[0]
if (rawNextEpisode === undefined) {
nextEpisodeDate = null
} else {
if (rawNextEpisode.children[1].data === ' ') {
nextEpisodeDate = null
} else {
nextEpisodeDate = rawNextEpisode
}
}
const eps_temp_list = [];
let episodes_aired = '';
$('div#container div.left-container div.navigation a').each(async(index , element) => {
const $element = $(element);
const total_eps = $element.text();
eps_temp_list.push(total_eps);
})
try{episodes_aired = eps_temp_list[0].split('-')[1].trim();}catch(err){}
const animeListEps = [{nextEpisodeDate: nextEpisodeDate}];
for (let i = 1; i <= episodes_aired; i++) {
let episode = i;
let animeId = $('div[id="container"] div.content-box div[id="episodes-content"]')[0].children[1].children[3].attribs.src.split('/')[7].split('.jpg')[0];
let imagePreview = $('div[id="container"] div.content-box div[id="episodes-content"]')[0].children[1].children[3].attribs.src
let link = `${animeId}/${episode}`
animeListEps.push({
episode: episode,
id: link,
imagePreview: imagePreview
})
}
listByEps = animeListEps;
return {listByEps, genres, animeExtraInfo};
} catch (err) {
console.error(err)
}
};
const animeflvGenres = async (id) => {
const promises = [];
let options = { scrapy: true }
let $ = await homgot(`${BASE_ANIMEFLV}${id}`, options);
$('main.Main section.WdgtCn nav.Nvgnrs a').each((index, element) => {
const $element = $(element);
const genre = $element.attr('href').split('=')[1] || null;
promises.push(genre);
});
return promises;
}
const animeflvInfo = async (id, index) => {
let poster = ""
let banner = ""
let synopsis = ""
let rating = ""
let debut = ""
let type = ""
let $
try {
let options = { scrapy: true }
$ = await homgot(`${BASE_ANIMEFLV}anime/${id}`, options);
const scripts = $('script');
const anime_info_ids = [];
const anime_eps_data = [];
const animeExtraInfo = [];
const genres = [];
let listByEps;
poster = `${BASE_ANIMEFLV}` + $('body div div div div div aside div.AnimeCover div.Image figure img').attr('src')
banner = poster.replace('covers', 'banners').trim();
synopsis = $('body div div div div div main section div.Description p').text().trim();
rating = $('body div div div.Ficha.fchlt div.Container div.vtshr div.Votes span#votes_prmd').text();
debut = $('body div.Wrapper div.Body div div.Container div.BX.Row.BFluid.Sp20 aside.SidebarA.BFixed p.AnmStts').text();
type = $('body div.Wrapper div.Body div div.Ficha.fchlt div.Container span.Type').text()
animeExtraInfo.push({
poster: poster,
banner: banner,
synopsis: synopsis,
rating: rating,
debut: debut,
type: type,
})
$('main.Main section.WdgtCn nav.Nvgnrs a').each((index, element) => {
const $element = $(element);
const genre = $element.attr('href').split('=')[1] || null;
genres.push(genre);
});
Array.from({length: scripts.length}, (v, k) => {
const $script = $(scripts[k]);
const contents = $script.html();
if ((contents || '').includes('var anime_info = [')) {
let anime_info = contents.split('var anime_info = ')[1].split(';\n')[0];
let dat_anime_info = JSON.parse(anime_info);
anime_info_ids.push(dat_anime_info);
}
if ((contents || '').includes('var episodes = [')) {
let episodes = contents.split('var episodes = ')[1].split(';')[0];
let eps_data = JSON.parse(episodes)
anime_eps_data.push(eps_data);
}
});
const AnimeThumbnailsId = index;
const animeId = id;
let nextEpisodeDate
if (anime_info_ids.length > 0) {
if (anime_info_ids[0].length === 4) {
nextEpisodeDate = anime_info_ids[0][3]
} else {
nextEpisodeDate = null
}
}
const amimeTempList = [];
for (const [key, value] of Object.entries(anime_eps_data)) {
let episode = anime_eps_data[key].map(x => x[0]);
let episodeId = anime_eps_data[key].map(x => x[1]);
amimeTempList.push(episode, episodeId);
}
const animeListEps = [{nextEpisodeDate: nextEpisodeDate}];
Array.from({length: amimeTempList[1].length}, (v, k) => {
let data = amimeTempList.map(x => x[k]);
let episode = data[0];
let id = data[1];
let imagePreview = `${BASE_EPISODE_IMG_URL}${AnimeThumbnailsId}/${episode}/th_3.jpg`
let link = `${id}/${animeId}-${episode}`
animeListEps.push({
episode: episode,
id: link,
imagePreview: imagePreview
})
})
listByEps = animeListEps;
return {listByEps, genres, animeExtraInfo};
} catch (err) {
console.error(err)
}
};
const getAnimeCharacters = async (title) => {
try {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}/characters_staff`, options);
let body = data.characters;
if (typeof body === 'undefined') return null;
const charactersId = body.map(doc => {
return doc.mal_id
})
const charactersNames = body.map(doc => {
return doc.name;
});
const charactersImages = body.map(doc => {
return doc.image_url
});
let characters = [];
Array.from({length: charactersNames.length}, (v, k) => {
const id = charactersId[k];
let name = charactersNames[k];
let characterImg = charactersImages[k];
characters.push({
id: id,
name: name,
image: characterImg
});
});
return Promise.all(characters);
} catch (e) {
console.log(e.message)
}
};
const getAnimeVideoPromo = async (title) => {
try {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}/videos`, options);
const body = data.promo;
const promises = [];
body.map(doc => {
promises.push({
title: doc.title,
previewImage: doc.image_url,
videoURL: doc.video_url
});
});
return Promise.all(promises);
} catch (e) {
console.log(e.message)
}
};
const animeExtraInfo = async (title) => {
try {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}`, options);
const body = Array(data);
const promises = [];
body.map(doc => {
let airDay = {
'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'
};
let broadcast
if (doc.broadcast === null) {
broadcast = null
} else {
broadcast = airDay[doc.broadcast.split('at')[0].replace(" ", "").toLowerCase()]
}
promises.push({
titleJapanese: doc.title_japanese,
source: doc.source,
totalEpisodes: doc.episodes,
aired: {
from: doc.aired.from,
to: doc.aired.to
},
duration: doc.duration.split('per')[0],
rank: doc.rank,
broadcast: broadcast,
producers: doc.producers.map(x => x.name) || null,
licensors: doc.licensors.map(x => x.name) || null,
studios: doc.studios.map(x => x.name) || null,
openingThemes: doc.opening_themes || null,
endingThemes: doc.ending_themes || null
});
});
return Promise.all(promises);
} catch (e) {
console.log(e.message)
}
};
const imageUrlToBase64 = async (url) => {
let img = await homgot(url)
return img.rawBody.toString('base64');
};
const helper = async () => {}
const searchAnime = async (query) => {
let $
let promises = []
const jkAnimeTitles = [
{ title: 'BNA', search: 'BNA'},
{ title: 'The God of High School', search: 'The god' },
{ title: 'Ansatsu Kyoshitsu', search: 'Assassination Classroom' },
];
let jkanime = false
let jkanimeName
for (let name in jkAnimeTitles) {
if (query === jkAnimeTitles[name].title) {
jkanime = true
jkanimeName = jkAnimeTitles[name].search
}
}
if (jkanime === false) {
let options = { scrapy: true }
$ = await homgot(`${SEARCH_URL}${query}`, options);
$('div.Container ul.ListAnimes li article').each((index, element) => {
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
let poster = $element.find('a div.Image figure img').attr('src') || $element.find('a div.Image figure img').attr('data-cfsrc');
const type = $element.find('div.Description p span.Type').text();
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: type || null,
image: await imageUrlToBase64(poster) || null
})));
})
} else {
let options = { scrapy: true }
$ = await homgot(`${JKANIME_URL}${jkanimeName}`, options);
$('.portada-box').each(function (index, element) {
const $element = $(element);
const title = $element.find('h2.portada-title a').attr('title');
const id = $element.find('a.let-link').attr('href').split('/')[3];
const poster = $element.find('a').children('img').attr('src');
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: 'Anime',
image: await imageUrlToBase64(poster) || null
})))
});
}
return Promise.all(promises);
};
const transformUrlServer = async (urlReal) => {
let res
const promises = []
for (const index in urlReal) {
if (urlReal[index].server === 'amus' || urlReal[index].server === 'natsuki') {
let options = { parse: true }
res = await homgot(urlReal[index].code.replace("embed", "check"), options);
urlReal[index].code = res.file || null
urlReal[index].direct = true
} else if (urlReal[index].server === 'gocdn' ) {
urlReal[index].code = `https://s1.streamium.xyz/gocdn.php?v=${urlReal[index].code.split('/player_gocdn.html#')[1]}`
urlReal[index].direct = true
}
}
urlReal.map(doc => {
promises.push({
id: doc.title.toLowerCase(),
url: doc.code,
direct: doc.direct || false
});
});
return promises;
}
const obtainPreviewNews = (encoded) => {
let image;
if (encoded.includes('src="https://img1.ak.crunchyroll.com/')) {
image = `https://img1.ak.crunchyroll.com/${encoded.split('https://img1.ak.crunchyroll.com/')[1].split('.jpg')[0]}.jpg`
} else if (encoded.includes('<img title=')) {
image = encoded.substring(encoded.indexOf("<img title=\""), encoded.indexOf("\" alt")).split('src=\"')[1]
} else if (encoded.includes('<img src=')) {
image = encoded
.substring(encoded.indexOf("<img src=\""), encoded.indexOf("\" alt"))
.substring(10).replace("http", "https")
.replace("httpss", "https")
} else if (encoded.includes('<img')) {
image = encoded.split("src=")[1].split(" class=")[0].replace("\"", '').replace('\"', '')
} else if (encoded.includes('https://www.youtube.com/embed/')) {
let getSecondThumb = encoded.split('https://www.youtube.com/embed/')[1].split('?feature')[0]
image = `https://img.youtube.com/vi/${getSecondThumb}/0.jpg`
} else if (encoded.includes('https://www.dailymotion.com/')) {
let getDailymotionThumb = encoded
.substring(encoded.indexOf("\" src=\""), encoded.indexOf("\" a"))
.substring(47)
image = `https://www.dailymotion.com/thumbnail/video/${getDailymotionThumb}`
} else {
let number = Math.floor(Math.random() * 30);
image = `${BASE_ARUPPI}news/${number}.png`
}
return image;
}
const structureThemes = async (body, indv) => {
const promises = []
let themes
if (indv === true) {
themes = await getThemesData(body.themes)
promises.push({
title: body.title,
year: body.year,
themes: themes,
});
} else {
for (let i = 0; i <= body.length - 1; i++) {
themes = await getThemesData(body[i].themes)
promises.push({
title: body[i].title,
year: body[i].year,
themes: themes,
});
}
}
return promises;
};
const getThemesData = async (themes) => {
let promises = []
for (let i = 0; i <= themes.length - 1; i++) {
promises.push({
title: themes[i].name.split('"')[1] || 'Remasterización',
type: themes[i].name.split('"')[0] || 'OP/ED',
episodes: themes[i].episodes || null,
video: themes[i].link
});
}
return promises;
};
const getThemes = async (themes) => {
let promises = []
themes.map(doc => {
promises.push({
name: doc.themeName,
type: doc.themeType,
video: doc.mirror.mirrorURL
});
});
return promises;
};
const getAnimes = async () => {
let options = { parse: true }
return await homgot(`${BASE_ANIMEFLV}api/animes/list`, options);
};
const getDirectory = async () => {
let $
let promises = []
let options = { scrapy: true }
$ = await homgot(`${SEARCH_URL}`, options);
const lastPage = $('body div.Wrapper div.Container main div.NvCnAnm ul li a')[11].children[0].data
for (let i = 1; i <= lastPage; i++) {
let options = { scrapy: true }
$ = await homgot(`${SEARCH_DIRECTORY}${i}`, options);
$('div.Container ul.ListAnimes li article').each((index, element) => {
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
let poster = $element.find('a div.Image figure img').attr('src') || $element.find('a div.Image figure img').attr('data-cfsrc');
const type = $element.find('div.Description p span.Type').text();
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: type || null,
image: await imageUrlToBase64(poster) || null
})));
})
}
return Promise.all(promises);
};
module.exports = {
jkanimeInfo,
animeflvGenres,
animeflvInfo,
getAnimeCharacters,
getAnimeVideoPromo,
animeExtraInfo,
imageUrlToBase64,
searchAnime,
transformUrlServer,
obtainPreviewNews,
structureThemes,
getThemes,
getAnimes,
getDirectory,
helper,
videoServersJK
}