mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
Decode+encode is a few ms (the encoder downscales before its pixel loops), and the tee fires on Close after the response is fully written, so the hash can be computed in the serving goroutine: the queue, wake/stop lifecycle, lazy-start admin context, and the e2e worker-teardown ordering all become unnecessary and are removed. This also closes two correctness holes the queue had: a deletion signal could be dropped behind a pending serve job, and buffered image bytes had no global cap. The in-memory dedup now remembers a checksum of the served bytes plus the persisted version: identical serves skip the decode and the write, but the same bytes under a newer entity version re-persist, so blur_hash_updated_at keeps pace with row updates and the Jellyfin DTO's staleness gate keeps emitting the stored hash after scans. Placeholder-triggered clears check the seen map, then the stored row, so coverless entities cost one row read once per process instead of a probe and write per serve. UpdateBlurHash is a plain UPDATE with no user filtering, so the request context is used directly (a client abort is survived via context.WithoutCancel).
58 lines
1.5 KiB
Go
58 lines
1.5 KiB
Go
package artwork_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
"github.com/navidrome/navidrome/conf/configtest"
|
|
"github.com/navidrome/navidrome/consts"
|
|
"github.com/navidrome/navidrome/core/artwork"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/resources"
|
|
"github.com/navidrome/navidrome/tests"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Artwork", func() {
|
|
var aw artwork.Artwork
|
|
var ds model.DataStore
|
|
var ffmpeg *tests.MockFFmpeg
|
|
|
|
BeforeEach(func() {
|
|
DeferCleanup(configtest.SetupConfig())
|
|
conf.Server.ImageCacheSize = "0" // Disable cache
|
|
cache := artwork.GetImageCache()
|
|
ffmpeg = tests.NewMockFFmpeg("content from ffmpeg")
|
|
aw = artwork.NewArtwork(ds, cache, ffmpeg, nil)
|
|
})
|
|
|
|
Context("GetOrPlaceholder", func() {
|
|
Context("Empty ID", func() {
|
|
It("returns placeholder if album is not in the DB", func() {
|
|
r, _, err := aw.GetOrPlaceholder(context.Background(), "", 0, false)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
ph, err := resources.FS().Open(consts.PlaceholderAlbumArt)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
phBytes, err := io.ReadAll(ph)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
result, err := io.ReadAll(r)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(result).To(Equal(phBytes))
|
|
})
|
|
})
|
|
})
|
|
Context("Get", func() {
|
|
Context("Empty ID", func() {
|
|
It("returns an ErrUnavailable error", func() {
|
|
_, _, err := aw.Get(context.Background(), model.ArtworkID{}, 0, false)
|
|
Expect(err).To(MatchError(artwork.ErrUnavailable))
|
|
})
|
|
})
|
|
})
|
|
})
|