mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04:37 +03:00
Generate content for specific scenes (#672)
* Add UI dialog for scene(s) * Move preview preset to config
This commit is contained in:
@@ -45,6 +45,10 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co
|
||||
config.Set(config.Cache, input.CachePath)
|
||||
}
|
||||
|
||||
if input.PreviewPreset != nil {
|
||||
config.Set(config.PreviewPreset, input.PreviewPreset.String())
|
||||
}
|
||||
|
||||
if input.MaxTranscodeSize != nil {
|
||||
config.Set(config.MaxTranscodeSize, input.MaxTranscodeSize.String())
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func (r *mutationResolver) MetadataExport(ctx context.Context) (string, error) {
|
||||
}
|
||||
|
||||
func (r *mutationResolver) MetadataGenerate(ctx context.Context, input models.GenerateMetadataInput) (string, error) {
|
||||
manager.GetInstance().Generate(input.Sprites, input.Previews, input.PreviewPreset, input.ImagePreviews, input.Markers, input.Transcodes, input.Thumbnails)
|
||||
manager.GetInstance().Generate(input)
|
||||
return "todo", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult {
|
||||
DatabasePath: config.GetDatabasePath(),
|
||||
GeneratedPath: config.GetGeneratedPath(),
|
||||
CachePath: config.GetCachePath(),
|
||||
PreviewPreset: config.GetPreviewPreset(),
|
||||
MaxTranscodeSize: &maxTranscodeSize,
|
||||
MaxStreamingTranscodeSize: &maxStreamingTranscodeSize,
|
||||
ForceMkv: config.GetForceMKV(),
|
||||
|
||||
@@ -27,6 +27,8 @@ const Database = "database"
|
||||
|
||||
const Exclude = "exclude"
|
||||
|
||||
const PreviewPreset = "preview_preset"
|
||||
|
||||
const MaxTranscodeSize = "max_transcode_size"
|
||||
const MaxStreamingTranscodeSize = "max_streaming_transcode_size"
|
||||
|
||||
@@ -160,6 +162,19 @@ func GetExternalHost() string {
|
||||
return viper.GetString(ExternalHost)
|
||||
}
|
||||
|
||||
// GetPreviewPreset returns the preset when generating previews. Defaults to
|
||||
// Slow.
|
||||
func GetPreviewPreset() models.PreviewPreset {
|
||||
ret := viper.GetString(PreviewPreset)
|
||||
|
||||
// default to slow
|
||||
if ret == "" {
|
||||
return models.PreviewPresetSlow
|
||||
}
|
||||
|
||||
return models.PreviewPreset(ret)
|
||||
}
|
||||
|
||||
func GetMaxTranscodeSize() models.StreamingResolutionEnum {
|
||||
ret := viper.GetString(MaxTranscodeSize)
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ package manager
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type PreviewGenerator struct {
|
||||
@@ -21,6 +22,8 @@ type PreviewGenerator struct {
|
||||
GenerateImage bool
|
||||
|
||||
PreviewPreset string
|
||||
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, imageFilename string, outputDirectory string, generateVideo bool, generateImage bool, previewPreset string) (*PreviewGenerator, error) {
|
||||
@@ -88,7 +91,7 @@ func (g *PreviewGenerator) generateConcatFile() error {
|
||||
func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder) error {
|
||||
outputPath := filepath.Join(g.OutputDirectory, g.VideoFilename)
|
||||
outputExists, _ := utils.FileExists(outputPath)
|
||||
if outputExists {
|
||||
if !g.Overwrite && outputExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -116,7 +119,7 @@ func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder) error {
|
||||
func (g *PreviewGenerator) generateImage(encoder *ffmpeg.Encoder) error {
|
||||
outputPath := filepath.Join(g.OutputDirectory, g.ImageFilename)
|
||||
outputExists, _ := utils.FileExists(outputPath)
|
||||
if outputExists {
|
||||
if !g.Overwrite && outputExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,18 @@ package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bmatcuk/doublestar"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
"image"
|
||||
"image/color"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bmatcuk/doublestar"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type SpriteGenerator struct {
|
||||
@@ -22,6 +23,8 @@ type SpriteGenerator struct {
|
||||
VTTOutputPath string
|
||||
Rows int
|
||||
Columns int
|
||||
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func NewSpriteGenerator(videoFile ffmpeg.VideoFile, imageOutputPath string, vttOutputPath string, rows int, cols int) (*SpriteGenerator, error) {
|
||||
@@ -60,7 +63,7 @@ func (g *SpriteGenerator) Generate() error {
|
||||
}
|
||||
|
||||
func (g *SpriteGenerator) generateSpriteImage(encoder *ffmpeg.Encoder) error {
|
||||
if g.imageExists() {
|
||||
if !g.Overwrite && g.imageExists() {
|
||||
return nil
|
||||
}
|
||||
logger.Infof("[generator] generating sprite image for %s", g.Info.VideoFile.Path)
|
||||
@@ -112,7 +115,7 @@ func (g *SpriteGenerator) generateSpriteImage(encoder *ffmpeg.Encoder) error {
|
||||
}
|
||||
|
||||
func (g *SpriteGenerator) generateSpriteVTT(encoder *ffmpeg.Encoder) error {
|
||||
if g.vttExists() {
|
||||
if !g.Overwrite && g.vttExists() {
|
||||
return nil
|
||||
}
|
||||
logger.Infof("[generator] generating sprite vtt for %s", g.Info.VideoFile.Path)
|
||||
|
||||
@@ -167,7 +167,7 @@ func (s *singleton) Export() {
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.PreviewPreset, imagePreviews bool, markers bool, transcodes bool, thumbnails bool) {
|
||||
func (s *singleton) Generate(input models.GenerateMetadataInput) {
|
||||
if s.Status.Status != Idle {
|
||||
return
|
||||
}
|
||||
@@ -176,32 +176,49 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.
|
||||
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
qg := models.NewGalleryQueryBuilder()
|
||||
mqb := models.NewSceneMarkerQueryBuilder()
|
||||
|
||||
//this.job.total = await ObjectionUtils.getCount(Scene);
|
||||
instance.Paths.Generated.EnsureTmpDir()
|
||||
|
||||
preset := string(models.PreviewPresetSlow)
|
||||
if previewPreset != nil && previewPreset.IsValid() {
|
||||
preset = string(*previewPreset)
|
||||
}
|
||||
preset := config.GetPreviewPreset().String()
|
||||
|
||||
galleryIDs := utils.StringSliceToIntSlice(input.GalleryIDs)
|
||||
sceneIDs := utils.StringSliceToIntSlice(input.SceneIDs)
|
||||
markerIDs := utils.StringSliceToIntSlice(input.MarkerIDs)
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
scenes, err := qb.All()
|
||||
var galleries []*models.Gallery
|
||||
var scenes []*models.Scene
|
||||
var err error
|
||||
|
||||
if len(sceneIDs) > 0 {
|
||||
scenes, err = qb.FindMany(sceneIDs)
|
||||
} else {
|
||||
scenes, err = qb.All()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get scenes for generate")
|
||||
return
|
||||
}
|
||||
|
||||
delta := utils.Btoi(sprites) + utils.Btoi(previews) + utils.Btoi(markers) + utils.Btoi(transcodes)
|
||||
delta := utils.Btoi(input.Sprites) + utils.Btoi(input.Previews) + utils.Btoi(input.Markers) + utils.Btoi(input.Transcodes)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
s.Status.Progress = 0
|
||||
lenScenes := len(scenes)
|
||||
total := lenScenes
|
||||
if thumbnails {
|
||||
galleries, err = qg.All()
|
||||
|
||||
var galleries []*models.Gallery
|
||||
if input.Thumbnails {
|
||||
if len(galleryIDs) > 0 {
|
||||
galleries, err = qg.FindMany(galleryIDs)
|
||||
} else {
|
||||
galleries, err = qg.All()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get galleries for generate")
|
||||
return
|
||||
@@ -209,17 +226,31 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.
|
||||
total += len(galleries)
|
||||
}
|
||||
|
||||
var markers []*models.SceneMarker
|
||||
if len(markerIDs) > 0 {
|
||||
markers, err = mqb.FindMany(markerIDs)
|
||||
|
||||
total += len(markers)
|
||||
}
|
||||
|
||||
if s.Status.stopping {
|
||||
logger.Info("Stopping due to user request")
|
||||
return
|
||||
}
|
||||
totalsNeeded := s.neededGenerate(scenes, sprites, previews, imagePreviews, markers, transcodes)
|
||||
|
||||
totalsNeeded := s.neededGenerate(scenes, input)
|
||||
if totalsNeeded == nil {
|
||||
logger.Infof("Taking too long to count content. Skipping...")
|
||||
logger.Infof("Generating content")
|
||||
} else {
|
||||
logger.Infof("Generating %d sprites %d previews %d image previews %d markers %d transcodes", totalsNeeded.sprites, totalsNeeded.previews, totalsNeeded.imagePreviews, totalsNeeded.markers, totalsNeeded.transcodes)
|
||||
}
|
||||
|
||||
overwrite := false
|
||||
if input.Overwrite != nil {
|
||||
overwrite = *input.Overwrite
|
||||
}
|
||||
|
||||
for i, scene := range scenes {
|
||||
s.Status.setProgress(i, total)
|
||||
if s.Status.stopping {
|
||||
@@ -235,34 +266,34 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.
|
||||
wg.Add(delta)
|
||||
|
||||
// Clear the tmp directory for each scene
|
||||
if sprites || previews || markers {
|
||||
if input.Sprites || input.Previews || input.Markers {
|
||||
instance.Paths.Generated.EmptyTmpDir()
|
||||
}
|
||||
|
||||
if sprites {
|
||||
task := GenerateSpriteTask{Scene: *scene}
|
||||
if input.Sprites {
|
||||
task := GenerateSpriteTask{Scene: *scene, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if previews {
|
||||
task := GeneratePreviewTask{Scene: *scene, ImagePreview: imagePreviews, PreviewPreset: preset}
|
||||
if input.Previews {
|
||||
task := GeneratePreviewTask{Scene: *scene, ImagePreview: input.ImagePreviews, PreviewPreset: preset, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if markers {
|
||||
task := GenerateMarkersTask{Scene: *scene}
|
||||
if input.Markers {
|
||||
task := GenerateMarkersTask{Scene: scene, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if transcodes {
|
||||
task := GenerateTranscodeTask{Scene: *scene}
|
||||
if input.Transcodes {
|
||||
task := GenerateTranscodeTask{Scene: *scene, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
if thumbnails {
|
||||
if input.Thumbnails {
|
||||
logger.Infof("Generating thumbnails for the galleries")
|
||||
for i, gallery := range galleries {
|
||||
s.Status.setProgress(lenScenes+i, total)
|
||||
@@ -277,12 +308,30 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
task := GenerateGthumbsTask{Gallery: *gallery}
|
||||
task := GenerateGthumbsTask{Gallery: *gallery, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
for i, marker := range markers {
|
||||
s.Status.setProgress(lenScenes+len(galleries)+i, total)
|
||||
if s.Status.stopping {
|
||||
logger.Info("Stopping due to user request")
|
||||
return
|
||||
}
|
||||
|
||||
if marker == nil {
|
||||
logger.Errorf("nil marker, skipping generate")
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
task := GenerateMarkersTask{Marker: marker, Overwrite: overwrite}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
logger.Infof("Generate finished")
|
||||
}()
|
||||
}
|
||||
@@ -610,7 +659,7 @@ type totalsGenerate struct {
|
||||
transcodes int64
|
||||
}
|
||||
|
||||
func (s *singleton) neededGenerate(scenes []*models.Scene, sprites, previews, imagePreviews, markers, transcodes bool) *totalsGenerate {
|
||||
func (s *singleton) neededGenerate(scenes []*models.Scene, input models.GenerateMetadataInput) *totalsGenerate {
|
||||
|
||||
var totals totalsGenerate
|
||||
const timeout = 90 * time.Second
|
||||
@@ -624,33 +673,38 @@ func (s *singleton) neededGenerate(scenes []*models.Scene, sprites, previews, im
|
||||
chTimeout <- struct{}{}
|
||||
}()
|
||||
|
||||
overwrite := false
|
||||
if input.Overwrite != nil {
|
||||
overwrite = *input.Overwrite
|
||||
}
|
||||
|
||||
logger.Infof("Counting content to generate...")
|
||||
for _, scene := range scenes {
|
||||
if scene != nil {
|
||||
if sprites {
|
||||
if input.Sprites {
|
||||
task := GenerateSpriteTask{Scene: *scene}
|
||||
if !task.doesSpriteExist(task.Scene.Checksum) {
|
||||
if overwrite || !task.doesSpriteExist(task.Scene.Checksum) {
|
||||
totals.sprites++
|
||||
}
|
||||
}
|
||||
|
||||
if previews {
|
||||
task := GeneratePreviewTask{Scene: *scene, ImagePreview: imagePreviews}
|
||||
if !task.doesVideoPreviewExist(task.Scene.Checksum) {
|
||||
if input.Previews {
|
||||
task := GeneratePreviewTask{Scene: *scene, ImagePreview: input.ImagePreviews}
|
||||
if overwrite || !task.doesVideoPreviewExist(task.Scene.Checksum) {
|
||||
totals.previews++
|
||||
}
|
||||
if imagePreviews && !task.doesImagePreviewExist(task.Scene.Checksum) {
|
||||
if input.ImagePreviews && (overwrite || !task.doesImagePreviewExist(task.Scene.Checksum)) {
|
||||
totals.imagePreviews++
|
||||
}
|
||||
}
|
||||
|
||||
if markers {
|
||||
task := GenerateMarkersTask{Scene: *scene}
|
||||
if input.Markers {
|
||||
task := GenerateMarkersTask{Scene: scene, Overwrite: overwrite}
|
||||
totals.markers += int64(task.isMarkerNeeded())
|
||||
|
||||
}
|
||||
if transcodes {
|
||||
task := GenerateTranscodeTask{Scene: *scene}
|
||||
|
||||
if input.Transcodes {
|
||||
task := GenerateTranscodeTask{Scene: *scene, Overwrite: overwrite}
|
||||
if task.isTranscodeNeeded() {
|
||||
totals.transcodes++
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/manager/paths"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GenerateGthumbsTask struct {
|
||||
Gallery models.Gallery
|
||||
Gallery models.Gallery
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func (t *GenerateGthumbsTask) Start(wg *sync.WaitGroup) {
|
||||
@@ -19,7 +21,7 @@ func (t *GenerateGthumbsTask) Start(wg *sync.WaitGroup) {
|
||||
for i := 0; i < count; i++ {
|
||||
thumbPath := paths.GetGthumbPath(t.Gallery.Checksum, i, models.DefaultGthumbWidth)
|
||||
exists, _ := utils.FileExists(thumbPath)
|
||||
if exists {
|
||||
if !t.Overwrite && exists {
|
||||
continue
|
||||
}
|
||||
data := t.Gallery.GetThumbnail(i, models.DefaultGthumbWidth)
|
||||
|
||||
@@ -13,12 +13,37 @@ import (
|
||||
)
|
||||
|
||||
type GenerateMarkersTask struct {
|
||||
Scene models.Scene
|
||||
Scene *models.Scene
|
||||
Marker *models.SceneMarker
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if t.Scene != nil {
|
||||
t.generateSceneMarkers()
|
||||
}
|
||||
|
||||
if t.Marker != nil {
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
scene, err := qb.Find(int(t.Marker.SceneID.Int64))
|
||||
if err != nil {
|
||||
logger.Errorf("error finding scene for marker: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
||||
if err != nil {
|
||||
logger.Errorf("error reading video file: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
t.generateMarker(videoFile, scene, t.Marker)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GenerateMarkersTask) generateSceneMarkers() {
|
||||
qb := models.NewSceneMarkerQueryBuilder()
|
||||
sceneMarkers, _ := qb.FindBySceneID(t.Scene.ID, nil)
|
||||
if len(sceneMarkers) == 0 {
|
||||
@@ -35,43 +60,49 @@ func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) {
|
||||
markersFolder := filepath.Join(instance.Paths.Generated.Markers, t.Scene.Checksum)
|
||||
_ = utils.EnsureDir(markersFolder)
|
||||
|
||||
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
||||
for i, sceneMarker := range sceneMarkers {
|
||||
index := i + 1
|
||||
logger.Progressf("[generator] <%s> scene marker %d of %d", t.Scene.Checksum, index, len(sceneMarkers))
|
||||
|
||||
seconds := int(sceneMarker.Seconds)
|
||||
baseFilename := strconv.Itoa(seconds)
|
||||
videoFilename := baseFilename + ".mp4"
|
||||
imageFilename := baseFilename + ".webp"
|
||||
videoPath := instance.Paths.SceneMarkers.GetStreamPath(t.Scene.Checksum, seconds)
|
||||
imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(t.Scene.Checksum, seconds)
|
||||
videoExists, _ := utils.FileExists(videoPath)
|
||||
imageExists, _ := utils.FileExists(imagePath)
|
||||
t.generateMarker(videoFile, t.Scene, sceneMarker)
|
||||
}
|
||||
}
|
||||
|
||||
options := ffmpeg.SceneMarkerOptions{
|
||||
ScenePath: t.Scene.Path,
|
||||
Seconds: seconds,
|
||||
Width: 640,
|
||||
}
|
||||
if !videoExists {
|
||||
options.OutputPath = instance.Paths.Generated.GetTmpPath(videoFilename) // tmp output in case the process ends abruptly
|
||||
if err := encoder.SceneMarkerVideo(*videoFile, options); err != nil {
|
||||
logger.Errorf("[generator] failed to generate marker video: %s", err)
|
||||
} else {
|
||||
_ = os.Rename(options.OutputPath, videoPath)
|
||||
logger.Debug("created marker video: ", videoPath)
|
||||
}
|
||||
}
|
||||
func (t *GenerateMarkersTask) generateMarker(videoFile *ffmpeg.VideoFile, scene *models.Scene, sceneMarker *models.SceneMarker) {
|
||||
seconds := int(sceneMarker.Seconds)
|
||||
baseFilename := strconv.Itoa(seconds)
|
||||
videoFilename := baseFilename + ".mp4"
|
||||
imageFilename := baseFilename + ".webp"
|
||||
videoPath := instance.Paths.SceneMarkers.GetStreamPath(scene.Checksum, seconds)
|
||||
imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(scene.Checksum, seconds)
|
||||
videoExists, _ := utils.FileExists(videoPath)
|
||||
imageExists, _ := utils.FileExists(imagePath)
|
||||
|
||||
if !imageExists {
|
||||
options.OutputPath = instance.Paths.Generated.GetTmpPath(imageFilename) // tmp output in case the process ends abruptly
|
||||
if err := encoder.SceneMarkerImage(*videoFile, options); err != nil {
|
||||
logger.Errorf("[generator] failed to generate marker image: %s", err)
|
||||
} else {
|
||||
_ = os.Rename(options.OutputPath, imagePath)
|
||||
logger.Debug("created marker image: ", videoPath)
|
||||
}
|
||||
options := ffmpeg.SceneMarkerOptions{
|
||||
ScenePath: scene.Path,
|
||||
Seconds: seconds,
|
||||
Width: 640,
|
||||
}
|
||||
|
||||
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
||||
|
||||
if t.Overwrite || !videoExists {
|
||||
options.OutputPath = instance.Paths.Generated.GetTmpPath(videoFilename) // tmp output in case the process ends abruptly
|
||||
if err := encoder.SceneMarkerVideo(*videoFile, options); err != nil {
|
||||
logger.Errorf("[generator] failed to generate marker video: %s", err)
|
||||
} else {
|
||||
_ = os.Rename(options.OutputPath, videoPath)
|
||||
logger.Debug("created marker video: ", videoPath)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Overwrite || !imageExists {
|
||||
options.OutputPath = instance.Paths.Generated.GetTmpPath(imageFilename) // tmp output in case the process ends abruptly
|
||||
if err := encoder.SceneMarkerImage(*videoFile, options); err != nil {
|
||||
logger.Errorf("[generator] failed to generate marker image: %s", err)
|
||||
} else {
|
||||
_ = os.Rename(options.OutputPath, imagePath)
|
||||
logger.Debug("created marker image: ", videoPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,10 +123,11 @@ func (t *GenerateMarkersTask) isMarkerNeeded() int {
|
||||
videoExists, _ := utils.FileExists(videoPath)
|
||||
imageExists, _ := utils.FileExists(imagePath)
|
||||
|
||||
if (!videoExists) || (!imageExists) {
|
||||
if t.Overwrite || !videoExists || !imageExists {
|
||||
markers++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return markers
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GeneratePreviewTask struct {
|
||||
Scene models.Scene
|
||||
ImagePreview bool
|
||||
PreviewPreset string
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) {
|
||||
@@ -20,7 +22,7 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) {
|
||||
videoFilename := t.videoFilename()
|
||||
imageFilename := t.imageFilename()
|
||||
videoExists := t.doesVideoPreviewExist(t.Scene.Checksum)
|
||||
if (!t.ImagePreview || t.doesImagePreviewExist(t.Scene.Checksum)) && videoExists {
|
||||
if !t.Overwrite && ((!t.ImagePreview || t.doesImagePreviewExist(t.Scene.Checksum)) && videoExists) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,11 +32,12 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
generator, err := NewPreviewGenerator(*videoFile, videoFilename, imageFilename, instance.Paths.Generated.Screenshots, !videoExists, t.ImagePreview, t.PreviewPreset)
|
||||
generator, err := NewPreviewGenerator(*videoFile, videoFilename, imageFilename, instance.Paths.Generated.Screenshots, true, t.ImagePreview, t.PreviewPreset)
|
||||
if err != nil {
|
||||
logger.Errorf("error creating preview generator: %s", err.Error())
|
||||
return
|
||||
}
|
||||
generator.Overwrite = t.Overwrite
|
||||
|
||||
if err := generator.Generate(); err != nil {
|
||||
logger.Errorf("error generating preview: %s", err.Error())
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GenerateSpriteTask struct {
|
||||
Scene models.Scene
|
||||
Scene models.Scene
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if t.doesSpriteExist(t.Scene.Checksum) {
|
||||
if t.doesSpriteExist(t.Scene.Checksum) && !t.Overwrite {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,6 +30,7 @@ func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) {
|
||||
imagePath := instance.Paths.Scene.GetSpriteImageFilePath(t.Scene.Checksum)
|
||||
vttPath := instance.Paths.Scene.GetSpriteVttFilePath(t.Scene.Checksum)
|
||||
generator, err := NewSpriteGenerator(*videoFile, imagePath, vttPath, 9, 9)
|
||||
generator.Overwrite = t.Overwrite
|
||||
if err != nil {
|
||||
logger.Errorf("error creating sprite generator: %s", err.Error())
|
||||
return
|
||||
|
||||
@@ -11,14 +11,15 @@ import (
|
||||
)
|
||||
|
||||
type GenerateTranscodeTask struct {
|
||||
Scene models.Scene
|
||||
Scene models.Scene
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
hasTranscode, _ := HasTranscode(&t.Scene)
|
||||
if hasTranscode {
|
||||
if !t.Overwrite && hasTranscode {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ func (t *GenerateTranscodeTask) isTranscodeNeeded() bool {
|
||||
}
|
||||
|
||||
hasTranscode, _ := HasTranscode(&t.Scene)
|
||||
if hasTranscode {
|
||||
if !t.Overwrite && hasTranscode {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
@@ -82,6 +83,24 @@ func (qb *GalleryQueryBuilder) Find(id int) (*Gallery, error) {
|
||||
return qb.queryGallery(query, args, nil)
|
||||
}
|
||||
|
||||
func (qb *GalleryQueryBuilder) FindMany(ids []int) ([]*Gallery, error) {
|
||||
var galleries []*Gallery
|
||||
for _, id := range ids {
|
||||
gallery, err := qb.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if gallery == nil {
|
||||
return nil, fmt.Errorf("gallery with id %d not found", id)
|
||||
}
|
||||
|
||||
galleries = append(galleries, gallery)
|
||||
}
|
||||
|
||||
return galleries, nil
|
||||
}
|
||||
|
||||
func (qb *GalleryQueryBuilder) FindByChecksum(checksum string, tx *sqlx.Tx) (*Gallery, error) {
|
||||
query := "SELECT * FROM galleries WHERE checksum = ? LIMIT 1"
|
||||
args := []interface{}{checksum}
|
||||
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -147,6 +148,24 @@ func (qb *SceneQueryBuilder) Find(id int) (*Scene, error) {
|
||||
return qb.find(id, nil)
|
||||
}
|
||||
|
||||
func (qb *SceneQueryBuilder) FindMany(ids []int) ([]*Scene, error) {
|
||||
var scenes []*Scene
|
||||
for _, id := range ids {
|
||||
scene, err := qb.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if scene == nil {
|
||||
return nil, fmt.Errorf("scene with id %d not found", id)
|
||||
}
|
||||
|
||||
scenes = append(scenes, scene)
|
||||
}
|
||||
|
||||
return scenes, nil
|
||||
}
|
||||
|
||||
func (qb *SceneQueryBuilder) find(id int, tx *sqlx.Tx) (*Scene, error) {
|
||||
query := selectAll(sceneTable) + "WHERE id = ? LIMIT 1"
|
||||
args := []interface{}{id}
|
||||
|
||||
@@ -2,9 +2,11 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stashapp/stash/pkg/database"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const countSceneMarkersForTagQuery = `
|
||||
@@ -72,6 +74,24 @@ func (qb *SceneMarkerQueryBuilder) Find(id int) (*SceneMarker, error) {
|
||||
return results[0], nil
|
||||
}
|
||||
|
||||
func (qb *SceneMarkerQueryBuilder) FindMany(ids []int) ([]*SceneMarker, error) {
|
||||
var markers []*SceneMarker
|
||||
for _, id := range ids {
|
||||
marker, err := qb.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if marker == nil {
|
||||
return nil, fmt.Errorf("scene marker with id %d not found", id)
|
||||
}
|
||||
|
||||
markers = append(markers, marker)
|
||||
}
|
||||
|
||||
return markers, nil
|
||||
}
|
||||
|
||||
func (qb *SceneMarkerQueryBuilder) FindBySceneID(sceneID int, tx *sqlx.Tx) ([]*SceneMarker, error) {
|
||||
query := `
|
||||
SELECT scene_markers.* FROM scene_markers
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package utils
|
||||
|
||||
import "strconv"
|
||||
|
||||
// https://gobyexample.com/collection-functions
|
||||
|
||||
func StrIndex(vs []string, t string) int {
|
||||
@@ -32,3 +34,15 @@ func StrMap(vs []string, f func(string) string) []string {
|
||||
}
|
||||
return vsm
|
||||
}
|
||||
|
||||
// StringSliceToIntSlice converts a slice of strings to a slice of ints. If any
|
||||
// values cannot be parsed, then they are inserted into the returned slice as
|
||||
// 0.
|
||||
func StringSliceToIntSlice(ss []string) []int {
|
||||
ret := make([]int, len(ss))
|
||||
for i, v := range ss {
|
||||
ret[i], _ = strconv.Atoi(v)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user