mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
feat(model): add blur_hash columns and ArtworkUpdatedAt version methods
This commit is contained in:
parent
470b4bdfa0
commit
a115726e71
15
db/migrations/20260716030719_add_artwork_blur_hash.sql
Normal file
15
db/migrations/20260716030719_add_artwork_blur_hash.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
alter table album add column blur_hash varchar;
|
||||
alter table album add column blur_hash_updated_at datetime;
|
||||
alter table artist add column blur_hash varchar;
|
||||
alter table artist add column blur_hash_updated_at datetime;
|
||||
alter table playlist add column blur_hash varchar;
|
||||
alter table playlist add column blur_hash_updated_at datetime;
|
||||
|
||||
-- +goose Down
|
||||
alter table album drop column blur_hash;
|
||||
alter table album drop column blur_hash_updated_at;
|
||||
alter table artist drop column blur_hash;
|
||||
alter table artist drop column blur_hash_updated_at;
|
||||
alter table playlist drop column blur_hash;
|
||||
alter table playlist drop column blur_hash_updated_at;
|
||||
@ -67,12 +67,29 @@ type Album struct {
|
||||
ImportedAt time.Time `structs:"imported_at" json:"importedAt" hash:"ignore"` // When this album was imported/updated
|
||||
CreatedAt time.Time `structs:"created_at" json:"createdAt"` // Oldest CreatedAt for all songs in this album
|
||||
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"` // Newest UpdatedAt for all songs in this album
|
||||
|
||||
// BlurHash of the album cover, computed asynchronously from the served artwork.
|
||||
BlurHash string `structs:"blur_hash" json:"blurHash,omitempty" hash:"ignore"`
|
||||
BlurHashUpdatedAt *time.Time `structs:"blur_hash_updated_at" json:"-" hash:"ignore"`
|
||||
}
|
||||
|
||||
func (a Album) CoverArtID() ArtworkID {
|
||||
return artworkIDFromAlbum(a)
|
||||
}
|
||||
|
||||
// ArtworkUpdatedAt is the album's artwork version: the newest row timestamp that can affect
|
||||
// which cover image is served (scan updates, imports, agent-fetched external images).
|
||||
func (a Album) ArtworkUpdatedAt() time.Time {
|
||||
t := a.UpdatedAt
|
||||
if a.ImportedAt.After(t) {
|
||||
t = a.ImportedAt
|
||||
}
|
||||
if a.ExternalInfoUpdatedAt != nil && a.ExternalInfoUpdatedAt.After(t) {
|
||||
t = *a.ExternalInfoUpdatedAt
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (a Album) FullName() string {
|
||||
if conf.Server.Subsonic.AppendAlbumVersion && len(a.Tags[TagAlbumVersion]) > 0 {
|
||||
return fmt.Sprintf("%s (%s)", a.Name, a.Tags[TagAlbumVersion][0])
|
||||
|
||||
@ -2,6 +2,7 @@ package model_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/conf/configtest"
|
||||
@ -50,3 +51,22 @@ var _ = Describe("Albums", func() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Album.ArtworkUpdatedAt", func() {
|
||||
base := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
later := base.Add(24 * time.Hour)
|
||||
latest := base.Add(48 * time.Hour)
|
||||
|
||||
It("returns UpdatedAt when it is the newest", func() {
|
||||
al := Album{UpdatedAt: later, ImportedAt: base}
|
||||
Expect(al.ArtworkUpdatedAt()).To(Equal(later))
|
||||
})
|
||||
It("returns ImportedAt when it is the newest", func() {
|
||||
al := Album{UpdatedAt: base, ImportedAt: later}
|
||||
Expect(al.ArtworkUpdatedAt()).To(Equal(later))
|
||||
})
|
||||
It("returns ExternalInfoUpdatedAt when it is the newest", func() {
|
||||
al := Album{UpdatedAt: base, ImportedAt: later, ExternalInfoUpdatedAt: &latest}
|
||||
Expect(al.ArtworkUpdatedAt()).To(Equal(latest))
|
||||
})
|
||||
})
|
||||
|
||||
@ -41,6 +41,9 @@ type Artist struct {
|
||||
|
||||
CreatedAt *time.Time `structs:"created_at" json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `structs:"updated_at" json:"updatedAt,omitempty"`
|
||||
|
||||
BlurHash string `structs:"blur_hash" json:"blurHash,omitempty"`
|
||||
BlurHashUpdatedAt *time.Time `structs:"blur_hash_updated_at" json:"-"`
|
||||
}
|
||||
|
||||
type ArtistStats struct {
|
||||
@ -63,6 +66,19 @@ func (a Artist) CoverArtID() ArtworkID {
|
||||
return artworkIDFromArtist(a)
|
||||
}
|
||||
|
||||
// ArtworkUpdatedAt is the artist's artwork version; images often arrive via external agents,
|
||||
// which bump ExternalInfoUpdatedAt rather than UpdatedAt.
|
||||
func (a Artist) ArtworkUpdatedAt() time.Time {
|
||||
var t time.Time
|
||||
if a.UpdatedAt != nil {
|
||||
t = *a.UpdatedAt
|
||||
}
|
||||
if a.ExternalInfoUpdatedAt != nil && a.ExternalInfoUpdatedAt.After(t) {
|
||||
t = *a.ExternalInfoUpdatedAt
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (a Artist) UploadedImagePath() string {
|
||||
return UploadedImagePath(consts.EntityArtist, a.UploadedImage)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package model_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/conf/configtest"
|
||||
@ -28,3 +29,18 @@ var _ = Describe("Artist", func() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Artist.ArtworkUpdatedAt", func() {
|
||||
base := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
later := base.Add(24 * time.Hour)
|
||||
|
||||
It("handles nil timestamps", func() {
|
||||
Expect(model.Artist{}.ArtworkUpdatedAt()).To(Equal(time.Time{}))
|
||||
})
|
||||
It("returns UpdatedAt when newest", func() {
|
||||
Expect(model.Artist{UpdatedAt: &later, ExternalInfoUpdatedAt: &base}.ArtworkUpdatedAt()).To(Equal(later))
|
||||
})
|
||||
It("returns ExternalInfoUpdatedAt when newest", func() {
|
||||
Expect(model.Artist{UpdatedAt: &base, ExternalInfoUpdatedAt: &later}.ArtworkUpdatedAt()).To(Equal(later))
|
||||
})
|
||||
})
|
||||
|
||||
@ -30,6 +30,9 @@ type Playlist struct {
|
||||
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
|
||||
|
||||
BlurHash string `structs:"blur_hash" json:"blurHash,omitempty"`
|
||||
BlurHashUpdatedAt *time.Time `structs:"blur_hash_updated_at" json:"-"`
|
||||
|
||||
// SmartPlaylist attributes
|
||||
Rules *criteria.Criteria `structs:"rules" json:"rules"`
|
||||
EvaluatedAt *time.Time `structs:"evaluated_at" json:"evaluatedAt"`
|
||||
@ -39,6 +42,10 @@ func (pls Playlist) IsSmartPlaylist() bool {
|
||||
return pls.Rules != nil && pls.Rules.Expression != nil
|
||||
}
|
||||
|
||||
func (pls Playlist) ArtworkUpdatedAt() time.Time {
|
||||
return pls.UpdatedAt
|
||||
}
|
||||
|
||||
func (pls Playlist) MediaFiles() MediaFiles {
|
||||
if len(pls.Tracks) == 0 {
|
||||
return nil
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package model_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@ -44,3 +46,10 @@ var _ = Describe("Playlist", func() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Playlist.ArtworkUpdatedAt", func() {
|
||||
It("returns UpdatedAt", func() {
|
||||
now := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
Expect(model.Playlist{UpdatedAt: now}.ArtworkUpdatedAt()).To(Equal(now))
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user