diff --git a/anime/animeav1/source.js b/anime/animeav1/source.js index 1ee6f23..a6909e7 100644 --- a/anime/animeav1/source.js +++ b/anime/animeav1/source.js @@ -6,55 +6,67 @@ class AnimeAV1 { } getSettings() { - return { - episodeServers: ["HLS", "HLS-DUB"], - supportsDub: true, - }; + return { episodeServers: ["HLS", "HLS-DUB"], supportsDub: true }; } - async search(query) { + _nativeFetch(url, method, headers, body) { + const raw = Native.fetch(String(url), method || "GET", JSON.stringify(headers || {}), body == null ? "" : String(body)); + try { return JSON.parse(raw || "{}"); } catch (e) { return { ok: false, status: 0, headers: {}, body: "" }; } + } + + _getText(url, headers) { + const res = this._nativeFetch(url, "GET", headers, ""); + return String(res.body || ""); + } + + _getJson(url, headers) { + const res = this._nativeFetch(url, "GET", headers, ""); + try { return JSON.parse(String(res.body || "{}")); } catch (e) { return {}; } + } + + _postJson(url, headers, obj) { + const res = this._nativeFetch(url, "POST", headers, JSON.stringify(obj || {})); + try { return JSON.parse(String(res.body || "{}")); } catch (e) { return null; } + } + + search(query) { const q = - (query && typeof query === "object" && query.query) - ? String(query.query) - : (typeof query === "string" ? query : ""); + (query && typeof query === "object" && query.query) ? String(query.query) + : (typeof query === "string" ? query : ""); if (!q.trim()) return []; - const res = await fetch(`${this.api}/api/search`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ query: q }), - }); + const data = this._postJson( + `${this.api}/api/search`, + { "Content-Type": "application/json" }, + { query: q } + ); - if (!res.ok) return []; - - const data = await res.json(); if (!Array.isArray(data)) return []; - return data - .map(anime => { - if (!anime || !anime.slug) return null; - return { - id: anime.slug, - title: anime.title || "Unknown", - url: `${this.api}/media/${anime.slug}`, - subOrDub: "both", - }; - }) - .filter(Boolean); + return data.map((anime) => { + const slug = anime && anime.slug ? String(anime.slug) : ""; + if (!slug) return null; + return { + id: slug, + title: (anime && anime.title) ? String(anime.title) : "Unknown", + url: `${this.api}/media/${slug}`, + subOrDub: "both", + }; + }).filter(Boolean); } - async getMetadata(id) { + getMetadata(id) { const slug = String(id || "").trim(); if (!slug) throw new Error("Missing id"); - const html = await fetch(`${this.api}/media/${slug}`).then(r => r.text()); + const html = this._getText(`${this.api}/media/${slug}`, {}); const parsed = this.parseSvelteData(html); - const media = parsed.find(x => x?.data?.media)?.data?.media || {}; + const media = (parsed.find(x => x && x.data && x.data.media)?.data?.media) || {}; let image = null; const imageMatch = html.match(/]*class="aspect-poster[^"]*"[^>]*src="([^"]+)"/i); - if (imageMatch) image = imageMatch[1]; + if (imageMatch && imageMatch[1]) image = imageMatch[1]; let year = null; if (media.startDate) { @@ -72,38 +84,42 @@ class AnimeAV1 { studio: "Unknown", score: media.score || 0, year, - genres: (media.genres || []).map(g => g?.name).filter(Boolean), + genres: (media.genres || []).map(g => g && g.name ? g.name : "").filter(Boolean), image, }; } - async findEpisodes(id) { + findEpisodes(id) { const slug = String(id || "").trim(); if (!slug) throw new Error("Missing id"); - const html = await fetch(`${this.api}/media/${slug}`).then(r => r.text()); + const html = this._getText(`${this.api}/media/${slug}`, {}); const parsed = this.parseSvelteData(html); - const media = parsed.find(x => x?.data?.media)?.data?.media; + const media = (parsed.find(x => x && x.data && x.data.media)?.data?.media) || null; - if (!media || !Array.isArray(media.episodes)) - throw new Error("No episodes"); + if (!media || !Array.isArray(media.episodes)) throw new Error("No episodes"); return media.episodes.map((ep, i) => { - const n = ep?.number ?? (i + 1); + const nRaw = ep && ep.number != null ? ep.number : (i + 1); + const n = Number(nRaw); + const num = Number.isFinite(n) ? n : (i + 1); + return { - id: `${media.slug || slug}$${n}`, - number: n, - title: ep?.title || `Episode ${n}`, - url: `${this.api}/media/${media.slug || slug}/${n}`, + id: `${media.slug || slug}$${num}`, + number: num, + title: (ep && ep.title) ? String(ep.title) : `Episode ${num}`, + url: `${this.api}/media/${media.slug || slug}/${num}`, }; }); } - async findEpisodeServer(episodeOrId, server) { - const ep = - typeof episodeOrId === "string" - ? (() => { try { return JSON.parse(episodeOrId); } catch { return { id: episodeOrId }; } })() - : episodeOrId; + findEpisodeServer(episodeOrId, server) { + let ep = episodeOrId; + if (typeof ep === "string") { try { ep = JSON.parse(ep); } catch (e) {} } + ep = ep || {}; + + const srv = String(server || "").trim(); + if (srv !== "HLS" && srv !== "HLS-DUB") throw new Error("Unknown server"); const pageUrl = ep.url || @@ -113,38 +129,28 @@ class AnimeAV1 { if (!pageUrl) throw new Error("Missing episode url"); - const html = await fetch(pageUrl, { - headers: { Referer: `${this.api}/` }, - }).then(r => r.text()); - + const html = this._getText(pageUrl, { Referer: `${this.api}/` }); const parsed = this.parseSvelteData(html); - const entry = parsed.find(x => x?.data?.embeds) || parsed[3]; - const embeds = entry?.data?.embeds; + const entry = parsed.find(x => x && x.data && x.data.embeds) || parsed[3]; + const embeds = entry && entry.data ? entry.data.embeds : null; if (!embeds) throw new Error("No embeds"); - const list = - server === "HLS" ? embeds.SUB : - server === "HLS-DUB" ? embeds.DUB : - null; + const list = (srv === "HLS") ? (embeds.SUB || []) : (embeds.DUB || []); + if (!Array.isArray(list) || !list.length) throw new Error("No mirrors"); - if (!Array.isArray(list) || !list.length) - throw new Error("No mirrors"); - - const match = list.find(m => String(m?.url || "").includes("zilla-networks.com/play/")); - if (!match) throw new Error("No valid source"); + const match = list.find(m => String((m && m.url) || "").includes("zilla-networks.com/play/")); + if (!match || !match.url) throw new Error("No valid source"); return { - server, + server: srv, headers: { Referer: pageUrl }, - videoSources: [ - { - url: String(match.url).replace("/play/", "/m3u8/"), - type: "m3u8", - quality: "auto", - subtitles: [], - }, - ], + videoSources: [{ + url: String(match.url).replace("/play/", "/m3u8/"), + type: "m3u8", + quality: "auto", + subtitles: [] + }] }; } @@ -159,10 +165,8 @@ class AnimeAV1 { try { return new Function(`"use strict"; return (${jsArray});`)(); - } catch { - const cleaned = jsArray - .replace(/\bvoid 0\b/g, "null") - .replace(/undefined/g, "null"); + } catch (e) { + const cleaned = jsArray.replace(/\bvoid 0\b/g, "null").replace(/undefined/g, "null"); return new Function(`"use strict"; return (${cleaned});`)(); } }