diff --git a/pkg/ffmpeg/encoder.go b/pkg/ffmpeg/encoder.go index 0374f6dfc..5beb09410 100644 --- a/pkg/ffmpeg/encoder.go +++ b/pkg/ffmpeg/encoder.go @@ -100,6 +100,7 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) { } buf := make([]byte, 80) + lastProgress := 0.0 var errBuilder strings.Builder for { n, err := stderr.Read(buf) @@ -108,7 +109,11 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) { time := GetTimeFromRegex(data) if time > 0 && probeResult.Duration > 0 { progress := time / probeResult.Duration - logger.Infof("Progress %.2f", progress) + + if progress > lastProgress+0.01 { + logger.Infof("Progress %.2f", progress) + lastProgress = progress + } } errBuilder.WriteString(data) diff --git a/pkg/manager/generator_preview.go b/pkg/manager/generator_preview.go index f320a0a44..df19981ab 100644 --- a/pkg/manager/generator_preview.go +++ b/pkg/manager/generator_preview.go @@ -139,7 +139,7 @@ func (g *PreviewGenerator) generateImage(encoder *ffmpeg.Encoder) error { if err := encoder.ScenePreviewVideoToImage(g.Info.VideoFile, 640, videoPreviewPath, tmpOutputPath); err != nil { return err } - if err := os.Rename(tmpOutputPath, outputPath); err != nil { + if err := utils.SafeMove(tmpOutputPath, outputPath); err != nil { return err } logger.Debug("created video preview image: ", outputPath) diff --git a/pkg/manager/generator_sprite.go b/pkg/manager/generator_sprite.go index 5efcf2127..66e16e559 100644 --- a/pkg/manager/generator_sprite.go +++ b/pkg/manager/generator_sprite.go @@ -6,6 +6,7 @@ import ( "image/color" "io/ioutil" "math" + "os" "path/filepath" "strings" @@ -120,13 +121,15 @@ func (g *SpriteGenerator) generateSpriteVTT(encoder *ffmpeg.Encoder) error { } logger.Infof("[generator] generating sprite vtt for %s", g.Info.VideoFile.Path) - spriteImage, err := imaging.Open(g.ImageOutputPath) + spriteImage, err := os.Open(g.ImageOutputPath) if err != nil { return err } + defer spriteImage.Close() spriteImageName := filepath.Base(g.ImageOutputPath) - width := spriteImage.Bounds().Size().X / g.Columns - height := spriteImage.Bounds().Size().Y / g.Rows + image, _, err := image.DecodeConfig(spriteImage) + width := image.Width / g.Columns + height := image.Height / g.Rows stepSize := float64(g.Info.NthFrame) / g.Info.FrameRate diff --git a/pkg/manager/task_generate_markers.go b/pkg/manager/task_generate_markers.go index e4f6720c0..deae76ec0 100644 --- a/pkg/manager/task_generate_markers.go +++ b/pkg/manager/task_generate_markers.go @@ -1,7 +1,6 @@ package manager import ( - "os" "path/filepath" "strconv" "sync" @@ -96,7 +95,7 @@ func (t *GenerateMarkersTask) generateMarker(videoFile *ffmpeg.VideoFile, scene if err := encoder.SceneMarkerVideo(*videoFile, options); err != nil { logger.Errorf("[generator] failed to generate marker video: %s", err) } else { - _ = os.Rename(options.OutputPath, videoPath) + _ = utils.SafeMove(options.OutputPath, videoPath) logger.Debug("created marker video: ", videoPath) } } @@ -109,7 +108,7 @@ func (t *GenerateMarkersTask) generateMarker(videoFile *ffmpeg.VideoFile, scene if err := encoder.SceneMarkerImage(*videoFile, options); err != nil { logger.Errorf("[generator] failed to generate marker image: %s", err) } else { - _ = os.Rename(options.OutputPath, imagePath) + _ = utils.SafeMove(options.OutputPath, imagePath) logger.Debug("created marker image: ", imagePath) } } diff --git a/pkg/manager/task_scan.go b/pkg/manager/task_scan.go index 9e2742ee4..1474b6b96 100644 --- a/pkg/manager/task_scan.go +++ b/pkg/manager/task_scan.go @@ -42,6 +42,11 @@ func (t *ScanTask) scanGallery() { return } + // Ignore directories. + if isDir, _ := utils.DirExists(t.FilePath); isDir { + return + } + ok, err := utils.IsZipFileUncompressed(t.FilePath) if err == nil && !ok { logger.Warnf("%s is using above store (0) level compression.", t.FilePath) @@ -95,8 +100,9 @@ func (t *ScanTask) associateGallery(wg *sync.WaitGroup) { qb := models.NewGalleryQueryBuilder() gallery, _ := qb.FindByPath(t.FilePath) if gallery == nil { - // shouldn't happen , associate is run after scan is finished - logger.Errorf("associate: gallery %s not found in DB", t.FilePath) + // associate is run after scan is finished + // should only happen if gallery is a directory or an io error occurs during hashing + logger.Warnf("associate: gallery %s not found in DB", t.FilePath) wg.Done() return } @@ -226,6 +232,11 @@ func (t *ScanTask) scanScene() { return } + // Ignore directories. + if isDir, _ := utils.DirExists(t.FilePath); isDir { + return + } + videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath) if err != nil { logger.Error(err.Error()) diff --git a/pkg/manager/task_transcode.go b/pkg/manager/task_transcode.go index a002c87d4..d21ec4059 100644 --- a/pkg/manager/task_transcode.go +++ b/pkg/manager/task_transcode.go @@ -1,13 +1,12 @@ package manager import ( - "os" - "sync" - "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" + "sync" ) type GenerateTranscodeTask struct { @@ -79,7 +78,7 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { } } - if err := os.Rename(outputPath, instance.Paths.Scene.GetTranscodePath(sceneHash)); err != nil { + if err := utils.SafeMove(outputPath, instance.Paths.Scene.GetTranscodePath(sceneHash)); err != nil { logger.Errorf("[transcode] error generating transcode: %s", err.Error()) return } diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 760263334..f4d947408 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -3,6 +3,7 @@ package utils import ( "archive/zip" "fmt" + "io" "io/ioutil" "math" "net/http" @@ -12,6 +13,7 @@ import ( "github.com/h2non/filetype" "github.com/h2non/filetype/types" + "github.com/stashapp/stash/pkg/logger" ) // FileType uses the filetype package to determine the given file path's type @@ -131,6 +133,43 @@ func GetHomeDirectory() string { return currentUser.HomeDir } +func SafeMove(src, dst string) error { + err := os.Rename(src, dst) + + if err != nil { + logger.Errorf("[Util] unable to rename: \"%s\" due to %s. Falling back to copying.", src, err.Error()) + + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + err = out.Close() + if err != nil { + return err + } + + err = os.Remove(src) + if err != nil { + return err + } + } + + return nil +} + // IsZipFileUnmcompressed returns true if zip file in path is using 0 compression level func IsZipFileUncompressed(path string) (bool, error) { r, err := zip.OpenReader(path)