Compare commits

..
2 Commits
Author SHA1 Message Date
av 5992d75b57 задачи: переписана disc-image-releases под проверенное поведение Jellyfin
- BDMV раскладывается каталогом, VIDEO_TS и .iso уходят в review: DVD-каталог
  Jellyfin распознаёт, но не проигрывает
- добавлены «Затрагивает» и критерии приёмки, задача проходит tasks.py ready
2026-08-10 20:12:06 +03:00
av f89911447d задачи: заведён баг о застывшей странице загрузки
- страница /download/{id} держит бейдж «распознаётся» после перехода задачи
  дальше; воспроизведено на боевом umbar на последней версии
- причина неизвестна: тик самообновления объявлен и покрыт тестом, поэтому
  первый критерий приёмки — назвать, на каком шаге он теряется
2026-08-10 18:05:34 +03:00
3 changed files with 135 additions and 5 deletions
+2 -1
View File
@@ -16,7 +16,7 @@
- [🔬 Форма ответа поиска TheTVDB и семантика параметра language](items/tvdb-search-response-live-check.md) — форма ответа поиска TheTVDB принята по swagger 4.7.10 и живым прогоном не подтверждена — при иной форме разбор молча уходит в фолбэк, гейт зелёный, локализованное название не работает
- [✨ Узаконить confidence-гейт авто-раскладки в спеке и сделать его выключаемым (дефолт 0.7)](items/auto-link-confidence-gate.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку
- [✨ Пересчитывать абсолютную нумерацию аниме в SxxEyy](items/anime-absolute-numbering.md) — аниме со сквозной нумерацией (#137) не раскладывается в SxxEyy, который ждёт Jellyfin — нужен пересчёт абсолютной нумерации
- [✨ Раскладывать раздачу-копию диска (VIDEO_TS/BDMV) каталогом целиком](items/disc-image-releases.md) — раздача-образ диска (VIDEO_TS/BDMV) сейчас разбирается пофайлово вместо раскладки каталога целиком — редкий, но реальный случай
- [✨ Раскладывать раздачу-копию Blu-ray (BDMV) каталогом целиком](items/disc-image-releases.md) — раздача-образ Blu-ray разбирается пофайлово и едет в библиотеку россыпью .m2ts, а Jellyfin умеет такой каталог целиком
- [✨ Принимать ссылку на .torrent и скачивать файл самим (нужен SSRF-гард)](items/torrent-url-fetch.md) — magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард)
- [✨ Докладывать недостающие эпизоды merge-раскладкой при повторной заливке](items/merge-incremental-redownload.md) — повторная заливка сериала целиком должна доложить недостающие эпизоды merge-раскладкой, не трогая существующие ссылки — блокирует типовой сценарий свежих сериалов
- [✨ Слать живые обновления через SSE вместо htmx-поллинга](items/sse-live-updates.md) — живые обновления на htmx-поллинге дают задержку и холостые запросы — SSE убрал бы то и другое (поллинг работает, поэтому улучшение, не блокер)
@@ -29,6 +29,7 @@
- [✨ Заказать спекой крайние случаи именования: многофайловый фильм, редакции, двойная серия](items/naming-edge-cases.md) — стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена
- [🐞 Не сносить раздачу, которой владеет другая активная загрузка](items/delete-checks-active-infohash-owner.md) — удаление старой закрытой задачи уничтожает файлы живой загрузки с тем же инфохэшем; воспроизведено падающим тестом на ревью bulk-delete-page
- [🐞 Не оставлять задачу в done, когда ссылки сняты, а раздача не снесена](items/delete-leaves-stale-done.md) — при недоступном qBittorrent задача весь простой соседа показывает done, хотя тайтла в Jellyfin уже нет: сверка падает на первом шаге и до коррекции не доходит
- [🐞 Обновлять страницу загрузки при выходе из «распознаётся»](items/download-page-stale-in-recognizing.md) — на боевом umbar последней версии страница держит бейдж «распознаётся» после того, как задача ушла в review или done — состояние видно только по F5
- [🔬 Канон нумерации серий и порядок у провайдера тега](items/episode-numbering-canon.md) — Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега
- [🔬 Тексты и формат уведомлений в Telegram](items/telegram-messages-audit.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи
- [🔬 guessit как сервис-спутник](items/guessit-sidecar.md) — go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём
+67 -4
View File
@@ -1,10 +1,73 @@
# ✨ Раскладывать раздачу-копию диска (VIDEO_TS/BDMV) каталогом целиком
# ✨ Раскладывать раздачу-копию Blu-ray (BDMV) каталогом целиком
- **Тип:** feature
- **Категория:** Ядро продукта
- **Зачем:** раздача-образ диска (VIDEO_TS/BDMV) сейчас разбирается пофайлово вместо раскладки каталога целиком — редкий, но реальный случай
- **Зачем:** раздача-образ Blu-ray разбирается пофайлово и едет в библиотеку россыпью .m2ts, а Jellyfin умеет такой каталог целиком
- **Теги:** goal:complex-releases
Иногда для очень редких фильмов скачивается не один видеофайл, а полная копия диска — структура VIDEO_TS/ (DVD) или BDMV/ (BluRay). Сейчас распознавание и раскладка заточены под пофайловый разбор, а тут «фильм» — это каталог целиком. Jellyfin такие раскладки поддерживает (папка фильма с вложенным VIDEO_TS/BDMV). Нужно: распознать, что раздача — образ диска (по наличию VIDEO_TS/BDMV), не разбирать её по отдельным VOB/m2ts как серии, разложить весь каталог хардлинками в папку фильма (Название (Год)/VIDEO_TS/…). Крайний, но реальный случай; частота низкая.
Иногда для очень редкого фильма качается не один видеофайл, а полная копия
диска — каталог `BDMV/` (Blu-ray) или `VIDEO_TS/` (DVD). Раскладка сегодня
сопоставляет источник цели пофайлово, поэтому такая раздача едет в библиотеку
россыпью: каждый `.m2ts` из `BDMV/STREAM/` претендует на отдельный фильм или
серию. Станет наблюдаемо иначе: раздача с `BDMV/` в корне попадает в библиотеку
одним элементом и играется, а раздача с `VIDEO_TS/` или единственным `.iso`
уходит в ревью с названной причиной, а не раскладывается вслепую.
Связано: specs/recognition.md (роли файлов), specs/jellyfin-layout.md (раскладка фильма), пакеты recognize, layout.
Что выяснено про Jellyfin (проверено 2026-08-10 по документации, исходникам
резолвера и трекеру; развёрнута версия 10.11):
- **BDMV работает.** Сканер распознаёт папку с вложенным `BDMV` как один элемент
(`MovieResolver``VideoType.BluRay`), прямое воспроизведение идёт. Каталог
`BDMV` обязан быть **непосредственным ребёнком** папки фильма — то есть форма
`Название (Год) [tmdbid-…]/BDMV/…`, которую мы и печатаем сейчас.
- **VIDEO_TS не играет.** Элемент в библиотеке появляется, воспроизведение
падает или виснет много лет подряд (баг в `DvdLib`); заведённые о нём задачи
закрыты как устаревшие, в заметках 10.10 и 10.11 починки нет. Поэтому DVD
раскладывать не будем: элемент, который не запускается, хуже честного ревью.
- **ISO вне поддержки** по прямому заявлению документации Jellyfin.
- Ограничения, действующие и на рабочий BDMV: несколько версий, разбиение на
части и внешние дорожки с субтитрами в дисковом каталоге не поддерживаются, а
извлечение картиночных субтитров на лету из него сломано.
## Затрагивает
- `layout.Plan` и `layout.PlanFile` — публичные типы пакета: план сегодня это
перечень файлов с ролями, дерево каталога-образа им не выражается;
- `layout.Role` и `layout.Kind` — роль файла образа и вид целевой ссылки;
- таблица `file_link` и её миграция — если для образа заводится свой вид ссылки;
- построение целевого пути в `layout` — имя цели для файла образа задаётся его
относительным путём внутри `BDMV/`, а не печатается из названия и года;
- требование «Сопоставление источник → цель хардлинками» спеки `file-layout`
оно сейчас говорит «для каждого распознанного файла (не каталога)»;
- требование «Роли файлов на краях раздачи» спеки `recognition``.m2ts` внутри
`BDMV/` не серии и не мусор;
- переход в `review` с новой причиной (`spec recognition`) — для `VIDEO_TS` и
`.iso`;
- промпт распознавания, если признак образа снимается до обращения к LLM.
## Критерии приёмки
- раздача с каталогом `BDMV` в корне раскладывается зеркалом дерева: каждый файл
под `BDMV/` получает хардлинк, а сам `BDMV` оказывается непосредственным
ребёнком папки фильма — оракул: тест раскладки на фикстуре дерева раздачи;
- ни один `.m2ts` из `BDMV/STREAM/` не попадает в план отдельным фильмом или
серией — оракул: тест построения плана;
- раздача с каталогом `VIDEO_TS` в корне и раздача, где весь видеоматериал это
`.iso`, уходят в `review` с причиной, называющей формат — оракул: тест
перехода состояния плюс текст причины в карточке;
- каждая цель дерева проходит существующую проверку «строго под библиотекой» —
оракул: тест на раздачу, где относительное имя файла содержит `..`.
Двигает «Завершение» цели в части «образ диска раскладывается без ручного
вмешательства либо честно уходит в ревью с названной причиной».
## Рамки
Исходная раздача не трогается — только чтение и `link(2)`. Ремукс, распаковка
`.iso` и любое перекодирование в задачу не входят. Воспроизведение проверяется
человеком вручную на umbar: гейт против живого Jellyfin не ходит. Поддержка
внешних субтитров к дисковому каталогу не делается — её нет у самого Jellyfin.
Связано: `openspec/specs/recognition/spec.md` (роли файлов, переход в ревью),
`openspec/specs/file-layout/spec.md` (целевые имена фильма, сопоставление
хардлинками), пакеты `recognize`, `layout`.
@@ -0,0 +1,66 @@
# 🐞 Обновлять страницу загрузки при выходе из «распознаётся»
- **Тип:** fix
- **Категория:** Ядро продукта
- **Зачем:** на боевом umbar последней версии страница держит бейдж «распознаётся» после того, как задача ушла в review или done — состояние видно только по F5
Спека `web-ui` («Самообновление живой задачи») заказывает: страница наблюдаемой
задачи обновляет себя сама, пока задачу может двигать фон. `recognizing`
наблюдаем (`State.IsObservable`), тик страницы объявлен `every 15s`, и разметка
покрыта тестом `TestDownloadPageSelfPoll` — а на боевом стенде страница
застывает. Расходится не разметка, а поведение живой страницы, и причина
неизвестна: тик либо не уходит, либо уходит и не свопит, либо гасит сам себя.
Речь только о странице `/download/{id}`. Список загрузок ведёт свой поллер
(карточка опрашивает `/fragments/downloads/{id}/card`) и в эту задачу не входит:
на нём застывания не заявлено и оно не проверялось.
## Воспроизведение
1. Открыть `/download/{id}` на боевом umbar, пока задача в `recognizing`.
2. Дождаться, когда фон уведёт её дальше — в `review` или `done`.
3. Смотреть на страницу, не трогая её.
Видно вместо ожидаемого: бейдж и вся главная область остаются на «распознаётся»
дольше одного тика (15 с) — сколько именно, не замерено. Нажатие F5 показывает
настоящее состояние сразу. Наблюдалось на umbar на последней выложенной версии,
то есть уже с самообновлением из коммита `a5d873b`.
Замера «через сколько секунд перестало обновляться», журнала сети из браузера и
проверки на локальном запуске нет — первым делом их и надо снять.
## Затрагивает
- `web/templates/partials/download_main.html` — объявление тика:
`hx-get="/download/{id}"`, `hx-trigger="every …"`, `hx-select="#download-main"`,
`hx-swap="outerHTML"` и `hx-preserve` на «Опасной зоне»;
- `handleDownload` и `buildDownloadView` (`internal/httpapi/download.go`) — ответ
на тик, ветка `isHTMX``fragTickErr` и вычисление `SelfPoll`/`PollEvery`;
- `fragNote` (`internal/httpapi/live.go`) — самозавершающийся фрагмент, который
снимает поллер с поверхности;
- `State.IsObservable` (`internal/store/download.go`) — предикат, на котором
стоит тик;
- спеки `web-ui` («Самообновление живой задачи») и `live-status`;
- боевой рантайм: версия развёрнутого бинаря, раздача статики и заголовки
кэширования страницы `/download/{id}`.
## Критерии приёмки
- Причина названа и подтверждена: показано, на каком шаге тик теряется — запрос
не уходит, ответ не свопится или поллер снят (оракул: журнал сети браузера или
лог сервера с боевого стенда, приложенный к задаче).
- Страница, открытая в `recognizing`, показывает новое состояние без F5 не позже
двух тиков после перехода (оракул: ручной прогон на umbar с засечкой времени
перехода по логу сервера и времени смены бейджа).
- Найденная причина закрыта проверкой, которая падала бы до починки (оракул:
тест в `internal/httpapi`; причина не ловится тестом — сказано прямо, чем она
закрыта вместо теста).
- Тик по-прежнему замолкает на ненаблюдаемом состоянии (оракул: существующий
`TestDownloadPageSelfPoll` зелёный).
- `task gate` зелёный (оракул: сам гейт).
## Рамки
На SSE не переезжать — это отдельная задача `sse-live-updates`; чинится
существующий htmx-поллинг. Ветку `fragTickErr` не убирать: она стоит там, чтобы
отказ чтения не оставлял вкладку стучать вечно (ADR-2026-08-10-observability-is-not-terminality).