8.6 KiB
Распознавание контента
Задача
По доступным сигналам определить: фильм или сериал; каноническое название
и год; для сериала — сезон(ы) и соответствие файлов сериям; при включённых
базах — провайдер и его id. На выходе — план раскладки, оценка уверенности
и решение «авто или review» (как оно встраивается в машину состояний —
workflow.md, состояния recognizing/linking/review).
Сигналы
- Имя торрента и структура каталогов.
- Список файлов с размерами и расширениями. Абсолютный путь источника
восстанавливаем как
save_pathиз qBit (= хост-путь;path_mapобычно тождественен) + относительное имя файла из/torrents/files. Имя уже включает корневую папку для многофайловых торрентов, поэтому префикс — именноsave_path, а неcontent_path(последний удвоил бы корневую папку и сломал бы однофайловые раздачи). - Текстовый контекст человека (+ накопленные подсказки из review).
- Распарсенное сообщение торрент-бота (если через Telegram): название с годом, качество, переводы — см. пример в BRIEF.md.
Все сигналы недоверенные — имя торрента, сообщение бота и контекст управляются извне и могут содержать инъекции. Выход LLM не отвечает за безопасность: целевой путь всё равно санитизируется и проверяется на выход за пределы библиотеки (см. architecture.md → «Раскладка файлов»).
Конвейер
- Пред-парс имени релиза (
go-ptn): черновые название/год/сезон/ серия и качество. Грубо, но бесплатно. - LLM (через провайдер-абстракцию, см. ниже): получает сигналы и пред-парс, возвращает структурированный план в нашей схеме. Хорошо берёт русские релиз-имена. Длинный список файлов усекаем/семплируем под контекст модели.
- Сверка с базой (если включена TMDB/TVDB/TVMaze): ищем по
названию+году, берём официальный id и каноническое имя, собираем
кандидатов. TVMaze — без ключа, только сериалы; внешний id
(TVDB/IMDb) из
externalsидёт в имя папки. - Оценка уверенности и решение: авто или review.
Структура ответа LLM (предварительная)
type movie | series
title каноническое название
original_title оригинальное название (если есть)
year год
provider_hint строка для поиска в базе (НЕ итоговый id)
files[] { src, role: main|episode|subtitle|extra|sample|ignore,
season?, episode? } # season/episode — на файл
confidence 0..1 — самооценка модели (вспомогательный сигнал)
notes пояснения, неоднозначности
Сезон/серия — на файле: так выражаются мультисезонные паки,
спецвыпуски и смешанные раскладки; отдельного скалярного season нет.
provider_hint — только подсказка для поиска; итоговые provider
(tmdb|tvdb|tvmaze|none) и provider_id появляются после сверки с базой
и хранятся отдельно.
Провайдер LLM
Доступ к LLM — за интерфейсом; реализация выбирается полем [llm].type
(дискриминатор). Это позволяет подключать локальные модели и сторонние
(в т.ч. китайские) эндпоинты — ради экономии и независимости от вендора.
- Первый и пока единственный тип —
openai-compat: OpenAI-совместимый Chat Completions API (base_url+api_key+model). Подходят локальные серверы (LM Studio, llama.cpp, Ollama) и облачные совместимые провайдеры (DeepSeek, Qwen и др.). - Структурированный вывод надёжно: просим JSON-режим
(
response_format: {"type":"json_object"}) — это поддерживают и мелкие локальные модели, в отличие от строгих JSON Schema. На приёме срезаемпри ошибке разбора ретраим, передавая модели саму ошибку и схему в промпте, до `llm.max_retries`. Если так и не распарсилось — уходим в **review** (не в `failed`) с причиной «ответ LLM не разобран». - Новые типы (напр. нативный
anthropic) добавляются, не трогаяrecognize.
Модель уверенности
Авто-раскладка — только если выполнено всё:
- Подтверждённый матч в базе — единственный сильный результат
TMDB/TVDB/TVMaze по названию+году, давший
provider_id. Нет матча (или база выключена) → всегда review. Это и закрывает основной кейс (рус/аниме часто отсутствуют в базах), и снимает риск «LLM придумал». - Структурная валидация без предупреждений:
- фильм: ровно один основной видеофайл (семплы/экстра/ignore отброшены);
- сериал: число серий бьётся с базой, нумерация S·E консистентна, без пропусков, дублей и неоднозначных спецвыпусков.
- Согласованность сигналов — пред-парс (
go-ptn) и LLM не противоречат по типу/названию/году.
Самооценку LLM (confidence) учитываем как вспомогательный сигнал, но
не как единственный гейт: она плохо откалибрована и поддаётся
инъекции. Решают матч в базе и валидация.
Иначе — review (review-ux.md) с явной причиной.
Что делаем с краёв
- Семплы/«экстра»/мусор → роль
ignore(эвристики размер/имя + LLM). - Внешние субтитры (
.srt,.ass, пары VobSub.idx+.sub) привязываем к видео и именуем по Jellyfin (*.ru.srt). - Сезон-паки разбираем по сериям; смешанные паки, спецвыпуски (
Season 00), двойные серии (SxxEyy-Eyy) — через per-file season/episode; любая неоднозначность → review. - Аниме с абсолютной нумерацией — отдельный крайний случай, см. drafts/ideas.md.
На будущее
go-ptn слабее питоновского guessit. Если точности пред-парса не
хватит — завернуть guessit лёгким сервисом-спутником (один файл рядом с
бинарём). См. drafts/ideas.md.