// Package video prepares uploaded video for browser playback using the external // ffmpeg/ffprobe toolchain. Availability is detected at startup; when the tools // are absent the rest of the application keeps working and video upload is // disabled. package video import ( "os/exec" "galo/internal/config" ) // Toolchain locates the external video tools and reports whether they are // usable. A nil Toolchain is treated as unavailable. type Toolchain struct { ffmpegPath string ffprobePath string tempDir string available bool } // Detect resolves the configured tool paths and records whether both binaries // are present. Missing tools are not an error: video is simply unavailable. func Detect(cfg config.Video) *Toolchain { t := &Toolchain{tempDir: cfg.TempDir} if p, err := exec.LookPath(cfg.FFmpegPath); err == nil { t.ffmpegPath = p } else { t.ffmpegPath = cfg.FFmpegPath } if p, err := exec.LookPath(cfg.FFprobePath); err == nil { t.ffprobePath = p } else { t.ffprobePath = cfg.FFprobePath } t.available = t.ffmpegPath != "" && t.ffprobePath != "" && isExecutable(t.ffmpegPath) && isExecutable(t.ffprobePath) return t } func isExecutable(path string) bool { if path == "" { return false } if _, err := exec.LookPath(path); err == nil { return true } return false } // Available reports whether both ffmpeg and ffprobe are usable. func (t *Toolchain) Available() bool { return t != nil && t.available } // FFmpegPath returns the resolved ffmpeg binary path. func (t *Toolchain) FFmpegPath() string { if t == nil { return "" } return t.ffmpegPath } // FFprobePath returns the resolved ffprobe binary path. func (t *Toolchain) FFprobePath() string { if t == nil { return "" } return t.ffprobePath } // TempDir returns the configured temporary directory (empty means the system // default). func (t *Toolchain) TempDir() string { if t == nil { return "" } return t.tempDir }