@tiktikimelbo7/extensions
Version:
Nodejs library that provides high-level APIs for obtaining information on various entertainment media such as books, movies, comic books, anime, manga, and so on.
326 lines • 16.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const cheerio_1 = require("cheerio");
const models_1 = require("../../models");
const extractors_1 = require("../../extractors");
const utils_1 = require("../../utils");
/**
* **Use at your own risk :)** 9anime devs keep changing the keys every week
*/
class NineAnime extends models_1.AnimeParser {
constructor(nineAnimeResolver, proxyConfig, apiKey, adapter) {
super(proxyConfig, adapter);
this.name = '9Anime';
this.nineAnimeResolver = '';
this.apiKey = '';
this.baseUrl = 'https://aniwave.to';
this.logo = 'https://d1nxzqpcg2bym0.cloudfront.net/google_play/com.my.nineanime/87b2fe48-9c36-11eb-8292-21241b1c199b/128x128';
this.classPath = 'ANIME.NineAnime';
this.isWorking = false;
this.nineAnimeResolver = nineAnimeResolver !== null && nineAnimeResolver !== void 0 ? nineAnimeResolver : this.nineAnimeResolver;
this.apiKey = apiKey !== null && apiKey !== void 0 ? apiKey : this.apiKey;
}
async search(query, page = 1) {
const searchResult = {
currentPage: page,
hasNextPage: false,
results: [],
};
try {
const vrf = await this.searchVrf(query);
const res = await this.client.get(`${this.baseUrl}/filter?keyword=${encodeURIComponent(query).replace(/%20/g, '+')}&vrf=${encodeURIComponent(vrf)}&page=${page}`);
const $ = (0, cheerio_1.load)(res.data);
searchResult.hasNextPage =
$(`ul.pagination`).length > 0
? $('ul.pagination > li').last().hasClass('disabled')
? false
: true
: false;
$('#list-items > div.item').each((i, el) => {
var _a;
let type = undefined;
switch ($(el).find('div > div.ani > a > div.meta > div > div.right').text().trim()) {
case 'MOVIE':
type = models_1.MediaFormat.MOVIE;
break;
case 'TV':
type = models_1.MediaFormat.TV;
break;
case 'OVA':
type = models_1.MediaFormat.OVA;
break;
case 'SPECIAL':
type = models_1.MediaFormat.SPECIAL;
break;
case 'ONA':
type = models_1.MediaFormat.ONA;
break;
case 'MUSIC':
type = models_1.MediaFormat.MUSIC;
break;
}
searchResult.results.push({
id: (_a = $(el).find('div > div.ani > a').attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[2],
title: $(el).find('div > div.info > div.b1 > a').text(),
url: `${this.baseUrl}${$(el).find('div > div.ani > a').attr('href')}`,
image: $(el).find('div > div.ani > a > img').attr('src'),
type: type,
hasSub: $(el).find('div > div.ani > a .meta .sub').length > 0,
hasDub: $(el).find('div > div.ani > a .meta .dub').length > 0,
});
});
return searchResult;
}
catch (err) {
throw new Error(err.message);
}
}
async fetchAnimeInfo(animeUrl) {
var _a, _b, _c, _d, _e;
if (!animeUrl.startsWith(this.baseUrl))
animeUrl = `${this.baseUrl}/watch/${animeUrl}`;
const animeInfo = {
id: '',
title: '',
url: animeUrl,
};
try {
const res = await this.client.get(animeUrl);
const $ = (0, cheerio_1.load)(res.data);
animeInfo.id = new URL(`${this.baseUrl}/animeUrl`).pathname.split('/')[2];
animeInfo.title = $('h1.title').text();
animeInfo.jpTitle = $('h1.title').attr('data-jp');
animeInfo.genres = Array.from($('div.meta:nth-child(1) > div:nth-child(5) > span > a').map((i, el) => $(el).text()));
animeInfo.image = $('.binfo > div.poster > span > img').attr('src');
animeInfo.description = (_a = $('.content').text()) === null || _a === void 0 ? void 0 : _a.trim();
switch ($('div.meta:nth-child(1) > div:nth-child(1) > span:nth-child(1) > a').text()) {
case 'MOVIE':
animeInfo.type = models_1.MediaFormat.MOVIE;
break;
case 'TV':
animeInfo.type = models_1.MediaFormat.TV;
break;
case 'OVA':
animeInfo.type = models_1.MediaFormat.OVA;
break;
case 'SPECIAL':
animeInfo.type = models_1.MediaFormat.SPECIAL;
break;
case 'ONA':
animeInfo.type = models_1.MediaFormat.ONA;
break;
case 'MUSIC':
animeInfo.type = models_1.MediaFormat.MUSIC;
break;
}
animeInfo.studios = Array.from($('div.meta:nth-child(1) > div:nth-child(2) > span:nth-child(1) > a').map((i, el) => { var _a; return (_a = $(el).text()) === null || _a === void 0 ? void 0 : _a.trim(); }));
animeInfo.releaseDate = (_b = $('div.meta:nth-child(1) > div:nth-child(3) > span:nth-child(1)')
.text()
.trim()
.split('to')[0]) === null || _b === void 0 ? void 0 : _b.trim();
switch ((_c = $('div.meta:nth-child(1) > div:nth-child(4) > span:nth-child(1)').text()) === null || _c === void 0 ? void 0 : _c.trim()) {
case 'Releasing':
animeInfo.status = models_1.MediaStatus.ONGOING;
break;
case 'Completed':
animeInfo.status = models_1.MediaStatus.COMPLETED;
break;
case 'Cancelled':
animeInfo.status = models_1.MediaStatus.CANCELLED;
break;
case 'Unknown':
animeInfo.status = models_1.MediaStatus.UNKNOWN;
break;
default:
animeInfo.status = models_1.MediaStatus.UNKNOWN;
break;
}
animeInfo.score = parseFloat((_d = $('.bmeta > div:nth-child(2) > div:nth-child(2) > span:nth-child(1)')) === null || _d === void 0 ? void 0 : _d.text().split('by')[0]);
animeInfo.premiered = $('.bmeta > div:nth-child(2) > div:nth-child(3) > span:nth-child(1) > a:nth-child(1)').text();
animeInfo.duration = $('.bmeta > div:nth-child(2) > div:nth-child(4) > span:nth-child(1)').text();
animeInfo.views = parseInt($('.bmeta > div:nth-child(2) > div:nth-child(5) > span:nth-child(1)')
.text()
.split('by')
.join('')
.split(',')
.join('')
.trim());
animeInfo.otherNames = $('.names')
.text()
.split('; ')
.map(name => name === null || name === void 0 ? void 0 : name.trim());
animeInfo.hasSub = $('div#w-info > .binfo > .info > .meta .sub').length == 1;
animeInfo.hasDub = $('div#w-info > .binfo > .info > .meta .dub').length == 1;
const id = $('#watch-main').attr('data-id');
const vrf = await this.ev(id);
const { data: { result }, } = await this.client.get(`${this.baseUrl}/ajax/episode/list/${id}?vrf=${encodeURIComponent(vrf)}`);
const $$ = (0, cheerio_1.load)(result);
animeInfo.totalEpisodes = $$('div.episodes > ul > li > a').length;
animeInfo.episodes = [];
const episodes = [];
$$('div.episodes > ul > li > a').map((i, el) => {
$$(el)
.map((i, el) => {
var _a, _b;
const possibleIds = (_a = $$(el).attr('data-ids')) === null || _a === void 0 ? void 0 : _a.split(',');
const number = parseInt((_b = $$(el).attr('data-num')) === null || _b === void 0 ? void 0 : _b.toString());
const title = $$(el).find('span').text().length > 0 ? $$(el).find('span').text() : undefined;
const isFiller = $$(el).hasClass('filler');
episodes.push({
id: possibleIds[0],
dubId: possibleIds[1],
number: number,
title: title,
isFiller: isFiller,
});
})
.get();
});
(_e = animeInfo.episodes) === null || _e === void 0 ? void 0 : _e.push(...episodes);
return animeInfo;
}
catch (err) {
console.log(err);
throw new Error(err.message);
}
}
async fetchEpisodeSources(episodeId, server = models_1.StreamingServers.VizCloud) {
var _a, _b, _c, _d, _e, _f;
if (episodeId.startsWith('http')) {
const serverUrl = new URL(episodeId);
switch (server) {
case models_1.StreamingServers.StreamTape:
return {
headers: { Referer: serverUrl.href, 'User-Agent': utils_1.USER_AGENT },
sources: await new extractors_1.StreamTape().extract(serverUrl),
};
case models_1.StreamingServers.VizCloud:
case models_1.StreamingServers.VidCloud:
return {
headers: { Referer: serverUrl.href, 'User-Agent': utils_1.USER_AGENT },
sources: await new extractors_1.VizCloud().extract(serverUrl, this.nineAnimeResolver, this.apiKey),
};
case models_1.StreamingServers.MyCloud:
return {
headers: { Referer: serverUrl.href, 'User-Agent': utils_1.USER_AGENT },
sources: await new extractors_1.VizCloud().extract(serverUrl, this.nineAnimeResolver, this.apiKey),
};
case models_1.StreamingServers.Filemoon:
return {
headers: { Referer: serverUrl.href, 'User-Agent': utils_1.USER_AGENT },
sources: await new extractors_1.Filemoon().extract(serverUrl),
};
default:
throw new Error('Server not supported');
}
}
try {
const servers = await this.fetchEpisodeServers(episodeId);
let s = servers.find(s => s.name === server);
switch (server) {
case models_1.StreamingServers.VizCloud:
s = servers.find(s => s.name === 'vidstream');
if (!s)
throw new Error('Vidstream server found');
break;
case models_1.StreamingServers.StreamTape:
s = servers.find(s => s.name === 'streamtape');
if (!s)
throw new Error('Streamtape server found');
break;
case models_1.StreamingServers.MyCloud:
s = servers.find(s => s.name === 'mycloud');
if (!s)
throw new Error('Mycloud server found');
break;
case models_1.StreamingServers.Filemoon:
s = servers.find(s => s.name === 'filemoon');
if (!s)
throw new Error('Filemoon server found');
break;
default:
throw new Error('Server not found');
}
const serverVrf = (await this.client.get(`${this.nineAnimeResolver}/vrf?query=${encodeURIComponent(s.url)}&apikey=${this.apiKey}`)).data.url;
const serverSource = (await this.client.get(`${this.baseUrl}/ajax/server/${s.url}?vrf=${encodeURIComponent(serverVrf)}`)).data;
const embedURL = (await this.client.get(`${this.nineAnimeResolver}/decrypt?query=${encodeURIComponent(serverSource.result.url)}&apikey=${this.apiKey}`)).data.url;
if (embedURL.startsWith('http')) {
const response = await this.fetchEpisodeSources(embedURL, server);
response.embedURL = embedURL;
response.intro = {
start: (_c = (_b = (_a = serverSource === null || serverSource === void 0 ? void 0 : serverSource.result) === null || _a === void 0 ? void 0 : _a.skip_data) === null || _b === void 0 ? void 0 : _b.intro_begin) !== null && _c !== void 0 ? _c : 0,
end: (_f = (_e = (_d = serverSource === null || serverSource === void 0 ? void 0 : serverSource.result) === null || _d === void 0 ? void 0 : _d.skip_data) === null || _e === void 0 ? void 0 : _e.intro_end) !== null && _f !== void 0 ? _f : 0,
};
return response;
}
else {
throw new Error('Server did not respond correctly');
}
}
catch (err) {
throw new Error(err.message);
}
}
async fetchEpisodeServers(episodeId) {
if (!episodeId.startsWith(this.baseUrl))
episodeId = `${this.baseUrl}/ajax/server/list/${episodeId}?vrf=${encodeURIComponent(await this.ev(episodeId))}`;
const { data: { result }, } = await this.client.get(episodeId);
const $ = (0, cheerio_1.load)(result);
const servers = [];
$('.type > ul > li').each((i, el) => {
const serverId = $(el).attr('data-link-id');
servers.push({
name: $(el).text().toLocaleLowerCase(),
url: `${serverId}`,
});
});
return servers;
}
async ev(query, raw = false) {
const { data } = await this.client.get(`${this.nineAnimeResolver}/vrf?query=${encodeURIComponent(query)}&apikey=${this.apiKey}`);
if (raw) {
return data;
}
else {
return data.url;
}
}
async searchVrf(query, raw = false) {
const { data } = await this.client.get(`${this.nineAnimeResolver}/9anime-search?query=${encodeURIComponent(query)}&apikey=${this.apiKey}`);
if (raw) {
return data;
}
else {
return data.url;
}
}
async decrypt(query, raw = false) {
const { data } = await this.client.get(`${this.nineAnimeResolver}/decrypt?query=${encodeURIComponent(query)}&apikey=${this.apiKey}`);
if (raw) {
return data;
}
else {
return data.url;
}
}
async vizcloud(query) {
const { data } = await this.client.get(`${this.nineAnimeResolver}/vizcloud?query=${encodeURIComponent(query)}&apikey=${this.apiKey}`);
return data;
}
async customRequest(query, action) {
const { data } = await this.client.get(`${this.nineAnimeResolver}/${action}?query=${encodeURIComponent(query)}&apikey=${this.apiKey}`);
return data;
}
}
// (async () => {
// // const nineAnime = new NineAnime();
// // const searchResults = await nineAnime.search('attack on titan');
// // const animeInfo = await nineAnime.fetchAnimeInfo('shadowverse-flame.rljqn');
// // @ts-ignore
// // const episodeSources = await nineAnime.fetchEpisodeSources("ab68", "decrypt");
// // console.log(await nineAnime.vizcloud("LNPEK8Q0QPXW"));
// // console.log(await nineAnime.decrypt("ab6/", true));
// // console.log(await nineAnime.customRequest("LNPEK8Q0QPXW", "9anime-search"));
// })();
exports.default = NineAnime;
//# sourceMappingURL=9anime.js.map