source/animebum/animebum.js
2025-10-11 17:18:45 +02:00

364 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://www.animebum.net/search?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="search-results__item[^>]*>[\s\S]*?<a href="([^"]+)"[^>]*><img src="([^"]+)"[^>]*><\/a>[\s\S]*?<h2>([^<]+)<\/h2>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<div class="description scrollV">\s*<p>([\s\S]*?)<\/p>/i;
const match = regex.exec(html);
let description = match ? match[1].trim() : "N/A";
description = description.replace(/<[^>]+>/g, "");
description = description.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&aacute;/g, "á")
.replace(/&eacute;/g, "é")
.replace(/&iacute;/g, "í")
.replace(/&oacute;/g, "ó")
.replace(/&uacute;/g, "ú")
.replace(/&ntilde;/g, "ñ")
.replace(/&uuml;/g, "ü")
.replace(/&ldquo;/g, "“")
.replace(/&rdquo;/g, "”")
.replace(/&lsquo;/g, "")
.replace(/&rsquo;/g, "")
.replace(/&iexcl;/g, "¡")
.replace(/&iquest;/g, "¿");
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const containerMatch = html.match(
/<div class="list-episodies-content">([\s\S]*?)<\/div>/
);
if (!containerMatch) {
return JSON.stringify([{ href: "Not found", number: "N/A" }]);
}
const containerHtml = containerMatch[1];
const regex = /<a[^>]+href="([^"]+)"[^>]*>[\s\S]*?Episodio\s+(\d+)\s*<\/a>/gi;
let match;
while ((match = regex.exec(containerHtml)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{ href: "Error", number: "Error" }]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
// Try Filemoon first
const filemoonRegex = /(https:\/\/filemoon\.(?:to|sx)\/e\/[A-Za-z0-9]+\/[^\s"']+)/;
const filemoonMatch = filemoonRegex.exec(html);
if (filemoonMatch) {
const filemoon = filemoonMatch[1];
console.log("Filemoon URL found: " + filemoon);
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": "https://animebum.net/",
};
const filemoonResponse = await fetchv2(filemoon, headers);
const filemoonHtml = await filemoonResponse.text();
try {
const streamUrl = await filemoonExtractor(filemoonHtml, "https://animebum.net/");
if (streamUrl) return streamUrl;
} catch (err) {
console.log("Filemoon extraction error:" + err);
}
}
const optionRegex = /<ul class="play-list-tabs">([\s\S]*?)<\/ul>/i;
const optionMatch = optionRegex.exec(html);
if (!optionMatch) return null;
const optionsHtml = optionMatch[1];
const faireMatch = /<li[^>]*data-id="(\d+)"[^>]*>\s*<a[^>]*title="Faire"/i.exec(optionsHtml);
if (!faireMatch) return null;
const faireId = parseInt(faireMatch[1], 10);
const videoRegex = new RegExp(`video\\[${faireId}\\]\\s*=\\s*'([^']+)'`);
const videoMatch = videoRegex.exec(html);
if (!videoMatch) return null;
const iframeHtml = videoMatch[1];
const srcMatch = /src=["']([^"']+)["']/.exec(iframeHtml);
const streamUrl = srcMatch ? srcMatch[1] : null;
const responeSome = await fetchv2(streamUrl);
const htmlSome = await responeSome.text();
const shareIdMatch = htmlSome.match(/var\s+shareId\s*=\s*"([^"]+)"/);
if (!shareIdMatch) return null;
const shareId = shareIdMatch[1];
const someUrl = `https://www.amazon.com/drive/v1/shares/${shareId}?resourceVersion=V2&ContentType=JSON&asset=ALL`;
const shareJsonResp = await fetchv2(someUrl);
const shareJson = await shareJsonResp.json();
console.log(JSON.stringify(shareJson));
const nodeId = shareJson.nodeInfo?.id;
if (!nodeId) return null;
const childrenUrl = `https://www.amazon.com/drive/v1/nodes/${nodeId}/children?resourceVersion=V2&ContentType=JSON&limit=200&sort=%5B%22kind+DESC%22%2C+%22modifiedDate+DESC%22%5D&asset=ALL&tempLink=true&shareId=${shareId}`;
const childrenResp = await fetchv2(childrenUrl);
const childrenData = await childrenResp.json();
console.log(JSON.stringify(childrenData));
const file = childrenData.data.find(item => item.kind === "FILE");
if (!file) return null;
return file.tempLink;
} catch (err) {
console.log("Fetch error:" + err);
return "https://error.org/";
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */