This commit is contained in:
50/50 2025-10-11 17:18:45 +02:00
commit 09c423e95a
276 changed files with 54396 additions and 0 deletions

315
animeytx/animeytx.js Normal file
View file

@ -0,0 +1,315 @@
function cleanTitle(title) {
return title
.replace(/’/g, "'")
.replace(/–/g, "-")
.replace(/&#[0-9]+;/g, "");
}
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`https://animeytx.net/?s=${keyword}`);
const html = await response.text();
const regex = /<article class="bs"[^>]*>[\s\S]*?<a href="([^"]+)"[^>]*>[\s\S]*?(?:data-src|src)="([^"]+)"[^>]*>[\s\S]*?<h2[^>]*>(.*?)<\/h2>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: cleanTitle(match[3].trim()),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="entry-content"[^>]*>([\s\S]*?)<\/div>/);
let description = "N/A";
if (match) {
description = match[1]
.replace(/<[^>]+>/g, '')
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(code))
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, "&")
.trim();
}
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const regex = /<li data-index="\d+">[\s\S]*?<a href="([^"]+)">/g;
let match;
let count = 1;
while ((match = regex.exec(html)) !== null) {
results.push({
href: encodeURI(match[1].trim()),
number: count
});
count++;
}
const total = results.length;
results.forEach((ep, i) => {
ep.number = total - i;
});
return JSON.stringify(results.reverse());
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(`https://passthrough-worker.simplepostrequest.workers.dev/?simple=${encodeURIComponent(url)}`);
const html = await response.text();
// Try primary method first
let iframeMatch = html.match(/<iframe[^>]+data-src="([^"]+)"/);
let filemoonUrl;
if (iframeMatch) {
const iframeUrl = iframeMatch[1];
const valueMatch = iframeUrl.match(/value=([^&]+)/);
if (valueMatch) {
const valueId = valueMatch[1];
const containerResponse = await fetchv2(`https://mytsumi.com/multiplayer/contenedor.php?id=${valueId}`);
const containerHtml = await containerResponse.text();
const filemoonMatch = containerHtml.match(/"tab_name":"Moon","url":"([^"]+)"/);
if (filemoonMatch) filemoonUrl = filemoonMatch[1].replace(/\\\//g, '/');
}
}
// Fallback if no Filemoon found
if (!filemoonUrl) {
const optionMatch = html.match(/<option[^>]+value="([^"]+)"[^>]*>\s*Moon\s*<\/option>/);
if (!optionMatch) return "https://error.org/";
const decoded = atob(optionMatch[1]);
const srcMatch = decoded.match(/src="([^"]+)"/);
if (!srcMatch) return "https://error.org/";
const fallbackUrl = srcMatch[1];
const fallbackResponse = await fetchv2(fallbackUrl);
const fallbackHtml = await fallbackResponse.text();
const filemoonMatch2 = fallbackHtml.match(/<a href="(https:\/\/filemoon\.to\/e\/[^"]+)">/);
if (!filemoonMatch2) return "https://error.org/";
filemoonUrl = filemoonMatch2[1];
}
console.log(filemoonUrl);
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": url
};
const finalResponse = await fetchv2(filemoonUrl, headers);
const finalHtml = await finalResponse.text();
const streamUrl = await filemoonExtractor(finalHtml, filemoonUrl);
if (streamUrl) return streamUrl;
return "filemoonUrl";
} catch (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 */

19
animeytx/animeytx.json Normal file
View file

@ -0,0 +1,19 @@
{
"sourceName": "AnimeYTX",
"iconUrl": "https://i1.wp.com/animeytx.net/wp-content/uploads/2024/09/cropped-hgn584ghj45-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeytx.net/",
"searchBaseUrl": "https://animeytx.net/",
"scriptUrl": "https://gitlab.com/50n50/sources/-/raw/main/animeytx/animeytx.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false
}