package video import ( "context" "errors" "fmt" "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/file" ) // Decorator adds video specific fields to a File. type Decorator struct { FFProbe ffmpeg.FFProbe } func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file.File, error) { if d.FFProbe == "" { return f, errors.New("ffprobe not configured") } base := f.Base() // TODO - copy to temp file if not an OsFS if _, isOs := fs.(*file.OsFS); !isOs { return f, fmt.Errorf("video.constructFile: only OsFS is supported") } probe := d.FFProbe videoFile, err := probe.NewVideoFile(base.Path) if err != nil { return f, fmt.Errorf("running ffprobe on %q: %w", base.Path, err) } container, err := ffmpeg.MatchContainer(videoFile.Container, base.Path) if err != nil { return f, fmt.Errorf("matching container for %q: %w", base.Path, err) } // check if there is a funscript file interactive := false if _, err := fs.Lstat(GetFunscriptPath(base.Path)); err == nil { interactive = true } return &file.VideoFile{ BaseFile: base, Format: string(container), VideoCodec: videoFile.VideoCodec, AudioCodec: videoFile.AudioCodec, Width: videoFile.Width, Height: videoFile.Height, Duration: videoFile.FileDuration, FrameRate: videoFile.FrameRate, BitRate: videoFile.Bitrate, Interactive: interactive, }, nil } func (d *Decorator) IsMissingMetadata(ctx context.Context, fs file.FS, f file.File) bool { const ( unsetString = "unset" unsetNumber = -1 ) vf, ok := f.(*file.VideoFile) if !ok { return true } interactive := false if _, err := fs.Lstat(GetFunscriptPath(vf.Base().Path)); err == nil { interactive = true } return vf.VideoCodec == unsetString || vf.AudioCodec == unsetString || vf.Format == unsetString || vf.Width == unsetNumber || vf.Height == unsetNumber || vf.FrameRate == unsetNumber || vf.Duration == unsetNumber || vf.BitRate == unsetNumber || interactive != vf.Interactive }