feat: rewrite player parsing to js and add it them to repo

This commit is contained in:
Kentai Radiquum 2025-05-29 22:36:44 +05:00
parent 743f756920
commit 990b3c1736
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
9 changed files with 1275 additions and 34 deletions

View file

@ -1,5 +1,5 @@
# пример заполнения: example.com
NEXT_PUBLIC_KODIK_PARSER_DOMAIN= # Домен парсера кодика, требуется для просмотра с данного источника
NEXT_PUBLIC_ANILIBRIA_PARSER_DOMAIN= # Домен парсера анилибрии, если не заполнено, используется официальное апи
NEXT_PUBLIC_SIBNET_PARSER_DOMAIN= # Домен парсера сибнет, требуется для просмотра с данного источника
# пример заполнения: https://example.com, http://0.0.0.0:80
NEXT_PUBLIC_KODIK_PARSER_URL= # Домен парсера кодика, требуется для просмотра с данного источника
NEXT_PUBLIC_ANILIBRIA_PARSER_URL= # Домен парсера анилибрии, если не заполнено, используется официальное апи
NEXT_PUBLIC_SIBNET_PARSER_URL= # Домен парсера сибнет, требуется для просмотра с данного источника
# ---

View file

@ -75,16 +75,16 @@ export const _fetchKodikManifest = async (
setPlayerError: (state) => void
) => {
// Fetch episode links via edge function
if (!process.env.NEXT_PUBLIC_KODIK_PARSER_DOMAIN) {
if (!process.env.NEXT_PUBLIC_KODIK_PARSER_URL) {
setPlayerError({
message: "Источник не настроен",
detail: "переменная 'NEXT_PUBLIC_KODIK_PARSER_DOMAIN' не обнаружена",
detail: "переменная 'NEXT_PUBLIC_KODIK_PARSER_URL' не обнаружена",
});
return { manifest: null, poster: null };
}
const data = await _fetchPlayer(
`https://${process.env.NEXT_PUBLIC_KODIK_PARSER_DOMAIN}/?url=${url}&player=kodik`,
`${process.env.NEXT_PUBLIC_KODIK_PARSER_URL}/?url=${url}&player=kodik`,
setPlayerError
);
if (data) {
@ -213,9 +213,9 @@ export const _fetchAnilibriaManifest = async (
const epid = url.split("?id=")[1].split("&ep=")[1];
const _url = `https://api.anilibria.tv/v3/title?id=${id}`;
let data = null;
if (process.env.NEXT_PUBLIC_ANILIBRIA_PARSER_DOMAIN) {
if (process.env.NEXT_PUBLIC_ANILIBRIA_PARSER_URL) {
data = await _fetchPlayer(
`https://${process.env.NEXT_PUBLIC_ANILIBRIA_PARSER_DOMAIN}/?url=${_url}&player=libria`,
`${process.env.NEXT_PUBLIC_ANILIBRIA_PARSER_URL}/?url=${_url}&player=libria`,
setPlayerError
);
} else {
@ -243,15 +243,15 @@ export const _fetchSibnetManifest = async (
setPlayerError: (state) => void
) => {
// Fetch data via cloud endpoint
if (!process.env.NEXT_PUBLIC_SIBNET_PARSER_DOMAIN) {
if (!process.env.NEXT_PUBLIC_SIBNET_PARSER_URL) {
setPlayerError({
message: "Источник не настроен",
detail: "переменная 'NEXT_PUBLIC_SIBNET_PARSER_DOMAIN' не обнаружена",
detail: "переменная 'NEXT_PUBLIC_SIBNET_PARSER_URL' не обнаружена",
});
return { manifest: null, poster: null };
}
const data = await _fetchPlayer(
`https://${process.env.NEXT_PUBLIC_SIBNET_PARSER_DOMAIN}/?url=${url}`,
`${process.env.NEXT_PUBLIC_SIBNET_PARSER_URL}/?url=${url}&player=sibnet`,
setPlayerError
);
if (data) {

1034
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
"dependencies": {
"apexcharts": "^3.52.0",
"deepmerge-ts": "^7.1.0",
"express": "^5.1.0",
"flowbite": "^2.4.1",
"flowbite-react": "^0.11.7",
"hls-video-element": "^1.5.0",
@ -25,6 +26,7 @@
"react-toastify": "^11.0.5",
"swiper": "^11.1.4",
"swr": "^2.2.5",
"tsx": "^4.19.4",
"videojs-video-element": "^1.4.1",
"zustand": "^4.5.4"
},

45
player-parsers/index.ts Normal file
View file

@ -0,0 +1,45 @@
import { asJSON } from "./shared";
import { getAnilibriaURL } from "./libria";
import { getSibnetURL } from "./sibnet";
import { getKodikURL } from "./kodik";
import express from "express";
const app = express();
const host = "0.0.0.0";
const port = 7000;
const allowedPlayers = ["kodik", "libria", "sibnet"];
app.get("/", (req, res) => {
const url = req.query.url;
const player = req.query.player;
if (!url) {
asJSON(res, { message: "no 'url' query provided" }, 400)
return
}
if (!player) {
asJSON(res, { message: "no 'player' query provided" }, 400)
return
}
switch (player) {
case "libria":
getAnilibriaURL(res, url)
return
case "sibnet":
getSibnetURL(res, url)
return
case "kodik":
getKodikURL(res, url)
return
default:
asJSON(res, { message: `player '${player}' is not supported. choose one of: ${allowedPlayers.join(", ")}` }, 400)
return
}
});
app.listen(port, host, function () {
console.log(`Server listens http://${host}:${port}`);
});

95
player-parsers/kodik.ts Normal file
View file

@ -0,0 +1,95 @@
import { asJSON, randomUA } from "./shared";
const altDomains = ["kodik.info", "aniqit.com", "kodik.cc", "kodik.biz"];
export async function getKodikURL(res, url: string) {
const origDomain = url.replace("https://", "").split("/")[0];
let domain = url.replace("https://", "").split("/")[0];
if (!altDomains.includes(domain)) {
asJSON(res, { message: "Wrong url provided for player kodik" }, 400);
return;
}
let user_agent = randomUA();
let pageRes = await fetch(url, {
headers: {
"User-Agent": user_agent,
},
});
if (!pageRes.ok) {
for (let i = 0; i < altDomains.length - 1; i++) {
if (url.includes(altDomains[i])) {
continue;
}
user_agent = randomUA();
const altDomain = altDomains[i];
const altUrl = url.replace(
`https://${origDomain}/`,
`https://${altDomain}/`
);
domain = altDomain;
pageRes = await fetch(altUrl, {
headers: {
"User-Agent": user_agent,
},
});
if (pageRes.ok) {
break;
}
}
}
if (!pageRes.ok) {
asJSON(res, { message: "KODIK: failed to load page" }, 500);
return;
}
const pageData = await pageRes.text();
const urlParamsRe = /var urlParams = .*;$/m;
const urlParamsMatch = urlParamsRe.exec(pageData);
if (!urlParamsMatch || urlParamsMatch.length == 0) {
asJSON(res, { message: `KODIK: failed to find data to parse` }, 500);
return;
}
const urlParamsStr = urlParamsMatch[0]
.replace("var urlParams = '", "")
.replace("';", "");
const urlStr = url.replace(`https://${origDomain}/`, "");
const type = urlStr.split("/")[0];
const id = urlStr.split("/")[1];
const hash = urlStr.split("/")[2];
const urlParams = JSON.parse(urlParamsStr);
urlParams["type"] = type;
urlParams["id"] = id;
urlParams["hash"] = hash;
const formData = new FormData();
for (const [key, value] of Object.entries(urlParams)) {
formData.append(key, value as any);
}
const linksRes = await fetch(`https://${domain}/ftor`, {
method: "POST",
body: formData,
headers: {
"User-Agent": user_agent,
},
});
if (!linksRes.ok) {
asJSON(res, { message: `KODIK: failed to get links` }, 500);
return;
}
asJSON(res, await linksRes.json(), 200);
return;
}

17
player-parsers/libria.ts Normal file
View file

@ -0,0 +1,17 @@
import { asJSON } from "./shared";
export async function getAnilibriaURL(res, url: string) {
if (!url.includes("anilibria")) {
asJSON(res, { message: "Wrong url provided for player libria" }, 400);
return
}
let apiRes = await fetch(url);
if (!apiRes.ok) {
asJSON(res, { message: "LIBRIA: failed to get api response" }, 500);
return
}
asJSON(res, await apiRes.json(), 200);
return
}

33
player-parsers/shared.ts Normal file
View file

@ -0,0 +1,33 @@
export const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
"Cache-Control": "no-cache",
};
export const resHeaders = {
...corsHeaders,
"Content-Type": "application/json",
};
export const USERAGENTS = [
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Windows; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8",
"Mozilla/5.0 (Windows NT 10.0; Windows; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Windows; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
];
export function asJSON(res, object: any, status: number) {
res.status(status).type('application/json');
res.set(corsHeaders)
res.send(JSON.stringify(object));
}
export function randomUA() {
return USERAGENTS[Math.floor(Math.random() * USERAGENTS.length - 1)]
}

59
player-parsers/sibnet.ts Normal file
View file

@ -0,0 +1,59 @@
import { asJSON, randomUA } from "./shared";
export async function getSibnetURL(res, url: string) {
if (!url.includes("sibnet")) {
asJSON(res, { message: "Wrong url provided for player sibnet" }, 400);
return
}
const user_agent = randomUA();
let pageRes = await fetch(url, {
headers: {
"User-Agent": user_agent,
},
});
if (!pageRes.ok) {
asJSON(res, { message: `SIBNET:${pageRes.status}: failed to load page` }, 500)
return
}
const pageData = await pageRes.text();
const videoRe = /\/v\/.*?\.mp4/;
const videoMatch = videoRe.exec(pageData);
if (!videoMatch || videoMatch.length == 0) {
asJSON(res, { message: `SIBNET: failed to find data to parse` }, 500)
return
}
const posterRe = /\/upload\/cover\/.*?\.jpg/;
const posterMatch = posterRe.exec(pageData);
const actualVideoRes = await fetch(
`https://video.sibnet.ru${videoMatch[0]}`,
{
headers: {
"User-Agent": user_agent,
Referer: url,
},
redirect: "manual",
}
);
if (!actualVideoRes.headers.get("location")) {
asJSON(res, { message: `SIBNET: failed to get video link` }, 500)
return
}
const video = actualVideoRes.headers.get("location");
const poster =
posterMatch ?
posterMatch.length > 0 ?
`https://st.sibnet.ru${posterMatch[0]}`
: null
: null;
asJSON(res, { video, poster }, 200)
return
}