feat: image variants job and variants in the post API
- golem15_journal_image_variants (migration 202610280002): a thumbnail
per width of golem15.journal.image_widths (480, 800, 1200 by default,
never wider than the original) and its WebP copy, with the original's
size
- conga job golem15.journal.image_variants (three attempts), queued after
a featured image upload and after an admin save of a post whose images
lack a width; the upload request never encodes
- journal:image-variants [--all] backfills
- featured_images[].variants {width, height, url, webp_url, source_width,
source_height} on the post answers (additive key)
This commit is contained in:
@@ -468,6 +468,8 @@ func UploadFeaturedImage(app *backpack.App) http.HandlerFunc {
|
||||
stored.AttachmentID = idStr
|
||||
stored.Field = "featured_images"
|
||||
purgeSiteCache(r, app, post)
|
||||
// The variants are written by a job, never in this request (D-85).
|
||||
classes.DispatchImageVariants(r.Context(), app, []uint{stored.ID})
|
||||
wire.WriteJSON(w, http.StatusCreated, map[string]any{
|
||||
"data": map[string]any{
|
||||
"id": stored.ID,
|
||||
@@ -535,6 +537,10 @@ func DeleteFeaturedImage(app *backpack.App) http.HandlerFunc {
|
||||
writeAPIError(w, http.StatusInternalServerError, "Internal server error")
|
||||
return
|
||||
}
|
||||
// The variants' blobs share the thumbnail prefix of BlobKeys.
|
||||
if err := db.WithContext(r.Context()).Where("file_id = ?", file.ID).Delete(&models.ImageVariant{}).Error; err != nil {
|
||||
pluginLog.Warn("journal: delete image variants", "file", file.ID, "error", err)
|
||||
}
|
||||
if bucket, ok := app.Lookup[*blob.Bucket](); ok && bucket != nil {
|
||||
_ = attach.DeleteKeys(r.Context(), bucket, keys)
|
||||
}
|
||||
@@ -761,13 +767,48 @@ func featuredImages(db *gorm.DB, p *models.Post) []map[string]any {
|
||||
).Order("sort_order ASC, id ASC").Find(&files).Error; err != nil {
|
||||
return []map[string]any{}
|
||||
}
|
||||
variants := imageVariants(db, files)
|
||||
out := make([]map[string]any, 0, len(files))
|
||||
for i := range files {
|
||||
list := variants[files[i].ID]
|
||||
if list == nil {
|
||||
list = []map[string]any{}
|
||||
}
|
||||
out = append(out, map[string]any{
|
||||
"id": files[i].ID,
|
||||
"url": files[i].URL(),
|
||||
"file_name": files[i].FileName,
|
||||
"file_size": files[i].FileSize,
|
||||
"variants": list,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// imageVariants lists the variants of files in one query, by file and
|
||||
// width (D-85): {width, height, url, webp_url} and, on each, the
|
||||
// original's source_width and source_height.
|
||||
func imageVariants(db *gorm.DB, files []attach.File) map[uint][]map[string]any {
|
||||
out := map[uint][]map[string]any{}
|
||||
if len(files) == 0 {
|
||||
return out
|
||||
}
|
||||
ids := make([]uint, 0, len(files))
|
||||
for _, f := range files {
|
||||
ids = append(ids, f.ID)
|
||||
}
|
||||
var rows []models.ImageVariant
|
||||
if err := db.Where("file_id IN ?", ids).Order("file_id ASC, width ASC").Find(&rows).Error; err != nil {
|
||||
return out
|
||||
}
|
||||
for _, v := range rows {
|
||||
out[v.FileID] = append(out[v.FileID], map[string]any{
|
||||
"width": v.Width,
|
||||
"height": v.Height,
|
||||
"url": attach.PublicURL(v.Key),
|
||||
"webp_url": attach.PublicURL(v.WebPKey),
|
||||
"source_width": v.SourceWidth,
|
||||
"source_height": v.SourceHeight,
|
||||
})
|
||||
}
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user