UI/UX списка и карточки загрузки: серверные фильтр/поиск/пагинация, матч-ссылка, имя раздачи (web-ui-list-detail)
- Список: серверные фильтр по группе состояний, поиск и пагинация (GET
f/q/page/all, по 25), сортировка по времени добавления в qBittorrent
(added_on) с фолбеком на created_at и tie-break по id.
- Заголовок загрузки = имя раздачи (display_name) → распознанное название →
усечённый источник; сырой magnet вынесен в блок «Информация о торренте».
- Матч метабазы показан ссылкой на запись (страница загрузки и ревью);
URL берётся у выбранного кандидата либо строится по provider+id и типу.
- Полировка вёрстки; клиентская JS-фильтрация убрана (всё серверное, без JS).
- Миграция 0005 (display_name, source_added_at); воркер однократно
фиксирует source_added_at при поллинге/усыновлении; ER-схема обновлена.
- OpenSpec: дельты влиты в specs/{web-ui,ingest}, change заархивирован.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ type Store interface {
|
||||
GetDownload(ctx context.Context, id int64) (*store.Download, error)
|
||||
SetDownloadState(ctx context.Context, id int64, state store.State, errCode, errMsg string) error
|
||||
SetSourceMissCount(ctx context.Context, id int64, n int) error
|
||||
SetSourceAddedAt(ctx context.Context, id int64, t time.Time) error
|
||||
|
||||
// Discovery (усыновление раздач по категории/тегу).
|
||||
ExistsByInfohash(ctx context.Context, infohash string) (bool, error)
|
||||
@@ -333,6 +334,7 @@ func (w *Worker) Poll(ctx context.Context) error {
|
||||
"capability", capIngest, "download_id", d.ID, "infohash", d.Infohash.String)
|
||||
continue
|
||||
}
|
||||
w.captureSourceAddedAt(ctx, d, t)
|
||||
w.reconcile(ctx, d, t)
|
||||
}
|
||||
|
||||
@@ -380,6 +382,21 @@ func (w *Worker) checkTimeouts(ctx context.Context, d store.Download, t qbt.Torr
|
||||
}
|
||||
}
|
||||
|
||||
// captureSourceAddedAt однократно сохраняет время добавления торрента в
|
||||
// qBittorrent (added_on) у задачи — базис сортировки списка. Пишем только при
|
||||
// первом наблюдении (в БД source_added_at ещё пуст, SQL-гард в store); значение
|
||||
// неизменно, поэтому повторные тики его не трогают. Учётная операция: её сбой не
|
||||
// двигает задачу, лишь логируем WARN. Вызывается под w.mu.
|
||||
func (w *Worker) captureSourceAddedAt(ctx context.Context, d store.Download, t qbt.Torrent) {
|
||||
if d.SourceAddedAt.Valid || t.AddedOn <= 0 {
|
||||
return
|
||||
}
|
||||
if err := w.store.SetSourceAddedAt(ctx, d.ID, time.Unix(t.AddedOn, 0)); err != nil {
|
||||
w.log.Warn("capture source_added_at failed",
|
||||
"capability", capIngest, "download_id", d.ID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// torrentAge — возраст торрента: от added_on в qBittorrent (надёжный базис,
|
||||
// переживает retry/усыновление), с фолбэком на created_at задачи, если qBit не
|
||||
// отдал added_on.
|
||||
|
||||
Reference in New Issue
Block a user