Документация: cookie для kinozal необязателен, API-токен необязателен
docker / build (push) Successful in 1m26s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Sonnet 5.5 committed 2026-10-29 14:29:26 +07:00
1 parent 4e83474652
commit 9528113cfd
7 files changed
+16 -12

No files matched your search

+2 -2
View File
@@ -24,7 +24,7 @@
| `html` | string | HTML (или текст) страницы раздачи → ответ-агрегат релиза | | `html` | string | HTML (или текст) страницы раздачи → ответ-агрегат релиза |
| `url` | string | ссылка на раздачу rutracker / nnmclub / kinozal; страницу сервис скачивает сам (при 403/503 — через FlareSolverr) → ответ-агрегат + `tracker` | | `url` | string | ссылка на раздачу rutracker / nnmclub / kinozal; страницу сервис скачивает сам (при 403/503 — через FlareSolverr) → ответ-агрегат + `tracker` |
| `title` | string, необязательно | заголовок раздачи для `html` (во фрагменте вкладки его нет); для `url` берётся со страницы | | `title` | string, необязательно | заголовок раздачи для `html` (во фрагменте вкладки его нет); для `url` берётся со страницы |
| `cookie` | string, необязательно | Cookie для `url` (kinozal требует авторизацию) | | `cookie` | string, необязательно | Cookie сессии (`uid=...; pass=...`) для `url`. **Для kinozal обычно не нужен**: описание многих раздач открывается и без входа (проверено на `details.php?id=2158906`). Передавайте, только если сервис вернул `400` про форму входа |
Коды ошибок: `400` — неверное тело, пустой текст, страница не разобрана или запрещённый хост; Коды ошибок: `400` — неверное тело, пустой текст, страница не разобрана или запрещённый хост;
`401` — нет/неверный токен (если задан `DUB_API_TOKEN`); `502` — не удалось скачать страницу. `401` — нет/неверный токен (если задан `DUB_API_TOKEN`); `502` — не удалось скачать страницу.
@@ -185,7 +185,7 @@ type Release = {
| Код | Когда | Пример `detail` | | Код | Когда | Пример `detail` |
|---|---|---| |---|---|---|
| 400 | передано не ровно одно из `text` / `html` / `url` | «нужно ровно одно поле: text (дорожка), html (страница) или url (ссылка на раздачу)» | | 400 | передано не ровно одно из `text` / `html` / `url` | «нужно ровно одно поле: text (дорожка), html (страница) или url (ссылка на раздачу)» |
| 400 | пустой `text`; страница не разобрана; хост ссылки не из разрешённых (rutracker / nnmclub / kinozal); страница недоступна или слишком большая; трекер отдал заглушку Cloudflare или форму входа без FlareSolverr/`cookie`; ошибка FlareSolverr | текст причины на русском («неподдерживаемый хост: …», «страница недоступна: …», «FlareSolverr: …») | | 400 | пустой `text`; страница не разобрана; хост ссылки не из разрешённых (rutracker / nnmclub / kinozal); страница недоступна или слишком большая; трекер отдал заглушку Cloudflare или форму входа (для kinozal — раздача, которая без входа не открывается, тогда нужен `cookie`) или Cloudflare без FlareSolverr; ошибка FlareSolverr | текст причины на русском («неподдерживаемый хост: …», «страница недоступна: …», «FlareSolverr: …») |
| 401 | задан `DUB_API_TOKEN`, а токена нет или он неверный | «нужен токен: Authorization: Bearer <токен> или X-API-Key» | | 401 | задан `DUB_API_TOKEN`, а токена нет или он неверный | «нужен токен: Authorization: Bearer <токен> или X-API-Key» |
| 422 | тело запроса не JSON-объект или поля не тех типов (валидация FastAPI; `detail` — массив) | `[{"loc": [...], "msg": "...", "type": "..."}]` | | 422 | тело запроса не JSON-объект или поля не тех типов (валидация FastAPI; `detail` — массив) | `[{"loc": [...], "msg": "...", "type": "..."}]` |
| 502 | сетевая ошибка при скачивании страницы по `url` (соединение, DNS, таймаут) | «не удалось загрузить страницу: …» | | 502 | сетевая ошибка при скачивании страницы по `url` (соединение, DNS, таймаут) | «не удалось загрузить страницу: …» |
+5 -3
View File
@@ -65,7 +65,7 @@ curl -X POST http://localhost:8000/predict \
| Переменная | По умолчанию | Назначение | | Переменная | По умолчанию | Назначение |
|---|---|---| |---|---|---|
| `DUB_FLARESOLVERR_URL` | не задана (в compose `http://flaresolverr:8191`) | адрес FlareSolverr без `/v1`; пусто — Cloudflare не обходится | | `DUB_FLARESOLVERR_URL` | не задана (в compose `http://flaresolverr:8191`) | адрес FlareSolverr без `/v1`; пусто — Cloudflare не обходится |
| `DUB_API_TOKEN` | не задан | если задан, `/predict` требует токен (см. [API](#api)); `/health` остаётся открытым | | `DUB_API_TOKEN` | не задан | **необязателен.** Если задан, `/predict` требует токен (см. [API](#api)); если нет — API открыт всем, кто достучится до порта (для домашней сети допустимо, для доступа из интернета задайте токен). `/health` всегда открыт |
| `DUB_FETCH_TIMEOUT` | `60` | таймаут загрузки страницы, с. FlareSolverr решает challenge 10–40 с, прежние 15 с не хватало | | `DUB_FETCH_TIMEOUT` | `60` | таймаут загрузки страницы, с. FlareSolverr решает challenge 10–40 с, прежние 15 с не хватало |
| `DUB_TORCH_THREADS` | все ядра | потоков torch; на VPS с несколькими другими сервисами ограничьте | | `DUB_TORCH_THREADS` | все ядра | потоков torch; на VPS с несколькими другими сервисами ограничьте |
| `DUB_MODEL_DIR` | `/app/models/best` | каталог основной модели | | `DUB_MODEL_DIR` | `/app/models/best` | каталог основной модели |
@@ -105,8 +105,10 @@ Hugging Face. Все файлы моделей, включая архитект
Замечания: Замечания:
* FlareSolverr нестабилен: бывают `Timeout after 15.0 seconds` и ответ ~40 с. Это ошибка 400/502 для * FlareSolverr нестабилен: бывают `Timeout after 15.0 seconds` и ответ ~40 с. Это ошибка 400/502 для
одного запроса — клиенту стоит повторить запрос. одного запроса — клиенту стоит повторить запрос.
* kinozal требует авторизацию: передавайте `cookie` в теле запроса (см. ниже), он уйдёт и напрямую, и в * kinozal: `cookie` **необязателен**. Описание большинства раздач открывается без входа, и запрос
FlareSolverr. `{"url": "https://kinozal.me/details.php?id=…"}` работает как есть. Если сервис ответил `400` про форму
входа (раздача закрыта для гостей), передайте `cookie` в теле запроса (см. ниже): он уйдёт и напрямую, и
в FlareSolverr.
* Сервис ходит только на rutracker, nnmclub и kinozal (список разрешённых хостов в `src/fetch.py`, в том * Сервис ходит только на rutracker, nnmclub и kinozal (список разрешённых хостов в `src/fetch.py`, в том
числе для редиректов): произвольные адреса через `url` не загружаются. числе для редиректов): произвольные адреса через `url` не загружаются.
+2 -1
View File
@@ -24,7 +24,8 @@ HTML распознаётся по содержимому, так что `--html
```bash ```bash
python -m src.predict --url https://rutracker.org/forum/viewtopic.php?t=123 python -m src.predict --url https://rutracker.org/forum/viewtopic.php?t=123
python -m src.predict --url https://kinozal.tv/details.php?id=1 --cookie "uid=..; pass=.." python -m src.predict --url https://kinozal.tv/details.php?id=1 # kinozal без входа
python -m src.predict --url https://kinozal.tv/details.php?id=1 --cookie "uid=..; pass=.." # если раздача закрыта для гостей
``` ```
Поддерживаются только rutracker / kinozal / nnm-club (allowlist доменов, Поддерживаются только rutracker / kinozal / nnm-club (allowlist доменов,
+2 -1
View File
@@ -141,7 +141,8 @@ python -m src.predict --url <ссылка> --flaresolverr http://127.0.0.1:8191
### grabber (PHP) ### grabber (PHP)
kinozal требует авторизации, поэтому grabber скачивает страницу сам Описание на kinozal часто открывается и без входа, и тогда достаточно `{"url": …}` (cookie необязателен). Для раздач,
закрытых для гостей, а также чтобы не хранить cookie в сервисе, grabber скачивает страницу сам
(`KinozalClient`) и отправляет сервису готовый HTML вместе с заголовком раздачи: (`KinozalClient`) и отправляет сервису готовый HTML вместе с заголовком раздачи:
```php ```php
+1 -1
View File
@@ -4,7 +4,7 @@
# powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -Text "Перевод: Дублированный | MovieDalen" # powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -Text "Перевод: Дублированный | MovieDalen"
# powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -HtmlFile page.html # powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -HtmlFile page.html
# powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -ReleaseUrl "https://rutracker.org/forum/viewtopic.php?t=123" # powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -ReleaseUrl "https://rutracker.org/forum/viewtopic.php?t=123"
# (kinozal: добавьте -Cookie "uid=..; pass=..") # (kinozal: cookie обычно не нужен; если раздача закрыта для гостей — добавьте -Cookie "uid=..; pass=..")
# pwsh -File examples\powershell\predict.ps1 -Text "..." -Url http://host:8000 # pwsh -File examples\powershell\predict.ps1 -Text "..." -Url http://host:8000
# #
# Тело запроса отправляется UTF-8 байтами — это одинаково работает и в # Тело запроса отправляется UTF-8 байтами — это одинаково работает и в
+3 -3
View File
@@ -8,9 +8,9 @@
fetch_html(url, ...) — скачать страницу, вернуть (html, tracker) fetch_html(url, ...) — скачать страницу, вернуть (html, tracker)
decode_bytes(data) — байты -> текст (utf-8 -> cp1251, чинит «двойную») decode_bytes(data) — байты -> текст (utf-8 -> cp1251, чинит «двойную»)
kinozal отдаёт описание только авторизованным пользователям, поэтому для него kinozal: описание многих раздач открывается и без входа (cookie необязателен). Если трекер
передавайте cookie (`cookie="uid=...; pass=..."`) либо скачивайте страницу сами вернул форму входа, передайте cookie (`cookie="uid=...; pass=..."`) либо скачайте страницу сами
(grabber уже умеет) и отдавайте сервису готовый `html`. (grabber уже умеет) и отдайте сервису готовый `html`.
Cloudflare / защита от ботов: если задан FlareSolverr (ваш собственный Cloudflare / защита от ботов: если задан FlareSolverr (ваш собственный
экземпляр, как у Jackett/Prowlarr) — `flaresolverr="http://host:8191"` или экземпляр, как у Jackett/Prowlarr) — `flaresolverr="http://host:8191"` или
+1 -1
View File
@@ -52,7 +52,7 @@ class PredictRequest(BaseModel):
html: str | None = None html: str | None = None
url: str | None = None url: str | None = None
title: str | None = None # заголовок раздачи для html (фрагмент вкладки его не содержит) title: str | None = None # заголовок раздачи для html (фрагмент вкладки его не содержит)
cookie: str | None = None # для url (kinozal требует авторизацию) cookie: str | None = None # для url (kinozal: только если раздача закрыта для гостей)
def env_float(name: str, default: float) -> float: def env_float(name: str, default: float) -> float: