mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
* Make ffmpeg/ffprobe settable and remove auto download * Detect when ffmpeg not present in setup * Add download ffmpeg task * Add download ffmpeg button in system settings * Download ffmpeg during setup
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
// Package ffmpeg provides a wrapper around the ffmpeg and ffprobe executables.
|
|
package ffmpeg
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
stashExec "github.com/stashapp/stash/pkg/exec"
|
|
"github.com/stashapp/stash/pkg/fsutil"
|
|
"github.com/stashapp/stash/pkg/logger"
|
|
)
|
|
|
|
func ValidateFFMpeg(ffmpegPath string) error {
|
|
cmd := stashExec.Command(ffmpegPath, "-h")
|
|
bytes, err := cmd.CombinedOutput()
|
|
output := string(bytes)
|
|
if err != nil {
|
|
var exitErr *exec.ExitError
|
|
if errors.As(err, &exitErr) {
|
|
return fmt.Errorf("error running ffmpeg: %v", output)
|
|
}
|
|
|
|
return fmt.Errorf("error running ffmpeg: %v", err)
|
|
}
|
|
|
|
if !strings.Contains(output, "--enable-libopus") {
|
|
return fmt.Errorf("ffmpeg is missing libopus support")
|
|
}
|
|
if !strings.Contains(output, "--enable-libvpx") {
|
|
return fmt.Errorf("ffmpeg is missing libvpx support")
|
|
}
|
|
if !strings.Contains(output, "--enable-libx264") {
|
|
return fmt.Errorf("ffmpeg is missing libx264 support")
|
|
}
|
|
if !strings.Contains(output, "--enable-libx265") {
|
|
return fmt.Errorf("ffmpeg is missing libx265 support")
|
|
}
|
|
if !strings.Contains(output, "--enable-libwebp") {
|
|
return fmt.Errorf("ffmpeg is missing libwebp support")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func LookPathFFMpeg() string {
|
|
ret, _ := exec.LookPath(getFFMpegFilename())
|
|
|
|
if ret != "" {
|
|
// ensure ffmpeg has the correct flags
|
|
if err := ValidateFFMpeg(ret); err != nil {
|
|
logger.Warnf("ffmpeg found in PATH (%s), but it is missing required flags: %v", ret, err)
|
|
ret = ""
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func FindFFMpeg(path string) string {
|
|
ret := fsutil.FindInPaths([]string{path}, getFFMpegFilename())
|
|
|
|
if ret != "" {
|
|
// ensure ffmpeg has the correct flags
|
|
if err := ValidateFFMpeg(ret); err != nil {
|
|
logger.Warnf("ffmpeg found (%s), but it is missing required flags: %v", ret, err)
|
|
ret = ""
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// ResolveFFMpeg attempts to resolve the path to the ffmpeg executable.
|
|
// It first looks in the provided path, then resolves from the environment, and finally looks in the fallback path.
|
|
// Returns an empty string if a valid ffmpeg cannot be found.
|
|
func ResolveFFMpeg(path string, fallbackPath string) string {
|
|
// look in the provided path first
|
|
ret := FindFFMpeg(path)
|
|
if ret != "" {
|
|
return ret
|
|
}
|
|
|
|
// then resolve from the environment
|
|
ret = LookPathFFMpeg()
|
|
if ret != "" {
|
|
return ret
|
|
}
|
|
|
|
// finally, look in the fallback path
|
|
ret = FindFFMpeg(fallbackPath)
|
|
return ret
|
|
}
|
|
|
|
// FFMpeg provides an interface to ffmpeg.
|
|
type FFMpeg struct {
|
|
ffmpeg string
|
|
hwCodecSupport []VideoCodec
|
|
}
|
|
|
|
// Creates a new FFMpeg encoder
|
|
func NewEncoder(ffmpegPath string) *FFMpeg {
|
|
ret := &FFMpeg{
|
|
ffmpeg: ffmpegPath,
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// Returns an exec.Cmd that can be used to run ffmpeg using args.
|
|
func (f *FFMpeg) Command(ctx context.Context, args []string) *exec.Cmd {
|
|
return stashExec.CommandContext(ctx, string(f.ffmpeg), args...)
|
|
}
|
|
|
|
func (f *FFMpeg) Path() string {
|
|
return f.ffmpeg
|
|
}
|