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

206 lines
4.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"
"git.makerforce.io/photos/photos/pkg/credentials"
"git.makerforce.io/photos/photos/pkg/signer"
"github.com/davidbyttow/govips/pkg/vips"
)
var creds *credentials.Client
var sig *signer.Signer
type resizeOperation struct {
input []byte
previewOption lib.PreviewOption
output []byte
err error
took time.Duration
}
func main() {
var err error
// Setup bucket credential source
creds, err = credentials.NewClientFromEnv()
if err != nil {
panic(err)
}
// Setup bucket signer
sig, err = signer.NewSignerFromEnv()
if err != nil {
panic(err)
}
transport := &http.Transport{
MaxIdleConns: 4,
MaxIdleConnsPerHost: 4,
IdleConnTimeout: 60 * time.Second,
DisableCompression: true,
}
http.DefaultClient = &http.Client{
Transport: transport,
Timeout: 60 * time.Second,
}
// Setup vips
vips.Startup(nil)
defer vips.Shutdown()
server := &http.Server{
Addr: ":8003",
ReadTimeout: 5 * time.Second,
WriteTimeout: 60 * time.Second,
}
http.HandleFunc("/update", update)
err = server.ListenAndServe()
if err != nil {
panic(err)
}
}
func update(w http.ResponseWriter, req *http.Request) {
var err error
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"))
err = bucket.Validate()
if err != nil {
err := fmt.Errorf("%w: %v", httphelpers.ErrorBadRequest, err.Error())
httphelpers.ErrorResponse(w, err)
return
}
err = photo.Validate()
if err != nil {
err := fmt.Errorf("%w: %v", httphelpers.ErrorBadRequest, err.Error())
httphelpers.ErrorResponse(w, err)
return
}
// Obtain bucket credentials
cred, err := creds.Get(bucket)
if err != nil {
err := fmt.Errorf("%w: error getting credentials: %v", httphelpers.ErrorBadRequest, err.Error())
httphelpers.ErrorResponse(w, err)
return
}
log.Printf("Got credentials")
// Obtain preview options
/*
bucketMetadata, err := client.GetBucketMetadata(bucket)
previewOptions := bucketMetadata.PreviewOptions
*/
previewOptions := lib.DefaultPreviewOptions()
log.Printf("Preview options: %v", previewOptions)
// Get photo
original, err := getPhoto(bucket, cred, photo)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
log.Printf("Original photo filesize: %v", len(original))
// First, extract photo size
width, height, err := readsize(original)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
s := lib.PhotoMetadataSize{
Width: width,
Height: height,
}
log.Printf("Photo size: %v", s)
err = putPhotoMetadataSize(bucket, cred, photo, s)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
// Do other resizes
log.Printf("Starting resizes")
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 := putPreview(bucket, cred, 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)
}
log.Printf("Resizes took: %v", timings)
w.Header().Add("X-Debug-Resize-Timings", fmt.Sprintf("%v", timings))
}