Files
avandClaude Opus 4.8 b8657120fe Приём: усыновление присутствующего в qBittorrent торрента вместо дубль-Add (409)
processCatched перед Add проверяет присутствие торрента в qBittorrent (один
листинг на тик): если раздача уже есть — усыновляем (promote catched→downloading
без повторного Add и без LLM-namer, имя из раздачи), иначе добавляем как раньше.
Это убирает бесконечный цикл дубль-Add → 409 → ретрай и лишние вызовы LLM.
Инвариант приёма «одна активная на infohash» делает различие «наш/чужой»
ненужным. source_type перечитывается под замком (сужение гонки апгрейда F6);
при недоступности qBittorrent тик пропускается без вызова LLM.

Дедуп на приёме (дубль на уже активную задачу) теперь отражается явным ответом
бота «дубль уже активной #id — добавление отменено».

Спека download-tracking обновлена (OpenSpec change заархивирован); закрыта
задача беклога review-f2-promote-without-add.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 18:36:47 +03:00

9.3 KiB
Raw Permalink Blame History

MODIFIED Requirements

Requirement: Добавление пойманной загрузки в qBittorrent

Worker SHALL периодически (в поллинг-цикле, под единой блокировкой переходов) подхватывать загрузки в состоянии catched и для каждой (кроме случая уже присутствующего в qBittorrent торрента, см. ниже): вывести отображаемое имя из контекста (см. ingest «Отображаемое имя торрента из контекста»), добавить источник в qBittorrent (категория qbittorrent.category, savepath, rename) и перевести загрузку catched → downloading. Отдельного состояния между catched и downloading быть SHALL NOT — успешный add сразу переводит в downloading (которое и означает «в qBit, возможно metaDL»).

Перед добавлением worker SHALL проверять, присутствует ли торрент загрузки уже в qBittorrent (по любому из её infohash), опираясь на листинг раздач того же тика. Если торрент уже присутствует, worker SHALL усыновить его: перевести загрузку catched → downloading без повторного add и без вывода имени через LLM (display_name берётся из имени присутствующей раздачи). Повторный add здесь не нужен и вреден — qBittorrent отверг бы дубль (напр. 409 Conflict), и загрузка зациклилась бы на ретраях. Усыновлённая раздача дальше идёт обычным путём отслеживания и раскладки. Проверка присутствия SHALL выполняться до вывода отображаемого имени, чтобы не тратить LLM-вызов на загрузку, которую добавлять не требуется.

Инвариант приёма («одна активная загрузка на infohash», см. ingest) гарантирует, что до этого шага доходит лишь загрузка, для которой в jellybit НЕТ другой активной задачи; поэтому присутствие торрента в qBittorrent worker трактует как «усыновить и разложить», а не как конфликт с чужой задачей.

Если листинг раздач qBittorrent недоступен (сетевой сбой), worker пойманную загрузку в этот тик трогать SHALL NOT (ни add, ни namer) и повторить на следующем; устойчивая недоступность отсекается предохранителем catch_timeout (см. «Предохранитель зависшего catched»).

Добавление в qBittorrent worker SHALL выполнять по типу источника (source_type):

  • Для magnet/url — передавать source_ref как ссылку (urls API /torrents/add); подсказку отображаемого имени брать из полей самой ссылки.
  • Для torrent — загружать сохранённые байты .torrent (привязанные к загрузке при приёме) и передавать их файлом (torrents API /torrents/add), НЕ как ссылку; подсказку отображаемого имени брать из метаданных торрента (имя раздачи). Добавление байтами SHALL сохранять полные метаданные (qBittorrent стартует без докачки), поэтому воскрешать раздачу по magnet-хешу вместо файла система SHALL NOT.

source_type для выбора способа добавления worker SHALL перечитывать под блокировкой переходов непосредственно перед добавлением (а не полагаться на снимок, снятый ранее вне блокировки): иначе при точном оверлапе тика с апгрейдом пойманной magnet-задачи до .torrent (см. ingest) воркер добавил бы magnet из устаревшего снимка, хотя БД уже torrent.

Неуспешный add (qBittorrent временно отверг/недоступен) SHALL оставлять загрузку в catched для повторной попытки на следующем тике; переход в терминальное состояние по единичному сбою происходить SHALL NOT (ретраи — естественными тиками поллинга, отсечка — catch_timeout).

Медленные вызовы (вывод имени через LLM, qbt.Add) SHALL выполняться вне блокировки сериализации переходов, чтобы не задерживать команды транспортов и поллинг. Под блокировкой сериализуется только запись перехода catched → downloading (см. «Переходы состояний сериализуются воркером»), с ре-валидацией, что загрузка всё ещё в catched (иначе переход отклоняется — например, при параллельной отмене).

Scenario: Пойманная magnet-загрузка добавляется в qBittorrent

  • GIVEN загрузка в состоянии catched с source_type = magnet, торрента ещё нет в qBittorrent
  • WHEN worker обрабатывает тик
  • THEN выводится отображаемое имя, ссылка добавляется в qBittorrent с нашей категорией и rename
  • AND загрузка переходит в downloading

Scenario: Пойманная .torrent-загрузка добавляется файлом

  • GIVEN загрузка в состоянии catched с source_type = torrent и сохранёнными байтами файла, торрента ещё нет в qBittorrent
  • WHEN worker обрабатывает тик
  • THEN сохранённые байты добавляются в qBittorrent файлом (torrents), с нашей категорией и rename, без обращения к magnet-хешу
  • AND загрузка переходит в downloading

Scenario: Торрент уже присутствует в qBittorrent — усыновление без add

  • GIVEN загрузка в состоянии catched, торрент которой уже присутствует в qBittorrent (добавлен ранее вручную/другим клиентом либо add прошёл на прошлом тике, а запись перехода не удалась)
  • WHEN worker обрабатывает тик
  • THEN worker НЕ вызывает qbt.Add и НЕ выводит отображаемое имя через LLM
  • AND display_name записывается из имени присутствующей раздачи
  • AND загрузка переходит в downloading и идёт обычным путём к раскладке

Scenario: qBittorrent недоступен при проверке присутствия — повтор

  • GIVEN загрузка в catched, листинг раздач qBittorrent не удался
  • WHEN worker обрабатывает тик
  • THEN worker НЕ вызывает namer и НЕ добавляет источник
  • AND загрузка остаётся в catched и попытка повторяется на следующем тике

Scenario: Временный сбой добавления — повтор

  • GIVEN загрузка в catched, торрента в qBittorrent нет, но add не удался
  • WHEN worker пытается добавить источник и add возвращает ошибку
  • THEN загрузка остаётся в catched
  • AND на следующем тике попытка добавления повторяется

Scenario: Отмена во время добавления

  • GIVEN загрузка в catched, worker выводит имя и добавляет её вне блокировки
  • WHEN параллельно приходит команда отмены (catched → cancelled), а затем worker берёт блокировку для записи перехода
  • THEN ре-валидация видит, что загрузка уже не в catched, и переход в downloading не применяется