quintodrome/ui/src
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
..
actions feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
album chore: format JSX components 2026-02-03 12:46:39 -05:00
artist fix(ui): use div for fragment, check lastfm url for artist page (#4980) 2026-02-04 17:34:26 -05:00
audioplayer feat(ui): prompt before closing window if music is playing (#4899) 2026-01-18 13:11:12 -05:00
common feat(server): implement FTS5-based full-text search (#5079) 2026-02-21 17:52:42 -05:00
dataProvider fix(ui): fix library selection state for single-library users (#4686) 2025-11-15 17:42:28 -05:00
dialogs feat(ui): add download config toml link, disable copy when clipboard not available (#5035) 2026-02-12 10:54:04 -05:00
i18n feat(playlist): add custom playlist cover art upload (#5110) 2026-03-01 14:07:18 -05:00
icons build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
layout feat(server): add ExtAuth logout URL configuration (#5074) 2026-02-23 20:28:38 -05:00
library feat(ui): conditionally display 'path' field in LibraryList for desktop view 2026-03-01 10:50:37 -05:00
missing fix(ui): export all missing files instead of first 1000 2025-12-16 06:43:02 -05:00
personal fix(ui): show last.fm api-key missing in a FormHelperText 2024-12-19 16:59:26 -05:00
player build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
playlist feat(playlist): support #EXTALBUMARTURL directive and sidecar images (#5131) 2026-03-02 11:39:59 -05:00
plugin feat(plugins): allow mounting library directories as read-write (#5122) 2026-02-28 10:59:13 -05:00
radio fix(ui): update titles for radios, shares and show pages (#4128) 2025-05-27 09:01:52 -04:00
reducers fix(ui): prevent "Play Next" restarting play at top of queue (#5049) 2026-02-17 08:34:24 -05:00
share fix(ui): allow scrolling in shareplayer queue by adding delay #4748 2025-11-29 12:54:46 -05:00
song feat(ui): add composer field to table views (#4857) 2026-01-18 13:15:53 -05:00
store feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
subsonic feat(scanner): implement selective folder scanning and file system watcher improvements (#4674) 2025-11-14 22:15:43 -05:00
themes fix(ui): make toggle switches visible in Gruvbox Dark theme (#5063) (#5064) 2026-02-18 15:38:20 -05:00
transcoding fix(ui): minor icon inconsistencies and "no missing files" translation (#3837) 2025-03-16 19:39:19 -04:00
user feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
utils fix(ui): use div for fragment, check lastfm url for artist page (#4980) 2026-02-04 17:34:26 -05:00
App.jsx feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
authProvider.js feat(server): add ExtAuth logout URL configuration (#5074) 2026-02-23 20:28:38 -05:00
config.js feat(server): implement FTS5-based full-text search (#5079) 2026-02-21 17:52:42 -05:00
consts.js feat(Insights): add anonymous usage data collection (#3543) 2024-12-17 17:10:55 -05:00
eventStream.js fix(ui): keep the NowPlayingPanel badge in sync. 2025-06-29 11:35:10 -04:00
eventStream.test.js chore(deps): update all dependencies (#4618) 2025-10-25 17:05:16 -04:00
hotkeys.js Change "Go to current song" hotkey. 2022-12-18 20:58:01 -05:00
index.css chore(deps): bump JS dependencies 2025-03-07 19:47:08 -05:00
index.jsx fix(ui): service worker does not load new version of ui (#3402) 2024-10-16 21:39:14 -04:00
routes.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
setupTests.js Use the default import path for jest-dom. 2024-01-17 17:07:43 -05:00
sw.js fix(ui): service worker crashing on precacheAndRoute (#3528) 2024-12-08 17:43:34 -05:00
useChangeThemeColor.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
vite-env.d.ts build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00