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
>
@ -12,21 +12,22 @@
![now](https://badgen.net/badge/icon/now?icon=now&label)
![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;
&nbsp;
&nbsp;
## :rocket: Custom Aruppi API Link
&nbsp;
&nbsp;
&nbsp;
Link to access the [Aruppi API](https://aruppi-api.jeluchu.now.sh/api/v2)
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**
@ -45,15 +46,14 @@ you need to have installed these versions:
Then:
- 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`.
Here are the steps to get started with the project on both platforms, use the corresponding commands if **npm** or **yarn**.
- Clone the repo in your folder path `git clone https://github.com/aruppi/aruppi-api`
---
### **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
```
or
```bash
yarn start
```
@ -73,6 +75,8 @@ yarn start
npm build
```
or
```bash
yarn build
```
@ -87,18 +91,47 @@ npm 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)
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.
- [Darkangeel](https://github.com/Darkangeel-hd) (System administration authority (SYSADM))
- [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**
@ -115,8 +148,8 @@ Currently the Aruppi app on Android is already using the v4.x.x API services fro
</a>
</td>
<td align="center">
<a href="https://github.com/Fmaldonado6/Akiyama">
<img src="https://raw.githubusercontent.com/Fmaldonado6/Akiyama/master/images/logo/logo.png" width="75px;" alt="Jeluchu"/><br />
<a href="https://fmaldonado6.github.io/Akiyama/">
<img src="https://raw.githubusercontent.com/Fmaldonado6/Akiyama/master/images/logo/web-logo.png" width="75px;" alt="Jeluchu"/><br />
<sub>
<b>Akiyama</b>
</sub>
@ -127,33 +160,13 @@ Currently the Aruppi app on Android is already using the v4.x.x API services fro
</tr>
</table>
## Contributors ✨
### **:robot: Author**
Here are the **main contributors to the API**, along with Aruppi's creator
_*Jéluchu*_
<table>
<tr>
<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>
> You can follow me on
> [github](https://github.com/Jeluchu)&nbsp;&middot;&nbsp;[twitter](https://twitter.com/Jeluchu)
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",
"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",
"main": "./src/api/api.ts",
"scripts": {
@ -50,35 +50,37 @@
"license": "MIT",
"dependencies": {
"body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.6",
"cheerio": "^1.0.0-rc.5",
"compose-middleware": "^5.0.1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.3",
"got": "^11.8.5",
"helmet": "^4.5.0",
"mongodb": "^3.6.6",
"mongoose": "^5.13.15",
"redis": "^3.1.2",
"express": "^4.17.1",
"got": "^11.8.1",
"helmet": "^4.4.1",
"mongodb": "^3.6.4",
"mongoose": "^5.11.18",
"redis": "^3.0.2",
"rss-parser": "^3.12.0",
"tough-cookie": "^4.0.0",
"ts-node-dev": "^1.1.1"
},
"devDependencies": {
"@types/cheerio": "^0.22.28",
"@types/cheerio": "^0.22.24",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11",
"@types/node": "^15.0.2",
"@types/node": "^14.14.31",
"@types/redis": "^2.8.28",
"@types/tough-cookie": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.15.2",
"eslint": "^5.16.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-prettier": "^3.4.0",
"eslint-plugin-prettier": "^3.3.1",
"prettier": "^2.2.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 { requestGot } from '../utils/requestCall';
import {
imageUrlToBase64,
animeFlvInfo,
jkanimeInfo,
monoschinosInfo,
tioanimeInfo,
videoServersJK,
videoServersMonosChinos,
videoServersTioAnime,
} from '../utils/util';
import { transformUrlServer } from '../utils/transformerUrl';
import AnimeModel, { Anime as ModelA } from '../database/models/anime.model';
@ -17,6 +15,7 @@ import {
animeExtraInfo,
getAnimeVideoPromo,
getAnimeCharacters,
getRelatedAnimesFLV,
getRelatedAnimesMAL,
} from '../utils/util';
import urls from '../utils/urls';
@ -81,51 +80,47 @@ interface Movie {
export default class AnimeController {
async schedule(req: Request, res: Response, next: NextFunction) {
const { day } = req.params;
let info: any;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`schedule_${hashStringMd5(day)}`,
);
const resultQueryRedis: any = await redisClient.get(
`schedule_${hashStringMd5(day)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}`, {
parse: true,
scrapy: false,
});
}
info = await requestGot(`${urls.BASE_JIKAN}schedules?filter=${day}`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const animeList: Schedule[] = info.data.map((item: any) => ({
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
const animeList: Schedule[] = data[day].map((item: Schedule) => ({
title: item.title,
malid: item.mal_id,
image: item.images.jpg.image_url,
image: item.image_url,
}));
if (animeList.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`schedule_${hashStringMd5(day)}`,
JSON.stringify({ day: animeList }),
);
redisClient.set(
`schedule_${hashStringMd5(day)}`,
JSON.stringify({ day: animeList }),
);
/* After 6hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expire(
`schedule_${hashStringMd5(day)}`,
+ 21600,
);
}
redisClient.expireat(
`schedule_${hashStringMd5(day)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
day: animeList,
@ -137,50 +132,47 @@ export default class AnimeController {
async top(req: Request, res: Response, next: NextFunction) {
const { type, subtype, page } = req.params;
let info: any;
let data: any;
try {
if (redisClient.connected) {
let resultQueryRedis: any;
if (subtype) {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${page}`)}`,
);
}
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
let resultQueryRedis: any;
return res.status(200).json(resultRedis);
}
if (subtype) {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${page}`)}`,
);
}
if (subtype !== undefined) {
info = await requestGot(
`${urls.BASE_JIKAN}top/${type}?filter=${subtype}&page=${page}`,
{ parse: true, scrapy: false },
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
} else {
info = await requestGot(`${urls.BASE_JIKAN}top/${type}?page=${page}`, {
parse: true,
scrapy: false,
});
if (subtype !== undefined) {
data = await requestGot(
`${urls.BASE_JIKAN}top/${type}/${page}/${subtype}`,
{ parse: true, scrapy: false },
);
} else {
data = await requestGot(`${urls.BASE_JIKAN}top/${type}/${page}`, {
parse: true,
scrapy: false,
});
}
}
} catch (err) {
return next(err);
}
const top: Top[] = info.data.map((item: any, index: number) => ({
// A little hacky way to fix null ranks
rank: item.rank || index + 1 + (info.pagination.current_page-1)*info.pagination.items.per_page,
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
const top: Top[] = data.top.map((item: Top) => ({
rank: item.rank,
title: item.title,
url: item.url,
image_url: item.images.jpg.image_url,
image_url: item.image_url,
type: type,
subtype: subtype,
page: page,
@ -188,33 +180,31 @@ export default class AnimeController {
}));
if (top.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
if (subtype) {
redisClient.set(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
JSON.stringify({ top }),
);
} else {
redisClient.set(
`top_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ top }),
);
}
/* Set the key in the redis cache. */
if (subtype) {
redisClient.set(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
JSON.stringify({ top }),
);
} else {
redisClient.set(
`top_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ top }),
);
}
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
if (subtype) {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
if (subtype) {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
return res.status(200).json({ top });
@ -230,7 +220,6 @@ export default class AnimeController {
data = await requestGot(`${urls.BASE_ANIMEFLV}api/animes/list`, {
parse: true,
scrapy: false,
spoof: true,
});
} catch (err) {
return next(err);
@ -252,68 +241,57 @@ export default class AnimeController {
}
async getLastEpisodes(req: Request, res: Response, next: NextFunction) {
let lastEpisodes;
let data: any;
let episodes: Episode[] = [];
let animeList: any[] = [];
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
);
const resultQueryRedis: any = await redisClient.get(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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`,
{
parse: true,
scrapy: false,
},
);
}
lastEpisodes = await requestGot(`${urls.BASE_ARUPPI_MONOSCHINOS}lastest`, {
scrapy: false,
parse: true,
});
} catch (err) {
return next(err);
}
for (const anime of lastEpisodes) {
animeList.push({
id: `ver/${anime.id}`,
title: anime.title,
image: anime.image,
episode: anime.no,
});
}
for (const episode of data.episodes) {
const formattedEpisode: Episode = {
id: '12345/' + episode.id,
title: episode.title,
image: episode.poster,
episode: episode.episode,
servers: await transformUrlServer(episode.servers),
};
for (const anime of animeList) {
episodes.push({
id: anime.id,
title: anime.title,
image: await imageUrlToBase64(anime.image),
episode: anime.episode,
servers: await videoServersMonosChinos(anime.id),
});
episodes.push(formattedEpisode);
}
if (episodes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
JSON.stringify({ episodes }),
);
redisClient.set(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
JSON.stringify({ episodes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
parseInt(`${+new Date() / 1000}`, 10) + 1800,
);
}
redisClient.expireat(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
parseInt(`${+new Date() / 1000}`, 10) + 1800,
);
res.status(200).json({
episodes,
@ -329,27 +307,25 @@ export default class AnimeController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
);
const resultQueryRedis: any = await redisClient.get(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
@ -371,21 +347,19 @@ export default class AnimeController {
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
redisClient.set(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
animes,
@ -401,27 +375,25 @@ export default class AnimeController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
);
const resultQueryRedis: any = await redisClient.get(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
@ -443,21 +415,19 @@ export default class AnimeController {
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
redisClient.set(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
animes,
@ -473,27 +443,25 @@ export default class AnimeController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
);
const resultQueryRedis: any = await redisClient.get(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
@ -515,21 +483,19 @@ export default class AnimeController {
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
redisClient.set(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
animes,
@ -545,27 +511,25 @@ export default class AnimeController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
);
const resultQueryRedis: any = await redisClient.get(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
@ -587,21 +551,19 @@ export default class AnimeController {
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
redisClient.set(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
animes,
@ -617,34 +579,32 @@ export default class AnimeController {
let episodes: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`episodes_${hashStringMd5(title)}`,
);
const resultQueryRedis: any = await redisClient.get(
`episodes_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
return res.status(200).json(resultRedis);
} else {
searchAnime = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
}
searchAnime = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
} catch (err) {
return next(err);
}
switch (searchAnime?.source) {
case 'animeflv':
episodes = await animeFlvInfo(searchAnime?.id);
break;
case 'jkanime':
episodes = await jkanimeInfo(searchAnime?.id, searchAnime?.mal_id);
episodes = await jkanimeInfo(searchAnime?.id);
break;
case 'monoschinos':
episodes = await monoschinosInfo(searchAnime?.id, searchAnime?.mal_id);
break;
case 'tioanime':
episodes = await tioanimeInfo(searchAnime?.id, searchAnime?.mal_id);
episodes = await monoschinosInfo(searchAnime?.id);
break;
default:
episodes = undefined;
@ -652,21 +612,19 @@ export default class AnimeController {
}
if (episodes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`episodes_${hashStringMd5(title)}`,
JSON.stringify({ episodes }),
);
redisClient.set(
`episodes_${hashStringMd5(title)}`,
JSON.stringify({ episodes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`episodes_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`episodes_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ episodes });
} else {
@ -679,52 +637,31 @@ export default class AnimeController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`servers_${hashStringMd5(id)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
let indicator = false;
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersTioAnime(id);
const resultQueryRedis: any = await redisClient.get(
`servers_${hashStringMd5(id)}`,
);
if (!data.name) {
indicator = true;
}
}
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersMonosChinos(id);
return res.status(200).json(resultRedis);
} else {
if (isNaN(parseInt(id.split('/')[0]))) {
if (id.split('/')[0] === 'ver') {
data = await videoServersMonosChinos(id);
} else {
data = await videoServersJK(id);
}
} else {
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`,
{ parse: true, scrapy: false },
);
if (!data.name) {
console.log(data.name);
indicator = true;
data = await transformUrlServer(data.servers);
}
}
if (!indicator) {
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 (redisClient.connected) {
if (data) {
/* Set the key in the redis cache. */
redisClient.set(
@ -738,11 +675,11 @@ export default class AnimeController {
`servers_${hashStringMd5(id)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ servers: data });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
res.status(200).json({ servers: data });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
} catch (err) {
return next(err);
@ -759,18 +696,41 @@ export default class AnimeController {
return next(err);
}
animeResult = {
title: animeQuery[0].title || null,
poster: animeQuery[0].poster || null,
synopsis: animeQuery[0].description || 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 getRelatedAnimesMAL(animeQuery[0].mal_id),
};
switch (animeQuery[0].source) {
case 'animeflv':
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,
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,
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 getRelatedAnimesMAL(animeQuery[0].mal_id),
};
break;
default:
animeResult = undefined;
break;
}
if (animeResult) {
res.set('Cache-Control', 'no-store');

@ -1,12 +1,14 @@
import { NextFunction, Request, Response } from 'express';
import { requestGot } from '../utils/requestCall';
import AnimeModel, { Anime } from '../database/models/anime.model';
import GenreModel, { Genre } from '../database/models/genre.model';
import util from 'util';
import { hashStringMd5 } from '../utils/util';
import {
animeExtraInfo,
getAnimeVideoPromo,
getAnimeCharacters,
getRelatedAnimesFLV,
getRelatedAnimesMAL,
} from '../utils/util';
import urls from '../utils/urls';
@ -59,6 +61,7 @@ export default class DirectoryController {
poster: item.poster,
type: item.type,
genres: item.genres,
state: item.state,
score: item.score,
source: item.source,
description: item.description,
@ -84,6 +87,7 @@ export default class DirectoryController {
poster: item.poster,
type: item.type,
genres: item.genres,
state: item.state,
score: item.score,
source: item.source,
description: item.description,
@ -104,53 +108,49 @@ export default class DirectoryController {
async getSeason(req: Request, res: Response, next: NextFunction) {
const { year, type } = req.params;
let info: any;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`season_${hashStringMd5(`${year}:${type}`)}`,
);
const resultQueryRedis: any = await redisClient.get(
`season_${hashStringMd5(`${year}:${type}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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}`, {
scrapy: false,
parse: true,
});
}
info = await requestGot(`${urls.BASE_JIKAN}seasons/${year}/${type}`, {
scrapy: false,
parse: true,
});
} catch (err) {
return next(err);
}
const season: TypeAnime[] = info.data.map((item: any) => {
const season: TypeAnime[] = data.anime.map((item: any) => {
return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
image: item.images.jpg.image_url,
title: item.title,
image: item.image_url,
genres: item.genres.map((genre: any) => genre.name),
};
});
if (season.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`season_${hashStringMd5(`${year}:${type}`)}`,
JSON.stringify({ season }),
);
redisClient.set(
`season_${hashStringMd5(`${year}:${type}`)}`,
JSON.stringify({ season }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`season_${hashStringMd5(`${year}:${type}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`season_${hashStringMd5(`${year}:${type}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({
season,
@ -161,30 +161,28 @@ export default class DirectoryController {
}
async allSeasons(req: Request, res: Response, next: NextFunction) {
let info: any;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`allSeasons_${hashStringMd5('allSeasons')}`,
);
const resultQueryRedis: any = await redisClient.get(
`allSeasons_${hashStringMd5('allSeasons')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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`, {
parse: true,
scrapy: false,
});
}
info = await requestGot(`${urls.BASE_JIKAN}seasons`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const archive: Archive[] = info.data.map((item: any) => {
const archive: Archive[] = data.archive.map((item: any) => {
return {
year: item.year,
seasons: item.seasons,
@ -192,21 +190,19 @@ export default class DirectoryController {
});
if (archive.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`allSeasons_${hashStringMd5('allSeasons')}`,
JSON.stringify({ archive }),
);
redisClient.set(
`allSeasons_${hashStringMd5('allSeasons')}`,
JSON.stringify({ archive }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`allSeasons_${hashStringMd5('allSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`allSeasons_${hashStringMd5('allSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ archive });
} else {
@ -215,53 +211,49 @@ export default class DirectoryController {
}
async laterSeasons(req: Request, res: Response, next: NextFunction) {
let info: any;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
);
const resultQueryRedis: any = await redisClient.get(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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`, {
parse: true,
scrapy: false,
});
}
info = await requestGot(`${urls.BASE_JIKAN}seasons/upcoming`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const future: Season[] = info.data.map((item: any) => {
const future: Season[] = data.anime.map((item: any) => {
return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
image: item.images.jpg.image_url,
title: item.title,
image: item.image_url,
malink: item.url,
};
});
if (future.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
JSON.stringify({ future }),
);
redisClient.set(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
JSON.stringify({ future }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ future });
} else {
@ -275,58 +267,88 @@ export default class DirectoryController {
let resultAnime: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`moreInfo_${hashStringMd5(title)}`,
);
const resultQueryRedis: any = await redisClient.get(
`moreInfo_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
} else {
resultQuery = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
return res.status(200).json(resultRedis);
switch (resultQuery?.source) {
case 'animeflv':
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 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),
characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesMAL(resultQuery!.mal_id),
};
break;
default:
resultAnime = undefined;
break;
}
}
resultQuery = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
const extraInfo: any = await animeExtraInfo(resultQuery!.mal_id);
resultAnime = {
//aruppi_key: hashStringMd5(title),
title: resultQuery?.title,
poster: resultQuery?.poster,
synopsis: resultQuery?.description,
status: !extraInfo.aired.to ? 'En emisión' : 'Finalizado',
type: resultQuery?.type,
rating: resultQuery?.score,
genres: resultQuery?.genres,
moreInfo: [extraInfo],
promo: await getAnimeVideoPromo(resultQuery!.mal_id),
characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesMAL(resultQuery!.mal_id),
};
} catch (err) {
return next(err);
}
if (resultAnime) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`moreInfo_${hashStringMd5(title)}`,
JSON.stringify(resultAnime),
);
redisClient.set(
`moreInfo_${hashStringMd5(title)}`,
JSON.stringify(resultAnime),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`moreInfo_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`moreInfo_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json(resultAnime);
} else {
@ -364,97 +386,30 @@ export default class DirectoryController {
async getAnimeGenres(req: Request, res: Response, next: NextFunction) {
const { genre, order, page } = req.params;
let result: 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',
};
let data: Genre[];
let resultReq: any;
try {
if (genre === undefined && order === undefined && page === undefined) {
result = await AnimeModel.aggregate([{ $sample: { size: 25 } }]);
data = await GenreModel.find();
} else {
// eslint-disable-next-line no-prototype-builtins
if (genres.hasOwnProperty(genre)) {
if (page !== undefined && parseInt(page) > 1) {
if (order === 'asc') {
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,
);
}
}
if (page !== undefined) {
resultReq = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}Genres/${genre}/${order}/${page}`,
{ parse: true, scrapy: false },
);
} 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) {
return next(err);
}
const animes: any[] = result.map((item: any) => {
const animes: any[] = resultReq.animes.map((item: any) => {
return {
id: item.id,
title: item.title.trim(),

@ -69,19 +69,17 @@ export default class UtilsController {
let feed: CustomFeed & Parser.Output<CustomItem>;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`anitakume_${hashStringMd5('anitakume')}`,
);
const resultQueryRedis: any = await redisClient.get(
`anitakume_${hashStringMd5('anitakume')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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) {
return next(err);
}
@ -118,21 +116,19 @@ export default class UtilsController {
});
if (podcast.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`anitakume_${hashStringMd5('anitakume')}`,
JSON.stringify({ podcast }),
);
redisClient.set(
`anitakume_${hashStringMd5('anitakume')}`,
JSON.stringify({ podcast }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`anitakume_${hashStringMd5('anitakume')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`anitakume_${hashStringMd5('anitakume')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ podcast });
} else {
@ -144,6 +140,11 @@ export default class UtilsController {
const news: News[] = [];
const pagesRss: rssPage[] = [
{ url: urls.BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
{
url: urls.BASE_PALOMITRON,
author: 'Palomitron',
content: 'description',
},
{
url: urls.BASE_RAMENPARADOS,
author: 'Ramen para dos',
@ -157,55 +158,51 @@ export default class UtilsController {
];
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`news_${hashStringMd5('news')}`,
);
const resultQueryRedis: any = await redisClient.get(
`news_${hashStringMd5('news')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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) {
const feed = await parser.parseURL(rssPage.url);
feed.items.forEach((item: any) => {
const formattedObject: News = {
title: item.title,
url: item.link,
author: feed.title?.includes('Crunchyroll')
? 'Crunchyroll'
: feed.title,
thumbnail: obtainPreviewNews(item['content:encoded']),
content: item['content:encoded'],
};
news.push(formattedObject);
});
}
}
for (const rssPage of pagesRss) {
const feed = await parser.parseURL(rssPage.url);
feed.items.forEach((item: any) => {
const formattedObject: News = {
title: item.title,
url: item.link,
author: feed.title?.includes('Crunchyroll')
? 'Crunchyroll'
: feed.title,
thumbnail: obtainPreviewNews(item['content:encoded']),
content: item['content:encoded'],
};
news.push(formattedObject);
});
}
} catch (err) {
return next(err);
}
if (news.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`news_${hashStringMd5('news')}`,
JSON.stringify({ news }),
);
redisClient.set(
`news_${hashStringMd5('news')}`,
JSON.stringify({ news }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`news_${hashStringMd5('news')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`news_${hashStringMd5('news')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ news });
} else {
@ -218,24 +215,20 @@ export default class UtilsController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`images_${hashStringMd5(title)}`,
);
const resultQueryRedis: any = await redisClient.get(
`images_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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_QWANT}count=51&q=${title}&t=images&safesearch=1&locale=es_ES&uiv=4`,
{ scrapy: false, parse: true },
);
}
data = await requestGot(
`${urls.BASE_QWANT}t=images&q=${encodeURIComponent(
title,
)}&count=51&locale=es_ES&safesearch=1`,
{ scrapy: false, parse: true, spoof: true, },
);
} catch (err) {
return next(err);
}
@ -243,29 +236,26 @@ export default class UtilsController {
const results: any[] = data.data.result.items.map((item: any) => {
return {
type: item.thumb_type,
thumbnail: `${item.thumbnail}`,
fullsize: `${item.media_fullsize}`,
thumbnail: `https:${item.thumbnail}`,
fullsize: `https:${item.media_fullsize}`,
};
});
if (results.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`images_${hashStringMd5(title)}`,
JSON.stringify({ images: results }),
);
redisClient.set(
`images_${hashStringMd5(title)}`,
JSON.stringify({ images: results }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`images_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`images_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.set('Cache-Control', 'max-age=604800');
res.status(200).json({ images: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
@ -277,81 +267,20 @@ export default class UtilsController {
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`videos_${hashStringMd5(channelId)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`,
{ scrapy: false, parse: true },
const resultQueryRedis: any = await redisClient.get(
`videos_${hashStringMd5(channelId)}`,
);
} 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(channelId)}`,
JSON.stringify({ videos: results }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`videos_${hashStringMd5(channelId)}`,
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) {
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
const { playlistId } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = redisClient.get(
`videos_${hashStringMd5(playlistId)}`,
return res.status(200).json(resultRedis);
} else {
data = await requestGot(
`${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`,
{ scrapy: false, parse: true },
);
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);
}
@ -367,21 +296,19 @@ export default class UtilsController {
});
if (results.length > 0) {
if (redisClient.connected) {
/!* Set the key in the redis cache. *!/
/* Set the key in the redis cache. */
redisClient.set(
`videos_${hashStringMd5(playlistId)}`,
JSON.stringify({ videos: results }),
);
redisClient.set(
`videos_${hashStringMd5(channelId)}`,
JSON.stringify({ videos: results }),
);
/!* After 24hrs expire the key. *!/
/* After 24hrs expire the key. */
redisClient.expireat(
`videos_${hashStringMd5(playlistId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`videos_${hashStringMd5(channelId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ videos: results });
} else {
@ -520,39 +447,35 @@ export default class UtilsController {
let themes: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`oped_${hashStringMd5(title)}`,
);
const resultQueryRedis: any = await redisClient.get(
`oped_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
if (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) {
return next(err);
}
if (themes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
/* Set the key in the redis cache. */
redisClient.set(
`oped_${hashStringMd5(title)}`,
JSON.stringify({ themes }),
);
redisClient.set(
`oped_${hashStringMd5(title)}`,
JSON.stringify({ themes }),
);
/* After 24hrs expire the key. */
/* After 24hrs expire the key. */
redisClient.expireat(
`oped_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
redisClient.expireat(
`oped_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
res.status(200).json({ themes });
} else {
@ -566,62 +489,58 @@ export default class UtilsController {
let resultQueryRedis: any;
try {
if (redisClient.connected) {
if (year) {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5(year)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5('allYear')}`,
);
}
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
if (year) {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5(year)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5('allYear')}`,
);
}
if (year === undefined) {
themes = await themeParser.allYears();
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
} else {
themes = await structureThemes(await themeParser.year(year), false);
if (year === undefined) {
themes = await themeParser.allYears();
} else {
themes = await structureThemes(await themeParser.year(year), false);
}
}
} catch (err) {
return next(err);
}
if (themes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
if (year) {
redisClient.set(
`themesyear_${hashStringMd5(year)}`,
JSON.stringify({ themes }),
);
} else {
redisClient.set(
`themesyear_${hashStringMd5('allYear')}`,
JSON.stringify({ themes }),
);
}
/* Set the key in the redis cache. */
/* After 24hrs expire the key. */
if (year) {
redisClient.set(
`themesyear_${hashStringMd5(year)}`,
JSON.stringify({ themes }),
);
} else {
redisClient.set(
`themesyear_${hashStringMd5('allYear')}`,
JSON.stringify({ themes }),
);
}
if (year) {
redisClient.expireat(
`themesyear_${hashStringMd5(year)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`themesyear_${hashStringMd5('allYear')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
/* After 24hrs expire the key. */
if (year) {
redisClient.expireat(
`themesyear_${hashStringMd5(year)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`themesyear_${hashStringMd5('allYear')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ themes });
@ -637,7 +556,6 @@ export default class UtilsController {
data = await requestGot(`${urls.BASE_THEMEMOE}roulette`, {
parse: true,
scrapy: false,
spoof: true,
});
} catch (err) {
return next(err);

@ -40,30 +40,8 @@ export const redisClient: RedisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT!),
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', () => {
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;
type: string;
genres: Types.Array<string>;
state: string;
score: string;
source: string;
description: string;
@ -25,6 +26,7 @@ const AnimeSchema: Schema = new Schema({
poster: { type: String },
type: { type: String },
genres: [{ type: String }],
state: { type: String },
score: { type: String },
source: { type: String },
description: { type: String },

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

@ -7,6 +7,7 @@ import {
createConnectionMongo,
} from './database/connection';
import routes from './routes';
import { Server } from 'node:http';
const app: Application = express();
@ -29,7 +30,7 @@ app.use(errorHandler);
is going to listen in the server.
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 {
server.close();

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

@ -1,52 +1,28 @@
import got from 'got';
import cheerio from 'cheerio';
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 aruppi_options: any = {
cookieJar,
'headers': {
'user-agent': `Aruppi-API/${pjson.version} ${got_pjson.name}/${got_pjson.version}`,
'x-client': 'aruppi-api'
},
};
interface Options {
scrapy?: boolean,
parse?: boolean,
spoof?: boolean
scrapy: boolean;
parse: boolean;
}
export const requestGot = async (
url: string,
options?: Options,
url: string,
options: Options,
): Promise<any> => {
const got_options: any = {...got.defaults.options, ...aruppi_options}
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) {
const response = await got(url, got_options);
return cheerio.load(response.body);
}
if (options !== undefined) {
if (options.scrapy) {
const response = await got(url, { cookieJar });
return await cheerio.load(response.body);
}
if (options.parse) {
got_options.responseType = 'json';
const response = await got(url, got_options);
return response.body;
}
if (options.parse) {
return await got(url, { cookieJar }).json();
}
const response = await got.get(url, got_options);
return response;
} else {
return await got.get(url, { cookieJar });
}
};

@ -1,23 +1,22 @@
export default {
BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/',
BASE_ANIMEFLV: 'https://www3.animeflv.net/',
BASE_ANIMEFLV: 'https://animeflv.net/',
BASE_MONOSCHINOS: 'https://monoschinos2.com/',
BASE_TIOANIME: 'https://tioanime.com/',
BASE_JKANIME: 'https://jkanime.net/',
BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/',
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/v4/',
BASE_IVOOX: 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml',
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',
JKANIME_SEARCH: 'https://jkanime.net/buscar/',
ANIMEFLV_SEARCH: '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/v3/search/images?',
BASE_QWANT: 'https://api.qwant.com/search/images?',
REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/',
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