navidrome/core/artwork/reader_artist.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

128 lines
3.5 KiB
Go
Raw Normal View History

2022-12-31 05:52:53 +01:00
package artwork
import (
"context"
"crypto/md5"
2022-12-31 05:52:53 +01:00
"fmt"
"io"
2023-01-12 20:13:23 +01:00
"io/fs"
"os"
"path/filepath"
2022-12-31 22:58:07 +01:00
"strings"
2022-12-31 05:52:53 +01:00
"time"
2022-12-31 22:58:07 +01:00
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
2023-01-13 20:30:26 +01:00
"github.com/navidrome/navidrome/core"
2023-01-12 20:13:23 +01:00
"github.com/navidrome/navidrome/log"
2022-12-31 05:52:53 +01:00
"github.com/navidrome/navidrome/model"
2023-01-12 20:13:23 +01:00
"github.com/navidrome/navidrome/utils"
2022-12-31 05:52:53 +01:00
)
type artistReader struct {
2022-12-31 22:58:07 +01:00
cacheKey
2023-01-12 20:13:23 +01:00
a *artwork
2023-01-13 20:30:26 +01:00
em core.ExternalMetadata
2023-01-12 20:13:23 +01:00
artist model.Artist
artistFolder string
files string
2022-12-31 05:52:53 +01:00
}
2023-01-13 20:30:26 +01:00
func newArtistReader(ctx context.Context, artwork *artwork, artID model.ArtworkID, em core.ExternalMetadata) (*artistReader, error) {
2022-12-31 22:58:07 +01:00
ar, err := artwork.ds.Artist(ctx).Get(artID.ID)
if err != nil {
return nil, err
}
als, err := artwork.ds.Album(ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"album_artist_id": artID.ID}})
if err != nil {
return nil, err
}
2022-12-31 05:52:53 +01:00
a := &artistReader{
2022-12-31 22:58:07 +01:00
a: artwork,
2023-01-13 20:30:26 +01:00
em: em,
2022-12-31 22:58:07 +01:00
artist: *ar,
}
// 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
2023-01-12 20:13:23 +01:00
var paths []string
2022-12-31 22:58:07 +01:00
for _, al := range als {
files = append(files, al.ImageFiles)
paths = append(paths, splitList(al.Paths)...)
2022-12-31 22:58:07 +01:00
if a.cacheKey.lastUpdate.Before(al.UpdatedAt) {
a.cacheKey.lastUpdate = al.UpdatedAt
}
2022-12-31 05:52:53 +01:00
}
a.files = strings.Join(files, consts.Zwsp)
2023-01-12 20:13:23 +01:00
a.artistFolder = utils.LongestCommonPrefix(paths)
if !strings.HasSuffix(a.artistFolder, string(filepath.Separator)) {
a.artistFolder, _ = filepath.Split(a.artistFolder)
}
2022-12-31 22:58:07 +01:00
a.cacheKey.artID = artID
2022-12-31 05:52:53 +01:00
return a, nil
}
func (a *artistReader) Key() string {
2023-01-18 02:37:10 +01:00
hash := md5.Sum([]byte(conf.Server.Agents + conf.Server.Spotify.ID))
return fmt.Sprintf(
"%s.%t.%x",
2023-01-18 02:37:10 +01:00
a.cacheKey.Key(),
conf.Server.EnableExternalServices,
hash,
)
}
2022-12-31 05:52:53 +01:00
func (a *artistReader) LastUpdated() time.Time {
2022-12-31 22:58:07 +01:00
return a.lastUpdate
2022-12-31 05:52:53 +01:00
}
2022-12-31 22:58:07 +01:00
func (a *artistReader) Reader(ctx context.Context) (io.ReadCloser, string, error) {
var ff = a.fromArtistArtPriority(ctx, conf.Server.ArtistArtPriority)
return selectImageReader(ctx, a.artID, ff...)
}
func (a *artistReader) fromArtistArtPriority(ctx context.Context, priority string) []sourceFunc {
var ff []sourceFunc
for _, pattern := range strings.Split(strings.ToLower(priority), ",") {
pattern = strings.TrimSpace(pattern)
switch {
case pattern == "external":
ff = append(ff, fromArtistExternalSource(ctx, a.artist, a.em))
case strings.HasPrefix(pattern, "album/"):
ff = append(ff, fromExternalFile(ctx, a.files, strings.TrimPrefix(pattern, "album/")))
default:
ff = append(ff, fromArtistFolder(ctx, a.artistFolder, pattern))
}
}
return ff
2022-12-31 05:52:53 +01:00
}
2023-01-12 20:13:23 +01:00
func fromArtistFolder(ctx context.Context, artistFolder string, pattern string) sourceFunc {
return func() (io.ReadCloser, string, error) {
fsys := os.DirFS(artistFolder)
matches, err := fs.Glob(fsys, pattern)
if err != nil {
log.Warn(ctx, "Error matching artist image pattern", "pattern", pattern, "folder", artistFolder)
return nil, "", err
}
if len(matches) == 0 {
return nil, "", fmt.Errorf(`no matches for '%s' in '%s'`, pattern, artistFolder)
2023-01-12 20:13:23 +01:00
}
for _, m := range matches {
filePath := filepath.Join(artistFolder, m)
if !model.IsImageFile(m) {
continue
}
f, err := os.Open(filePath)
if err != nil {
log.Warn(ctx, "Could not open cover art file", "file", filePath, err)
return nil, "", err
}
return f, filePath, nil
2023-01-12 20:13:23 +01:00
}
return nil, "", nil
2023-01-12 20:13:23 +01:00
}
}