UNPKG

hianime

Version:
203 lines (200 loc) 5.92 kB
// src/hianime.ts import axios from "axios"; import * as cheerio from "cheerio"; var Hianime = class { BASE_URL = "https://hianime.to"; MALSYNC_URL = "https://api.malsync.moe"; async getAnimeList(category, page = 1, query) { const { data } = await axios.get(`${this.BASE_URL}/${category}`, { params: { page, ...query && { keyword: decodeURIComponent(query) } } }); const $ = cheerio.load(data); const animeList = $(".tab-content div.film_list-wrap > div.flw-item").map((_, element) => { const anime = { id: $(element).find("a[data-id]").attr("href"), image: $(element).find("div.film-poster > img").attr("data-src"), title: $(element).find("div.film-detail > .film-name > a").attr("title"), type: $(element).find("div.fd-infor > span.fdi-item").first().text().toUpperCase(), language: { sub: $(element).find(".tick.ltr > .tick-sub").text() || null, dub: $(element).find(".tick.ltr > .tick-dub").text() || null }, dataId: $(element).find("a[data-id]").attr("data-id") }; return anime; }).get(); const totalPage = parseInt( $('a[title="Last"]').attr("href")?.match(/page=(\d+)/)?.[1] ?? page.toString() ); return { page, totalPage, hasNextPage: page < totalPage, results: animeList }; } async getSubbedAnime(page = 1) { return this.getAnimeList("subbed-anime", page); } async getDubbedAnime(page = 1) { return this.getAnimeList("dubbed-anime", page); } async getMostPopular(page = 1) { return this.getAnimeList("most-popular", page); } async getMovies(page = 1) { return this.getAnimeList("movie", page); } async getTVShows(page = 1) { return this.getAnimeList("tv", page); } async getSpecialList(page = 1) { return this.getAnimeList("special", page); } async getONAList(page = 1) { return this.getAnimeList("ona", page); } async getOVAList(page = 1) { return this.getAnimeList("ova", page); } async getTopAiring(page = 1) { return this.getAnimeList("top-airing", page); } async search(query, page = 1) { return this.getAnimeList("search", page, query); } async getEpisodesByMALID(malId) { const { data: malsyncData } = await axios.get( `${this.MALSYNC_URL}/mal/anime/${malId}` ); const ids = malsyncData.Sites.Zoro; if (!ids) { throw new Error(`No Zoro IDs found for MAL ID ${malId}`); } let id = Object.keys(ids)[0]; const episodes = await this.getEpisodes(id ?? ""); return episodes; } async getEpisodes(dataId) { const { data } = await axios.get( `${this.BASE_URL}/ajax/v2/episode/list/${dataId}` ); const $ = cheerio.load(data.html); const episodes = $("div.ss-list > a.ep-item").map((_, element) => { const episode = { id: $(element).attr("data-id"), number: $(element).attr("data-number"), title: $(element).attr("title"), href: $(element).attr("href") }; return episode; }).get(); return episodes; } async getEpisodeServers(episodeId) { const { data } = await axios.get( `${this.BASE_URL}/ajax/v2/episode/servers`, { params: { episodeId } } ); const $ = cheerio.load(data.html); const subServers = $("div.servers-sub .server-item").map((_, element) => { const server = { type: $(element).attr("data-type"), id: $(element).attr("data-id"), serverId: $(element).attr("data-server-id"), name: $(element).find("a.btn").text() }; return server; }).get(); const dubServers = $("div.servers-dub .server-item").map((_, element) => { const server = { type: $(element).attr("data-type"), id: $(element).attr("data-id"), serverId: $(element).attr("data-server-id"), name: $(element).find("a.btn").text() }; return server; }).get(); return { sub: subServers, dub: dubServers }; } async getEpisodeSources(serverId) { const { data } = await axios.get( `${this.BASE_URL}/ajax/v2/episode/sources?id=${serverId}` ); const embedLink = data?.link; if (!embedLink) { throw new Error( `No embed link returned for serverId ${serverId}. Response: ${JSON.stringify( data ).slice(0, 200)}...` ); } const { data: embedData } = await axios.get(embedLink, { headers: { Referer: this.BASE_URL } }); const $ = cheerio.load(embedData); const dataId = $("div[data-id]").attr("data-id"); let nonce = null; const regex48 = /\b[a-zA-Z0-9]{48}\b/; const match48 = embedData.match(regex48); if (match48 && match48[0]) { nonce = match48[0]; } else { const regex16 = /"([a-zA-Z0-9]{16})"/g; const parts = []; let m; while ((m = regex16.exec(embedData)) !== null) { if (m[1]) parts.push(m[1]); } if (parts.length) nonce = parts.join(""); } if (!nonce) { throw new Error( `Failed to extract nonce from embed page for serverId ${serverId}` ); } const { data: sources } = await axios.get( `https://megacloud.blog/embed-2/v3/e-1/getSources`, { params: { id: dataId, _k: nonce }, headers: { Referer: embedLink } } ); sources.headers = { Referer: "https://megacloud.blog/" }; return sources; } }; var hianime_default = Hianime; // src/types/hianime-result.ts var Type = /* @__PURE__ */ ((Type2) => { Type2["Ona"] = "ONA"; Type2["Special"] = "SPECIAL"; Type2["Tv"] = "TV"; return Type2; })(Type || {}); // src/index.ts var index_default = hianime_default; export { hianime_default as Hianime, Type, index_default as default };