updates and new extensions

This commit is contained in:
2026-01-13 17:26:06 +01:00
parent 83c51a82da
commit e8d64174fd
15 changed files with 3516 additions and 468 deletions

View File

@@ -1,7 +1,7 @@
class AnimeAV1 {
constructor() {
this.type = "anime-board";
this.version = "1.3";
this.version = "1.4";
this.api = "https://animeav1.com";
}
@@ -12,23 +12,178 @@ class AnimeAV1 {
};
}
async search(query) {
const res = await fetch(`${this.api}/api/search`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: query.query }),
getFilters() {
return {
letter: {
label: 'Letra',
type: 'select',
options: [
{ value: '', label: 'Seleccionar...' },
{ value: '#', label: '#' },
...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(l => ({
value: l,
label: l
}))
]
},
category: {
label: 'Tipo',
type: 'multiselect',
options: [
{ value: 'tv-anime', label: 'TV Anime' },
{ value: 'pelicula', label: 'Película' },
{ value: 'ova', label: 'OVA' },
{ value: 'ona', label: 'ONA' },
{ value: 'especial', label: 'Especial' }
]
},
genre: {
label: 'Género',
type: 'multiselect',
options: [
{ value: 'accion', label: 'Acción' },
{ value: 'aventura', label: 'Aventura' },
{ value: 'ciencia-ficcion', label: 'Ciencia Ficción' },
{ value: 'comedia', label: 'Comedia' },
{ value: 'deportes', label: 'Deportes' },
{ value: 'drama', label: 'Drama' },
{ value: 'fantasia', label: 'Fantasía' },
{ value: 'misterio', label: 'Misterio' },
{ value: 'recuentos-de-la-vida', label: 'Recuentos de la Vida' },
{ value: 'romance', label: 'Romance' },
{ value: 'seinen', label: 'Seinen' },
{ value: 'shoujo', label: 'Shoujo' },
{ value: 'shounen', label: 'Shounen' },
{ value: 'sobrenatural', label: 'Sobrenatural' },
{ value: 'suspenso', label: 'Suspenso' },
{ value: 'terror', label: 'Terror' },
{ value: 'artes-marciales', label: 'Artes Marciales' },
{ value: 'ecchi', label: 'Ecchi' },
{ value: 'escolares', label: 'Escolares' },
{ value: 'gore', label: 'Gore' },
{ value: 'harem', label: 'Harem' },
{ value: 'historico', label: 'Histórico' },
{ value: 'isekai', label: 'Isekai' },
{ value: 'josei', label: 'Josei' },
{ value: 'magia', label: 'Magia' },
{ value: 'mecha', label: 'Mecha' },
{ value: 'militar', label: 'Militar' },
{ value: 'mitologia', label: 'Mitología' },
{ value: 'musica', label: 'Música' },
{ value: 'parodia', label: 'Parodia' },
{ value: 'psicologico', label: 'Psicológico' },
{ value: 'superpoderes', label: 'Superpoderes' },
{ value: 'vampiros', label: 'Vampiros' },
{ value: 'yuri', label: 'Yuri' },
{ value: 'yaoi', label: 'Yaoi' }
]
},
year: {
label: 'Año (Máximo)',
type: 'number'
},
status: {
label: 'Estado',
type: 'select',
options: [
{ value: 'emision', label: 'En emisión' },
{ value: 'finalizado', label: 'Finalizado' },
{ value: 'proximamente', label: 'Próximamente' }
]
},
order: {
label: 'Ordenar por',
type: 'select',
options: [
{ value: 'default', label: 'Por defecto' },
{ value: 'updated', label: 'Recientes' },
{ value: 'likes', label: 'Populares' },
{ value: 'title', label: 'Alfabético' }
]
}
};
}
async search({ query, filters }) {
if (query && (!filters || Object.keys(filters).length === 0)) {
const res = await fetch(`${this.api}/api/search`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 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}`,
image: `https://cdn.animeav1.com/covers/${anime.id}.jpg`,
}));
}
const params = new URLSearchParams();
if (filters) {
if (filters.category) {
const cats = String(filters.category).split(',');
cats.forEach(c => {
if(c.trim()) params.append('category', c.trim());
});
}
if (filters.genre) {
const genres = String(filters.genre).split(',');
genres.forEach(g => {
if(g.trim()) params.append('genre', g.trim());
});
}
if (filters.year) params.set('maxYear', String(filters.year));
if (filters.status) params.set('status', filters.status);
if (filters.letter) params.set('letter', filters.letter);
if (filters.order && filters.order !== 'default') params.set('order', filters.order);
}
const url = `${this.api}/catalogo?${params.toString()}`;
const res = await fetch(url);
if (!res.ok) return [];
const html = await res.text();
const $ = this.cheerio.load(html);
const results = [];
$('article.group\\/item').each((_, el) => {
const card = $(el);
const title = card.find('h3').first().text().trim();
const href = card.find('a[href^="/media/"]').attr('href');
const img = card.find('img').first().attr('src');
if (!href) return;
const slug = href.replace('/media/', '');
results.push({
id: slug,
title,
url: `${this.api}${href}`,
image: img || '',
year: null
});
});
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`,
}));
return results;
}
async getMetadata(id) {
@@ -74,17 +229,9 @@ class AnimeAV1 {
}
async findEpisodeServer(episodeOrId, _server, category = "sub") {
const ep =
typeof episodeOrId === "string"
? (() => {
try {
return JSON.parse(episodeOrId);
} catch {
return { id: episodeOrId };
}
})()
: episodeOrId;
const ep = typeof episodeOrId === "string"
? JSON.parse(episodeOrId)
: episodeOrId;
let pageUrl = ep.url;
@@ -97,36 +244,22 @@ class AnimeAV1 {
}
}
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).then((r) => r.text());
const parsedData = this.parseSvelteData(html);
const entry = parsedData.find((x) => x?.data?.embeds);
const embeds = entry?.data?.embeds;
if (!embeds) throw new Error("No embeds encontrados");
const list =
category === "dub"
? embeds.DUB
: embeds.SUB;
const list = category === "dub" ? embeds.DUB : embeds.SUB;
if (!Array.isArray(list))
throw new Error(`No hay streams ${category.toUpperCase()}`);
if (!Array.isArray(list)) throw new Error(`No hay streams ${category.toUpperCase()}`);
const hls = list.find(
(m) =>
m.server === "HLS" &&
m.url?.includes("zilla-networks.com/play/")
);
const hls = list.find(m => m.server === "HLS" && m.url?.includes("zilla-networks.com/play/"));
if (!hls)
throw new Error(`No se encontró stream HLS ${category.toUpperCase()}`);
if (!hls) throw new Error(`No se encontró stream HLS ${category.toUpperCase()}`);
return {
server: "HLS",