From 0ba89dae4945babdbf303b2c01211562f3589be3 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sun, 26 Jul 2026 22:10:31 -0400 Subject: [PATCH] refactor(artwork): name the service for its domain, not its role Every other core interface is named for what it is -- Playlists, Library, Scrobbler, MediaStreamer -- while this one was artwork.Service, the only X.Service in the tree. It becomes artwork.Artwork/NewArtwork, and serving.go follows the type to artwork.go. ProvideImageStore was likewise the only "func Provide" in the repo; it is GetImageStore now, matching GetImageCache beside it. cmd/wire_gen.go regenerated via make wire. --- cmd/wire_gen.go | 20 +++++++++---------- core/artwork/{serving.go => artwork.go} | 4 ++-- .../{serving_test.go => artwork_test.go} | 6 +++--- core/artwork/e2e/acquire_serve_test.go | 4 ++-- core/artwork/e2e/resolution_harness_test.go | 4 ++-- core/artwork/image_store.go | 4 ++-- core/artwork/wire_providers.go | 4 ++-- server/jellyfin/api.go | 4 ++-- server/jellyfin/e2e/e2e_suite_test.go | 2 +- server/jellyfin/images_test.go | 2 +- server/public/handle_images_test.go | 2 +- server/public/public.go | 4 ++-- server/subsonic/api.go | 4 ++-- server/subsonic/e2e/e2e_suite_test.go | 4 ++-- server/subsonic/e2e/subsonic_artwork_test.go | 12 +++++------ server/subsonic/media_retrieval_test.go | 2 +- 16 files changed, 41 insertions(+), 41 deletions(-) rename core/artwork/{serving.go => artwork.go} (99%) rename core/artwork/{serving_test.go => artwork_test.go} (99%) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index e3021b0c6..4bdd48422 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -84,9 +84,9 @@ func CreateSubsonicAPIRouter(ctx context.Context) *subsonic.Router { sqlDB := db.Db() dataStore := persistence.New(sqlDB) fileCache := artwork.GetImageCache() - imageStore := artwork.ProvideImageStore() + imageStore := artwork.GetImageStore() fFmpeg := ffmpeg.New() - service := artwork.NewService(dataStore, fileCache, imageStore, fFmpeg) + artworkArtwork := artwork.NewArtwork(dataStore, fileCache, imageStore, fFmpeg) transcodingCache := stream.GetTranscodingCache() mediaStreamer := stream.NewMediaStreamer(dataStore, fFmpeg, transcodingCache) share := core.NewShare(dataStore) @@ -106,7 +106,7 @@ func CreateSubsonicAPIRouter(ctx context.Context) *subsonic.Router { lyricsLyrics := lyrics.NewLyrics(dataStore, manager) transcodeDecider := stream.NewTranscodeDecider(dataStore, fFmpeg) sonicSonic := sonic.New(dataStore, manager, matcherMatcher) - router := subsonic.New(dataStore, service, mediaStreamer, archiver, players, provider, modelScanner, broker, playlistsPlaylists, playTracker, share, playbackServer, metricsMetrics, lyricsLyrics, transcodeDecider, sonicSonic) + router := subsonic.New(dataStore, artworkArtwork, mediaStreamer, archiver, players, provider, modelScanner, broker, playlistsPlaylists, playTracker, share, playbackServer, metricsMetrics, lyricsLyrics, transcodeDecider, sonicSonic) return router } @@ -114,9 +114,9 @@ func CreateJellyfinAPIRouter(ctx context.Context) *jellyfin.Router { sqlDB := db.Db() dataStore := persistence.New(sqlDB) fileCache := artwork.GetImageCache() - imageStore := artwork.ProvideImageStore() + imageStore := artwork.GetImageStore() fFmpeg := ffmpeg.New() - service := artwork.NewService(dataStore, fileCache, imageStore, fFmpeg) + artworkArtwork := artwork.NewArtwork(dataStore, fileCache, imageStore, fFmpeg) transcodingCache := stream.GetTranscodingCache() mediaStreamer := stream.NewMediaStreamer(dataStore, fFmpeg, transcodingCache) transcodeDecider := stream.NewTranscodeDecider(dataStore, fFmpeg) @@ -132,7 +132,7 @@ func CreateJellyfinAPIRouter(ctx context.Context) *jellyfin.Router { provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher) sonicSonic := sonic.New(dataStore, manager, matcherMatcher) lyricsLyrics := lyrics.NewLyrics(dataStore, manager) - router := jellyfin.New(dataStore, service, mediaStreamer, transcodeDecider, players, playTracker, playlistsPlaylists, provider, sonicSonic, lyricsLyrics, broker) + router := jellyfin.New(dataStore, artworkArtwork, mediaStreamer, transcodeDecider, players, playTracker, playlistsPlaylists, provider, sonicSonic, lyricsLyrics, broker) return router } @@ -140,14 +140,14 @@ func CreatePublicRouter() *public.Router { sqlDB := db.Db() dataStore := persistence.New(sqlDB) fileCache := artwork.GetImageCache() - imageStore := artwork.ProvideImageStore() + imageStore := artwork.GetImageStore() fFmpeg := ffmpeg.New() - service := artwork.NewService(dataStore, fileCache, imageStore, fFmpeg) + artworkArtwork := artwork.NewArtwork(dataStore, fileCache, imageStore, fFmpeg) transcodingCache := stream.GetTranscodingCache() mediaStreamer := stream.NewMediaStreamer(dataStore, fFmpeg, transcodingCache) share := core.NewShare(dataStore) archiver := core.NewArchiver(mediaStreamer, dataStore, share) - router := public.New(dataStore, service, mediaStreamer, share, archiver) + router := public.New(dataStore, artworkArtwork, mediaStreamer, share, archiver) return router } @@ -212,7 +212,7 @@ func GetPlaybackServer() playback.PlaybackServer { func CreateArtworkWorker() *artwork.Worker { sqlDB := db.Db() dataStore := persistence.New(sqlDB) - imageStore := artwork.ProvideImageStore() + imageStore := artwork.GetImageStore() broker := events.GetBroker() metricsMetrics := metrics.GetPrometheusInstance(dataStore) manager := plugins.GetManager(dataStore, broker, metricsMetrics) diff --git a/core/artwork/serving.go b/core/artwork/artwork.go similarity index 99% rename from core/artwork/serving.go rename to core/artwork/artwork.go index a63ac77b3..280a49619 100644 --- a/core/artwork/serving.go +++ b/core/artwork/artwork.go @@ -40,7 +40,7 @@ func representationTag(hash string, size int, square bool) string { return fmt.Sprintf("%s.%d.%v.%s", hash, size, square, formatQualityTag()) } -type Service interface { +type Artwork interface { // Get serves resolved/provisional artwork; ErrUnavailable or model.ErrNotFound when // there is nothing to serve (absent, pending, dangling) — caller picks placeholder vs 404. Get(ctx context.Context, artID model.ArtworkID, size int, square bool) (*Image, error) @@ -49,7 +49,7 @@ type Service interface { GetOrPlaceholder(ctx context.Context, id string, size int, square bool) (*Image, error) } -func NewService(ds model.DataStore, cache cache.FileCache, store *ImageStore, ffm ffmpeg.FFmpeg) Service { +func NewArtwork(ds model.DataStore, cache cache.FileCache, store *ImageStore, ffm ffmpeg.FFmpeg) Artwork { return &service{ds: ds, cache: cache, store: store, ffmpeg: ffm} } diff --git a/core/artwork/serving_test.go b/core/artwork/artwork_test.go similarity index 99% rename from core/artwork/serving_test.go rename to core/artwork/artwork_test.go index 2774b6ac8..1807194a7 100644 --- a/core/artwork/serving_test.go +++ b/core/artwork/artwork_test.go @@ -20,7 +20,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Service", func() { +var _ = Describe("Artwork", func() { var ( ctx context.Context ds *tests.MockDataStore @@ -33,7 +33,7 @@ var _ = Describe("Service", func() { ffm *tests.MockFFmpeg store *ImageStore imgCache cache.FileCache - svc Service + svc Artwork repoRoot string coverBytes []byte ) @@ -96,7 +96,7 @@ var _ = Describe("Service", func() { return arg.(artworkReader).Reader(ctx) }) Eventually(func() bool { return imgCache.Available(ctx) }).Should(BeTrue()) - svc = NewService(ds, imgCache, store, ffm) + svc = NewArtwork(ds, imgCache, store, ffm) }) Describe("found state", func() { diff --git a/core/artwork/e2e/acquire_serve_test.go b/core/artwork/e2e/acquire_serve_test.go index a17cc8b90..61f37e0bd 100644 --- a/core/artwork/e2e/acquire_serve_test.go +++ b/core/artwork/e2e/acquire_serve_test.go @@ -37,7 +37,7 @@ var _ = Describe("Acquisition → serve loop", func() { folderRepo *fakeFolderRepo libRepo *tests.MockLibraryRepo store *artwork.ImageStore - svc artwork.Service + svc artwork.Artwork worker *artwork.Worker coverBytes []byte ) @@ -102,7 +102,7 @@ var _ = Describe("Acquisition → serve loop", func() { }) Eventually(func() bool { return imgCache.Available(ctx) }).Should(BeTrue()) - svc = artwork.NewService(ds, imgCache, store, ffm) + svc = artwork.NewArtwork(ds, imgCache, store, ffm) worker = artwork.NewWorker(ds, store, agents.GetAgents(ds, nil), ffm, events.NoopBroker(), imgCache) }) diff --git a/core/artwork/e2e/resolution_harness_test.go b/core/artwork/e2e/resolution_harness_test.go index 1aa60d49c..d79cf498c 100644 --- a/core/artwork/e2e/resolution_harness_test.go +++ b/core/artwork/e2e/resolution_harness_test.go @@ -56,7 +56,7 @@ var ( rctx context.Context rds *tests.MockDataStore rstore *artwork.ImageStore - rsvc artwork.Service + rsvc artwork.Artwork rworker *artwork.Worker fakeFS *storagetest.FakeFS ) @@ -125,7 +125,7 @@ func setupResolutionHarness() { }) Eventually(func() bool { return imgCache.Available(rctx) }).Should(BeTrue()) - rsvc = artwork.NewService(rds, imgCache, rstore, ffm) + rsvc = artwork.NewArtwork(rds, imgCache, rstore, ffm) rworker = artwork.NewWorker(rds, rstore, agents.GetAgents(rds, nil), ffm, events.NoopBroker(), imgCache) } diff --git a/core/artwork/image_store.go b/core/artwork/image_store.go index 77c69de7d..684426e18 100644 --- a/core/artwork/image_store.go +++ b/core/artwork/image_store.go @@ -34,9 +34,9 @@ func NewImageStore(rootDir string) *ImageStore { return &ImageStore{root: rootDir} } -// ProvideImageStore roots the store in its own subtree under the data folder, so +// GetImageStore roots the store in its own subtree under the data folder, so // Prune's recursive sweep never reaches the per-entity upload folders next to it. -func ProvideImageStore() *ImageStore { +func GetImageStore() *ImageStore { return NewImageStore(filepath.Join(conf.Server.DataFolder.String(), consts.ArtworkFolder, "store")) } diff --git a/core/artwork/wire_providers.go b/core/artwork/wire_providers.go index ebd24858a..54c2ad397 100644 --- a/core/artwork/wire_providers.go +++ b/core/artwork/wire_providers.go @@ -5,9 +5,9 @@ import ( ) var Set = wire.NewSet( - NewService, + NewArtwork, GetImageCache, NewWorker, - ProvideImageStore, + GetImageStore, NewUploader, ) diff --git a/server/jellyfin/api.go b/server/jellyfin/api.go index f7a38feb4..1f46c08b4 100644 --- a/server/jellyfin/api.go +++ b/server/jellyfin/api.go @@ -30,7 +30,7 @@ import ( type Router struct { http.Handler ds model.DataStore - artwork artwork.Service + artwork artwork.Artwork streamer stream.MediaStreamer transcodeDecider stream.TranscodeDecider players core.Players @@ -46,7 +46,7 @@ type Router struct { serverIDVal string } -func New(ds model.DataStore, artwork artwork.Service, streamer stream.MediaStreamer, +func New(ds model.DataStore, artwork artwork.Artwork, streamer stream.MediaStreamer, transcodeDecider stream.TranscodeDecider, players core.Players, scrobbler scrobbler.PlayTracker, playlists playlists.Playlists, provider external.Provider, sonicSvc sonic.Engine, lyricsSvc lyrics.Lyrics, broker events.Broker) *Router { diff --git a/server/jellyfin/e2e/e2e_suite_test.go b/server/jellyfin/e2e/e2e_suite_test.go index 7706eafa3..ca1308e9e 100644 --- a/server/jellyfin/e2e/e2e_suite_test.go +++ b/server/jellyfin/e2e/e2e_suite_test.go @@ -419,4 +419,4 @@ func (s *spyArtwork) GetOrPlaceholder(c context.Context, id string, _ int, _ boo return &artwork.Image{ReadCloser: io.NopCloser(bytes.NewReader(d))}, nil } -var _ artwork.Service = &spyArtwork{} +var _ artwork.Artwork = &spyArtwork{} diff --git a/server/jellyfin/images_test.go b/server/jellyfin/images_test.go index 65285e3c5..9d30e7d6c 100644 --- a/server/jellyfin/images_test.go +++ b/server/jellyfin/images_test.go @@ -29,7 +29,7 @@ import ( ) type fakeArtwork struct { - artwork.Service + artwork.Artwork recvId string recvCtx context.Context data []byte diff --git a/server/public/handle_images_test.go b/server/public/handle_images_test.go index edcf9d8c0..329ff58c9 100644 --- a/server/public/handle_images_test.go +++ b/server/public/handle_images_test.go @@ -34,7 +34,7 @@ var _ = Describe("decodeArtworkID", func() { }) type fakeArtwork struct { - artwork.Service + artwork.Artwork img *artwork.Image err error } diff --git a/server/public/public.go b/server/public/public.go index 35155ec59..18867e1c4 100644 --- a/server/public/public.go +++ b/server/public/public.go @@ -18,7 +18,7 @@ import ( type Router struct { http.Handler - artwork artwork.Service + artwork artwork.Artwork streamer stream.MediaStreamer archiver core.Archiver share core.Share @@ -26,7 +26,7 @@ type Router struct { ds model.DataStore } -func New(ds model.DataStore, artwork artwork.Service, streamer stream.MediaStreamer, share core.Share, archiver core.Archiver) *Router { +func New(ds model.DataStore, artwork artwork.Artwork, streamer stream.MediaStreamer, share core.Share, archiver core.Archiver) *Router { p := &Router{ds: ds, artwork: artwork, streamer: streamer, share: share, archiver: archiver} shareRoot := path.Join(conf.Server.BasePath, consts.URLPathPublic) p.assetsHandler = http.StripPrefix(shareRoot, http.FileServer(http.FS(ui.BuildAssets()))) diff --git a/server/subsonic/api.go b/server/subsonic/api.go index d57da7452..82e404228 100644 --- a/server/subsonic/api.go +++ b/server/subsonic/api.go @@ -40,7 +40,7 @@ type handlerRaw = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, type Router struct { http.Handler ds model.DataStore - artwork artwork.Service + artwork artwork.Artwork streamer stream.MediaStreamer archiver core.Archiver players core.Players @@ -57,7 +57,7 @@ type Router struct { sonic *sonicsvc.Sonic } -func New(ds model.DataStore, artwork artwork.Service, streamer stream.MediaStreamer, archiver core.Archiver, +func New(ds model.DataStore, artwork artwork.Artwork, streamer stream.MediaStreamer, archiver core.Archiver, players core.Players, provider external.Provider, scanner model.Scanner, broker events.Broker, playlists playlistsvc.Playlists, scrobbler scrobbler.PlayTracker, share core.Share, playback playback.PlaybackServer, metrics metrics.Metrics, lyrics lyricssvc.Lyrics, transcodeDecision stream.TranscodeDecider, diff --git a/server/subsonic/e2e/e2e_suite_test.go b/server/subsonic/e2e/e2e_suite_test.go index 9f1fa830b..8db9b621c 100644 --- a/server/subsonic/e2e/e2e_suite_test.go +++ b/server/subsonic/e2e/e2e_suite_test.go @@ -308,7 +308,7 @@ func parseJSONResponse(w *httptest.ResponseRecorder) *responses.Subsonic { // --- Noop stub implementations for Router dependencies --- -// noopArtwork implements artwork.Service +// noopArtwork implements artwork.Artwork type noopArtwork struct{} func (n noopArtwork) Get(context.Context, model.ArtworkID, int, bool) (*artwork.Image, error) { @@ -359,7 +359,7 @@ func (n noopProvider) TopSongs(context.Context, string, int) (model.MediaFiles, // Compile-time interface checks var ( - _ artwork.Service = noopArtwork{} + _ artwork.Artwork = noopArtwork{} _ core.Archiver = noopArchiver{} _ external.Provider = noopProvider{} ) diff --git a/server/subsonic/e2e/subsonic_artwork_test.go b/server/subsonic/e2e/subsonic_artwork_test.go index a1d874557..05a25f222 100644 --- a/server/subsonic/e2e/subsonic_artwork_test.go +++ b/server/subsonic/e2e/subsonic_artwork_test.go @@ -45,12 +45,12 @@ import ( // The artwork serving path streams folder-backed originals via os.Open, which a fake FS cannot // back, so this suite scans a small REAL on-disk library and drives the real acquisition worker -// and artwork.Service through the Subsonic and public image handlers. +// and artwork.Artwork through the Subsonic and public image handlers. var _ = Describe("Artwork Serving", Ordered, func() { var ( artRouter *subsonic.Router pubRouter *public.Router - artSvc artwork.Service + artSvc artwork.Artwork worker *artwork.Worker artfulID string artlessID string @@ -134,7 +134,7 @@ var _ = Describe("Artwork Serving", Ordered, func() { store := artwork.NewImageStore(GinkgoT().TempDir()) imgCache := newDummyImageCache(ctx) ffm := harness.NoopFFmpeg{} - artSvc = artwork.NewService(ds, imgCache, store, ffm) + artSvc = artwork.NewArtwork(ds, imgCache, store, ffm) worker = artwork.NewWorker(ds, store, agents.GetAgents(ds, nil), ffm, events.NoopBroker(), imgCache) artRouter = buildArtworkRouter(artSvc) @@ -233,8 +233,8 @@ var _ = Describe("Artwork Serving", Ordered, func() { }) }) -// buildArtworkRouter mirrors setupTestDB's Subsonic wiring but with the real artwork.Service. -func buildArtworkRouter(art artwork.Service) *subsonic.Router { +// buildArtworkRouter mirrors setupTestDB's Subsonic wiring but with the real artwork.Artwork. +func buildArtworkRouter(art artwork.Artwork) *subsonic.Router { decider := stream.NewTranscodeDecider(ds, harness.NoopFFmpeg{}) s := scanner.New(ctx, ds, events.NoopBroker(), playlists.NewPlaylists(ds, artwork.NewUploader(ds)), metrics.NewNoopInstance()) @@ -291,7 +291,7 @@ func readArtworkFixture(name string) []byte { return data } -// newDummyImageCache backs the artwork.Service's resize cache. size=0 requests stream originals +// newDummyImageCache backs the artwork.Artwork's resize cache. size=0 requests stream originals // and never invoke the reader, so it only needs to satisfy the constructor; resize behavior is // covered by the artwork package's own suites. func newDummyImageCache(ctx context.Context) cache.FileCache { diff --git a/server/subsonic/media_retrieval_test.go b/server/subsonic/media_retrieval_test.go index 1f6cbe84d..d937d991a 100644 --- a/server/subsonic/media_retrieval_test.go +++ b/server/subsonic/media_retrieval_test.go @@ -248,7 +248,7 @@ var _ = Describe("MediaRetrievalController", func() { }) type fakeArtwork struct { - artwork.Service + artwork.Artwork data string hash string lastUpdated time.Time