refactor: inject matcher.Matcher via Wire instead of creating it inline

Replaced inline matcher.New(ds) calls in external.provider and
PlaylistGenerator orchestrator with proper dependency injection via
Google Wire. Added matcher.New to the Wire provider set, updated
NewProvider and GetManager signatures to accept *matcher.Matcher, and
deleted the trivial provider_matching.go wrapper. This eliminates tight
coupling where each caller knew how to construct a Matcher, following
the same DI pattern used by other core services.

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2026-03-05 16:03:06 -05:00
parent 0127b8c607
commit 03a36c5bdd
6 changed files with 22 additions and 16 deletions

View File

@ -71,9 +71,9 @@ func CreateNativeAPIRouter(ctx context.Context) *nativeapi.Router {
fFmpeg := ffmpeg.New()
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
agentsAgents := agents.GetAgents(dataStore, manager)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
agentsAgents := agents.GetAgents(dataStore, manager)
provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
cacheWarmer := artwork.NewCacheWarmer(artworkArtwork, fileCache)
@ -93,9 +93,9 @@ func CreateSubsonicAPIRouter(ctx context.Context) *subsonic.Router {
fFmpeg := ffmpeg.New()
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
agentsAgents := agents.GetAgents(dataStore, manager)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
agentsAgents := agents.GetAgents(dataStore, manager)
provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
transcodingCache := stream.GetTranscodingCache()
@ -122,9 +122,9 @@ func CreatePublicRouter() *public.Router {
fFmpeg := ffmpeg.New()
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
agentsAgents := agents.GetAgents(dataStore, manager)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
agentsAgents := agents.GetAgents(dataStore, manager)
provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
transcodingCache := stream.GetTranscodingCache()
@ -170,9 +170,9 @@ func CreateScanner(ctx context.Context) model.Scanner {
fFmpeg := ffmpeg.New()
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
agentsAgents := agents.GetAgents(dataStore, manager)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
agentsAgents := agents.GetAgents(dataStore, manager)
provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
cacheWarmer := artwork.NewCacheWarmer(artworkArtwork, fileCache)
@ -189,9 +189,9 @@ func CreateScanWatcher(ctx context.Context) scanner.Watcher {
fFmpeg := ffmpeg.New()
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
agentsAgents := agents.GetAgents(dataStore, manager)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
agentsAgents := agents.GetAgents(dataStore, manager)
provider := external.NewProvider(dataStore, agentsAgents, matcherMatcher)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
cacheWarmer := artwork.NewCacheWarmer(artworkArtwork, fileCache)
@ -214,7 +214,8 @@ func getPluginManager() *plugins.Manager {
dataStore := persistence.New(sqlDB)
broker := events.GetBroker()
metricsMetrics := metrics.GetPrometheusInstance(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics)
matcherMatcher := matcher.New(dataStore)
manager := plugins.GetManager(dataStore, broker, metricsMetrics, matcherMatcher)
return manager
}

View File

@ -324,7 +324,7 @@ func (c *insightsCollector) hasSmartPlaylists(ctx context.Context) (bool, error)
// collectPlugins collects information about installed plugins
func (c *insightsCollector) collectPlugins(_ context.Context) map[string]insights.PluginInfo {
// TODO Fix import/inject cycles
manager := plugins.GetManager(c.ds, events.GetBroker(), nil)
manager := plugins.GetManager(c.ds, events.GetBroker(), nil, nil)
info := manager.GetPluginInfo()
result := make(map[string]insights.PluginInfo, len(info))

View File

@ -17,6 +17,7 @@ import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/lyrics"
"github.com/navidrome/navidrome/core/matcher"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
@ -66,16 +67,18 @@ type Manager struct {
ds model.DataStore
broker events.Broker
metrics PluginMetricsRecorder
matcher *matcher.Matcher
}
// GetManager returns a singleton instance of the plugin manager.
// The manager is not started automatically; call Start() to begin loading plugins.
func GetManager(ds model.DataStore, broker events.Broker, m PluginMetricsRecorder) *Manager {
func GetManager(ds model.DataStore, broker events.Broker, m PluginMetricsRecorder, mt *matcher.Matcher) *Manager {
return singleton.GetInstance(func() *Manager {
return &Manager{
ds: ds,
broker: broker,
metrics: m,
matcher: mt,
plugins: make(map[string]*plugin),
}
})

View File

@ -394,7 +394,7 @@ func (m *Manager) loadPluginWithConfig(p *model.Plugin) error {
// Start PlaylistGenerator orchestrator if capability is detected
loadedPlugin := m.plugins[p.ID]
if hasCapability(loadedPlugin.capabilities, CapabilityPlaylistGenerator) {
orch := newPlaylistGeneratorOrchestrator(m.ctx, p.ID, loadedPlugin, m.ds)
orch := newPlaylistGeneratorOrchestrator(m.ctx, p.ID, loadedPlugin, m.ds, m.matcher)
loadedPlugin.closers = append(loadedPlugin.closers, orch)
go orch.run()
}

View File

@ -68,13 +68,13 @@ type playlistGeneratorOrchestrator struct {
done chan struct{} // closed when worker exits
}
func newPlaylistGeneratorOrchestrator(parentCtx context.Context, pluginName string, p *plugin, ds model.DataStore) *playlistGeneratorOrchestrator {
func newPlaylistGeneratorOrchestrator(parentCtx context.Context, pluginName string, p *plugin, ds model.DataStore, m *matcher.Matcher) *playlistGeneratorOrchestrator {
ctx, cancel := context.WithCancel(parentCtx)
return &playlistGeneratorOrchestrator{
pluginName: pluginName,
plugin: p,
ds: ds,
matcher: matcher.New(ds),
matcher: m,
ctx: ctx,
cancel: cancel,
workCh: make(chan workItem, workChCapacity),

View File

@ -17,6 +17,7 @@ import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/core/matcher"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
@ -145,6 +146,7 @@ func createTestManagerWithPluginsAndMetrics(pluginConfig map[string]map[string]s
manager := &Manager{
plugins: make(map[string]*plugin),
ds: dataStore,
matcher: matcher.New(dataStore),
metrics: metrics,
subsonicRouter: http.NotFoundHandler(), // Stub router for tests
}