mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Generate cover image (#376)
* Make mutating metadata ops mutation * Implement scene generate screenshot * Remove fetch policy on metadata mutations * Port UI changes to v2.5 * Set generated image in database
This commit is contained in:
@@ -216,6 +216,55 @@ func (s *singleton) Generate(sprites bool, previews bool, markers bool, transcod
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) GenerateDefaultScreenshot(sceneId string) {
|
||||
s.generateScreenshot(sceneId, nil)
|
||||
}
|
||||
|
||||
func (s *singleton) GenerateScreenshot(sceneId string, at float64) {
|
||||
s.generateScreenshot(sceneId, &at)
|
||||
}
|
||||
|
||||
// generate default screenshot if at is nil
|
||||
func (s *singleton) generateScreenshot(sceneId string, at *float64) {
|
||||
if s.Status.Status != Idle {
|
||||
return
|
||||
}
|
||||
s.Status.SetStatus(Generate)
|
||||
s.Status.indefiniteProgress()
|
||||
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
instance.Paths.Generated.EnsureTmpDir()
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
sceneIdInt, err := strconv.Atoi(sceneId)
|
||||
if err != nil {
|
||||
logger.Errorf("Error parsing scene id %s: %s", sceneId, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
scene, err := qb.Find(sceneIdInt)
|
||||
if err != nil || scene == nil {
|
||||
logger.Errorf("failed to get scene for generate")
|
||||
return
|
||||
}
|
||||
|
||||
task := GenerateScreenshotTask{
|
||||
Scene: *scene,
|
||||
ScreenshotAt: at,
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go task.Start(&wg)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
logger.Infof("Generate finished")
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) AutoTag(performerIds []string, studioIds []string, tagIds []string) {
|
||||
if s.Status.Status != Idle {
|
||||
return
|
||||
|
||||
16
pkg/manager/screenshot.go
Normal file
16
pkg/manager/screenshot.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
)
|
||||
|
||||
func makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int, width int, time float64) {
|
||||
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
||||
options := ffmpeg.ScreenshotOptions{
|
||||
OutputPath: outputPath,
|
||||
Quality: quality,
|
||||
Time: time,
|
||||
Width: width,
|
||||
}
|
||||
encoder.Screenshot(probeResult, options)
|
||||
}
|
||||
84
pkg/manager/task_generate_screenshot.go
Normal file
84
pkg/manager/task_generate_screenshot.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/database"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type GenerateScreenshotTask struct {
|
||||
Scene models.Scene
|
||||
ScreenshotAt *float64
|
||||
}
|
||||
|
||||
func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
scenePath := t.Scene.Path
|
||||
probeResult, err := ffmpeg.NewVideoFile(instance.FFProbePath, scenePath)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var at float64
|
||||
if t.ScreenshotAt == nil {
|
||||
at = float64(probeResult.Duration) * 0.2
|
||||
} else {
|
||||
at = *t.ScreenshotAt
|
||||
}
|
||||
|
||||
checksum := t.Scene.Checksum
|
||||
normalPath := instance.Paths.Scene.GetScreenshotPath(checksum)
|
||||
|
||||
// we'll generate the screenshot, grab the generated data and set it
|
||||
// in the database. We'll use SetSceneScreenshot to set the data
|
||||
// which also generates the thumbnail
|
||||
|
||||
logger.Debugf("Creating screenshot for %s", scenePath)
|
||||
makeScreenshot(*probeResult, normalPath, 2, probeResult.Width, at)
|
||||
|
||||
f, err := os.Open(normalPath)
|
||||
if err != nil {
|
||||
logger.Errorf("Error reading screenshot: %s", err.Error())
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
coverImageData, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
logger.Errorf("Error reading screenshot: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
tx := database.DB.MustBeginTx(ctx, nil)
|
||||
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
updatedTime := time.Now()
|
||||
updatedScene := models.ScenePartial{
|
||||
ID: t.Scene.ID,
|
||||
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
|
||||
}
|
||||
|
||||
updatedScene.Cover = &coverImageData
|
||||
err = SetSceneScreenshot(t.Scene.Checksum, coverImageData)
|
||||
_, err = qb.Update(updatedScene, tx)
|
||||
if err != nil {
|
||||
logger.Errorf("Error setting screenshot: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logger.Errorf("Error setting screenshot: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -177,28 +177,19 @@ func (t *ScanTask) makeScreenshots(probeResult *ffmpeg.VideoFile, checksum strin
|
||||
logger.Infof("Regenerating images for %s", t.FilePath)
|
||||
}
|
||||
|
||||
at := float64(probeResult.Duration) * 0.2
|
||||
|
||||
if !thumbExists {
|
||||
logger.Debugf("Creating thumbnail for %s", t.FilePath)
|
||||
t.makeScreenshot(*probeResult, thumbPath, 5, 320)
|
||||
makeScreenshot(*probeResult, thumbPath, 5, 320, at)
|
||||
}
|
||||
|
||||
if !normalExists {
|
||||
logger.Debugf("Creating screenshot for %s", t.FilePath)
|
||||
t.makeScreenshot(*probeResult, normalPath, 2, probeResult.Width)
|
||||
makeScreenshot(*probeResult, normalPath, 2, probeResult.Width, at)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ScanTask) makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int, width int) {
|
||||
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
||||
options := ffmpeg.ScreenshotOptions{
|
||||
OutputPath: outputPath,
|
||||
Quality: quality,
|
||||
Time: float64(probeResult.Duration) * 0.2,
|
||||
Width: width,
|
||||
}
|
||||
encoder.Screenshot(probeResult, options)
|
||||
}
|
||||
|
||||
func (t *ScanTask) calculateChecksum() (string, error) {
|
||||
logger.Infof("%s not found. Calculating checksum...", t.FilePath)
|
||||
checksum, err := utils.MD5FromFilePath(t.FilePath)
|
||||
|
||||
Reference in New Issue
Block a user