Resize if requested
This commit is contained in:
+85
-2
@@ -4,12 +4,19 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/png"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dhowden/tag"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
@@ -34,11 +41,17 @@ func (a *artwork) Get(ctx context.Context, id string, size int) (io.ReadCloser,
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (a *artwork) get(ctx context.Context, id string, size int) (io.ReadCloser, string, error) {
|
||||
func (a *artwork) get(ctx context.Context, id string, size int) (reader io.ReadCloser, path string, err error) {
|
||||
artId, err := model.ParseArtworkID(id)
|
||||
if err != nil {
|
||||
return nil, "", errors.New("invalid ID")
|
||||
}
|
||||
|
||||
// If requested a resized
|
||||
if size > 0 {
|
||||
return a.resizedFromOriginal(ctx, id, size)
|
||||
}
|
||||
|
||||
id = artId.ID
|
||||
al, err := a.ds.Album(ctx).Get(id)
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
@@ -48,13 +61,34 @@ func (a *artwork) get(ctx context.Context, id string, size int) (io.ReadCloser,
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
r, path := extractImage(ctx, artId,
|
||||
fromExternalFile(al.ImageFiles, "cover.png", "cover.jpg", "cover.jpeg", "cover.webp"),
|
||||
fromExternalFile(al.ImageFiles, "folder.png", "folder.jpg", "folder.jpeg", "folder.webp"),
|
||||
fromExternalFile(al.ImageFiles, "album.png", "album.jpg", "album.jpeg", "album.webp"),
|
||||
fromExternalFile(al.ImageFiles, "albumart.png", "albumart.jpg", "albumart.jpeg", "albumart.webp"),
|
||||
fromExternalFile(al.ImageFiles, "front.png", "front.jpg", "front.jpeg", "front.webp"),
|
||||
fromTag(al.EmbedArtPath),
|
||||
fromPlaceholder(),
|
||||
)
|
||||
return r, path, nil
|
||||
}
|
||||
|
||||
func (a *artwork) resizedFromOriginal(ctx context.Context, id string, size int) (io.ReadCloser, string, error) {
|
||||
r, path, err := a.get(ctx, id, 0)
|
||||
if err != nil || r == nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer r.Close()
|
||||
usePng := strings.ToLower(filepath.Ext(path)) == ".png"
|
||||
r, err = resizeImage(r, size, usePng)
|
||||
if err != nil {
|
||||
r, path := fromPlaceholder()()
|
||||
return r, path, err
|
||||
}
|
||||
return r, fmt.Sprintf("%s@%d", path, size), nil
|
||||
}
|
||||
|
||||
func extractImage(ctx context.Context, artId model.ArtworkID, extractFuncs ...func() (io.ReadCloser, string)) (io.ReadCloser, string) {
|
||||
for _, f := range extractFuncs {
|
||||
r, path := f()
|
||||
@@ -67,8 +101,33 @@ func extractImage(ctx context.Context, artId model.ArtworkID, extractFuncs ...fu
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// This seems unoptimized, but we need to make sure the priority order of validNames
|
||||
// is preserved (i.e. png is better than jpg)
|
||||
func fromExternalFile(files string, validNames ...string) func() (io.ReadCloser, string) {
|
||||
return func() (io.ReadCloser, string) {
|
||||
fileList := filepath.SplitList(files)
|
||||
for _, validName := range validNames {
|
||||
for _, file := range fileList {
|
||||
_, name := filepath.Split(file)
|
||||
if !strings.EqualFold(validName, name) {
|
||||
continue
|
||||
}
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return f, file
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
|
||||
func fromTag(path string) func() (io.ReadCloser, string) {
|
||||
return func() (io.ReadCloser, string) {
|
||||
if path == "" {
|
||||
return nil, ""
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, ""
|
||||
@@ -94,3 +153,27 @@ func fromPlaceholder() func() (io.ReadCloser, string) {
|
||||
return r, consts.PlaceholderAlbumArt
|
||||
}
|
||||
}
|
||||
|
||||
func resizeImage(reader io.Reader, size int, usePng bool) (io.ReadCloser, error) {
|
||||
img, _, err := image.Decode(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Preserve the aspect ratio of the image.
|
||||
var m *image.NRGBA
|
||||
bounds := img.Bounds()
|
||||
if bounds.Max.X > bounds.Max.Y {
|
||||
m = imaging.Resize(img, size, 0, imaging.Lanczos)
|
||||
} else {
|
||||
m = imaging.Resize(img, 0, size, imaging.Lanczos)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if usePng {
|
||||
err = png.Encode(buf, m)
|
||||
} else {
|
||||
err = jpeg.Encode(buf, m, &jpeg.Options{Quality: conf.Server.CoverJpegQuality})
|
||||
}
|
||||
return io.NopCloser(buf), err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user