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 }