git/intake/main
internal/artwork/image.go
66 lines2 KB last change raw · history
package artwork

import (
	"bytes"
	"context"
	"crypto/sha1"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

func sha1sum(data []byte) []byte {
	sum := sha1.Sum(data)
	return sum[:]
}

func execCommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
	return exec.CommandContext(ctx, name, args...)
}

func Optimize(ctx context.Context, src string, maxPx int, quality int, outPath string) (int64, error) {
	if maxPx <= 0 {
		maxPx = 1000
	}
	if quality <= 0 {
		quality = 85
	}
	if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
		return 0, err
	}
	args := []string{
		src,
		"-resize", fmt.Sprintf("%dx%d>", maxPx, maxPx),
		"-strip",
		"-interlace", "Plane",
		"-quality", strconv.Itoa(quality),
		outPath,
	}
	var errb bytes.Buffer
	cmd := execCommandContext(ctx, "magick", args...)
	cmd.Stderr = &errb
	if err := cmd.Run(); err != nil {
		return 0, fmt.Errorf("optimising cover: %s", strings.TrimSpace(errb.String()))
	}
	info, err := os.Stat(outPath)
	if err != nil {
		return 0, err
	}
	return info.Size(), nil
}

func Embed(ctx context.Context, audioPath, imagePath string) error {
	clear := execCommandContext(ctx, "metaflac", "--remove", "--block-type=PICTURE", audioPath)
	if out, err := clear.CombinedOutput(); err != nil {
		return fmt.Errorf("clearing existing art in %s: %s", filepath.Base(audioPath), strings.TrimSpace(string(out)))
	}
	cmd := execCommandContext(ctx, "metaflac", "--import-picture-from="+imagePath, audioPath)
	if out, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("embedding cover in %s: %s", filepath.Base(audioPath), strings.TrimSpace(string(out)))
	}
	return nil
}