refactor(artwork): name the hashed image store folder for how it is addressed

The content-addressed store sat in artwork/store/, which did not
distinguish it from the artist/, playlist/ and radio/ upload folders
beside it — those hold artwork too. It is now artwork/hashed/, naming the
one property that sets it apart, and the path comes from a consts entry
rather than a bare literal, matching how the sibling folders are built.

Deliberately not under cache/: that folder holds resizes that rebuild
offline from a local source, while this one holds the only local copy of
externally fetched images, whose re-fetch depends on a third party still
serving them and whose bytes back the stored blurhash and dimensions.

No migration: anything left in the old artwork/store/ is orphaned and
re-resolved into the new location.
This commit is contained in:
Deluan 2026-07-29 21:29:16 -04:00
parent 34f6684922
commit 1e0a55a793
3 changed files with 7 additions and 4 deletions

View File

@ -26,7 +26,7 @@ const (
DBAnalyzeFailureCountKey = "DBAnalyzeFailureCount"
// ArtConfFingerprintPropertyKey is the model.PropertyRepository key Backfill compares against
// to detect artwork-affecting config changes across restarts.
ArtConfFingerprintPropertyKey = "artwork.fingerprint"
ArtConfFingerprintPropertyKey = "ArtConfFingerprint"
UIAuthorizationHeader = "X-ND-Authorization"
UIClientUniqueIDHeader = "X-ND-Client-Unique-Id"
@ -86,6 +86,9 @@ const (
I18nFolder = "i18n"
ScanIgnoreFile = ".ndignore"
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.
HashedArtworkFolder = "hashed"
PlaceholderArtistArt = "artist-placeholder.webp"
PlaceholderAlbumArt = "album-placeholder.webp"

View File

@ -20,6 +20,7 @@ import (
_ "github.com/navidrome/navidrome/adapters/gotaglib"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/core/metrics"
@ -110,7 +111,7 @@ func setupResolutionHarness() {
storagetest.Register(fakeLibScheme, fakeFS)
ffm := tests.NewMockFFmpeg("")
rstore = artwork.NewImageStore(filepath.Join(tempDir, "store"))
rstore = artwork.NewImageStore(filepath.Join(tempDir, consts.HashedArtworkFolder))
// size=0 requests stream originals, so this reader is never called (serving_test covers resizing).
imgCache := cache.NewFileCache("ArtworkResolutionE2E", "100MB", "images", 0,
func(context.Context, cache.Item) (io.Reader, error) {

View File

@ -25,9 +25,8 @@ func NewImageStore(rootDir string) *ImageStore {
return &ImageStore{root: rootDir}
}
// GetImageStore roots the store in its own subtree so Prune's sweep never reaches the upload folders beside it.
func GetImageStore() *ImageStore {
return NewImageStore(filepath.Join(conf.Server.DataFolder.String(), consts.ArtworkFolder, "store"))
return NewImageStore(filepath.Join(conf.Server.DataFolder.String(), consts.ArtworkFolder, consts.HashedArtworkFolder))
}
// extForMime must stay stable across OSes: extensions are baked into stored paths and re-derived on Open.