Add video as a media kind in photos: streaming upload with per-kind limits, ffprobe metadata, poster frame reusing the image variant pipeline, and a playback object (remux when web-compatible, else h264/yuv420p/aac transcode). Video jobs run on a separate pool with a long lease. Frontend gains a video viewer, grid badge/duration and video upload.
78 lines
1.9 KiB
Go
78 lines
1.9 KiB
Go
// 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
|
|
}
|