Add API command to get the color palette for any image#4193
Conversation
Add metadata/get_image_palette, accepting a MediaItemImage or an image URL and returning the Sendspin color@v1 palette. Reuses the existing process-wide palette memory cache (shared with the player queue logic), so repeated requests for the same image are cheap.
Store extracted palettes in the cache controller (sqlite) so they persist across restarts, are shared, and clear with the cache. Drop the bespoke in-memory LRU and the synchronous peek_palette_for_url: the cache controller is async-only and cannot be read during the (sync) player-state serialization, so the resolved palette for the currently shown image is now carried on player state (set by the async fetch) and read back at serialize time. User-visible behaviour is unchanged.
There was a problem hiding this comment.
Pull request overview
This PR exposes image color-palette extraction (Sendspin color@v1) as a public metadata API command and changes palette caching to use the cache controller (sqlite) so results persist across restarts, while keeping synchronous player state serialization non-blocking by carrying the resolved palette on Player.
Changes:
- Add
metadata/get_image_paletteAPI command to return aMediaItemPalettefor aMediaItemImageor image URL. - Move palette caching from an in-memory LRU to the cache controller (sqlite) and remove the sync
peek_palette_for_urlpath. - Attach asynchronously-resolved palettes to player state so sync serialization can include palettes without blocking.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/core/test_image_proxy.py | Updates test docstring wording around cache key behavior. |
| music_assistant/models/player.py | Adds carried palette fields + setter and uses carried palette during sync state serialization. |
| music_assistant/helpers/colors.py | Switches palette caching to cache controller; removes in-memory LRU + peek_palette_for_url. |
| music_assistant/controllers/players/controller.py | Updates palette-fetch scheduling/attachment flow to use async resolution and player-carried palette. |
| music_assistant/controllers/metadata.py | Adds metadata/get_image_palette API command wiring to palette extraction helpers. |
Comments suppressed due to low confidence (1)
music_assistant/controllers/players/controller.py:1680
- [CRITICAL]
_schedule_palette_fetchdedupes tasks only byplayer_id+slot, so if the current track changes while a previous palette fetch is still running, the new fetch will be dropped and the new track may never get a palette until another state update happens. Setabort_existing=Truefor current-track fetches (or include an image-specific suffix in the task_id) so the latest image always gets fetched.
# The caller only schedules a current-track fetch when no palette is set yet,
# and the task_id dedupes concurrent fetches, so no extra cache probe is needed.
slot = "current" if trigger_update else "next"
self.mass.create_task(
self._fetch_palette(player_id, image_url, trigger_update=trigger_update),
task_id=f"palette_fetch_{player_id}_{slot}",
abort_existing=False,
)
- Fix stale docstring: palettes are cached via the cache controller, not memory. - Key the palette-fetch task on the image so a track change always schedules a fetch for the new image instead of being dropped by an in-flight fetch for the previous one (same-image schedules still dedupe). - Add tests for the get_image_palette command dispatch and error handling.
|
re the suppressed task_id comment: valid edge - a quick track change could drop the new fetch while the previous one is still running. keyed the task on the image now (d8caebf) so the new image always schedules, same-image schedules still dedupe. went with that over abort_existing=True since that would restart an in-flight fetch on unrelated state updates. |
|
This should maybe be backported to stable because this also improves the caching of the palette and it can be helpful for the (mobile) clients to have this command available |
What does this implement/fix?
Color-palette extraction (Sendspin color@v1) was only available internally, for now-playing artwork. This exposes it as a metadata API command so any image can be turned into a palette, and moves the palette cache to the cache controller so results persist across restarts instead of living in a volatile in-memory LRU.
Changes:
metadata/get_image_palette— accepts aMediaItemImageor an image URL and returns aMediaItemPalette.peek_palette_for_url.Types of changes
bugfixnew-featureenhancementnew-providerbreaking-changerefactordocumentationmaintenancecidependenciesChecklist
pre-commit run --all-filespasses.pytestpasses, and tests have been added/updated undertests/where applicable.music-assistant/modelsis linked.music-assistant/frontendis linked.