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

165 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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",
};
// Взвешенное среднее из 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 { score: "N/A", 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 { score: "N/A", weightedScore: "N/A", characters: [] };
}
// Взвешенный рейтинг из 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.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 {
score: anime.score || "N/A",
weightedScore,
characters: mainCharacters,
};
} catch {
return { score: "N/A", 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 || "";
let noteExtra = "";
try {
const shikiData = await getShikimoriData(title, year);
const shikiScore = shikiData.score || "N/A";
const shikiWeighted = shikiData.weightedScore || "N/A";
const characters = shikiData.characters.length
? shikiData.characters.map((c) => `<a href="${c.url}">${c.name}</a>`).join(", ")
: "N/A";
const malScore = await getMALScore(title);
noteExtra =
`<b>Shikimori (официальный):</b> ${shikiScore}<br>` +
`<b>Shikimori (взвешенный):</b> ${shikiWeighted}<br>` +
`<b>MyAnimeList:</b> ${malScore}<br>` +
`<b>Главные персонажи:</b> ${characters}<br>`;
} catch {
noteExtra =
`<b>Shikimori (официальный):</b> N/A<br>` +
`<b>Shikimori (взвешенный):</b> N/A<br>` +
`<b>MyAnimeList:</b> N/A<br>` +
`<b>Главные персонажи:</b> N/A<br>`;
}
const originalNote = release.note?.trim();
let finalNote = noteExtra;
if (originalNote) {
finalNote += `<br><b>Примечание от Anixart:</b><br>${originalNote}`;
}
release.note = finalNote;
return { code: 0, release };
} catch {
return { code: 2, release: null };
}
}
// Серверный роутинг
serve(async (req) => {
const url = new URL(req.url);
const path = url.pathname;
const token = url.searchParams.get("token");
// /api/release/{releaseId}
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 });
});