mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
feat(artwork): implement artwork repository
This commit is contained in:
parent
6cce65f759
commit
f926539c04
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
. "github.com/Masterminds/squirrel"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/pocketbase/dbx"
|
||||
)
|
||||
@ -21,23 +22,61 @@ func NewArtworkRepository(ctx context.Context, db dbx.Builder) model.ArtworkRepo
|
||||
}
|
||||
|
||||
func (r *artworkRepository) Get(hash string) (*model.Artwork, error) {
|
||||
return nil, model.ErrNotFound
|
||||
sel := Select("*").From(r.tableName).Where(Eq{"hash": hash})
|
||||
var res model.Artwork
|
||||
if err := r.queryOne(sel, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (r *artworkRepository) Put(a *model.Artwork) error {
|
||||
return nil
|
||||
if a.CreatedAt.IsZero() {
|
||||
a.CreatedAt = time.Now()
|
||||
}
|
||||
ins := Insert(r.tableName).SetMap(map[string]any{
|
||||
"hash": a.Hash, "mime": a.Mime, "width": a.Width, "height": a.Height,
|
||||
"size_bytes": a.SizeBytes, "blur_hash": a.BlurHash,
|
||||
"source_path": a.SourcePath, "ref_mtime": a.RefMtime, "created_at": a.CreatedAt,
|
||||
}).Suffix(`ON CONFLICT (hash) DO UPDATE SET mime=excluded.mime, width=excluded.width,
|
||||
height=excluded.height, size_bytes=excluded.size_bytes, blur_hash=excluded.blur_hash,
|
||||
source_path=excluded.source_path, ref_mtime=excluded.ref_mtime`)
|
||||
_, err := r.executeSQL(ins)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *artworkRepository) GetBatch(hashes []string) (map[string]model.Artwork, error) {
|
||||
return nil, nil
|
||||
res := map[string]model.Artwork{}
|
||||
if len(hashes) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
sel := Select("*").From(r.tableName).Where(Eq{"hash": hashes})
|
||||
var all []model.Artwork
|
||||
if err := r.queryAll(sel, &all); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range all {
|
||||
res[a.Hash] = a
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *artworkRepository) GetOrphanHashes(createdBefore time.Time) ([]string, error) {
|
||||
return nil, nil
|
||||
sel := Select("hash").From(r.tableName).
|
||||
Where(And{
|
||||
Lt{"created_at": createdBefore},
|
||||
Expr("hash NOT IN (SELECT hash FROM item_artwork WHERE hash <> '')"),
|
||||
})
|
||||
var hashes []string
|
||||
err := r.queryAllSlice(sel, &hashes)
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
func (r *artworkRepository) Delete(hashes ...string) error {
|
||||
return nil
|
||||
if len(hashes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.delete(Eq{"hash": hashes})
|
||||
}
|
||||
|
||||
var _ model.ArtworkRepository = (*artworkRepository)(nil)
|
||||
|
||||
85
persistence/artwork_repository_test.go
Normal file
85
persistence/artwork_repository_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
// clearArtworkTables resets the shared test DB's artwork tables so specs don't leak state.
|
||||
func clearArtworkTables() {
|
||||
db := GetDBXBuilder()
|
||||
for _, t := range []string{"artwork_queue", "item_artwork", "artwork"} {
|
||||
_, err := db.NewQuery("DELETE FROM " + t).Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("ArtworkRepository", func() {
|
||||
var repo model.ArtworkRepository
|
||||
|
||||
BeforeEach(func() {
|
||||
clearArtworkTables()
|
||||
repo = NewArtworkRepository(context.Background(), GetDBXBuilder())
|
||||
})
|
||||
|
||||
It("stores and retrieves an artwork by hash", func() {
|
||||
a := &model.Artwork{Hash: "abc123", Mime: "image/jpeg", Width: 500, Height: 500, SizeBytes: 1234, BlurHash: "LKO2?U%2Tw=w"}
|
||||
Expect(repo.Put(a)).To(Succeed())
|
||||
|
||||
got, err := repo.Get("abc123")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Mime).To(Equal("image/jpeg"))
|
||||
Expect(got.BlurHash).To(Equal("LKO2?U%2Tw=w"))
|
||||
Expect(got.CreatedAt).ToNot(BeZero())
|
||||
})
|
||||
|
||||
It("is idempotent on Put (upsert by hash)", func() {
|
||||
a := &model.Artwork{Hash: "dup1", Mime: "image/png"}
|
||||
Expect(repo.Put(a)).To(Succeed())
|
||||
a.BlurHash = "XYZ"
|
||||
Expect(repo.Put(a)).To(Succeed())
|
||||
got, _ := repo.Get("dup1")
|
||||
Expect(got.BlurHash).To(Equal("XYZ"))
|
||||
})
|
||||
|
||||
It("returns ErrNotFound for a missing hash", func() {
|
||||
_, err := repo.Get("nope")
|
||||
Expect(err).To(MatchError(model.ErrNotFound))
|
||||
})
|
||||
|
||||
It("fetches a batch", func() {
|
||||
Expect(repo.Put(&model.Artwork{Hash: "b1", Mime: "image/jpeg"})).To(Succeed())
|
||||
Expect(repo.Put(&model.Artwork{Hash: "b2", Mime: "image/png"})).To(Succeed())
|
||||
got, err := repo.GetBatch([]string{"b1", "b2", "missing"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got).To(HaveLen(2))
|
||||
Expect(got["b2"].Mime).To(Equal("image/png"))
|
||||
})
|
||||
|
||||
It("finds orphans older than cutoff, honoring item_artwork references", func() {
|
||||
Expect(repo.Put(&model.Artwork{Hash: "orph1", Mime: "image/jpeg"})).To(Succeed())
|
||||
Expect(repo.Put(&model.Artwork{Hash: "ref1", Mime: "image/jpeg"})).To(Succeed())
|
||||
iaRepo := NewItemArtworkRepository(context.Background(), GetDBXBuilder())
|
||||
Expect(iaRepo.Put(&model.ItemArtwork{ItemKind: "al", ItemID: "a1", ImageType: model.ImageTypePrimary, Hash: "ref1", Source: "folder"})).To(Succeed())
|
||||
|
||||
orphans, err := repo.GetOrphanHashes(time.Now().Add(time.Minute))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(orphans).To(ContainElement("orph1"))
|
||||
Expect(orphans).ToNot(ContainElement("ref1"))
|
||||
|
||||
orphans, err = repo.GetOrphanHashes(time.Now().Add(-time.Hour))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(orphans).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("deletes by hashes", func() {
|
||||
Expect(repo.Put(&model.Artwork{Hash: "d1", Mime: "image/jpeg"})).To(Succeed())
|
||||
Expect(repo.Delete("d1")).To(Succeed())
|
||||
_, err := repo.Get("d1")
|
||||
Expect(err).To(MatchError(model.ErrNotFound))
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user