Уточнение деталей архитектуры
This commit is contained in:
@@ -19,10 +19,10 @@ Jellyfin ждёт `SxxEyy`. Нужен пересчёт абсолютной н
|
|||||||
|
|
||||||
## Завершение загрузки через webhook
|
## Завершение загрузки через webhook
|
||||||
|
|
||||||
Сейчас план — поллинг qBittorrent. Альтернатива: «Run external program on
|
Сейчас принято — поллинг qBittorrent раз в несколько секунд.
|
||||||
torrent completion» в qBittorrent дёргает эндпоинт jellybit. Реагирует
|
Альтернатива: «Run external program on torrent completion» в qBittorrent
|
||||||
быстрее, но связывает нас с конфигом qBittorrent. Решим по опыту
|
дёргает эндпоинт jellybit. Реагирует быстрее, но связывает нас с конфигом
|
||||||
эксплуатации.
|
qBittorrent. Решим по опыту эксплуатации.
|
||||||
|
|
||||||
## Нотификации о готовности
|
## Нотификации о готовности
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -6,22 +6,28 @@
|
|||||||
## Фазы
|
## Фазы
|
||||||
|
|
||||||
- **Ф0 — каркас.** go.mod, раскладка пакетов, загрузка TOML-конфига,
|
- **Ф0 — каркас.** go.mod, раскладка пакетов, загрузка TOML-конфига,
|
||||||
SQLite + миграции, slog-логи, Dockerfile (static → distroless),
|
SQLite + миграции, slog-логи, `Dockerfile` (минимальный рантайм-образ,
|
||||||
golangci-lint, lefthook. Документация (этот этап — частично готов).
|
копирует готовый бинарь), golangci-lint, lefthook. Документация (этот
|
||||||
|
этап — частично готов).
|
||||||
- **Ф1 — ingest + tracking (без LLM).** `Ingest()` + добавление в
|
- **Ф1 — ingest + tracking (без LLM).** `Ingest()` + добавление в
|
||||||
qBittorrent (категория `jellybit`) + `worker`-поллинг завершения +
|
qBittorrent (категория `jellybit`) + `worker`-поллинг завершения +
|
||||||
машина состояний. Наружу: HTTP API, список в веб-UI, `jellybit add`.
|
машина состояний. Наружу: HTTP API, список в веб-UI, `jellybit add`.
|
||||||
- **Ф2 — распознавание.** `go-ptn` + LLM (structured output) → план +
|
- **Ф2 — распознавание.** `go-ptn` + LLM (structured output) → план +
|
||||||
оценка уверенности. Без записи на диск.
|
оценка уверенности. Без записи на диск.
|
||||||
- **Ф3 — раскладка + минимальный review.** Хардлинки по конвенциям
|
- **Ф3 — раскладка + минимальный review.** Хардлинки по конвенциям
|
||||||
Jellyfin, субтитры, идемпотентность. Авто при высокой уверенности;
|
Jellyfin, субтитры, идемпотентность, **undo**. Авто при высокой
|
||||||
низкая → экран подтверждения (htmx).
|
уверенности; низкая → review (htmx): подсказка + перераспознавание, из
|
||||||
|
ручного — тип, выбор кандидата базы, пометка «игнор». Полный редактор
|
||||||
|
маппинга — Ф5. См. [review-ux.md](../specs/review-ux.md).
|
||||||
- **Ф4 — метаданные.** TMDB/TVDB опционально, provider-id в именах,
|
- **Ф4 — метаданные.** TMDB/TVDB опционально, provider-id в именах,
|
||||||
валидация распознавания против числа серий.
|
валидация распознавания против числа серий.
|
||||||
- **Ф5 — Telegram + UX.** Бот-адаптер + парсер сообщений торрент-бота,
|
- **Ф5 — Telegram + UX.** Бот-адаптер + парсер сообщений торрент-бота,
|
||||||
подтверждение в боте, триггер скана Jellyfin, нотификации.
|
подтверждение в боте (карточка + кнопки + reply-подсказка, эскалация в
|
||||||
- **Ф6 — деплой.** Static-образ/бинарь + обвязка в umbar
|
веб), полный редактор маппинга «файл → серия», триггер скана Jellyfin,
|
||||||
(`playbook-jellybit.yml`).
|
нотификации.
|
||||||
|
- **Ф6 — деплой.** Сборка статического бинаря здесь; доставка бинаря +
|
||||||
|
`Dockerfile` на сервер, `docker build` и запуск на месте; оркестрация —
|
||||||
|
`playbook-jellybit.yml` в umbar.
|
||||||
|
|
||||||
## Заметки по порядку
|
## Заметки по порядку
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,7 @@
|
|||||||
поток, машина состояний, хранилище, конфигурация.
|
поток, машина состояний, хранилище, конфигурация.
|
||||||
- [recognition.md](recognition.md) — распознавание контента и модель
|
- [recognition.md](recognition.md) — распознавание контента и модель
|
||||||
уверенности.
|
уверенности.
|
||||||
|
- [review-ux.md](review-ux.md) — ревью раскладки человеком: UI/UX-сценарии
|
||||||
|
на случай, когда система не уверена.
|
||||||
- [jellyfin-layout.md](jellyfin-layout.md) — конвенции именования файлов
|
- [jellyfin-layout.md](jellyfin-layout.md) — конвенции именования файлов
|
||||||
Jellyfin, в которые раскладываем.
|
Jellyfin, в которые раскладываем.
|
||||||
|
|||||||
+53
-14
@@ -26,7 +26,8 @@ qBittorrent, определяет содержимое (фильм или сер
|
|||||||
| `ingest` | use-case приёма загрузки, общий для всех транспортов |
|
| `ingest` | use-case приёма загрузки, общий для всех транспортов |
|
||||||
| `qbt` | клиент qBittorrent WebUI API |
|
| `qbt` | клиент qBittorrent WebUI API |
|
||||||
| `worker` | фоновый цикл: машина состояний, поллинг завершения |
|
| `worker` | фоновый цикл: машина состояний, поллинг завершения |
|
||||||
| `recognize` | пред-парс имени + LLM + модель уверенности |
|
| `recognize` | пред-парс имени + вызов LLM + модель уверенности |
|
||||||
|
| `llm` | провайдер LLM за интерфейсом (дискриминатор `type`) |
|
||||||
| `metadata` | интерфейс баз метаданных + TMDB/TVDB (опц.) |
|
| `metadata` | интерфейс баз метаданных + TMDB/TVDB (опц.) |
|
||||||
| `layout` | конвенции Jellyfin + хардлинкер |
|
| `layout` | конвенции Jellyfin + хардлинкер |
|
||||||
| `store` | SQLite: загрузки, распознавание, ссылки |
|
| `store` | SQLite: загрузки, распознавание, ссылки |
|
||||||
@@ -44,10 +45,12 @@ ingest → downloading → completed → recognizing ─┬─ уверенно
|
|||||||
|
|
||||||
- **ingest** — приняли источник + контекст, поставили в qBittorrent
|
- **ingest** — приняли источник + контекст, поставили в qBittorrent
|
||||||
(категория `jellybit`), записали в БД.
|
(категория `jellybit`), записали в БД.
|
||||||
- **downloading / completed** — `worker` поллит qBittorrent по категории.
|
- **downloading / completed** — `worker` поллит qBittorrent по категории
|
||||||
|
(интервал `worker.poll_interval`, по умолчанию 5 с).
|
||||||
- **recognizing** — `recognize` строит план раскладки и оценку
|
- **recognizing** — `recognize` строит план раскладки и оценку
|
||||||
уверенности (см. [recognition.md](recognition.md)).
|
уверенности (см. [recognition.md](recognition.md)).
|
||||||
- **review** — план уходит человеку (веб-UI / Telegram), ждём решения.
|
- **review** — план уходит человеку (веб-UI / Telegram), ждём решения;
|
||||||
|
сценарии — в [review-ux.md](review-ux.md).
|
||||||
- **linking** — `layout` создаёт хардлинки в библиотеке.
|
- **linking** — `layout` создаёт хардлинки в библиотеке.
|
||||||
- **done** — опционально дёргаем скан библиотеки Jellyfin.
|
- **done** — опционально дёргаем скан библиотеки Jellyfin.
|
||||||
|
|
||||||
@@ -77,7 +80,10 @@ SQLite, минимум таблиц:
|
|||||||
|
|
||||||
## Конфигурация
|
## Конфигурация
|
||||||
|
|
||||||
TOML, секреты — placeholder'ы; реальный конфиг не коммитим. Пример:
|
TOML. В репозитории — `config.example.toml` с placeholder'ами; реальный
|
||||||
|
`config.toml` рендерится при деплое Ansible-шаблоном из переменных umbar
|
||||||
|
(секреты — в `vars/secrets.yml` под ansible-vault) и не коммитится.
|
||||||
|
Пример:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[qbittorrent]
|
[qbittorrent]
|
||||||
@@ -92,9 +98,11 @@ movies = "/srv/media/movies"
|
|||||||
series = "/srv/media/series"
|
series = "/srv/media/series"
|
||||||
|
|
||||||
[llm]
|
[llm]
|
||||||
provider = "anthropic"
|
# type — дискриминатор реализации; пока поддерживается "openai-compat"
|
||||||
model = "claude-sonnet-4-6" # сложные случаи — claude-opus-4-8
|
type = "openai-compat"
|
||||||
|
base_url = "http://127.0.0.1:1234/v1"
|
||||||
api_key = ""
|
api_key = ""
|
||||||
|
model = "qwen2.5-32b-instruct"
|
||||||
|
|
||||||
[metadata.tmdb]
|
[metadata.tmdb]
|
||||||
enabled = true
|
enabled = true
|
||||||
@@ -104,6 +112,9 @@ api_key = ""
|
|||||||
enabled = false
|
enabled = false
|
||||||
api_key = ""
|
api_key = ""
|
||||||
|
|
||||||
|
[worker]
|
||||||
|
poll_interval = "5s" # как часто опрашивать qBittorrent
|
||||||
|
|
||||||
[recognition]
|
[recognition]
|
||||||
auto_confidence_threshold = 0.85
|
auto_confidence_threshold = 0.85
|
||||||
|
|
||||||
@@ -126,20 +137,39 @@ format = "json"
|
|||||||
сквозным идентификатором; решения распознавания (почему авто/ревью)
|
сквозным идентификатором; решения распознавания (почему авто/ревью)
|
||||||
логируются явно.
|
логируются явно.
|
||||||
|
|
||||||
|
## Деплой
|
||||||
|
|
||||||
|
Jellybit работает в **docker** — в одной среде с qBittorrent и Jellyfin
|
||||||
|
(они тоже в контейнерах на umbar). Единая среда запуска перевешивает
|
||||||
|
простоту нативного systemd.
|
||||||
|
|
||||||
|
Сборка — дёшево и сердито: статический бинарь собирается здесь; на сервер
|
||||||
|
во временную build-папку кладутся бинарь + `Dockerfile` (он просто
|
||||||
|
копирует бинарь в минимальный образ), образ собирается прямо на сервере и
|
||||||
|
запускается. Go-тулчейн на сервере не нужен — только docker.
|
||||||
|
|
||||||
|
Разделение ответственности:
|
||||||
|
|
||||||
|
- **jellybit** (этот репозиторий) — производит статический бинарь и
|
||||||
|
`Dockerfile`.
|
||||||
|
- **umbar** — оркестрация деплоя: доставка артефактов, `docker build` и
|
||||||
|
запуск через docker compose (`playbook-jellybit.yml`).
|
||||||
|
|
||||||
## Раскладка файлов
|
## Раскладка файлов
|
||||||
|
|
||||||
Хардлинки в `paths.movies` / `paths.series` по конвенциям Jellyfin.
|
Хардлинки в `paths.movies` / `paths.series` по конвенциям Jellyfin.
|
||||||
Детали и крайние случаи — в [jellyfin-layout.md](jellyfin-layout.md).
|
Детали и крайние случаи — в [jellyfin-layout.md](jellyfin-layout.md).
|
||||||
Требование: `downloads` и `media` на одной ФС (иначе хардлинк
|
`/srv/downloads` и `/srv/media` — одна ФС (подтверждено), поэтому
|
||||||
невозможен). Если jellybit в docker — смонтировать общего родителя,
|
хардлинки применимы. Так как jellybit в docker (см. «Деплой»), контейнеру
|
||||||
чтобы хардлинк работал внутри контейнера.
|
монтируем общего родителя `/srv` — чтобы внутри оба каталога остались на
|
||||||
|
одной ФС и хардлинк проходил.
|
||||||
|
|
||||||
## Предполагаемая структура репозитория
|
## Предполагаемая структура репозитория
|
||||||
|
|
||||||
```
|
```
|
||||||
cmd/jellybit/ точка входа, сборка зависимостей
|
cmd/jellybit/ точка входа, сборка зависимостей
|
||||||
internal/
|
internal/
|
||||||
ingest/ qbt/ worker/ recognize/ metadata/
|
ingest/ qbt/ worker/ recognize/ llm/ metadata/
|
||||||
layout/ store/ httpapi/ tgbot/ config/
|
layout/ store/ httpapi/ tgbot/ config/
|
||||||
migrations/ миграции SQLite
|
migrations/ миграции SQLite
|
||||||
web/templates/ шаблоны веб-UI
|
web/templates/ шаблоны веб-UI
|
||||||
@@ -147,9 +177,18 @@ docs/ specs / adr / drafts
|
|||||||
config.example.toml
|
config.example.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Решённые вопросы
|
||||||
|
|
||||||
|
- `/srv/downloads` и `/srv/media` — одна ФС (подтверждено); хардлинки
|
||||||
|
применимы.
|
||||||
|
- Детект завершения — поллинг qBittorrent раз в несколько секунд
|
||||||
|
(`worker.poll_interval`). Webhook — возможная оптимизация на будущее
|
||||||
|
([drafts/ideas.md](../drafts/ideas.md)).
|
||||||
|
- Секреты — в переменных umbar; `config.toml` рендерится Ansible-шаблоном
|
||||||
|
при деплое.
|
||||||
|
- Форма запуска — **docker**, образ собирается на сервере из готового
|
||||||
|
бинаря (см. «Деплой»).
|
||||||
|
|
||||||
## Открытые вопросы
|
## Открытые вопросы
|
||||||
|
|
||||||
- Подтвердить, что `/srv/downloads` и `/srv/media` — одна ФС.
|
Существенных пока нет.
|
||||||
- Способ детекта завершения: поллинг (старт) или webhook qBittorrent
|
|
||||||
«run on completion» (позже).
|
|
||||||
- Где хранить секреты при деплое (шаблонизация из umbar).
|
|
||||||
|
|||||||
@@ -20,9 +20,10 @@
|
|||||||
|
|
||||||
1. **Пред-парс** имени релиза дешёвым парсером (`go-ptn`): черновые
|
1. **Пред-парс** имени релиза дешёвым парсером (`go-ptn`): черновые
|
||||||
название/год/сезон/серия и качество. Грубо, но бесплатно.
|
название/год/сезон/серия и качество. Грубо, но бесплатно.
|
||||||
2. **LLM** (Anthropic, structured output): получает все сигналы и
|
2. **LLM** (через провайдер-абстракцию, см. «Провайдер LLM»): получает
|
||||||
пред-парс, возвращает структурированный план. Хорошо справляется с
|
все сигналы и пред-парс, возвращает структурированный план в нашей
|
||||||
русскими релиз-именами, чего не умеет парсер.
|
схеме. Хорошо справляется с русскими релиз-именами, чего не умеет
|
||||||
|
парсер.
|
||||||
3. **Сверка с базой** (опц., если включена TMDB/TVDB): подтверждаем
|
3. **Сверка с базой** (опц., если включена TMDB/TVDB): подтверждаем
|
||||||
название+год, берём официальный id и каноническое имя.
|
название+год, берём официальный id и каноническое имя.
|
||||||
4. **Оценка уверенности** и решение: авто-раскладка или ревью.
|
4. **Оценка уверенности** и решение: авто-раскладка или ревью.
|
||||||
@@ -42,6 +43,24 @@ confidence 0..1 — самооценка модели по полям
|
|||||||
notes пояснения, неоднозначности
|
notes пояснения, неоднозначности
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Провайдер 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` со схемой там, где поддерживается; иначе json-режим
|
||||||
|
или tool-call), **валидируем в Go** и ретраим при несоответствии —
|
||||||
|
серверы различаются по поддержке строгих схем.
|
||||||
|
- Новые типы (напр. нативный `anthropic`) добавляются, не трогая
|
||||||
|
`recognize`.
|
||||||
|
|
||||||
## Модель уверенности
|
## Модель уверенности
|
||||||
|
|
||||||
Авто-раскладка только если выполнено всё:
|
Авто-раскладка только если выполнено всё:
|
||||||
@@ -54,8 +73,9 @@ notes пояснения, неоднозначности
|
|||||||
- сериал: число серий бьётся с базой (если есть), нумерация S·E
|
- сериал: число серий бьётся с базой (если есть), нумерация S·E
|
||||||
консистентна, без пропусков и дублей.
|
консистентна, без пропусков и дублей.
|
||||||
|
|
||||||
Иначе план уходит в **review**. На экране подтверждения всегда видно,
|
Иначе план уходит в **review** (сценарии — [review-ux.md](review-ux.md)).
|
||||||
*почему* не авто — это страховка на дорогих файлах.
|
На экране подтверждения всегда видно, *почему* не авто — это страховка на
|
||||||
|
дорогих файлах.
|
||||||
|
|
||||||
## Что делаем с краёв
|
## Что делаем с краёв
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# Ревью раскладки человеком
|
||||||
|
|
||||||
|
Что происходит, когда система не уверена в распознавании и не
|
||||||
|
раскладывает файлы автоматически. Когда именно наступает ревью — см.
|
||||||
|
[recognition.md](recognition.md); конвенции целевых имён —
|
||||||
|
[jellyfin-layout.md](jellyfin-layout.md).
|
||||||
|
|
||||||
|
Главный принцип: ревью — это **петля «догадка → подсказка человека →
|
||||||
|
перераспознавание»**, а не статичное «ок/нет». Человек остаётся
|
||||||
|
супервизором, а не оператором ручного ввода.
|
||||||
|
|
||||||
|
## Когда наступает
|
||||||
|
|
||||||
|
Загрузка уходит в `review`, если сработал любой триггер модели
|
||||||
|
уверенности: низкая самооценка LLM; нет матча в базе (или несколько
|
||||||
|
кандидатов); структурная валидация ругается (у фильма >1 основного
|
||||||
|
файла; число серий не бьётся с базой; дыры/дубли в нумерации S·E).
|
||||||
|
В интерфейсе всегда видна **конкретная причина**, а не просто «не уверен».
|
||||||
|
|
||||||
|
## Поверхность решения (едина для всех транспортов)
|
||||||
|
|
||||||
|
1. **Источник:** имя торрента, переданный контекст, дерево файлов с
|
||||||
|
размерами, (если из бота) распарсенное сообщение.
|
||||||
|
2. **Догадка системы:** тип, название, год, сезон, матч базы и
|
||||||
|
**превью целевой раскладки** — буквальные пути, которые создадутся.
|
||||||
|
3. **Причина сомнения.**
|
||||||
|
|
||||||
|
## Действия
|
||||||
|
|
||||||
|
- **Применить** — сделать хардлинки по плану.
|
||||||
|
- **Уточнить и перераспознать** — добавить подсказку текстом → LLM
|
||||||
|
перезапускается с исходными сигналами и накопленными подсказками →
|
||||||
|
новый план. Главный путь, когда «LLM не справился».
|
||||||
|
- **Поправить вручную** — объём зависит от версии (см. ниже).
|
||||||
|
- **Выбрать кандидата базы** / ввести id / «без базы».
|
||||||
|
- **Отклонить** / **Позже**.
|
||||||
|
|
||||||
|
**Подсказка vs override.** Подсказка мягкая — LLM её интерпретирует.
|
||||||
|
Ручная правка поля — жёсткий **override**: система берёт значение как
|
||||||
|
есть и «пиннит» его, перераспознавание не затирает уже поправленное.
|
||||||
|
|
||||||
|
## Веб-UI — точные правки
|
||||||
|
|
||||||
|
```
|
||||||
|
Fargo.S02.2015.WEB-DL.1080p.rus.eng 🟡 review
|
||||||
|
Причины: нет в TMDB · уверенность 0.46
|
||||||
|
|
||||||
|
Контекст: «второй сезон, рус+англ дорожки» [+ добавить → 🔁 перераспознать]
|
||||||
|
|
||||||
|
Тип: ( ) фильм (•) сериал Название: Фарго Год: 2015 Сезон: 02
|
||||||
|
База: [TMDB поиск…] [TVDB поиск…] выбрано: — (без базы) [ввести id]
|
||||||
|
|
||||||
|
Файлы → серии:
|
||||||
|
# | файл | размер | роль | S | E
|
||||||
|
1 | Fargo.S02E01.rus.mkv | 3.1 GB | эпизод | 02 | 01
|
||||||
|
… [нумеровать подряд] [сброс]
|
||||||
|
9 | sample.mkv | 40 MB | игнор | – | –
|
||||||
|
|
||||||
|
Превью:
|
||||||
|
series/Фарго (2015)/Season 02/Фарго (2015) S02E01.mkv ← #1
|
||||||
|
[ Применить ] [ Отклонить ] [ Позже ]
|
||||||
|
```
|
||||||
|
|
||||||
|
Ядро экрана для сериала — таблица «файл → серия» с живой валидацией
|
||||||
|
дыр/дублей и кнопкой «нумеровать подряд» (частый случай: файлы по
|
||||||
|
порядку, но подписаны криво). Для фильма проще: выбрать основной файл,
|
||||||
|
остальное — extra/sample/субтитры/игнор.
|
||||||
|
|
||||||
|
## Telegram — быстро, где пользователь и так есть
|
||||||
|
|
||||||
|
```
|
||||||
|
🟡 Нужно подтверждение
|
||||||
|
Источник: Fargo.S02.2015.WEB-DL.1080p
|
||||||
|
Похоже на: 📺 сериал «Фарго», сезон 2 (2015)
|
||||||
|
База: TMDB не найдено · уверенность низкая
|
||||||
|
План: 10 видео → series/Фарго (2015)/Season 02/…E01–E10
|
||||||
|
|
||||||
|
[✅ Применить] [📺↔🎬 Тип]
|
||||||
|
[🔢 Выбрать в базе] [🔁 Уточнить]
|
||||||
|
[🌐 Открыть в вебе] [❌ Отклонить]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **🔁 Уточнить** → бот просит подсказку ответом → перераспознаёт →
|
||||||
|
редактирует то же сообщение новым планом. Петля коррекции прямо в чате.
|
||||||
|
- **🔢 Выбрать в базе** → кнопки по кандидатам (название · год · id).
|
||||||
|
- Точечное переназначение файлов в чат не помещается → **🌐 Открыть в
|
||||||
|
вебе** (deep-link на ту же страницу).
|
||||||
|
|
||||||
|
## Разделение труда
|
||||||
|
|
||||||
|
Telegram = одобрить / подсказать / выбрать кандидата / эскалировать в
|
||||||
|
веб. Веб = точные правки. Состояние ревью одно (в SQLite) — действовать
|
||||||
|
можно из любого транспорта, последнее слово побеждает.
|
||||||
|
|
||||||
|
## Крайние сценарии
|
||||||
|
|
||||||
|
- **База неоднозначна** → выбор кандидата (часто чинит всё разом: пиннит
|
||||||
|
provider-id и каноническое имя).
|
||||||
|
- **База пустая (рус/аниме)** → «без базы» или ручной id/url. Аниме с
|
||||||
|
абсолютной нумерацией → веб-хелпер «absolute → S·E» (см.
|
||||||
|
[drafts/ideas.md](../drafts/ideas.md)).
|
||||||
|
- **Не тот тип (movie↔series)** → переключатель пересобирает форму плана.
|
||||||
|
- **Мусор (sample/extra/дубли дорожек)** → роль «игнор».
|
||||||
|
- **Полный провал** (LLM ничего не вытащил) → веб-«ручной режим»: выбрать
|
||||||
|
тип, ввести название/год, разложить файлы руками; в Telegram — сразу
|
||||||
|
эскалация в веб.
|
||||||
|
|
||||||
|
## Вход в ревью и откат
|
||||||
|
|
||||||
|
- Переход в `review` **пингует** (сообщение в Telegram / бейдж в вебе) —
|
||||||
|
пользователя зовут, а не он опрашивает. Таймера нет, источник
|
||||||
|
продолжает сидировать.
|
||||||
|
- После «Применить» показываем, что создано. **Undo** — убрать созданные
|
||||||
|
хардлинки одной кнопкой (источник цел); страховка от ошибочного
|
||||||
|
подтверждения.
|
||||||
|
|
||||||
|
## Объём по версиям
|
||||||
|
|
||||||
|
- **Ф3 (первая версия):** подсказка + перераспознавание; из ручного —
|
||||||
|
переключатель типа, выбор кандидата базы, пометка файла «игнор». Undo —
|
||||||
|
есть.
|
||||||
|
- **Ф5:** полный редактор маппинга «файл → серия», ручной режим,
|
||||||
|
подтверждение в Telegram с reply-подсказкой и эскалацией в веб.
|
||||||
Reference in New Issue
Block a user