mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Improve handling of moved and added video files (#4598)
* If old file path is not in library, treat as move * Use existing phash if file with same oshash exists
This commit is contained in:
parent
8b1d4ccc97
commit
76e5598876
4 changed files with 75 additions and 18 deletions
|
|
@ -19,14 +19,17 @@ import (
|
|||
)
|
||||
|
||||
func useAsVideo(pathname string) bool {
|
||||
if instance.Config.IsCreateImageClipsFromVideos() && config.StashConfigs.GetStashFromDirPath(instance.Config.GetStashPaths(), pathname).ExcludeVideo {
|
||||
stash := config.StashConfigs.GetStashFromDirPath(instance.Config.GetStashPaths(), pathname)
|
||||
|
||||
if instance.Config.IsCreateImageClipsFromVideos() && stash != nil && stash.ExcludeVideo {
|
||||
return false
|
||||
}
|
||||
return isVideo(pathname)
|
||||
}
|
||||
|
||||
func useAsImage(pathname string) bool {
|
||||
if instance.Config.IsCreateImageClipsFromVideos() && config.StashConfigs.GetStashFromDirPath(instance.Config.GetStashPaths(), pathname).ExcludeVideo {
|
||||
stash := config.StashConfigs.GetStashFromDirPath(instance.Config.GetStashPaths(), pathname)
|
||||
if instance.Config.IsCreateImageClipsFromVideos() && stash != nil && stash.ExcludeVideo {
|
||||
return isImage(pathname) || isVideo(pathname)
|
||||
}
|
||||
return isImage(pathname)
|
||||
|
|
|
|||
|
|
@ -25,19 +25,38 @@ func (t *GeneratePhashTask) Start(ctx context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
hash, err := videophash.Generate(instance.FFMpeg, t.File)
|
||||
if err != nil {
|
||||
logger.Errorf("error generating phash: %s", err.Error())
|
||||
logErrorOutput(err)
|
||||
return
|
||||
var hash int64
|
||||
set := false
|
||||
|
||||
// #4393 - if there is a file with the same oshash, we can use the same phash
|
||||
// only use this if we're not overwriting
|
||||
if !t.Overwrite {
|
||||
existing, err := t.findExistingPhash(ctx)
|
||||
if err != nil {
|
||||
logger.Warnf("Error finding existing phash: %v", err)
|
||||
} else if existing != nil {
|
||||
logger.Infof("Using existing phash for %s", t.File.Path)
|
||||
hash = existing.(int64)
|
||||
set = true
|
||||
}
|
||||
}
|
||||
|
||||
if !set {
|
||||
generated, err := videophash.Generate(instance.FFMpeg, t.File)
|
||||
if err != nil {
|
||||
logger.Errorf("Error generating phash: %v", err)
|
||||
logErrorOutput(err)
|
||||
return
|
||||
}
|
||||
|
||||
hash = int64(*generated)
|
||||
}
|
||||
|
||||
r := t.repository
|
||||
if err := r.WithTxn(ctx, func(ctx context.Context) error {
|
||||
hashValue := int64(*hash)
|
||||
t.File.Fingerprints = t.File.Fingerprints.AppendUnique(models.Fingerprint{
|
||||
Type: models.FingerprintTypePhash,
|
||||
Fingerprint: hashValue,
|
||||
Fingerprint: hash,
|
||||
})
|
||||
|
||||
return r.File.Update(ctx, t.File)
|
||||
|
|
@ -46,6 +65,36 @@ func (t *GeneratePhashTask) Start(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *GeneratePhashTask) findExistingPhash(ctx context.Context) (interface{}, error) {
|
||||
r := t.repository
|
||||
var ret interface{}
|
||||
if err := r.WithReadTxn(ctx, func(ctx context.Context) error {
|
||||
oshash := t.File.Fingerprints.Get(models.FingerprintTypeOshash)
|
||||
|
||||
// find other files with the same oshash
|
||||
files, err := r.File.FindByFingerprint(ctx, models.Fingerprint{
|
||||
Type: models.FingerprintTypeOshash,
|
||||
Fingerprint: oshash,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding files by oshash: %w", err)
|
||||
}
|
||||
|
||||
// find the first file with a phash
|
||||
for _, file := range files {
|
||||
if phash := file.Base().Fingerprints.Get(models.FingerprintTypePhash); phash != nil {
|
||||
ret = phash
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (t *GeneratePhashTask) required() bool {
|
||||
if t.Overwrite {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -264,6 +264,12 @@ func (f *scanFilter) Accept(ctx context.Context, path string, info fs.FileInfo)
|
|||
return false
|
||||
}
|
||||
|
||||
s := f.stashPaths.GetStashFromDirPath(path)
|
||||
if s == nil {
|
||||
logger.Debugf("Skipping %s as it is not in the stash library", path)
|
||||
return false
|
||||
}
|
||||
|
||||
isVideoFile := useAsVideo(path)
|
||||
isImageFile := useAsImage(path)
|
||||
isZipFile := fsutil.MatchExtension(path, f.zipExt)
|
||||
|
|
@ -288,13 +294,6 @@ func (f *scanFilter) Accept(ctx context.Context, path string, info fs.FileInfo)
|
|||
return false
|
||||
}
|
||||
|
||||
s := f.stashPaths.GetStashFromDirPath(path)
|
||||
|
||||
if s == nil {
|
||||
logger.Debugf("Skipping %s as it is not in the stash library", path)
|
||||
return false
|
||||
}
|
||||
|
||||
// shortcut: skip the directory entirely if it matches both exclusion patterns
|
||||
// add a trailing separator so that it correctly matches against patterns like path/.*
|
||||
pathExcludeTest := path + string(filepath.Separator)
|
||||
|
|
|
|||
|
|
@ -867,9 +867,11 @@ func (s *scanJob) handleRename(ctx context.Context, f models.File, fp []models.F
|
|||
continue
|
||||
}
|
||||
|
||||
if _, err := fs.Lstat(other.Base().Path); err != nil {
|
||||
info, err := fs.Lstat(other.Base().Path)
|
||||
switch {
|
||||
case err != nil:
|
||||
missing = append(missing, other)
|
||||
} else if strings.EqualFold(f.Base().Path, other.Base().Path) {
|
||||
case strings.EqualFold(f.Base().Path, other.Base().Path):
|
||||
// #1426 - if file exists but is a case-insensitive match for the
|
||||
// original filename, and the filesystem is case-insensitive
|
||||
// then treat it as a move
|
||||
|
|
@ -877,6 +879,10 @@ func (s *scanJob) handleRename(ctx context.Context, f models.File, fp []models.F
|
|||
// treat as a move
|
||||
missing = append(missing, other)
|
||||
}
|
||||
case !s.acceptEntry(ctx, other.Base().Path, info):
|
||||
// #4393 - if the file is no longer in the configured library paths, treat it as a move
|
||||
logger.Debugf("File %q no longer in library paths. Treating as a move.", other.Base().Path)
|
||||
missing = append(missing, other)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue