* feat(artwork): add KindRadioArtwork and EntityRadio constant
* feat(model): add UploadedImage field and artwork methods to Radio
* feat(model): add Radio to GetEntityByID lookup chain
* feat(db): add uploaded_image column to radio table
* feat(artwork): add radio artwork reader with uploaded image fallback
* feat(api): add radio image upload/delete endpoints
* feat(ui): add radio artwork ID prefix to getCoverArtUrl
* feat(ui): add cover art display and upload to RadioEdit
* feat(ui): add cover art thumbnails to radio list
* feat(ui): prefer artwork URL in radio player helper
* refactor: remove redundant code in radio artwork
- Remove duplicate Avatar rendering in RadioList by reusing CoverArtField
- Remove redundant UpdatedAt assignment in radio image handlers (already set by repository Put)
* refactor(ui): extract shared useImageLoadingState hook
Move image loading/error/lightbox state management into a shared
useImageLoadingState hook in common/. Consolidates duplicated logic
from AlbumDetails, PlaylistDetails, RadioEdit, and artist detail views.
* feat(ui): use radio placeholder icon when no uploaded image
Remove album placeholder fallback from radio artwork reader so radios
without an uploaded image return ErrUnavailable. On the frontend, show
the internet-radio-icon.svg placeholder instead of requesting server
artwork when no image is uploaded, allowing favicon fallback in the
player.
* refactor(ui): update defaultOff fields in useSelectedFields for RadioList
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: address code review feedback
- Add missing alt attribute to CardMedia in RadioEdit for accessibility
- Fix UpdateInternetRadio to preserve UploadedImage field by fetching
existing radio before updating (prevents Subsonic API from clearing
custom artwork)
- Add Reader() level tests to verify ErrUnavailable is returned when
radio has no uploaded image
* refactor: add colsToUpdate to RadioRepository.Put
Use the base sqlRepository.put with column filtering instead of
hand-rolled SQL. UpdateInternetRadio now specifies only the Subsonic API
fields, preventing UploadedImage from being cleared. Image upload/delete
handlers specify only UploadedImage.
* fix: ensure UpdatedAt is included in colsToUpdate for radio Put
---------
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(artwork): add KindDiscArtwork and ParseDiscArtworkID
Add new disc artwork kind with 'dc' prefix for per-disc cover art
support. The composite ID format is albumID:discNumber, parsed by
the new ParseDiscArtworkID helper.
* feat(conf): add DiscArtPriority configuration option
Default: 'disc*.*, cd*.*, embedded'. Controls how per-disc cover
art is resolved, following the same pattern as CoverArtPriority
and ArtistArtPriority.
* feat(artwork): implement extractDiscNumber helper
Extracts disc number from filenames based on glob patterns by
parsing leading digits from the wildcard-matched portion.
Used for matching disc-specific artwork files like disc1.jpg.
* feat(artwork): implement fromDiscExternalFile source function
Disc-aware variant of fromExternalFile that filters image files
by disc number (extracted from filename) or folder association
(for multi-folder albums).
* feat(artwork): implement discArtworkReader
Resolves disc artwork using DiscArtPriority config patterns.
Supports glob patterns with disc number extraction, embedded
images from first track, and falls back to album cover art.
Handles both multi-folder and single-folder multi-disc albums.
* feat(artwork): register disc artwork reader in dispatcher
Add KindDiscArtwork case to getArtworkReader switch, routing
disc artwork requests to the new discArtworkReader.
* feat(subsonic): add CoverArt field to DiscTitle response
Implements OpenSubsonic PR #220: optional cover art ID in
DiscTitle responses for per-disc artwork support.
* feat(subsonic): populate CoverArt in DiscTitle responses
Each DiscTitle now includes a disc artwork ID (dc-albumID:discNum)
that clients can use with getCoverArt to retrieve per-disc artwork.
* style: fix file permission in test to satisfy gosec
* feat(ui): add disc cover art display and lightbox functionality
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: simplify disc artwork code
- Add DiscArtworkID constructor to encapsulate the "albumID:discNumber"
format in one place
- Convert fromDiscExternalFile to a method on discArtworkReader,
reducing parameter count from 6 to 2
- Remove unused rootFolder field from discArtworkReader
* style: fix prettier formatting in subsonic index
* style(ui): move cursor style to makeStyles in SongDatagrid
* feat(artwork): add discsubtitle option to DiscArtPriority
Allow matching disc cover art by the disc's subtitle/name.
When the "discsubtitle" keyword is in the priority list, image files
whose stem matches the disc subtitle (case-insensitive) are used.
This is useful for box sets with named discs (e.g., "The Blue Disc.jpg").
* feat(configuration): update discartpriority to include cover art options
Signed-off-by: Deluan <deluan@navidrome.org>
---------
Signed-off-by: Deluan <deluan@navidrome.org>
* lastfm album.getInfo, getAlbuminfo(2) endpoints
* ... for description and reduce not found log level
* address first comments
* return all images
* Update migration timestamp
* Handle a few edge cases
* Add CoverArtPriority option to retrieve albumart from external sources
* Make agents methods more descriptive
* Use Last.fm name consistently
Co-authored-by: Deluan <deluan@navidrome.org>