169 lines
3.5 KiB
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
|
|
}
|