From bc868b71cd671ac08f4c2da97000be0ec1680f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9luchu?= Date: Mon, 6 Jul 2020 11:40:36 +0200 Subject: [PATCH] Update Ops/Eds - v2.5.0 --- README.md | 2 +- package.json | 2 +- src/api/api.js | 90 +++++-------- src/api/index.js | 5 +- src/api/routes/index.js | 22 ++- src/api/urls.js | 2 +- src/utils/animetheme.js | 288 ++++++++++++++++++++++++++++++++++++++++ src/utils/index.js | 75 ++++------- 8 files changed, 373 insertions(+), 113 deletions(-) create mode 100644 src/utils/animetheme.js diff --git a/README.md b/README.md index dcdd93b..16fde9e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# **Aruppi API** (v2.4.0) +# **Aruppi API** (v2.5.0) > This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture > diff --git a/package.json b/package.json index 703e036..89c660f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aruppi", - "version": "2.4.0", + "version": "2.5.0", "description": "Aruppi is a custom API to obtain data from the Japanese culture for the mobile app", "main": "./src/api/api.js", "scripts": { diff --git a/src/api/api.js b/src/api/api.js index 982bb39..d1d5aa5 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -19,8 +19,11 @@ const { helper } = require('../utils/index'); +const ThemeParser = require('../utils/animetheme'); +const parserThemes = new ThemeParser() + const { - BASE_ANIMEFLV_JELU, BASE_JIKAN, BASE_IVOOX, BASE_QWANT, BASE_YOUTUBE, BASE_THEMEMOE, GENRES_URL + BASE_ANIMEFLV_JELU, BASE_JIKAN, BASE_IVOOX, BASE_QWANT, BASE_YOUTUBE, GENRES_URL } = require('./urls'); const schedule = async (day) =>{ @@ -394,75 +397,46 @@ const getRadioStations = async () => { } const getOpAndEd = async (title) => { - - let data - - const special = [ - { title: 'Kaguya-sama wa Kokurasetai: Tensai-tachi no Renai Zunousen', code: 37999 }, - { title: 'Kaguya-sama wa Kokurasetai: Tensai-tachi no Renai Zunousen 2nd Season', code: 40591 }, - { title: 'Princess Connect! Re:Dive', code: 39292 }, - { title: 'Shachou, Battle no Jikan Desu!', code: 40783 } - ]; - - for (let name in special) { - if (title === special[name].title) { - data = JSON.parse("[" + special[name].code + "]") - break; - } - } - - if (data === undefined) { - let options = { parse: true } - data = await homgot(`${BASE_THEMEMOE}anime/search/${title}`, options); - } - - return await structureThemes(data, true, 0) + let data = await parserThemes.serie(title) + return await structureThemes(data, true) }; -const getThemesSeason = async (year, season) => { - - let data - let options = { parse: true } +const getThemesYear = async (year) => { + let data = [] - if (season === undefined) { - data = await homgot(`${BASE_THEMEMOE}seasons/${year}`, options); + if (year === undefined) { + return await parserThemes.allYears(); } else { - data = await homgot(`${BASE_THEMEMOE}seasons/${year}/${season}`, options); + data = await parserThemes.year(year) + return await structureThemes(data, false) } - return await structureThemes(data, false, 0) - }; const getRandomTheme = async () => { - let options = { parse: true } - const data = await homgot(`${BASE_THEMEMOE}roulette`, options); - return await structureThemes(data, true) + + let promise = [] + let data = await parserThemes.random() + let random = Math.round(Math.random()*(data.themes.length - 1)); + + promise.push({ + name: data.title, + title: data.themes[random].name.split('"')[1] || null, + link: data.themes[random].link + }) + + return promise; }; const getArtist = async (id) => { let data - let promises = [] - let options = { parse: true } if (id === undefined) { - - data = await homgot(`${BASE_THEMEMOE}artists`, options); - data.map(doc => { - - promises.push({ - id: doc.artistID, - name: doc.artistName - }) - - }); - - return promises; - + return await parserThemes.artists(); } else { - data = await homgot(`${BASE_THEMEMOE}artists/${id}`, options); - return await structureThemes(data, false, 1) + data = await parserThemes.artist(id) + return await structureThemes(data, false) } }; @@ -506,6 +480,11 @@ const getAnimeGenres = async(genre, order, page) => { }; +const getAllThemes = async () => { + let data = await parserThemes.all() + return await structureThemes(data, false) +}; + module.exports = { schedule, top, @@ -525,8 +504,9 @@ module.exports = { getYoutubeVideos, getRadioStations, getOpAndEd, - getThemesSeason, + getThemesYear, getRandomTheme, getArtist, - getAnimeGenres + getAnimeGenres, + getAllThemes }; diff --git a/src/api/index.js b/src/api/index.js index 1f51de0..e4e7026 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -7,7 +7,7 @@ router.get('/', (req, res) => { res.json({ message: 'Aruppi API - 🎏', author: 'Jéluchu', - version: '2.4.0', + version: '2.5.0', credits: 'The bitch loves APIs that offers data to Aruppi App', entries: [ { @@ -32,8 +32,9 @@ router.get('/', (req, res) => { 'Images': '/api/v2/images/:query', 'Videos': '/api/v2/videos/:channelId', 'Radios': '/api/v2/radio', + 'All Themes': '/api/v2/allThemes', 'Themes': '/api/v2/themes/:title', - 'Season Themes': '/api/v2/themeSeason/:year/:season?', + 'Year Themes': '/api/v2/themesYear/:year?', 'Random Theme': '/api/v2/randomTheme', 'Artists Theme': '/api/v2/artists/:id?' } diff --git a/src/api/routes/index.js b/src/api/routes/index.js index 7304969..eface27 100644 --- a/src/api/routes/index.js +++ b/src/api/routes/index.js @@ -386,6 +386,24 @@ router.get('/radio' , (req, res) =>{ }); +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; @@ -405,12 +423,12 @@ router.get('/themes/:title' , (req, res) =>{ }); -router.get('/themeSeason/:year/:season?', (req, res) =>{ +router.get('/themesYear/:year?', (req, res) =>{ let year = req.params.year; let season = req.params.season - api.getThemesSeason(year, season) + api.getThemesYear(year, season) .then(themes =>{ if (themes.length > 0) { res.status(200).json({ diff --git a/src/api/urls.js b/src/api/urls.js index d369e47..9a233b9 100644 --- a/src/api/urls.js +++ b/src/api/urls.js @@ -14,5 +14,5 @@ module.exports = { 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?', - BASE_THEMEMOE: 'https://themes.moe/api/' + REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/' }; diff --git a/src/utils/animetheme.js b/src/utils/animetheme.js new file mode 100644 index 0000000..ecb68f3 --- /dev/null +++ b/src/utils/animetheme.js @@ -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(), """, "\"") + 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, "<", "<") + html = replaceAll(html, ">", ">") + return html; +} + +function replaceAll(str, find, replace) { + return str.replace(new RegExp(find, 'g'), replace); +} + +module.exports = ThemeParser; diff --git a/src/utils/index.js b/src/utils/index.js index 9489787..363e4e2 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,5 +1,5 @@ const { - BASE_ANIMEFLV, BASE_JIKAN, BASE_EPISODE_IMG_URL, SEARCH_URL, SEARCH_DIRECTORY, BASE_ARUPPI, BASE_THEMEMOE + BASE_ANIMEFLV, BASE_JIKAN, BASE_EPISODE_IMG_URL, SEARCH_URL, SEARCH_DIRECTORY, BASE_ARUPPI } = require('../api/urls'); const { @@ -319,58 +319,33 @@ const obtainPreviewNews = (encoded) => { return image; } -const structureThemes = async (body, indv, task) => { +const structureThemes = async (body, indv) => { const promises = [] let themes - let data - - if (task === 0) { - for (let i = 0; i <= body.length - 1; i++) { - - if (indv === true) { - let options = { parse: true } - data = await homgot(`${BASE_THEMEMOE}themes/${body[i]}`, options); - themes = await getThemes(data[0].themes) - } else { - data = body - themes = await getThemes(body[0].themes) - } - data.map(doc => { - - promises.push({ - title: doc.name, - season: doc.season, - year: doc.year, - themes: themes, - }); - - }); - } - } else if (task === 1) { - - data = body - themes = await getHeaderTheme(data.themes) + if (indv === true) { + themes = await getThemesData(body.themes) promises.push({ - title: data.artistName, - season: data.season, - year: data.year, - series: themes, + title: body.title, + year: body.year, + themes: themes, }); } else { - data = body - themes = await getThemes(data.themes) + for (let i = 0; i <= body.length - 1; i++) { - promises.push({ - title: data.name, - season: data.season, - year: data.year, - themes: themes, - }); + themes = await getThemesData(body[i].themes) + + promises.push({ + title: body[i].title, + year: body[i].year, + themes: themes, + }); + + } } @@ -378,20 +353,18 @@ const structureThemes = async (body, indv, task) => { }; -const getHeaderTheme = async (series) => { - let promises = [] - let data +const getThemesData = async (themes) => { - for (let i = 0; i <= series.length - 1; i++) { + let promises = [] - data = await getThemes(series[i].themes) + for (let i = 0; i <= themes.length - 1; i++) { promises.push({ - title: series[i].name, - season: series[i].season, - year: series[i].year, - themes: data, + 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 }); }