diff --git a/aniwatch/aniwatch.js b/aniwatch/aniwatch.js new file mode 100644 index 0000000..0b37c1b --- /dev/null +++ b/aniwatch/aniwatch.js @@ -0,0 +1,217 @@ +async function searchResults(keyword) { + const results = []; + + try { + const response = await fetchv2("https://aniwatchtv.to/search?keyword=" + encodeURIComponent(keyword)); + const html = await response.text(); + + const blocks = html.split('
').slice(1); + + for (const block of blocks) { + const href = block.match(/[\s\S]*?
\s*([\s\S]*?)\s*<\/div>/); + const dateMatch = html.match(/Released:\s*<\/strong>\s*([^<\n]+)/); + + const description = descMatch ? descMatch[1].trim() : "N/A"; + const airdate = dateMatch ? dateMatch[1].trim() : "N/A"; + + return JSON.stringify([{ + description: description, + aliases: "N/A", + airdate: airdate + }]); + + } catch (err) { + return JSON.stringify([{ + description: "Error", + aliases: "Error", + airdate: "Error" + }]); + } +} + +async function extractEpisodes(url) { + const results = []; + try { + let watchUrl = url; + if (!/\/watch\//.test(watchUrl)) { + watchUrl = watchUrl.replace(/\/([^\/]+)$/, '/watch/$1'); + } + + const watchResp = await fetchv2("https://aniwatchtv.to" + watchUrl); + const watchHtml = await watchResp.text(); + const idMatch = watchHtml.match(/]+id="wrapper"[^>]+data-id="(\d+)"[^>]*>/); + if (!idMatch) throw new Error("movie_id not found"); + const movieId = idMatch[1]; + + const epListResp = await fetchv2(`https://aniwatchtv.to/ajax/v2/episode/list/${movieId}`); + const epListJson = await epListResp.json(); + const epHtml = epListJson.html; + + const epRegex = /]+class="ssl-item\s+ep-item"[^>]+data-number="(\d+)"[^>]+data-id="(\d+)"[^>]*>/g; + let match; + while ((match = epRegex.exec(epHtml)) !== null) { + results.push({ + href: match[2], + number: parseInt(match[1], 10) + }); + } + + return JSON.stringify(results); + } catch (err) { + console.error(err); + return JSON.stringify([{ id: "Error", href: "Error", number: "Error", title: "Error" }]); + } +} + +async function extractStreamUrl(ID) { + try { + const serversResp = await fetchv2(`https://aniwatchtv.to/ajax/v2/episode/servers?episodeId=${ID}`); + const serversJson = await serversResp.json(); + const serversHtml = serversJson.html; + + const subServerMatch = serversHtml.match(/
{ + try { + const sourcesResp = await fetchv2(`https://aniwatchtv.to/ajax/v2/episode/sources?id=${serverId}`); + const sourcesJson = await sourcesResp.json(); + const iframeUrl = sourcesJson.link; + + if (!iframeUrl) return null; + + const iframeResp = await fetchv2(iframeUrl, headers); + const iframeHtml = await iframeResp.text(); + + const videoTagMatch = iframeHtml.match(/data-id="([^"]+)"/); + if (!videoTagMatch) return null; + const fileId = videoTagMatch[1]; + + const nonceMatch = iframeHtml.match(/\b[a-zA-Z0-9]{48}\b/) || + iframeHtml.match(/\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b/); + if (!nonceMatch) return null; + + const nonce = nonceMatch.length === 4 ? + nonceMatch[1] + nonceMatch[2] + nonceMatch[3] : + nonceMatch[0]; + + const urlParts = iframeUrl.split('/'); + const protocol = iframeUrl.startsWith('https') ? 'https:' : 'http:'; + const hostname = urlParts[2]; + const defaultDomain = `${protocol}//${hostname}/`; + + const getSourcesUrl = `${defaultDomain}embed-2/v3/e-1/getSources?id=${fileId}&_k=${nonce}`; + const getSourcesResp = await fetchv2(getSourcesUrl, headers); + const getSourcesJson = await getSourcesResp.json(); + console.log(JSON.stringify(getSourcesJson)); + const videoUrl = getSourcesJson.sources?.[0]?.file || ""; + if (!videoUrl) return null; + + const streamHeaders = { + "Referer": defaultDomain, + "User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36" + }; + + return { + title: title, + streamUrl: videoUrl, + headers: streamHeaders, + sourcesData: getSourcesJson + }; + } catch (e) { + console.log(`${title} failed:`, e); + return null; + } + }; + + const serverPromises = []; + if (subServerId) serverPromises.push(processServer(subServerId, "SUB")); + if (dubServerId) serverPromises.push(processServer(dubServerId, "DUB")); + + const results = await Promise.all(serverPromises); + const streams = results.filter(r => r !== null); + + if (streams.length === 0) { + return "https://error.org/"; + } + + const englishTrack = streams[0].sourcesData.tracks?.find(t => t.kind === "captions" && t.label === "English"); + const subtitle = englishTrack ? englishTrack.file : ""; + + const finalStreams = streams.map(s => ({ + title: s.title, + streamUrl: s.streamUrl, + headers: s.headers + })); + console.log(JSON.stringify({ + streams: finalStreams, + subtitle: subtitle + })); + return JSON.stringify({ + streams: finalStreams, + subtitle: subtitle + }); + } catch (err) { + console.error(err); + return "https://error.org/"; + } +} + +function decodeHtmlEntities(text) { + if (!text) { + return ""; + } + return text + .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec)) + .replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => String.fromCharCode(parseInt(hex, 16))) + .replace(/"/g, "\"") + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/ /g, " "); +} + + + +