Phones cannot land downloads in the photo library, so the gallery opens a share sheet for a cacheable preview while streaming via a dedicated download endpoint and recording preview sizes. Co-authored-by: Cursor <cursoragent@cursor.com>
65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"sync"
|
|
|
|
"git.azzamo.net/Michilis/Spanglish/photo-api/internal/store"
|
|
)
|
|
|
|
// statConcurrency bounds the one-off Stat burst below; a gallery of ~70
|
|
// photos then costs a handful of round-trips against S3, once ever.
|
|
const statConcurrency = 8
|
|
|
|
// fillPreviewSizes measures the preview objects of photos whose
|
|
// preview_size_bytes is still unset and writes the result back, updating the
|
|
// slice in place.
|
|
//
|
|
// The worker records the size when it generates the preview, so this only
|
|
// ever fires for rows that predate the column (or arrived via `photo-api
|
|
// sync`). It is a self-healing cache fill rather than a migration step: the
|
|
// first gallery view after deploy pays for one Stat per photo, every view
|
|
// after that reads the stored value. Errors are logged and swallowed — a
|
|
// missing size costs the save sheet its row label, which is not worth
|
|
// failing a gallery response over.
|
|
func (s *Server) fillPreviewSizes(ctx context.Context, photos []store.Photo) {
|
|
var (
|
|
wg sync.WaitGroup
|
|
sem = make(chan struct{}, statConcurrency)
|
|
mu sync.Mutex
|
|
size = make(map[string]int64, len(photos))
|
|
)
|
|
for _, p := range photos {
|
|
if p.PreviewKey == "" || p.PreviewSizeBytes > 0 {
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func(p store.Photo) {
|
|
defer wg.Done()
|
|
sem <- struct{}{}
|
|
defer func() { <-sem }()
|
|
n, err := s.storage.Stat(ctx, p.PreviewKey)
|
|
if err != nil {
|
|
log.Printf("preview size %s: %v", p.PreviewKey, err)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
size[p.ID] = n
|
|
mu.Unlock()
|
|
}(p)
|
|
}
|
|
wg.Wait()
|
|
if len(size) == 0 {
|
|
return
|
|
}
|
|
for i := range photos {
|
|
if n, ok := size[photos[i].ID]; ok {
|
|
photos[i].PreviewSizeBytes = n
|
|
if err := s.db.SetPreviewSize(ctx, photos[i].ID, n); err != nil {
|
|
log.Printf("store preview size %s: %v", photos[i].ID, err)
|
|
}
|
|
}
|
|
}
|
|
}
|