diff --git a/player-parsers/kodik.ts b/player-parsers/kodik.ts index 2cca395..a41b0b6 100644 --- a/player-parsers/kodik.ts +++ b/player-parsers/kodik.ts @@ -6,7 +6,7 @@ export async function getKodikURL(req, res, url: string) { let domain = url.replace("https://", "").split("/")[0]; if (!altDomains.includes(domain)) { - asJSON(req, res, { message: "Wrong url provided for player kodik" }, 400); + asJSON(req, res, { message: "KODIK: Неправильная ссылка на плеер" }, 400); return; } @@ -45,7 +45,7 @@ export async function getKodikURL(req, res, url: string) { } if (!pageRes.ok) { - asJSON(req, res, { message: "KODIK: failed to load page" }, 500); + asJSON(req, res, { message: "KODIK: Не удалось загрузить страницу с плеером" }, 500); return; } @@ -54,7 +54,7 @@ export async function getKodikURL(req, res, url: string) { const urlParamsMatch = urlParamsRe.exec(pageData); if (!urlParamsMatch || urlParamsMatch.length == 0) { - asJSON(req, res, { message: `KODIK: failed to find data to parse` }, 500); + asJSON(req, res, { message: `KODIK: Не удалось найти данные эпизода` }, 500); return; } @@ -86,7 +86,7 @@ export async function getKodikURL(req, res, url: string) { }); if (!linksRes.ok) { - asJSON(req, res, { message: `KODIK: failed to get links` }, 500); + asJSON(req, res, { message: `KODIK: Не удалось получить прямую ссылку` }, 500); return; } diff --git a/player-parsers/libria.ts b/player-parsers/libria.ts index 845ba5f..8382198 100644 --- a/player-parsers/libria.ts +++ b/player-parsers/libria.ts @@ -1,72 +1,135 @@ import { asJSON } from "./shared"; -const API_URL = "https://api.anilibria.tv/v3/title" + +export interface APIStatusResponse { + request: Request; + is_alive: boolean; + available_api_endpoints: string[]; +} + +export interface Request { + ip: string; + country: string; + iso_code: string; + timezone: string; +} + +async function checkApiStatus(req, res) { + const endpoints = ["https://anilibria.top", "https://anilibria.wtf"]; + let selectedEndpoint: string | null = null; + + for (let i = 0; i < endpoints.length; i++) { + const endpoint = endpoints[i]; + const apiRes = await fetch(`${endpoint}/api/v1/app/status`, { + signal: AbortSignal.timeout(3000), + }); + if (apiRes.ok) { + const data: APIStatusResponse = await apiRes.json(); + if (data.is_alive != true) { + asJSON(req, res, { message: "LIBRIA: API сервер не доступен" }, 500); + return null; + } + selectedEndpoint = endpoint; + break; + } + } + + if (!selectedEndpoint) { + asJSON(req, res, { message: "LIBRIA: Нет доступных эндпоинтов API" }, 500); + return null; + } + + return selectedEndpoint; +} export async function getAnilibriaURL(req, res, url: string) { if (!url.includes("libria")) { - asJSON(req, res, { message: "Wrong url provided for player libria" }, 400); - return + asJSON(req, res, { message: "LIBRIA: Неправильная ссылка на плеер" }, 400); + return; } - const decodedUrl = new URL(url) + const apiEndpoint = await checkApiStatus(req, res); + if (!apiEndpoint) { + return; + } - const releaseId = decodedUrl.searchParams.get("id") || null - const releaseEp = decodedUrl.searchParams.get("ep") || null + const decodedUrl = new URL(url); - let apiRes = await fetch(`${API_URL}?id=${releaseId}`); + const releaseId = decodedUrl.searchParams.get("id") || null; + const releaseEp = decodedUrl.searchParams.get("ep") || null; + + let apiRes = await fetch(`${apiEndpoint}/api/v1/anime/releases/${releaseId}`); if (!apiRes.ok) { - asJSON(req, res, { message: "LIBRIA: failed to get api response" }, 500); - return + if (apiRes.status == 404) { + asJSON(req, res, { message: "LIBRIA: Релиз не найден" }, 404); + return; + } + + asJSON( + req, + res, + { message: "LIBRIA: Ошибка получения ответа от API" }, + 500 + ); + return; } - let data = stripResponse(await apiRes.json(), releaseEp); + let data = stripResponse(req, res, await apiRes.json(), releaseEp); + if (!data) { + return; + } if (releaseEp) { - data["manifest"] = createManifest(data, releaseEp) - data["poster"] = getPoster(data, releaseEp) + data["manifest"] = createManifest(data); + data["poster"] = getPoster(data); } - asJSON(req, res, data, 200); - return + return; } -function stripResponse(data, releaseEp) { - const resp = {} - resp["posters"] = data.posters - - resp["player"] = {} - resp["player"]["host"] = data.player.host - resp["player"]["list"] = data.player.list +function stripResponse(req, res, data, releaseEp) { + const resp = {}; + resp["posters"] = data.poster; + resp["episodes"] = data.episodes; if (releaseEp) { - resp["player"]["list"] = {} - resp["player"]["list"][releaseEp] = data.player.list[releaseEp] + const episode = data.episodes.find((item) => item.ordinal == releaseEp); + if (!episode) { + asJSON(req, res, { message: "LIBRIA: Эпизод не найден" }, 404); + return null; + } + resp["episodes"] = [episode]; } - return resp + return resp; } -function createManifest(data, releaseEp) { +function createManifest(data) { + const episode = data.episodes[0]; const resolutions = { - sd: "854x480", - hd: "1280x720", - fhd: "1920x1080", + hls_480: "854x480", + hls_720: "1280x720", + hls_1080: "1920x1080", }; const stringBuilder: string[] = []; stringBuilder.push("#EXTM3U"); - for (const [key, value] of Object.entries(data.player.list[releaseEp].hls).reverse()) { - if (!value) continue - stringBuilder.push(`#EXT-X-STREAM-INF:RESOLUTION=${resolutions[key]}`); - stringBuilder.push(`https://${data.player.host}${value}`); + for (const [key, value] of Object.entries(resolutions)) { + if (!episode[key]) continue; + stringBuilder.push(`#EXT-X-STREAM-INF:RESOLUTION=${value}`); + const url = new URL(episode[key]); + url.search = ""; + stringBuilder.push(url.toString()); } return stringBuilder.join("\n"); } -function getPoster(data, releaseEp) { - if (data.player.list[releaseEp].preview) return `https://anixart.libria.fun${data.player.list[releaseEp].preview}` - if (data.posters.medium.raw_base64_file) return data.posters.medium.url - return `https://anilibria.top${data.posters.medium.url}` -} \ No newline at end of file +function getPoster(data) { + const episode = data.episodes[0]; + + if (episode.preview && episode.preview.preview) + return `https://anixart.libria.fun${episode.preview.preview}`; + return `https://anilibria.top${data.poster.preview}`; +} diff --git a/player-parsers/sibnet.ts b/player-parsers/sibnet.ts index 756d25f..235f4b6 100644 --- a/player-parsers/sibnet.ts +++ b/player-parsers/sibnet.ts @@ -3,7 +3,7 @@ import { asJSON, randomUA } from "./shared"; export async function getSibnetURL(req, res, url: string) { if (!url.includes("sibnet")) { - asJSON(req, res, { message: "Wrong url provided for player sibnet" }, 400); + asJSON(req, res, { message: "SIBNET: Неправильная ссылка на плеер" }, 400); return } @@ -15,7 +15,7 @@ export async function getSibnetURL(req, res, url: string) { }, }); if (!pageRes.ok) { - asJSON(req, res, { message: `SIBNET:${pageRes.status}: failed to load page` }, 500) + asJSON(req, res, { message: `SIBNET: Не удалось загрузить страницу с плеером` }, 500) return } const pageData = await pageRes.text(); @@ -23,7 +23,7 @@ export async function getSibnetURL(req, res, url: string) { const videoMatch = videoRe.exec(pageData); if (!videoMatch || videoMatch.length == 0) { - asJSON(req, res, { message: `SIBNET: failed to find data to parse` }, 500) + asJSON(req, res, { message: `SIBNET: Не удалось найти данные эпизода` }, 500) return } @@ -42,7 +42,7 @@ export async function getSibnetURL(req, res, url: string) { ); if (!actualVideoRes.headers.get("location")) { - asJSON(req, res, { message: `SIBNET: failed to get video link` }, 500) + asJSON(req, res, { message: `SIBNET: Не удалось получить прямую ссылку` }, 500) return }