stash/pkg/manager/generator_preview.go

126 lines
3.3 KiB
Go

package manager
import (
"bufio"
"fmt"
"github.com/stashapp/stash/pkg/ffmpeg"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/utils"
"os"
"path/filepath"
)
type PreviewGenerator struct {
Info *GeneratorInfo
VideoFilename string
ImageFilename string
OutputDirectory string
}
func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, imageFilename string, outputDirectory string) (*PreviewGenerator, error) {
exists, err := utils.FileExists(videoFile.Path)
if !exists {
return nil, err
}
generator, err := newGeneratorInfo(videoFile)
if err != nil {
return nil, err
}
generator.ChunkCount = 12 // 12 segments to the preview
if err := generator.configure(); err != nil {
return nil, err
}
return &PreviewGenerator{
Info: generator,
VideoFilename: videoFilename,
ImageFilename: imageFilename,
OutputDirectory: outputDirectory,
}, nil
}
func (g *PreviewGenerator) Generate() error {
logger.Infof("[generator] generating scene preview for %s", g.Info.VideoFile.Path)
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
if err := g.generateConcatFile(); err != nil {
return err
}
if err := g.generateVideo(&encoder); err != nil {
return err
}
if err := g.generateImage(&encoder); err != nil {
return err
}
return nil
}
func (g *PreviewGenerator) generateConcatFile() error {
f, err := os.Create(g.getConcatFilePath())
if err != nil {
return err
}
defer f.Close()
w := bufio.NewWriter(f)
for i := 0; i < g.Info.ChunkCount; i++ {
num := fmt.Sprintf("%.3d", i)
filename := "preview" + num + ".mp4"
_, _ = w.WriteString(fmt.Sprintf("file '%s'\n", filename))
}
return w.Flush()
}
func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder) error {
outputPath := filepath.Join(g.OutputDirectory, g.VideoFilename)
outputExists, _ := utils.FileExists(outputPath)
if outputExists {
return nil
}
stepSize := int(g.Info.VideoFile.Duration / float64(g.Info.ChunkCount))
for i := 0; i < g.Info.ChunkCount; i++ {
time := i * stepSize
num := fmt.Sprintf("%.3d", i)
filename := "preview" + num + ".mp4"
chunkOutputPath := instance.Paths.Generated.GetTmpPath(filename)
options := ffmpeg.ScenePreviewChunkOptions{
Time: time,
Width: 640,
OutputPath: chunkOutputPath,
}
encoder.ScenePreviewVideoChunk(g.Info.VideoFile, options)
}
videoOutputPath := filepath.Join(g.OutputDirectory, g.VideoFilename)
encoder.ScenePreviewVideoChunkCombine(g.Info.VideoFile, g.getConcatFilePath(), videoOutputPath)
logger.Debug("created video preview: ", videoOutputPath)
return nil
}
func (g *PreviewGenerator) generateImage(encoder *ffmpeg.Encoder) error {
outputPath := filepath.Join(g.OutputDirectory, g.ImageFilename)
outputExists, _ := utils.FileExists(outputPath)
if outputExists {
return nil
}
videoPreviewPath := filepath.Join(g.OutputDirectory, g.VideoFilename)
tmpOutputPath := instance.Paths.Generated.GetTmpPath(g.ImageFilename)
if err := encoder.ScenePreviewVideoToImage(g.Info.VideoFile, 640, videoPreviewPath, tmpOutputPath); err != nil {
return err
}
if err := os.Rename(tmpOutputPath, outputPath); err != nil {
return err
}
logger.Debug("created video preview image: ", outputPath)
return nil
}
func (g *PreviewGenerator) getConcatFilePath() string {
return instance.Paths.Generated.GetTmpPath("files.txt")
}