navidrome/model/artwork_test.go
Deluan d732e5419a fix(artwork): shape the blurhash placeholder to the artwork's aspect ratio
The UI decoded every blurhash into a 32x32 bitmap and stretched it to fill its
container, so a non-square cover showed a full-box blur that collapsed into a
letterboxed image the moment it loaded. On the album detail page the
placeholder overhung the image by a third of the box height.

A blurhash string carries no aspect ratio of its own, so the dimensions have to
come from the server. artwork.width/height were already stored and read by
nobody; they now surface on ItemImage as imageWidth/imageHeight, hydrated
through the join that was already in place. Existing rows already carry them,
so no migration or rescan is needed.

A square request is padded rather than cropped, which aspect-fits the content
inside the square the server returns. That made the grid a second instance of
the same bug, so `square` now implies contain for the image as well as the
placeholder, instead of the two renderers reading it differently.
2026-07-26 10:21:45 -04:00

66 lines
2.1 KiB
Go

package model_test
import (
"encoding/json"
"github.com/navidrome/navidrome/model"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("ItemImage JSON", func() {
It("exposes artwork state on an album", func() {
al := model.Album{ID: "al-1", Name: "Album"}
al.ImageHash = "0123456789abcdef"
al.BlurHash = "LEHV6nWB2yk8"
var out map[string]any
data, err := json.Marshal(al)
Expect(err).ToNot(HaveOccurred())
Expect(json.Unmarshal(data, &out)).To(Succeed())
Expect(out).To(HaveKeyWithValue("imageHash", "0123456789abcdef"))
Expect(out).To(HaveKeyWithValue("blurHash", "LEHV6nWB2yk8"))
})
// Clients decode the blurhash into a bitmap of their choosing, so without the dimensions they
// cannot know the placeholder's shape and default to a square.
It("exposes the image dimensions alongside the blurhash", func() {
al := model.Album{ID: "al-3", Name: "Album"}
al.BlurHash = "LEHV6nWB2yk8"
al.ImageWidth, al.ImageHeight = 1200, 800
var out map[string]any
data, err := json.Marshal(al)
Expect(err).ToNot(HaveOccurred())
Expect(json.Unmarshal(data, &out)).To(Succeed())
Expect(out).To(HaveKeyWithValue("imageWidth", BeNumerically("==", 1200)))
Expect(out).To(HaveKeyWithValue("imageHeight", BeNumerically("==", 800)))
})
It("omits artwork state when the entity has none", func() {
var out map[string]any
data, err := json.Marshal(model.Album{ID: "al-2", Name: "Album"})
Expect(err).ToNot(HaveOccurred())
Expect(json.Unmarshal(data, &out)).To(Succeed())
Expect(out).ToNot(HaveKey("imageHash"))
Expect(out).ToNot(HaveKey("blurHash"))
Expect(out).ToNot(HaveKey("imageAbsent"))
Expect(out).ToNot(HaveKey("imageWidth"))
Expect(out).ToNot(HaveKey("imageHeight"))
})
It("exposes known-absent artwork so clients can skip the request", func() {
ar := model.Artist{ID: "ar-1", Name: "Artist"}
ar.ImageAbsent = true
var out map[string]any
data, err := json.Marshal(ar)
Expect(err).ToNot(HaveOccurred())
Expect(json.Unmarshal(data, &out)).To(Succeed())
Expect(out).To(HaveKeyWithValue("imageAbsent", true))
})
})