mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-08 10:27:08 +02:00
feat(scanner): skip folders containing a .nomedia file
Folders with a .nomedia file are now skipped during scans, the same way as folders with an empty .ndignore file. The file contents are ignored, and it takes precedence over any .ndignore in the same folder. .nomedia is the marker used by Kodi, Plex and Android to exclude folders from media scans, so libraries already set up for those tools work without extra files. Note: folders that only use .nomedia to hide cover art from Android gallery apps will no longer be scanned.
This commit is contained in:
parent
dd71f1c57f
commit
c67c19c302
5 changed files with 29 additions and 4 deletions
|
|
@ -88,6 +88,7 @@ const (
|
|||
|
||||
I18nFolder = "i18n"
|
||||
ScanIgnoreFile = ".ndignore"
|
||||
NoMediaFile = ".nomedia"
|
||||
ArtworkFolder = "artwork"
|
||||
// HashedArtworkFolder is a subtree of ArtworkFolder, kept apart from the name-addressed
|
||||
// upload folders beside it so Prune's sweep never reaches them.
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ flowchart TD
|
|||
1. **Folder Traversal**
|
||||
- Uses `walkDirTree` to traverse the directory structure
|
||||
- Handles symbolic links and hidden files
|
||||
- Processes `.ndignore` files for exclusions
|
||||
- Processes `.ndignore` files for exclusions, and skips folders containing a `.nomedia` file
|
||||
- Maps files to appropriate types (audio, image, playlist)
|
||||
|
||||
2. **Metadata Extraction**
|
||||
|
|
|
|||
|
|
@ -102,8 +102,13 @@ func (ic *IgnoreChecker) ShouldIgnore(ctx context.Context, relPath string) bool
|
|||
|
||||
// loadPatternsFromFolder reads the .ndignore file in the specified folder and returns the patterns.
|
||||
// If the file doesn't exist, returns an empty slice.
|
||||
// If the file exists but is empty, returns a pattern to ignore everything ("**/*").
|
||||
// If the file exists but is empty, or a .nomedia file exists, returns a pattern to ignore everything ("**/*").
|
||||
func (ic *IgnoreChecker) loadPatternsFromFolder(ctx context.Context, folder string) []string {
|
||||
if _, err := fs.Stat(ic.fsys, path.Join(folder, consts.NoMediaFile)); err == nil {
|
||||
log.Trace(ctx, "Scanner: .nomedia file found, ignoring everything", "path", folder)
|
||||
return []string{"**/*"}
|
||||
}
|
||||
|
||||
ignoreFilePath := path.Join(folder, consts.ScanIgnoreFile)
|
||||
var patterns []string
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,20 @@ var _ = Describe("IgnoreChecker", func() {
|
|||
})
|
||||
})
|
||||
|
||||
DescribeTable("when .nomedia file exists",
|
||||
func(files fstest.MapFS) {
|
||||
ic = newIgnoreChecker(files)
|
||||
patterns := ic.loadPatternsFromFolder(ctx, ".")
|
||||
Expect(patterns).To(Equal([]string{"**/*"}))
|
||||
},
|
||||
Entry("empty", fstest.MapFS{".nomedia": &fstest.MapFile{}}),
|
||||
Entry("with content", fstest.MapFS{".nomedia": &fstest.MapFile{Data: []byte("*.txt")}}),
|
||||
Entry("alongside .ndignore", fstest.MapFS{
|
||||
".nomedia": &fstest.MapFile{},
|
||||
".ndignore": &fstest.MapFile{Data: []byte("*.txt")},
|
||||
}),
|
||||
)
|
||||
|
||||
DescribeTable("parsing .ndignore content",
|
||||
func(content string, expectedPatterns []string) {
|
||||
fsys := fstest.MapFS{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ var _ = Describe("walk_dir_tree", func() {
|
|||
"root/h/.hidden.mp3": {},
|
||||
"root/i/.git/config": {},
|
||||
"root/i/.streams/stream.mp3": {},
|
||||
"root/j/.nomedia": {},
|
||||
"root/j/f1.mp3": {},
|
||||
"root/j/sub/f2.mp3": {},
|
||||
},
|
||||
}
|
||||
job = &scanJob{
|
||||
|
|
@ -100,6 +103,8 @@ var _ = Describe("walk_dir_tree", func() {
|
|||
Expect(folders["root/c"].audioFiles).To(BeEmpty())
|
||||
Expect(folders["root/c"].imageFiles).To(BeEmpty())
|
||||
Expect(folders).ToNot(HaveKey("root/d"))
|
||||
Expect(folders["root/j"].audioFiles).To(BeEmpty())
|
||||
Expect(folders).ToNot(HaveKey("root/j/sub"))
|
||||
|
||||
// By default (Scanner.IgnoreDotFolders == true), dot-prefixed
|
||||
// folders are skipped, dot-prefixed files are not indexed, and
|
||||
|
|
@ -122,8 +127,8 @@ var _ = Describe("walk_dir_tree", func() {
|
|||
Expect(folders["root/f"].audioFiles).ToNot(HaveKey("evil.mp3"))
|
||||
}
|
||||
},
|
||||
Entry("with symlinks enabled", true, 11),
|
||||
Entry("with symlinks disabled", false, 10),
|
||||
Entry("with symlinks enabled", true, 12),
|
||||
Entry("with symlinks disabled", false, 11),
|
||||
)
|
||||
|
||||
DescribeTable("dot-prefixed folders with IgnoreDotFolders disabled",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue