quintodrome/utils
Deluan Quintão acd69f6a4f
feat(playlist): support #EXTALBUMARTURL directive and sidecar images (#5131)
* feat(playlist): add migration for playlist image field rename and external URL

* refactor(playlist): rename ImageFile to UploadedImage and ArtworkPath to UploadedImagePath

Rename playlist model fields and methods for clarity in preparation for
adding external image URL and sidecar image support. Add the new
ExternalImageURL field to the Playlist model.

* feat(playlist): parse #EXTALBUMARTURL directive in M3U imports

* feat(playlist): always sync ExternalImageURL on re-scan, preserve UploadedImage

* feat(artwork): add sidecar image discovery and cache invalidation for playlists

Add playlist sidecar image support to the artwork reader fallback chain.
A sidecar image (e.g., MyPlaylist.jpg next to MyPlaylist.m3u) is discovered
via case-insensitive base name matching using model.IsImageFile(). Cache
invalidation uses max(playlist.UpdatedAt, imageFile.ModTime()) to bust
stale artwork when sidecar or ExternalImageURL local files change.

* 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.

* refactor(artwork): simplify playlist artwork source functions

Extract shared fromLocalFile helper, use url.Parse for scheme check,
and collapse sidecar directory scan conditions.

* test(artwork): remove redundant fromPlaylistSidecar tests

These tests duplicated scenarios already covered by findPlaylistSidecarPath
tests combined with fromLocalFile (tested via fromPlaylistExternalImage).
After refactoring fromPlaylistSidecar to a one-liner composing those two
functions, the wrapper tests add no value.

* fix(playlist): address security review comments from PR #5131:

- Use url.PathUnescape instead of url.QueryUnescape for file:// URLs so
  that '+' in filenames is preserved (not decoded as space).
- Validate all local image paths (file://, absolute, relative) against
  known library boundaries via libraryMatcher, rejecting paths outside
  any configured library.
- Harden #EXTALBUMARTURL against path traversal and SSRF by adding EnableM3UExternalAlbumArt config flag (default false, also
  disabled by EnableExternalServices=false) to gate HTTP(S) URL storage
  at parse time and fetching at read time (defense in depth).
- Log a warning when os.ReadDir fails in findPlaylistSidecarPath for
  diagnosability.
- Extract resolveLocalPath helper to simplify resolveImageURL.

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(playlist): implement human-friendly filename generation for uploaded playlist cover images

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-02 11:39:59 -05:00
..
cache refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
chrono test: improve test reliability with longer sleep durations and generous tolerances 2025-05-25 22:03:55 -04:00
gg feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
gravatar feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
hasher refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
ioutils fix: handle UTF BOM in lyrics and playlist files (#4637) 2025-10-31 09:07:23 -04:00
merge Upgrade Go to 1.23 (#3190) 2024-08-19 17:47:54 -04:00
number chore(deps): remove direct dependency on golang.org/x/exp 2025-12-31 17:03:44 -05:00
pl refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
random refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
req fix(subsonic): clearing playlist comment and public in Subsonic API (#4258) 2025-06-24 08:50:06 -04:00
run refactor: rename chain package to run and update references 2025-06-14 17:19:06 -04:00
singleton refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
slice chore(deps): remove direct dependency on golang.org/x/exp 2025-12-31 17:03:44 -05:00
str refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
context.go Refactored agents calling into its own struct 2021-06-08 17:00:02 -04:00
context_test.go Upgrade Ginkgo to V2 2022-07-26 16:53:17 -04:00
encrypt.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
encrypt_test.go Upgrade Ginkgo to V2 2022-07-26 16:53:17 -04:00
files.go feat(playlist): support #EXTALBUMARTURL directive and sidecar images (#5131) 2026-03-02 11:39:59 -05:00
files_test.go feat(playlist): support #EXTALBUMARTURL directive and sidecar images (#5131) 2026-03-02 11:39:59 -05:00
index_group_parser.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
index_group_parser_test.go Upgrade Ginkgo to V2 2022-07-26 16:53:17 -04:00
limiter.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
time.go fix(scanner): always refresh folder image time when adding first image (#3764) 2025-03-06 22:16:37 -05:00
time_test.go fix(scanner): always refresh folder image time when adding first image (#3764) 2025-03-06 22:16:37 -05:00
utils_suite_test.go Upgrade Ginkgo to V2 2022-07-26 16:53:17 -04:00