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.
This commit is contained in:
Deluan 2026-07-26 22:10:31 -04:00
parent 1ceb8ca723
commit 0ba89dae49
16 changed files with 41 additions and 41 deletions

View File

@ -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)

View File

@ -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}
}

View File

@ -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() {

View File

@ -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)
})

View File

@ -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)
}

View File

@ -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"))
}

View File

@ -5,9 +5,9 @@ import (
)
var Set = wire.NewSet(
NewService,
NewArtwork,
GetImageCache,
NewWorker,
ProvideImageStore,
GetImageStore,
NewUploader,
)

View File

@ -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 {

View File

@ -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{}

View File

@ -29,7 +29,7 @@ import (
)
type fakeArtwork struct {
artwork.Service
artwork.Artwork
recvId string
recvCtx context.Context
data []byte

View File

@ -34,7 +34,7 @@ var _ = Describe("decodeArtworkID", func() {
})
type fakeArtwork struct {
artwork.Service
artwork.Artwork
img *artwork.Image
err error
}

View File

@ -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())))

View File

@ -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,

View File

@ -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{}
)

View File

@ -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 {

View File

@ -248,7 +248,7 @@ var _ = Describe("MediaRetrievalController", func() {
})
type fakeArtwork struct {
artwork.Service
artwork.Artwork
data string
hash string
lastUpdated time.Time