feat(artwork): add external image URL source to playlist artwork reader

Add fromPlaylistExternalImage source function that resolves playlist
cover art from ExternalImageURL, supporting both HTTP(S) URLs (via
the existing fromURL helper) and local file paths (via os.Open).
Insert it in the Reader() fallback chain between sidecar and tiled cover.
This commit is contained in:
Deluan 2026-03-01 21:48:46 -05:00
parent c596aed591
commit dc61dfd6d6
2 changed files with 60 additions and 0 deletions

View File

@ -318,6 +318,44 @@ var _ = Describe("Artwork", func() {
Expect(path).To(BeEmpty())
})
})
Describe("fromPlaylistExternalImage", func() {
It("opens local path from ExternalImageURL", func() {
tmpDir := GinkgoT().TempDir()
imgPath := filepath.Join(tmpDir, "cover.jpg")
Expect(os.WriteFile(imgPath, []byte("external image data"), 0600)).To(Succeed())
reader := &playlistArtworkReader{
pl: model.Playlist{ExternalImageURL: imgPath},
}
r, path, err := reader.fromPlaylistExternalImage(ctx)()
Expect(err).ToNot(HaveOccurred())
Expect(r).ToNot(BeNil())
Expect(path).To(Equal(imgPath))
data, _ := io.ReadAll(r)
Expect(string(data)).To(Equal("external image data"))
r.Close()
})
It("returns nil when ExternalImageURL is empty", func() {
reader := &playlistArtworkReader{
pl: model.Playlist{ExternalImageURL: ""},
}
r, path, err := reader.fromPlaylistExternalImage(ctx)()
Expect(err).ToNot(HaveOccurred())
Expect(r).To(BeNil())
Expect(path).To(BeEmpty())
})
It("returns error when local file does not exist", func() {
reader := &playlistArtworkReader{
pl: model.Playlist{ExternalImageURL: "/non/existent/path/cover.jpg"},
}
r, _, err := reader.fromPlaylistExternalImage(ctx)()
Expect(err).To(HaveOccurred())
Expect(r).To(BeNil())
})
})
})
Describe("resizedArtworkReader", func() {

View File

@ -8,6 +8,7 @@ import (
"image/draw"
"image/png"
"io"
"net/url"
"os"
"path/filepath"
"strings"
@ -67,6 +68,7 @@ func (a *playlistArtworkReader) Reader(ctx context.Context) (io.ReadCloser, stri
return selectImageReader(ctx, a.artID,
a.fromPlaylistUploadedImage(),
a.fromPlaylistSidecar(),
a.fromPlaylistExternalImage(ctx),
a.fromGeneratedTiledCover(ctx),
fromAlbumPlaceholder(),
)
@ -131,6 +133,26 @@ func (a *playlistArtworkReader) fromPlaylistSidecar() sourceFunc {
}
}
func (a *playlistArtworkReader) fromPlaylistExternalImage(ctx context.Context) sourceFunc {
return func() (io.ReadCloser, string, error) {
if a.pl.ExternalImageURL == "" {
return nil, "", nil
}
if strings.HasPrefix(a.pl.ExternalImageURL, "http://") || strings.HasPrefix(a.pl.ExternalImageURL, "https://") {
parsed, err := url.Parse(a.pl.ExternalImageURL)
if err != nil {
return nil, "", err
}
return fromURL(ctx, parsed)
}
f, err := os.Open(a.pl.ExternalImageURL)
if err != nil {
return nil, "", err
}
return f, a.pl.ExternalImageURL, nil
}
}
func (a *playlistArtworkReader) fromGeneratedTiledCover(ctx context.Context) sourceFunc {
return func() (io.ReadCloser, string, error) {
tiles, err := a.loadTiles(ctx)