From a1eb5e83432c01ad842908dd4c8d12c6fda46e98 Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 24 Jul 2026 10:46:59 -0400 Subject: [PATCH] refactor(artwork): route song own-art through primaryImageTag; align chunk size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanups surfaced by /simplify: the song mapper's own-art branch reimplemented primaryImageTag's tag+blurhash-map construction (and its one-entry invariant) — route it through the helper so that invariant lives in one place. Tie artworkChunkSize to a whole multiple of artworkBatchSize so a cursor page re-chunks into even hydration batches. Hoist a duplicated imageLoading && blurHash boolean in the album grid. --- persistence/artwork_hydration.go | 6 +++--- server/jellyfin/dto/mappers.go | 10 ++++------ ui/src/album/AlbumGridView.jsx | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/persistence/artwork_hydration.go b/persistence/artwork_hydration.go index 1b03484b5..a8a6fd5eb 100644 --- a/persistence/artwork_hydration.go +++ b/persistence/artwork_hydration.go @@ -11,9 +11,9 @@ import ( "github.com/pocketbase/dbx" ) -// artworkChunkSize bounds the id IN-list of each chunk fetch, keeping it under SQLite's bound -// parameter limit. -const artworkChunkSize = 500 +// artworkChunkSize bounds each chunk fetch's id IN-list (under SQLite's bound-parameter limit); a +// whole multiple of artworkBatchSize so a page re-chunks into even hydration batches. +const artworkChunkSize = artworkBatchSize * 3 // streamByIDs yields the rows of ids in chunks, fetching each chunk through the caller's hydrating // fetch. Resolving ids first keeps OFFSET out of the joined query (spec §6). diff --git a/server/jellyfin/dto/mappers.go b/server/jellyfin/dto/mappers.go index e78eb0e81..f47b3788d 100644 --- a/server/jellyfin/dto/mappers.go +++ b/server/jellyfin/dto/mappers.go @@ -192,13 +192,11 @@ func SongToBaseItem(mf model.MediaFile, fields Fields) BaseItemDto { } else if mf.Genre != "" { item.Genres = []string{mf.Genre} } - // A track's own cover wins in Finamp's precedence (ImageTags.Primary before AlbumId); emit only - // the matching blurhash, since Go sorts map keys and a second entry could pair the wrong one. + // A track's own cover wins in Finamp's precedence (ImageTags.Primary before AlbumId). if mf.ImageHash != "" && mf.ImageHash != mf.AlbumImage.ImageHash { - item.ImageTags = map[string]string{"Primary": mf.ImageHash} - if mf.BlurHash != "" { - item.ImageBlurHashes = map[string]map[string]string{"Primary": {mf.ImageHash: mf.BlurHash}} - } + tag, blurs := primaryImageTag(mf.ItemImage, mf.ID) + item.ImageTags = map[string]string{"Primary": tag} + item.ImageBlurHashes = blurs } else if mf.AlbumID != "" { if tag, blurs := primaryImageTag(mf.AlbumImage, mf.AlbumID); tag != "" { item.AlbumPrimaryImageTag = tag diff --git a/ui/src/album/AlbumGridView.jsx b/ui/src/album/AlbumGridView.jsx index ebdac6a36..aaa1d5a6a 100644 --- a/ui/src/album/AlbumGridView.jsx +++ b/ui/src/album/AlbumGridView.jsx @@ -141,11 +141,12 @@ const Cover = withContentRect('bounds')(({ const url = subsonic.getCoverArtUrl(record, config.uiCoverArtSize, true) const { imgUrl, loading: imageLoading } = useImageUrl(url) + const showBlurHash = imageLoading && record.blurHash return (
- {imageLoading && record.blurHash && ( + {showBlurHash && ( )} {record.name}