web-ui: добавлена страница группового удаления загрузок
- выбор → поимённое подтверждение → отчёт: пачка до 20 загрузок, гарды входа на обеих границах, потолок времени и остановка после трёх подряд отказов внешнего сервиса - допуск полного удаления сведён в единую точку store.State.CanDelete() — worker, страница загрузки и Telegram больше не держат своих перечней
This commit is contained in:
+36
-4
@@ -89,6 +89,41 @@ func listWhere(f ListFilter) (string, []any) {
|
||||
return " WHERE " + strings.Join(conds, " AND "), args
|
||||
}
|
||||
|
||||
// listSelect и listOrder — общая форма выборки списка загрузок: LEFT JOIN
|
||||
// текущей попытки распознавания (фолбэк заголовка и значок типа) и порядок по
|
||||
// времени добавления с tie-break по id. Держим одним куском, чтобы страница
|
||||
// списка и страница группового удаления не разъехались порядком.
|
||||
const listSelect = `SELECT download.*, r.title AS rec_title, r.media_type AS rec_media_type
|
||||
FROM download
|
||||
LEFT JOIN recognition r ON r.download_id = download.id AND r.is_current = 1`
|
||||
|
||||
const listOrder = `
|
||||
ORDER BY COALESCE(download.source_added_at, download.created_at) DESC, download.id DESC`
|
||||
|
||||
// ListDeletableDownloads возвращает все загрузки, для которых домен допускает
|
||||
// полное удаление, без постраничной выдачи: страница группового удаления
|
||||
// показывает их разом, а разбиение по страницам лишило бы возможности выбрать
|
||||
// пачку. Перечень состояний берётся из единой точки домена — своего списка
|
||||
// запрос не держит.
|
||||
func (s *Store) ListDeletableDownloads(ctx context.Context) ([]Download, error) {
|
||||
ph := make([]string, len(deletableStates))
|
||||
args := make([]any, len(deletableStates))
|
||||
for i, st := range deletableStates {
|
||||
ph[i] = "?"
|
||||
args[i] = string(st)
|
||||
}
|
||||
q := listSelect + " WHERE download.state IN (" + strings.Join(ph, ",") + ")" + listOrder
|
||||
|
||||
var out []Download
|
||||
if err := s.DB.SelectContext(ctx, &out, q, args...); err != nil {
|
||||
return nil, fmt.Errorf("list deletable downloads: %w", err)
|
||||
}
|
||||
if err := s.attachInfohashes(ctx, out); err != nil {
|
||||
return nil, fmt.Errorf("list deletable downloads: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListDownloadsPage возвращает страницу загрузок под фильтром и общее число
|
||||
// строк, удовлетворяющих фильтру (для пагинации). Сортировка — по времени
|
||||
// добавления в источник (source_added_at) с фолбеком на created_at и tie-break
|
||||
@@ -104,10 +139,7 @@ func (s *Store) ListDownloadsPage(ctx context.Context, f ListFilter) ([]Download
|
||||
return nil, 0, fmt.Errorf("list downloads page count: %w", err)
|
||||
}
|
||||
|
||||
q := `SELECT download.*, r.title AS rec_title, r.media_type AS rec_media_type
|
||||
FROM download
|
||||
LEFT JOIN recognition r ON r.download_id = download.id AND r.is_current = 1` + where +
|
||||
` ORDER BY COALESCE(download.source_added_at, download.created_at) DESC, download.id DESC
|
||||
q := listSelect + where + listOrder + `
|
||||
LIMIT ? OFFSET ?`
|
||||
pageArgs := append(append([]any{}, args...), f.Limit, f.Offset)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user