diff --git a/anidub/anidub.js b/.archive/anidub/anidub.js
similarity index 100%
rename from anidub/anidub.js
rename to .archive/anidub/anidub.js
diff --git a/anidub/anidub.json b/.archive/anidub/anidub.json
similarity index 100%
rename from anidub/anidub.json
rename to .archive/anidub/anidub.json
diff --git a/aniliberty/aniliberty.js b/.archive/aniliberty/aniliberty.js
similarity index 100%
rename from aniliberty/aniliberty.js
rename to .archive/aniliberty/aniliberty.js
diff --git a/aniliberty/aniliberty.json b/.archive/aniliberty/aniliberty.json
similarity index 100%
rename from aniliberty/aniliberty.json
rename to .archive/aniliberty/aniliberty.json
diff --git a/animevost/animevost.js b/.archive/animevost/animevost.js
similarity index 95%
rename from animevost/animevost.js
rename to .archive/animevost/animevost.js
index 1531357..bb7600d 100644
--- a/animevost/animevost.js
+++ b/.archive/animevost/animevost.js
@@ -1,309 +1,309 @@
-// AnimeVost for Sora (AsyncJS)
-// Author: emp0ry
-// Version: 1.0.2
-
-const API_BASE = "https://api.animevost.org/v1/";
-const FORM_CT = "application/x-www-form-urlencoded; charset=UTF-8";
-const SITE_BASE = "https://animevost.org";
-const DEFAULT_SUBTITLE = "https://none.com";
-
-// --- 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();
-}
-
-function cleanUrl(url) {
- const s = String(url || "").trim();
- return s || null;
-}
-
-function _safeJsonParse(value, fallback) {
- try {
- return JSON.parse(value);
- } catch (_) {
- return fallback;
- }
-}
-
-// 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;
-}
-
-// Same packing style as AniLiberty / SameBand.
-function _packEpisode(payload) {
- return "animevost:" + encodeURIComponent(JSON.stringify(payload || {}));
-}
-
-function _unpackEpisode(href) {
- const raw = String(href || "");
- if (!raw.startsWith("animevost:")) return null;
-
- return _safeJsonParse(decodeURIComponent(raw.slice("animevost:".length)), null);
-}
-
-function _streamHeaders() {
- return {
- "User-Agent": "Mozilla/5.0",
- "Referer": SITE_BASE + "/"
- };
-}
-
-// --- 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 (_) {
- 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 (_) {
- 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);
-
- // AnimeVost usually provides only hd/std.
- // Match Sora picker format used in AniLiberty/SameBand:
- // hd -> 720p
- // std -> 480p
- const url1080 = null;
- const url720 = cleanUrl(ep?.hd);
- const url480 = cleanUrl(ep?.std);
-
- const fallback = url720 || url480;
- if (!fallback) return null;
-
- return {
- href: _packEpisode({
- url1080,
- url720,
- url480,
- fallback
- }),
- number: num,
- title: name || `Episode ${num}`,
- image: ep?.preview || ""
- };
- }).filter(Boolean);
-
- return JSON.stringify(out);
- } catch (_) {
- return JSON.stringify([]);
- }
-}
-
-// --- stream: returns quality picker JSON ---
-async function extractStreamUrl(href) {
- try {
- const payload = _unpackEpisode(href);
-
- // Backward compatibility for old AnimeVost episode href format.
- if (!payload) {
- return href;
- }
-
- const url1080 = cleanUrl(payload.url1080);
- const url720 = cleanUrl(payload.url720);
- const url480 = cleanUrl(payload.url480);
- const fallback = cleanUrl(payload.fallback);
-
- const headers = _streamHeaders();
- const streams = [];
-
- if (url1080) {
- streams.push({
- title: "1080p",
- streamUrl: url1080,
- url1080,
- url720,
- url480,
- headers
- });
- }
-
- if (url720) {
- streams.push({
- title: "720p",
- streamUrl: url720,
- url1080,
- url720,
- url480,
- headers
- });
- }
-
- if (url480) {
- streams.push({
- title: "480p",
- streamUrl: url480,
- url1080,
- url720,
- url480,
- headers
- });
- }
-
- if (!streams.length && fallback) {
- streams.push({
- title: "720p",
- streamUrl: fallback,
- url1080: null,
- url720: fallback,
- url480: null,
- headers
- });
- }
-
- return JSON.stringify({
- streams,
- subtitle: DEFAULT_SUBTITLE
- });
- } catch (_) {
- return JSON.stringify({
- streams: [],
- subtitle: DEFAULT_SUBTITLE
- });
- }
+// AnimeVost for Sora (AsyncJS)
+// Author: emp0ry
+// Version: 1.0.2
+
+const API_BASE = "https://api.animevost.org/v1/";
+const FORM_CT = "application/x-www-form-urlencoded; charset=UTF-8";
+const SITE_BASE = "https://animevost.org";
+const DEFAULT_SUBTITLE = "https://none.com";
+
+// --- 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();
+}
+
+function cleanUrl(url) {
+ const s = String(url || "").trim();
+ return s || null;
+}
+
+function _safeJsonParse(value, fallback) {
+ try {
+ return JSON.parse(value);
+ } catch (_) {
+ return fallback;
+ }
+}
+
+// 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;
+}
+
+// Same packing style as AniLiberty / SameBand.
+function _packEpisode(payload) {
+ return "animevost:" + encodeURIComponent(JSON.stringify(payload || {}));
+}
+
+function _unpackEpisode(href) {
+ const raw = String(href || "");
+ if (!raw.startsWith("animevost:")) return null;
+
+ return _safeJsonParse(decodeURIComponent(raw.slice("animevost:".length)), null);
+}
+
+function _streamHeaders() {
+ return {
+ "User-Agent": "Mozilla/5.0",
+ "Referer": SITE_BASE + "/"
+ };
+}
+
+// --- 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 (_) {
+ 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 (_) {
+ 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);
+
+ // AnimeVost usually provides only hd/std.
+ // Match Sora picker format used in AniLiberty/SameBand:
+ // hd -> 720p
+ // std -> 480p
+ const url1080 = null;
+ const url720 = cleanUrl(ep?.hd);
+ const url480 = cleanUrl(ep?.std);
+
+ const fallback = url720 || url480;
+ if (!fallback) return null;
+
+ return {
+ href: _packEpisode({
+ url1080,
+ url720,
+ url480,
+ fallback
+ }),
+ number: num,
+ title: name || `Episode ${num}`,
+ image: ep?.preview || ""
+ };
+ }).filter(Boolean);
+
+ return JSON.stringify(out);
+ } catch (_) {
+ return JSON.stringify([]);
+ }
+}
+
+// --- stream: returns quality picker JSON ---
+async function extractStreamUrl(href) {
+ try {
+ const payload = _unpackEpisode(href);
+
+ // Backward compatibility for old AnimeVost episode href format.
+ if (!payload) {
+ return href;
+ }
+
+ const url1080 = cleanUrl(payload.url1080);
+ const url720 = cleanUrl(payload.url720);
+ const url480 = cleanUrl(payload.url480);
+ const fallback = cleanUrl(payload.fallback);
+
+ const headers = _streamHeaders();
+ const streams = [];
+
+ if (url1080) {
+ streams.push({
+ title: "1080p",
+ streamUrl: url1080,
+ url1080,
+ url720,
+ url480,
+ headers
+ });
+ }
+
+ if (url720) {
+ streams.push({
+ title: "720p",
+ streamUrl: url720,
+ url1080,
+ url720,
+ url480,
+ headers
+ });
+ }
+
+ if (url480) {
+ streams.push({
+ title: "480p",
+ streamUrl: url480,
+ url1080,
+ url720,
+ url480,
+ headers
+ });
+ }
+
+ if (!streams.length && fallback) {
+ streams.push({
+ title: "720p",
+ streamUrl: fallback,
+ url1080: null,
+ url720: fallback,
+ url480: null,
+ headers
+ });
+ }
+
+ return JSON.stringify({
+ streams,
+ subtitle: DEFAULT_SUBTITLE
+ });
+ } catch (_) {
+ return JSON.stringify({
+ streams: [],
+ subtitle: DEFAULT_SUBTITLE
+ });
+ }
}
\ No newline at end of file
diff --git a/animevost/animevost.json b/.archive/animevost/animevost.json
similarity index 100%
rename from animevost/animevost.json
rename to .archive/animevost/animevost.json
diff --git a/onwave/onwave.js b/.archive/onwave/onwave.js
similarity index 100%
rename from onwave/onwave.js
rename to .archive/onwave/onwave.js
diff --git a/onwave/onwave.json b/.archive/onwave/onwave.json
similarity index 100%
rename from onwave/onwave.json
rename to .archive/onwave/onwave.json
diff --git a/sameband/sameband.js b/.archive/sameband/sameband.js
similarity index 100%
rename from sameband/sameband.js
rename to .archive/sameband/sameband.js
diff --git a/sameband/sameband.json b/.archive/sameband/sameband.json
similarity index 100%
rename from sameband/sameband.json
rename to .archive/sameband/sameband.json
diff --git a/shizaproject/shizaproject.js b/.archive/shizaproject/shizaproject.js
similarity index 100%
rename from shizaproject/shizaproject.js
rename to .archive/shizaproject/shizaproject.js
diff --git a/shizaproject/shizaproject.json b/.archive/shizaproject/shizaproject.json
similarity index 100%
rename from shizaproject/shizaproject.json
rename to .archive/shizaproject/shizaproject.json
diff --git a/yummyanime/yummyanime.js b/.archive/yummyanime/yummyanime.js
similarity index 100%
rename from yummyanime/yummyanime.js
rename to .archive/yummyanime/yummyanime.js
diff --git a/yummyanime/yummyanime.json b/.archive/yummyanime/yummyanime.json
similarity index 100%
rename from yummyanime/yummyanime.json
rename to .archive/yummyanime/yummyanime.json