mirror of https://github.com/aruppi/aruppi-api.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
18 KiB
TypeScript
716 lines
18 KiB
TypeScript
import { NextFunction, Request, Response } from 'express';
|
|
import { requestGot } from '../utils/requestCall';
|
|
import { animeFlvInfo, jkanimeInfo, videoServersJK } from '../utils/util';
|
|
import { transformUrlServer } from '../utils/transformerUrl';
|
|
import AnimeModel, { Anime as ModelA } from '../database/models/anime.model';
|
|
import util from 'util';
|
|
import { hashStringMd5 } from '../utils/util';
|
|
import {
|
|
animeExtraInfo,
|
|
getAnimeVideoPromo,
|
|
getAnimeCharacters,
|
|
getRelatedAnimesFLV,
|
|
getRelatedAnimesMAL,
|
|
} from '../utils/util';
|
|
import urls from '../utils/urls';
|
|
import { redisClient } from '../database/connection';
|
|
|
|
// @ts-ignore
|
|
redisClient.get = util.promisify(redisClient.get);
|
|
|
|
/*
|
|
AnimeController - a class to manage the schedule,
|
|
top, all the animes, return the last episodes
|
|
with async to return promises.
|
|
*/
|
|
|
|
interface Schedule {
|
|
title: string;
|
|
mal_id: number;
|
|
image_url: any;
|
|
}
|
|
|
|
interface Anime {
|
|
index: string;
|
|
animeId: string;
|
|
title: string;
|
|
id: string;
|
|
type: string;
|
|
}
|
|
|
|
interface Top {
|
|
rank: string;
|
|
title: string;
|
|
url: string;
|
|
image_url: string;
|
|
type: string;
|
|
subtype: string;
|
|
page: number;
|
|
score: string;
|
|
}
|
|
|
|
interface Episode {
|
|
id: string;
|
|
title: string;
|
|
image: string;
|
|
episode: number;
|
|
servers: { id: string; url: string; direct: boolean };
|
|
}
|
|
|
|
interface Movie {
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
page: string;
|
|
banner: string;
|
|
image: string;
|
|
synopsis: string;
|
|
status: string;
|
|
rate: string;
|
|
genres: string[];
|
|
episodes: object[];
|
|
}
|
|
|
|
export default class AnimeController {
|
|
async schedule(req: Request, res: Response, next: NextFunction) {
|
|
const { day } = req.params;
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`schedule_${hashStringMd5(day)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
return res.status(200).json(resultRedis);
|
|
} else {
|
|
data = await requestGot(`${urls.BASE_JIKAN}schedule/${day}`, {
|
|
parse: true,
|
|
scrapy: false,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animeList: Schedule[] = data[day].map((item: Schedule) => ({
|
|
title: item.title,
|
|
malid: item.mal_id,
|
|
image: item.image_url,
|
|
}));
|
|
|
|
if (animeList.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`schedule_${hashStringMd5(day)}`,
|
|
JSON.stringify({ day: animeList }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`schedule_${hashStringMd5(day)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({
|
|
day: animeList,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async top(req: Request, res: Response, next: NextFunction) {
|
|
const { type, subtype, page } = req.params;
|
|
let data: any;
|
|
|
|
try {
|
|
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);
|
|
|
|
return res.status(200).json(resultRedis);
|
|
} else {
|
|
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[] = data.top.map((item: Top) => ({
|
|
rank: item.rank,
|
|
title: item.title,
|
|
url: item.url,
|
|
image_url: item.image_url,
|
|
type: type,
|
|
subtype: subtype,
|
|
page: page,
|
|
score: item.score,
|
|
}));
|
|
|
|
if (top.length > 0) {
|
|
/* 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. */
|
|
|
|
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 });
|
|
} else {
|
|
return res.status(400).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getAllAnimes(req: Request, res: Response, next: NextFunction) {
|
|
let data: any;
|
|
|
|
try {
|
|
data = await requestGot(`${urls.BASE_ANIMEFLV}api/animes/list`, {
|
|
parse: true,
|
|
scrapy: false,
|
|
});
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animes: Anime[] = data.map((item: any) => ({
|
|
index: item[0],
|
|
animeId: item[3],
|
|
title: item[1],
|
|
id: item[2],
|
|
type: item[4],
|
|
}));
|
|
|
|
if (animes.length > 0) {
|
|
res.status(200).send({ animes });
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getLastEpisodes(req: Request, res: Response, next: NextFunction) {
|
|
let data: any;
|
|
let episodes: Episode[] = [];
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
return res.status(200).json(resultRedis);
|
|
} else {
|
|
data = await requestGot(
|
|
`${urls.BASE_ANIMEFLV_JELU}LatestEpisodesAdded`,
|
|
{
|
|
parse: true,
|
|
scrapy: false,
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
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),
|
|
};
|
|
|
|
episodes.push(formattedEpisode);
|
|
}
|
|
|
|
if (episodes.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
|
|
JSON.stringify({ episodes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 1800,
|
|
);
|
|
|
|
res.status(200).json({
|
|
episodes,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getContentTv(req: Request, res: Response, next: NextFunction) {
|
|
const { type, page } = req.params;
|
|
const url = 'tv';
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
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,
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animes: Movie[] = data[url].map((item: any) => {
|
|
return {
|
|
id: item.id,
|
|
title: item.title,
|
|
type: url,
|
|
page: page,
|
|
banner: item.banner,
|
|
image: item.poster,
|
|
synopsis: item.synopsis,
|
|
status: item.debut,
|
|
rate: item.rating,
|
|
genres: item.genres.map((genre: any) => genre),
|
|
episodes: item.episodes.map((episode: any) => episode),
|
|
};
|
|
});
|
|
|
|
if (animes.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
|
|
JSON.stringify({ animes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({
|
|
animes,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getContentSpecial(req: Request, res: Response, next: NextFunction) {
|
|
const { type, page } = req.params;
|
|
const url = 'special';
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
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,
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animes: Movie[] = data[url].map((item: any) => {
|
|
return {
|
|
id: item.id,
|
|
title: item.title,
|
|
type: url,
|
|
page: page,
|
|
banner: item.banner,
|
|
image: item.poster,
|
|
synopsis: item.synopsis,
|
|
status: item.debut,
|
|
rate: item.rating,
|
|
genres: item.genres.map((genre: any) => genre),
|
|
episodes: item.episodes.map((episode: any) => episode),
|
|
};
|
|
});
|
|
|
|
if (animes.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
|
|
JSON.stringify({ animes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({
|
|
animes,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getContentOva(req: Request, res: Response, next: NextFunction) {
|
|
const { type, page } = req.params;
|
|
const url = 'ova';
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
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,
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animes: Movie[] = data[url].map((item: any) => {
|
|
return {
|
|
id: item.id,
|
|
title: item.title,
|
|
type: url,
|
|
page: page,
|
|
banner: item.banner,
|
|
image: item.poster,
|
|
synopsis: item.synopsis,
|
|
status: item.debut,
|
|
rate: item.rating,
|
|
genres: item.genres.map((genre: any) => genre),
|
|
episodes: item.episodes.map((episode: any) => episode),
|
|
};
|
|
});
|
|
|
|
if (animes.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
|
|
JSON.stringify({ animes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({
|
|
animes,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getContentMovie(req: Request, res: Response, next: NextFunction) {
|
|
const { type, page } = req.params;
|
|
const url = 'movies';
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
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,
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
const animes: Movie[] = data[url].map((item: any) => {
|
|
return {
|
|
id: item.id,
|
|
title: item.title,
|
|
type: url,
|
|
page: page,
|
|
banner: item.banner,
|
|
image: item.poster,
|
|
synopsis: item.synopsis,
|
|
status: item.debut,
|
|
rate: item.rating,
|
|
genres: item.genres.map((genre: any) => genre),
|
|
episodes: item.episodes.map((episode: any) => episode),
|
|
};
|
|
});
|
|
|
|
if (animes.length > 0) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
|
|
JSON.stringify({ animes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({
|
|
animes,
|
|
});
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getEpisodes(req: Request, res: Response, next: NextFunction) {
|
|
const { title } = req.params;
|
|
let searchAnime: ModelA | null;
|
|
let episodes: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`episodes_${hashStringMd5(title)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
return res.status(200).json(resultRedis);
|
|
} else {
|
|
searchAnime = await AnimeModel.findOne({ title: { $eq: title } });
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
if (!searchAnime?.jkanime) {
|
|
episodes = await animeFlvInfo(searchAnime?.id);
|
|
} else {
|
|
episodes = await jkanimeInfo(searchAnime?.id);
|
|
}
|
|
|
|
if (episodes) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`episodes_${hashStringMd5(title)}`,
|
|
JSON.stringify({ episodes }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`episodes_${hashStringMd5(title)}`,
|
|
parseInt(`${+new Date() / 1000}`, 10) + 7200,
|
|
);
|
|
|
|
res.status(200).json({ episodes });
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
|
|
async getServers(req: Request, res: Response, next: NextFunction) {
|
|
const { id } = req.params;
|
|
let data: any;
|
|
|
|
try {
|
|
const resultQueryRedis: any = await redisClient.get(
|
|
`servers_${hashStringMd5(id)}`,
|
|
);
|
|
|
|
if (resultQueryRedis) {
|
|
const resultRedis: any = JSON.parse(resultQueryRedis);
|
|
|
|
return res.status(200).json(resultRedis);
|
|
} else {
|
|
if (isNaN(parseInt(id.split('/')[0]))) {
|
|
data = await videoServersJK(id);
|
|
} else {
|
|
data = await requestGot(
|
|
`${urls.BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`,
|
|
{ parse: true, scrapy: false },
|
|
);
|
|
|
|
data = await transformUrlServer(data.servers);
|
|
}
|
|
|
|
if (data) {
|
|
/* Set the key in the redis cache. */
|
|
|
|
redisClient.set(
|
|
`servers_${hashStringMd5(id)}`,
|
|
JSON.stringify({ servers: data }),
|
|
);
|
|
|
|
/* After 24hrs expire the key. */
|
|
|
|
redisClient.expireat(
|
|
`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' });
|
|
}
|
|
}
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
}
|
|
|
|
async getRandomAnime(req: Request, res: Response, next: NextFunction) {
|
|
let animeQuery: ModelA[] | null;
|
|
let animeResult: any;
|
|
|
|
try {
|
|
animeQuery = await AnimeModel.aggregate([{ $sample: { size: 1 } }]);
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
if (!animeQuery[0].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 getRelatedAnimesFLV(animeQuery[0].id),
|
|
};
|
|
} else {
|
|
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),
|
|
};
|
|
}
|
|
|
|
if (animeResult) {
|
|
res.set('Cache-Control', 'no-store');
|
|
res.status(200).json(animeResult);
|
|
} else {
|
|
res.status(500).json({ message: 'Aruppi lost in the shell' });
|
|
}
|
|
}
|
|
}
|