// Command compress-images scans a directory for raster images over max-bytes // and rewrites them as JPEG ≤ limit, keeping a .bak sibling of the original. // // go run ./cmd/compress-images -dir ./uploads -max-bytes 153600 // go run ./cmd/compress-images -dir ./uploads -dry-run // // Safe for local/mounted dirs. Does not wipe remote stores or touch admin JWT. package main import ( "flag" "fmt" "os" "path/filepath" "strings" infmedia "spark-server/internal/infrastructure/media" pkgmedia "spark-server/internal/pkg/media" ) func main() { dir := flag.String("dir", "", "directory to scan (required)") maxBytes := flag.Int64("max-bytes", pkgmedia.DefaultImageMaxBytes, "target max bytes (default 150KiB)") dryRun := flag.Bool("dry-run", false, "only report oversized files") flag.Parse() if strings.TrimSpace(*dir) == "" { fmt.Fprintln(os.Stderr, "usage: compress-images -dir PATH [-max-bytes 153600] [-dry-run]") os.Exit(2) } opt := &infmedia.Options{ Limits: pkgmedia.Limits{ ImageMaxEdge: pkgmedia.DefaultImageMaxEdge, ImageMaxBytes: *maxBytes, }.Normalize(), JPEGQuality: pkgmedia.DefaultJPEGQuality, } var scanned, oversized, rewritten, failed int err := filepath.WalkDir(*dir, func(path string, d os.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { return nil } name := strings.ToLower(d.Name()) if strings.HasSuffix(name, ".bak") { return nil } mime := mimeFromExt(name) if mime == "" { return nil } scanned++ info, err := d.Info() if err != nil { return nil } if info.Size() <= *maxBytes { return nil } oversized++ fmt.Printf("OVER %s (%d bytes)\n", path, info.Size()) if *dryRun { return nil } raw, err := os.ReadFile(path) if err != nil { failed++ fmt.Printf(" read err: %v\n", err) return nil } out, outMIME, err := infmedia.EnsureUnderLimit(raw, mime, opt) if err != nil { failed++ fmt.Printf(" compress err: %v\n", err) return nil } if int64(len(out)) >= info.Size() && int64(len(out)) > *maxBytes { failed++ fmt.Printf(" still oversized (%d)\n", len(out)) return nil } bak := path + ".bak" if err := os.WriteFile(bak, raw, 0o644); err != nil { failed++ fmt.Printf(" bak err: %v\n", err) return nil } target := path if outMIME == "image/jpeg" && !hasJPEGExt(name) { target = stripExt(path) + ".jpg" } if err := os.WriteFile(target, out, 0o644); err != nil { failed++ fmt.Printf(" write err: %v\n", err) return nil } if target != path { _ = os.Remove(path) } rewritten++ fmt.Printf(" OK %d -> %d bytes (%s)\n", info.Size(), len(out), target) return nil }) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Printf("done scanned=%d oversized=%d rewritten=%d failed=%d dry-run=%v\n", scanned, oversized, rewritten, failed, *dryRun) } func mimeFromExt(name string) string { switch { case strings.HasSuffix(name, ".jpg"), strings.HasSuffix(name, ".jpeg"): return "image/jpeg" case strings.HasSuffix(name, ".png"): return "image/png" case strings.HasSuffix(name, ".gif"): return "image/gif" case strings.HasSuffix(name, ".webp"): return "image/webp" default: return "" } } func hasJPEGExt(name string) bool { return strings.HasSuffix(name, ".jpg") || strings.HasSuffix(name, ".jpeg") } func stripExt(path string) string { ext := filepath.Ext(path) if ext == "" { return path } return strings.TrimSuffix(path, ext) }