diff --git a/core/artwork/artwork_internal_test.go b/core/artwork/artwork_internal_test.go index c7e7f25a2..e74a213d0 100644 --- a/core/artwork/artwork_internal_test.go +++ b/core/artwork/artwork_internal_test.go @@ -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() { diff --git a/core/artwork/reader_playlist.go b/core/artwork/reader_playlist.go index ecc29e6b2..16601b56a 100644 --- a/core/artwork/reader_playlist.go +++ b/core/artwork/reader_playlist.go @@ -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)