From db53136fc955a8c6b0cb842da9819f8d3dbf39b3 Mon Sep 17 00:00:00 2001 From: Stephan Wahlen <44159957+metalheim@users.noreply.github.com> Date: Thu, 14 May 2026 08:38:43 +0200 Subject: [PATCH 1/5] dont skip files that taglib doesnt like instead import with empty metadata/audioproperties --- adapters/gotaglib/gotaglib.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/adapters/gotaglib/gotaglib.go b/adapters/gotaglib/gotaglib.go index 7ea98a4421f..f929bd40b50 100644 --- a/adapters/gotaglib/gotaglib.go +++ b/adapters/gotaglib/gotaglib.go @@ -23,6 +23,7 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/core/storage/local" "github.com/navidrome/navidrome/log" + "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/metadata" "go.senan.xyz/taglib" ) @@ -37,6 +38,27 @@ func (e extractor) Parse(files ...string) (map[string]metadata.Info, error) { props, err := e.extractMetadata(path) if err != nil { continue + log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) ++ f, openErr := e.fs.Open(path) ++ if openErr != nil { ++ continue ++ } ++ info, statErr := f.Stat() ++ _ = f.Close() ++ if statErr != nil { ++ continue ++ } ++ fileInfo, ok := info.(metadata.FileInfo) ++ if !ok { ++ continue ++ } ++ results[path] = metadata.Info{ ++ FileInfo: fileInfo, ++ Tags: model.RawTags{}, ++ AudioProperties: metadata.AudioProperties{}, ++ HasPicture: false, ++ } ++ continue } results[path] = *props } From 993eaf8812e26de4633bc139816245b2a2a804ec Mon Sep 17 00:00:00 2001 From: Stephan Wahlen <44159957+metalheim@users.noreply.github.com> Date: Thu, 14 May 2026 08:51:21 +0200 Subject: [PATCH 2/5] gate change behind configuration new configuration option scanner.importonreaderror. Defaults false to preserve original behaviour --- adapters/gotaglib/gotaglib.go | 6 ++++-- conf/configuration.go | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/adapters/gotaglib/gotaglib.go b/adapters/gotaglib/gotaglib.go index f929bd40b50..39f8595358e 100644 --- a/adapters/gotaglib/gotaglib.go +++ b/adapters/gotaglib/gotaglib.go @@ -37,8 +37,10 @@ func (e extractor) Parse(files ...string) (map[string]metadata.Info, error) { for _, path := range files { props, err := e.extractMetadata(path) if err != nil { - continue - log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) + if !conf.Server.Scanner.ImportOnReadError { + log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) + continue + } + f, openErr := e.fs.Open(path) + if openErr != nil { + continue diff --git a/conf/configuration.go b/conf/configuration.go index 6fff1641a80..2fd97c4b226 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -163,6 +163,7 @@ type scannerOptions struct { GroupAlbumReleases bool // Deprecated: Use PID.Album instead FollowSymlinks bool // Whether to follow symlinks when scanning directories PurgeMissing string // Values: "never", "always", "full" + ImportOnReadError bool } type subsonicOptions struct { @@ -815,6 +816,7 @@ func setViperDefaults() { viper.SetDefault("scanner.groupalbumreleases", false) viper.SetDefault("scanner.followsymlinks", true) viper.SetDefault("scanner.purgemissing", consts.PurgeMissingNever) + viper.SetDefault("scanner.importonreaderror", false) viper.SetDefault("subsonic.appendsubtitle", true) viper.SetDefault("subsonic.appendalbumversion", true) viper.SetDefault("subsonic.artistparticipations", false) From ba2a1fac1ac51b1c7aea218d73f69b77225ecf84 Mon Sep 17 00:00:00 2001 From: Stephan Wahlen <44159957+metalheim@users.noreply.github.com> Date: Thu, 14 May 2026 08:54:12 +0200 Subject: [PATCH 3/5] logline in wrong place --- adapters/gotaglib/gotaglib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/gotaglib/gotaglib.go b/adapters/gotaglib/gotaglib.go index 39f8595358e..42f59e86f7a 100644 --- a/adapters/gotaglib/gotaglib.go +++ b/adapters/gotaglib/gotaglib.go @@ -38,9 +38,9 @@ func (e extractor) Parse(files ...string) (map[string]metadata.Info, error) { props, err := e.extractMetadata(path) if err != nil { if !conf.Server.Scanner.ImportOnReadError { - log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) continue } + log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) + f, openErr := e.fs.Open(path) + if openErr != nil { + continue From 81fb66188cdafa377fa705e49c08570008ef26e4 Mon Sep 17 00:00:00 2001 From: Stephan Wahlen <44159957+metalheim@users.noreply.github.com> Date: Thu, 14 May 2026 08:58:09 +0200 Subject: [PATCH 4/5] Update gotaglib.go --- adapters/gotaglib/gotaglib.go | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/adapters/gotaglib/gotaglib.go b/adapters/gotaglib/gotaglib.go index 42f59e86f7a..c4f8a509594 100644 --- a/adapters/gotaglib/gotaglib.go +++ b/adapters/gotaglib/gotaglib.go @@ -41,26 +41,26 @@ func (e extractor) Parse(files ...string) (map[string]metadata.Info, error) { continue } log.Warn("gotaglib: Error reading metadata from file. Importing without metadata", "filePath", path, err) -+ f, openErr := e.fs.Open(path) -+ if openErr != nil { -+ continue -+ } -+ info, statErr := f.Stat() -+ _ = f.Close() -+ if statErr != nil { -+ continue -+ } -+ fileInfo, ok := info.(metadata.FileInfo) -+ if !ok { -+ continue -+ } -+ results[path] = metadata.Info{ -+ FileInfo: fileInfo, -+ Tags: model.RawTags{}, -+ AudioProperties: metadata.AudioProperties{}, -+ HasPicture: false, -+ } -+ continue + f, openErr := e.fs.Open(path) + if openErr != nil { + continue + } + info, statErr := f.Stat() + _ = f.Close() + if statErr != nil { + continue + } + fileInfo, ok := info.(metadata.FileInfo) + if !ok { + continue + } + results[path] = metadata.Info{ + FileInfo: fileInfo, + Tags: model.RawTags{}, + AudioProperties: metadata.AudioProperties{}, + HasPicture: false, + } + continue } results[path] = *props } From 61bde0c38d6858c6ec6c30b5d8bb59ce2f23c410 Mon Sep 17 00:00:00 2001 From: Stephan Wahlen <44159957+metalheim@users.noreply.github.com> Date: Thu, 14 May 2026 09:15:00 +0200 Subject: [PATCH 5/5] add test --- adapters/gotaglib/gotaglib_test.go | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/adapters/gotaglib/gotaglib_test.go b/adapters/gotaglib/gotaglib_test.go index 05924914d72..3568732ef76 100644 --- a/adapters/gotaglib/gotaglib_test.go +++ b/adapters/gotaglib/gotaglib_test.go @@ -7,6 +7,8 @@ import ( "github.com/navidrome/navidrome/tests" "github.com/navidrome/navidrome/utils" + "github.com/navidrome/navidrome/conf" + "github.com/navidrome/navidrome/model/metadata" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -252,6 +254,46 @@ var _ = Describe("Extractor", func() { }) }) + Context("when metadata cannot be read", func() { + var invalidFile string + + BeforeEach(func() { + invalidFile = utils.TempFileName("invalid-audio-", ".xm") + + Expect(os.WriteFile(invalidFile, []byte("this is not a valid audio file"), 0644)).To(Succeed()) + + DeferCleanup(func() { + Expect(os.Remove(invalidFile)).To(Succeed()) + }) + + // Use root fs for absolute paths in temp directory + e = &extractor{fs: os.DirFS("/")} + }) + + It("skips the file when ImportOnReadError is disabled", func() { + conf.Server.Scanner.ImportOnReadError = false + + // Strip leading slash for DirFS rooted at "/" + mds, err := e.Parse(invalidFile[1:]) + Expect(err).ToNot(HaveOccurred()) + Expect(mds).ToNot(HaveKey(invalidFile[1:])) + }) + + It("imports the file with empty metadata when ImportOnReadError is enabled", func() { + conf.Server.Scanner.ImportOnReadError = true + + // Strip leading slash for DirFS rooted at "/" + mds, err := e.Parse(invalidFile[1:]) + Expect(err).ToNot(HaveOccurred()) + Expect(mds).To(HaveKey(invalidFile[1:])) + + m := mds[invalidFile[1:]] + Expect(m.Tags).To(BeEmpty()) + Expect(m.HasPicture).To(BeFalse()) + Expect(m.AudioProperties).To(Equal(metadata.AudioProperties{})) + }) + }) + }) Describe("Error Checking", func() {