fix(scanner): improve folderEntry methods and hashing logic for better change detection

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2025-06-14 12:35:28 -04:00
parent 6f749b387b
commit 44834204de
3 changed files with 163 additions and 23 deletions
+39 -15
View File
@@ -8,7 +8,6 @@ import (
"io/fs"
"maps"
"slices"
"strings"
"time"
"github.com/navidrome/navidrome/core"
@@ -54,13 +53,24 @@ type folderEntry struct {
}
func (f *folderEntry) hasNoFiles() bool {
return len(f.audioFiles) == 0 && len(f.imageFiles) == 0 && f.numPlaylists == 0 && f.numSubFolders == 0
return len(f.audioFiles) == 0 && len(f.imageFiles) == 0 && f.numPlaylists == 0
}
func (f *folderEntry) isEmpty() bool {
return f.hasNoFiles() && f.numSubFolders == 0
}
func (f *folderEntry) isNew() bool {
return f.updTime.IsZero()
}
func (f *folderEntry) isOutdated() bool {
if f.job.lib.FullScanInProgress && f.updTime.Before(f.job.lib.LastScanStartedAt) {
return true
}
return f.prevHash != f.hash()
}
func (f *folderEntry) toFolder() *model.Folder {
folder := model.NewFolder(f.job.lib, f.path)
folder.NumAudioFiles = len(f.audioFiles)
@@ -74,23 +84,37 @@ func (f *folderEntry) toFolder() *model.Folder {
}
func (f *folderEntry) hash() string {
h := md5.New()
_, _ = fmt.Fprintf(
h,
"%s:%d:%d:%s",
f.modTime.UTC(),
f.numPlaylists,
f.numSubFolders,
f.imagesUpdatedAt.UTC(),
)
// Sort the keys of audio and image files to ensure consistent hashing
audioKeys := slices.Collect(maps.Keys(f.audioFiles))
slices.Sort(audioKeys)
imageKeys := slices.Collect(maps.Keys(f.imageFiles))
slices.Sort(imageKeys)
h := md5.New()
_, _ = io.WriteString(h, f.modTime.UTC().String())
_, _ = io.WriteString(h, strings.Join(audioKeys, ","))
_, _ = io.WriteString(h, strings.Join(imageKeys, ","))
fmt.Fprintf(h, "%d-%d", f.numPlaylists, f.numSubFolders)
_, _ = io.WriteString(h, f.imagesUpdatedAt.UTC().String())
// Include audio files with their size and modtime
for _, key := range audioKeys {
_, _ = io.WriteString(h, key)
if info, err := f.audioFiles[key].Info(); err == nil {
_, _ = fmt.Fprintf(h, ":%d:%s", info.Size(), info.ModTime().UTC().String())
}
}
// Include image files with their size and modtime
for _, key := range imageKeys {
_, _ = io.WriteString(h, key)
if info, err := f.imageFiles[key].Info(); err == nil {
_, _ = fmt.Fprintf(h, ":%d:%s", info.Size(), info.ModTime().UTC().String())
}
}
return hex.EncodeToString(h.Sum(nil))
}
func (f *folderEntry) isOutdated() bool {
if f.job.lib.FullScanInProgress && f.updTime.Before(f.job.lib.LastScanStartedAt) {
return true
}
return f.prevHash != f.hash()
}