source/aniliberty/aniliberty.js
2025-10-12 22:08:11 +00:00

155 lines
No EOL
4.6 KiB
JavaScript

// AniLiberty module for Sora (AsyncJS)
// Author: emp0ry
// Version: 1.0.1
const IMAGE_HOST = "https://anilibria.top";
function fullImg(path) {
if (!path) return;
return path.startsWith("http") ? path : `${IMAGE_HOST}${path}`;
}
function pickBestHls(ep) {
return ep?.hls_1080 || ep?.hls_720 || ep?.hls_480 || null;
}
// ------------------------------------------------------------
// Detect working API domain
// ------------------------------------------------------------
async function checkApiStatus() {
const domains = [
"https://anilibria.top/api/v1/",
"https://aniliberty.top/api/v1/",
"https://anilibria.wtf/api/v1/"
];
for (const base of domains) {
try {
const res = await fetchv2(base + "app/status");
const data = await res.json();
if (data?.is_alive || data?.result === "ok") return base;
} catch (_) {}
}
return "https://anilibria.top/api/v1/";
}
// ------------------------------------------------------------
// Search -> JSON string
// ------------------------------------------------------------
async function searchResults(keyword) {
try {
const base = await checkApiStatus();
const url = `${base}app/search/releases?query=${encodeURIComponent(keyword)}&include=id,name.main,poster.src`;
const res = await fetchv2(url);
const data = await res.json();
const out = (Array.isArray(data) ? data : []).map(it => ({
title: it?.name?.main || "Unknown title",
image: fullImg(it?.poster?.src),
href:
`${base}anime/releases/${it.id}?` +
[
"include=name.main,poster.src,description,average_duration_of_episode",
"episodes.ordinal,episodes.name,episodes.duration",
"episodes.preview.src",
"episodes.opening.start,episodes.opening.stop",
"episodes.ending.start,episodes.ending.stop",
"episodes.hls_1080,episodes.hls_720,episodes.hls_480"
].join(",")
}));
if (!out.length) {
return JSON.stringify([]);;
}
return JSON.stringify(out);
} catch (e) {
return JSON.stringify([]);;
}
}
// ------------------------------------------------------------
// Details -> JSON string (Duration: Xm in aliases)
// ------------------------------------------------------------
async function extractDetails(url) {
try {
const res = await fetchv2(url);
const data = await res.json();
const description = data?.description || "No description available.";
const mins = Number.isFinite(data?.average_duration_of_episode)
? `${data.average_duration_of_episode}m`
: "Unknown";
const out = [{
description,
aliases: `Duration: ${mins}`,
airdate: "Unknown"
}];
return JSON.stringify(out);
} catch (e) {
return JSON.stringify([]);;
}
}
// ------------------------------------------------------------
// Episodes -> JSON string (with opening/ending skips)
// ------------------------------------------------------------
async function extractEpisodes(url) {
try {
const res = await fetchv2(url);
const data = await res.json();
const seriesPoster = fullImg(data?.poster?.src);
const eps = Array.isArray(data?.episodes) ? data.episodes : [];
const out = eps.map((ep, idx) => {
const href = pickBestHls(ep);
if (!href) return null;
const num = Number.isFinite(ep?.ordinal)
? ep.ordinal
: (Number.isFinite(ep?.sort_order) ? ep.sort_order : (idx + 1));
const title = ep?.name ? String(ep.name) : `Episode ${num}`;
const image = fullImg(ep?.preview?.src) || seriesPoster;
// Build skip blocks only if numbers present
const opening = (ep?.opening && Number.isFinite(ep.opening.start) && Number.isFinite(ep.opening.stop))
? { start: ep.opening.start, stop: ep.opening.stop }
: undefined;
const ending = (ep?.ending && Number.isFinite(ep.ending.start) && Number.isFinite(ep.ending.stop))
? { start: ep.ending.start, stop: ep.ending.stop }
: undefined;
const entry = {
href,
number: num,
title,
image
};
// Attach only when available
if (opening) entry.opening = opening;
if (ending) entry.ending = ending;
if (Number.isFinite(ep?.duration)) entry.duration = ep.duration; // seconds (optional)
return entry;
}).filter(Boolean);
return JSON.stringify(out);
} catch (e) {
return JSON.stringify([]);
}
}
// ------------------------------------------------------------
// Stream -> RAW URL string
// ------------------------------------------------------------
async function extractStreamUrl(url) {
try {
return url; // direct HLS
} catch (e) {
return null;
}
}