From cace57b46018375ea299ec467121aefd759561b9 Mon Sep 17 00:00:00 2001 From: MrGus Date: Wed, 24 Dec 2025 17:16:02 +0100 Subject: [PATCH] Update anime/hianime/source.js --- anime/hianime/source.js | 254 ++++++++++++++++++++++++++-------------- 1 file changed, 167 insertions(+), 87 deletions(-) diff --git a/anime/hianime/source.js b/anime/hianime/source.js index be1f66f..70e8827 100644 --- a/anime/hianime/source.js +++ b/anime/hianime/source.js @@ -3,11 +3,11 @@ console.log("[HiAnime-TV] source.js loaded"); function HiAnime() { console.log("[HiAnime-TV] constructor()"); this.type = "anime-board"; - this.version = "1.0"; + this.version = "1.1"; this.baseUrl = "https://hianime.to"; } -HiAnime.prototype.getSettings = function() { +HiAnime.prototype.getSettings = function () { console.log("[HiAnime-TV] getSettings()"); return { episodeServers: ["HD-1", "HD-2", "HD-3", "HD-4"], @@ -15,19 +15,11 @@ HiAnime.prototype.getSettings = function() { }; }; -HiAnime.prototype.safeString = function(str) { +HiAnime.prototype.safeString = function (str) { return (typeof str === "string" ? str : ""); }; -HiAnime.prototype.normalizeSeasonParts = function(title) { - var s = this.safeString(title); - return s.toLowerCase() - .replace(/[^a-z0-9]+/g, "") - .replace(/\d+(st|nd|rd|th)/g, function(m) { return m.replace(/st|nd|rd|th/, ""); }) - .replace(/season|cour|part/g, ""); -}; - -HiAnime.prototype._decodeHtml = function(s) { +HiAnime.prototype._decodeHtml = function (s) { return this.safeString(s) .replace(/&/g, "&") .replace(/"/g, '"') @@ -35,71 +27,86 @@ HiAnime.prototype._decodeHtml = function(s) { .replace(/'/g, "'") .replace(/</g, "<") .replace(/>/g, ">") - .replace(/&#(\d+);?/g, function(m, d) { + .replace(/&#(\d+);?/g, function (m, d) { var n = parseInt(d, 10); return (typeof n === "number" && !isNaN(n)) ? String.fromCharCode(n) : m; }); }; -HiAnime.prototype._normalize = function(str) { - return this.safeString(str).toLowerCase().replace(/[^a-z0-9]+/g, ""); -}; - -HiAnime.prototype._escapeRe = function(s) { +HiAnime.prototype._escapeRe = function (s) { return this.safeString(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; -HiAnime.prototype._fetch = function(url, opts) { +HiAnime.prototype._fetch = function (url, opts) { var o = opts || {}; var headers = {}; - - headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"; + + headers["User-Agent"] = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"; headers["Accept"] = "text/html,application/json;q=0.9,*/*;q=0.8"; headers["Referer"] = this.baseUrl + "/"; - + if (o.headers && typeof o.headers === "object") { for (var k in o.headers) { - if (o.headers.hasOwnProperty(k)) { + if (Object.prototype.hasOwnProperty.call(o.headers, k)) { headers[k] = o.headers[k]; } } } - + var method = o.method || "GET"; var body = (o.body === undefined || o.body === null) ? "" : String(o.body); - console.log("[HiAnime-TV] fetch", method, url, "headers=", Object.keys(headers).length, "bodyLen=", body.length); + console.log( + "[HiAnime-TV] fetch", + method, + url, + "headers=", + Object.keys(headers).length, + "bodyLen=", + body.length + ); var r = fetch(url, { method: method, headers: headers, body: body }); console.log("[HiAnime-TV] fetchResp status=", r.status, "ok=", r.ok); return r; }; -HiAnime.prototype.search = function(query) { +HiAnime.prototype.search = function (query) { console.log("[HiAnime-TV] search() raw=", query); try { var q = query; if (typeof q === "string") { - try { q = JSON.parse(q); } - catch (e) { q = { query: query, dub: false, media: { startDate: {} } }; } + try { + q = JSON.parse(q); + } catch (e) { + q = { query: query }; + } } q = q || {}; var queryText = this.safeString(q.query || q.title); - var dub = !!q.dub; - var start = (q.media && q.media.startDate) ? q.media.startDate : {}; var year = start.year || ""; var month = start.month || ""; - console.log("[HiAnime-TV] search() parsed queryText=", queryText, "dub=", dub, "year=", year, "month=", month); + console.log( + "[HiAnime-TV] search() parsed queryText=", + queryText, + "year=", + year, + "month=", + month + ); if (!queryText) return []; - var self = this; - - var url = this.baseUrl + "/search?keyword=" + encodeURIComponent(queryText) + "&sort=default"; + var url = + this.baseUrl + + "/search?keyword=" + + encodeURIComponent(queryText) + + "&sort=default"; if (year) url += "&sy=" + year; if (month) url += "&sm=" + month; @@ -108,48 +115,55 @@ HiAnime.prototype.search = function(query) { var html = this._fetch(url).text(); console.log("[HiAnime-TV] search() htmlLen=", (html || "").length); - var regex = /]+title="([^"]+)"[^>]+data-id="(\d+)"/g; - var matches = []; - var match; - - while ((match = regex.exec(html)) !== null) { - matches.push(match); - } - - console.log("[HiAnime-TV] search() matchAll count=", matches.length); + var regex = /]+href="\/watch\/([^"]+)"[^>]*data-id="(\d+)"[^>]*title="([^"]+)"/gi; var out = []; - for (var i = 0; i < matches.length; i++) { - var m = matches[i]; - var id = m[3]; - var pageUrl = m[1]; - var title = this._decodeHtml(m[2]); + var match; - var imageRegex = new RegExp( - "]+data-src=\"([^\"]+)\"", - "i" - ); - var imageMatch = html.match(imageRegex); - var image = imageMatch ? imageMatch[1] : null; + while ((match = regex.exec(html)) !== null) { + var pagePath = match[1]; + var id = match[2]; + var title = this._decodeHtml(match[3]); + + var image = null; + try { + var imageRegex = new RegExp( + "]+href=\"/watch/" + + this._escapeRe(pagePath) + + "\"[\\s\\S]*?]+(?:data-src|src)=\"([^\"]+)\"", + "i" + ); + var im = html.match(imageRegex); + image = im ? im[1] : null; + } catch (e2) {} out.push({ - id: id + "/" + (dub ? "dub" : "sub"), + id: String(id), title: title, image: image, - url: this.baseUrl + "/" + pageUrl, - subOrDub: dub ? "dub" : "sub" + url: this.baseUrl + "/watch/" + pagePath, + subOrDub: "sub" }); } - console.log("[HiAnime-TV] search() returning=", out.length, "first=", out[0] ? out[0].title : "none"); + console.log( + "[HiAnime-TV] search() returning=", + out.length, + "first=", + out[0] ? out[0].title : "none" + ); return out; } catch (e) { - console.error("[HiAnime-TV] search() ERROR", String(e), e && e.stack ? e.stack : ""); + console.error( + "[HiAnime-TV] search() ERROR", + String(e), + e && e.stack ? e.stack : "" + ); throw e; } }; -HiAnime.prototype.findEpisodes = function(animeId) { +HiAnime.prototype.findEpisodes = function (animeId) { console.log("[HiAnime-TV] findEpisodes() animeId=", animeId); try { var raw = animeId; @@ -179,6 +193,9 @@ HiAnime.prototype.findEpisodes = function(animeId) { } } + id = String(id || "").trim(); + if (!id) throw new Error("Missing anime id"); + console.log("[HiAnime-TV] findEpisodes() parsed id=", id, "subOrDub=", subOrDub); var url = this.baseUrl + "/ajax/v2/episode/list/" + id; @@ -186,10 +203,18 @@ HiAnime.prototype.findEpisodes = function(animeId) { var json = res.json(); var html = this.safeString(json.html); - console.log("[HiAnime-TV] findEpisodes() status=", res.status, "ok=", res.ok, "htmlLen=", html.length); + console.log( + "[HiAnime-TV] findEpisodes() status=", + res.status, + "ok=", + res.ok, + "htmlLen=", + html.length + ); var episodes = []; - var regex = /]*class="[^"]*\bep-item\b[^"]*"[^>]*data-number="(\d+)"[^>]*data-id="(\d+)"[^>]*href="([^"]+)"[\s\S]*?
]*title="([^"]+)"/g; + var regex = + /]*class="[^"]*\bep-item\b[^"]*"[^>]*data-number="(\d+)"[^>]*data-id="(\d+)"[^>]*href="([^"]+)"[\s\S]*?
]*title="([^"]+)"/g; var match; while ((match = regex.exec(html)) !== null) { @@ -197,19 +222,29 @@ HiAnime.prototype.findEpisodes = function(animeId) { id: match[2] + "/" + subOrDub, number: parseInt(match[1], 10), url: this.baseUrl + match[3], - title: this._decodeHtml(match[4]) + title: this._decodeHtml(match[4]), + subOrDub: subOrDub }); } - console.log("[HiAnime-TV] findEpisodes() returning=", episodes.length, "firstEp=", episodes[0] ? episodes[0].number : "none"); + console.log( + "[HiAnime-TV] findEpisodes() returning=", + episodes.length, + "firstEp=", + episodes[0] ? episodes[0].number : "none" + ); return episodes; } catch (e) { - console.error("[HiAnime-TV] findEpisodes() ERROR", String(e), e && e.stack ? e.stack : ""); + console.error( + "[HiAnime-TV] findEpisodes() ERROR", + String(e), + e && e.stack ? e.stack : "" + ); throw e; } }; -HiAnime.prototype.findEpisodeServer = function(episode, _server) { +HiAnime.prototype.findEpisodeServer = function (episode, _server) { console.log("[HiAnime-TV] findEpisodeServer() episode=", episode, "server=", _server); try { var ep = episode; @@ -242,22 +277,33 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { } } + episodeId = String(episodeId || "").trim(); if (!episodeId) throw new Error("Missing episode id"); var serverName = (_server && _server !== "default") ? String(_server) : "HD-1"; - console.log("[HiAnime-TV] parsed episodeId=", episodeId, "subOrDub=", subOrDub, "serverName=", serverName); + console.log( + "[HiAnime-TV] parsed episodeId=", + episodeId, + "subOrDub=", + subOrDub, + "serverName=", + serverName + ); if (serverName === "HD-1" || serverName === "HD-2" || serverName === "HD-3") { - var serversUrl = this.baseUrl + "/ajax/v2/episode/servers?episodeId=" + episodeId; + var serversUrl = this.baseUrl + "/ajax/v2/episode/servers?episodeId=" + encodeURIComponent(episodeId); console.log("[HiAnime-TV] serversUrl=", serversUrl); var serverJson = this._fetch(serversUrl, { headers: { "X-Requested-With": "XMLHttpRequest" } }).json(); var serverHtml = this.safeString(serverJson.html); - console.log("[HiAnime-TV] serverHtmlLen=", serverHtml.length); var regex = new RegExp( - "]*class=\"item server-item\"[^>]*data-type=\"" + this._escapeRe(subOrDub) + "\"[^>]*data-id=\"(\\d+)\"[^>]*>\\s*]*>\\s*" + this._escapeRe(serverName) + "\\s*", + "]*class=\"item server-item\"[^>]*data-type=\"" + + this._escapeRe(subOrDub) + + "\"[^>]*data-id=\"(\\d+)\"[^>]*>[\\s\\S]*?]*>[\\s\\S]*?" + + this._escapeRe(serverName) + + "[\\s\\S]*?<\\/a>", "i" ); @@ -267,7 +313,7 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { var serverId = m[1]; console.log("[HiAnime-TV] serverId=", serverId); - var sourcesUrl = this.baseUrl + "/ajax/v2/episode/sources?id=" + serverId; + var sourcesUrl = this.baseUrl + "/ajax/v2/episode/sources?id=" + encodeURIComponent(serverId); console.log("[HiAnime-TV] sourcesUrl=", sourcesUrl); var sourcesJson = this._fetch(sourcesUrl, { headers: { "X-Requested-With": "XMLHttpRequest" } }).json(); @@ -279,14 +325,19 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { try { decryptData = this.extractMegaCloud(sourcesJson.link, true); if (decryptData && decryptData.headersProvided) requiredHeaders = decryptData.headersProvided; - console.log("[HiAnime-TV] extractMegaCloud ok sources=", (decryptData && decryptData.sources ? decryptData.sources.length : 0)); + console.log( + "[HiAnime-TV] extractMegaCloud ok sources=", + (decryptData && decryptData.sources ? decryptData.sources.length : 0) + ); } catch (err) { console.warn("[HiAnime-TV] Primary decrypter failed:", String(err)); } if (!decryptData) { console.warn("[HiAnime-TV] Trying fallback decrypt..."); - var fallbackUrl = "https://ac-api.ofchaos.com/api/anime/embed/convert/v2?embedUrl=" + encodeURIComponent(sourcesJson.link); + var fallbackUrl = + "https://ac-api.ofchaos.com/api/anime/embed/convert/v2?embedUrl=" + + encodeURIComponent(sourcesJson.link); console.log("[HiAnime-TV] fallbackUrl=", fallbackUrl); var fallbackRes = this._fetch(fallbackUrl); @@ -295,16 +346,20 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { 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" }; - console.log("[HiAnime-TV] fallback decrypt sources=", (decryptData && decryptData.sources ? decryptData.sources.length : 0)); + console.log( + "[HiAnime-TV] fallback decrypt sources=", + (decryptData && decryptData.sources ? decryptData.sources.length : 0) + ); } var sourcesArr = (decryptData && decryptData.sources) ? decryptData.sources : []; var streamSource = null; - + for (var i = 0; i < sourcesArr.length; i++) { var s = sourcesArr[i]; if (s && s.type === "hls" && s.file) { @@ -312,7 +367,7 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { break; } } - + if (!streamSource) { for (var j = 0; j < sourcesArr.length; j++) { var s2 = sourcesArr[j]; @@ -339,7 +394,14 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { } } - console.log("[HiAnime-TV] FINAL stream file=", streamSource.file, "type=", streamSource.type, "subs=", subtitles.length); + console.log( + "[HiAnime-TV] FINAL stream file=", + streamSource.file, + "type=", + streamSource.type, + "subs=", + subtitles.length + ); return { server: serverName, @@ -361,12 +423,16 @@ HiAnime.prototype.findEpisodeServer = function(episode, _server) { console.warn("[HiAnime-TV] Unsupported server=", serverName); return null; } catch (e) { - console.error("[HiAnime-TV] findEpisodeServer() ERROR", String(e), e && e.stack ? e.stack : ""); + console.error( + "[HiAnime-TV] findEpisodeServer() ERROR", + String(e), + e && e.stack ? e.stack : "" + ); throw e; } }; -HiAnime.prototype.extractMegaCloud = function(embedUrl, returnHeaders) { +HiAnime.prototype.extractMegaCloud = function (embedUrl, returnHeaders) { console.log("[HiAnime-TV] extractMegaCloud() embedUrl=", embedUrl, "returnHeaders=", returnHeaders); var url = new URL(embedUrl); @@ -377,13 +443,21 @@ HiAnime.prototype.extractMegaCloud = function(embedUrl, returnHeaders) { "X-Requested-With": "XMLHttpRequest", "Referer": baseDomain, "Origin": url.protocol + "//" + url.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" }; var r = this._fetch(embedUrl, { headers: headers }); var html = r.text(); - console.log("[HiAnime-TV] extractMegaCloud() embed status=", r.status, "ok=", r.ok, "htmlLen=", (html || "").length); + console.log( + "[HiAnime-TV] extractMegaCloud() embed status=", + r.status, + "ok=", + r.ok, + "htmlLen=", + (html || "").length + ); var fileIdMatch = html.match(/\s*File\s+#([a-zA-Z0-9]+)\s*-/i); if (!fileIdMatch) throw new Error("file_id not found in embed page"); @@ -406,15 +480,21 @@ HiAnime.prototype.extractMegaCloud = function(embedUrl, returnHeaders) { } if (!nonce) throw new Error("nonce not found"); - var sourcesUrl = baseDomain + "embed-2/v3/e-1/getSources?id=" + fileId + "&_k=" + nonce; + var sourcesUrl = baseDomain + "embed-2/v3/e-1/getSources?id=" + encodeURIComponent(fileId) + "&_k=" + encodeURIComponent(nonce); console.log("[HiAnime-TV] extractMegaCloud() sourcesUrl=", sourcesUrl); var sr = this._fetch(sourcesUrl, { headers: headers }); var sourcesJson = sr.json(); - console.log("[HiAnime-TV] extractMegaCloud() sources status=", sr.status, "ok=", sr.ok, - "sources=", (sourcesJson.sources ? sourcesJson.sources.length : 0), - "tracks=", (sourcesJson.tracks ? sourcesJson.tracks.length : 0) + console.log( + "[HiAnime-TV] extractMegaCloud() sources status=", + sr.status, + "ok=", + sr.ok, + "sources=", + (sourcesJson.sources ? sourcesJson.sources.length : 0), + "tracks=", + (sourcesJson.tracks ? sourcesJson.tracks.length : 0) ); return { @@ -428,4 +508,4 @@ HiAnime.prototype.extractMegaCloud = function(embedUrl, returnHeaders) { }; module.exports = HiAnime; -console.log("[HiAnime-TV] module.exports set"); \ No newline at end of file +console.log("[HiAnime-TV] module.exports set");