contentapi.ts
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.
812 lines • 36.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const cheerio_1 = require("cheerio");
const models_1 = require("../../models");
const utils_1 = require("../../utils");
const utils_2 = require("../../utils");
class Zoro extends models_1.AnimeParser {
constructor(customBaseURL) {
super(...arguments);
this.name = 'Zoro';
this.baseUrl = 'https://hianime.to';
this.logo = 'https://is3-ssl.mzstatic.com/image/thumb/Purple112/v4/7e/91/00/7e9100ee-2b62-0942-4cdc-e9b93252ce1c/source/512x512bb.jpg';
this.classPath = 'ANIME.Zoro';
/**
* @param id Anime id
*/
this.fetchAnimeInfo = async (id) => {
const info = {
id: id,
title: '',
};
try {
const { data } = await this.client.get(`${this.baseUrl}/watch/${id}`);
const $ = (0, cheerio_1.load)(data);
const { mal_id, anilist_id } = JSON.parse($('#syncData').text());
info.malID = Number(mal_id);
info.alID = Number(anilist_id);
info.title = $('h2.film-name > a.text-white').text();
info.japaneseTitle = $('div.anisc-info div:nth-child(2) span.name').text();
info.image = $('img.film-poster-img').attr('src');
info.description = $('div.film-description').text().trim();
// Movie, TV, OVA, ONA, Special, Music
info.type = $('span.item').last().prev().prev().text().toUpperCase();
info.url = `${this.baseUrl}/${id}`;
info.recommendations = await this.scrapeCard($);
info.relatedAnime = [];
$('#main-sidebar section:nth-child(1) div.anif-block-ul li').each((i, ele) => {
var _a, _b, _c, _d, _e, _f, _g;
const card = $(ele);
const aTag = card.find('.film-name a');
const id = (_a = aTag.attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[1].split('?')[0];
info.relatedAnime.push({
id: id,
title: aTag.text(),
url: `${this.baseUrl}${aTag.attr('href')}`,
image: (_b = card.find('img')) === null || _b === void 0 ? void 0 : _b.attr('data-src'),
japaneseTitle: aTag.attr('data-jname'),
type: (_d = (_c = card.find('.tick').contents().last()) === null || _c === void 0 ? void 0 : _c.text()) === null || _d === void 0 ? void 0 : _d.trim(),
sub: parseInt((_e = card.find('.tick-item.tick-sub')) === null || _e === void 0 ? void 0 : _e.text()) || 0,
dub: parseInt((_f = card.find('.tick-item.tick-dub')) === null || _f === void 0 ? void 0 : _f.text()) || 0,
episodes: parseInt((_g = card.find('.tick-item.tick-eps')) === null || _g === void 0 ? void 0 : _g.text()) || 0,
});
});
const hasSub = $('div.film-stats div.tick div.tick-item.tick-sub').length > 0;
const hasDub = $('div.film-stats div.tick div.tick-item.tick-dub').length > 0;
if (hasSub) {
info.subOrDub = models_1.SubOrSub.SUB;
info.hasSub = hasSub;
}
if (hasDub) {
info.subOrDub = models_1.SubOrSub.DUB;
info.hasDub = hasDub;
}
if (hasSub && hasDub) {
info.subOrDub = models_1.SubOrSub.BOTH;
}
// ZORO - PAGE INFO
const zInfo = await this.client.get(info.url);
const $$$ = (0, cheerio_1.load)(zInfo.data);
info.genres = [];
$$$('.item.item-list')
.find('a')
.each(function () {
var _a;
const genre = $(this).text().trim();
if (genre != undefined)
(_a = info.genres) === null || _a === void 0 ? void 0 : _a.push(genre);
});
switch ($$$('.item.item-title').find("span.item-head:contains('Status')").next('span.name').text().trim()) {
case 'Finished Airing':
info.status = models_1.MediaStatus.COMPLETED;
break;
case 'Currently Airing':
info.status = models_1.MediaStatus.ONGOING;
break;
case 'Not yet aired':
info.status = models_1.MediaStatus.NOT_YET_AIRED;
break;
default:
info.status = models_1.MediaStatus.UNKNOWN;
break;
}
info.season = $$$('.item.item-title')
.find("span.item-head:contains('Premiered')")
.next('span.name')
.text()
.trim();
if (info.japaneseTitle == '' || info.japaneseTitle == undefined) {
info.japaneseTitle = $$$('.item.item-title')
.find("span.item-head:contains('Japanese')")
.next('span.name')
.text()
.trim();
}
const episodesAjax = await this.client.get(`${this.baseUrl}/ajax/v2/episode/list/${id.split('-').pop()}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
Referer: `${this.baseUrl}/watch/${id}`,
},
});
const $$ = (0, cheerio_1.load)(episodesAjax.data.html);
info.totalEpisodes = $$('div.detail-infor-content > div > a').length;
info.episodes = [];
$$('div.detail-infor-content > div > a').each((i, el) => {
var _a, _b, _c;
const episodeId = (_b = (_a = $$(el).attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[2]) === null || _b === void 0 ? void 0 : _b.replace('?ep=', '$episode$');
const number = parseInt($$(el).attr('data-number'));
const title = $$(el).attr('title');
const url = this.baseUrl + $$(el).attr('href');
const isFiller = $$(el).hasClass('ssl-item-filler');
const isSubbed = number <= (parseInt($('div.film-stats div.tick div.tick-item.tick-sub').text().trim()) || 0);
const isDubbed = number <= (parseInt($('div.film-stats div.tick div.tick-item.tick-dub').text().trim()) || 0);
(_c = info.episodes) === null || _c === void 0 ? void 0 : _c.push({
id: episodeId,
number: number,
title: title,
isFiller: isFiller,
isSubbed: isSubbed,
isDubbed: isDubbed,
url: url,
});
});
return info;
}
catch (err) {
throw new Error(err.message);
}
};
/**
*
* @param episodeId Episode id
* @param server server type (default `VidCloud`) (optional)
* @param subOrDub sub or dub (default `SubOrSub.SUB`) (optional)
*/
this.fetchEpisodeSources = async (episodeId, server = models_1.StreamingServers.VidCloud, subOrDub = models_1.SubOrSub.SUB) => {
if (episodeId.startsWith('http')) {
const serverUrl = new URL(episodeId);
switch (server) {
case models_1.StreamingServers.VidStreaming:
case models_1.StreamingServers.VidCloud:
return {
...(await new utils_1.MegaCloud().extract(serverUrl, this.baseUrl)),
};
case models_1.StreamingServers.StreamSB:
return {
headers: {
Referer: serverUrl.href,
watchsb: 'streamsb',
'User-Agent': utils_2.USER_AGENT,
},
sources: await new utils_1.StreamSB(this.proxyConfig, this.adapter).extract(serverUrl, true),
};
case models_1.StreamingServers.StreamTape:
return {
headers: { Referer: serverUrl.href, 'User-Agent': utils_2.USER_AGENT },
sources: await new utils_1.StreamTape(this.proxyConfig, this.adapter).extract(serverUrl),
};
default:
case models_1.StreamingServers.VidCloud:
return {
headers: { Referer: serverUrl.href },
...(await new utils_1.MegaCloud().extract(serverUrl, this.baseUrl)),
};
}
}
if (!episodeId.includes('$episode$'))
throw new Error('Invalid episode id');
// keeping this for future use
// Fallback to using sub if no info found in case of compatibility
// TODO: add both options later
// subOrDub = episodeId.split('$')?.pop() === 'dub' ? 'dub' : 'sub';
episodeId = `${this.baseUrl}/watch/${episodeId
.replace('$episode$', '?ep=')
.replace(/\$auto|\$sub|\$dub/gi, '')}`;
try {
const { data } = await this.client.get(`${this.baseUrl}/ajax/v2/episode/servers?episodeId=${episodeId.split('?ep=')[1]}`);
const $ = (0, cheerio_1.load)(data.html);
/**
* vidtreaming -> 4
* rapidcloud -> 1
* streamsb -> 5
* streamtape -> 3
*/
let serverId = '';
try {
switch (server) {
case models_1.StreamingServers.VidCloud:
serverId = this.retrieveServerId($, 1, subOrDub);
// zoro's vidcloud server is rapidcloud
if (!serverId)
throw new Error('RapidCloud not found');
break;
case models_1.StreamingServers.VidStreaming:
serverId = this.retrieveServerId($, 4, subOrDub);
// zoro's vidcloud server is rapidcloud
if (!serverId)
throw new Error('vidtreaming not found');
break;
case models_1.StreamingServers.StreamSB:
serverId = this.retrieveServerId($, 5, subOrDub);
if (!serverId)
throw new Error('StreamSB not found');
break;
case models_1.StreamingServers.StreamTape:
serverId = this.retrieveServerId($, 3, subOrDub);
if (!serverId)
throw new Error('StreamTape not found');
break;
}
}
catch (err) {
throw new Error("Couldn't find server. Try another server");
}
const { data: { link }, } = await this.client.get(`${this.baseUrl}/ajax/v2/episode/sources?id=${serverId}`);
return await this.fetchEpisodeSources(link, server, models_1.SubOrSub.SUB);
}
catch (err) {
throw err;
}
};
this.verifyLoginState = async (connectSid) => {
try {
const { data } = await this.client.get(`${this.baseUrl}/ajax/login-state`, {
headers: {
Cookie: `connect.sid=${connectSid}`,
},
});
return data.is_login;
}
catch (err) {
return false;
}
};
this.retrieveServerId = ($, index, subOrDub) => {
const rawOrSubOrDub = (raw) => $(`.ps_-block.ps_-block-sub.servers-${raw ? 'raw' : subOrDub} > .ps__-list .server-item`)
.map((i, el) => ($(el).attr('data-server-id') == `${index}` ? $(el) : null))
.get()[0]
.attr('data-id');
try {
// Attempt to get the subOrDub ID
return rawOrSubOrDub(false);
}
catch (error) {
// If an error is thrown, attempt to get the raw ID (The raw is the newest episode uploaded to zoro)
return rawOrSubOrDub(true);
}
};
/**
* @param url string
*/
this.scrapeCardPage = async (url, headers) => {
var _a, _b, _c;
try {
const res = {
currentPage: 0,
hasNextPage: false,
totalPages: 0,
results: [],
};
console.log(url);
const { data } = await this.client.get(url, headers);
const $ = (0, cheerio_1.load)(data);
const pagination = $('ul.pagination');
res.currentPage = parseInt((_a = pagination.find('.page-item.active')) === null || _a === void 0 ? void 0 : _a.text());
const nextPage = (_b = pagination.find('a[title=Next]')) === null || _b === void 0 ? void 0 : _b.attr('href');
if (nextPage != undefined && nextPage != '') {
res.hasNextPage = true;
}
const totalPages = (_c = pagination.find('a[title=Last]').attr('href')) === null || _c === void 0 ? void 0 : _c.split('=').pop();
if (totalPages === undefined || totalPages === '') {
res.totalPages = res.currentPage;
}
else {
res.totalPages = parseInt(totalPages);
}
res.results = await this.scrapeCard($);
if (res.results.length === 0) {
res.currentPage = 0;
res.hasNextPage = false;
res.totalPages = 0;
}
return res;
}
catch (err) {
throw new Error('Something went wrong. Please try again later.');
}
};
/**
* @param $ cheerio instance
*/
this.scrapeCard = async ($) => {
try {
const results = [];
$('.flw-item').each((i, ele) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const card = $(ele);
const atag = card.find('.film-name a');
const id = (_a = atag.attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[1].split('?')[0];
const watchList = card.find('.dropdown-menu .added').text().trim();
const type = (_c = (_b = card
.find('.fdi-item')) === null || _b === void 0 ? void 0 : _b.first()) === null || _c === void 0 ? void 0 : _c.text().replace(' (? eps)', '').replace(/\s\(\d+ eps\)/g, '');
results.push({
id: id,
title: atag.text(),
url: `${this.baseUrl}${atag.attr('href')}`,
image: (_d = card.find('img')) === null || _d === void 0 ? void 0 : _d.attr('data-src'),
duration: (_e = card.find('.fdi-duration')) === null || _e === void 0 ? void 0 : _e.text(),
watchList: watchList || models_1.WatchListType.NONE,
japaneseTitle: atag.attr('data-jname'),
type: type,
nsfw: ((_f = card.find('.tick-rate')) === null || _f === void 0 ? void 0 : _f.text()) === '18+' ? true : false,
sub: parseInt((_g = card.find('.tick-item.tick-sub')) === null || _g === void 0 ? void 0 : _g.text()) || 0,
dub: parseInt((_h = card.find('.tick-item.tick-dub')) === null || _h === void 0 ? void 0 : _h.text()) || 0,
episodes: parseInt((_j = card.find('.tick-item.tick-eps')) === null || _j === void 0 ? void 0 : _j.text()) || 0,
});
});
return results;
}
catch (err) {
throw new Error('Something went wrong. Please try again later.');
}
};
/**
* @deprecated
* @param episodeId Episode id
*/
this.fetchEpisodeServers = (episodeId) => {
throw new Error('Method not implemented.');
};
if (customBaseURL) {
if (customBaseURL.startsWith('http://') || customBaseURL.startsWith('https://')) {
this.baseUrl = customBaseURL;
}
else {
this.baseUrl = `http://${customBaseURL}`;
}
}
else {
this.baseUrl = this.baseUrl;
}
}
/**
* @param query Search query
* @param page Page number (optional)
*/
search(query, page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/search?keyword=${decodeURIComponent(query)}&page=${page}`);
}
/**
* Fetch advanced anime search results with various filters.
*
* @param page Page number (default: 1)
* @param type One of (Optional): movie, tv, ova, ona, special, music
* @param status One of (Optional): finished_airing, currently_airing, not_yet_aired
* @param rated One of (Optional): g, pg, pg_13, r, r_plus, rx
* @param score Number from 1 to 10 (Optional)
* @param season One of (Optional): spring, summer, fall, winter
* @param language One of (Optional): sub, dub, sub_dub
* @param startDate Start date object { year, month, day } (Optional)
* @param endDate End date object { year, month, day } (Optional)
* @param sort One of (Optional): recently_added, recently_updated, score, name_az, released_date, most_watched
* @param genres Array of genres (Optional): action, adventure, cars, comedy, dementia, demons, mystery, drama, ecchi, fantasy, game, historical, horror, kids, magic, martial_arts, mecha, music, parody, samurai, romance, school, sci_fi, shoujo, shoujo_ai, shounen, shounen_ai, space, sports, super_power, vampire, harem, military, slice_of_life, supernatural, police, psychological, thriller, seinen, isekai, josei
* @returns A Promise resolving to the search results.
*/
fetchAdvancedSearch(page = 1, type, status, rated, score, season, language, startDate, endDate, sort, genres) {
if (page <= 0)
page = 1;
const mappings = {
type: { movie: 1, tv: 2, ova: 3, ona: 4, special: 5, music: 6 },
status: { finished_airing: 1, currently_airing: 2, not_yet_aired: 3 },
rated: { g: 1, pg: 2, pg_13: 3, r: 4, r_plus: 5, rx: 6 },
season: { spring: 1, summer: 2, fall: 3, winter: 4 },
language: { sub: 1, dub: 2, sub_dub: 3 },
genre: {
action: 1,
adventure: 2,
cars: 3,
comedy: 4,
dementia: 5,
demons: 6,
mystery: 7,
drama: 8,
ecchi: 9,
fantasy: 10,
game: 11,
historical: 13,
horror: 14,
kids: 15,
magic: 16,
martial_arts: 17,
mecha: 18,
music: 19,
parody: 20,
samurai: 21,
romance: 22,
school: 23,
sci_fi: 24,
shoujo: 25,
shoujo_ai: 26,
shounen: 27,
shounen_ai: 28,
space: 29,
sports: 30,
super_power: 31,
vampire: 32,
harem: 35,
military: 38,
slice_of_life: 36,
supernatural: 37,
police: 39,
psychological: 40,
thriller: 41,
seinen: 42,
isekai: 44,
josei: 43,
},
};
const params = new URLSearchParams({ page: page.toString() });
const addParam = (key, value) => {
var _a;
if (value)
params.append(key, (((_a = mappings[key]) === null || _a === void 0 ? void 0 : _a[value]) || value).toString());
};
addParam('type', type);
addParam('status', status);
addParam('rated', rated);
if (score)
params.append('score', score.toString());
addParam('season', season);
addParam('language', language);
if (startDate) {
params.append('sy', startDate.year.toString());
params.append('sm', startDate.month.toString());
params.append('sd', startDate.day.toString());
}
if (endDate) {
params.append('ey', endDate.year.toString());
params.append('em', endDate.month.toString());
params.append('ed', endDate.day.toString());
}
if (sort)
params.append('sort', sort);
if (genres === null || genres === void 0 ? void 0 : genres.length) {
const genreIds = genres.map(genre => (mappings.genre[genre] || genre).toString()).join('%2C');
params.append('genres', genreIds);
}
return this.scrapeCardPage(`${this.baseUrl}/filter?${params.toString()}`);
}
/**
* @param page number
*/
fetchTopAiring(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/top-airing?page=${page}`);
}
/**
* @param page number
*/
fetchMostPopular(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/most-popular?page=${page}`);
}
/**
* @param page number
*/
fetchMostFavorite(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/most-favorite?page=${page}`);
}
/**
* @param page number
*/
fetchLatestCompleted(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/completed?page=${page}`);
}
/**
* @param page number
*/
fetchRecentlyUpdated(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/recently-updated?page=${page}`);
}
/**
* @param page number
*/
fetchRecentlyAdded(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/recently-added?page=${page}`);
}
/**
* @param page number
*/
fetchTopUpcoming(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/top-upcoming?page=${page}`);
}
/**
* @param studio Studio id, e.g. "toei-animation"
* @param page page number (optional) `default 1`
*/
fetchStudio(studio, page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/producer/${studio}?page=${page}`);
}
/**
* @param page number
*/
fetchSubbedAnime(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/subbed-anime?page=${page}`);
}
/**
* @param page number
*/
fetchDubbedAnime(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/dubbed-anime?page=${page}`);
}
/**
* @param page number
*/
fetchMovie(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/movie?page=${page}`);
}
/**
* @param page number
*/
fetchTV(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/tv?page=${page}`);
}
/**
* @param page number
*/
fetchOVA(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/ova?page=${page}`);
}
/**
* @param page number
*/
fetchONA(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/ona?page=${page}`);
}
/**
* @param page number
*/
fetchSpecial(page = 1) {
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/special?page=${page}`);
}
async fetchGenres() {
try {
const res = [];
const { data } = await this.client.get(`${this.baseUrl}/home`);
const $ = (0, cheerio_1.load)(data);
const sideBar = $('#main-sidebar');
sideBar.find('ul.sb-genre-list li a').each((i, ele) => {
const genres = $(ele);
res.push(genres.text().toLowerCase());
});
return res;
}
catch (err) {
throw new Error('Something went wrong. Please try again later.');
}
}
/**
* @param page number
*/
genreSearch(genre, page = 1) {
if (genre == '') {
throw new Error('genre is empty');
}
if (0 >= page) {
page = 1;
}
return this.scrapeCardPage(`${this.baseUrl}/genre/${genre}?page=${page}`);
}
/**
* Fetches the schedule for a given date.
* @param date The date in format 'YYYY-MM-DD'. Defaults to the current date.
* @returns A promise that resolves to an object containing the search results.
*/
async fetchSchedule(date = new Date().toISOString().slice(0, 10)) {
try {
const res = {
results: [],
};
const { data: { html }, } = await this.client.get(`${this.baseUrl}/ajax/schedule/list?tzOffset=360&date=${date}`);
const $ = (0, cheerio_1.load)(html);
$('li').each((i, ele) => {
var _a;
const card = $(ele);
const title = card.find('.film-name');
const id = (_a = card.find('a.tsl-link').attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[1].split('?')[0];
const airingTime = card.find('div.time').text().replace('\n', '').trim();
const airingEpisode = card.find('div.film-detail div.fd-play button').text().replace('\n', '').trim();
res.results.push({
id: id,
title: title.text(),
japaneseTitle: title.attr('data-jname'),
url: `${this.baseUrl}/${id}`,
airingEpisode: airingEpisode,
airingTime: airingTime,
});
});
return res;
}
catch (err) {
throw new Error('Something went wrong. Please try again later.');
}
}
async fetchSpotlight() {
try {
const res = { results: [] };
const { data } = await this.client.get(`${this.baseUrl}/home`);
const $ = (0, cheerio_1.load)(data);
$('#slider div.swiper-wrapper div.swiper-slide').each((i, el) => {
var _a, _b, _c;
const card = $(el);
const titleElement = card.find('div.desi-head-title');
const id = ((_b = (_a = card
.find('div.desi-buttons .btn-secondary')
.attr('href')) === null || _a === void 0 ? void 0 : _a.match(/\/([^/]+)$/)) === null || _b === void 0 ? void 0 : _b[1]) || null;
const img = card.find('img.film-poster-img');
res.results.push({
id: id,
title: titleElement.text(),
japaneseTitle: titleElement.attr('data-jname'),
banner: img.attr('data-src') || img.attr('src') || null,
rank: parseInt((_c = card.find('.desi-sub-text').text().match(/(\d+)/g)) === null || _c === void 0 ? void 0 : _c[0]),
url: `${this.baseUrl}/${id}`,
type: card.find('div.sc-detail .scd-item:nth-child(1)').text().trim(),
duration: card.find('div.sc-detail > div:nth-child(2)').text().trim(),
releaseDate: card.find('div.sc-detail > div:nth-child(3)').text().trim(),
quality: card.find('div.sc-detail > div:nth-child(4)').text().trim(),
sub: parseInt(card.find('div.sc-detail div.tick-sub').text().trim()) || 0,
dub: parseInt(card.find('div.sc-detail div.tick-dub').text().trim()) || 0,
episodes: parseInt(card.find('div.sc-detail div.tick-eps').text()) || 0,
description: card.find('div.desi-description').text().trim(),
});
});
return res;
}
catch (error) {
throw new Error('Something went wrong. Please try again later.');
}
}
async fetchSearchSuggestions(query) {
try {
const encodedQuery = encodeURIComponent(query);
const { data } = await this.client.get(`${this.baseUrl}/ajax/search/suggest?keyword=${encodedQuery}`);
const $ = (0, cheerio_1.load)(data.html);
const res = {
results: [],
};
$('.nav-item').each((i, el) => {
var _a;
const card = $(el);
if (!card.hasClass('nav-bottom')) {
const image = card.find('.film-poster img').attr('data-src');
const title = card.find('.film-name');
const id = (_a = card.attr('href')) === null || _a === void 0 ? void 0 : _a.split('/')[1].split('?')[0];
const duration = card.find('.film-infor span').last().text().trim();
const releaseDate = card.find('.film-infor span:nth-child(1)').text().trim();
const type = card.find('.film-infor').find('span, i').remove().end().text().trim();
res.results.push({
image: image,
id: id,
title: title.text(),
japaneseTitle: title.attr('data-jname'),
aliasTitle: card.find('.alias-name').text(),
releaseDate: releaseDate,
type: type,
duration: duration,
url: `${this.baseUrl}/${id}`,
});
}
});
return res;
}
catch (error) {
throw new Error('Something went wrong. Please try again later.');
}
}
/**
* Fetches the list of episodes that the user is currently watching.
* @param connectSid The session ID of the user. Note: This can be obtained from the browser cookies (needs to be signed in)
* @returns A promise that resolves to an array of anime episodes.
*/
async fetchContinueWatching(connectSid) {
try {
if (!(await this.verifyLoginState(connectSid))) {
throw new Error('Invalid session ID');
}
const res = [];
const { data } = await this.client.get(`${this.baseUrl}/user/continue-watching`, {
headers: {
Cookie: `connect.sid=${connectSid}`,
},
});
const $ = (0, cheerio_1.load)(data);
$('.flw-item').each((i, ele) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
const card = $(ele);
const atag = card.find('.film-name a');
const id = (_b = (_a = atag.attr('href')) === null || _a === void 0 ? void 0 : _a.replace('/watch/', '')) === null || _b === void 0 ? void 0 : _b.replace('?ep=', '$episode$');
const timeText = (_e = (_d = (_c = card.find('.fdb-time')) === null || _c === void 0 ? void 0 : _c.text()) === null || _d === void 0 ? void 0 : _d.split('/')) !== null && _e !== void 0 ? _e : [];
const duration = (_g = (_f = timeText.pop()) === null || _f === void 0 ? void 0 : _f.trim()) !== null && _g !== void 0 ? _g : '';
const watchedTime = timeText.length > 0 ? timeText[0].trim() : '';
res.push({
id: id,
title: atag.text(),
number: parseInt(card.find('.fdb-type').text().replace('EP', '').trim()),
duration: duration,
watchedTime: watchedTime,
url: `${this.baseUrl}${atag.attr('href')}`,
image: (_h = card.find('img')) === null || _h === void 0 ? void 0 : _h.attr('data-src'),
japaneseTitle: atag.attr('data-jname'),
nsfw: ((_j = card.find('.tick-rate')) === null || _j === void 0 ? void 0 : _j.text()) === '18+' ? true : false,
sub: parseInt((_k = card.find('.tick-item.tick-sub')) === null || _k === void 0 ? void 0 : _k.text()) || 0,
dub: parseInt((_l = card.find('.tick-item.tick-dub')) === null || _l === void 0 ? void 0 : _l.text()) || 0,
episodes: parseInt((_m = card.find('.tick-item.tick-eps')) === null || _m === void 0 ? void 0 : _m.text()) || 0,
});
});
return res;
}
catch (err) {
throw new Error(err.message);
}
}
async fetchWatchList(connectSid, page = 1, sortListType) {
if (!(await this.verifyLoginState(connectSid))) {
throw new Error('Invalid session ID');
}
if (0 >= page) {
page = 1;
}
let type = 0;
switch (sortListType) {
case models_1.WatchListType.WATCHING:
type = 1;
case models_1.WatchListType.ONHOLD:
type = 2;
case models_1.WatchListType.PLAN_TO_WATCH:
type = 3;
case models_1.WatchListType.DROPPED:
type = 4;
case models_1.WatchListType.COMPLETED:
type = 5;
}
return this.scrapeCardPage(`${this.baseUrl}/user/watch-list?page=${page}${type != 0 ? '&type=' + type : ''}`, {
headers: { Cookie: `connect.sid=${connectSid}` },
});
}
}
// (async () => {
// const zoro = new Zoro();
// const anime = await zoro.search('Dandadan');
// const info = await zoro.fetchAnimeInfo('solo-leveling-season-2-arise-from-the-shadow-19413');
// console.log(info.episodes);
// const sources = await zoro.fetchEpisodeSources("solo-leveling-season-2-arise-from-the-shadow-19413$episode$131394$dub", StreamingServers.VidCloud,SubOrSub.DUB);
// })();
exports.default = Zoro;
//# sourceMappingURL=zoro.js.map