fix(artwork): open library-backed artwork through its on-disk root

A library configured with a file:// path stored absRoot as the raw URI, so Abs
produced strings like file:/music/cover.jpg that os.Open/os.Stat reject — folder,
upload and embedded art were treated as dangling on every request, looping forever.
Normalize a file:// path to its parsed OS path (the same root os.DirFS uses);
non-local schemes are left unchanged (out of scope, per the artwork-musicfs TODO).
This commit is contained in:
Deluan 2026-07-23 08:01:00 -04:00
parent f016192eec
commit ce06599288
2 changed files with 23 additions and 1 deletions

View File

@ -2,7 +2,9 @@ package artwork
import (
"context"
"net/url"
"path/filepath"
"strings"
"github.com/navidrome/navidrome/core/storage"
"github.com/navidrome/navidrome/model"
@ -40,5 +42,18 @@ func loadLibraryView(ctx context.Context, ds model.DataStore, libID int) (librar
if err != nil {
return libraryView{}, err
}
return libraryView{FS: fs, absRoot: lib.Path}, nil
return libraryView{FS: fs, absRoot: localOSRoot(lib.Path)}, nil
}
// localOSRoot maps a library path to its on-disk root so Abs() yields paths os.Open/os.Stat accept:
// a file:// URL becomes its parsed OS path (bare paths already are; non-local schemes stay unchanged).
func localOSRoot(libPath string) string {
if !strings.Contains(libPath, "://") {
return libPath
}
u, err := url.Parse(libPath)
if err != nil || u.Scheme != storage.LocalSchemaID {
return libPath
}
return u.Path
}

View File

@ -32,6 +32,13 @@ var _ = Describe("loadLibraryView", Ordered, func() {
Expect(lib.absRoot).To(Equal("fake:///music"))
})
It("normalizes a library path to an OS root that Abs can join for os.Open/os.Stat", func() {
// file:// URLs become their parsed OS path; bare paths and non-local schemes are unchanged.
Expect(localOSRoot("file:///music/library")).To(Equal("/music/library"))
Expect(localOSRoot("/music/library")).To(Equal("/music/library"))
Expect(localOSRoot("fake:///music")).To(Equal("fake:///music"))
})
It("returns an error when the library does not exist", func() {
_, err := loadLibraryView(ctx, ds, 999)
Expect(err).To(HaveOccurred())