Compare commits

..

No commits in common. 'v4' and 'v4.0.0' have entirely different histories.
v4 ... v4.0.0

@ -1,32 +0,0 @@
module.exports = {
env: {
browser: true,
commonjs: true,
node: true,
},
extends: ['prettier', 'eslint:recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: false,
},
},
plugins: ['@typescript-eslint/eslint-plugin'],
rules: {
'no-underscore-dangle': 'off',
'class-methods-use-this': 'off',
camelcase: 'off',
'no-unused-vars': 'warn',
'no-undef': 'warn',
},
settings: {
'import/resolver': {
node: {
extensions: ['.ts'],
typescript: {},
},
},
},
};

@ -0,0 +1,47 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true,
"node": true
},
"extends": [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "prettier"],
"rules": {
"no-underscore-dangle": "off",
"prettier/prettier": "error",
"class-methods-use-this": "off",
"camelcase": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never"
}
]
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".ts"],
"typescript": {}
}
}
}
}

@ -1,5 +0,0 @@
module.exports = {
singleQuote: true,
trailingComma: 'all',
arrowParens: 'avoid',
};

@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid"
}

@ -1,4 +1,4 @@
# **Aruppi API** (v4.2.2) # **Aruppi API** (v4.0.0)
> This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture > This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture
> >
@ -12,21 +12,22 @@
![now](https://badgen.net/badge/icon/now?icon=now&label) ![now](https://badgen.net/badge/icon/now?icon=now&label)
![gitrepo](https://img.shields.io/github/stars/aruppi/aruppi-api?style=social) ![gitrepo](https://img.shields.io/github/stars/aruppi/aruppi-api?style=social)
# ---
<img src="./assets/img/logo.png" width="100%" alt="">
## 📖 API Documentation
<img src="./assets/img/cover.png" width="100%" alt=""> Soon we will add the documentation information in a link
&nbsp; ## :rocket: Custom Aruppi API Link
&nbsp;
&nbsp;
&nbsp; Link to access the [Aruppi API](https://aruppi-api.jeluchu.now.sh/api/v2)
&nbsp;
&nbsp;
Aruppi API has been developed to bring together all the information about Japanese culture, from anime and manga to the most secret places in Japan, its gastronomy and even its festivities. ## 📚 **Development Diary**
We are in continuous development to implement more features and improvements in the functioning of it, to later implement it in the mobile application. Describe the purpose of the project and give clues about what the code does.
For more information go to the following link [Diary Reference](./development_diary/README.md).
## **:wrench: Developer usage** ## **:wrench: Developer usage**
@ -45,15 +46,14 @@ you need to have installed these versions:
Then: Then:
- Choose a folder project in your system and switch in `cd [folder path]` - Choose a folder project in your system and switch in `cd [folder path]`
- Clone the repo in your folder path `git clone https://github.com/aruppi/aruppi-api`. - Clone the repo in your folder path `git clone https://github.com/aruppi/aruppi-api`
Here are the steps to get started with the project on both platforms, use the corresponding commands if **npm** or **yarn**.
--- ---
### **Installation** ### **Installation**
In order to install the project and all dependencies, enter in the project folder and run `npm install` or you can do the same with yarn with `yarn` in the project In order to install the project and all dependencies, enter in the project folder and run `npm install`
or you can do the same with yarn with `yarn` in the project
--- ---
@ -63,6 +63,8 @@ In order to install the project and all dependencies, enter in the project folde
npm start npm start
``` ```
or
```bash ```bash
yarn start yarn start
``` ```
@ -73,6 +75,8 @@ yarn start
npm build npm build
``` ```
or
```bash ```bash
yarn build yarn build
``` ```
@ -87,18 +91,47 @@ npm test
yarn test yarn test
``` ```
## 📖 API Documentation ---
## Deprecated v1 for 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.
But if you need to see the code or the operation of the old version you can do it
- [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.9 will remain functional until Aruppi App users fully migrate to version 1.5.0 of the app
## **:handshake: Contributing**
- Fork it!
- Create your feature branch: `git checkout -b my-new-feature`
- Commit your changes: `git commit -am 'Add some feature'`
- Push to the branch: `git push origin my-new-feature`
- Submit a pull request
---
### **:busts_in_silhouette: Credits**
**Documentation coming soon** at the following link: [**Aruppi Wiki**](https://github.com/aruppi/aruppi-api/wiki) - [Darkangeel](https://github.com/Darkangeel-hd) (System administration authority (SYSADM))
Where we will show more information about the calls and queries together with the response obtained or the different types of variables in some of the queries. - [Jéluchu](https://github.com/Jeluchu) (Android Developer, designer, and others)
- [Capitanwesler](https://github.com/capitanwesler) (Backend developer, web developer and others)
## Countdown to deprecation of v3 API ---
Aruppi API version 3.x.x has been deprecated, that's why all of you who are using it should migrate as soon as possible to version 4.x.x which we have already released. ### **:heart: Show your support**
Otherwise, if you want to use older versions you can host them yourself on your servers, and download the code in the corresponding branches of [**v2.x.x**](https://github.com/aruppi/aruppi-api/tree/v2) and [**v3.x.x**](https://github.com/aruppi/aruppi-api/tree/v3). In case you want to use an even lower version of the API we recommend you to have a look at this other version [**v1.x.x**](https://github.com/aruppi/aruppi-api-v1) Please :star: this repository if you like it or this project helped you!\
Feel free to open issues or submit pull-requests to help me improving my work.
Currently the Aruppi app on Android is already using the v4.x.x API services from version v2.0.8, which you can download from our website: [**Download Aruppi App**](https://aruppi.jeluchu.com/download) ---
### **📚 Projects that use the API** ### **📚 Projects that use the API**
@ -115,8 +148,8 @@ Currently the Aruppi app on Android is already using the v4.x.x API services fro
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/Fmaldonado6/Akiyama"> <a href="https://fmaldonado6.github.io/Akiyama/">
<img src="https://raw.githubusercontent.com/Fmaldonado6/Akiyama/master/images/logo/logo.png" width="75px;" alt="Jeluchu"/><br /> <img src="https://raw.githubusercontent.com/Fmaldonado6/Akiyama/master/images/logo/web-logo.png" width="75px;" alt="Jeluchu"/><br />
<sub> <sub>
<b>Akiyama</b> <b>Akiyama</b>
</sub> </sub>
@ -127,33 +160,13 @@ Currently the Aruppi app on Android is already using the v4.x.x API services fro
</tr> </tr>
</table> </table>
## Contributors ✨ ### **:robot: Author**
Here are the **main contributors to the API**, along with Aruppi's creator _*Jéluchu*_
<table> > You can follow me on
<tr> > [github](https://github.com/Jeluchu)&nbsp;&middot;&nbsp;[twitter](https://twitter.com/Jeluchu)
<td align="center"><a href="https://github.com/Jeluchu"><img src="https://avatars.githubusercontent.com/u/32357592?v=4" width="100px;" alt=""/><br /><sub><b>Jéluchu</b></sub></a><br /><a href="https://www.instagram.com/jeluchu/" title="Instagram">📸</a> <a href="https://about.jeluchu.com/" title="About Jelu">🌍</a> <a href="https://twitter.com/Jeluchu" title="Twitter">📢</a><a href="https://www.linkedin.com/in/jesusmariacalderon/" title="LinkedIn">🔍</a></td>
<td align="center"><a href="https://github.com/capitanwesler"><img src="https://avatars.githubusercontent.com/u/61250854?v=4" width="100px;" alt=""/><br /><sub><b>Guillermo</b></sub></a><br/><a href="https://www.facebook.com/profile.php?id=100009163736196" title="Facebook">👀</a> <a href="mailto:guillermo.campanudo@hotmail.com" title="E-mail">📧</a> <a href="https://www.linkedin.com/in/guillermo-campanudo/" title="LinkedIn">🔍</a></td>
<td align="center"><a href="https://github.com/Darkangeel-hd"><img src="https://i.pinimg.com/564x/24/73/c0/2473c02e2ac93f617a28b2b5058bb41d.jpg" width="100px;" alt=""/><br /><sub><b>Darkangeel-hd</b></sub></a><br /><a href="https://i.pinimg.com/originals/19/41/22/1941222eaee4de7d08dc21cc3993e791.jpg" title="Let's All Love lain!">👀</a></td>
</tr>
</table>
There are also **people who have made contributions** and therefore it is also important to highlight them. ---
<table>
<tr>
<td align="center"><a href="https://github.com/Fmaldonado6"><img src="https://avatars.githubusercontent.com/u/28517542?v=4" width="100px;" alt=""/><br /><sub><b>Fmaldonado6</b></sub></a><br/></td>
<td align="center"><a href="https://github.com/HernanSsj"><img src="https://avatars.githubusercontent.com/u/41026227?v=4" width="100px;" alt=""/><br /><sub><b>HernanSsj</b></sub></a><br/></td>
</tr>
</table>
#
### **:busts_in_silhouette: Credits**
- [Darkangeel](https://github.com/Darkangeel-hd) (System administration authority (SYSADM))
- [Jéluchu](https://github.com/Jeluchu) (Multiplatform Developer, designer, and others)
- [Capitanwesler](https://github.com/capitanwesler) (Backend developer, web developer and others)
Copyright © 2022 [Jéluchu](https://about.jeluchu.com/). Copyright © 2020 [Jéluchu](https://about.jeluchu.com/).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

8109
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "aruppi", "name": "aruppi",
"version": "4.2.2", "version": "4.0.0",
"description": "Aruppi is a custom API to obtain data from the Japanese culture for the mobile app", "description": "Aruppi is a custom API to obtain data from the Japanese culture for the mobile app",
"main": "./src/api/api.ts", "main": "./src/api/api.ts",
"scripts": { "scripts": {
@ -50,35 +50,37 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.6", "cheerio": "^1.0.0-rc.5",
"compose-middleware": "^5.0.1", "compose-middleware": "^5.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.3", "express": "^4.17.1",
"got": "^11.8.5", "got": "^11.8.1",
"helmet": "^4.5.0", "helmet": "^4.4.1",
"mongodb": "^3.6.6", "mongodb": "^3.6.4",
"mongoose": "^5.13.15", "mongoose": "^5.11.18",
"redis": "^3.1.2", "redis": "^3.0.2",
"rss-parser": "^3.12.0", "rss-parser": "^3.12.0",
"tough-cookie": "^4.0.0", "tough-cookie": "^4.0.0",
"ts-node-dev": "^1.1.1" "ts-node-dev": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/cheerio": "^0.22.28", "@types/cheerio": "^0.22.24",
"@types/cors": "^2.8.10", "@types/cors": "^2.8.10",
"@types/express": "^4.17.11", "@types/express": "^4.17.11",
"@types/node": "^15.0.2", "@types/node": "^14.14.31",
"@types/redis": "^2.8.28", "@types/redis": "^2.8.28",
"@types/tough-cookie": "^4.0.0", "@types/tough-cookie": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.15.2",
"eslint": "^7.24.0", "eslint": "^5.16.0",
"eslint-config-prettier": "^8.2.0", "eslint-config-airbnb-base": "^14.2.1",
"eslint-config-prettier": "^8.0.0",
"eslint-import-resolver-typescript": "^2.4.0",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.3.1",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"ts-node": "^9.1.1", "ts-node": "^9.1.1",
"typescript": "^4.2.4" "typescript": "^4.2.3"
} }
} }

@ -1,13 +1,11 @@
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { requestGot } from '../utils/requestCall'; import { requestGot } from '../utils/requestCall';
import { import {
imageUrlToBase64, animeFlvInfo,
jkanimeInfo, jkanimeInfo,
monoschinosInfo, monoschinosInfo,
tioanimeInfo,
videoServersJK, videoServersJK,
videoServersMonosChinos, videoServersMonosChinos,
videoServersTioAnime,
} from '../utils/util'; } from '../utils/util';
import { transformUrlServer } from '../utils/transformerUrl'; import { transformUrlServer } from '../utils/transformerUrl';
import AnimeModel, { Anime as ModelA } from '../database/models/anime.model'; import AnimeModel, { Anime as ModelA } from '../database/models/anime.model';
@ -17,6 +15,7 @@ import {
animeExtraInfo, animeExtraInfo,
getAnimeVideoPromo, getAnimeVideoPromo,
getAnimeCharacters, getAnimeCharacters,
getRelatedAnimesFLV,
getRelatedAnimesMAL, getRelatedAnimesMAL,
} from '../utils/util'; } from '../utils/util';
import urls from '../utils/urls'; import urls from '../utils/urls';
@ -81,10 +80,9 @@ interface Movie {
export default class AnimeController { export default class AnimeController {
async schedule(req: Request, res: Response, next: NextFunction) { async schedule(req: Request, res: Response, next: NextFunction) {
const { day } = req.params; const { day } = req.params;
let info: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`schedule_${hashStringMd5(day)}`, `schedule_${hashStringMd5(day)}`,
); );
@ -93,25 +91,23 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} data = await requestGot(`${urls.BASE_JIKAN}schedule/${day}`, {
info = await requestGot(`${urls.BASE_JIKAN}schedules?filter=${day}`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
}); });
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const animeList: Schedule[] = info.data.map((item: any) => ({ const animeList: Schedule[] = data[day].map((item: Schedule) => ({
title: item.titles.find((x: { type: string; }) => x.type === "Default").title, title: item.title,
malid: item.mal_id, malid: item.mal_id,
image: item.images.jpg.image_url, image: item.image_url,
})); }));
if (animeList.length > 0) { if (animeList.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -119,13 +115,12 @@ export default class AnimeController {
JSON.stringify({ day: animeList }), JSON.stringify({ day: animeList }),
); );
/* After 6hrs expire the key. */ /* After 24hrs expire the key. */
redisClient.expire( redisClient.expireat(
`schedule_${hashStringMd5(day)}`, `schedule_${hashStringMd5(day)}`,
+ 21600, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
day: animeList, day: animeList,
@ -137,10 +132,9 @@ export default class AnimeController {
async top(req: Request, res: Response, next: NextFunction) { async top(req: Request, res: Response, next: NextFunction) {
const { type, subtype, page } = req.params; const { type, subtype, page } = req.params;
let info: any; let data: any;
try { try {
if (redisClient.connected) {
let resultQueryRedis: any; let resultQueryRedis: any;
if (subtype) { if (subtype) {
@ -157,30 +151,28 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
if (subtype !== undefined) { if (subtype !== undefined) {
info = await requestGot( data = await requestGot(
`${urls.BASE_JIKAN}top/${type}?filter=${subtype}&page=${page}`, `${urls.BASE_JIKAN}top/${type}/${page}/${subtype}`,
{ parse: true, scrapy: false }, { parse: true, scrapy: false },
); );
} else { } else {
info = await requestGot(`${urls.BASE_JIKAN}top/${type}?page=${page}`, { data = await requestGot(`${urls.BASE_JIKAN}top/${type}/${page}`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
}); });
} }
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const top: Top[] = info.data.map((item: any, index: number) => ({ const top: Top[] = data.top.map((item: Top) => ({
// A little hacky way to fix null ranks rank: item.rank,
rank: item.rank || index + 1 + (info.pagination.current_page-1)*info.pagination.items.per_page, title: item.title,
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
url: item.url, url: item.url,
image_url: item.images.jpg.image_url, image_url: item.image_url,
type: type, type: type,
subtype: subtype, subtype: subtype,
page: page, page: page,
@ -188,7 +180,6 @@ export default class AnimeController {
})); }));
if (top.length > 0) { if (top.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
if (subtype) { if (subtype) {
redisClient.set( redisClient.set(
@ -215,7 +206,6 @@ export default class AnimeController {
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
} }
}
return res.status(200).json({ top }); return res.status(200).json({ top });
} else { } else {
@ -230,7 +220,6 @@ export default class AnimeController {
data = await requestGot(`${urls.BASE_ANIMEFLV}api/animes/list`, { data = await requestGot(`${urls.BASE_ANIMEFLV}api/animes/list`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
spoof: true,
}); });
} catch (err) { } catch (err) {
return next(err); return next(err);
@ -252,13 +241,10 @@ export default class AnimeController {
} }
async getLastEpisodes(req: Request, res: Response, next: NextFunction) { async getLastEpisodes(req: Request, res: Response, next: NextFunction) {
let data: any;
let lastEpisodes;
let episodes: Episode[] = []; let episodes: Episode[] = [];
let animeList: any[] = [];
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`, `lastEpisodes_${hashStringMd5('lastEpisodes')}`,
); );
@ -267,39 +253,32 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}LatestEpisodesAdded`,
lastEpisodes = await requestGot(`${urls.BASE_ARUPPI_MONOSCHINOS}lastest`, { {
scrapy: false,
parse: true, parse: true,
}); scrapy: false,
},
);
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
for (const anime of lastEpisodes) { for (const episode of data.episodes) {
animeList.push({ const formattedEpisode: Episode = {
id: `ver/${anime.id}`, id: '12345/' + episode.id,
title: anime.title, title: episode.title,
image: anime.image, image: episode.poster,
episode: anime.no, episode: episode.episode,
}); servers: await transformUrlServer(episode.servers),
} };
for (const anime of animeList) { episodes.push(formattedEpisode);
episodes.push({
id: anime.id,
title: anime.title,
image: await imageUrlToBase64(anime.image),
episode: anime.episode,
servers: await videoServersMonosChinos(anime.id),
});
} }
if (episodes.length > 0) { if (episodes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -313,7 +292,6 @@ export default class AnimeController {
`lastEpisodes_${hashStringMd5('lastEpisodes')}`, `lastEpisodes_${hashStringMd5('lastEpisodes')}`,
parseInt(`${+new Date() / 1000}`, 10) + 1800, parseInt(`${+new Date() / 1000}`, 10) + 1800,
); );
}
res.status(200).json({ res.status(200).json({
episodes, episodes,
@ -329,7 +307,6 @@ export default class AnimeController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`contentTv_${hashStringMd5(`${type}:${page}`)}`, `contentTv_${hashStringMd5(`${type}:${page}`)}`,
); );
@ -338,9 +315,7 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${ `${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1) url.charAt(0).toUpperCase() + url.slice(1)
@ -350,6 +325,7 @@ export default class AnimeController {
scrapy: false, scrapy: false,
}, },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -371,7 +347,6 @@ export default class AnimeController {
}); });
if (animes.length > 0) { if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -385,7 +360,6 @@ export default class AnimeController {
`contentTv_${hashStringMd5(`${type}:${page}`)}`, `contentTv_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
animes, animes,
@ -401,7 +375,6 @@ export default class AnimeController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`, `contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
); );
@ -410,9 +383,7 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${ `${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1) url.charAt(0).toUpperCase() + url.slice(1)
@ -422,6 +393,7 @@ export default class AnimeController {
scrapy: false, scrapy: false,
}, },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -443,7 +415,6 @@ export default class AnimeController {
}); });
if (animes.length > 0) { if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -457,7 +428,6 @@ export default class AnimeController {
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`, `contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
animes, animes,
@ -473,7 +443,6 @@ export default class AnimeController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`contentOva_${hashStringMd5(`${type}:${page}`)}`, `contentOva_${hashStringMd5(`${type}:${page}`)}`,
); );
@ -482,9 +451,7 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${ `${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1) url.charAt(0).toUpperCase() + url.slice(1)
@ -494,6 +461,7 @@ export default class AnimeController {
scrapy: false, scrapy: false,
}, },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -515,7 +483,6 @@ export default class AnimeController {
}); });
if (animes.length > 0) { if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -529,7 +496,6 @@ export default class AnimeController {
`contentOva_${hashStringMd5(`${type}:${page}`)}`, `contentOva_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
animes, animes,
@ -545,7 +511,6 @@ export default class AnimeController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`, `contentMovie_${hashStringMd5(`${type}:${page}`)}`,
); );
@ -554,9 +519,7 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${ `${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1) url.charAt(0).toUpperCase() + url.slice(1)
@ -566,6 +529,7 @@ export default class AnimeController {
scrapy: false, scrapy: false,
}, },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -587,7 +551,6 @@ export default class AnimeController {
}); });
if (animes.length > 0) { if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -601,7 +564,6 @@ export default class AnimeController {
`contentMovie_${hashStringMd5(`${type}:${page}`)}`, `contentMovie_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
animes, animes,
@ -617,7 +579,6 @@ export default class AnimeController {
let episodes: any; let episodes: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`episodes_${hashStringMd5(title)}`, `episodes_${hashStringMd5(title)}`,
); );
@ -626,25 +587,24 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
searchAnime = await AnimeModel.findOne({ searchAnime = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }], $or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
}); });
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
switch (searchAnime?.source) { switch (searchAnime?.source) {
case 'animeflv':
episodes = await animeFlvInfo(searchAnime?.id);
break;
case 'jkanime': case 'jkanime':
episodes = await jkanimeInfo(searchAnime?.id, searchAnime?.mal_id); episodes = await jkanimeInfo(searchAnime?.id);
break; break;
case 'monoschinos': case 'monoschinos':
episodes = await monoschinosInfo(searchAnime?.id, searchAnime?.mal_id); episodes = await monoschinosInfo(searchAnime?.id);
break;
case 'tioanime':
episodes = await tioanimeInfo(searchAnime?.id, searchAnime?.mal_id);
break; break;
default: default:
episodes = undefined; episodes = undefined;
@ -652,7 +612,6 @@ export default class AnimeController {
} }
if (episodes) { if (episodes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -666,7 +625,6 @@ export default class AnimeController {
`episodes_${hashStringMd5(title)}`, `episodes_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ episodes }); res.status(200).json({ episodes });
} else { } else {
@ -679,7 +637,6 @@ export default class AnimeController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`servers_${hashStringMd5(id)}`, `servers_${hashStringMd5(id)}`,
); );
@ -688,43 +645,23 @@ export default class AnimeController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} if (isNaN(parseInt(id.split('/')[0]))) {
if (id.split('/')[0] === 'ver') {
let indicator = false;
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersTioAnime(id);
if (!data.name) {
indicator = true;
}
}
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersMonosChinos(id); data = await videoServersMonosChinos(id);
} else {
if (!data.name) { data = await videoServersJK(id);
console.log(data.name);
indicator = true;
}
} }
} else {
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`,
{ parse: true, scrapy: false },
);
if (!indicator) { data = await transformUrlServer(data.servers);
data = undefined;
indicator = true;
/*
This part is just for handling the error
if the two above doesn't complete the operation
does not make sense to have the getServers from
JKAnime.
*/
} }
if (data) { if (data) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -738,12 +675,12 @@ export default class AnimeController {
`servers_${hashStringMd5(id)}`, `servers_${hashStringMd5(id)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ servers: data }); res.status(200).json({ servers: data });
} else { } else {
res.status(500).json({ message: 'Aruppi lost in the shell' }); res.status(500).json({ message: 'Aruppi lost in the shell' });
} }
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -759,10 +696,28 @@ export default class AnimeController {
return next(err); return next(err);
} }
switch (animeQuery[0].source) {
case 'animeflv':
animeResult = { animeResult = {
title: animeQuery[0].title || null, title: animeQuery[0].title || null,
poster: animeQuery[0].poster || null, poster: animeQuery[0].poster || null,
synopsis: animeQuery[0].description || null, synopsis: animeQuery[0].description || null,
status: animeQuery[0].state || null,
type: animeQuery[0].type || null,
rating: animeQuery[0].score || null,
genres: animeQuery[0].genres || null,
moreInfo: [await animeExtraInfo(animeQuery[0].mal_id)],
promo: await getAnimeVideoPromo(animeQuery[0].mal_id),
characters: await getAnimeCharacters(animeQuery[0].mal_id),
related: await getRelatedAnimesFLV(animeQuery[0].id),
};
break;
case 'jkanime':
animeResult = {
title: animeQuery[0].title || null,
poster: animeQuery[0].poster || null,
synopsis: animeQuery[0].description || null,
status: animeQuery[0].state || null,
type: animeQuery[0].type || null, type: animeQuery[0].type || null,
rating: animeQuery[0].score || null, rating: animeQuery[0].score || null,
genres: animeQuery[0].genres || null, genres: animeQuery[0].genres || null,
@ -771,6 +726,11 @@ export default class AnimeController {
characters: await getAnimeCharacters(animeQuery[0].mal_id), characters: await getAnimeCharacters(animeQuery[0].mal_id),
related: await getRelatedAnimesMAL(animeQuery[0].mal_id), related: await getRelatedAnimesMAL(animeQuery[0].mal_id),
}; };
break;
default:
animeResult = undefined;
break;
}
if (animeResult) { if (animeResult) {
res.set('Cache-Control', 'no-store'); res.set('Cache-Control', 'no-store');

@ -1,12 +1,14 @@
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { requestGot } from '../utils/requestCall'; import { requestGot } from '../utils/requestCall';
import AnimeModel, { Anime } from '../database/models/anime.model'; import AnimeModel, { Anime } from '../database/models/anime.model';
import GenreModel, { Genre } from '../database/models/genre.model';
import util from 'util'; import util from 'util';
import { hashStringMd5 } from '../utils/util'; import { hashStringMd5 } from '../utils/util';
import { import {
animeExtraInfo, animeExtraInfo,
getAnimeVideoPromo, getAnimeVideoPromo,
getAnimeCharacters, getAnimeCharacters,
getRelatedAnimesFLV,
getRelatedAnimesMAL, getRelatedAnimesMAL,
} from '../utils/util'; } from '../utils/util';
import urls from '../utils/urls'; import urls from '../utils/urls';
@ -59,6 +61,7 @@ export default class DirectoryController {
poster: item.poster, poster: item.poster,
type: item.type, type: item.type,
genres: item.genres, genres: item.genres,
state: item.state,
score: item.score, score: item.score,
source: item.source, source: item.source,
description: item.description, description: item.description,
@ -84,6 +87,7 @@ export default class DirectoryController {
poster: item.poster, poster: item.poster,
type: item.type, type: item.type,
genres: item.genres, genres: item.genres,
state: item.state,
score: item.score, score: item.score,
source: item.source, source: item.source,
description: item.description, description: item.description,
@ -104,10 +108,9 @@ export default class DirectoryController {
async getSeason(req: Request, res: Response, next: NextFunction) { async getSeason(req: Request, res: Response, next: NextFunction) {
const { year, type } = req.params; const { year, type } = req.params;
let info: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`season_${hashStringMd5(`${year}:${type}`)}`, `season_${hashStringMd5(`${year}:${type}`)}`,
); );
@ -116,27 +119,25 @@ export default class DirectoryController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} data = await requestGot(`${urls.BASE_JIKAN}season/${year}/${type}`, {
info = await requestGot(`${urls.BASE_JIKAN}seasons/${year}/${type}`, {
scrapy: false, scrapy: false,
parse: true, parse: true,
}); });
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const season: TypeAnime[] = info.data.map((item: any) => { const season: TypeAnime[] = data.anime.map((item: any) => {
return { return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title, title: item.title,
image: item.images.jpg.image_url, image: item.image_url,
genres: item.genres.map((genre: any) => genre.name), genres: item.genres.map((genre: any) => genre.name),
}; };
}); });
if (season.length > 0) { if (season.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -150,7 +151,6 @@ export default class DirectoryController {
`season_${hashStringMd5(`${year}:${type}`)}`, `season_${hashStringMd5(`${year}:${type}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ res.status(200).json({
season, season,
@ -161,10 +161,9 @@ export default class DirectoryController {
} }
async allSeasons(req: Request, res: Response, next: NextFunction) { async allSeasons(req: Request, res: Response, next: NextFunction) {
let info: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`allSeasons_${hashStringMd5('allSeasons')}`, `allSeasons_${hashStringMd5('allSeasons')}`,
); );
@ -173,18 +172,17 @@ export default class DirectoryController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} data = await requestGot(`${urls.BASE_JIKAN}season/archive`, {
info = await requestGot(`${urls.BASE_JIKAN}seasons`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
}); });
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const archive: Archive[] = info.data.map((item: any) => { const archive: Archive[] = data.archive.map((item: any) => {
return { return {
year: item.year, year: item.year,
seasons: item.seasons, seasons: item.seasons,
@ -192,7 +190,6 @@ export default class DirectoryController {
}); });
if (archive.length > 0) { if (archive.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -206,7 +203,6 @@ export default class DirectoryController {
`allSeasons_${hashStringMd5('allSeasons')}`, `allSeasons_${hashStringMd5('allSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ archive }); res.status(200).json({ archive });
} else { } else {
@ -215,10 +211,9 @@ export default class DirectoryController {
} }
async laterSeasons(req: Request, res: Response, next: NextFunction) { async laterSeasons(req: Request, res: Response, next: NextFunction) {
let info: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`laterSeasons_${hashStringMd5('laterSeasons')}`, `laterSeasons_${hashStringMd5('laterSeasons')}`,
); );
@ -227,27 +222,25 @@ export default class DirectoryController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
} data = await requestGot(`${urls.BASE_JIKAN}season/later`, {
info = await requestGot(`${urls.BASE_JIKAN}seasons/upcoming`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
}); });
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const future: Season[] = info.data.map((item: any) => { const future: Season[] = data.anime.map((item: any) => {
return { return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title, title: item.title,
image: item.images.jpg.image_url, image: item.image_url,
malink: item.url, malink: item.url,
}; };
}); });
if (future.length > 0) { if (future.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -261,7 +254,6 @@ export default class DirectoryController {
`laterSeasons_${hashStringMd5('laterSeasons')}`, `laterSeasons_${hashStringMd5('laterSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ future }); res.status(200).json({ future });
} else { } else {
@ -275,7 +267,6 @@ export default class DirectoryController {
let resultAnime: any; let resultAnime: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`moreInfo_${hashStringMd5(title)}`, `moreInfo_${hashStringMd5(title)}`,
); );
@ -284,35 +275,67 @@ export default class DirectoryController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
resultQuery = await AnimeModel.findOne({ resultQuery = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }], $or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
}); });
const extraInfo: any = await animeExtraInfo(resultQuery!.mal_id); switch (resultQuery?.source) {
case 'animeflv':
resultAnime = { resultAnime = {
//aruppi_key: hashStringMd5(title),
title: resultQuery?.title, title: resultQuery?.title,
poster: resultQuery?.poster, poster: resultQuery?.poster,
synopsis: resultQuery?.description, synopsis: resultQuery?.description,
status: !extraInfo.aired.to ? 'En emisión' : 'Finalizado', status: resultQuery?.state,
type: resultQuery?.type, type: resultQuery?.type,
rating: resultQuery?.score, rating: resultQuery?.score,
genres: resultQuery?.genres, genres: resultQuery?.genres,
moreInfo: [extraInfo], moreInfo: [await animeExtraInfo(resultQuery!.mal_id)],
promo: await getAnimeVideoPromo(resultQuery!.mal_id),
characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesFLV(resultQuery!.id),
};
break;
case 'jkanime':
resultAnime = {
title: resultQuery?.title,
poster: resultQuery?.poster,
synopsis: resultQuery?.description,
status: resultQuery?.state,
type: resultQuery?.type,
rating: resultQuery?.score,
genres: resultQuery?.genres,
moreInfo: [await animeExtraInfo(resultQuery!.mal_id)],
promo: await getAnimeVideoPromo(resultQuery!.mal_id),
characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesMAL(resultQuery!.mal_id),
};
break;
case 'monoschinos':
resultAnime = {
title: resultQuery?.title,
poster: resultQuery?.poster,
synopsis: resultQuery?.description,
status: resultQuery?.state,
type: resultQuery?.type,
rating: resultQuery?.score,
genres: resultQuery?.genres,
moreInfo: [await animeExtraInfo(resultQuery!.mal_id)],
promo: await getAnimeVideoPromo(resultQuery!.mal_id), promo: await getAnimeVideoPromo(resultQuery!.mal_id),
characters: await getAnimeCharacters(resultQuery!.mal_id), characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesMAL(resultQuery!.mal_id), related: await getRelatedAnimesMAL(resultQuery!.mal_id),
}; };
break;
default:
resultAnime = undefined;
break;
}
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
if (resultAnime) { if (resultAnime) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -326,7 +349,6 @@ export default class DirectoryController {
`moreInfo_${hashStringMd5(title)}`, `moreInfo_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json(resultAnime); res.status(200).json(resultAnime);
} else { } else {
@ -364,97 +386,30 @@ export default class DirectoryController {
async getAnimeGenres(req: Request, res: Response, next: NextFunction) { async getAnimeGenres(req: Request, res: Response, next: NextFunction) {
const { genre, order, page } = req.params; const { genre, order, page } = req.params;
let result: any; let data: Genre[];
let resultReq: any;
const genres: any = {
accion: 'Acción',
'artes-marciales': 'Artes Marciales',
aventura: 'Aventuras',
carreras: 'Carreras',
'ciencia-ficcion': 'Ciencia Ficción',
comedia: 'Comedia',
demencia: 'Demencia',
demonios: 'Demonios',
deportes: 'Deportes',
drama: 'Drama',
ecchi: 'Ecchi',
escolares: 'Escolares',
espacial: 'Espacial',
fantasia: 'Fantasía',
harem: 'Harem',
historico: 'Historico',
infantil: 'Infantil',
josei: 'Josei',
juegos: 'Juegos',
magia: 'Magia',
mecha: 'Mecha',
militar: 'Militar',
misterio: 'Misterio',
musica: 'Música',
parodia: 'Parodia',
policia: 'Policía',
psicologico: 'Psicológico',
'recuentos-de-la-vida': 'Recuentos de la vida',
romance: 'Romance',
samurai: 'Samurai',
seinen: 'Seinen',
shoujo: 'Shoujo',
shounen: 'Shounen',
sobrenatural: 'Sobrenatural',
superpoderes: 'Superpoderes',
suspenso: 'Suspenso',
terror: 'Terror',
vampiros: 'Vampiros',
yaoi: 'Yaoi',
yuri: 'Yuri',
};
try { try {
if (genre === undefined && order === undefined && page === undefined) { if (genre === undefined && order === undefined && page === undefined) {
result = await AnimeModel.aggregate([{ $sample: { size: 25 } }]); data = await GenreModel.find();
} else { } else {
// eslint-disable-next-line no-prototype-builtins if (page !== undefined) {
if (genres.hasOwnProperty(genre)) { resultReq = await requestGot(
if (page !== undefined && parseInt(page) > 1) { `${urls.BASE_ANIMEFLV_JELU}Genres/${genre}/${order}/${page}`,
if (order === 'asc') { { parse: true, scrapy: false },
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page))
.sort({ title: 'ascending' });
} else if (order === 'desc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page))
.sort({ title: 'descending' });
} else {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page));
}
} else {
if (order === 'asc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.sort({ title: 'ascending' });
} else if (order === 'desc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.sort({ title: 'descending' });
} else {
result = await AnimeModel.find({ genres: genres[genre] }).limit(
25,
); );
}
}
} else { } else {
return res.status(500).json({ message: 'Aruppi lost in the shell' }); resultReq = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}Genres/${genre}/${order}/1`,
{ parse: true, scrapy: false },
);
} }
} }
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
const animes: any[] = result.map((item: any) => { const animes: any[] = resultReq.animes.map((item: any) => {
return { return {
id: item.id, id: item.id,
title: item.title.trim(), title: item.title.trim(),

@ -69,7 +69,6 @@ export default class UtilsController {
let feed: CustomFeed & Parser.Output<CustomItem>; let feed: CustomFeed & Parser.Output<CustomItem>;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`anitakume_${hashStringMd5('anitakume')}`, `anitakume_${hashStringMd5('anitakume')}`,
); );
@ -78,10 +77,9 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
feed = await parser.parseURL(urls.BASE_IVOOX); feed = await parser.parseURL(urls.BASE_IVOOX);
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -118,7 +116,6 @@ export default class UtilsController {
}); });
if (podcast.length > 0) { if (podcast.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -132,7 +129,6 @@ export default class UtilsController {
`anitakume_${hashStringMd5('anitakume')}`, `anitakume_${hashStringMd5('anitakume')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ podcast }); res.status(200).json({ podcast });
} else { } else {
@ -144,6 +140,11 @@ export default class UtilsController {
const news: News[] = []; const news: News[] = [];
const pagesRss: rssPage[] = [ const pagesRss: rssPage[] = [
{ url: urls.BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' }, { url: urls.BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
{
url: urls.BASE_PALOMITRON,
author: 'Palomitron',
content: 'description',
},
{ {
url: urls.BASE_RAMENPARADOS, url: urls.BASE_RAMENPARADOS,
author: 'Ramen para dos', author: 'Ramen para dos',
@ -157,7 +158,6 @@ export default class UtilsController {
]; ];
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`news_${hashStringMd5('news')}`, `news_${hashStringMd5('news')}`,
); );
@ -166,9 +166,7 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
for (const rssPage of pagesRss) { for (const rssPage of pagesRss) {
const feed = await parser.parseURL(rssPage.url); const feed = await parser.parseURL(rssPage.url);
@ -186,12 +184,12 @@ export default class UtilsController {
news.push(formattedObject); news.push(formattedObject);
}); });
} }
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
if (news.length > 0) { if (news.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -205,7 +203,6 @@ export default class UtilsController {
`news_${hashStringMd5('news')}`, `news_${hashStringMd5('news')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ news }); res.status(200).json({ news });
} else { } else {
@ -218,7 +215,6 @@ export default class UtilsController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`images_${hashStringMd5(title)}`, `images_${hashStringMd5(title)}`,
); );
@ -227,15 +223,12 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_QWANT}t=images&q=${encodeURIComponent( `${urls.BASE_QWANT}count=51&q=${title}&t=images&safesearch=1&locale=es_ES&uiv=4`,
title, { scrapy: false, parse: true },
)}&count=51&locale=es_ES&safesearch=1`,
{ scrapy: false, parse: true, spoof: true, },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -243,13 +236,12 @@ export default class UtilsController {
const results: any[] = data.data.result.items.map((item: any) => { const results: any[] = data.data.result.items.map((item: any) => {
return { return {
type: item.thumb_type, type: item.thumb_type,
thumbnail: `${item.thumbnail}`, thumbnail: `https:${item.thumbnail}`,
fullsize: `${item.media_fullsize}`, fullsize: `https:${item.media_fullsize}`,
}; };
}); });
if (results.length > 0) { if (results.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -263,9 +255,7 @@ export default class UtilsController {
`images_${hashStringMd5(title)}`, `images_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.set('Cache-Control', 'max-age=604800');
res.status(200).json({ images: results }); res.status(200).json({ images: results });
} else { } else {
res.status(500).json({ message: 'Aruppi lost in the shell' }); res.status(500).json({ message: 'Aruppi lost in the shell' });
@ -277,7 +267,6 @@ export default class UtilsController {
let data: any; let data: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`videos_${hashStringMd5(channelId)}`, `videos_${hashStringMd5(channelId)}`,
); );
@ -286,13 +275,12 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
data = await requestGot( data = await requestGot(
`${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`, `${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`,
{ scrapy: false, parse: true }, { scrapy: false, parse: true },
); );
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -308,7 +296,6 @@ export default class UtilsController {
}); });
if (results.length > 0) { if (results.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -322,66 +309,6 @@ export default class UtilsController {
`videos_${hashStringMd5(channelId)}`, `videos_${hashStringMd5(channelId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ videos: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getPlaylists(req: Request, res: Response, next: NextFunction) {
const { playlistId } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = redisClient.get(
`videos_${hashStringMd5(playlistId)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_YOUTUBE_PLAYLIST}${playlistId}`,
{ scrapy: false, parse: true },
);
} catch (err) {
return next(err);
}
const results: any[] = data.items.map((item: any) => {
return {
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
};
});
if (results.length > 0) {
if (redisClient.connected) {
/!* Set the key in the redis cache. *!/
redisClient.set(
`videos_${hashStringMd5(playlistId)}`,
JSON.stringify({ videos: results }),
);
/!* After 24hrs expire the key. *!/
redisClient.expireat(
`videos_${hashStringMd5(playlistId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ videos: results }); res.status(200).json({ videos: results });
} else { } else {
@ -520,7 +447,6 @@ export default class UtilsController {
let themes: any; let themes: any;
try { try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get( const resultQueryRedis: any = await redisClient.get(
`oped_${hashStringMd5(title)}`, `oped_${hashStringMd5(title)}`,
); );
@ -529,16 +455,14 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
themes = await structureThemes(await themeParser.serie(title), true); themes = await structureThemes(await themeParser.serie(title), true);
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
if (themes) { if (themes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
redisClient.set( redisClient.set(
@ -552,7 +476,6 @@ export default class UtilsController {
`oped_${hashStringMd5(title)}`, `oped_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
}
res.status(200).json({ themes }); res.status(200).json({ themes });
} else { } else {
@ -566,7 +489,6 @@ export default class UtilsController {
let resultQueryRedis: any; let resultQueryRedis: any;
try { try {
if (redisClient.connected) {
if (year) { if (year) {
resultQueryRedis = await redisClient.get( resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5(year)}`, `themesyear_${hashStringMd5(year)}`,
@ -581,20 +503,18 @@ export default class UtilsController {
const resultRedis: any = JSON.parse(resultQueryRedis); const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis); return res.status(200).json(resultRedis);
} } else {
}
if (year === undefined) { if (year === undefined) {
themes = await themeParser.allYears(); themes = await themeParser.allYears();
} else { } else {
themes = await structureThemes(await themeParser.year(year), false); themes = await structureThemes(await themeParser.year(year), false);
} }
}
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
if (themes.length > 0) { if (themes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */ /* Set the key in the redis cache. */
if (year) { if (year) {
@ -622,7 +542,6 @@ export default class UtilsController {
parseInt(`${+new Date() / 1000}`, 10) + 7200, parseInt(`${+new Date() / 1000}`, 10) + 7200,
); );
} }
}
res.status(200).json({ themes }); res.status(200).json({ themes });
} else { } else {
@ -637,7 +556,6 @@ export default class UtilsController {
data = await requestGot(`${urls.BASE_THEMEMOE}roulette`, { data = await requestGot(`${urls.BASE_THEMEMOE}roulette`, {
parse: true, parse: true,
scrapy: false, scrapy: false,
spoof: true,
}); });
} catch (err) { } catch (err) {
return next(err); return next(err);

@ -40,30 +40,8 @@ export const redisClient: RedisClient = redis.createClient({
host: process.env.REDIS_HOST, host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT!), port: parseInt(process.env.REDIS_PORT!),
password: process.env.REDIS_PASSWORD, password: process.env.REDIS_PASSWORD,
retry_strategy: function (options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error('The server refused the connection');
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error('Retry time exhausted');
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
}); });
redisClient.on('connect', () => { redisClient.on('connect', () => {
console.log('Redis connected: redis.'); console.log('Redis connected: redis.');
}); });
redisClient.on('error', function (err) {
console.log('Redis error: ' + err);
});

@ -12,6 +12,7 @@ export interface Anime extends Document {
poster: string; poster: string;
type: string; type: string;
genres: Types.Array<string>; genres: Types.Array<string>;
state: string;
score: string; score: string;
source: string; source: string;
description: string; description: string;
@ -25,6 +26,7 @@ const AnimeSchema: Schema = new Schema({
poster: { type: String }, poster: { type: String },
type: { type: String }, type: { type: String },
genres: [{ type: String }], genres: [{ type: String }],
state: { type: String },
score: { type: String }, score: { type: String },
source: { type: String }, source: { type: String },
description: { type: String }, description: { type: String },

@ -26,7 +26,7 @@ routes.get('/api/v4/', (req: Request, res: Response) => {
res.json({ res.json({
message: 'Aruppi /api - 🎏', message: 'Aruppi /api - 🎏',
author: 'Jéluchu', author: 'Jéluchu',
version: '4.2.2', version: '4.0.0',
credits: 'The bitch loves /apis that offers data to Aruppi App', credits: 'The bitch loves /apis that offers data to Aruppi App',
entries: [ entries: [
{ {
@ -52,7 +52,6 @@ routes.get('/api/v4/', (req: Request, res: Response) => {
Search: '/api/v4/search/:title', Search: '/api/v4/search/:title',
Images: '/api/v4/images/:query', Images: '/api/v4/images/:query',
Videos: '/api/v4/videos/:channelId', Videos: '/api/v4/videos/:channelId',
Playlist: '/api/v4/playlistVideos/:playlistId',
'Type Videos': '/api/v4/sectionedVideos/:type', 'Type Videos': '/api/v4/sectionedVideos/:type',
Radios: '/api/v4/radio', Radios: '/api/v4/radio',
'All Themes': '/api/v4/allThemes', 'All Themes': '/api/v4/allThemes',
@ -105,7 +104,6 @@ routes.get('/api/v4/anitakume', utilsController.getAnitakume);
routes.get('/api/v4/news', utilsController.getNews); routes.get('/api/v4/news', utilsController.getNews);
routes.get('/api/v4/images/:title', utilsController.getImages); routes.get('/api/v4/images/:title', utilsController.getImages);
routes.get('/api/v4/videos/:channelId', utilsController.getVideos); routes.get('/api/v4/videos/:channelId', utilsController.getVideos);
routes.get('/api/v4/playlistVideos/:playlistId', utilsController.getPlaylists);
routes.get('/api/v4/sectionedVideos/:type', utilsController.getSectionVideos); routes.get('/api/v4/sectionedVideos/:type', utilsController.getSectionVideos);
routes.get('/api/v4/radio', utilsController.getRadioStations); routes.get('/api/v4/radio', utilsController.getRadioStations);
routes.get('/api/v4/allThemes', utilsController.getAllThemes); routes.get('/api/v4/allThemes', utilsController.getAllThemes);

@ -7,6 +7,7 @@ import {
createConnectionMongo, createConnectionMongo,
} from './database/connection'; } from './database/connection';
import routes from './routes'; import routes from './routes';
import { Server } from 'node:http';
const app: Application = express(); const app: Application = express();
@ -29,7 +30,7 @@ app.use(errorHandler);
is going to listen in the server. is going to listen in the server.
ex: PORT=3000. ex: PORT=3000.
*/ */
const server = app.listen(process.env.PORT_LISTEN || 3000); const server: Server = app.listen(process.env.PORT_LISTEN || 3000);
function shutdown(): void { function shutdown(): void {
server.close(); server.close();

@ -285,7 +285,6 @@ async function redditocall(href: string) {
const resp = await requestGot(urls.REDDIT_ANIMETHEMES + href + '.json', { const resp = await requestGot(urls.REDDIT_ANIMETHEMES + href + '.json', {
parse: true, parse: true,
scrapy: false, scrapy: false,
spoof: true,
}); });
return cheerio.load(getHTML(resp.data.content_html)); return cheerio.load(getHTML(resp.data.content_html));

@ -1,52 +1,28 @@
import got from 'got'; import got from 'got';
import cheerio from 'cheerio'; import cheerio from 'cheerio';
import { CookieJar } from 'tough-cookie'; import { CookieJar } from 'tough-cookie';
// @ts-ignore
import * as got_pjson from 'got/package.json'
const pjson = require('../../package.json');
const cookieJar = new CookieJar(); const cookieJar = new CookieJar();
const aruppi_options: any = {
cookieJar,
'headers': {
'user-agent': `Aruppi-API/${pjson.version} ${got_pjson.name}/${got_pjson.version}`,
'x-client': 'aruppi-api'
},
};
interface Options { interface Options {
scrapy?: boolean, scrapy: boolean;
parse?: boolean, parse: boolean;
spoof?: boolean
} }
export const requestGot = async ( export const requestGot = async (
url: string, url: string,
options?: Options, options: Options,
): Promise<any> => { ): Promise<any> => {
const got_options: any = {...got.defaults.options, ...aruppi_options} if (options !== undefined) {
if (options) {
if (options.spoof != null) {
got_options.headers["user-agent"] = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/69.0";
delete got_options.headers['x-client'];
if (!options.spoof)
got_options.headers['user-agent'] = got.defaults.options.headers['user-agent'];
} else if (process.env.ALPI_KEY && (new URL(url)).hostname.match(/\.jeluchu\.xyz$/)) {
got_options.headers['x-aruppi-key'] = process.env.ALPI_KEY;
}
if (options.scrapy) { if (options.scrapy) {
const response = await got(url, got_options); const response = await got(url, { cookieJar });
return cheerio.load(response.body); return await cheerio.load(response.body);
} }
if (options.parse) { if (options.parse) {
got_options.responseType = 'json'; return await got(url, { cookieJar }).json();
const response = await got(url, got_options);
return response.body;
} }
} else {
return await got.get(url, { cookieJar });
} }
const response = await got.get(url, got_options);
return response;
}; };

@ -1,23 +1,22 @@
export default { export default {
BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/', BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/',
BASE_ANIMEFLV: 'https://www3.animeflv.net/', BASE_ANIMEFLV: 'https://animeflv.net/',
BASE_MONOSCHINOS: 'https://monoschinos2.com/', BASE_MONOSCHINOS: 'https://monoschinos2.com/',
BASE_TIOANIME: 'https://tioanime.com/',
BASE_JKANIME: 'https://jkanime.net/', BASE_JKANIME: 'https://jkanime.net/',
BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/', BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/',
BASE_YOUTUBE: 'https://aruppi.jeluchu.xyz/api/Youtube/?channelId=', BASE_YOUTUBE: 'https://aruppi.jeluchu.xyz/api/Youtube/?channelId=',
BASE_YOUTUBE_PLAYLIST: 'https://aruppi.jeluchu.xyz/api/Youtube/playlist/?playlistId=', BASE_JIKAN: 'https://aruppi.jeluchu.xyz/apis/jikan/v3/',
BASE_JIKAN: 'https://aruppi.jeluchu.xyz/apis/jikan/v4/', BASE_IVOOX:
BASE_IVOOX: 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml', 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml',
BASE_KUDASAI: 'https://somoskudasai.com/feed/', BASE_KUDASAI: 'https://somoskudasai.com/feed/',
BASE_PALOMITRON: 'https://elpalomitron.com/category/animemanga/feed/',
BASE_RAMENPARADOS: 'https://ramenparados.com/category/noticias/anime/feed/', BASE_RAMENPARADOS: 'https://ramenparados.com/category/noticias/anime/feed/',
BASE_CRUNCHYROLL: 'https://www.crunchyroll.com/newsrss?lang=esES', BASE_CRUNCHYROLL: 'https://www.crunchyroll.com/newsrss?lang=esES',
JKANIME_SEARCH: 'https://jkanime.net/buscar/', JKANIME_SEARCH: 'https://jkanime.net/buscar/',
ANIMEFLV_SEARCH: 'https://animeflv.net/browse?', ANIMEFLV_SEARCH: 'https://animeflv.net/browse?',
SEARCH_DIRECTORY: 'https://animeflv.net/browse?order=title&page=', SEARCH_DIRECTORY: 'https://animeflv.net/browse?order=title&page=',
BASE_EPISODE_IMG_URL: 'https://cdn.animeflv.net/screenshots/', BASE_EPISODE_IMG_URL: 'https://cdn.animeflv.net/screenshots/',
BASE_QWANT: 'https://api.qwant.com/v3/search/images?', BASE_QWANT: 'https://api.qwant.com/search/images?',
REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/', REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/',
BASE_THEMEMOE: 'https://themes.moe/api/', BASE_THEMEMOE: 'https://themes.moe/api/',
BASE_ARUPPI_MONOSCHINOS: 'https://aruppi.jeluchu.xyz/apis/monoschinos/',
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save