mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
feat(jellyfin): version album and artist image tags by content hash
This commit is contained in:
parent
49e8464db4
commit
6f8e33d28a
@ -200,7 +200,25 @@ func SongToBaseItem(mf model.MediaFile, fields Fields) BaseItemDto {
|
||||
return item
|
||||
}
|
||||
|
||||
// primaryImageTag returns the image cache tag plus the real-blurhash map; absent art yields ("", nil),
|
||||
// and unresolved art falls back to the id. A blurhash is never synthesized: Finamp keys its cover cache
|
||||
// on the value, so a fake one pins a stale cover forever (#5798).
|
||||
func primaryImageTag(img model.ItemImage, fallback string) (string, map[string]map[string]string) {
|
||||
if img.ImageAbsent {
|
||||
return "", nil
|
||||
}
|
||||
tag := img.ImageHash
|
||||
if tag == "" {
|
||||
tag = fallback
|
||||
}
|
||||
if img.BlurHash == "" {
|
||||
return tag, nil
|
||||
}
|
||||
return tag, map[string]map[string]string{"Primary": {tag: img.BlurHash}}
|
||||
}
|
||||
|
||||
func AlbumToBaseItem(al model.Album, fields Fields) BaseItemDto {
|
||||
tag, blurs := primaryImageTag(al.ItemImage, al.ID)
|
||||
item := BaseItemDto{
|
||||
Name: al.Name,
|
||||
Id: EncodeID(al.ID),
|
||||
@ -213,11 +231,13 @@ func AlbumToBaseItem(al model.Album, fields Fields) BaseItemDto {
|
||||
SongCount: new(al.SongCount),
|
||||
RunTimeTicks: TicksFromSeconds(al.Duration),
|
||||
DateCreated: jellyfinDate(&al.CreatedAt),
|
||||
ImageTags: map[string]string{"Primary": al.ID},
|
||||
ImageBlurHashes: map[string]map[string]string{"Primary": {al.ID: blurHash(al.ID)}},
|
||||
ImageBlurHashes: blurs,
|
||||
BackdropImageTags: []string{},
|
||||
UserData: UserData(al.Annotations, al.ID),
|
||||
}
|
||||
if tag != "" {
|
||||
item.ImageTags = map[string]string{"Primary": tag}
|
||||
}
|
||||
if al.AlbumArtistID != "" {
|
||||
item.AlbumArtists = []NameGuidPair{{Name: al.AlbumArtist, Id: EncodeID(al.AlbumArtistID)}}
|
||||
item.ArtistItems = item.AlbumArtists
|
||||
@ -247,7 +267,8 @@ func AlbumToBaseItem(al model.Album, fields Fields) BaseItemDto {
|
||||
}
|
||||
|
||||
func ArtistToBaseItem(ar model.Artist) BaseItemDto {
|
||||
return BaseItemDto{
|
||||
tag, blurs := primaryImageTag(ar.ItemImage, ar.ID)
|
||||
item := BaseItemDto{
|
||||
Name: ar.Name,
|
||||
Id: EncodeID(ar.ID),
|
||||
Type: "MusicArtist",
|
||||
@ -255,11 +276,14 @@ func ArtistToBaseItem(ar model.Artist) BaseItemDto {
|
||||
AlbumCount: new(ar.AlbumCount),
|
||||
SongCount: new(ar.SongCount),
|
||||
DateCreated: jellyfinDate(ar.CreatedAt),
|
||||
ImageTags: map[string]string{"Primary": ar.ID},
|
||||
ImageBlurHashes: map[string]map[string]string{"Primary": {ar.ID: blurHash(ar.ID)}},
|
||||
ImageBlurHashes: blurs,
|
||||
BackdropImageTags: []string{},
|
||||
UserData: UserData(ar.Annotations, ar.ID),
|
||||
}
|
||||
if tag != "" {
|
||||
item.ImageTags = map[string]string{"Primary": tag}
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func GenreToBaseItem(g model.Genre) BaseItemDto {
|
||||
|
||||
@ -254,8 +254,8 @@ var _ = Describe("mappers", func() {
|
||||
Expect(item.ArtistItems).To(Equal(item.AlbumArtists))
|
||||
Expect(*item.ProductionYear).To(Equal(1999))
|
||||
Expect(*item.ChildCount).To(Equal(10))
|
||||
Expect(item.ImageBlurHashes["Primary"]).To(HaveKey(item.ImageTags["Primary"]))
|
||||
Expect(item.ImageBlurHashes["Primary"][item.ImageTags["Primary"]]).To(HaveLen(6))
|
||||
Expect(item.ImageTags).To(HaveKeyWithValue("Primary", "alb-1"))
|
||||
Expect(item.ImageBlurHashes).To(BeNil())
|
||||
Expect(item.Genres).To(Equal([]string{"genre 1", "genre 2"}))
|
||||
Expect(item.GenreItems).To(Equal([]NameGuidPair{{Id: EncodeID("1"), Name: "genre 1"}, {Id: EncodeID("2"), Name: "genre 2"}}))
|
||||
})
|
||||
@ -384,6 +384,52 @@ var _ = Describe("mappers", func() {
|
||||
p := model.Playlist{ID: "pl-1", UpdatedAt: time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC)}
|
||||
Expect(PlaylistToBaseItem(p).ImageTags).To(Equal(PlaylistToBaseItem(p).ImageTags))
|
||||
})
|
||||
|
||||
Describe("primary image tags", func() {
|
||||
It("uses the content hash as the tag and emits the real blurhash", func() {
|
||||
al := model.Album{ID: "alb-1", Name: "Album"}
|
||||
al.ImageHash = "0123456789abcdef"
|
||||
al.BlurHash = "LEHV6nWB2yk8"
|
||||
|
||||
item := AlbumToBaseItem(al, nil)
|
||||
Expect(item.ImageTags).To(HaveKeyWithValue("Primary", "0123456789abcdef"))
|
||||
Expect(item.ImageBlurHashes["Primary"]).To(HaveKeyWithValue("0123456789abcdef", "LEHV6nWB2yk8"))
|
||||
})
|
||||
|
||||
It("omits the blurhash entirely when none was computed", func() {
|
||||
al := model.Album{ID: "alb-2", Name: "Album"}
|
||||
al.ImageHash = "0123456789abcdef"
|
||||
|
||||
item := AlbumToBaseItem(al, nil)
|
||||
Expect(item.ImageTags).To(HaveKeyWithValue("Primary", "0123456789abcdef"))
|
||||
Expect(item.ImageBlurHashes).To(BeNil(), "a synthesized blurhash pins stale covers in Finamp")
|
||||
})
|
||||
|
||||
It("omits tags for known-absent artwork", func() {
|
||||
al := model.Album{ID: "alb-3", Name: "Album"}
|
||||
al.ImageAbsent = true
|
||||
|
||||
item := AlbumToBaseItem(al, nil)
|
||||
Expect(item.ImageTags).To(BeEmpty())
|
||||
Expect(item.ImageBlurHashes).To(BeNil())
|
||||
})
|
||||
|
||||
It("falls back to the entity id while artwork is still unresolved", func() {
|
||||
item := AlbumToBaseItem(model.Album{ID: "alb-4", Name: "Album"}, nil)
|
||||
Expect(item.ImageTags).To(HaveKeyWithValue("Primary", "alb-4"))
|
||||
Expect(item.ImageBlurHashes).To(BeNil())
|
||||
})
|
||||
|
||||
It("versions an artist's tag by content hash", func() {
|
||||
ar := model.Artist{ID: "art-1", Name: "Artist"}
|
||||
ar.ImageHash = "fedcba9876543210"
|
||||
ar.BlurHash = "L6PZfSi_.AyE"
|
||||
|
||||
item := ArtistToBaseItem(ar)
|
||||
Expect(item.ImageTags).To(HaveKeyWithValue("Primary", "fedcba9876543210"))
|
||||
Expect(item.ImageBlurHashes["Primary"]).To(HaveKeyWithValue("fedcba9876543210", "L6PZfSi_.AyE"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("LyricDtoFromLyrics", func() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user