Update aniliberty/aniliberty.js

This commit is contained in:
aka paul 2025-10-12 19:37:50 +00:00
parent b390ba1af3
commit 240e533609

View file

@ -1,14 +1,18 @@
// AniLiberty module for Sora // AniLiberty module for Sora (AsyncJS)
// Author: emp0ry // Author: emp0ry
// Version: 1.0.0 // Version: 1.0.3
// Description: // Changes: add skip markers (opening/ending) and average_duration_of_episode -> "Duration: Xm".
// Streams anime directly from AniLiberty / AniLibria API v1
// Endpoints used: const IMAGE_HOST = "https://anilibria.top";
// • /app/status
// • /app/search/releases?query= function fullImg(path) {
// • /anime/releases/{id} if (!path) return `${IMAGE_HOST}/favicon.ico`;
// return path.startsWith("http") ? path : `${IMAGE_HOST}${path}`;
// Supports 1080p / 720p / 480p HLS playback }
function pickBestHls(ep) {
return ep?.hls_1080 || ep?.hls_720 || ep?.hls_480 || null;
}
// ------------------------------------------------------------ // ------------------------------------------------------------
// Detect working API domain // Detect working API domain
@ -19,7 +23,6 @@ async function checkApiStatus() {
"https://aniliberty.top/api/v1/", "https://aniliberty.top/api/v1/",
"https://anilibria.wtf/api/v1/" "https://anilibria.wtf/api/v1/"
]; ];
for (const base of domains) { for (const base of domains) {
try { try {
const res = await fetchv2(base + "app/status"); const res = await fetchv2(base + "app/status");
@ -31,102 +34,136 @@ async function checkApiStatus() {
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Search anime releases // Search -> JSON string
// ------------------------------------------------------------ // ------------------------------------------------------------
async function searchResults(keyword) { async function searchResults(keyword) {
const base = await checkApiStatus();
const url = `${base}app/search/releases?query=${encodeURIComponent(keyword)}&include=id,name,poster,year,description`;
try { try {
const response = await fetchv2(url); const base = await checkApiStatus();
const data = await response.json(); const url = `${base}app/search/releases?query=${encodeURIComponent(keyword)}&include=id,name.main,poster.src`;
if (!Array.isArray(data) || !data.length)
return [{
title: `No results from AniLiberty (${base})`,
image: "https://anilibria.top/favicon.ico",
href: "https://anilibria.top"
}];
return data.map(item => { const res = await fetchv2(url);
const title = item.name?.main || item.name?.english || "Unknown title"; const data = await res.json();
const year = item.year ? ` (${item.year})` : "";
const img = item.poster?.optimized?.preview const out = (Array.isArray(data) ? data : []).map(it => ({
? `https://anilibria.top${item.poster.optimized.preview}` title: it?.name?.main || "Unknown title",
: `https://anilibria.top${item.poster?.preview || "/favicon.ico"}`; image: fullImg(it?.poster?.src),
return { // Pass a details endpoint as href (will be reused by details/episodes)
title: title + year, href:
image: img, `${base}anime/releases/${it.id}?` +
href: `${base}anime/releases/${item.id}` [
}; "include=name.main,poster.src,description,average_duration_of_episode",
}); "episodes.ordinal,episodes.name,episodes.duration",
} catch (err) { "episodes.preview.src",
console.log("AniLiberty search error:", err); "episodes.opening.start,episodes.opening.stop",
return [{ "episodes.ending.start,episodes.ending.stop",
title: "AniLiberty: Error during search", "episodes.hls_1080,episodes.hls_720,episodes.hls_480"
image: "https://anilibria.top/favicon.ico", ].join(",")
href: "https://anilibria.top" }));
}];
if (!out.length) {
out.push({
title: "No results from AniLiberty",
image: `${IMAGE_HOST}/favicon.ico`,
href: IMAGE_HOST
});
}
return JSON.stringify(out);
} catch (e) {
console.log("AniLiberty search error:", e);
return JSON.stringify([{ title: "Error", image: `${IMAGE_HOST}/favicon.ico`, href: IMAGE_HOST }]);
} }
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Extract anime details // Details -> JSON string (Duration: Xm in aliases)
// ------------------------------------------------------------ // ------------------------------------------------------------
async function extractDetails(url) { async function extractDetails(url) {
try { try {
const response = await fetchv2(url); const res = await fetchv2(url);
const data = await response.json(); const data = await res.json();
return [{
description: data.description || "No description available.", const description = data?.description || "No description available.";
aliases: data.name?.english || "N/A", const mins = Number.isFinite(data?.average_duration_of_episode)
airdate: data.year ? String(data.year) : "Unknown" ? `${data.average_duration_of_episode}m`
: "Unknown";
const out = [{
description,
aliases: `Duration: ${mins}`,
airdate: "Unknown"
}]; }];
} catch (err) { return JSON.stringify(out);
console.log("AniLiberty details error:", err); } catch (e) {
return [{ console.log("AniLiberty details error:", e);
return JSON.stringify([{
description: "Error loading details", description: "Error loading details",
aliases: "N/A", aliases: "Duration: Unknown",
airdate: "N/A" airdate: "Unknown"
}]; }]);
} }
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Extract episode list (with HLS sources) // Episodes -> JSON string (with opening/ending skips)
// ------------------------------------------------------------ // ------------------------------------------------------------
async function extractEpisodes(url) { async function extractEpisodes(url) {
try { try {
const response = await fetchv2(url); const res = await fetchv2(url);
const data = await response.json(); const data = await res.json();
const eps = data.episodes || [];
if (!eps.length) return [];
return eps.map(ep => ({ const seriesPoster = fullImg(data?.poster?.src);
number: ep.ordinal || ep.sort_order || 0, const eps = Array.isArray(data?.episodes) ? data.episodes : [];
image: ep.preview?.optimized?.preview
? `https://anilibria.top${ep.preview.optimized.preview}` const out = eps.map((ep, idx) => {
: "https://anilibria.top/favicon.ico", const href = pickBestHls(ep);
href: if (!href) return null;
ep.hls_1080 ||
ep.hls_720 || const num = Number.isFinite(ep?.ordinal)
ep.hls_480 || ? ep.ordinal
null : (Number.isFinite(ep?.sort_order) ? ep.sort_order : (idx + 1));
})).filter(e => e.href);
} catch (err) { const title = ep?.name ? String(ep.name) : `Episode ${num}`;
console.log("AniLiberty episodes error:", err); const image = fullImg(ep?.preview?.src) || seriesPoster;
return [];
// 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) {
console.log("AniLiberty episodes error:", e);
return JSON.stringify([]);
} }
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Extract direct stream URL (HLS) // Stream -> RAW URL string
// ------------------------------------------------------------ // ------------------------------------------------------------
async function extractStreamUrl(url) { async function extractStreamUrl(url) {
try { try {
// Direct HLS links are already provided; just return the same URL return url; // direct HLS
return url; } catch (e) {
} catch (err) { console.log("AniLiberty stream error:", e);
console.log("AniLiberty stream error:", err);
return null; return null;
} }
} }