206 lines
4.5 KiB
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)
|
|
}
|
|
|
|
// Setup vips
|
|
vips.Startup(nil)
|
|
defer vips.Shutdown()
|
|
|
|
transport := &http.Transport{
|
|
MaxIdleConns: 4,
|
|
MaxIdleConnsPerHost: 4,
|
|
IdleConnTimeout: 60 * time.Second,
|
|
DisableCompression: true,
|
|
}
|
|
http.DefaultClient = &http.Client{
|
|
Transport: transport,
|
|
Timeout: 60 * time.Second,
|
|
}
|
|
|
|
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))
|
|
}
|