feat(artwork): improve album cover art selection by considering disc numbers

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-04-11 19:10:03 -04:00
parent 8ac867429e
commit 03427b64e0
2 changed files with 44 additions and 6 deletions

View File

@ -183,6 +183,8 @@ func (mfs MediaFiles) ToAlbum() Album {
tags := make(TagList, 0, len(mfs[0].Tags)*len(mfs))
a.Missing = true
embedArtPath := ""
embedArtDisc := 0
for _, m := range mfs {
// We assume these attributes are all the same for all songs in an album
a.ID = m.AlbumID
@ -218,7 +220,7 @@ func (mfs MediaFiles) ToAlbum() Album {
a.Participants.Merge(m.Participants)
// Find the MediaFile with cover art and the lowest disc number to use for album cover
a.EmbedArtPath = firstArtPath(a.EmbedArtPath, m)
embedArtPath, embedArtDisc = firstArtPath(embedArtPath, embedArtDisc, m)
if m.ExplicitStatus == "c" && a.ExplicitStatus != "e" {
a.ExplicitStatus = "c"
@ -231,6 +233,7 @@ func (mfs MediaFiles) ToAlbum() Album {
a.Missing = a.Missing && m.Missing
}
a.EmbedArtPath = embedArtPath
a.SetTags(tags)
a.FolderIDs = slice.Unique(slice.Map(mfs, func(m MediaFile) string { return m.FolderID }))
a.Date, _ = allOrNothing(dates)
@ -307,15 +310,24 @@ func fixAlbumArtist(a *Album) {
// firstArtPath determines which media file path should be used for album artwork
// based on disc number (preferring lower disc numbers) and path (for consistency)
func firstArtPath(currentPath string, m MediaFile) string {
func firstArtPath(currentPath string, currentDisc int, m MediaFile) (string, int) {
if !m.HasCoverArt {
return currentPath
return currentPath, currentDisc
}
if m.Path < currentPath || currentPath == "" {
return m.Path
// If current has no disc number (currentDisc == 0) or new file has lower disc number
if currentDisc == 0 || (m.DiscNumber < currentDisc && m.DiscNumber > 0) {
return m.Path, m.DiscNumber
}
return currentPath
// If disc numbers are equal, use path for ordering
if m.DiscNumber == currentDisc {
if m.Path < currentPath || currentPath == "" {
return m.Path, m.DiscNumber
}
}
return currentPath, currentDisc
}
type MediaFileCursor iter.Seq2[MediaFile, error]

View File

@ -373,6 +373,32 @@ var _ = Describe("MediaFiles", func() {
Expect(album.EmbedArtPath).To(Equal("Artist/Album/Disc2/01.mp3"))
})
})
When("we have media files with path names that don't correlate with disc numbers", func() {
BeforeEach(func() {
mfs = MediaFiles{
{
Path: "Artist/Album/file-z.mp3", // Path would be sorted last alphabetically
HasCoverArt: true,
DiscNumber: 1, // But it has lowest disc number
},
{
Path: "Artist/Album/file-a.mp3", // Path would be sorted first alphabetically
HasCoverArt: true,
DiscNumber: 2, // But it has higher disc number
},
{
Path: "Artist/Album/file-m.mp3",
HasCoverArt: true,
DiscNumber: 3,
},
}
})
It("selects the cover art from the lowest disc number regardless of path", func() {
album := mfs.ToAlbum()
Expect(album.EmbedArtPath).To(Equal("Artist/Album/file-z.mp3"))
})
})
})
})
})