package media import ( "bytes" "context" "fmt" "os/exec" "strings" ) const maxTelegramPhotoSize = 10 * 1024 * 1024 // 10 MB // CompressImage compresses an image if it exceeds Telegram's 10 MB photo limit. // It uses ImageMagick's magick command to convert via stdin/stdout. // Returns the (possibly compressed) data, updated filename, and any error. func CompressImage(ctx context.Context, data []byte, filename string) (out []byte, outName string, err error) { if len(data) <= maxTelegramPhotoSize { return data, filename, nil } // First attempt: just re-encode as JPEG with quality 85 out, err = runMagick(ctx, data, "-quality", "85") if err != nil { return nil, "", fmt.Errorf("compress image: %w", err) } newFilename := replaceExt(filename, ".jpg") if len(out) <= maxTelegramPhotoSize { return out, newFilename, nil } // Second attempt: resize to 50% and quality 85 out, err = runMagick(ctx, data, "-resize", "50%", "-quality", "85") if err != nil { return nil, "", fmt.Errorf("compress image with resize: %w", err) } if len(out) > maxTelegramPhotoSize { return nil, "", fmt.Errorf("image still too large after compression (%d bytes)", len(out)) } return out, newFilename, nil } func runMagick(ctx context.Context, data []byte, args ...string) ([]byte, error) { cmdArgs := append([]string{"-"}, args...) cmdArgs = append(cmdArgs, "jpeg:-") cmd := exec.CommandContext(ctx, "magick", cmdArgs...) cmd.Stdin = bytes.NewReader(data) out, err := cmd.Output() if err != nil { return nil, fmt.Errorf("magick: %w", err) } return out, nil } func replaceExt(filename, newExt string) string { if i := strings.LastIndex(filename, "."); i >= 0 { return filename[:i] + newExt } return filename + newExt }