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.
79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
package mediainfo
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// ResolveFFmpeg finds the ffmpeg binary. Search order mirrors ResolveFFprobe
|
|
// so the same operator setup works for both:
|
|
// 1. Explicit path (--ffmpeg flag / library.ffmpeg_path config)
|
|
// 2. FFMPEG_PATH env var
|
|
// 3. "ffmpeg" on PATH
|
|
// 4. Adjacent to the current executable (release tarball bundles ffmpeg
|
|
// next to the unarr binary — this is the preferred install path)
|
|
// 5. Previously downloaded in the unarr cache dir
|
|
// 6. Auto-download static binary as last resort (~50MB, slow start)
|
|
//
|
|
// ffmpeg is required for the WebRTC streaming pipeline; ffprobe alone can't
|
|
// transcode HEVC/MKV to browser-friendly H.264/MP4 fragments.
|
|
func ResolveFFmpeg(explicit string) (string, error) {
|
|
if explicit != "" {
|
|
if _, err := os.Stat(explicit); err == nil {
|
|
return explicit, nil
|
|
}
|
|
return "", fmt.Errorf("ffmpeg not found at explicit path: %s", explicit)
|
|
}
|
|
|
|
if envPath := os.Getenv("FFMPEG_PATH"); envPath != "" {
|
|
if _, err := os.Stat(envPath); err == nil {
|
|
return envPath, nil
|
|
}
|
|
}
|
|
|
|
if p, err := exec.LookPath("ffmpeg"); err == nil {
|
|
return p, nil
|
|
}
|
|
|
|
if exePath, err := os.Executable(); err == nil {
|
|
name := "ffmpeg"
|
|
if runtime.GOOS == "windows" {
|
|
name = "ffmpeg.exe"
|
|
}
|
|
adjacent := filepath.Join(filepath.Dir(exePath), name)
|
|
if _, err := os.Stat(adjacent); err == nil {
|
|
return adjacent, nil
|
|
}
|
|
}
|
|
|
|
if cached, err := FFmpegCachePath(); err == nil {
|
|
if _, err := os.Stat(cached); err == nil {
|
|
return cached, nil
|
|
}
|
|
}
|
|
|
|
if p, err := DownloadFFmpeg(); err == nil {
|
|
return p, nil
|
|
}
|
|
|
|
if isDocker() {
|
|
return "", fmt.Errorf(
|
|
"ffmpeg not found and auto-download failed (read-only filesystem?).\n" +
|
|
"Options:\n" +
|
|
" • Use the official image: torrentclaw/unarr (includes ffmpeg)\n" +
|
|
" • Set FFMPEG_PATH env var to point to a pre-installed ffmpeg binary\n" +
|
|
" • Add to config.toml: [library]\\nffmpeg_path = \"/path/to/ffmpeg\"",
|
|
)
|
|
}
|
|
return "", fmt.Errorf(
|
|
"ffmpeg not found and auto-download failed.\n" +
|
|
"Options:\n" +
|
|
" • Install ffmpeg: sudo apt install ffmpeg (or brew install ffmpeg)\n" +
|
|
" • Use the unarr release tarball — ffmpeg is bundled next to the binary\n" +
|
|
" • Set FFMPEG_PATH env var to point to the ffmpeg binary\n" +
|
|
" • Add to config.toml: [library]\\nffmpeg_path = \"/path/to/ffmpeg\"",
|
|
)
|
|
}
|