Compare commits

...

8 commits

31 changed files with 1833 additions and 324 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) {

View file

@ -1,20 +0,0 @@
Это расширение для firefox и chrome для добавления кнопки Смотреть в Anix на сайт anixart.tv, а так-же найти в Anix на сайт кинопоиск, если обнаружен жанр аниме
## Скачать
Firefox: https://addons.mozilla.org/en-US/firefox/addon/watch-on-anix/
Chrome: https://github.com/Radiquum/anix/raw/V3/extension/chrome/watch-on-anix-chrome.zip
## Установка
Firefox:
- Загрузите расширение из AMO
Chrome:
1. скачайте и распакуйте архив
2. зайдите в расширения браузера chrome://extensions/
3. включите режим разработчика
4. нажмите "загрузить распакованное расширение" и выберите директорию куда вы распаковали архив

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,116 +0,0 @@
function determineHost() {
const url = new URL(window.location.href);
return {
host: url.host,
pathname: url.pathname,
};
}
function addButtonToAnixart(pathname) {
// find a container and an open in app link with button
const container = document.querySelector('div[style="text-align: center;"]');
const openInAppLink = document.querySelector('a[href^="anixart"');
const openInAppLinkButton = openInAppLink.querySelector("button");
openInAppLinkButton.style = "margin-top: 0px !important;"; // disable default button margin
openInAppLinkButton.classList = "btn btn-secondary"; // change default button from primary to secondary
// create a custom footer
const footer = document.createElement("div");
footer.style =
"display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; align-items: center; margin-top: 20px;";
// create and set custom link
const link = document.createElement("a");
const button = document.createElement("button");
button.style = "margin-top: 0px !important;";
button.classList = "btn btn-primary";
button.textContent = "Открыть в Anix";
const url = new URL(window.location.href);
link.href = `https://anix.wah.su${pathname}?ref=anixart.tv&source=extension`;
link.appendChild(button);
// append link and open in app link to footer
footer.appendChild(link);
footer.appendChild(openInAppLink);
// append footer to container
container.appendChild(footer);
}
function kinopoiskIsAnimeGenrePresent() {
const genre = document.querySelector('a[href^="/lists/movies/genre--anime"]');
if (genre) {
return true;
}
return false;
}
function addButtonToKinopoisk() {
let isAnime = kinopoiskIsAnimeGenrePresent();
if (!isAnime) {
console.log("genre not found");
return;
}
let title = document.querySelector('h1[itemprop="name"]');
if (!title) {
console.log("title not found");
return;
}
title = title.textContent.split(" (")[0];
const buttonStyle = `
display: inline-block;
font-weight: 400;
text-align: center;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
font-size: 1.5rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
padding: 1rem 2rem;
color: #fff;
background-color: #F04E4E;
border-color: #F04E4E;
`;
const buttonHoverStyle = `
color: #fff !important;
background-color: #E23D3D !important;
border-color: #E23D3D !important;
`
const link = document.createElement("a");
const button = document.createElement("button");
link.style =
"text-decoration: none; position: fixed; bottom: 0; right: 0; margin: 1.5rem; z-index: 1000;";
link.href = "https://anix.wah.su/search?q=" + title + "&ref=kinopoisk.ru&source=extension";
link.appendChild(button);
button.style = buttonStyle;
button.onmouseover = function () {
button.style = buttonStyle + buttonHoverStyle
}
button.onmouseout = function () {
button.style = buttonStyle;
}
button.textContent = "Найти в Anix";
document.body.appendChild(link);
}
const { host, pathname } = determineHost();
if (host == "anixart.tv") {
addButtonToAnixart(pathname);
} else if (host == "www.kinopoisk.ru") {
addButtonToKinopoisk();
}

View file

@ -1,27 +0,0 @@
{
"manifest_version": 3,
"version": "1.2",
"name": "Watch on Anix",
"description": "Adds a button to watch on Anix.",
"content_scripts": [
{
"matches": [
"https://anixart.tv/release/*",
"https://anixart.tv/collection/*",
"https://anixart.tv/profile/*",
"https://www.kinopoisk.ru/film/*",
"https://www.kinopoisk.ru/series/*"
],
"js": [
"main.js"
]
}
],
"icons": {
"16": "icon-16x16.png",
"32": "icon-32x32.png",
"48": "icon-48x48.png",
"72": "icon-72x72.png",
"96": "icon-96x96.png"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,116 +0,0 @@
function determineHost() {
const url = new URL(window.location.href);
return {
host: url.host,
pathname: url.pathname,
};
}
function addButtonToAnixart(pathname) {
// find a container and an open in app link with button
const container = document.querySelector('div[style="text-align: center;"]');
const openInAppLink = document.querySelector('a[href^="anixart"');
const openInAppLinkButton = openInAppLink.querySelector("button");
openInAppLinkButton.style = "margin-top: 0px !important;"; // disable default button margin
openInAppLinkButton.classList = "btn btn-secondary"; // change default button from primary to secondary
// create a custom footer
const footer = document.createElement("div");
footer.style =
"display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; align-items: center; margin-top: 20px;";
// create and set custom link
const link = document.createElement("a");
const button = document.createElement("button");
button.style = "margin-top: 0px !important;";
button.classList = "btn btn-primary";
button.textContent = "Открыть в Anix";
const url = new URL(window.location.href);
link.href = `https://anix.wah.su${pathname}?ref=anixart.tv&source=extension`;
link.appendChild(button);
// append link and open in app link to footer
footer.appendChild(link);
footer.appendChild(openInAppLink);
// append footer to container
container.appendChild(footer);
}
function kinopoiskIsAnimeGenrePresent() {
const genre = document.querySelector('a[href^="/lists/movies/genre--anime"]');
if (genre) {
return true;
}
return false;
}
function addButtonToKinopoisk() {
let isAnime = kinopoiskIsAnimeGenrePresent();
if (!isAnime) {
console.log("genre not found");
return;
}
let title = document.querySelector('h1[itemprop="name"]');
if (!title) {
console.log("title not found");
return;
}
title = title.textContent.split(" (")[0];
const buttonStyle = `
display: inline-block;
font-weight: 400;
text-align: center;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
font-size: 1.5rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
padding: 1rem 2rem;
color: #fff;
background-color: #F04E4E;
border-color: #F04E4E;
`;
const buttonHoverStyle = `
color: #fff !important;
background-color: #E23D3D !important;
border-color: #E23D3D !important;
`
const link = document.createElement("a");
const button = document.createElement("button");
link.style =
"text-decoration: none; position: fixed; bottom: 0; right: 0; margin: 1.5rem; z-index: 1000;";
link.href = "https://anix.wah.su/search?q=" + title + "&ref=kinopoisk.ru&source=extension";
link.appendChild(button);
button.style = buttonStyle;
button.onmouseover = function () {
button.style = buttonStyle + buttonHoverStyle
}
button.onmouseout = function () {
button.style = buttonStyle;
}
button.textContent = "Найти в Anix";
document.body.appendChild(link);
}
const { host, pathname } = determineHost();
if (host == "anixart.tv") {
addButtonToAnixart(pathname);
} else if (host == "www.kinopoisk.ru") {
addButtonToKinopoisk();
}

View file

@ -1,32 +0,0 @@
{
"manifest_version": 2,
"version": "1.2",
"name": "Watch on Anix",
"description": "Adds a button to watch on Anix.",
"browser_specific_settings": {
"gecko": {
"id": "{8c53d0c2-43ad-4498-b700-290bd2e1030f}"
}
},
"content_scripts": [
{
"matches": [
"https://anixart.tv/release/*",
"https://anixart.tv/collection/*",
"https://anixart.tv/profile/*",
"https://www.kinopoisk.ru/film/*",
"https://www.kinopoisk.ru/series/*"
],
"js": [
"main.js"
]
}
],
"icons": {
"16": "icon-16x16.png",
"32": "icon-32x32.png",
"48": "icon-48x48.png",
"72": "icon-72x72.png",
"96": "icon-96x96.png"
}
}

View file

@ -0,0 +1,2 @@
node_modules
README.md

12
player-parsers/Dockerfile Normal file
View file

@ -0,0 +1,12 @@
FROM node:23-alpine
LABEL org.opencontainers.image.source=https://github.com/radiquum/anix
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY *.ts ./
CMD ["npm", "run", "serve"]

116
player-parsers/README.RU.md Normal file
View file

@ -0,0 +1,116 @@
# AniX - Player Parsers
Данный под-проект позволяет получить прямые ссылки на видеофайлы с источников Sibnet, Kodik, Libria
Он может использоваться как для основного проекта AniX, так и как отдельный сервис.
В основном проекте, парсеры используются для работы своего плеере, если вам не важна данная функция, вы можете не развёртывать данный суб-сервис.
Лицензия: [MIT](../LICENSE)
## Использование
В строке веб-браузера необходимо ввести:
`<http|https>://<ip|domain><:port>/?url=<VIDEO_URL>&player=<PLAYER_SOURCE>`
где:
- http|https - схема по которой будет осуществляться подключение к сервису
- ip|domain - IP адрес или домен на котором находится сервис
- :port - порт сервиса, опционально
- VIDEO_URL - ссылка на видео от источника
- PLAYER_SOURCE - источник, один из: kodik, sibnet, libria
Ответ:
- 500|400: произошла ошибка, подробнее в строке `message` в теле ответа
- 200: запрос прошёл успешно
## Развёртывание
> В связи с спецификой источников, рекомендуется использовать виртуальный сервер в россии или нидерландах, т.к. они могут быть недоступны из других стран.
>
> Из-за данной специфики, парсеры невозможно развернуть на edge сервисах, таких как CloudFlare Workers или Deno, а только на отдельном сервере.
<details>
<summary>С помощью docker</summary>
Требования:
- [docker](https://docs.docker.com/engine/install/)
### Пре-билд
1. выполните комманду:
`docker run -d --name anix-player -p 7000:7000 ghcr.io/radiquum/anix-player-parser:29-05-2025`
### Ручной билд
Доп. Требования:
- [git](https://git-scm.com/)
1. Клонируйте репозиторий `git clone https://github.com/Radiquum/AniX`
2. Переместитесь в директорию репозитория `cd AniX`
3. Переместитесь в директорию парсеров `cd player-parsers`
4. Выполните команду `docker build -t anix-player-parser .`
5. После окончания, выполните команду: `docker run -d --restart always --name anix-player -p 7000:7000 anix-player-parser`
### Обозначения
- -d - запустить контейнер в фоне
- --restart always - всегда запускать после перезагрузки сервера
- --name - название контейнера
- -p - порт контейнера который будет доступен из вне. ПОРТ:7000
### После развёртывания
Сервис будет доступен по адресу: `http://<ВАШ IP><:ВАШ ПОРТ>/`
### Примечание
Для использования своего домена и поддержки протокола https, вы можете использовать traefik или другой reverse-proxy, с сертификатом SSL.
Полезные ссылки:
- [Конвертер из команды docker run в синтакс для docker compose](https://it-tools.tech/docker-run-to-docker-compose-converter)
- [Как настроить traefik + свой домен + SSL](https://letmegooglethat.com/?q=how+to+setup+traefik+with+custom+domain+and+ssl+certificate+from+lets+encrypt%3F)
</details>
<details>
<summary>С помощью pm2</summary>
Требования:
- [git](https://git-scm.com/)
- [nodejs 23+ с npm](http://nodejs.org/)
- [pm2](https://pm2.keymetrics.io/)
Инструкция:
1. Клонируйте репозиторий `git clone https://github.com/Radiquum/AniX`
2. Переместитесь в директорию репозитория `cd AniX`
3. Переместитесь в директорию парсеров `cd player-parsers`
4. Выполните команду `npm install`
5. После окончания, Выполните команду `pm2 start index.ts -n anix-player-parser`
### Обозначения
- -n - название сервиса в pm2
### После развёртывания
Сервис будет доступен по адресу: `http://<ВАШ IP>:7000/`
### Примечание
Для автоматического запуска приложения, рекомендуется настроить pm2 на автозапуск, с помощью команды: `pm2 startup`
Полезные ссылки:
- [PM2: подходим к вопросу процесс-менеджмента с умом @ Habr](https://habr.com/ru/articles/480670/)
</details>

116
player-parsers/README.md Normal file
View file

@ -0,0 +1,116 @@
# AniX - Player Parsers
This sub-project allows obtaining direct video file links from sources Sibnet, Kodik, Libria
It can be used both for the main AniX project and as a standalone service.
In the main project, the parsers are used to operate the internal player. If this function is not important to you, you may choose not to deploy this sub-service.
License: [MIT](../LICENSE)
## Usage
In the web browser address bar, enter:
`<http|https>://<ip|domain><:port>/?url=<VIDEO_URL>&player=<PLAYER_SOURCE>`
where:
- http|https - the scheme used to connect to the service
- ip|domain - IP address or domain where the service is hosted
- :port - service port, optional
- VIDEO_URL - the link to the video from the source
- PLAYER_SOURCE - the source, one of: kodik, sibnet, libria
Response:
- 500|400: an error occurred, see the `message` field in the response body for details
- 200: request was successful
## Deployment
> Due to the nature of the sources, it is recommended to use a virtual server in Russia or the Netherlands, as they may be inaccessible from other countries.
>
> Because of this specificity, the parsers cannot be deployed on edge services like CloudFlare Workers or Deno, only on a dedicated server.
<details>
<summary>Using docker</summary>
Requirements:
- [docker](https://docs.docker.com/engine/install/)
### Pre-built
1. Run the command:
`docker run -d --name anix-player -p 7000:7000 ghcr.io/radiquum/anix-player-parser:29-05-2025`
### Manual build
Additional Requirements:
- [git](https://git-scm.com/)
1. Clone the repository `git clone https://github.com/Radiquum/AniX`
2. Navigate to the repository directory `cd AniX`
3. Navigate to the parsers directory `cd player-parsers`
4. Run the command `docker build -t anix-player-parser .`
5. Once finished, run the command: `docker run -d --restart always --name anix-player -p 7000:7000 anix-player-parser`
### Legend
- -d - run the container in background
- --restart always - always restart after server reboot
- --name - container name
- -p - container port accessible externally. PORT:7000
### After deployment
The service will be available at: `http://<YOUR IP><:YOUR PORT>/`
### Note
To use your own domain and support the https protocol, you can use traefik or another reverse-proxy with an SSL certificate.
Useful links:
- [Docker run to docker compose syntax converter](https://it-tools.tech/docker-run-to-docker-compose-converter)
- [How to setup traefik + custom domain + SSL](https://letmegooglethat.com/?q=how+to+setup+traefik+with+custom+domain+and+ssl+certificate+from+lets+encrypt%3F)
</details>
<details>
<summary>Using pm2</summary>
Requirements:
- [git](https://git-scm.com/)
- [nodejs 23+ with npm](http://nodejs.org/)
- [pm2](https://pm2.keymetrics.io/)
Instructions:
1. Clone the repository `git clone https://github.com/Radiquum/AniX`
2. Navigate to the repository directory `cd AniX`
3. Navigate to the parsers directory `cd player-parsers`
4. Run the command `npm install`
5. Once finished, Run the command `pm2 start index.ts -n anix-player-parser`
### Legend
- -n - service name in pm2
### After deployment
The service will be available at: `http://<YOUR IP>:7000/`
### Note
To enable automatic application start, it is recommended to configure pm2 to start on boot with the command: `pm2 startup`
Useful links:
- [PM2: smart approach to process management @ Habr](https://habr.com/ru/articles/480670/)
</details>

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
}

1311
player-parsers/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
{
"name": "player-parsers",
"version": "1.0.0",
"description": "Player Parsing for AniX",
"scripts": {
"serve": "npx tsx ./index.ts"
},
"author": "",
"license": "MIT",
"dependencies": {
"express": "^5.1.0",
"tsx": "^4.19.4"
}
}

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
}

View file

@ -33,5 +33,5 @@
"**/*.tsx",
"next.config.js"
],
"exclude": ["node_modules"]
"exclude": ["node_modules", "player-parsers"]
}