111 lines
3.9 KiB
Go
111 lines
3.9 KiB
Go
|
|
package artworke2e_test
|
||
|
|
|
||
|
|
import (
|
||
|
|
"testing/fstest"
|
||
|
|
|
||
|
|
"github.com/Masterminds/squirrel"
|
||
|
|
"github.com/navidrome/navidrome/conf"
|
||
|
|
"github.com/navidrome/navidrome/model"
|
||
|
|
. "github.com/onsi/ginkgo/v2"
|
||
|
|
. "github.com/onsi/gomega"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Doc reference:
|
||
|
|
// https://www.navidrome.org/docs/usage/library/artwork/#mediafiles
|
||
|
|
// Navidrome resolves mediafile artwork in this order:
|
||
|
|
// 1. Embedded image from the mediafile itself
|
||
|
|
// 2. For multi-disc albums, disc-level artwork
|
||
|
|
// 3. Album cover art
|
||
|
|
//
|
||
|
|
// FakeFS cannot synthesize taglib-readable embedded JPEGs, so scenario (1)
|
||
|
|
// is covered by the existing embedded-art album tests (which currently
|
||
|
|
// Skip under FakeFS). The tests below cover (2) and (3): the fallback
|
||
|
|
// chain for tracks without embedded art.
|
||
|
|
var _ = Describe("MediaFile artwork fallback", func() {
|
||
|
|
BeforeEach(func() {
|
||
|
|
setupHarness()
|
||
|
|
})
|
||
|
|
|
||
|
|
When("a multi-disc album track has no embedded art", func() {
|
||
|
|
// Artist/
|
||
|
|
// └── Album/
|
||
|
|
// ├── CD1/
|
||
|
|
// │ ├── 01 - Track.mp3
|
||
|
|
// │ └── disc1.jpg
|
||
|
|
// ├── CD2/
|
||
|
|
// │ ├── 01 - Track.mp3 ← track requested
|
||
|
|
// │ └── disc2.jpg ← wins (disc-level before album-level)
|
||
|
|
// └── cover.jpg
|
||
|
|
It("falls back to the disc-level artwork (not the album cover)", func() {
|
||
|
|
conf.Server.CoverArtPriority = defaultCoverPriority
|
||
|
|
conf.Server.DiscArtPriority = defaultDiscPriority
|
||
|
|
setLayout(fstest.MapFS{
|
||
|
|
"Artist/Album/CD1/01 - Track.mp3": trackFile(1, "T1", map[string]any{"disc": "1"}),
|
||
|
|
"Artist/Album/CD2/01 - Track.mp3": trackFile(1, "T2", map[string]any{"disc": "2"}),
|
||
|
|
"Artist/Album/CD1/disc1.jpg": imageFile("disc-1"),
|
||
|
|
"Artist/Album/CD2/disc2.jpg": imageFile("disc-2"),
|
||
|
|
"Artist/Album/cover.jpg": imageFile("album-root"),
|
||
|
|
})
|
||
|
|
scan()
|
||
|
|
|
||
|
|
mf := mediafileOn("Artist/Album/CD2/01 - Track.mp3")
|
||
|
|
Expect(readArtwork(mf.CoverArtID())).To(Equal(imageBytes("disc-2")))
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
When("a single-disc album track has no embedded art", func() {
|
||
|
|
// Artist/
|
||
|
|
// └── Album/
|
||
|
|
// ├── 01 - Track.mp3 ← track requested
|
||
|
|
// └── cover.jpg ← wins (album-level fallback, no disc subfolder)
|
||
|
|
It("falls back to the album cover", func() {
|
||
|
|
conf.Server.CoverArtPriority = defaultCoverPriority
|
||
|
|
conf.Server.DiscArtPriority = defaultDiscPriority
|
||
|
|
setLayout(fstest.MapFS{
|
||
|
|
"Artist/Album/01 - Track.mp3": trackFile(1, "Track"),
|
||
|
|
"Artist/Album/cover.jpg": imageFile("album-cover"),
|
||
|
|
})
|
||
|
|
scan()
|
||
|
|
|
||
|
|
mf := mediafileOn("Artist/Album/01 - Track.mp3")
|
||
|
|
Expect(readArtwork(mf.CoverArtID())).To(Equal(imageBytes("album-cover")))
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
When("a multi-disc album track has no embedded art and the disc has no disc-level image", func() {
|
||
|
|
// Artist/
|
||
|
|
// └── Album/
|
||
|
|
// ├── CD1/
|
||
|
|
// │ └── 01 - Track.mp3
|
||
|
|
// ├── CD2/
|
||
|
|
// │ └── 01 - Track.mp3 ← track requested
|
||
|
|
// └── cover.jpg ← wins (no disc image → album-level fallback)
|
||
|
|
It("falls through from disc to album cover", func() {
|
||
|
|
conf.Server.CoverArtPriority = defaultCoverPriority
|
||
|
|
conf.Server.DiscArtPriority = defaultDiscPriority
|
||
|
|
setLayout(fstest.MapFS{
|
||
|
|
"Artist/Album/CD1/01 - Track.mp3": trackFile(1, "T1", map[string]any{"disc": "1"}),
|
||
|
|
"Artist/Album/CD2/01 - Track.mp3": trackFile(1, "T2", map[string]any{"disc": "2"}),
|
||
|
|
"Artist/Album/cover.jpg": imageFile("album-root"),
|
||
|
|
})
|
||
|
|
scan()
|
||
|
|
|
||
|
|
mf := mediafileOn("Artist/Album/CD2/01 - Track.mp3")
|
||
|
|
Expect(readArtwork(mf.CoverArtID())).To(Equal(imageBytes("album-root")))
|
||
|
|
})
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
func mediafileOn(relPath string) model.MediaFile {
|
||
|
|
GinkgoHelper()
|
||
|
|
mfs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{
|
||
|
|
Filters: squirrel.Like{"media_file.path": relPath},
|
||
|
|
})
|
||
|
|
Expect(err).ToNot(HaveOccurred())
|
||
|
|
if len(mfs) == 0 {
|
||
|
|
Fail("mediafile not found: " + relPath)
|
||
|
|
return model.MediaFile{}
|
||
|
|
}
|
||
|
|
return mfs[0]
|
||
|
|
}
|