fix(artwork): keep multi-disc tracks requestable when the album is absent

Round-1's hydration fix still copied the album's known-absent onto a non-eligible
(or own-absent) track, but MediaFile.CoverArtID routes a multi-disc track to disc
art, which resolves provisionally and is never known-absent. Marking it absent made
Subsonic omit coverArt so clients never requested a valid disc image. Only mark a
single-disc track absent, and only when its own art won't resolve.
This commit is contained in:
Deluan 2026-07-23 05:30:46 -04:00
parent 66d3d23149
commit 49039fab47
2 changed files with 33 additions and 16 deletions

View File

@ -249,6 +249,17 @@ var _ = Describe("Artwork hydration", func() {
Expect(byID["1003"].ImageAbsent).To(BeTrue())
})
It("keeps a multi-disc track requestable when its album is absent (disc art may resolve)", func() {
putInfo("al", "104", "") // songs 2002/2004 album known-absent
byID := getByID()
// 2002 is a multi-disc track (DiscNumber>0); CoverArtID points at disc art, which
// resolves provisionally, so it must never inherit the album's absence.
Expect(byID["2002"].ImageAbsent).To(BeFalse())
Expect(byID["2002"].ImageHash).To(BeEmpty())
})
It("uses the album hash for an eligible file whose own art is unresolved but album is found", func() {
setCover("1004", true)
DeferCleanup(func() { setCover("1004", false) })

View File

@ -240,24 +240,30 @@ func (r *mediaFileRepository) hydrateArtwork(mfs model.MediaFiles) {
albumInfos := hydrateItemImages(r.ctx, r.db, model.KindAlbumArtwork.Prefix(), albumIDs)
mfInfos := hydrateItemImages(r.ctx, r.db, model.KindMediaFileArtwork.Prefix(), eligibleIDs)
for i := range mfs {
if mfs[i].HasCoverArt && conf.Server.EnableMediaFileCoverArt {
if info, ok := mfInfos[mfs[i].ID]; ok {
if !info.Absent() {
mfs[i].ImageHash = info.Hash
continue
}
// Own art resolved absent → serving delegates to the album, so mirror that.
applyItemImage(albumInfos, mfs[i].AlbumID, &mfs[i].ItemImage)
continue
}
// Eligible but unresolved: take a found album hash for caching, but never the
// album's absence — serving still extracts the track's own embedded art on request.
if albumInfo, ok := albumInfos[mfs[i].AlbumID]; ok && !albumInfo.Absent() {
mfs[i].ImageHash = albumInfo.Hash
}
mf := &mfs[i]
eligible := mf.HasCoverArt && conf.Server.EnableMediaFileCoverArt
ownInfo, ownResolved := mfInfos[mf.ID]
if eligible && ownResolved && !ownInfo.Absent() {
mf.ImageHash = ownInfo.Hash // own resolved art wins
continue
}
applyItemImage(albumInfos, mfs[i].AlbumID, &mfs[i].ItemImage)
// Fallback (see MediaFile.CoverArtID): inherit a found album hash for optimistic caching.
if album, ok := albumInfos[mf.AlbumID]; ok && !album.Absent() {
mf.ImageHash = album.Hash
continue
}
// Nothing found. Mark absent only when serving would definitively yield a placeholder:
// a single-disc track whose album is known-absent and whose own art won't resolve. A
// multi-disc track resolves disc art provisionally (never known-absent), and an
// eligible-but-unresolved track can still extract its own embedded art — both stay
// requestable.
if mf.DiscNumber > 0 {
continue
}
ownWontResolve := !eligible || (ownResolved && ownInfo.Absent())
if album, ok := albumInfos[mf.AlbumID]; ok && album.Absent() && ownWontResolve {
mf.ImageAbsent = true
}
}
}