diff --git a/anime/hianime/source.js b/anime/hianime/source.js index 408ab43..fbb77b9 100644 --- a/anime/hianime/source.js +++ b/anime/hianime/source.js @@ -6,24 +6,12 @@ class HiAnime { } getSettings() { - return { - episodeServers: ["HD-1", "HD-2", "HD-3", "HD-4"], - supportsDub: true, - }; + return { episodeServers: ["HD-1", "HD-2", "HD-3", "HD-4"], supportsDub: true }; } _nativeFetch(url, method, headers, body) { - const raw = Native.fetch( - String(url), - method || "GET", - JSON.stringify(headers || {}), - body == null ? "" : String(body) - ); - try { - return JSON.parse(raw || "{}"); - } catch (e) { - return { ok: false, status: 0, headers: {}, body: "" }; - } + const raw = Native.fetch(String(url), method || "GET", JSON.stringify(headers || {}), body == null ? "" : String(body)); + try { return JSON.parse(raw || "{}"); } catch (e) { return { ok: false, status: 0, headers: {}, body: "" }; } } _getText(url, headers) { @@ -33,62 +21,48 @@ class HiAnime { _getJson(url, headers) { const res = this._nativeFetch(url, "GET", headers, ""); - try { - return JSON.parse(String(res.body || "{}")); - } catch (e) { - return {}; - } + try { return JSON.parse(String(res.body || "{}")); } catch (e) { return {}; } } search(query) { - if (typeof query === "string") { - query = { query, media: { startDate: { year: 0, month: 0 } } }; - } + if (typeof query === "string") query = { query, media: { startDate: { year: 0, month: 0, day: 0 } } }; - const normalize = (str) => - this.safeString(str).toLowerCase().replace(/[^a-z0-9]+/g, ""); - - const start = (query.media && query.media.startDate) || { year: 0, month: 0 }; - - const fetchMatches = (url) => { - const html = this._getText(url, {}); - const regex = /]+title="([^"]+)"[^>]+data-id="(\d+)"/g; - - return [...html.matchAll(regex)].map((m) => { - const id = m[3]; - const pageUrl = m[1]; - const title = m[2]; - - const jnameRegex = new RegExp( - `

[\\s\\S]*?]+href="\\/${pageUrl}[^"]*"[^>]+data-jname="([^"]+)"`, - "i" - ); - const jnameMatch = html.match(jnameRegex); - const jname = jnameMatch ? jnameMatch[1] : null; - - const imageRegex = new RegExp( - `]+data-src="([^"]+)"`, - "i" - ); - const imageMatch = html.match(imageRegex); - const image = imageMatch ? imageMatch[1] : null; - - return { - id, - pageUrl, - title, - image, - normTitleJP: normalize(this.normalizeSeasonParts(jname)), - normTitle: normalize(this.normalizeSeasonParts(title)), - }; - }); - }; + const start = (query.media && query.media.startDate) || { year: 0, month: 0, day: 0 }; + const sy = start.year || 0; + const sm = start.month || 0; + const sd = start.day || 0; const url = `${this.baseUrl}/search?keyword=${encodeURIComponent(query.query)}` + - `&sy=${start.year || 0}&sm=${start.month || 0}&sort=default`; + `&sy=${sy}&sm=${sm}` + + (sd ? `&sd=${sd}` : ``) + + `&sort=default`; + + const html = this._getText(url, {}); + const regex = /]+title="([^"]+)"[^>]+data-id="(\d+)"/g; + + const matches = [...html.matchAll(regex)].map((m) => { + const id = m[3]; + const pageUrl = m[1]; + const title = m[2]; + + const jnameRegex = new RegExp( + `

[\\s\\S]*?]+href="\\/${pageUrl}[^"]*"[^>]+data-jname="([^"]+)"`, + "i" + ); + const jnameMatch = html.match(jnameRegex); + const jname = jnameMatch ? jnameMatch[1] : null; + + const imageRegex = new RegExp( + `]+data-src="([^"]+)"`, + "i" + ); + const imageMatch = html.match(imageRegex); + const image = imageMatch ? imageMatch[1] : null; + + return { id, pageUrl, title, image }; + }); - const matches = fetchMatches(url); if (!matches.length) return []; const subOrDub = query.dub ? "dub" : "sub"; @@ -106,11 +80,9 @@ class HiAnime { const id = parts[0]; const subOrDub = parts[1] || "sub"; - const json = this._getJson(`${this.baseUrl}/ajax/v2/episode/list/${id}`, { - "X-Requested-With": "XMLHttpRequest", - }); - + const json = this._getJson(`${this.baseUrl}/ajax/v2/episode/list/${id}`, { "X-Requested-With": "XMLHttpRequest" }); const html = String(json.html || ""); + const episodes = []; const regex = /]*class="[^"]*\bep-item\b[^"]*"[^>]*data-number="(\d+)"[^>]*data-id="(\d+)"[^>]*href="([^"]+)"[\s\S]*?
]*title="([^"]+)"/g; @@ -129,25 +101,17 @@ class HiAnime { } findEpisodeServer(episode, _server) { - if (typeof episode === "string") { - try { - episode = JSON.parse(episode); - } catch (e) {} - } + if (typeof episode === "string") { try { episode = JSON.parse(episode); } catch (e) {} } const parts = String((episode && episode.id) || "").split("/"); const id = parts[0]; const subOrDub = parts[1] || "sub"; const serverName = _server !== "default" ? _server : "HD-1"; - if (_server === "HD-4") return null; - const serverJson = this._getJson( - `${this.baseUrl}/ajax/v2/episode/servers?episodeId=${id}`, - { "X-Requested-With": "XMLHttpRequest" } - ); - + const serverJson = this._getJson(`${this.baseUrl}/ajax/v2/episode/servers?episodeId=${id}`, { "X-Requested-With": "XMLHttpRequest" }); const serverHtml = String(serverJson.html || ""); + const regex = new RegExp( `]*class="item server-item"[^>]*data-type="${subOrDub}"[^>]*data-id="(\\d+)"[^>]*>\\s*]*>\\s*${serverName}\\s*`, "i" @@ -155,13 +119,9 @@ class HiAnime { const match = regex.exec(serverHtml); if (!match) throw new Error(`Server "${serverName}" (${subOrDub}) not found`); - const serverId = match[1]; - const sourcesJson = this._getJson( - `${this.baseUrl}/ajax/v2/episode/sources?id=${serverId}`, - { "X-Requested-With": "XMLHttpRequest" } - ); + const sourcesJson = this._getJson(`${this.baseUrl}/ajax/v2/episode/sources?id=${serverId}`, { "X-Requested-With": "XMLHttpRequest" }); let decryptData = null; let requiredHeaders = {}; @@ -173,24 +133,19 @@ class HiAnime { if (!decryptData) { decryptData = this._getJson( - `https://ac-api.ofchaos.com/api/anime/embed/convert/v2?embedUrl=${encodeURIComponent( - sourcesJson.link - )}`, + `https://ac-api.ofchaos.com/api/anime/embed/convert/v2?embedUrl=${encodeURIComponent(sourcesJson.link)}`, {} ); requiredHeaders = { Referer: "https://megacloud.club/", Origin: "https://megacloud.club", - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", "X-Requested-With": "XMLHttpRequest", }; } const sources = decryptData.sources || []; - const streamSource = - sources.find((s) => s.type === "hls") || sources.find((s) => s.type === "mp4"); - + const streamSource = sources.find((s) => s.type === "hls") || sources.find((s) => s.type === "mp4"); if (!streamSource || !streamSource.file) throw new Error("No valid stream file found"); const subtitles = (decryptData.tracks || []) @@ -205,30 +160,10 @@ class HiAnime { return { server: serverName, headers: requiredHeaders, - videoSources: [ - { - url: streamSource.file, - type: streamSource.type === "hls" ? "m3u8" : "mp4", - quality: "auto", - subtitles, - }, - ], + videoSources: [{ url: streamSource.file, type: streamSource.type === "hls" ? "m3u8" : "mp4", quality: "auto", subtitles }], }; } - safeString(str) { - return typeof str === "string" ? str : ""; - } - - normalizeSeasonParts(title) { - const s = this.safeString(title); - return s - .toLowerCase() - .replace(/[^a-z0-9]+/g, "") - .replace(/\d+(st|nd|rd|th)/g, (m) => m.replace(/st|nd|rd|th/, "")) - .replace(/season|cour|part/g, ""); - } - extractMegaCloudSync(embedUrl) { const s = String(embedUrl); const mm = s.match(/^(https?):\/\/([^\/]+)(\/.*)?$/i); @@ -243,8 +178,7 @@ class HiAnime { "X-Requested-With": "XMLHttpRequest", Referer: baseDomain, Origin: `${protocol}://${host}`, - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", }; const html = this._getText(embedUrl, headers); @@ -258,16 +192,11 @@ class HiAnime { if (match48) nonce = match48[0]; else { const match3x16 = [...html.matchAll(/["']([A-Za-z0-9]{16})["']/g)]; - if (match3x16.length >= 3) { - nonce = match3x16[0][1] + match3x16[1][1] + match3x16[2][1]; - } + if (match3x16.length >= 3) nonce = match3x16[0][1] + match3x16[1][1] + match3x16[2][1]; } if (!nonce) throw new Error("nonce not found"); - const sourcesJson = this._getJson( - `${baseDomain}embed-2/v3/e-1/getSources?id=${fileId}&_k=${nonce}`, - headers - ); + const sourcesJson = this._getJson(`${baseDomain}embed-2/v3/e-1/getSources?id=${fileId}&_k=${nonce}`, headers); return { sources: sourcesJson.sources || [],