]*title="([^"]+)"/g;
+
+ var match;
+ while ((match = regex.exec(html)) !== null) {
+ episodes.push({
+ id: match[2] + "/" + subOrDub,
+ number: parseInt(match[1], 10),
+ url: this.baseUrl + match[3],
+ title: this._decodeHtml(match[4]),
+ subOrDub: subOrDub
+ });
+ }
+
+ 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 : "");
+ throw e;
+ }
+ }
+
+ findEpisodeServer(episode, _server) {
+ console.log("[HiAnime-TV] findEpisodeServer() episode=", episode, "server=", _server);
+ try {
+ var ep = episode;
+
+ if (typeof ep === "string") {
+ try { ep = JSON.parse(ep); } catch (e) {}
+ }
+
+ var episodeId = "";
+ var subOrDub = "sub";
+
+ if (typeof ep === "string") {
+ var raw = ep;
+ if (raw.indexOf("/") !== -1) {
+ var parts = raw.split("/");
+ episodeId = parts[0];
+ subOrDub = parts[1] || "sub";
+ } else {
+ episodeId = raw;
+ }
+ } else if (ep && typeof ep === "object") {
+ var rawId = ep.id || ep.episodeId || "";
+ if (String(rawId).indexOf("/") !== -1) {
+ var parts2 = String(rawId).split("/");
+ episodeId = parts2[0];
+ subOrDub = parts2[1] || (ep.subOrDub || "sub");
+ } else {
+ episodeId = String(rawId);
+ subOrDub = ep.subOrDub || "sub";
+ }
+ }
+
+ 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);
+
+ if (serverName === "HD-1" || serverName === "HD-2" || serverName === "HD-3") {
+ 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]*?
]*>[\\s\\S]*?" +
+ this._escapeRe(serverName) +
+ "[\\s\\S]*?<\\/a>",
+ "i"
+ );
+
+ var m = regex.exec(serverHtml);
+ if (!m) throw new Error("Server \"" + serverName + "\" (" + subOrDub + ") not found");
+
+ var serverId = m[1];
+ console.log("[HiAnime-TV] serverId=", 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();
+ console.log("[HiAnime-TV] sourcesJson has link=", !!sourcesJson.link);
+
+ var decryptData = null;
+ var requiredHeaders = {};
+
+ 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));
+ } 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);
+ console.log("[HiAnime-TV] fallbackUrl=", fallbackUrl);
+
+ var fallbackRes = this._fetch(fallbackUrl);
+ decryptData = fallbackRes.json();
+
+ 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",
+ "X-Requested-With": "XMLHttpRequest"
+ };
+
+ 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) { streamSource = s; break; }
+ }
+ if (!streamSource) {
+ for (var j = 0; j < sourcesArr.length; j++) {
+ var s2 = sourcesArr[j];
+ if (s2 && s2.type === "mp4" && s2.file) { streamSource = s2; break; }
}
}
- }
- if (!streamSource || !streamSource.file) throw new Error("No valid stream file found");
+ if (!streamSource || !streamSource.file) throw new Error("No valid stream file found");
- var subtitles = [];
- var tracks = decryptData.tracks || [];
- for (var k = 0; k < tracks.length; k++) {
- var track = tracks[k];
- if (track && track.kind === "captions" && track.file) {
- subtitles.push({
- id: "sub-" + k,
- language: track.label || "Unknown",
- url: track.file,
- isDefault: !!track.default
- });
+ var subtitles = [];
+ var tracks = decryptData.tracks || [];
+ for (var k = 0; k < tracks.length; k++) {
+ var track = tracks[k];
+ if (track && track.kind === "captions" && track.file) {
+ subtitles.push({
+ id: "sub-" + k,
+ language: track.label || "Unknown",
+ url: track.file,
+ isDefault: !!track.default
+ });
+ }
}
+
+ console.log("[HiAnime-TV] FINAL stream file=", streamSource.file, "type=", streamSource.type, "subs=", subtitles.length);
+
+ return {
+ server: serverName,
+ headers: requiredHeaders,
+ videoSources: [{
+ url: streamSource.file,
+ type: streamSource.type === "hls" ? "m3u8" : "mp4",
+ quality: "auto",
+ subtitles: subtitles
+ }]
+ };
}
- console.log(
- "[HiAnime-TV] FINAL stream file=",
- streamSource.file,
- "type=",
- streamSource.type,
- "subs=",
- subtitles.length
- );
+ if (serverName === "HD-4") {
+ console.warn("[HiAnime-TV] HD-4 not implemented");
+ return null;
+ }
- return {
- server: serverName,
- headers: requiredHeaders,
- videoSources: [{
- url: streamSource.file,
- type: streamSource.type === "hls" ? "m3u8" : "mp4",
- quality: "auto",
- subtitles: subtitles
- }]
- };
- }
-
- if (serverName === "HD-4") {
- console.warn("[HiAnime-TV] HD-4 not implemented");
+ console.warn("[HiAnime-TV] Unsupported server=", serverName);
return null;
+ } catch (e) {
+ console.error("[HiAnime-TV] findEpisodeServer() ERROR", String(e), e && e.stack ? e.stack : "");
+ throw e;
}
+ }
- console.warn("[HiAnime-TV] Unsupported server=", serverName);
- return null;
- } catch (e) {
- console.error(
- "[HiAnime-TV] findEpisodeServer() ERROR",
- String(e),
- e && e.stack ? e.stack : ""
+ extractMegaCloud(embedUrl, returnHeaders) {
+ console.log("[HiAnime-TV] extractMegaCloud() embedUrl=", embedUrl, "returnHeaders=", returnHeaders);
+
+ var url = new URL(embedUrl);
+ var baseDomain = url.protocol + "//" + url.host + "/";
+
+ var headers = {
+ "Accept": "*/*",
+ "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"
+ };
+
+ 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);
+
+ 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");
+ var fileId = fileIdMatch[1];
+
+ var nonce = null;
+ var match48 = html.match(/\b[a-zA-Z0-9]{48}\b/);
+ if (match48) {
+ nonce = match48[0];
+ } else {
+ var regex16 = /["']([A-Za-z0-9]{16})["']/g;
+ var matches = [];
+ var m;
+ while ((m = regex16.exec(html)) !== null) matches.push(m[1]);
+ if (matches.length >= 3) nonce = matches[0] + matches[1] + matches[2];
+ }
+ if (!nonce) throw new Error("nonce not found");
+
+ 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)
);
- throw e;
+
+ return {
+ sources: sourcesJson.sources || [],
+ tracks: sourcesJson.tracks || [],
+ intro: sourcesJson.intro || null,
+ outro: sourcesJson.outro || null,
+ server: sourcesJson.server || null,
+ headersProvided: returnHeaders ? headers : undefined
+ };
}
-};
-
-HiAnime.prototype.extractMegaCloud = function (embedUrl, returnHeaders) {
- console.log("[HiAnime-TV] extractMegaCloud() embedUrl=", embedUrl, "returnHeaders=", returnHeaders);
-
- var url = new URL(embedUrl);
- var baseDomain = url.protocol + "//" + url.host + "/";
-
- var headers = {
- "Accept": "*/*",
- "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"
- };
-
- 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
- );
-
- 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");
- var fileId = fileIdMatch[1];
-
- var nonce = null;
- var match48 = html.match(/\b[a-zA-Z0-9]{48}\b/);
- if (match48) {
- nonce = match48[0];
- } else {
- var regex16 = /["']([A-Za-z0-9]{16})["']/g;
- var matches = [];
- var m;
- while ((m = regex16.exec(html)) !== null) {
- matches.push(m[1]);
- }
- if (matches.length >= 3) {
- nonce = matches[0] + matches[1] + matches[2];
- }
- }
- if (!nonce) throw new Error("nonce not found");
-
- 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)
- );
-
- return {
- sources: sourcesJson.sources || [],
- tracks: sourcesJson.tracks || [],
- intro: sourcesJson.intro || null,
- outro: sourcesJson.outro || null,
- server: sourcesJson.server || null,
- headersProvided: returnHeaders ? headers : undefined
- };
-};
+}
module.exports = HiAnime;
console.log("[HiAnime-TV] module.exports set");