mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
refactor(plugins): update GetManager to accept DataStore parameter
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
3605d5bf08
commit
7c6c49c7a1
@ -330,12 +330,12 @@ func startPlaybackServer(ctx context.Context) func() error {
|
||||
// startPluginManager starts the plugin manager, if configured.
|
||||
func startPluginManager(ctx context.Context) func() error {
|
||||
return func() error {
|
||||
manager := GetPluginManager(ctx)
|
||||
if !conf.Server.Plugins.Enabled {
|
||||
log.Debug("Plugin system is DISABLED")
|
||||
return nil
|
||||
}
|
||||
log.Info(ctx, "Starting plugin manager")
|
||||
manager := GetPluginManager(ctx)
|
||||
return manager.Start(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ func CreateNativeAPIRouter(ctx context.Context) *nativeapi.Router {
|
||||
insights := metrics.GetInstance(dataStore)
|
||||
fileCache := artwork.GetImageCache()
|
||||
fFmpeg := ffmpeg.New()
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(dataStore)
|
||||
agentsAgents := agents.GetAgents(dataStore, manager)
|
||||
provider := external.NewProvider(dataStore, agentsAgents)
|
||||
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
|
||||
@ -80,7 +80,7 @@ func CreateSubsonicAPIRouter(ctx context.Context) *subsonic.Router {
|
||||
dataStore := persistence.New(sqlDB)
|
||||
fileCache := artwork.GetImageCache()
|
||||
fFmpeg := ffmpeg.New()
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(dataStore)
|
||||
agentsAgents := agents.GetAgents(dataStore, manager)
|
||||
provider := external.NewProvider(dataStore, agentsAgents)
|
||||
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
|
||||
@ -105,7 +105,7 @@ func CreatePublicRouter() *public.Router {
|
||||
dataStore := persistence.New(sqlDB)
|
||||
fileCache := artwork.GetImageCache()
|
||||
fFmpeg := ffmpeg.New()
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(dataStore)
|
||||
agentsAgents := agents.GetAgents(dataStore, manager)
|
||||
provider := external.NewProvider(dataStore, agentsAgents)
|
||||
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
|
||||
@ -150,7 +150,7 @@ func CreateScanner(ctx context.Context) model.Scanner {
|
||||
dataStore := persistence.New(sqlDB)
|
||||
fileCache := artwork.GetImageCache()
|
||||
fFmpeg := ffmpeg.New()
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(dataStore)
|
||||
agentsAgents := agents.GetAgents(dataStore, manager)
|
||||
provider := external.NewProvider(dataStore, agentsAgents)
|
||||
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
|
||||
@ -167,7 +167,7 @@ func CreateScanWatcher(ctx context.Context) scanner.Watcher {
|
||||
dataStore := persistence.New(sqlDB)
|
||||
fileCache := artwork.GetImageCache()
|
||||
fFmpeg := ffmpeg.New()
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(dataStore)
|
||||
agentsAgents := agents.GetAgents(dataStore, manager)
|
||||
provider := external.NewProvider(dataStore, agentsAgents)
|
||||
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, provider)
|
||||
@ -188,7 +188,9 @@ func GetPlaybackServer() playback.PlaybackServer {
|
||||
}
|
||||
|
||||
func getPluginManager() *plugins.Manager {
|
||||
manager := plugins.GetManager()
|
||||
sqlDB := db.Db()
|
||||
dataStore := persistence.New(sqlDB)
|
||||
manager := plugins.GetManager(dataStore)
|
||||
return manager
|
||||
}
|
||||
|
||||
@ -199,6 +201,5 @@ var allProviders = wire.NewSet(core.Set, artwork.Set, server.New, subsonic.New,
|
||||
func GetPluginManager(ctx context.Context) *plugins.Manager {
|
||||
manager := getPluginManager()
|
||||
manager.SetSubsonicRouter(CreateSubsonicAPIRouter(ctx))
|
||||
manager.SetDataStore(CreateDataStore())
|
||||
return manager
|
||||
}
|
||||
|
||||
@ -129,6 +129,5 @@ func getPluginManager() *plugins.Manager {
|
||||
func GetPluginManager(ctx context.Context) *plugins.Manager {
|
||||
manager := getPluginManager()
|
||||
manager.SetSubsonicRouter(CreateSubsonicAPIRouter(ctx))
|
||||
manager.SetDataStore(CreateDataStore())
|
||||
return manager
|
||||
}
|
||||
|
||||
@ -313,7 +313,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 {
|
||||
manager := plugins.GetManager()
|
||||
manager := plugins.GetManager(c.ds)
|
||||
info := manager.GetPluginInfo()
|
||||
|
||||
result := make(map[string]insights.PluginInfo, len(info))
|
||||
|
||||
@ -71,8 +71,8 @@ var _ = Describe("ArtworkService", Ordered, func() {
|
||||
// Create and start manager
|
||||
manager = &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetDataStore(dataStore)
|
||||
err = manager.Start(GinkgoT().Context())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
||||
@ -360,8 +360,8 @@ var _ = Describe("CacheService Integration", Ordered, func() {
|
||||
// Create and start manager
|
||||
manager = &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetDataStore(dataStore)
|
||||
err = manager.Start(GinkgoT().Context())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
||||
@ -76,8 +76,8 @@ var _ = Describe("SchedulerService", Ordered, func() {
|
||||
// Create and start manager
|
||||
manager = &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetDataStore(dataStore)
|
||||
err = manager.Start(GinkgoT().Context())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
||||
@ -67,9 +67,9 @@ var _ = Describe("SubsonicAPI Host Function", Ordered, func() {
|
||||
// Create and configure manager
|
||||
manager = &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetSubsonicRouter(router)
|
||||
manager.SetDataStore(dataStore)
|
||||
|
||||
// Pre-enable the plugin in the mock repo so it loads on startup
|
||||
// Compute SHA256 of the plugin file to match what SyncPlugins will compute
|
||||
|
||||
@ -69,8 +69,8 @@ var _ = Describe("WebSocketService", Ordered, func() {
|
||||
// Create and start manager
|
||||
manager = &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetDataStore(dataStore)
|
||||
err = manager.Start(GinkgoT().Context())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
||||
@ -109,81 +109,15 @@ func (p *plugin) Close() error {
|
||||
|
||||
// GetManager returns a singleton instance of the plugin manager.
|
||||
// The manager is not started automatically; call Start() to begin loading plugins.
|
||||
func GetManager() *Manager {
|
||||
func GetManager(ds model.DataStore) *Manager {
|
||||
return singleton.GetInstance(func() *Manager {
|
||||
return &Manager{
|
||||
ds: ds,
|
||||
plugins: make(map[string]*plugin),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// adminContext returns a context with admin privileges for DB operations.
|
||||
func adminContext(ctx context.Context) context.Context {
|
||||
return request.WithUser(ctx, model.User{IsAdmin: true})
|
||||
}
|
||||
|
||||
// marshalManifest marshals a manifest to JSON string, returning empty string on error.
|
||||
func marshalManifest(m *Manifest) string {
|
||||
b, _ := json.Marshal(m)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// addPluginToDB adds a new plugin to the database as disabled.
|
||||
func (m *Manager) addPluginToDB(ctx context.Context, repo model.PluginRepository, name, path string, metadata *PluginMetadata) error {
|
||||
now := time.Now()
|
||||
newPlugin := &model.Plugin{
|
||||
ID: name,
|
||||
Path: path,
|
||||
Manifest: marshalManifest(metadata.Manifest),
|
||||
SHA256: metadata.SHA256,
|
||||
Enabled: false,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := repo.Put(newPlugin); err != nil {
|
||||
return fmt.Errorf("adding plugin to DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Discovered new plugin", "plugin", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePluginInDB updates an existing plugin in the database after a file change.
|
||||
// If the plugin was enabled, it will be unloaded and disabled.
|
||||
func (m *Manager) updatePluginInDB(ctx context.Context, repo model.PluginRepository, dbPlugin *model.Plugin, path string, metadata *PluginMetadata) error {
|
||||
wasEnabled := dbPlugin.Enabled
|
||||
if wasEnabled {
|
||||
if err := m.UnloadPlugin(dbPlugin.ID); err != nil {
|
||||
log.Debug(ctx, "Plugin not loaded during change", "plugin", dbPlugin.ID)
|
||||
}
|
||||
}
|
||||
dbPlugin.Path = path
|
||||
dbPlugin.Manifest = marshalManifest(metadata.Manifest)
|
||||
dbPlugin.SHA256 = metadata.SHA256
|
||||
dbPlugin.Enabled = false
|
||||
dbPlugin.LastError = ""
|
||||
dbPlugin.UpdatedAt = time.Now()
|
||||
if err := repo.Put(dbPlugin); err != nil {
|
||||
return fmt.Errorf("updating plugin in DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Plugin file changed", "plugin", dbPlugin.ID, "wasEnabled", wasEnabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// removePluginFromDB removes a plugin from the database.
|
||||
// If the plugin was enabled, it will be unloaded first.
|
||||
func (m *Manager) removePluginFromDB(ctx context.Context, repo model.PluginRepository, dbPlugin *model.Plugin) error {
|
||||
if dbPlugin.Enabled {
|
||||
if err := m.UnloadPlugin(dbPlugin.ID); err != nil {
|
||||
log.Debug(ctx, "Plugin not loaded during removal", "plugin", dbPlugin.ID)
|
||||
}
|
||||
}
|
||||
if err := repo.Delete(dbPlugin.ID); err != nil {
|
||||
return fmt.Errorf("deleting plugin from DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Plugin removed", "plugin", dbPlugin.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSubsonicRouter sets the Subsonic router for SubsonicAPI host functions.
|
||||
// This should be called after the subsonic router is created but before plugins
|
||||
// that require SubsonicAPI access are loaded.
|
||||
@ -191,12 +125,6 @@ func (m *Manager) SetSubsonicRouter(router SubsonicRouter) {
|
||||
m.subsonicRouter = router
|
||||
}
|
||||
|
||||
// SetDataStore sets the data store for plugins that need database access.
|
||||
// This should be called before plugins are loaded.
|
||||
func (m *Manager) SetDataStore(ds model.DataStore) {
|
||||
m.ds = ds
|
||||
}
|
||||
|
||||
// Start initializes the plugin manager and loads plugins from the configured folder.
|
||||
// It should be called once during application startup when plugins are enabled.
|
||||
// The startup flow is:
|
||||
@ -384,6 +312,73 @@ func (m *Manager) GetPluginInfo() map[string]PluginInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
// adminContext returns a context with admin privileges for DB operations.
|
||||
func adminContext(ctx context.Context) context.Context {
|
||||
return request.WithUser(ctx, model.User{IsAdmin: true})
|
||||
}
|
||||
|
||||
// marshalManifest marshals a manifest to JSON string, returning empty string on error.
|
||||
func marshalManifest(m *Manifest) string {
|
||||
b, _ := json.Marshal(m)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// addPluginToDB adds a new plugin to the database as disabled.
|
||||
func (m *Manager) addPluginToDB(ctx context.Context, repo model.PluginRepository, name, path string, metadata *PluginMetadata) error {
|
||||
now := time.Now()
|
||||
newPlugin := &model.Plugin{
|
||||
ID: name,
|
||||
Path: path,
|
||||
Manifest: marshalManifest(metadata.Manifest),
|
||||
SHA256: metadata.SHA256,
|
||||
Enabled: false,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := repo.Put(newPlugin); err != nil {
|
||||
return fmt.Errorf("adding plugin to DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Discovered new plugin", "plugin", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePluginInDB updates an existing plugin in the database after a file change.
|
||||
// If the plugin was enabled, it will be unloaded and disabled.
|
||||
func (m *Manager) updatePluginInDB(ctx context.Context, repo model.PluginRepository, dbPlugin *model.Plugin, path string, metadata *PluginMetadata) error {
|
||||
wasEnabled := dbPlugin.Enabled
|
||||
if wasEnabled {
|
||||
if err := m.UnloadPlugin(dbPlugin.ID); err != nil {
|
||||
log.Debug(ctx, "Plugin not loaded during change", "plugin", dbPlugin.ID)
|
||||
}
|
||||
}
|
||||
dbPlugin.Path = path
|
||||
dbPlugin.Manifest = marshalManifest(metadata.Manifest)
|
||||
dbPlugin.SHA256 = metadata.SHA256
|
||||
dbPlugin.Enabled = false
|
||||
dbPlugin.LastError = ""
|
||||
dbPlugin.UpdatedAt = time.Now()
|
||||
if err := repo.Put(dbPlugin); err != nil {
|
||||
return fmt.Errorf("updating plugin in DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Plugin file changed", "plugin", dbPlugin.ID, "wasEnabled", wasEnabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// removePluginFromDB removes a plugin from the database.
|
||||
// If the plugin was enabled, it will be unloaded first.
|
||||
func (m *Manager) removePluginFromDB(ctx context.Context, repo model.PluginRepository, dbPlugin *model.Plugin) error {
|
||||
if dbPlugin.Enabled {
|
||||
if err := m.UnloadPlugin(dbPlugin.ID); err != nil {
|
||||
log.Debug(ctx, "Plugin not loaded during removal", "plugin", dbPlugin.ID)
|
||||
}
|
||||
}
|
||||
if err := repo.Delete(dbPlugin.ID); err != nil {
|
||||
return fmt.Errorf("deleting plugin from DB: %w", err)
|
||||
}
|
||||
log.Info(ctx, "Plugin removed", "plugin", dbPlugin.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PluginMetadata holds the extracted information from a plugin file
|
||||
// without fully initializing the plugin.
|
||||
type PluginMetadata struct {
|
||||
|
||||
@ -114,8 +114,8 @@ func createTestManagerWithPlugins(pluginConfig map[string]map[string]string, plu
|
||||
// Create and start manager
|
||||
manager := &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
manager.SetDataStore(dataStore)
|
||||
err = manager.Start(GinkgoT().Context())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
||||
@ -136,8 +136,8 @@ var _ = Describe("Plugin Watcher", func() {
|
||||
|
||||
autoReloadManager := &Manager{
|
||||
plugins: make(map[string]*plugin),
|
||||
ds: dataStore,
|
||||
}
|
||||
autoReloadManager.SetDataStore(dataStore)
|
||||
err := autoReloadManager.Start(ctx)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
DeferCleanup(autoReloadManager.Stop)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user