mirror of
https://github.com/navidrome/navidrome.git
synced 2025-05-08 06:11:07 +03:00
Modify the createAgents function to call each agent constructor only once.\n\nPreviously, the constructor (init(ds)) was called first to check if the\nagent could be initialized, and then called again when appending the agent\nto the result slice. This caused unnecessary work and duplicate log messages.\n\nThe fix stores the result of the first call in the 'agent' variable and\nreuses this instance when appending, ensuring each constructor runs only once.
244 lines
6.2 KiB
Go
244 lines
6.2 KiB
Go
package agents
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
"github.com/navidrome/navidrome/consts"
|
|
"github.com/navidrome/navidrome/log"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/utils"
|
|
"github.com/navidrome/navidrome/utils/singleton"
|
|
)
|
|
|
|
type Agents struct {
|
|
ds model.DataStore
|
|
agents []Interface
|
|
}
|
|
|
|
func GetAgents(ds model.DataStore) *Agents {
|
|
return singleton.GetInstance(func() *Agents {
|
|
return createAgents(ds)
|
|
})
|
|
}
|
|
|
|
func createAgents(ds model.DataStore) *Agents {
|
|
var order []string
|
|
if conf.Server.Agents != "" {
|
|
order = strings.Split(conf.Server.Agents, ",")
|
|
}
|
|
order = append(order, LocalAgentName)
|
|
var res []Interface
|
|
var enabled []string
|
|
for _, name := range order {
|
|
init, ok := Map[name]
|
|
if !ok {
|
|
log.Error("Invalid agent. Check `Agents` configuration", "name", name, "conf", conf.Server.Agents)
|
|
continue
|
|
}
|
|
|
|
agent := init(ds)
|
|
if agent == nil {
|
|
log.Debug("Agent not available. Missing configuration?", "name", name)
|
|
continue
|
|
}
|
|
enabled = append(enabled, name)
|
|
res = append(res, agent)
|
|
}
|
|
log.Debug("List of agents enabled", "names", enabled)
|
|
|
|
return &Agents{ds: ds, agents: res}
|
|
}
|
|
|
|
func (a *Agents) AgentName() string {
|
|
return "agents"
|
|
}
|
|
|
|
func (a *Agents) GetArtistMBID(ctx context.Context, id string, name string) (string, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return "", ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return "", nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistMBIDRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
mbid, err := agent.GetArtistMBID(ctx, id, name)
|
|
if mbid != "" && err == nil {
|
|
log.Debug(ctx, "Got MBID", "agent", ag.AgentName(), "artist", name, "mbid", mbid, "elapsed", time.Since(start))
|
|
return mbid, nil
|
|
}
|
|
}
|
|
return "", ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return "", ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return "", nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistURLRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
url, err := agent.GetArtistURL(ctx, id, name, mbid)
|
|
if url != "" && err == nil {
|
|
log.Debug(ctx, "Got External Url", "agent", ag.AgentName(), "artist", name, "url", url, "elapsed", time.Since(start))
|
|
return url, nil
|
|
}
|
|
}
|
|
return "", ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return "", ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return "", nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistBiographyRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
bio, err := agent.GetArtistBiography(ctx, id, name, mbid)
|
|
if err == nil {
|
|
log.Debug(ctx, "Got Biography", "agent", ag.AgentName(), "artist", name, "len", len(bio), "elapsed", time.Since(start))
|
|
return bio, nil
|
|
}
|
|
}
|
|
return "", ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return nil, ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return nil, nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistSimilarRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
similar, err := agent.GetSimilarArtists(ctx, id, name, mbid, limit)
|
|
if len(similar) > 0 && err == nil {
|
|
if log.IsGreaterOrEqualTo(log.LevelTrace) {
|
|
log.Debug(ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similar", similar, "elapsed", time.Since(start))
|
|
} else {
|
|
log.Debug(ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similarReceived", len(similar), "elapsed", time.Since(start))
|
|
}
|
|
return similar, err
|
|
}
|
|
}
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetArtistImages(ctx context.Context, id, name, mbid string) ([]ExternalImage, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return nil, ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return nil, nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistImageRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
images, err := agent.GetArtistImages(ctx, id, name, mbid)
|
|
if len(images) > 0 && err == nil {
|
|
log.Debug(ctx, "Got Images", "agent", ag.AgentName(), "artist", name, "images", images, "elapsed", time.Since(start))
|
|
return images, nil
|
|
}
|
|
}
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error) {
|
|
switch id {
|
|
case consts.UnknownArtistID:
|
|
return nil, ErrNotFound
|
|
case consts.VariousArtistsID:
|
|
return nil, nil
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(ArtistTopSongsRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
songs, err := agent.GetArtistTopSongs(ctx, id, artistName, mbid, count)
|
|
if len(songs) > 0 && err == nil {
|
|
log.Debug(ctx, "Got Top Songs", "agent", ag.AgentName(), "artist", artistName, "songs", songs, "elapsed", time.Since(start))
|
|
return songs, nil
|
|
}
|
|
}
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
func (a *Agents) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*AlbumInfo, error) {
|
|
if name == consts.UnknownAlbum {
|
|
return nil, ErrNotFound
|
|
}
|
|
start := time.Now()
|
|
for _, ag := range a.agents {
|
|
if utils.IsCtxDone(ctx) {
|
|
break
|
|
}
|
|
agent, ok := ag.(AlbumInfoRetriever)
|
|
if !ok {
|
|
continue
|
|
}
|
|
album, err := agent.GetAlbumInfo(ctx, name, artist, mbid)
|
|
if err == nil {
|
|
log.Debug(ctx, "Got Album Info", "agent", ag.AgentName(), "album", name, "artist", artist,
|
|
"mbid", mbid, "elapsed", time.Since(start))
|
|
return album, nil
|
|
}
|
|
}
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
var _ Interface = (*Agents)(nil)
|
|
var _ ArtistMBIDRetriever = (*Agents)(nil)
|
|
var _ ArtistURLRetriever = (*Agents)(nil)
|
|
var _ ArtistBiographyRetriever = (*Agents)(nil)
|
|
var _ ArtistSimilarRetriever = (*Agents)(nil)
|
|
var _ ArtistImageRetriever = (*Agents)(nil)
|
|
var _ ArtistTopSongsRetriever = (*Agents)(nil)
|
|
var _ AlbumInfoRetriever = (*Agents)(nil)
|