1
0
Fork 0
photos/cmd/preview/preview.go

169 lines
3.5 KiB
Go

package main
import (
"fmt"
"log"
"net/http"
"sync"
"time"
"git.makerforce.io/photos/photos/internal/httphelpers"
lib "git.makerforce.io/photos/photos/pkg/bucket"
"github.com/davidbyttow/govips/pkg/vips"
)
var client *lib.Client
type resizeOperation struct {
input []byte
previewOption lib.PreviewOption
output []byte
err error
took time.Duration
}
const maxAspectRatio = 6
func main() {
// Setup bucket client
var err error
client, err = lib.NewClientFromEnv()
if err != nil {
panic(err)
}
// Setup vips
vips.Startup(nil)
defer vips.Shutdown()
server := &http.Server{
Addr: ":8003",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
http.HandleFunc("/update", update)
err = server.ListenAndServe()
if err != nil {
panic(err)
}
}
func update(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
err := fmt.Errorf("%w: %v", httphelpers.ErrorMethodNotAllowed, req.Method)
httphelpers.ErrorResponse(w, err)
return
}
bucket := lib.Bucket(req.FormValue("bucket"))
photo := lib.Photo(req.FormValue("photo"))
/*
bucketMetadata, err := client.GetBucketMetadata(bucket)
previewOptions := bucketMetadata.PreviewOptions
*/
previewOptions := lib.DefaultPreviewOptions()
original, err := client.GetPhoto(bucket, photo)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
resizes := make([]resizeOperation, len(previewOptions))
for i := range resizes {
resizes[i] = resizeOperation{
input: original,
previewOption: previewOptions[i],
}
}
var wg sync.WaitGroup
for i := range resizes {
op := &resizes[i]
wg.Add(1)
// TODO: timeouts
go func() {
start := time.Now()
log.Printf("Resizing photo %s at h%dq%d", photo, op.previewOption.Height, op.previewOption.Quality)
output, err := resize(
op.input,
op.previewOption.Height,
toVipsImageType(op.previewOption.Format),
op.previewOption.Quality,
)
op.output = output
op.err = err
end := time.Now()
op.took = end.Sub(start)
wg.Done()
}()
}
wg.Wait()
// Check for errors
for _, op := range resizes {
if op.err != nil {
httphelpers.ErrorResponse(w, err)
return
}
}
// Save
for i := range resizes {
op := &resizes[i]
wg.Add(1)
go func() {
preview := photo.GetPreview(op.previewOption)
err := client.PutPreview(bucket, preview, op.output)
log.Printf("Uploaded photo %s at h%dq%d: %v", photo, op.previewOption.Height, op.previewOption.Quality, err)
op.err = err
wg.Done()
}()
}
wg.Wait()
// Check for errors
for _, op := range resizes {
if op.err != nil {
httphelpers.ErrorResponse(w, err)
return
}
}
timings := make([]time.Duration, 0)
for _, op := range resizes {
timings = append(timings, op.took)
}
w.Header().Add("X-Debug-Resize-Timings", fmt.Sprintf("%v", timings))
}
func resize(buf []byte, height int, format vips.ImageType, quality int) ([]byte, error) {
i, err := vips.NewImageFromBuffer(buf)
if err != nil {
return nil, err
}
defer i.Close()
scale := float64(height) / float64(i.Height())
err = i.Resize(scale)
if err != nil {
return nil, err
}
outBuf, _, err := i.Export(vips.ExportParams{
Format: format,
Quality: quality,
})
return outBuf, err
}
func toVipsImageType(format lib.PhotoFormat) vips.ImageType {
if format == lib.PhotoFormatWEBP {
return vips.ImageTypeWEBP
}
if format == lib.PhotoFormatJPEG {
return vips.ImageTypeJPEG
}
return vips.ImageTypeUnknown
}