Files
anixart-extension/api/v1/src/release.ts
2025-05-21 18:36:37 +05:00

158 lines
5.0 KiB
TypeScript

import { serve } from "https://deno.land/std@0.140.0/http/server.ts";
const baseHeaders = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Accept": "application/json",
};
// Функция для расчёта рейтинга из rates_scores_stats
function calculateWeightedScore(stats) {
if (!stats?.length) return null;
let total = 0;
let count = 0;
for (const item of stats) {
const score = parseInt(item.name);
const votes = item.value;
total += score * votes;
count += votes;
}
return count ? (total / count).toFixed(2) : null;
}
// Получаем данные Shikimori
async function getShikimoriData(search, year) {
if (!search) {
return { weightedScore: "N/A", characters: [] };
}
const animeUrl = `https://shikimori.one/api/animes?search=${encodeURIComponent(search)}&limit=1${year ? `&year=${year}` : ""}`;
try {
const animeRes = await fetch(animeUrl, { headers: baseHeaders });
const animeData = await animeRes.json();
const anime = animeData?.[0];
if (!anime || !anime.id) {
return { weightedScore: "N/A", characters: [] };
}
// Получаем рейтинг из rates_scores_stats
const statsUrl = `https://shikimori.one/api/animes/${anime.id}`;
const statsRes = await fetch(statsUrl, { headers: baseHeaders });
const statsData = await statsRes.json();
const weightedScore = calculateWeightedScore(statsData.rates_scores_stats) || "N/A";
// Получаем главных персонажей
const rolesUrl = `https://shikimori.one/api/animes/${anime.id}/roles`;
const rolesRes = await fetch(rolesUrl, { headers: baseHeaders });
const roles = await rolesRes.json();
const mainCharacters = roles
.filter((c) => c.roles.includes("Main"))
.slice(0, 5)
.map((c) => ({
name: c.character.russian,
url: `https://shikimori.one${c.character.url}`,
}));
return {
weightedScore,
characters: mainCharacters,
};
} catch {
return { weightedScore: "N/A", characters: [] };
}
}
// Получаем рейтинг MyAnimeList через Jikan
async function getMALScore(title) {
const url = `https://api.jikan.moe/v4/anime?q=${encodeURIComponent(title)}&limit=1`;
try {
const response = await fetch(url);
const data = await response.json();
const anime = data?.data?.[0];
if (!anime || !anime.score) {
return "N/A";
}
return `${anime.score}`;
} catch {
return "N/A";
}
}
// Основная функция получения релиза и формирования примечания
async function getReleaseFromAnixart(releaseId, token = "") {
const url = `https://api.anixart.tv/release/${releaseId}${token ? `?token=${token}` : ""}`;
try {
const response = await fetch(url, { headers: baseHeaders });
const data = await response.json();
const release = data?.release;
if (!release) {
return { code: 2, release: null };
}
const title = release.title_original || release.title_ru || "";
const year = release.year || "";
try {
const shikiData = await getShikimoriData(title, year);
const shikiScore = shikiData.weightedScore !== "N/A" ? `<b>Рейтинг Shikimori:</b> ${shikiData.weightedScore}` : "";
const malScoreRaw = await getMALScore(title);
const malScore = malScoreRaw !== "N/A" ? `<b>Рейтинг MyAnimeList:</b> ${malScoreRaw}` : "";
const characters = shikiData.characters.length
? `<b>Главные персонажи:</b> ` + shikiData.characters.map(c => `<a href="${c.url}">${c.name}</a>`).join(", ")
: "";
// Собираем рейтинг и персонажей, фильтруя пустые строки
const ratingParts = [shikiScore, malScore].filter(Boolean).join("<br>");
const charsPart = characters ? characters : "";
// Примечание от Anixart (без лишних br если пустое)
const originalNote = release.note?.trim();
let finalNote = "";
if (ratingParts) finalNote += ratingParts;
if (charsPart) finalNote += (finalNote ? "<br>" : "") + charsPart;
if (originalNote) finalNote += (finalNote ? "<br><br>" : "") + `<b>Примечание от Anixart:</b><br>${originalNote}`;
release.note = finalNote || null;
} catch {
release.note = null;
}
return { code: 0, release };
} catch {
return { code: 2, release: null };
}
}
// HTTP сервер
serve(async (req) => {
const url = new URL(req.url);
const path = url.pathname;
const token = url.searchParams.get("token");
const releaseMatch = path.match(/^\/api\/release\/([^\/]+)/);
if (releaseMatch) {
const releaseId = releaseMatch[1];
const result = await getReleaseFromAnixart(releaseId, token);
return new Response(JSON.stringify(result), {
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
return new Response("Invalid endpoint", { status: 404 });
});