updates and new extensions

This commit is contained in:
2026-01-05 04:46:26 +01:00
parent 5ee2bde49a
commit 83c51a82da
9 changed files with 1500 additions and 129 deletions

View File

@@ -1,14 +1,13 @@
class AnimeAV1 {
constructor() {
this.type = "anime-board";
this.version = "1.2"
this.version = "1.3";
this.api = "https://animeav1.com";
}
getSettings() {
return {
episodeServers: ["HLS", "HLS-DUB"],
episodeServers: ["HLS"],
supportsDub: true,
};
}
@@ -21,10 +20,9 @@ class AnimeAV1 {
});
if (!res.ok) return [];
const data = await res.json();
return data.map(anime => ({
return data.map((anime) => ({
id: anime.slug,
title: anime.title,
url: `${this.api}/media/${anime.slug}`,
@@ -34,70 +32,37 @@ class AnimeAV1 {
}
async getMetadata(id) {
const html = await fetch(`${this.api}/media/${id}`).then(r => r.text());
const html = await fetch(`${this.api}/media/${id}`).then((r) => r.text());
const parsed = this.parseSvelteData(html);
const media = parsed.find(x => x?.data?.media)?.data.media ?? {};
const media = parsed.find((x) => x?.data?.media)?.data.media ?? {};
// IMAGE
const imageMatch = html.match(/<img[^>]*class="aspect-poster[^"]*"[^>]*src="([^"]+)"/i);
const image = imageMatch ? imageMatch[1] : null;
// BLOCK INFO (STATUS, SEASON, YEAR)
const infoBlockMatch = html.match(
/<div class="flex flex-wrap items-center gap-2 text-sm">([\s\S]*?)<\/div>/
const imageMatch = html.match(
/<img[^>]*class="aspect-poster[^"]*"[^>]*src="([^"]+)"/i
);
let status = media.status ?? "Unknown";
let season = media.seasons ?? null;
let year = media.startDate ? Number(media.startDate.slice(0, 4)) : null;
if (infoBlockMatch) {
const raw = infoBlockMatch[1];
// Extraer spans internos
const spans = [...raw.matchAll(/<span[^>]*>([^<]+)<\/span>/g)].map(m => m[1].trim());
// EJEMPLO:
// ["TV Anime", "•", "2025", "•", "Temporada Otoño", "•", "En emisión"]
const clean = spans.filter(x => x !== "•");
// YEAR
const yearMatch = clean.find(x => /^\d{4}$/.test(x));
if (yearMatch) year = Number(yearMatch);
// SEASON (el que contiene "Temporada")
const seasonMatch = clean.find(x => x.toLowerCase().includes("temporada"));
if (seasonMatch) season = seasonMatch;
// STATUS (normalmente "En emisión", "Finalizado", etc)
const statusMatch = clean.find(x =>
/emisión|finalizado|completado|pausa|cancelado/i.test(x)
);
if (statusMatch) status = statusMatch;
}
const image = imageMatch ? imageMatch[1] : null;
return {
title: media.title ?? "Unknown",
summary: media.synopsis ?? "No summary available",
episodes: media.episodesCount ?? 0,
characters: [],
season,
status,
season: media.seasons ?? null,
status: media.status ?? "Unknown",
studio: "Unknown",
score: media.score ?? 0,
year,
genres: media.genres?.map(g => g.name) ?? [],
image
year: media.startDate
? Number(media.startDate.slice(0, 4))
: null,
genres: media.genres?.map((g) => g.name) ?? [],
image,
};
}
async findEpisodes(id) {
const html = await fetch(`${this.api}/media/${id}`).then(r => r.text());
const html = await fetch(`${this.api}/media/${id}`).then((r) => r.text());
const parsed = this.parseSvelteData(html);
const media = parsed.find(x => x?.data?.media)?.data?.media;
const media = parsed.find((x) => x?.data?.media)?.data?.media;
if (!media?.episodes) throw new Error("No se encontró media.episodes");
return media.episodes.map((ep, i) => ({
@@ -108,71 +73,94 @@ class AnimeAV1 {
}));
}
async findEpisodeServer(episodeOrId, _server) {
const ep = typeof episodeOrId === "string"
? (() => { try { return JSON.parse(episodeOrId); } catch { return { id: episodeOrId }; } })()
: episodeOrId;
async findEpisodeServer(episodeOrId, _server, category = "sub") {
const ep =
typeof episodeOrId === "string"
? (() => {
try {
return JSON.parse(episodeOrId);
} catch {
return { id: episodeOrId };
}
})()
: episodeOrId;
const pageUrl = ep.url ?? (
typeof ep.id === "string" && ep.id.includes("$")
? `${this.api}/media/${ep.id.split("$")[0]}/${ep.number ?? ep.id.split("$")[1]}`
: undefined
);
let pageUrl = ep.url;
if (!pageUrl && typeof ep.id === "string") {
if (ep.id.includes("$")) {
const [slug, num] = ep.id.split("$");
pageUrl = `${this.api}/media/${slug}/${ep.number ?? num}`;
} else {
pageUrl = `${this.api}/media/${ep.id}/${ep.number}`;
}
}
if (!pageUrl) {
throw new Error(
`No se pudo determinar la URL del episodio (id=${ep.id}, number=${ep.number})`
);
}
if (!pageUrl) throw new Error("No se pudo determinar la URL del episodio.");
const html = await fetch(pageUrl, {
headers: { Cookie: "__ddg1_=;__ddg2_=;" },
}).then(r => r.text());
const html = await fetch(pageUrl).then((r) => r.text());
const parsedData = this.parseSvelteData(html);
const entry = parsedData.find(x => x?.data?.embeds) || parsedData[3];
const entry = parsedData.find((x) => x?.data?.embeds);
const embeds = entry?.data?.embeds;
if (!embeds) throw new Error("No se encontraron 'embeds' en los datos del episodio.");
if (!embeds) throw new Error("No embeds encontrados");
const selectedEmbeds =
_server === "HLS"
? embeds.SUB ?? []
: _server === "HLS-DUB"
? embeds.DUB ?? []
: (() => { throw new Error(`Servidor desconocido: ${_server}`); })();
const list =
category === "dub"
? embeds.DUB
: embeds.SUB;
if (!selectedEmbeds.length)
throw new Error(`No hay mirrors disponibles para ${_server === "HLS" ? "SUB" : "DUB"}.`);
if (!Array.isArray(list))
throw new Error(`No hay streams ${category.toUpperCase()}`);
const match = selectedEmbeds.find(m =>
(m.url || "").includes("zilla-networks.com/play/")
const hls = list.find(
(m) =>
m.server === "HLS" &&
m.url?.includes("zilla-networks.com/play/")
);
if (!match)
throw new Error(`No se encontró ningún embed de ZillaNetworks en ${_server}.`);
if (!hls)
throw new Error(`No se encontró stream HLS ${category.toUpperCase()}`);
return {
server: _server,
headers: { Referer: 'null' },
server: "HLS",
headers: { Referer: "null" },
videoSources: [
{
url: match.url.replace("/play/", "/m3u8/"),
url: hls.url.replace("/play/", "/m3u8/"),
type: "m3u8",
quality: "auto",
subtitles: [],
subOrDub: category,
},
],
};
}
parseSvelteData(html) {
const scriptMatch = html.match(/<script[^>]*>\s*({[^<]*__sveltekit_[\s\S]*?)<\/script>/i);
if (!scriptMatch) throw new Error("No se encontró bloque SvelteKit en el HTML.");
const scriptMatch = html.match(
/<script[^>]*>\s*({[^<]*__sveltekit_[\s\S]*?)<\/script>/i
);
if (!scriptMatch) throw new Error("SvelteKit block not found");
const dataMatch = scriptMatch[1].match(/data:\s*(\[[\s\S]*?\])\s*,\s*form:/);
if (!dataMatch) throw new Error("No se encontró el bloque 'data' en el script SvelteKit.");
const dataMatch = scriptMatch[1].match(
/data:\s*(\[[\s\S]*?\])\s*,\s*form:/
);
if (!dataMatch) throw new Error("SvelteKit data block not found");
const jsArray = dataMatch[1];
try {
return new Function(`"use strict"; return (${jsArray});`)();
} catch {
const cleaned = jsArray.replace(/\bvoid 0\b/g, "null").replace(/undefined/g, "null");
const cleaned = jsArray
.replace(/\bvoid 0\b/g, "null")
.replace(/undefined/g, "null");
return new Function(`"use strict"; return (${cleaned});`)();
}
}