From bc99b6e8a6007b539a3b60b04ce43d6c1af7fd1b Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Tue, 16 Jun 2026 08:24:58 +0200 Subject: [PATCH] feat(music): friendly cross-platform ffmpeg-missing error --- internal/music/music.go | 17 +++++++++++++++-- internal/music/music_test.go | 13 +++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/internal/music/music.go b/internal/music/music.go index 64584e5..d68ec3d 100644 --- a/internal/music/music.go +++ b/internal/music/music.go @@ -1,6 +1,7 @@ package music import ( + "fmt" "os" "os/exec" "path/filepath" @@ -104,7 +105,7 @@ func ResolveFFmpegWithConfig(configuredPath string) (string, error) { } path, err := exec.LookPath("ffmpeg") if err != nil { - return "", err + return "", ffmpegMissingErr("ffmpeg") } return path, nil } @@ -122,11 +123,23 @@ func ResolveFFprobeWithConfig(configuredPath string) (string, error) { } path, err := exec.LookPath("ffprobe") if err != nil { - return "", err + return "", ffmpegMissingErr("ffprobe") } return path, nil } +// ffmpegMissingErr renders an actionable, cross-platform "tool not found" +// message. Only the music builder needs ffmpeg/ffprobe; every other builder +// runs with zero external dependencies. +func ffmpegMissingErr(tool string) error { + return fmt.Errorf(`%[1]s not found on PATH. + Windows: winget install Gyan.FFmpeg + macOS: brew install ffmpeg + Linux: apt install ffmpeg (or your distro's package manager) +Only the music builder needs %[1]s; set SOW_%[2]s to a full path to override.`, + tool, strings.ToUpper(tool)) +} + func IsMusicAssetPath(rel string) bool { rel = filepath.ToSlash(strings.TrimSpace(rel)) return rel == "envi/music" || strings.HasPrefix(rel, "envi/music/") diff --git a/internal/music/music_test.go b/internal/music/music_test.go index ba33de3..bb11d12 100644 --- a/internal/music/music_test.go +++ b/internal/music/music_test.go @@ -402,3 +402,16 @@ func TestResolveFFprobeEnv(t *testing.T) { t.Fatalf("expected '/custom/ffprobe', got %q", path) } } + +func TestFFmpegMissingErrHasInstallHints(t *testing.T) { + msg := ffmpegMissingErr("ffmpeg").Error() + for _, want := range []string{"ffmpeg", "winget", "brew", "apt", "SOW_FFMPEG"} { + if !strings.Contains(msg, want) { + t.Errorf("ffmpeg error missing %q; got:\n%s", want, msg) + } + } + probe := ffmpegMissingErr("ffprobe").Error() + if !strings.Contains(probe, "SOW_FFPROBE") { + t.Errorf("ffprobe error should mention SOW_FFPROBE; got:\n%s", probe) + } +}