// AnimeVost for Sora (AsyncJS) // Author: emp0ry // Version: 1.0.0 const API_BASE = "https://api.animevost.org/v1/"; const FORM_CT = "application/x-www-form-urlencoded; charset=UTF-8"; // --- utils --- function encodeForm(fields) { return Object.keys(fields) .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(fields[k])}`) .join("&"); } async function postForm(url, fields) { const bodyStr = encodeForm(fields); try { const resA = await fetchv2(url, { "Content-Type": FORM_CT }, "POST", bodyStr); if (resA && typeof resA.text === "function") return resA; } catch (_) {} return await fetchv2(url, { method: "POST", headers: { "Content-Type": FORM_CT }, body: bodyStr }); } async function parseJsonSafe(res) { const txt = await res.text(); try { return JSON.parse(txt); } catch { return JSON.parse(txt.replace(/^\uFEFF/, "").trim()); } } function cleanTitle(raw) { if (!raw || typeof raw !== "string") return "Unknown title"; let t = raw.split(" /")[0]; return t.replace(/\s*\[.*?\]\s*$/g, "").trim() || "Unknown title"; } function htmlToText(html) { if (!html || typeof html !== "string") return ""; return html .replace(//gi, "\n") .replace(/<\/p>/gi, "\n") .replace(/<[^>]+>/g, "") .replace(/\n{3,}/g, "\n\n") .trim(); } // Pack payload into href to avoid relying on global state. function makeHrefFromPayload(obj) { return `animevost://payload/${encodeURIComponent(JSON.stringify(obj))}`; } function readPayloadFromHref(href) { const m = String(href).match(/^animevost:\/\/payload\/(.+)$/); if (!m) return null; try { return JSON.parse(decodeURIComponent(m[1])); } catch { return null; } } function parseIdFromAny(hrefOrId) { const p = readPayloadFromHref(hrefOrId); if (p && p.id) return parseInt(p.id, 10); const m1 = String(hrefOrId).match(/^animevost:\/\/release\/(\d+)$/); if (m1) return parseInt(m1[1], 10); const m2 = String(hrefOrId).match(/[?&]id=(\d+)/); if (m2) return parseInt(m2[1], 10); if (/^\d+$/.test(String(hrefOrId))) return parseInt(hrefOrId, 10); return null; } // --- search (POST /search) --- async function searchResults(keyword) { try { let res = await postForm(API_BASE + "search", { name: String(keyword) }); let json = await parseJsonSafe(res); if (json?.error || !Array.isArray(json?.data) || json.data.length === 0) { res = await postForm(API_BASE + "search", { name: `"${String(keyword)}"` }); json = await parseJsonSafe(res); } if (json?.error) { return JSON.stringify([]); } const list = Array.isArray(json?.data) ? json.data : []; if (!list.length) { return JSON.stringify([]); } const tiles = list.map(item => { const payload = { id: item.id, title: cleanTitle(item.title), description: htmlToText(item.description || ""), year: item.year || "", type: item.type || "", image: item.urlImagePreview || "" }; return { title: payload.title, image: payload.image, href: makeHrefFromPayload(payload) }; }); return JSON.stringify(tiles); } catch (e) { return JSON.stringify([]); } } // --- details (from payload) --- async function extractDetails(href) { try { const p = readPayloadFromHref(href); const out = [{ description: p?.description || "No description available.", aliases: `Type: ${p?.type || "Unknown"}`, airdate: p?.year ? String(p.year) : "Unknown" }]; return JSON.stringify(out); } catch (e) { return JSON.stringify([]); } } // --- episodes (POST /playlist) --- async function extractEpisodes(href) { try { const p = readPayloadFromHref(href); const id = p?.id ?? parseIdFromAny(href); if (!id) { return JSON.stringify([]); } const res = await postForm(API_BASE + "playlist", { id: String(id) }); const arr = await parseJsonSafe(res); if (!Array.isArray(arr)) return JSON.stringify([]); const out = arr.map((ep, idx) => { const name = ep?.name || ""; const m = name.match(/(\d+)/); const num = m ? parseInt(m[1], 10) : (idx + 1); const link = ep?.hd || ep?.std || null; if (!link) return null; return { number: num, title: name || `Episode ${num}`, image: ep?.preview || "", href: link }; }).filter(Boolean); return JSON.stringify(out); } catch (e) { return JSON.stringify([]); } } // --- stream --- async function extractStreamUrl(url) { try { return url; } catch { return null; } }