]*title="([^"]+)"/g;
-
- let match;
- while ((match = regex.exec(html)) !== null) {
- episodes.push({
- id: `${match[2]}/${subOrDub}`,
- number: parseInt(match[1], 10),
- url: this.baseUrl + match[3],
- title: match[4]
- });
}
-
- return episodes;
- }
-
- async findEpisodeServer(episode, _server) {
- const [id, subOrDub] = episode.id.split("/");
- const serverName = _server !== "default" ? _server : "HD-1";
-
- if (_server === "HD-1" || _server === "HD-2" || _server === "HD-3" || _server === "default") {
- const serverJson = await fetch(
- `${this.baseUrl}/ajax/v2/episode/servers?episodeId=${id}`,
- { headers: { "X-Requested-With": "XMLHttpRequest" } }
- ).then((res) => res.json());
-
- const serverHtml = serverJson.html;
- const regex = new RegExp(
- `
]*class="item server-item"[^>]*data-type="${subOrDub}"[^>]*data-id="(\\d+)"[^>]*>\\s*
]*>\\s*${serverName}\\s*`,
- "i"
- );
-
- const m = regex.exec(serverHtml);
- if (!m) throw new Error(`Server "${serverName}" (${subOrDub}) not found`);
-
- const serverId = m[1];
-
- const sourcesJson = await fetch(
- `${this.baseUrl}/ajax/v2/episode/sources?id=${serverId}`,
- { headers: { "X-Requested-With": "XMLHttpRequest" } }
- ).then((res) => res.json());
-
- let decryptData = null;
- let requiredHeaders = {};
-
- try {
- decryptData = await this.extractMegaCloud(sourcesJson.link, true);
- if (decryptData && decryptData.headersProvided) requiredHeaders = decryptData.headersProvided;
- } catch (err) {
- // ignore
- }
-
- if (!decryptData) {
- const fallbackRes = await fetch(
- `https://ac-api.ofchaos.com/api/anime/embed/convert/v2?embedUrl=${encodeURIComponent(
- sourcesJson.link
- )}`
- );
- decryptData = await 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"
- };
- }
-
- const streamSource =
- (decryptData.sources || []).find((s) => s.type === "hls") ||
- (decryptData.sources || []).find((s) => s.type === "mp4");
-
- if (!streamSource?.file) throw new Error("No valid stream file found");
-
- const subtitles = (decryptData.tracks || [])
- .filter((t) => t.kind === "captions")
- .map((track, index) => ({
- id: `sub-${index}`,
- language: track.label || "Unknown",
- url: track.file,
- isDefault: !!track.default
- }));
-
- return {
- server: serverName,
- headers: requiredHeaders,
- videoSources: [
- {
- url: streamSource.file,
- type: streamSource.type === "hls" ? "m3u8" : "mp4",
- quality: "auto",
- subtitles
- }
- ]
- };
- }
-
- if (_server === "HD-4") {
- return null;
- }
-
- return null;
- }
-
- 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, "");
- }
-
- async extractMegaCloud(embedUrl, returnHeaders = false) {
- const url = new URL(embedUrl);
- const baseDomain = `${url.protocol}//${url.host}/`;
-
- const 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"
- };
-
- const html = await fetch(embedUrl, { headers }).then((r) => r.text());
-
- const fileIdMatch = html.match(/
\s*File\s+#([a-zA-Z0-9]+)\s*-/i);
- if (!fileIdMatch) throw new Error("file_id not found in embed page");
- const fileId = fileIdMatch[1];
-
- let nonce = null;
- const match48 = html.match(/\b[a-zA-Z0-9]{48}\b/);
- 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 (!nonce) throw new Error("nonce not found");
-
- const sourcesJson = await fetch(
- `${baseDomain}embed-2/v3/e-1/getSources?id=${fileId}&_k=${nonce}`,
- { headers }
- ).then((r) => r.json());
-
- 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;
+module.exports = HiAnime;
\ No newline at end of file