# Инференс: CLI, Python API, JSON-контракт ## CLI (src/predict.py) ```bash # одна дорожка текстом (префикс «Перевод:» не обязателен, но допустим) python -m src.predict --text "Перевод: Профессиональный (многоголосый закадровый) | HDRezka Studio 18+" # HTML-страница целиком -> все дорожки релиза python -m src.predict --html data/samples/rt-topic.html # текстовый дамп страницы python -m src.predict --file data/pages/6907007.txt # результат в файл + выбор модели python -m src.predict --file page.txt --out result.json --model models/base ``` Кодировка входных файлов определяется автоматически (utf-8 → cp1251). HTML распознаётся по содержимому, так что `--html` и `--file` взаимозаменяемы. ## Ссылка на раздачу и агрегат релиза ```bash 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=.." ``` Поддерживаются только rutracker / kinozal / nnm-club (allowlist доменов, редиректы за его пределы запрещены). Для `--html`, `--file` и `--url` ответ — агрегат релиза: к `tracks` добавлены | Поле | Описание | |---|---| | `studios` | уникальные `{studio, translation_type, is_18plus}` | | `translation_types` | отсортированный список типов релиза | | `is_18plus` | `true`, если 18+ хотя бы у одной дорожки | | `title` | заголовок раздачи (если найден в HTML) | | `tracker` | только для `--url` | Дорожки дедуплицируются по (тип, студия). Студии из заголовка раздачи вида `ПМ (LostFilm)`, которых нет в описании, добавляются с `source: "title"` (у остальных `source: "page"`). `--text` по-прежнему возвращает только `tracks`. ## Python API ```python from src.predict import DubPredictor model = DubPredictor() # models/best; параметр device определяется сам model.predict_text("Авторский (одноголосый закадровый) - Ю. Сербин") # -> dict (см. контракт ниже) model.predict_html(open("page.html", encoding="utf-8").read()) # -> list[dict] — по одному на каждую найденную дорожку model.predict_page("Перевод: Дублированный | MovieDalen\n...") # текст без HTML model.predict_file("page.txt") ``` ## JSON-контракт ```json { "tracks": [ { "segment": "Профессиональный (многоголосый закадровый) | HDRezka Studio 18+", "translation_type": "проф. многоголосый", "type_confidence": 0.99, "studio_span": "HDRezka Studio", "studio": "HDrezka", "studio_confidence": 0.97, "is_18plus": true, "age_confidence": 0.99 } ] } ``` | Поле | Тип | Описание | |---|---|---| | `segment` | string | текст дорожки, по которому сделано предсказание (префикс снят) | | `translation_type` | string | один из 14 классов (см. docs/dataset.md) | | `type_confidence` | float | softmax-вероятность класса | | `studio_span` | string \| null | фрагмент текста, распознанный как имя автора | | `studio` | string \| null | каноническое имя автора по словарю алиасов; null — автора в тексте нет | | `studio_confidence` | float \| null | средняя вероятность токенов спана | | `is_18plus` | bool | дорожка помечена 18+ | | `age_confidence` | float | вероятность предсказанного класса 18+ | ## Как обрабатывается страница 1. HTML → текст (BeautifulSoup: `div.post_body` (rutracker) → `#tabs` (kinozal) → весь документ); 2. строки вида «Перевод/Озвучка/Озвучивание/Озвучено/Звук/Аудио N: …»; 3. разбиение многострочных дорожек: нумерация «1. … 2. …», разделители «+ ; /» перед словами типов, перечисление типов через запятую; 4. строки без признаков типа озвучки (например, чисто кодек-строки) не попадают в результат; 5. каждая дорожка — отдельное предсказание модели. ## Ограничения * `studio` — нормализация по словарю; неизвестные имена возвращаются как есть (спан), без канонизации; * если дорожка в тексте написана без какой-либо информации о типе, модель вернёт наиболее вероятный класс (обычно «неизвестно») — фильтрация таких строк уже сделана на этапе сегментации; * классы с единичными примерами в обучении (см. docs/dataset.md) предсказываются хуже — видно в confusion matrix.