class AnimeAV1 {
constructor() {
this.type = "anime-board";
this.version = "1.2"
this.api = "https://animeav1.com";
}
getSettings() {
return {
episodeServers: ["HLS", "HLS-DUB"],
supportsDub: true,
};
}
async search(query) {
const res = await fetch(`${this.api}/api/search`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: query.query }),
});
if (!res.ok) return [];
const data = await res.json();
return data.map(anime => ({
id: anime.slug,
title: anime.title,
url: `${this.api}/media/${anime.slug}`,
subOrDub: "both",
image: `https://cdn.animeav1.com/covers/${anime.id}.jpg`,
}));
}
async getMetadata(id) {
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 ?? {};
// IMAGE
const imageMatch = html.match(/
]*class="aspect-poster[^"]*"[^>]*src="([^"]+)"/i);
const image = imageMatch ? imageMatch[1] : null;
// BLOCK INFO (STATUS, SEASON, YEAR)
const infoBlockMatch = html.match(
/
([\s\S]*?)<\/div>/
);
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>/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;
}
return {
title: media.title ?? "Unknown",
summary: media.synopsis ?? "No summary available",
episodes: media.episodesCount ?? 0,
characters: [],
season,
status,
studio: "Unknown",
score: media.score ?? 0,
year,
genres: media.genres?.map(g => g.name) ?? [],
image
};
}
async findEpisodes(id) {
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;
if (!media?.episodes) throw new Error("No se encontró media.episodes");
return media.episodes.map((ep, i) => ({
id: `${media.slug}$${ep.number ?? i + 1}`,
number: ep.number ?? i + 1,
title: ep.title ?? `Episode ${ep.number ?? i + 1}`,
url: `${this.api}/media/${media.slug}/${ep.number ?? i + 1}`,
}));
}
async findEpisodeServer(episodeOrId, _server) {
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
);
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 parsedData = this.parseSvelteData(html);
const entry = parsedData.find(x => x?.data?.embeds) || parsedData[3];
const embeds = entry?.data?.embeds;
if (!embeds) throw new Error("No se encontraron 'embeds' en los datos del episodio.");
const selectedEmbeds =
_server === "HLS"
? embeds.SUB ?? []
: _server === "HLS-DUB"
? embeds.DUB ?? []
: (() => { throw new Error(`Servidor desconocido: ${_server}`); })();
if (!selectedEmbeds.length)
throw new Error(`No hay mirrors disponibles para ${_server === "HLS" ? "SUB" : "DUB"}.`);
const match = selectedEmbeds.find(m =>
(m.url || "").includes("zilla-networks.com/play/")
);
if (!match)
throw new Error(`No se encontró ningún embed de ZillaNetworks en ${_server}.`);
return {
server: _server,
headers: { Referer: 'null' },
videoSources: [
{
url: match.url.replace("/play/", "/m3u8/"),
type: "m3u8",
quality: "auto",
subtitles: [],
},
],
};
}
parseSvelteData(html) {
const scriptMatch = html.match(/