Восстановление зависших загрузок и уведомления о падении (state-reconciliation)
Долгий metaDL больше не убивается агрессивным таймаутом: дефолт magnet_timeout 30m → 24h (страховочный предохранитель), базис отсчёта — added_on из qBittorrent, а не created_at (переживает retry/усыновление). Авто-восстановление: фоновая сверка возвращает в поток задачи, упавшие по нашей нетерпеливости (magnet_timeout/stalled), когда источник ожил и продвинулся за условие падения (downloading/completed по статусу торрента); qbit_error не воскрешается. Конфликт idempotency (infohash занят другой активной задачей) — оставляем в failed. Уведомления: любой переход в failed/stuck пингует автора (включая приёмный qbit_add через ingest), с дебаунсом против спама при флаппинге stalled. Ручной retry добавлен в веб-UI и Telegram; Retry перецепляется к живому торренту вместо слепого Add. Дельта state-reconciliation влита в живые спеки; обновлён workflow.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,22 @@ func (f *fakeStore) ListDownloadsByState(_ context.Context, states ...store.Stat
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (f *fakeStore) ListRecoverable(_ context.Context, codes ...string) ([]store.Download, error) {
|
||||
var out []store.Download
|
||||
for _, d := range f.downloads {
|
||||
if d.State != store.StateFailed && d.State != store.StateStuck {
|
||||
continue
|
||||
}
|
||||
for _, c := range codes {
|
||||
if d.ErrorCode.Valid && d.ErrorCode.String == c {
|
||||
out = append(out, *d)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (f *fakeStore) GetDownload(_ context.Context, id int64) (*store.Download, error) {
|
||||
d, ok := f.downloads[id]
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user