Adds the ffmpeg-binary half of the resolution stack so the upcoming
WebRTC streaming transcoder (Fase 3.3) has a single point of entry.
Search order matches ResolveFFprobe so operators don't need to learn a
second mental model:
1. Explicit path (--ffmpeg flag / library.ffmpeg_path config)
2. FFMPEG_PATH env var
3. "ffmpeg" on PATH (system install)
4. Adjacent to the unarr executable (release tarball bundles it here —
this is the preferred path; see Fase 3.2 goreleaser changes)
5. Cache dir (sibling of the cached ffprobe binary)
6. Auto-download from ffbinaries.com (~70MB) as last resort
Includes:
- internal/library/mediainfo/ffmpeg.go — ResolveFFmpeg + actionable
Docker / non-Docker error messages
- internal/library/mediainfo/ffmpeg_download.go — DownloadFFmpeg, reuses
ffprobePlatformKey + ffprobeAPIClient + ffprobeDLClient + extractFromZip
helpers; bumps maxZipSize to 200MB (ffmpeg static is ~70-100MB)
- internal/config: LibraryConfig.FFmpegPath toml field for explicit paths
- 4 unit tests: explicit OK, explicit missing, env var, sibling cache path
Tarball bundling and the actual transcoding pipeline land in the next
two commits.
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package mediainfo
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
// TestResolveFFmpeg_ExplicitOK verifies the explicit-path branch returns
|
|
// the requested binary if it exists on disk.
|
|
func TestResolveFFmpeg_ExplicitOK(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fake := filepath.Join(dir, "ffmpeg")
|
|
if err := os.WriteFile(fake, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
|
t.Fatalf("write fake: %v", err)
|
|
}
|
|
|
|
got, err := ResolveFFmpeg(fake)
|
|
if err != nil {
|
|
t.Fatalf("ResolveFFmpeg(explicit): %v", err)
|
|
}
|
|
if got != fake {
|
|
t.Fatalf("got %q want %q", got, fake)
|
|
}
|
|
}
|
|
|
|
// TestResolveFFmpeg_ExplicitMissing returns a clear error when the path
|
|
// the operator supplied doesn't exist — we do NOT silently fall back.
|
|
func TestResolveFFmpeg_ExplicitMissing(t *testing.T) {
|
|
_, err := ResolveFFmpeg("/nonexistent/path/ffmpeg-XXXXXX")
|
|
if err == nil {
|
|
t.Fatal("expected error for missing explicit path")
|
|
}
|
|
}
|
|
|
|
// TestResolveFFmpeg_EnvVar honours FFMPEG_PATH when no explicit path is given.
|
|
func TestResolveFFmpeg_EnvVar(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fake := filepath.Join(dir, "ffmpeg")
|
|
if err := os.WriteFile(fake, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
|
t.Fatalf("write fake: %v", err)
|
|
}
|
|
t.Setenv("FFMPEG_PATH", fake)
|
|
// Hide the real ffmpeg from PATH so the env var is the next branch hit.
|
|
t.Setenv("PATH", "/nonexistent")
|
|
|
|
got, err := ResolveFFmpeg("")
|
|
if err != nil {
|
|
t.Fatalf("ResolveFFmpeg(env): %v", err)
|
|
}
|
|
if got != fake {
|
|
t.Fatalf("got %q want %q (env-var branch)", got, fake)
|
|
}
|
|
}
|
|
|
|
// TestFFmpegCachePath returns a sibling path to the ffprobe cache,
|
|
// consistent with the install layout the tarball produces.
|
|
func TestFFmpegCachePath(t *testing.T) {
|
|
got, err := FFmpegCachePath()
|
|
if err != nil {
|
|
t.Fatalf("FFmpegCachePath: %v", err)
|
|
}
|
|
want := "ffmpeg"
|
|
if runtime.GOOS == "windows" {
|
|
want = "ffmpeg.exe"
|
|
}
|
|
if filepath.Base(got) != want {
|
|
t.Fatalf("cache path basename = %q want %q", filepath.Base(got), want)
|
|
}
|
|
probeCache, err := FFprobeCachePath()
|
|
if err != nil {
|
|
t.Fatalf("FFprobeCachePath: %v", err)
|
|
}
|
|
if filepath.Dir(got) != filepath.Dir(probeCache) {
|
|
t.Fatalf("ffmpeg cache (%s) and ffprobe cache (%s) should share a directory", got, probeCache)
|
|
}
|
|
}
|