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:
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"git.vakhrushev.me/av/jellybit/internal/store"
|
||||
"git.vakhrushev.me/av/jellybit/internal/worker"
|
||||
)
|
||||
|
||||
// --- Страница просмотра одной загрузки ---
|
||||
@@ -13,7 +14,8 @@ import (
|
||||
type downloadDetailView struct {
|
||||
ID int64
|
||||
Title string
|
||||
Source string
|
||||
SourceType string // тип источника (magnet/torrent/url) — блок «Информация о торренте»
|
||||
SourceFull string // полный источник (magnet) — блок «Информация о торренте»
|
||||
Infohash string
|
||||
InfohashShort string
|
||||
Context string
|
||||
@@ -32,6 +34,7 @@ type downloadDetailView struct {
|
||||
Year int
|
||||
Provider string
|
||||
ProviderID string
|
||||
MatchURL string // ссылка на запись метабазы (пусто — показываем текстом)
|
||||
NoBase bool
|
||||
Confidence string
|
||||
Files []fileRow
|
||||
@@ -47,6 +50,18 @@ type downloadDetailView struct {
|
||||
Retriable bool
|
||||
}
|
||||
|
||||
// detailTitle — заголовок страницы просмотра: имя раздачи (display_name) →
|
||||
// распознанное название (план) → усечённый до одной строки сырой источник.
|
||||
func detailTitle(d store.Download, rd *worker.ReviewData) string {
|
||||
if d.DisplayName != "" {
|
||||
return d.DisplayName
|
||||
}
|
||||
if rd.Plan.Title != "" {
|
||||
return rd.Plan.Title
|
||||
}
|
||||
return shorten(oneLine(d.SourceRef), 120)
|
||||
}
|
||||
|
||||
func (s *server) handleDownload(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := pathID(r)
|
||||
if err != nil {
|
||||
@@ -67,8 +82,9 @@ func (s *server) handleDownload(w http.ResponseWriter, r *http.Request) {
|
||||
d := rd.Download
|
||||
view := downloadDetailView{
|
||||
ID: id,
|
||||
Title: d.SourceRef,
|
||||
Source: shorten(d.SourceRef, 120),
|
||||
Title: detailTitle(d, rd),
|
||||
SourceType: string(d.SourceType),
|
||||
SourceFull: d.SourceRef,
|
||||
Infohash: d.Infohash.String,
|
||||
InfohashShort: shortenHash(d.Infohash.String),
|
||||
Context: d.Context,
|
||||
@@ -98,6 +114,7 @@ func (s *server) handleDownload(w http.ResponseWriter, r *http.Request) {
|
||||
default:
|
||||
view.Provider = rd.Provider
|
||||
view.ProviderID = rd.ProviderID
|
||||
view.MatchURL = matchURL(rd, view.MediaType)
|
||||
}
|
||||
if rd.Recognition.Confidence.Valid {
|
||||
view.Confidence = strconv.FormatFloat(rd.Recognition.Confidence.Float64, 'f', 2, 64)
|
||||
|
||||
Reference in New Issue
Block a user