fix(photos): faster large-album loading and album cover lifecycle

Large albums opened slowly and flickered; the default album cover was
also lost in the media-objects refactor.

Large-album loading:
- quantize signed object URLs within a window and send Cache-Control so
  previews are reused instead of re-fetched
- publish photo.updated (not photo.created) when variants are ready, so
  clients update in place without reordering the grid
- worker concurrency + newest-first variant priority; raise default batch

Album cover:
- first uploaded photo becomes the cover when none is set
- clear the cover when the cover photo is trashed or moved out
- "set as album cover" in the photo context menu and actions panel

OpenSpec changes: fix-large-album-load, fix-album-cover
This commit is contained in:
iu
2026-10-11 14:15:59 +03:00
parent 6361e48b20
commit d7a37328f0
40 changed files with 999 additions and 57 deletions
@@ -0,0 +1,46 @@
# Proposal
## Why
Большие альбомы (например `seed-1791714157-0 #2`, 1000 фото) открываются медленно и
«мерцают». Замеры на реальной базе: запрос списка фото выполняется за ~1 мс, но при
открытии первые 50 ячеек — это ещё не обработанные фото («обработка…»), генерация
вариантов идёт со скоростью ~2 фото/с (порядка 8 минут на альбом), а сетка перетасовывается
из-за семантики SSE-событий и перекачивается из-за нестабильных подписанных URL.
## What Changes
- **Стабильные ссылки на объекты.** Подписанный URL перестаёт меняться каждую секунду:
срок действия квантуется по окну, ответы бэкенда получают `Cache-Control`, чтобы браузер
переиспользовал уже загруженные превью вместо повторной загрузки.
- **Корректная семантика SSE-событий.** Готовность вариантов worker'а публикуется как
изменение фото (не как новое фото), поэтому клиент обновляет уже показанное фото и не
вставляет чужие фото в начало сетки. Событие изменения фото добавляется в набор
отслеживаемых на клиенте.
- **Ускорение и приоритезация очереди вариантов.** Повышается пропускная способность
генерации вариантов (параллелизм и параметры пакета) и свежие фото обрабатываются первыми,
чтобы видимый экран заполнялся превью быстрее.
## Capabilities
### New Capabilities
<!-- нет -->
### Modified Capabilities
- `photos`: подписанные URL становятся детерминированными в пределах окна и отдаются с
заголовками кэширования.
- `realtime`: готовность вариантов публикуется как изменение фото; набор событий включает
изменение фото.
- `media-objects`: очередь генерации вариантов получает настраиваемую пропускную способность
и приоритет по свежести фото.
## Impact
- Backend: `internal/storage/signer.go`, `internal/app/app.go` (заголовки объектов),
`internal/photos/worker.go`, `internal/photos/sqlite_objects.go` (порядок и пакет claim),
`internal/config/config.go` (параметры worker'а).
- Frontend: `src/lib/sse.ts` (набор событий), `src/routes/(app)/albums/[id]/+page.svelte`
(обработка событий и вставка в сетку).
- Поведение/совместимость: подписанный URL живёт до `ttl + окно` вместо `ttl`; событие
`photo.created` больше не переиспользуется для готовности вариантов.
- Данные/миграции: изменений схемы не требуется.