Files
MichilisandCursor be4dd5b47f Add mobile save sheet with same-origin photo downloads.
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>
2026-08-06 20:14:31 +00:00

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)
}
}
}
}