UNPKG

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.

332 lines 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cheerio_1 = require("cheerio"); class AnimeKaiPro { constructor() { this.api = "https://animekai.to"; } getSettings() { return { episodeServers: ["Server 1", "Server 2"], supportsDub: true, }; } async search(query) { let normalizedQuery = this.normalizeQuery(query["query"]); console.log("Normalized Query: " + normalizedQuery); const url = `${this.api}/browser?keyword=${encodeURIComponent(normalizedQuery)}`; try { const data = await this._makeRequest(url); const $ = (0, cheerio_1.load)(data); const animes = []; $("div.aitem-wrapper>div.aitem").each((_, elem) => { var _a, _b, _c; const id = (_b = (_a = $(elem).find("a.poster").attr("href")) === null || _a === void 0 ? void 0 : _a.slice(1)) !== null && _b !== void 0 ? _b : ""; const title = (_c = $(elem).find("a.title").attr("title")) !== null && _c !== void 0 ? _c : ""; const subOrDub = this.isSubOrDubOrBoth($, elem); const url = `${this.api}/${id.slice(1)}`; const anime = { id: `${id}?dub=${query['dub']}`, url: url, title: title, subOrDub: subOrDub, }; animes.push(anime); }); return animes; } catch (ex) { throw new Error(ex); } } async findEpisodes(id) { const url = `${this.api}/${id.split('?dub')[0]}`; const rateBoxIdRegex = /<div class="rate-box"[^>]*data-id="([^"]+)"/; try { let data = await this._makeRequest(url); const idMatch = data.match(rateBoxIdRegex); const aniId = idMatch ? idMatch[1] : null; const urlFetchToken = KAICODEX.enc(aniId); const fetchUrlListApi = `${this.api}/ajax/episodes/list?ani_id=${aniId}&_=${urlFetchToken}`; const responseTextListApi = await this._makeRequest(fetchUrlListApi); data = await JSON.parse(responseTextListApi); const episodes = []; const $ = (0, cheerio_1.load)(data.result); $('ul.range>li>a').each((_, elem) => { const title = $(elem).find('span').text().replace(/\s/g, ' '); const number = parseInt($(elem).attr('num'), 10); const token = $(elem).attr('token'); const tokenEncoded = KAICODEX.enc(token); const episodeUrl = `https://animekai.to/ajax/links/list?token=${token}&_=${tokenEncoded}`; episodes.push({ id: token !== null && token !== void 0 ? token : "", number: number, title: title, url: `${episodeUrl}?dub=${id.split('?dub=')[1]}` }); }); return episodes; } catch (ex) { throw new Error(ex); } } async findEpisodeServer(episode, _server) { let server = "Server 1"; if (_server !== "default") server = _server; const episodeUrl = episode.url.replace('\u0026', '&').split('?dub')[0]; const dubRequested = episode.url.split('?dub=')[1]; try { const responseText = await this._makeRequest(episodeUrl); const cleanedHtml = cleanJsonHtml(responseText); const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/; const softsubRegex = /<div class="server-items lang-group" data-id="softsub"[^>]*>([\s\S]*?)<\/div>/; const dubRegex = /<div class="server-items lang-group" data-id="dub"[^>]*>([\s\S]*?)<\/div>/; const subMatch = subRegex.exec(cleanedHtml); const softsubMatch = softsubRegex.exec(cleanedHtml); const dubMatch = dubRegex.exec(cleanedHtml); const sub = subMatch ? subMatch[1].trim() : ""; const softsub = softsubMatch ? softsubMatch[1].trim() : ""; const dub = dubMatch ? dubMatch[1].trim() : ""; let dataLid = ""; let fetchUrlServerApi = ""; let KaiMegaUrlJson = ""; let megaELinkJson = ""; let megaEmbeddedUrl = ""; let megaMediaUrl = ""; let streamUrlJson = ""; let streamUrl = ""; let serverSpanRegex = ""; if (server == "Server 1") serverSpanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>Server 1<\/span>/; else serverSpanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>Server 2<\/span>/; const serverMatch = dubRequested === 'true' ? serverSpanRegex.exec(dub) : serverSpanRegex.exec(sub); if (serverMatch && serverMatch[1]) { dataLid = serverMatch[1]; let dataLidToken = KAICODEX.enc(dataLid); fetchUrlServerApi = `https://animekai.to/ajax/links/view?id=${dataLid}&_=${dataLidToken}`; const responseTextServerApi = await this._makeRequest(fetchUrlServerApi); const dataServerApi = await JSON.parse(responseTextServerApi); KaiMegaUrlJson = KAICODEX.dec(dataServerApi.result); megaELinkJson = JSON.parse(KaiMegaUrlJson); megaEmbeddedUrl = megaELinkJson.url; megaMediaUrl = megaEmbeddedUrl.replace("/e/", "/media/"); const mediaText = await this._makeRequest(megaMediaUrl); const mediaJson = await JSON.parse(mediaText); streamUrlJson = mediaJson.result; streamUrlJson = KAICODEX.decMega(streamUrlJson); const parsedStreamData = JSON.parse(streamUrlJson); if (parsedStreamData && parsedStreamData.sources && parsedStreamData.sources.length > 0) { streamUrl = parsedStreamData.sources[0].file; } else { console.log('No stream sources found in the response' + parsedStreamData); } } if (streamUrl == "") { throw new Error("Unable to find a valid source"); } const streams = await fetch(streamUrl, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "DNT": "1" } }); const regex = /#EXT-X-STREAM-INF:BANDWIDTH=\d+,RESOLUTION=(\d+x\d+)\s*(.*)/g; const videoSources = []; let resolutionMatch; const streamText = await streams.text(); while ((resolutionMatch = regex.exec(streamText)) !== null) { let url = ""; if (server == "Server 2") { console.log("yes"); url = `${streamUrl.split(',')[0]}/${resolutionMatch[2]}`; } else { console.log("no"); url = `${streamUrl.split('/list')[0]}/${resolutionMatch[2]}`; } videoSources.push({ quality: resolutionMatch[1].split('x')[1] + 'p', subtitles: [], type: 'm3u8', url: url }); console.log(videoSources); } const episodeServer = { server: server, headers: { "Access-Control-Allow-Origin": "*", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0' }, videoSources: [videoSources] }; return episodeServer; } catch (e) { throw new Error(e); } } normalizeQuery(query) { let normalizedQuery = query .replace(/\b(\d+)(st|nd|rd|th)\b/g, "$1") .replace(/\s+/g, " ") .replace(/(\d+)\s*Season/i, "$1") .replace(/Season\s*(\d+)/i, "$1") .trim(); return normalizedQuery; } async _makeRequest(url) { const response = await fetch(url, { method: "GET", headers: { "DNT": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0", Cookie: "__ddg1_=;__ddg2_=;", }, }); if (!response.ok) { throw new Error(`Failed to fetch: ${response.statusText}`); } const body = await response.text(); return body; } isSubOrDubOrBoth($, elem) { const sub = $(elem).find("span.sub").text(); const dub = $(elem).find("span.dub").text(); if (sub != "" && dub != "") { return "both"; } if (sub != "") { return "sub"; } return "dub"; } } const KAICODEX = { enc(n) { var u = KAICODEX.safeBtoa; var a = KAICODEX.rc4; var s = KAICODEX.replaceChars; var r = KAICODEX.reverseString; n = u(s(u(a('sXmH96C4vhRrgi8', r(r(u(a('kOCJnByYmfI', s(s(r(u(a('0DU8ksIVlFcia2', n))), '1wctXeHqb2', '1tecHq2Xbw'), '48KbrZx1ml', 'Km8Zb4lxr1'))))))), 'hTn79AMjduR5', 'djn5uT7AMR9h')); return encodeURIComponent(n); }, encPlain(n) { return KAICODEX.safeBtoa(KAICODEX.rc4('n1PEbDBiipbJZvZc', encodeURIComponent(n))); }, dec(n) { var u = KAICODEX.safeAtob; var a = KAICODEX.rc4; var s = KAICODEX.replaceChars; var r = KAICODEX.reverseString; n = a('0DU8ksIVlFcia2', u(r(s(s(a('kOCJnByYmfI', u(r(r(a('sXmH96C4vhRrgi8', u(s(u(n), 'djn5uT7AMR9h', 'hTn79AMjduR5'))))))), 'Km8Zb4lxr1', '48KbrZx1ml'), '1tecHq2Xbw', '1wctXeHqb2')))); return decodeURIComponent(n); }, decPlain(n) { return decodeURIComponent(KAICODEX.rc4('n1PEbDBiipbJZvZc', KAICODEX.safeAtob(n))); }, decMega(n) { var o = KAICODEX.safeAtob; var e = KAICODEX.rc4; var c = KAICODEX.replaceChars; var v = KAICODEX.reverseString; n = c(e('fnxEj3tD4Bl0X', o(e('IjilzMV57GrnF', o(c(v(c(e('PlzI69YVCtGwoa8', o(o(n))), 'c2IfHZwSX1mj', 'mwfXcS2ZjI1H')), '82NkgQDYbIF', '82IQNkFgYbD'))))), 'crwkth05iJR8', 'JRkt8rw0i5ch'); return decodeURIComponent(n); }, rc4: function (key, str) { var s = [], j = 0, x, res = ''; for (var i = 0; i < 256; i++) { s[i] = i; } for (i = 0; i < 256; i++) { j = (j + s[i] + key.charCodeAt(i % key.length)) % 256; x = s[i]; s[i] = s[j]; s[j] = x; } i = 0; j = 0; for (var y = 0; y < str.length; y++) { i = (i + 1) % 256; j = (j + s[i]) % 256; x = s[i]; s[i] = s[j]; s[j] = x; res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]); } return res; }, safeBtoa: function (s) { return btoa(s).replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, ''); }, safeAtob: function (s) { return atob(s.replace(/_/g, '/').replace(/-/g, '+')); }, reverseString: function (s) { return s.split('').reverse().join(''); }, replaceChars: function (s, f, r) { let i = f.length; let m = {}; while (i-- && (m[f[i]] = r[i])) { } return s.split("").map((v) => m[v] || v).join(''); } }; function btoa(input) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; let str = String(input); let output = ''; for (let block = 0, charCode, i = 0, map = chars; str.charAt(i | 0) || (map = '=', i % 1); output += map.charAt(63 & (block >> (8 - (i % 1) * 8)))) { charCode = str.charCodeAt(i += 3 / 4); if (charCode > 0xFF) { throw new Error("btoa failed: The string contains characters outside of the Latin1 range."); } block = (block << 8) | charCode; } return output; } function atob(input) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; let str = String(input).replace(/=+$/, ''); let output = ''; if (str.length % 4 == 1) { throw new Error("atob failed: The input is not correctly encoded."); } for (let bc = 0, bs, buffer, i = 0; (buffer = str.charAt(i++)); ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))) : 0) { buffer = chars.indexOf(buffer); } return output; } function cleanHtmlSymbols(string) { if (!string) return ""; return string .replace(/’/g, "'") .replace(/–/g, "-") .replace(/&#[0-9]+;/g, "") .replace(/\r?\n|\r/g, " ") .replace(/\s+/g, " ") .trim(); } function cleanJsonHtml(jsonHtml) { if (!jsonHtml) return ""; return jsonHtml .replace(/\\"/g, '"') .replace(/\\'/g, "'") .replace(/\\\\/g, '\\') .replace(/\\n/g, '\n') .replace(/\\t/g, '\t') .replace(/\\r/g, '\r'); } function LoadDoc(data) { throw new Error('Function not implemented.'); } exports.default = AnimeKaiPro; //# sourceMappingURL=animekaipro.js.map