When retrieving images from external sources, avoid calling it again if data is already cached locally.

Relates to https://github.com/navidrome/navidrome/issues/2130#issuecomment-1412742918
This commit is contained in:
Deluan 2023-02-02 10:38:17 -05:00
parent 4a7e86e989
commit f4b50c493c
4 changed files with 30 additions and 12 deletions

View File

@ -117,6 +117,7 @@ var (
VariousArtists = "Various Artists" VariousArtists = "Various Artists"
VariousArtistsID = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(VariousArtists)))) VariousArtistsID = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(VariousArtists))))
UnknownArtist = "[Unknown Artist]" UnknownArtist = "[Unknown Artist]"
UnknownArtistID = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(UnknownArtist))))
VariousArtistsMbzId = "89ad4ac3-39f7-470e-963a-56509c546377" VariousArtistsMbzId = "89ad4ac3-39f7-470e-963a-56509c546377"
ServerStart = time.Now() ServerStart = time.Now()

View File

@ -48,7 +48,15 @@ type cacheWarmer struct {
wakeSignal chan struct{} wakeSignal chan struct{}
} }
var ignoredIds = map[string]struct{}{
consts.VariousArtistsID: {},
consts.UnknownArtistID: {},
}
func (a *cacheWarmer) PreCache(artID model.ArtworkID) { func (a *cacheWarmer) PreCache(artID model.ArtworkID) {
if _, shouldIgnore := ignoredIds[artID.ID]; shouldIgnore {
return
}
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
a.buffer[artID] = struct{}{} a.buffer[artID] = struct{}{}

View File

@ -42,7 +42,9 @@ func newArtistReader(ctx context.Context, artwork *artwork, artID model.ArtworkI
em: em, em: em,
artist: *ar, artist: *ar,
} }
a.cacheKey.lastUpdate = ar.ExternalInfoUpdatedAt // TODO Find a way to factor in the ExternalUpdateInfoAt in the cache key. Problem is that it can
// change _after_ retrieving from external sources, making the key invalid
//a.cacheKey.lastUpdate = ar.ExternalInfoUpdatedAt
var files []string var files []string
var paths []string var paths []string
for _, al := range als { for _, al := range als {
@ -64,10 +66,10 @@ func newArtistReader(ctx context.Context, artwork *artwork, artID model.ArtworkI
func (a *artistReader) Key() string { func (a *artistReader) Key() string {
hash := md5.Sum([]byte(conf.Server.Agents + conf.Server.Spotify.ID)) hash := md5.Sum([]byte(conf.Server.Agents + conf.Server.Spotify.ID))
return fmt.Sprintf( return fmt.Sprintf(
"%s.%x.%t ", "%s.%t.%x",
a.cacheKey.Key(), a.cacheKey.Key(),
hash,
conf.Server.EnableExternalServices, conf.Server.EnableExternalServices,
hash,
) )
} }

View File

@ -180,6 +180,16 @@ func clearName(name string) string {
} }
func (e *externalMetadata) UpdateArtistInfo(ctx context.Context, id string, similarCount int, includeNotPresent bool) (*model.Artist, error) { func (e *externalMetadata) UpdateArtistInfo(ctx context.Context, id string, similarCount int, includeNotPresent bool) (*model.Artist, error) {
artist, err := e.refreshArtistInfo(ctx, id)
if err != nil {
return nil, err
}
err = e.loadSimilar(ctx, artist, similarCount, includeNotPresent)
return &artist.Artist, err
}
func (e *externalMetadata) refreshArtistInfo(ctx context.Context, id string) (*auxArtist, error) {
artist, err := e.getArtist(ctx, id) artist, err := e.getArtist(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -188,30 +198,28 @@ func (e *externalMetadata) UpdateArtistInfo(ctx context.Context, id string, simi
// If we don't have any info, retrieves it now // If we don't have any info, retrieves it now
if artist.ExternalInfoUpdatedAt.IsZero() { if artist.ExternalInfoUpdatedAt.IsZero() {
log.Debug(ctx, "ArtistInfo not cached. Retrieving it now", "updatedAt", artist.ExternalInfoUpdatedAt, "id", id, "name", artist.Name) log.Debug(ctx, "ArtistInfo not cached. Retrieving it now", "updatedAt", artist.ExternalInfoUpdatedAt, "id", id, "name", artist.Name)
err = e.refreshArtistInfo(ctx, artist) err := e.populateArtistInfo(ctx, artist)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
// If info is expired, trigger a refresh in the background // If info is expired, trigger a populateArtistInfo in the background
if time.Since(artist.ExternalInfoUpdatedAt) > consts.ArtistInfoTimeToLive { if time.Since(artist.ExternalInfoUpdatedAt) > consts.ArtistInfoTimeToLive {
log.Debug("Found expired cached ArtistInfo, refreshing in the background", "updatedAt", artist.ExternalInfoUpdatedAt, "name", artist.Name) log.Debug("Found expired cached ArtistInfo, refreshing in the background", "updatedAt", artist.ExternalInfoUpdatedAt, "name", artist.Name)
go func() { go func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
err := e.refreshArtistInfo(ctx, artist) err := e.populateArtistInfo(ctx, artist)
if err != nil { if err != nil {
log.Error("Error refreshing ArtistInfo", "id", id, "name", artist.Name, err) log.Error("Error refreshing ArtistInfo", "id", id, "name", artist.Name, err)
} }
}() }()
} }
return artist, nil
err = e.loadSimilar(ctx, artist, similarCount, includeNotPresent)
return &artist.Artist, err
} }
func (e *externalMetadata) refreshArtistInfo(ctx context.Context, artist *auxArtist) error { func (e *externalMetadata) populateArtistInfo(ctx context.Context, artist *auxArtist) error {
// Get MBID first, if it is not yet available // Get MBID first, if it is not yet available
if artist.MbzArtistID == "" { if artist.MbzArtistID == "" {
mbid, err := e.ag.GetArtistMBID(ctx, artist.ID, artist.Name) mbid, err := e.ag.GetArtistMBID(ctx, artist.ID, artist.Name)
@ -314,12 +322,11 @@ func (e *externalMetadata) SimilarSongs(ctx context.Context, id string, count in
} }
func (e *externalMetadata) ArtistImage(ctx context.Context, id string) (*url.URL, error) { func (e *externalMetadata) ArtistImage(ctx context.Context, id string) (*url.URL, error) {
artist, err := e.getArtist(ctx, id) artist, err := e.refreshArtistInfo(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.callGetImage(ctx, e.ag, artist)
if utils.IsCtxDone(ctx) { if utils.IsCtxDone(ctx) {
log.Warn(ctx, "ArtistImage call canceled", ctx.Err()) log.Warn(ctx, "ArtistImage call canceled", ctx.Err())
return nil, ctx.Err() return nil, ctx.Err()