Introduces the Go photo-api service, nginx/systemd deploy wiring, and Next.js gallery/lightbox pages so event photos can be managed and browsed. Co-authored-by: Cursor <cursoragent@cursor.com>
61 lines
1.7 KiB
Go
61 lines
1.7 KiB
Go
package imaging
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
)
|
|
|
|
// HEIC cannot be decoded in pure Go (HEVC), so conversion shells out to a
|
|
// host-installed CLI. The Go binary itself stays cgo-free; the converter is
|
|
// a runtime dependency (Debian: libvips-tools or libheif-examples).
|
|
type HeicConverter struct {
|
|
kind string // "vips" | "heif-convert" | "custom"
|
|
path string
|
|
}
|
|
|
|
// DetectHeicConverter honors an explicit HEIC_CONVERTER command, else
|
|
// autodetects vips then heif-convert. Returns nil when none is available;
|
|
// uploads of HEIC are then rejected with a clear message.
|
|
func DetectHeicConverter(explicit string) *HeicConverter {
|
|
if explicit != "" {
|
|
if p, err := exec.LookPath(explicit); err == nil {
|
|
return &HeicConverter{kind: "custom", path: p}
|
|
}
|
|
return nil
|
|
}
|
|
if p, err := exec.LookPath("vips"); err == nil {
|
|
return &HeicConverter{kind: "vips", path: p}
|
|
}
|
|
if p, err := exec.LookPath("heif-convert"); err == nil {
|
|
return &HeicConverter{kind: "heif-convert", path: p}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *HeicConverter) Name() string { return h.path }
|
|
|
|
// ToJPEG converts src (a .heic file) to a JPEG at dst.
|
|
func (h *HeicConverter) ToJPEG(src, dst string) error {
|
|
var cmd *exec.Cmd
|
|
switch h.kind {
|
|
case "vips":
|
|
cmd = exec.Command(h.path, "copy", src, dst+"[Q=95]")
|
|
case "heif-convert":
|
|
cmd = exec.Command(h.path, "-q", "95", src, dst)
|
|
default:
|
|
// custom converter contract: <cmd> <src> <dst>
|
|
cmd = exec.Command(h.path, src, dst)
|
|
}
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("heic conversion failed: %v: %s", err, truncate(string(out), 300))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func truncate(s string, n int) string {
|
|
if len(s) > n {
|
|
return s[:n]
|
|
}
|
|
return s
|
|
}
|