97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
_ "image/gif"
|
|
_ "image/png"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/dhowden/tag"
|
|
"github.com/navidrome/navidrome/consts"
|
|
"github.com/navidrome/navidrome/log"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/resources"
|
|
_ "golang.org/x/image/webp"
|
|
)
|
|
|
|
type Artwork interface {
|
|
Get(ctx context.Context, id string, size int) (io.ReadCloser, error)
|
|
}
|
|
|
|
func NewArtwork(ds model.DataStore) Artwork {
|
|
return &artwork{ds: ds}
|
|
}
|
|
|
|
type artwork struct {
|
|
ds model.DataStore
|
|
}
|
|
|
|
func (a *artwork) Get(ctx context.Context, id string, size int) (io.ReadCloser, error) {
|
|
r, _, err := a.get(ctx, id, size)
|
|
return r, err
|
|
}
|
|
|
|
func (a *artwork) get(ctx context.Context, id string, size int) (io.ReadCloser, string, error) {
|
|
artId, err := model.ParseArtworkID(id)
|
|
if err != nil {
|
|
return nil, "", errors.New("invalid ID")
|
|
}
|
|
id = artId.ID
|
|
al, err := a.ds.Album(ctx).Get(id)
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
r, path := fromPlaceholder()()
|
|
return r, path, nil
|
|
}
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
r, path := extractImage(ctx, artId,
|
|
fromTag(al.EmbedArtPath),
|
|
fromPlaceholder(),
|
|
)
|
|
return r, path, nil
|
|
}
|
|
|
|
func extractImage(ctx context.Context, artId model.ArtworkID, extractFuncs ...func() (io.ReadCloser, string)) (io.ReadCloser, string) {
|
|
for _, f := range extractFuncs {
|
|
r, path := f()
|
|
if r != nil {
|
|
log.Trace(ctx, "Found artwork", "artId", artId, "path", path)
|
|
return r, path
|
|
}
|
|
}
|
|
log.Error(ctx, "extractImage should never reach this point!", "artId", artId, "path")
|
|
return nil, ""
|
|
}
|
|
|
|
func fromTag(path string) func() (io.ReadCloser, string) {
|
|
return func() (io.ReadCloser, string) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, ""
|
|
}
|
|
defer f.Close()
|
|
|
|
m, err := tag.ReadFrom(f)
|
|
if err != nil {
|
|
return nil, ""
|
|
}
|
|
|
|
picture := m.Picture()
|
|
if picture == nil {
|
|
return nil, ""
|
|
}
|
|
return io.NopCloser(bytes.NewReader(picture.Data)), path
|
|
}
|
|
}
|
|
|
|
func fromPlaceholder() func() (io.ReadCloser, string) {
|
|
return func() (io.ReadCloser, string) {
|
|
r, _ := resources.FS().Open(consts.PlaceholderAlbumArt)
|
|
return r, consts.PlaceholderAlbumArt
|
|
}
|
|
}
|