Errcheck phase 1 (#1715)

* Avoid redundant logging in migrations

Return the error and let the caller handle the logging of the error if
needed.

While here, defer m.Close() to the function boundary.

* Treat errors as values

Use %v rather than %s and pass the errors directly.

* Generate a wrapped error on stat-failure

* Log 3 unchecked errors

Rather than ignore errors, log them at
the WARNING log level.

The server has been functioning without these, so assume they are not at
the ERROR level.

* Propagate errors upward

Failure in path generation was ignored. Propagate the errors upward the
call stack, so it can be handled at the level of orchestration.

* Warn on errors

Log errors rather than quenching them.

Errors are logged at the Warn-level for now.

* Check error when creating test databases

Use the builtin log package and stop the program fatally on error.

* Add warnings to uncheck task errors

Focus on the task system in a single commit, logging unchecked
errors as warnings.

* Warn-on-error in API routes

Look through the API routes, and make sure errors are being logged if
they occur. Prefer the Warn-log-level because none of these has proven
to be fatal in the system up until now.

* Propagate error when adding Util API

* Propagate error on adding util API

* Return unhandled error

* JS log API: propagate and log errors

* JS Plugins: log GQL addition failures.

* Warn on failure to write to stdin

* Warn on failure to stop task

* Wrap viper.BindEnv

The current viper code only errors if no name is provided, so it should
never fail. Rewrite the code flow to factor through a panic-function.

This removes error warnings from this part of the code.

* Log errors in concurrency test

If we can't initialize the configuration, treat the test as a failure.

* Warn on errors in configuration code

* Plug an unchecked error in gallery zip walking

* Warn on screenshot serving failure

* Warn on encoder screenshot failure

* Warn on errors in path-handling code

* Undo the errcheck on configurations for now.

* Use one-line initializers where applicable

rather than using

  err := f()
  if err!= nil { ..

prefer the shorter

  if err := f(); err != nil { ..

If f() isn't too long of a name, or wraps a function with a body.
This commit is contained in:
SmallCoccinelle
2021-09-21 01:34:25 +02:00
committed by GitHub
parent af6232ec97
commit 87709fd018
33 changed files with 296 additions and 107 deletions

View File

@@ -289,7 +289,9 @@ func (r *mutationResolver) ConfigureDlna(ctx context.Context, input models.Confi
if !*input.Enabled && dlnaService.IsRunning() { if !*input.Enabled && dlnaService.IsRunning() {
dlnaService.Stop(nil) dlnaService.Stop(nil)
} else if *input.Enabled && !dlnaService.IsRunning() { } else if *input.Enabled && !dlnaService.IsRunning() {
dlnaService.Start(nil) if err := dlnaService.Start(nil); err != nil {
logger.Warnf("error starting DLNA service: %v", err)
}
} }
} }

View File

@@ -17,7 +17,7 @@ func (r *mutationResolver) RunPluginTask(ctx context.Context, pluginID string, t
func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) { func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) {
err := manager.GetInstance().PluginCache.LoadPlugins() err := manager.GetInstance().PluginCache.LoadPlugins()
if err != nil { if err != nil {
logger.Errorf("Error reading plugin configs: %s", err.Error()) logger.Errorf("Error reading plugin configs: %v", err)
} }
return true, nil return true, nil

View File

@@ -7,6 +7,7 @@ import (
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/image" "github.com/stashapp/stash/pkg/image"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
@@ -59,7 +60,7 @@ func ImageCtx(next http.Handler) http.Handler {
imageID, _ := strconv.Atoi(imageIdentifierQueryParam) imageID, _ := strconv.Atoi(imageIdentifierQueryParam)
var image *models.Image var image *models.Image
manager.GetInstance().TxnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { readTxnErr := manager.GetInstance().TxnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
qb := repo.Image() qb := repo.Image()
if imageID == 0 { if imageID == 0 {
image, _ = qb.FindByChecksum(imageIdentifierQueryParam) image, _ = qb.FindByChecksum(imageIdentifierQueryParam)
@@ -69,6 +70,9 @@ func ImageCtx(next http.Handler) http.Handler {
return nil return nil
}) })
if readTxnErr != nil {
logger.Warnf("read transaction failure while trying to read image by id: %v", readTxnErr)
}
if image == nil { if image == nil {
http.Error(w, http.StatusText(404), 404) http.Error(w, http.StatusText(404), 404)

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
@@ -32,17 +33,22 @@ func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
defaultParam := r.URL.Query().Get("default") defaultParam := r.URL.Query().Get("default")
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { err := rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
image, _ = repo.Movie().GetFrontImage(movie.ID) image, _ = repo.Movie().GetFrontImage(movie.ID)
return nil return nil
}) })
if err != nil {
logger.Warnf("read transaction error while getting front image: %v", err)
}
} }
if len(image) == 0 { if len(image) == 0 {
_, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage) _, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage)
} }
utils.ServeImage(image, w, r) if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving front image: %v", err)
}
} }
func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) { func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
@@ -50,17 +56,22 @@ func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
defaultParam := r.URL.Query().Get("default") defaultParam := r.URL.Query().Get("default")
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { err := rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
image, _ = repo.Movie().GetBackImage(movie.ID) image, _ = repo.Movie().GetBackImage(movie.ID)
return nil return nil
}) })
if err != nil {
logger.Warnf("read transaction error on fetch back image: %v", err)
}
} }
if len(image) == 0 { if len(image) == 0 {
_, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage) _, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage)
} }
utils.ServeImage(image, w, r) if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error while serving image: %v", err)
}
} }
func MovieCtx(next http.Handler) http.Handler { func MovieCtx(next http.Handler) http.Handler {

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/manager/config"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
@@ -33,17 +34,22 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { readTxnErr := rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
image, _ = repo.Performer().GetImage(performer.ID) image, _ = repo.Performer().GetImage(performer.ID)
return nil return nil
}) })
if readTxnErr != nil {
logger.Warnf("couldn't execute getting a performer image from read transaction: %v", readTxnErr)
}
} }
if len(image) == 0 || defaultParam == "true" { if len(image) == 0 || defaultParam == "true" {
image, _ = getRandomPerformerImageUsingName(performer.Name.String, performer.Gender.String, config.GetInstance().GetCustomPerformerImageLocation()) image, _ = getRandomPerformerImageUsingName(performer.Name.String, performer.Gender.String, config.GetInstance().GetCustomPerformerImageLocation())
} }
utils.ServeImage(image, w, r) if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving image: %v", err)
}
} }
func PerformerCtx(next http.Handler) http.Handler { func PerformerCtx(next http.Handler) http.Handler {

View File

@@ -59,7 +59,7 @@ func getSceneFileContainer(scene *models.Scene) ffmpeg.Container {
// shouldn't happen, fallback to ffprobe // shouldn't happen, fallback to ffprobe
tmpVideoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false) tmpVideoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
if err != nil { if err != nil {
logger.Errorf("[transcode] error reading video file: %s", err.Error()) logger.Errorf("[transcode] error reading video file: %v", err)
return ffmpeg.Container("") return ffmpeg.Container("")
} }
@@ -85,7 +85,9 @@ func (rs sceneRoutes) StreamMKV(w http.ResponseWriter, r *http.Request) {
container := getSceneFileContainer(scene) container := getSceneFileContainer(scene)
if container != ffmpeg.Matroska { if container != ffmpeg.Matroska {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("not an mkv file")) if _, err := w.Write([]byte("not an mkv file")); err != nil {
logger.Warnf("[stream] error writing to stream: %v", err)
}
return return
} }
@@ -105,7 +107,7 @@ func (rs sceneRoutes) StreamHLS(w http.ResponseWriter, r *http.Request) {
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false) videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
if err != nil { if err != nil {
logger.Errorf("[stream] error reading video file: %s", err.Error()) logger.Errorf("[stream] error reading video file: %v", err)
return return
} }
@@ -126,7 +128,9 @@ func (rs sceneRoutes) StreamHLS(w http.ResponseWriter, r *http.Request) {
rangeStr := requestByteRange.ToHeaderValue(int64(str.Len())) rangeStr := requestByteRange.ToHeaderValue(int64(str.Len()))
w.Header().Set("Content-Range", rangeStr) w.Header().Set("Content-Range", rangeStr)
w.Write(ret) if n, err := w.Write(ret); err != nil {
logger.Warnf("[stream] error writing stream (wrote %v bytes): %v", n, err)
}
} }
func (rs sceneRoutes) StreamTS(w http.ResponseWriter, r *http.Request) { func (rs sceneRoutes) StreamTS(w http.ResponseWriter, r *http.Request) {
@@ -141,12 +145,15 @@ func (rs sceneRoutes) streamTranscode(w http.ResponseWriter, r *http.Request, vi
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false) videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
if err != nil { if err != nil {
logger.Errorf("[stream] error reading video file: %s", err.Error()) logger.Errorf("[stream] error reading video file: %v", err)
return return
} }
// start stream based on query param, if provided // start stream based on query param, if provided
r.ParseForm() if err = r.ParseForm(); err != nil {
logger.Warnf("[stream] error parsing query form: %v", err)
}
startTime := r.Form.Get("start") startTime := r.Form.Get("start")
requestedSize := r.Form.Get("resolution") requestedSize := r.Form.Get("resolution")
@@ -168,9 +175,11 @@ func (rs sceneRoutes) streamTranscode(w http.ResponseWriter, r *http.Request, vi
stream, err = encoder.GetTranscodeStream(options) stream, err = encoder.GetTranscodeStream(options)
if err != nil { if err != nil {
logger.Errorf("[stream] error transcoding video file: %s", err.Error()) logger.Errorf("[stream] error transcoding video file: %v", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) if _, err := w.Write([]byte(err.Error())); err != nil {
logger.Warnf("[stream] error writing response: %v", err)
}
return return
} }
@@ -355,7 +364,7 @@ func SceneCtx(next http.Handler) http.Handler {
sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam) sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam)
var scene *models.Scene var scene *models.Scene
manager.GetInstance().TxnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { readTxnErr := manager.GetInstance().TxnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
qb := repo.Scene() qb := repo.Scene()
if sceneID == 0 { if sceneID == 0 {
// determine checksum/os by the length of the query param // determine checksum/os by the length of the query param
@@ -370,6 +379,9 @@ func SceneCtx(next http.Handler) http.Handler {
return nil return nil
}) })
if readTxnErr != nil {
logger.Warnf("error executing SceneCtx transaction: %v", readTxnErr)
}
if scene == nil { if scene == nil {
http.Error(w, http.StatusText(404), 404) http.Error(w, http.StatusText(404), 404)

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
@@ -32,17 +33,22 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { err := rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
image, _ = repo.Studio().GetImage(studio.ID) image, _ = repo.Studio().GetImage(studio.ID)
return nil return nil
}) })
if err != nil {
logger.Warnf("read transaction error while fetching studio image: %v", err)
}
} }
if len(image) == 0 { if len(image) == 0 {
_, image, _ = utils.ProcessBase64Image(models.DefaultStudioImage) _, image, _ = utils.ProcessBase64Image(models.DefaultStudioImage)
} }
utils.ServeImage(image, w, r) if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving studio image: %v", err)
}
} }
func StudioCtx(next http.Handler) http.Handler { func StudioCtx(next http.Handler) http.Handler {

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
@@ -32,17 +33,22 @@ func (rs tagRoutes) Image(w http.ResponseWriter, r *http.Request) {
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { err := rs.txnManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
image, _ = repo.Tag().GetImage(tag.ID) image, _ = repo.Tag().GetImage(tag.ID)
return nil return nil
}) })
if err != nil {
logger.Warnf("read transaction error while getting tag image: %v", err)
}
} }
if len(image) == 0 { if len(image) == 0 {
image = models.DefaultTagImage image = models.DefaultTagImage
} }
utils.ServeImage(image, w, r) if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving tag image: %v", err)
}
} }
func TagCtx(next http.Handler) http.Handler { func TagCtx(next http.Handler) http.Handler {

View File

@@ -232,6 +232,7 @@ func RunMigrations() error {
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
} }
defer m.Close()
databaseSchemaVersion, _, _ = m.Version() databaseSchemaVersion, _, _ = m.Version()
stepNumber := appSchemaVersion - databaseSchemaVersion stepNumber := appSchemaVersion - databaseSchemaVersion
@@ -240,14 +241,10 @@ func RunMigrations() error {
err = m.Steps(int(stepNumber)) err = m.Steps(int(stepNumber))
if err != nil { if err != nil {
// migration failed // migration failed
logger.Errorf("Error migrating database: %s", err.Error())
m.Close()
return err return err
} }
} }
m.Close()
// re-initialise the database // re-initialise the database
Initialize(dbPath) Initialize(dbPath)
@@ -255,7 +252,7 @@ func RunMigrations() error {
logger.Info("Performing vacuum on database") logger.Info("Performing vacuum on database")
_, err = DB.Exec("VACUUM") _, err = DB.Exec("VACUUM")
if err != nil { if err != nil {
logger.Warnf("error while performing post-migration vacuum: %s", err.Error()) logger.Warnf("error while performing post-migration vacuum: %v", err)
} }
return nil return nil

View File

@@ -251,7 +251,9 @@ func (s *Service) Stop(duration *time.Duration) {
if s.startTimer == nil { if s.startTimer == nil {
s.startTimer = time.AfterFunc(*duration, func() { s.startTimer = time.AfterFunc(*duration, func() {
s.Start(nil) if err := s.Start(nil); err != nil {
logger.Warnf("error restarting DLNA server: %v", err)
}
}) })
t := time.Now().Add(*duration) t := time.Now().Add(*duration)
s.startTime = &t s.startTime = &t

View File

@@ -273,8 +273,9 @@ func parse(filePath string, probeJSON *FFProbeJSON, stripExt bool) (*VideoFile,
result.Duration = math.Round(duration*100) / 100 result.Duration = math.Round(duration*100) / 100
fileStat, err := os.Stat(filePath) fileStat, err := os.Stat(filePath)
if err != nil { if err != nil {
logger.Errorf("Error statting file <%s>: %s", filePath, err.Error()) statErr := fmt.Errorf("error statting file <%s>: %w", filePath, err)
return nil, err logger.Errorf("%v", statErr)
return nil, statErr
} }
result.Size = fileStat.Size() result.Size = fileStat.Size()
result.StartTime, _ = strconv.ParseFloat(probeJSON.Format.StartTime, 64) result.StartTime, _ = strconv.ParseFloat(probeJSON.Format.StartTime, 64)

View File

@@ -224,7 +224,11 @@ func (e *Encoder) stream(probeResult VideoFile, options TranscodeStreamOptions)
} }
registerRunningEncoder(probeResult.Path, cmd.Process) registerRunningEncoder(probeResult.Path, cmd.Process)
go waitAndDeregister(probeResult.Path, cmd) go func() {
if err := waitAndDeregister(probeResult.Path, cmd); err != nil {
logger.Warnf("Error while deregistering ffmpeg stream: %v", err)
}
}()
// stderr must be consumed or the process deadlocks // stderr must be consumed or the process deadlocks
go func() { go func() {

View File

@@ -16,6 +16,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager/paths" "github.com/stashapp/stash/pkg/manager/paths"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
@@ -453,7 +454,10 @@ func (i *Instance) GetStashBoxes() []*models.StashBox {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
var boxes []*models.StashBox var boxes []*models.StashBox
viper.UnmarshalKey(StashBoxes, &boxes) if err := viper.UnmarshalKey(StashBoxes, &boxes); err != nil {
logger.Warnf("error in unmarshalkey: %v", err)
}
return boxes return boxes
} }
@@ -801,7 +805,9 @@ func (i *Instance) SetCSS(css string) {
buf := []byte(css) buf := []byte(css)
ioutil.WriteFile(fn, buf, 0777) if err := ioutil.WriteFile(fn, buf, 0777); err != nil {
logger.Warnf("error while writing %v bytes to %v: %v", len(buf), fn, err)
}
} }
func (i *Instance) GetCSSEnabled() bool { func (i *Instance) GetCSSEnabled() bool {

View File

@@ -13,9 +13,9 @@ func TestConcurrentConfigAccess(t *testing.T) {
//const loops = 1000 //const loops = 1000
const loops = 200 const loops = 200
var wg sync.WaitGroup var wg sync.WaitGroup
for t := 0; t < workers; t++ { for k := 0; k < workers; k++ {
wg.Add(1) wg.Add(1)
go func() { go func(wk int) {
for l := 0; l < loops; l++ { for l := 0; l < loops; l++ {
i.SetInitialConfig() i.SetInitialConfig()
@@ -93,7 +93,7 @@ func TestConcurrentConfigAccess(t *testing.T) {
i.Set(MaxUploadSize, i.GetMaxUploadSize()) i.Set(MaxUploadSize, i.GetMaxUploadSize())
} }
wg.Done() wg.Done()
}() }(k)
} }
wg.Wait() wg.Wait()

View File

@@ -113,15 +113,21 @@ func initFlags() flagStruct {
func initEnvs() { func initEnvs() {
viper.SetEnvPrefix("stash") // will be uppercased automatically viper.SetEnvPrefix("stash") // will be uppercased automatically
viper.BindEnv("host") // STASH_HOST bindEnv("host") // STASH_HOST
viper.BindEnv("port") // STASH_PORT bindEnv("port") // STASH_PORT
viper.BindEnv("external_host") // STASH_EXTERNAL_HOST bindEnv("external_host") // STASH_EXTERNAL_HOST
viper.BindEnv("generated") // STASH_GENERATED bindEnv("generated") // STASH_GENERATED
viper.BindEnv("metadata") // STASH_METADATA bindEnv("metadata") // STASH_METADATA
viper.BindEnv("cache") // STASH_CACHE bindEnv("cache") // STASH_CACHE
// only set stash config flag if not already set // only set stash config flag if not already set
if instance.GetStashPaths() == nil { if instance.GetStashPaths() == nil {
viper.BindEnv("stash") // STASH_STASH bindEnv("stash") // STASH_STASH
}
}
func bindEnv(key string) {
if err := viper.BindEnv(key); err != nil {
panic(fmt.Sprintf("unable to set environment key (%v): %v", key, err))
} }
} }

View File

@@ -61,10 +61,13 @@ func walkGalleryZip(path string, walkFunc func(file *zip.File) error) error {
func countImagesInZip(path string) int { func countImagesInZip(path string) int {
ret := 0 ret := 0
walkGalleryZip(path, func(file *zip.File) error { err := walkGalleryZip(path, func(file *zip.File) error {
ret++ ret++
return nil return nil
}) })
if err != nil {
logger.Warnf("Error while walking gallery zip: %v", err)
}
return ret return ret
} }

View File

@@ -163,7 +163,9 @@ func (s *singleton) Generate(ctx context.Context, input models.GenerateMetadataI
if err := s.validateFFMPEG(); err != nil { if err := s.validateFFMPEG(); err != nil {
return 0, err return 0, err
} }
instance.Paths.Generated.EnsureTmpDir() if err := instance.Paths.Generated.EnsureTmpDir(); err != nil {
logger.Warnf("could not generate temporary directory: %v", err)
}
sceneIDs, err := utils.StringSliceToIntSlice(input.SceneIDs) sceneIDs, err := utils.StringSliceToIntSlice(input.SceneIDs)
if err != nil { if err != nil {
@@ -250,14 +252,18 @@ func (s *singleton) Generate(ctx context.Context, input models.GenerateMetadataI
// Start measuring how long the generate has taken. (consider moving this up) // Start measuring how long the generate has taken. (consider moving this up)
start := time.Now() start := time.Now()
instance.Paths.Generated.EnsureTmpDir() if err = instance.Paths.Generated.EnsureTmpDir(); err != nil {
logger.Warnf("could not create temprary directory: %v", err)
}
for _, scene := range scenes { for _, scene := range scenes {
progress.Increment() progress.Increment()
if job.IsCancelled(ctx) { if job.IsCancelled(ctx) {
logger.Info("Stopping due to user request") logger.Info("Stopping due to user request")
wg.Wait() wg.Wait()
instance.Paths.Generated.EmptyTmpDir() if err := instance.Paths.Generated.EmptyTmpDir(); err != nil {
logger.Warnf("failure emptying temporary directory: %v", err)
}
return return
} }
@@ -340,7 +346,9 @@ func (s *singleton) Generate(ctx context.Context, input models.GenerateMetadataI
if job.IsCancelled(ctx) { if job.IsCancelled(ctx) {
logger.Info("Stopping due to user request") logger.Info("Stopping due to user request")
wg.Wait() wg.Wait()
instance.Paths.Generated.EmptyTmpDir() if err := instance.Paths.Generated.EmptyTmpDir(); err != nil {
logger.Warnf("failure emptying temporary directory: %v", err)
}
elapsed := time.Since(start) elapsed := time.Since(start)
logger.Info(fmt.Sprintf("Generate finished (%s)", elapsed)) logger.Info(fmt.Sprintf("Generate finished (%s)", elapsed))
return return
@@ -365,7 +373,9 @@ func (s *singleton) Generate(ctx context.Context, input models.GenerateMetadataI
wg.Wait() wg.Wait()
instance.Paths.Generated.EmptyTmpDir() if err = instance.Paths.Generated.EmptyTmpDir(); err != nil {
logger.Warnf("failure emptying temporary directory: %v", err)
}
elapsed := time.Since(start) elapsed := time.Since(start)
logger.Info(fmt.Sprintf("Generate finished (%s)", elapsed)) logger.Info(fmt.Sprintf("Generate finished (%s)", elapsed))
}) })
@@ -383,7 +393,9 @@ func (s *singleton) GenerateScreenshot(ctx context.Context, sceneId string, at f
// generate default screenshot if at is nil // generate default screenshot if at is nil
func (s *singleton) generateScreenshot(ctx context.Context, sceneId string, at *float64) int { func (s *singleton) generateScreenshot(ctx context.Context, sceneId string, at *float64) int {
instance.Paths.Generated.EnsureTmpDir() if err := instance.Paths.Generated.EnsureTmpDir(); err != nil {
logger.Warnf("failure generating screenshot: %v", err)
}
j := job.MakeJobExec(func(ctx context.Context, progress *job.Progress) { j := job.MakeJobExec(func(ctx context.Context, progress *job.Progress) {
sceneIdInt, err := strconv.Atoi(sceneId) sceneIdInt, err := strconv.Atoi(sceneId)

View File

@@ -5,6 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
) )
@@ -37,26 +38,30 @@ func (gp *generatedPaths) GetTmpPath(fileName string) string {
return filepath.Join(gp.Tmp, fileName) return filepath.Join(gp.Tmp, fileName)
} }
func (gp *generatedPaths) EnsureTmpDir() { func (gp *generatedPaths) EnsureTmpDir() error {
utils.EnsureDir(gp.Tmp) return utils.EnsureDir(gp.Tmp)
} }
func (gp *generatedPaths) EmptyTmpDir() { func (gp *generatedPaths) EmptyTmpDir() error {
utils.EmptyDir(gp.Tmp) return utils.EmptyDir(gp.Tmp)
} }
func (gp *generatedPaths) RemoveTmpDir() { func (gp *generatedPaths) RemoveTmpDir() error {
utils.RemoveDir(gp.Tmp) return utils.RemoveDir(gp.Tmp)
} }
func (gp *generatedPaths) TempDir(pattern string) (string, error) { func (gp *generatedPaths) TempDir(pattern string) (string, error) {
gp.EnsureTmpDir() if err := gp.EnsureTmpDir(); err != nil {
logger.Warnf("Could not ensure existence of a temporary directory: %v", err)
}
ret, err := ioutil.TempDir(gp.Tmp, pattern) ret, err := ioutil.TempDir(gp.Tmp, pattern)
if err != nil { if err != nil {
return "", err return "", err
} }
utils.EmptyDir(ret) if err = utils.EmptyDir(ret); err != nil {
logger.Warnf("could not recursively empty dir: %v", err)
}
return ret, nil return ret, nil
} }

View File

@@ -3,6 +3,7 @@ package paths
import ( import (
"path/filepath" "path/filepath"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
) )
@@ -43,14 +44,30 @@ func GetJSONPaths(baseDir string) *JSONPaths {
func EnsureJSONDirs(baseDir string) { func EnsureJSONDirs(baseDir string) {
jsonPaths := GetJSONPaths(baseDir) jsonPaths := GetJSONPaths(baseDir)
utils.EnsureDir(jsonPaths.Metadata) if err := utils.EnsureDir(jsonPaths.Metadata); err != nil {
utils.EnsureDir(jsonPaths.Scenes) logger.Warnf("couldn't create directories for Metadata: %v", err)
utils.EnsureDir(jsonPaths.Images) }
utils.EnsureDir(jsonPaths.Galleries) if err := utils.EnsureDir(jsonPaths.Scenes); err != nil {
utils.EnsureDir(jsonPaths.Performers) logger.Warnf("couldn't create directories for Scenes: %v", err)
utils.EnsureDir(jsonPaths.Studios) }
utils.EnsureDir(jsonPaths.Movies) if err := utils.EnsureDir(jsonPaths.Images); err != nil {
utils.EnsureDir(jsonPaths.Tags) logger.Warnf("couldn't create directories for Images: %v", err)
}
if err := utils.EnsureDir(jsonPaths.Galleries); err != nil {
logger.Warnf("couldn't create directories for Galleries: %v", err)
}
if err := utils.EnsureDir(jsonPaths.Performers); err != nil {
logger.Warnf("couldn't create directories for Performers: %v", err)
}
if err := utils.EnsureDir(jsonPaths.Studios); err != nil {
logger.Warnf("couldn't create directories for Studios: %v", err)
}
if err := utils.EnsureDir(jsonPaths.Movies); err != nil {
logger.Warnf("couldn't create directories for Movies: %v", err)
}
if err := utils.EnsureDir(jsonPaths.Tags); err != nil {
logger.Warnf("couldn't create directories for Tags: %v", err)
}
} }
func (jp *JSONPaths) PerformerJSONPath(checksum string) string { func (jp *JSONPaths) PerformerJSONPath(checksum string) string {

View File

@@ -91,10 +91,16 @@ func (s *SceneServer) ServeScreenshot(scene *models.Scene, w http.ResponseWriter
http.ServeFile(w, r, filepath) http.ServeFile(w, r, filepath)
} else { } else {
var cover []byte var cover []byte
s.TXNManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error { err := s.TXNManager.WithReadTxn(r.Context(), func(repo models.ReaderRepository) error {
cover, _ = repo.Scene().GetCover(scene.ID) cover, _ = repo.Scene().GetCover(scene.ID)
return nil return nil
}) })
utils.ServeImage(cover, w, r) if err != nil {
logger.Warnf("read transaction failed while serving screenshot: %v", err)
}
if err = utils.ServeImage(cover, w, r); err != nil {
logger.Warnf("unable to serve screenshot image: %v", err)
}
} }
} }

View File

@@ -2,6 +2,7 @@ package manager
import ( import (
"github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/ffmpeg"
"github.com/stashapp/stash/pkg/logger"
) )
func makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int, width int, time float64) { func makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int, width int, time float64) {
@@ -12,5 +13,8 @@ func makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int
Time: time, Time: time,
Width: width, Width: width,
} }
encoder.Screenshot(probeResult, options)
if err := encoder.Screenshot(probeResult, options); err != nil {
logger.Warnf("[encoder] failure to generate screenshot: %v", err)
}
} }

View File

@@ -126,7 +126,7 @@ func (t *ExportTask) Start(wg *sync.WaitGroup) {
paths.EnsureJSONDirs(t.baseDir) paths.EnsureJSONDirs(t.baseDir)
t.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { txnErr := t.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
// include movie scenes and gallery images // include movie scenes and gallery images
if !t.full { if !t.full {
// only include movie scenes if includeDependencies is also set // only include movie scenes if includeDependencies is also set
@@ -154,6 +154,9 @@ func (t *ExportTask) Start(wg *sync.WaitGroup) {
return nil return nil
}) })
if txnErr != nil {
logger.Warnf("error while running export transaction: %v", txnErr)
}
if err := t.json.saveMappings(t.Mappings); err != nil { if err := t.json.saveMappings(t.Mappings); err != nil {
logger.Errorf("[mappings] failed to save json: %s", err.Error()) logger.Errorf("[mappings] failed to save json: %s", err.Error())
@@ -202,17 +205,24 @@ func (t *ExportTask) zipFiles(w io.Writer) error {
return err return err
} }
filepath.Walk(t.json.json.Tags, t.zipWalkFunc(u.json.Tags, z)) walkWarn(t.json.json.Tags, t.zipWalkFunc(u.json.Tags, z))
filepath.Walk(t.json.json.Galleries, t.zipWalkFunc(u.json.Galleries, z)) walkWarn(t.json.json.Galleries, t.zipWalkFunc(u.json.Galleries, z))
filepath.Walk(t.json.json.Performers, t.zipWalkFunc(u.json.Performers, z)) walkWarn(t.json.json.Performers, t.zipWalkFunc(u.json.Performers, z))
filepath.Walk(t.json.json.Studios, t.zipWalkFunc(u.json.Studios, z)) walkWarn(t.json.json.Studios, t.zipWalkFunc(u.json.Studios, z))
filepath.Walk(t.json.json.Movies, t.zipWalkFunc(u.json.Movies, z)) walkWarn(t.json.json.Movies, t.zipWalkFunc(u.json.Movies, z))
filepath.Walk(t.json.json.Scenes, t.zipWalkFunc(u.json.Scenes, z)) walkWarn(t.json.json.Scenes, t.zipWalkFunc(u.json.Scenes, z))
filepath.Walk(t.json.json.Images, t.zipWalkFunc(u.json.Images, z)) walkWarn(t.json.json.Images, t.zipWalkFunc(u.json.Images, z))
return nil return nil
} }
// like filepath.Walk but issue a warning on error
func walkWarn(root string, fn filepath.WalkFunc) {
if err := filepath.Walk(root, fn); err != nil {
logger.Warnf("error walking structure %v: %v", root, err)
}
}
func (t *ExportTask) zipWalkFunc(outDir string, z *zip.Writer) filepath.WalkFunc { func (t *ExportTask) zipWalkFunc(outDir string, z *zip.Writer) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error { return func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {

View File

@@ -160,7 +160,9 @@ func (t *ImportTask) unzipFile() error {
fn := filepath.Join(t.BaseDir, f.Name) fn := filepath.Join(t.BaseDir, f.Name)
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {
os.MkdirAll(fn, os.ModePerm) if err := os.MkdirAll(fn, os.ModePerm); err != nil {
logger.Warnf("couldn't create directory %v while unzipping import file: %v", fn, err)
}
continue continue
} }

View File

@@ -87,7 +87,9 @@ func (j *ScanJob) Execute(ctx context.Context, progress *job.Progress) {
galleries = append(galleries, path) galleries = append(galleries, path)
} }
instance.Paths.Generated.EnsureTmpDir() if err := instance.Paths.Generated.EnsureTmpDir(); err != nil {
logger.Warnf("couldn't create temporary directory: %v", err)
}
wg.Add() wg.Add()
task := ScanTask{ task := ScanTask{
@@ -126,7 +128,11 @@ func (j *ScanJob) Execute(ctx context.Context, progress *job.Progress) {
} }
wg.Wait() wg.Wait()
instance.Paths.Generated.EmptyTmpDir()
if err := instance.Paths.Generated.EmptyTmpDir(); err != nil {
logger.Warnf("couldn't empty temporary directory: %v", err)
}
elapsed := time.Since(start) elapsed := time.Since(start)
logger.Info(fmt.Sprintf("Scan finished (%s)", elapsed)) logger.Info(fmt.Sprintf("Scan finished (%s)", elapsed))
@@ -753,7 +759,7 @@ func (t *ScanTask) scanScene() *models.Scene {
// check for scene by checksum and oshash - MD5 should be // check for scene by checksum and oshash - MD5 should be
// redundant, but check both // redundant, but check both
t.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { txnErr := t.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
qb := r.Scene() qb := r.Scene()
if checksum != "" { if checksum != "" {
s, _ = qb.FindByChecksum(checksum) s, _ = qb.FindByChecksum(checksum)
@@ -765,6 +771,9 @@ func (t *ScanTask) scanScene() *models.Scene {
return nil return nil
}) })
if txnErr != nil {
logger.Warnf("error in read transaction: %v", txnErr)
}
sceneHash := oshash sceneHash := oshash
@@ -1320,7 +1329,7 @@ func (t *ScanTask) doesPathExist() bool {
gExt := config.GetGalleryExtensions() gExt := config.GetGalleryExtensions()
ret := false ret := false
t.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { txnErr := t.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
if matchExtension(t.FilePath, gExt) { if matchExtension(t.FilePath, gExt) {
gallery, _ := r.Gallery().FindByPath(t.FilePath) gallery, _ := r.Gallery().FindByPath(t.FilePath)
if gallery != nil { if gallery != nil {
@@ -1340,6 +1349,9 @@ func (t *ScanTask) doesPathExist() bool {
return nil return nil
}) })
if txnErr != nil {
logger.Warnf("error while executing read transaction: %v", txnErr)
}
return ret return ret
} }

View File

@@ -47,7 +47,7 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() {
if t.refresh { if t.refresh {
var performerID string var performerID string
t.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { txnErr := t.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
stashids, _ := r.Performer().GetStashIDs(t.performer.ID) stashids, _ := r.Performer().GetStashIDs(t.performer.ID)
for _, id := range stashids { for _, id := range stashids {
if id.Endpoint == t.box.Endpoint { if id.Endpoint == t.box.Endpoint {
@@ -56,6 +56,9 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() {
} }
return nil return nil
}) })
if txnErr != nil {
logger.Warnf("error while executing read transaction: %v", err)
}
if performerID != "" { if performerID != "" {
performer, err = client.FindStashBoxPerformerByID(performerID) performer, err = client.FindStashBoxPerformerByID(performerID)
} }

View File

@@ -2,6 +2,7 @@ package plugin
import ( import (
"errors" "errors"
"fmt"
"path/filepath" "path/filepath"
"sync" "sync"
@@ -71,10 +72,21 @@ func (t *jsPluginTask) Start() error {
return err return err
} }
t.vm.Set("input", t.input) if err := t.vm.Set("input", t.input); err != nil {
js.AddLogAPI(t.vm, t.progress) return fmt.Errorf("error setting input: %w", err)
js.AddUtilAPI(t.vm) }
js.AddGQLAPI(t.vm, t.input.ServerConnection.SessionCookie, t.gqlHandler)
if err := js.AddLogAPI(t.vm, t.progress); err != nil {
return fmt.Errorf("error adding log API: %w", err)
}
if err := js.AddUtilAPI(t.vm); err != nil {
return fmt.Errorf("error adding util API: %w", err)
}
if err := js.AddGQLAPI(t.vm, t.input.ServerConnection.SessionCookie, t.gqlHandler); err != nil {
return fmt.Errorf("error adding GraphQL API: %w", err)
}
t.vm.Interrupt = make(chan func(), 1) t.vm.Interrupt = make(chan func(), 1)

View File

@@ -103,9 +103,15 @@ func gqlRequestFunc(vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler)
} }
} }
func AddGQLAPI(vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) { func AddGQLAPI(vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) error {
gql, _ := vm.Object("({})") gql, _ := vm.Object("({})")
gql.Set("Do", gqlRequestFunc(vm, cookie, gqlHandler)) if err := gql.Set("Do", gqlRequestFunc(vm, cookie, gqlHandler)); err != nil {
return fmt.Errorf("unable to set GraphQL Do function: %w", err)
}
vm.Set("gql", gql) if err := vm.Set("gql", gql); err != nil {
return fmt.Errorf("unable to set gql: %w", err)
}
return nil
} }

View File

@@ -2,6 +2,7 @@ package js
import ( import (
"encoding/json" "encoding/json"
"fmt"
"math" "math"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
@@ -64,14 +65,29 @@ func logProgressFunc(c chan float64) func(call otto.FunctionCall) otto.Value {
} }
} }
func AddLogAPI(vm *otto.Otto, progress chan float64) { func AddLogAPI(vm *otto.Otto, progress chan float64) error {
log, _ := vm.Object("({})") log, _ := vm.Object("({})")
log.Set("Trace", logTrace) if err := log.Set("Trace", logTrace); err != nil {
log.Set("Debug", logDebug) return fmt.Errorf("error setting Trace: %w", err)
log.Set("Info", logInfo) }
log.Set("Warn", logWarn) if err := log.Set("Debug", logDebug); err != nil {
log.Set("Error", logError) return fmt.Errorf("error setting Debug: %w", err)
log.Set("Progress", logProgressFunc(progress)) }
if err := log.Set("Info", logInfo); err != nil {
return fmt.Errorf("error setting Info: %w", err)
}
if err := log.Set("Warn", logWarn); err != nil {
return fmt.Errorf("error setting Warn: %w", err)
}
if err := log.Set("Error", logError); err != nil {
return fmt.Errorf("error setting Error: %w", err)
}
if err := log.Set("Progress", logProgressFunc(progress)); err != nil {
return fmt.Errorf("error setting Progress: %v", err)
}
if err := vm.Set("log", log); err != nil {
return fmt.Errorf("unable to set log: %w", err)
}
vm.Set("log", log) return nil
} }

View File

@@ -1,6 +1,7 @@
package js package js
import ( import (
"fmt"
"time" "time"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
@@ -14,9 +15,15 @@ func sleepFunc(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
func AddUtilAPI(vm *otto.Otto) { func AddUtilAPI(vm *otto.Otto) error {
util, _ := vm.Object("({})") util, _ := vm.Object("({})")
util.Set("Sleep", sleepFunc) if err := util.Set("Sleep", sleepFunc); err != nil {
return fmt.Errorf("unable to set sleep func: %w", err)
}
vm.Set("util", util) if err := vm.Set("util", util); err != nil {
return fmt.Errorf("unable to set util: %w", err)
}
return nil
} }

View File

@@ -219,7 +219,9 @@ func (c Cache) executePostHooks(ctx context.Context, hookType HookTriggerEnum, h
select { select {
case <-ctx.Done(): case <-ctx.Done():
task.Stop() if err := task.Stop(); err != nil {
logger.Warnf("could not stop task: %v", err)
}
return fmt.Errorf("operation cancelled") return fmt.Errorf("operation cancelled")
case <-c: case <-c:
// task finished normally // task finished normally

View File

@@ -51,7 +51,9 @@ func (t *rawPluginTask) Start() error {
defer stdin.Close() defer stdin.Close()
inBytes, _ := json.Marshal(t.input) inBytes, _ := json.Marshal(t.input)
io.WriteString(stdin, string(inBytes)) if k, err := io.WriteString(stdin, string(inBytes)); err != nil {
logger.Warnf("error writing input to plugins stdin (wrote %v bytes out of %v): %v", k, len(string(inBytes)), err)
}
}() }()
stderr, err := cmd.StderrPipe() stderr, err := cmd.StderrPipe()

View File

@@ -7,6 +7,8 @@ import (
"hash/fnv" "hash/fnv"
"io" "io"
"os" "os"
"github.com/stashapp/stash/pkg/logger"
) )
func MD5FromBytes(data []byte) string { func MD5FromBytes(data []byte) string {
@@ -40,7 +42,9 @@ func MD5FromReader(src io.Reader) (string, error) {
func GenerateRandomKey(l int) string { func GenerateRandomKey(l int) string {
b := make([]byte, l) b := make([]byte, l)
rand.Read(b) if n, err := rand.Read(b); err != nil {
logger.Warnf("failure generating random key: %v (only read %v bytes)", err, n)
}
return fmt.Sprintf("%x", b) return fmt.Sprintf("%x", b)
} }

View File

@@ -6,6 +6,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"log"
"math/rand" "math/rand"
"os" "os"
"strconv" "strconv"
@@ -43,12 +44,14 @@ func main() {
var err error var err error
c, err = loadConfig() c, err = loadConfig()
if err != nil { if err != nil {
panic(err) log.Fatalf("couldn't load configuration: %v", err)
} }
initNaming(*c) initNaming(*c)
database.Initialize(c.Database) if err = database.Initialize(c.Database); err != nil {
log.Fatalf("couldn't initialize database: %v", err)
}
populateDB() populateDB()
} }