Релиз целиком: вход по ссылке, агрегат студий/типов/18+, малая int8-модель

- src/fetch.py: загрузка по URL (allowlist трекеров, защита от SSRF, cp1251)
- src/release.py: агрегат релиза, студии из заголовка раздачи (ПМ (LostFilm))
- predict --url, POST /predict {"url"}; ответ расширен, tracks сохранён
- export_onnx --quantize: tiny2 int8 29 МБ, parity 99.2-100%
- тесты (52 ok), docs/plan.md, docs; target/ убран из git

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Sonnet 5.5 committed 2026-10-27 17:02:04 +07:00
1 parent 98e4031037
commit 8cbd800ec7
2473 files changed
+424 -6060

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
# План доработки: модель озвучки для grabber
## Цель
По ссылке на раздачу (rutracker / kinozal / nnm-club) или её HTML определить
**на уровне релиза**: какие дорожки озвучки есть, кто их сделал (студия /
переводчик), какого они типа (дубляж, многоголосый, …) и есть ли 18+.
Модель должна быть маленькой и встраиваемой в grabber.
## Что уже есть (анализ)
* Multi-task RuBERT (тип 14 классов + BIO-спан автора + 18+), 3742 строки,
joint exact 0.946 (base) / 0.915 (tiny2); ONNX-экспорт с parity 100%;
FastAPI-сервис; клиенты Rust/TS/PowerShell; тесты и документация.
* grabber (`C:\Users\shats\Dev\grabber`) — **PHP 8.1 + Docker**, интеграции
Radarr/Sonarr/qBittorrent/Prowlarr; студии с kinozal разбирает регулярками
(`KinozalReleaseParser.php`, `ProwlarrReleaseEnricher.php`), идёт Rust-порт.
Естественный путь интеграции — **HTTP-сервис** (PHP ходит curl-ом), ONNX
нужен для Rust-порта.
## Пробелы относительно задачи
| # | Пробел | Решение |
|---|---|---|
| 1 | Нет входа «по ссылке» (только `text`/`html`) | `url` в CLI, сервисе, API: fetch (таймаут, UA, cp1251), определение трекера по домену |
| 2 | Ответ — плоский список дорожек | Агрегат релиза: дедуп дорожек, уникальные студии с типами, `is_18plus` релиза; поле `tracks` не ломаем |
| 3 | Заголовок раздачи (`/ ПМ (LostFilm) /`) не используется | fallback-парсинг title, если дорожек 0 или студии нет в дорожках |
| 4 | kinozal: авторы грузятся JS | при работе по URL запросить вкладку озвучки напрямую (эндпоинт — из `data/samples/kinozal*.html`, не угадывать) |
| 5 | «Маленькая» модель: best = base (~440 МБ) | tiny2 (30M) + ONNX + int8-квантизация; решение по цифрам parity/размера |
| 6 | Precision 0.83 на rutracker | разобрать ложные срабатывания e2e — скорее всего регулярки сегментации |
| 7 | В git закоммичен `examples/rust/*/target/` | `.gitignore` + удалить из индекса |
## Порядок работ
1. **Baseline**: tests + e2e на текущих моделях (цифры в конец документа).
2. **URL + агрегат релиза** (`src/fetch.py`, `src/release.py`, эндпоинт
`POST /predict` с `url`, CLI `--url`).
3. **Title-fallback** в `html_extract.py`.
4. **kinozal-вкладка** (если эндпоинт найден в сэмплах).
5. **Разбор false-positives** сегментации.
6. **tiny2 + int8 ONNX**, замер размера/parity.
7. **Тесты** на каждый пункт, обновление docs/README.
8. `.gitignore` + чистка `target/`.
## Критерии готовности
* `python -m src.predict --url <ссылка>` печатает агрегат релиза;
* e2e precision/recall не хуже baseline;
* малая модель < ~40 МБ (int8), parity ≥ 99%;
* тесты зелёные, docs обновлены.
## Baseline
Модель `models/best` (ruBert-base), `tests/e2e_check.py`:
* rutracker: precision 0.826 / recall 0.984 (n=290);
* kinozal: 0.107 / 0.107 (n=28) — в текстовых дампах нет авторов (JS), потолок данных;
* smoke вне датасета: тип 9/10, автор 5/10, 18+ 10/10;
* `tests/run_tests.py`: 34 ok.
## Статус
| Шаг | Состояние |
|---|---|
| 1 baseline | сделано |
| 2 URL + агрегат релиза | сделано: `src/fetch.py`, `src/release.py`, `--url`, `POST /predict {"url"}` |
| 3 title-fallback | сделано (`ПМ (LostFilm)` → дорожка с `source: "title"`); работает для HTML, у текстовых дампов заголовка нет |
| 4 kinozal-вкладка | **не сделано** в сервисе: страница релиза требует авторизации, а `get_srv_details.php?id=…&pagesd=N` уже вызывает `KinozalClient.php` в grabber. Рекомендация: grabber скачивает вкладку сам и шлёт `{"html": …}` |
| 5 false-positives сегментации | **не сделано** (precision rutracker 0.826 — отдельный разбор) |
| 6 tiny2 + int8 | сделано: 29.3 МБ, parity 99.8% тип / 99.2% спан / 100% 18+ (`reports/onnx_parity_tiny2_int8.md`) |
| 7 тесты | `run_tests.py`: 52 ok (URL allowlist/SSRF, декодирование, title, агрегат) |
| 8 `target/` | сделано |
## Открытые вопросы
* Подтвердить, что grabber (PHP) интегрируется по HTTP (рекомендуется), а
Rust-порт — через ONNX int8 (`models/tiny2-onnx/model.int8.onnx`).
* Качество tiny2 против base по e2e на страницах не мерили — только parity.