music-assistant-server.git
8 weeks agoAdd Zvuk Music provider (#3090)
Mikhail Nevskiy [Tue, 10 Feb 2026 07:01:39 +0000 (10:01 +0300)]
Add Zvuk Music provider (#3090)

* Add zvuk_music provider scaffold (manifest, constants, icons)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zvuk Music API client wrapper

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zvuk Music model parsers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add ZvukMusicProvider implementation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update zvuk_music icons to official 2024 logo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zvuk Music provider integration tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Replace Zvuk Music integration tests with unit tests

Remove integration tests that required real API tokens and a running MA
server. Add comprehensive unit tests for parsers using mock objects,
covering all parser functions (parse_artist, parse_album, parse_track,
parse_playlist) with various scenarios.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address PR #3090 review: decorator, duration, allow_seek

- Refactor API error handling into @handle_zvuk_errors(not_found_return=...)
  decorator (api_client.py)
- Add duration from get_track() and allow_seek=True in get_stream_details
  (provider.py)

Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(zvuk): clarify why get_track is needed for duration in get_stream_details

Co-authored-by: Cursor <cursoragent@cursor.com>
---------

Co-authored-by: Михаил Невский <renso@MacBook-Pro-Mihail.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
8 weeks agoAirplay stability fixes (#3121)
Marvin Schenkel [Tue, 10 Feb 2026 06:56:55 +0000 (07:56 +0100)]
Airplay stability fixes (#3121)

8 weeks agofeat(deezer): add Flow, Radios and Top Charts as recommendations (#3077)
Dionysis Fortis [Tue, 10 Feb 2026 06:56:01 +0000 (08:56 +0200)]
feat(deezer): add Flow, Radios and Top Charts as recommendations (#3077)

* feat(deezer): add Flow, Radios and Top Charts as dynamic playlists

Implements recommendations for Deezer provider:
- User's personalized Flow as a virtual playlist
- All available Deezer Radios (genres, moods, etc.)
- Top Charts by country

These appear under the Recommendations panel and use virtual
playlist objects (in_library=false) for dynamic content.

Tested on dev environment and working as expected.

* fix: add missing type annotations for mypy

* Extract virtual playlist IDs and radio IDs into module-level constants

8 weeks ago⬆️ Update music-assistant-frontend to 2.17.83 (#3129)
music-assistant-machine [Tue, 10 Feb 2026 04:01:00 +0000 (05:01 +0100)]
⬆️ Update music-assistant-frontend to 2.17.83 (#3129)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
8 weeks agoAdd back pressure to radio streams (#3116)
Marvin Schenkel [Mon, 9 Feb 2026 14:32:31 +0000 (15:32 +0100)]
Add back pressure to radio streams (#3116)

8 weeks agoFix sync groups with dynamic option losing members on power off (#3118)
OzGav [Mon, 9 Feb 2026 12:58:50 +0000 (22:58 +1000)]
Fix sync groups with dynamic option losing members on power off (#3118)

* Fix sync groups with dynamic option losing members on power off

* Remove comment

* Apply suggestion from @OzGav

8 weeks ago⬆️ Update music-assistant-models to 1.1.98 (#3117)
music-assistant-machine [Mon, 9 Feb 2026 11:28:36 +0000 (12:28 +0100)]
⬆️ Update music-assistant-models to 1.1.98 (#3117)

Co-authored-by: MarvinSchenkel <17671719+MarvinSchenkel@users.noreply.github.com>
8 weeks agoAdd PlayerOptions to backend and MusicCast (#3064)
Fabian Munkes [Mon, 9 Feb 2026 08:43:50 +0000 (09:43 +0100)]
Add PlayerOptions to backend and MusicCast (#3064)

8 weeks agofix(yandex_music): fix playlist loading and missing album cover art (#3099)
Mikhail Nevskiy [Mon, 9 Feb 2026 08:42:59 +0000 (11:42 +0300)]
fix(yandex_music): fix playlist loading and missing album cover art (#3099)

* fix(yandex_music): fix playlist tracks not loading in UI

- Return empty list for page > 0 since Yandex Music API returns all
  tracks in a single call; without this the controller pagination loop
  never terminates
- Add fetch_tracks_async() fallback when API returns playlist metadata
  without tracks
- Raise ResourceTemporarilyUnavailable instead of returning empty list
  when tracks are expected but unavailable, preventing cache of empty
  results
- Fetch full track details in batches of 50 to reduce timeout risk
- Retry get_tracks once on NetworkError in api_client
- Raise ResourceTemporarilyUnavailable on NetworkError in get_playlist
  to prevent caching None as empty result
- Suppress yandex_music library DEBUG logs to avoid huge API dumps

Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: apply ruff fixes and update snapshot

- Remove unused noqa comment (RUF100) in provider.py
- Update test_parsers snapshot for upstream model changes (year/style fields)

Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(yandex_music): fix missing album cover art in library

- In parse_track(), use parse_album() instead of ItemMapping for the
  track's album reference so albums are created with full metadata
  (including cover_uri) when tracks are synced to the library
- In get_liked_albums(), fetch full album details via client.albums()
  in batches of 50, since the users_likes_albums endpoint returns
  minimal data without cover_uri

Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(yandex_music): regenerate parser snapshots after models bump

Co-authored-by: Cursor <cursoragent@cursor.com>
* Update music_assistant/providers/yandex_music/api_client.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update music_assistant/providers/yandex_music/provider.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add tests for api_client batching/retry and playlist edge cases

Cover the 3 unresolved Copilot review comments from PR #3099:
- get_liked_albums() batch fetching and NetworkError fallback
- get_tracks() retry-on-NetworkError logic
- get_playlist_tracks() page>0, fetch_tracks_async fallback, empty batch error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------

Co-authored-by: Михаил Невский <renso@MacBook-Pro-Mihail.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
8 weeks agoApple Music: Fix syncing shared playlists (#3095)
MizterB [Mon, 9 Feb 2026 08:28:13 +0000 (03:28 -0500)]
Apple Music: Fix syncing shared playlists (#3095)

* Catch MediaNotFoundError in get_library_playlists

* Undo try/except, fix is_library_id

* Tests for is_library_id

---------

Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
8 weeks agoAllow radio stations to be added to playlists (#2951)
OzGav [Mon, 9 Feb 2026 07:35:38 +0000 (17:35 +1000)]
Allow radio stations to be added to playlists (#2951)

* Add support for radio station URIs in playlists

Enable radio stations to be added to Music Assistant (builtin) playlists
by expanding the playlist track type system to support both Track and Radio items.

Changes:
- playlists.py: Add RADIO media type handling in add_playlist_tracks()
- playlists.py: Update return types to support Track | Radio
- music_provider.py: Expand get_playlist_tracks() return type to list[Track | Radio]
- builtin/__init__.py: Replace Track assertion with type check for Track | Radio

This allows users to mix tracks and radio stations in the same playlist,
with radio URIs stored and retrieved alongside track URIs.

* Add validation to restrict radio items to builtin playlists only

Radio items can only be added to builtin playlists because other streaming
providers (Spotify, Apple Music, etc.) don't support arbitrary radio stream
URLs in their playlists. This adds backend validation to enforce this
constraint and provide a clear error message when attempted.

* Fix bug: Radio URIs were being resolved as track IDs

When adding radio items with library URIs (library://radio/ID) to builtin
playlists, the code was incorrectly passing through track-specific resolution
logic that called tracks.get(ID) instead of radio.get(ID), causing the wrong
items to be added.

Fixed by handling all radio items for builtin playlists early in the URI
processing, adding them directly by URI before the track-specific logic runs.
This ensures library://radio/8 adds radio ID 8, not track ID 8.

* Fix metadata and queue controllers to handle Radio items in playlists

The metadata controller was crashing when processing playlists containing
radio items because it tried to access track.album on Radio objects.

The player_queues controller had incorrect type annotations that assumed
playlist items were always Tracks.

Changes:
- metadata.py: Add isinstance(track, Track) check before accessing .album
- player_queues.py: Update get_playlist_tracks() return type to Track | Radio
- player_queues.py: Add Radio to imports

Both controllers now properly handle playlists containing mixed Track and
Radio items.

* Fix mypy type errors for radio playlist support

The previous implementation changed the base MusicProvider.get_playlist_tracks()
interface to return list[Track | Radio], which caused type errors across all
provider implementations that only return list[Track].

Fixes:
1. Reverted base interface to return list[Track] (standard for all providers)
2. Builtin provider overrides with list[Track | Radio] (covariant return type)
3. Added type: ignore[override] for builtin's covariant override
4. Added type: ignore[return-value] where playlist controller calls providers
5. Fixed radio_mode_base_tracks to filter out Radio items (only works with Track)
6. Fixed variable naming: 'track' -> 'item' where both types are possible

This approach is correct because:
- Only builtin provider supports radio in playlists
- All other providers (Spotify, Tidal, etc.) only support tracks
- Python's type system supports covariant return types in overrides
- list[Track] is compatible with list[Track | Radio] at call sites

* Refactor playlist URI handling logic for clarity

The previous logic combined radio and track handling in one condition,
making it unclear why library URIs were being added for radio items.

Separated into two clear cases:

1. Radio items: ALWAYS add by URI (even library://radio/ID)
   - Radio items cannot go through track-matching logic
   - They must be retrieved by their full URI
   - This is a necessary exception to the "no library URIs" rule

2. Track items: Only add non-library URIs directly
   - Provider URIs (spotify://track/xyz) are portable
   - Library URIs (library://track/123) fall through to matching logic
   - This finds the actual provider URI to store instead

This addresses the review concern that the logic didn't make sense and
makes the special handling of radio items explicit.

* Convert library radio URIs to provider URIs for DB rebuild resilience

Previously, library radio URIs (library://radio/8) were being stored
directly in playlists. This would break on database rebuilds when the
radio item gets a different database ID.

Now when adding a library radio URI:
1. Fetch the full radio item from the library
2. Get its provider mapping (typically builtin)
3. Extract the actual provider item_id (the real stream URL)
4. Create and store the provider URI (builtin://radio/https://stream...)

This ensures playlists contain portable provider URIs with the actual
stream URLs, surviving database rebuilds.

Example:
- Before: Stored library://radio/8 (breaks on rebuild)
- After: Stored builtin://radio/https://stream.example.com/radio.mp3 (portable)

Provider radio URIs are already portable and stored as-is.

* Apply reviewer suggestion: simplify comment in radio_mode_base_tracks

Changed comment from 'filter out unavailable tracks and radio items' to
'filter out unavailable items' as suggested by reviewer for better clarity.

* Make playlist validation generic for non-track items

Changed validation from checking specifically for radio items to checking
for any non-track media type. This makes the code more future-proof for
supporting other media types like podcast episodes in playlists.

Applied reviewer suggestion to make the logic more generic.

* Make library URI conversion universal using get_item_by_uri

Refactored the radio-specific library URI conversion to work universally
for all non-track media types (radio, podcast episodes, audiobooks, etc.)
by using self.mass.music.get_item_by_uri() instead of calling media-type-
specific controllers.

This addresses reviewer feedback to avoid code duplication and makes the
backend ready to support additional media types in playlists as the
frontend adds support for them.

Changes:
- Changed condition from "media_type == MediaType.RADIO" to "media_type != MediaType.TRACK"
- Use get_item_by_uri() to fetch items generically
- Renamed variables from radio-specific to generic (prov_mapping, full_item)
- Updated comments to reflect universal support

* Add type safety check for items with provider_mappings

Fixed mypy error where get_item_by_uri can return BrowseFolder which
doesn't have provider_mappings attribute. Added hasattr check and
restructured to use if/else instead of early continue to properly
handle items without provider mappings.

* Replace hasattr with isinstance for type-safe media item checking

Replaced hasattr() check with isinstance() for better type safety:
- Added Audiobook and PodcastEpisode to imports
- Use isinstance(full_item, (Radio, Audiobook, PodcastEpisode))
- Removed need for type: ignore[attr-defined] comment
- mypy now understands full_item has provider_mappings attribute

This is more explicit, type-safe, and self-documenting about which
media types are supported in playlists.

* Add PlaylistItem type alias for better maintainability

Created a shared type alias PlaylistItem = Track | Radio | PodcastEpisode | Audiobook
to avoid hardcoding union types in multiple places.

Changes:
- playlists.py: Added PlaylistItem type alias at top of file
- playlists.py: Updated all Track | Radio annotations to use PlaylistItem
- player_queues.py: Added PlaylistItem type alias
- player_queues.py: Updated get_playlist_tracks to use PlaylistItem
- builtin/__init__.py: Added Audiobook and PodcastEpisode to imports
- builtin/__init__.py: Updated all type annotations to include all supported types
- builtin/__init__.py: Updated isinstance checks to include all types

Now there's a single source of truth for what media types can be in playlists.
Adding future types only requires changing the type alias definition.

* Add PlaylistItem type alias for better maintainability

Added type aliases and constants to keep all playlist item type definitions synchronized:
- PlaylistItem type alias defined in playlists.py, builtin/__init__.py, and player_queues.py
- PLAYLIST_MEDIA_TYPES constant for MediaType enum checks
- PLAYLIST_ITEM_CLASSES constant for isinstance checks

This ensures that adding new playlist item types in the future only requires updating the type alias and constants, with all runtime checks automatically including the new type.

* Centralize playlist item type definitions in constants.py

Moved all playlist item type definitions to a single location in constants.py to eliminate duplication and ensure consistency:
- PlaylistItem type alias
- PLAYLIST_MEDIA_TYPES tuple for MediaType enum checks
- PLAYLIST_ITEM_CLASSES tuple for isinstance checks
- PLAYLIST_NON_TRACK_ITEM_CLASSES for cases where Track needs separate handling

Updated playlists.py, builtin/__init__.py, and player_queues.py to import from constants.py.

Also improved comments:
- Better explanation of type ignore for covariant override
- More generic comment for radio mode filtering
- Updated to reference PlaylistItem instead of specific types

* Replace hardcoded types with type ignore comments

Removed cast statements with hardcoded types and replaced with type ignore comments for mypy:
- playlists.py: Added type: ignore[union-attr] for provider_mappings access
- builtin/__init__.py: Added type: ignore[attr-defined] and type: ignore[arg-type] for position and append

This ensures all type checking uses the constants from constants.py without any hardcoded type repetition.

* Use cast with type alias constants instead of type ignore

Added PlaylistNonTrackItem type alias to constants.py and updated code to use cast() with type aliases instead of type ignore comments:
- playlists.py: cast(PlaylistNonTrackItem, full_item)
- builtin/__init__.py: cast(PlaylistItem, track)

This maintains type safety while using only the centralized type definitions from constants.py.

* Simplify type definitions using get_args()

Reduced complexity by deriving PLAYLIST_ITEM_CLASSES from PlaylistItem using get_args():
- Removed PlaylistNonTrackItem type alias (only used once)
- PLAYLIST_ITEM_CLASSES now auto-derived from PlaylistItem
- Replaced cast with type ignore comment for single non-track case

This reduces maintenance: adding a new type only requires updating PlaylistItem and PLAYLIST_MEDIA_TYPES.

* Use get_args() inline instead of PLAYLIST_ITEM_CLASSES constant

Removed PLAYLIST_ITEM_CLASSES constant and use get_args(PlaylistItem) directly at the single call site.

This reduces to just 3 definitions in constants.py:
- PlaylistItem type alias
- PLAYLIST_MEDIA_TYPES tuple
- PLAYLIST_NON_TRACK_ITEM_CLASSES tuple

* Add type ignore for cast assignment in builtin provider

Added type: ignore[assignment] comment to fix mypy error where cast result is assigned to broader-typed variable.

* Use new variable name instead of type ignore for cast

Instead of reassigning track with a type ignore, use a new variable playlist_item.
This makes the isinstance check meaningful for type narrowing without needing type ignore comments.

* Clarify comments about playlist types in builtin provider

Updated comments to distinguish between:
- System-generated playlists (favorites, random, etc.) which only contain tracks
- User-created playlists which can contain Track, Radio, PodcastEpisode, and Audiobook items

This makes it clear that the new support for non-track items only applies to user-created playlists.

* Lint

* Unify track and non-track playlist item handling logic

Merged separate code paths for tracks and non-track items into unified logic:
- Same structure for all media types (Track, Radio, PodcastEpisode, Audiobook)
- For builtin playlists: Get full item, iterate provider mappings, add first available
- Tracks get quality sorting before selection
- Non-track items use first available mapping (typically only one)

This simplifies maintenance and follows the same pattern for all playlist item types.

* Fix mypy error in unified playlist logic

Added cast to Track type for match_provider call since full_item has broad union type from get_item_by_uri().
We know it's a Track from the media_type check but mypy can't infer this.

* Rename PlaylistItem to PlaylistPlayableItem to avoid naming conflict

The PlaylistItem union type conflicted with the existing PlaylistItem
dataclass in music_assistant.helpers.playlists, causing import collisions
and confusing type hints. Renamed the union type to PlaylistPlayableItem
to clearly distinguish it from the helper dataclass.

Changes:
- Renamed type alias in constants.py
- Updated all imports and usages in:
  - playlists.py (import, return types, comments)
  - builtin/__init__.py (import, return type, isinstance, cast)
  - player_queues.py (import, return types)
- Updated comments to reference new name

* Remove unused constant and duplicate import

- Remove PLAYLIST_NON_TRACK_ITEM_CLASSES as it was declared but never used
- Remove duplicate AsyncGenerator import from TYPE_CHECKING block in playlists.py

* Sort provider mappings for deterministic non-track item selection

---------

Co-authored-by: Claude <noreply@anthropic.com>
8 weeks agoSnapcast: Introduce fallback Snapcast setup for dev environments (#3108)
Mischa Siekmann [Mon, 9 Feb 2026 07:34:16 +0000 (08:34 +0100)]
Snapcast: Introduce fallback Snapcast setup for dev environments (#3108)

* snapcast: introduce fallback config and plugin-directory for dev environments

* snapcast: suggested copilot fixes

* snapcast: another comment with a local path in the snapserver.conf

* merge fixes

8 weeks agoPrint provider name in logging output (#3113)
Fabian Munkes [Mon, 9 Feb 2026 07:16:00 +0000 (08:16 +0100)]
Print provider name in logging output (#3113)

* multi-instance logging name

* use only name

8 weeks agoSnapcast: Fix player availability issues and align state with server truth (#3104)
Mischa Siekmann [Sun, 8 Feb 2026 19:18:50 +0000 (20:18 +0100)]
Snapcast: Fix player availability issues and align state with server truth (#3104)

8 weeks ago⬆️ Update music-assistant-models to 1.1.97 (#3114)
music-assistant-machine [Sun, 8 Feb 2026 19:07:32 +0000 (20:07 +0100)]
⬆️ Update music-assistant-models to 1.1.97 (#3114)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
8 weeks agoFix IPv6 address handling in bind config and ifaddr parsing (#3111)
Firdavs Murodov [Sun, 8 Feb 2026 18:36:30 +0000 (19:36 +0100)]
Fix IPv6 address handling in bind config and ifaddr parsing (#3111)

8 weeks agoSanitize queue_item.name in icy headers (#3112)
Fabian Munkes [Sun, 8 Feb 2026 18:34:29 +0000 (19:34 +0100)]
Sanitize queue_item.name in icy headers (#3112)

2 months agoSupport multiple artist and other tags in FLAC/OGG files (#3076)
OzGav [Sat, 7 Feb 2026 12:32:34 +0000 (23:32 +1100)]
Support multiple artist and other tags in FLAC/OGG files (#3076)

2 months ago⬆️ Update music-assistant-frontend to 2.17.82 (#3107)
music-assistant-machine [Sat, 7 Feb 2026 03:35:34 +0000 (04:35 +0100)]
⬆️ Update music-assistant-frontend to 2.17.82 (#3107)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoFix local file system audiobook edge cases (#3080)
OzGav [Fri, 6 Feb 2026 22:39:36 +0000 (09:39 +1100)]
Fix local file system audiobook edge cases (#3080)

2 months agoAdd group volume mute support (#3034)
scyto [Fri, 6 Feb 2026 22:38:01 +0000 (14:38 -0800)]
Add group volume mute support (#3034)

2 months agoChore(deps): Bump actions/github-script from 7 to 8 (#3105)
dependabot[bot] [Fri, 6 Feb 2026 18:51:08 +0000 (19:51 +0100)]
Chore(deps): Bump actions/github-script from 7 to 8 (#3105)

Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 months agoRemove Sonos ERA from broken AirPlay models
Marcel van der Veldt [Fri, 6 Feb 2026 18:26:56 +0000 (19:26 +0100)]
Remove Sonos ERA from broken AirPlay models

2 months agoFix zeroconf interfaces config
Marcel van der Veldt [Fri, 6 Feb 2026 18:25:09 +0000 (19:25 +0100)]
Fix zeroconf interfaces config

2 months agoImprove dependency checks workflow
Marcel van der Veldt [Fri, 6 Feb 2026 13:42:29 +0000 (14:42 +0100)]
Improve dependency checks workflow

2 months agoUse RAOP protocol for Juke Audio devices (#3102)
Justin Kenyon [Fri, 6 Feb 2026 12:12:35 +0000 (07:12 -0500)]
Use RAOP protocol for Juke Audio devices (#3102)

2 months agoImprove dependency check workflow
Marcel van der Veldt [Fri, 6 Feb 2026 12:11:33 +0000 (13:11 +0100)]
Improve dependency check workflow

2 months agoEnable IPv6 support for zeroconf, stream server and AirPlay DACP (#3086)
Firdavs Murodov [Fri, 6 Feb 2026 09:34:22 +0000 (10:34 +0100)]
Enable IPv6 support for zeroconf, stream server and AirPlay DACP (#3086)

2 months ago⬆️ Update music-assistant-frontend to 2.17.81 (#3101)
music-assistant-machine [Fri, 6 Feb 2026 08:51:31 +0000 (09:51 +0100)]
⬆️ Update music-assistant-frontend to 2.17.81 (#3101)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoMerge branch 'dev' of https://github.com/music-assistant/server into dev
Marcel van der Veldt [Fri, 6 Feb 2026 08:32:09 +0000 (09:32 +0100)]
Merge branch 'dev' of https://github.com/music-assistant/server into dev

2 months agoFix broken test
Marcel van der Veldt [Fri, 6 Feb 2026 08:32:00 +0000 (09:32 +0100)]
Fix broken test

2 months agoRevert "Fix squeezelite WAV playback" (#3103)
Marvin Schenkel [Fri, 6 Feb 2026 08:25:05 +0000 (09:25 +0100)]
Revert "Fix squeezelite WAV playback" (#3103)

Revert "Fix squeezelite WAV playback (#3084)"

This reverts commit fdc7b350949c83cc34d413e04c732acbd446fb49.

2 months agoAnother small fix for the security workflow
Marcel van der Veldt [Thu, 5 Feb 2026 23:38:55 +0000 (00:38 +0100)]
Another small fix for the security workflow

2 months agoFix issue in workflow
Marcel van der Veldt [Thu, 5 Feb 2026 23:07:50 +0000 (00:07 +0100)]
Fix issue in workflow

2 months agoAdd workflow to check dependencies for supply chain risks
Marcel van der Veldt [Thu, 5 Feb 2026 22:30:52 +0000 (23:30 +0100)]
Add workflow to check dependencies for supply chain risks

2 months agoFix squeezelite WAV playback (#3084)
OzGav [Thu, 5 Feb 2026 21:27:17 +0000 (08:27 +1100)]
Fix squeezelite WAV playback (#3084)

2 months agoSnapcast: Stop the control scripts gracefully before shutting down the built-in snapc...
Mischa Siekmann [Thu, 5 Feb 2026 13:52:29 +0000 (14:52 +0100)]
Snapcast: Stop the control scripts gracefully before shutting down the built-in snapcast server (#3092)

gracefully stop control scripts before shutting down the snapcast server

2 months agoAdd debug logging for scrobbled tracks (#3091)
Monkey Do [Thu, 5 Feb 2026 13:27:27 +0000 (14:27 +0100)]
Add debug logging for scrobbled tracks (#3091)

There is a debug trace for playing-now listens, but not for full scrobbles, making it harder to diagnose potential issues

2 months agoFix Yandex Music provider for lossless streaming support (#3093)
Mikhail Nevskiy [Thu, 5 Feb 2026 13:25:29 +0000 (16:25 +0300)]
Fix Yandex Music provider for lossless streaming support (#3093)

* yandex_music: Add lossless streaming via get-file-info API and unit tests

Improve Yandex Music provider with proper lossless (FLAC) streaming support
and comprehensive test coverage.

## Changes

### Lossless Streaming (api_client.py, streaming.py)
- Add `get_track_file_info_lossless()` API method using `/get-file-info`
  endpoint with quality=lossless parameter
- Prefer flac-mp4/aac-mp4 codecs (Yandex API 2025 format)
- Implement retry logic: try transports=encraw first, fallback to
  transports=raw on 401 Unauthorized
- When lossless requested, try get-file-info first (returns FLAC),
  fallback to download-info if unavailable

### Provider improvements (provider.py)
- Minor variable naming fix in get_track()

### Documentation (README.md)
- Add provider README with OAuth token instructions
- Document audio quality settings and lossless troubleshooting

### Tests
- Add unit tests for parsers (artists, albums, tracks, playlists)
- Add unit tests for streaming quality selection logic
- Add snapshot tests for consistent parsing output
- Add fixtures and stubs in conftest.py
- Add integration and e2e test scaffolding

https://claude.ai/code/session_01S4Eth5mUC7hLd3K3JxX7Cy

* Delete tests/providers/yandex_music/test_e2e_server.py

* Update Yandex Music documentation URL

---------

Co-authored-by: Claude <noreply@anthropic.com>
2 months agoAdd album year to track SQL query (#3085)
Dionysis Fortis [Thu, 5 Feb 2026 13:04:04 +0000 (15:04 +0200)]
Add album year to track SQL query (#3085)

* Add album year to track SQL query

* Bump models to 1.1.96 and update snapshots

2 months agofix: cache check for expire (#3087)
relic664 [Thu, 5 Feb 2026 10:24:18 +0000 (05:24 -0500)]
fix: cache check for expire (#3087)

* fix: cache check for expire

* fixed formatting

2 months agoApple Music: Limit get_library_tracks to 150 songs per batch (#3094)
MizterB [Thu, 5 Feb 2026 09:32:59 +0000 (04:32 -0500)]
Apple Music: Limit get_library_tracks to 150 songs per batch (#3094)

Reduce max_limit to 150

2 months ago⬆️ Update music-assistant-frontend to 2.17.80 (#3097)
music-assistant-machine [Thu, 5 Feb 2026 03:42:16 +0000 (04:42 +0100)]
⬆️ Update music-assistant-frontend to 2.17.80 (#3097)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months ago⬆️ Update music-assistant-frontend to 2.17.79 (#3089)
music-assistant-machine [Wed, 4 Feb 2026 03:41:30 +0000 (04:41 +0100)]
⬆️ Update music-assistant-frontend to 2.17.79 (#3089)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoChore(deps): Bump ytmusicapi from 1.11.3 to 1.11.5 (#3081)
dependabot[bot] [Tue, 3 Feb 2026 10:18:48 +0000 (10:18 +0000)]
Chore(deps): Bump ytmusicapi from 1.11.3 to 1.11.5 (#3081)

* Chore(deps): Bump ytmusicapi from 1.11.3 to 1.11.5

Bumps [ytmusicapi](https://github.com/sigma67/ytmusicapi) from 1.11.3 to 1.11.5.
- [Release notes](https://github.com/sigma67/ytmusicapi/releases)
- [Commits](https://github.com/sigma67/ytmusicapi/compare/1.11.3...1.11.5)

---
updated-dependencies:
- dependency-name: ytmusicapi
  dependency-version: 1.11.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
* Also bump the provider manifest.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoChore(deps-dev): Bump pre-commit from 4.5.0 to 4.5.1 (#3082)
dependabot[bot] [Tue, 3 Feb 2026 07:39:45 +0000 (08:39 +0100)]
Chore(deps-dev): Bump pre-commit from 4.5.0 to 4.5.1 (#3082)

Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.5.0...v4.5.1)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 months agoChore(deps): Bump mashumaro from 3.17 to 3.18 (#3083)
dependabot[bot] [Tue, 3 Feb 2026 07:39:31 +0000 (08:39 +0100)]
Chore(deps): Bump mashumaro from 3.17 to 3.18 (#3083)

Bumps [mashumaro](https://github.com/Fatal1ty/mashumaro) from 3.17 to 3.18.
- [Release notes](https://github.com/Fatal1ty/mashumaro/releases)
- [Commits](https://github.com/Fatal1ty/mashumaro/compare/v3.17...v3.18)

---
updated-dependencies:
- dependency-name: mashumaro
  dependency-version: '3.18'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 months agoAllow all special characters in passwords (#3072)
OzGav [Mon, 2 Feb 2026 13:12:55 +0000 (00:12 +1100)]
Allow all special characters in passwords (#3072)

* fix-commas-in-passwords

* shorten comment

2 months ago⬆️ Update music-assistant-frontend to 2.17.78 (#3075)
music-assistant-machine [Mon, 2 Feb 2026 12:58:32 +0000 (13:58 +0100)]
⬆️ Update music-assistant-frontend to 2.17.78 (#3075)

Co-authored-by: MarvinSchenkel <17671719+MarvinSchenkel@users.noreply.github.com>
2 months agohass_players: Show now playing info from external playback (#3015)
Ulrich Lichtenegger [Mon, 2 Feb 2026 11:48:21 +0000 (12:48 +0100)]
hass_players: Show now playing info from external playback (#3015)

* HASS Provider: Show now-playing information if playback is steered externally

* Fix: Prev/Next action availability not updated in hass player

2 months agoChore(deps): Bump pylast from 6.0.0 to 7.0.2 (#2994)
dependabot[bot] [Mon, 2 Feb 2026 10:43:39 +0000 (11:43 +0100)]
Chore(deps): Bump pylast from 6.0.0 to 7.0.2 (#2994)

* Chore(deps): Bump pylast from 6.0.0 to 7.0.2

Bumps [pylast](https://github.com/pylast/pylast) from 6.0.0 to 7.0.2.
- [Release notes](https://github.com/pylast/pylast/releases)
- [Changelog](https://github.com/pylast/pylast/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pylast/pylast/compare/6.0.0...7.0.2)

---
updated-dependencies:
- dependency-name: pylast
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
* Also bump the provider manifest.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
Co-authored-by: OzGav <gavnosp@hotmail.com>
2 months agoAdd auto discovery to HEOS (#3056)
Tom Matheussen [Mon, 2 Feb 2026 10:28:08 +0000 (11:28 +0100)]
Add auto discovery to HEOS (#3056)

* Adjust player registration and availability state

* Discover controller via mDNS

* Add some logging, fix possible None references

* Review adjustments

* Minor docstring update

* Move manual config to advanced section

2 months agoFix players/get_by_name not always returning a result (#2945)
OzGav [Mon, 2 Feb 2026 09:23:33 +0000 (20:23 +1100)]
Fix players/get_by_name not always returning a result (#2945)

* fix: Improve player name matching in get_by_name API

The players/get_by_name API was failing to find some players because it only
performed exact matching on the player's name property. This commit enhances
the matching logic to:

- Match against both player.name and player.display_name (which may differ
  if users have customized the player name in config)
- First attempt exact match (case-sensitive)
- Fall back to case-insensitive, whitespace-trimmed matching if exact match fails

This fixes the issue where players like "Alfresco", "Sonos Roaming", and
"pCP-Airplay Test" were returning NULL from the API despite being available
in the players/all endpoint.

* Improve duplicate player name handling in get_by_name

Enhances the get_player_by_name method to handle the edge case where
multiple players might have the same display_name:

- Collects all matching players instead of returning first match immediately
- Logs a warning when multiple players match the same name, including
  the player_ids of all matches
- Guides users to use player_id for unambiguous lookups
- Maintains backward compatibility by returning the first match

This improves debuggability when users have players with duplicate names
(e.g., multiple identical devices with default names or custom config
that creates name collisions).

* Clarify warning message for duplicate player names

Improves the warning message when multiple players match the same name:
- Now shows which player_id was actually returned (first match)
- Explicitly mentions "players/get API" instead of vague "use player_id"
- Makes it clear users should use a different API endpoint for
  unambiguous lookups, not try to pass player_id to get_by_name

This addresses confusion about how to resolve duplicate name lookups.

* Include API endpoint name in duplicate player warning

Prefixes the warning message with "players/get_by_name:" to make it
immediately clear which API call triggered the warning. This helps users
debug their automations and integrations when they see the warning in logs.

Warning now reads:
"players/get_by_name: Multiple players found with name 'X': [...] -
returning first match (Y). Consider using the players/get API..."

* Simplify to single-pass case-insensitive matching

Removes the two-pass approach (exact match first, then case-insensitive)
in favor of a simpler single-pass case-insensitive search.

Benefits:
- Single iteration through players (better performance)
- Simpler, more readable code
- More predictable behavior (always case-insensitive)
- Still handles all real-world use cases

The two-pass approach was protecting against the extremely unlikely
scenario of having players "Kitchen" and "kitchen" simultaneously,
which would generate a duplicate warning anyway.

* Remove redundant text from docstring return statement

* Fix line length to comply with E501 linting rule

* Use player.state.name for matching instead of multiple properties

Simplifies the matching logic to check only player.state.name instead of
checking both player.name and player.display_name. The state.name already
contains the final player name as visible in clients and the API, making
it the single source of truth for player name matching.

As suggested by head dev review.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2 months agoBandcamp Rate Limiting (#3069)
Alexey ALERT Rubashёff [Mon, 2 Feb 2026 09:11:40 +0000 (11:11 +0200)]
Bandcamp Rate Limiting (#3069)

feat(bandcamp): add rate limiting and retry logic to all API methods

- Upgrade bandcamp-async-api to 0.0.7
- Add BandcampRateLimitError import and handling throughout
- Configure ThrottlerManager with 50 req/10s rate limit and 10 retry attempts
- Add @throttle_with_retries decorator to: get_artist, get_album, get_track,
  get_album_tracks, get_artist_albums, get_artist_toptracks
- Add throttler.acquire() context manager to async generator methods:
  get_library_artists, get_library_albums
- Add proper BandcampRateLimitError handling converting to
  ResourceTemporarilyUnavailable with backoff_time
- Remove redundant exception handlers from get_library_tracks,
  get_stream_details, get_artist_toptracks (called methods handle exceptions)
- Update tests for new default_retry_after parameter

2 months agoRemove legacy logic for radiobrowser item copying (#3058)
OzGav [Mon, 2 Feb 2026 09:07:25 +0000 (20:07 +1100)]
Remove legacy logic for radiobrowser item copying (#3058)

* Remove legacy logic for radiobrowser item copying

Removed outdated logic for copying radiobrowser items added to the library, which is marked for removal after version 2.3.0.

* Fix indentation in RadioBrowser provider

2 months agoAllow Emoji in folder names (#3071)
OzGav [Mon, 2 Feb 2026 09:05:18 +0000 (20:05 +1100)]
Allow Emoji in folder names (#3071)

* Allow Emoji in folder names

* Shorten comment

2 months agoFix track import with multiple artists and mixed separators (#3065)
OzGav [Mon, 2 Feb 2026 08:37:27 +0000 (19:37 +1100)]
Fix track import with multiple artists and mixed separators (#3065)

* Fix metadata with triple artist track

* Increase robustness of artist name parsing logic

* Add TODO comment

2 months agochore: update bandcamp API library dependency (#3066)
Alexey ALERT Rubashёff [Mon, 2 Feb 2026 08:22:07 +0000 (10:22 +0200)]
chore: update bandcamp API library dependency (#3066)

chore: update bandcamp dependencies

- Update Bandcamp provider dependency to version 0.0.5.
- Update `bandcamp-async-api` to version 0.0.5.

2 months agofix(tidal): ensure no @ symbol in tidal recommendation rows (#3070)
Jozef Kruszynski [Mon, 2 Feb 2026 08:20:03 +0000 (09:20 +0100)]
fix(tidal): ensure no @ symbol in tidal recommendation rows (#3070)

* fix(tidal): ensure no @ symbol in tidal recommendation rows

Closes 4295

* test(tidal): add test for @ symbol removal

2 months agoFix type error in load_provider_config (#3073)
OzGav [Mon, 2 Feb 2026 08:09:57 +0000 (19:09 +1100)]
Fix type error in load_provider_config (#3073)

2 months agoAdd update_provider_mapping function (#3037)
Jozef Kruszynski [Sun, 1 Feb 2026 11:00:29 +0000 (12:00 +0100)]
Add update_provider_mapping function (#3037)

2 months agoChore(deps): Bump peter-evans/repository-dispatch from 3 to 4 (#3055)
dependabot[bot] [Sat, 31 Jan 2026 08:45:05 +0000 (09:45 +0100)]
Chore(deps): Bump peter-evans/repository-dispatch from 3 to 4 (#3055)

Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 3 to 4.
- [Release notes](https://github.com/peter-evans/repository-dispatch/releases)
- [Commits](https://github.com/peter-evans/repository-dispatch/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/repository-dispatch
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 months agoFix deprecation warnings for asyncio.iscoroutinefunction (#3054)
Marvin Schenkel [Sat, 31 Jan 2026 08:14:06 +0000 (09:14 +0100)]
Fix deprecation warnings for asyncio.iscoroutinefunction (#3054)

Fix deprecation warnings for asyncio.iscoroutinefunciton.

2 months ago⬆️ Update music-assistant-frontend to 2.17.77 (#3062)
music-assistant-machine [Sat, 31 Jan 2026 08:10:12 +0000 (09:10 +0100)]
⬆️ Update music-assistant-frontend to 2.17.77 (#3062)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoAdd album year from Deezer release_date (#3063)
Dionysis Fortis [Sat, 31 Jan 2026 08:09:12 +0000 (10:09 +0200)]
Add album year from Deezer release_date (#3063)

Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoFix mypy jwt
Marvin Schenkel [Sat, 31 Jan 2026 08:05:42 +0000 (09:05 +0100)]
Fix mypy jwt

2 months agoFix issue where Sonos would sometimes skip a smart fade (#3057)
Marvin Schenkel [Fri, 30 Jan 2026 23:13:52 +0000 (00:13 +0100)]
Fix issue where Sonos would sometimes skip a smart fade (#3057)

2 months agoFix AirPlay devices that need pairing not visible in the config (#3060)
Marcel van der Veldt [Fri, 30 Jan 2026 19:49:32 +0000 (20:49 +0100)]
Fix AirPlay devices that need pairing not visible in the config (#3060)

2 months agoRename audio category to playback
Marcel van der Veldt [Fri, 30 Jan 2026 14:33:58 +0000 (15:33 +0100)]
Rename audio category to playback

2 months agoUse advanced flag on config entries
Marcel van der Veldt [Fri, 30 Jan 2026 13:41:21 +0000 (14:41 +0100)]
Use advanced flag on config entries

2 months ago⬆️ Update music-assistant-models to 1.1.95 (#3059)
music-assistant-machine [Fri, 30 Jan 2026 13:21:56 +0000 (14:21 +0100)]
⬆️ Update music-assistant-models to 1.1.95 (#3059)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoAdd YouSee Musik provider (#3043)
Mathias R [Fri, 30 Jan 2026 07:20:52 +0000 (08:20 +0100)]
Add YouSee Musik provider (#3043)

* YouSee Musik provider

* Fix lint

* Restructuring the provider

* Change log level + fix auth invalidation

* Fix bitrate, simplify playbackContext

* Improve recommendations readability

* Lyrics support

* Apply suggestion from @MarvinSchenkel

Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
* cleanup

* Made quality configurable

* icons

---------

Co-authored-by: Mathias Rasmussen <mra@ordbogen.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoAdd claude instructions about comment usage.
Marvin Schenkel [Thu, 29 Jan 2026 13:39:28 +0000 (14:39 +0100)]
Add claude instructions about comment usage.

2 months agoFix announcement loops for Sonos S1 (#3053)
Marvin Schenkel [Thu, 29 Jan 2026 12:53:12 +0000 (13:53 +0100)]
Fix announcement loops for Sonos S1 (#3053)

Fix announcement loops for Sonos S1.

2 months agoRemove corrupt player configurations (#3051)
Marcel van der Veldt [Thu, 29 Jan 2026 11:57:04 +0000 (12:57 +0100)]
Remove corrupt player configurations (#3051)

Remove corrupt player configurations that are missing the required 'provider' key

2 months agoImprovements and bugfixes for player and provider config entry handling (#3049)
Marcel van der Veldt [Thu, 29 Jan 2026 11:15:04 +0000 (12:15 +0100)]
Improvements and bugfixes for player and provider config entry handling (#3049)

2 months ago⬆️ Update music-assistant-models to 1.1.94 (#3048)
music-assistant-machine [Thu, 29 Jan 2026 10:44:19 +0000 (11:44 +0100)]
⬆️ Update music-assistant-models to 1.1.94 (#3048)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months ago⬆️ Update music-assistant-models to 1.1.93 (#3047)
music-assistant-machine [Thu, 29 Jan 2026 08:52:46 +0000 (09:52 +0100)]
⬆️ Update music-assistant-models to 1.1.93 (#3047)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoAdd update frontend PWA to release action
Marcel van der Veldt [Thu, 29 Jan 2026 08:28:10 +0000 (09:28 +0100)]
Add update frontend PWA to release action

2 months agoRadio Paradise. Switch to simple API (#3046)
OzGav [Thu, 29 Jan 2026 07:16:41 +0000 (18:16 +1100)]
Radio Paradise. Switch to simple API (#3046)

Switch to simple API

2 months agoImplement Apple Music playlist browsing with folders (#3008)
MizterB [Thu, 29 Jan 2026 06:53:58 +0000 (01:53 -0500)]
Implement Apple Music playlist browsing with folders (#3008)

* Implement Apple Music playlist browsing with folders

* Update music_assistant/providers/apple_music/__init__.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Separate browse logic and handle catalog/library edge cases

* Resolve copilot feedback

* Revert & repair get_playlist

---------

Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2 months agoAdds date_added field to Tidal provider (#2969)
FL550 [Thu, 29 Jan 2026 06:48:33 +0000 (07:48 +0100)]
Adds date_added field to Tidal provider (#2969)

* feat: adds date_added to tidal provider

* applied copilot suggestions

* Update snapshot

---------

Co-authored-by: FL550 <glacial0124@posteo.com>
2 months agoDon't force reload on all config changes (#3045)
apophisnow [Wed, 28 Jan 2026 23:26:24 +0000 (15:26 -0800)]
Don't force reload on all config changes (#3045)

2 months agoChore(deps): Bump docker/login-action from 3.6.0 to 3.7.0 (#3044)
dependabot[bot] [Wed, 28 Jan 2026 23:25:56 +0000 (00:25 +0100)]
Chore(deps): Bump docker/login-action from 3.6.0 to 3.7.0 (#3044)

Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 months agoUse streams server bind ip as spotify connect zeroconf bind interface (#3042)
Marvin Schenkel [Wed, 28 Jan 2026 20:07:33 +0000 (21:07 +0100)]
Use streams server bind ip as spotify connect zeroconf bind interface (#3042)

2 months agoFix not being able to ungroup dynamic group members (#3040)
Marvin Schenkel [Wed, 28 Jan 2026 19:33:24 +0000 (20:33 +0100)]
Fix not being able to ungroup dynamic group members (#3040)

Fix not being able to ungroup dynamic group members.

2 months agoFix Sqeezelite playing next enqueued song after announcement. (#3039)
Marvin Schenkel [Wed, 28 Jan 2026 14:59:34 +0000 (15:59 +0100)]
Fix Sqeezelite playing next enqueued song after announcement. (#3039)

2 months agoAdd Yandex Music provider (#3002)
Mikhail Nevskiy [Wed, 28 Jan 2026 12:30:23 +0000 (15:30 +0300)]
Add Yandex Music provider (#3002)

* Add Yandex Music provider

Implement a new music provider for Yandex Music streaming service using the
unofficial yandex-music-api library. Features include:

- Library sync (artists, albums, tracks, playlists)
- Library editing (add/remove tracks, albums, artists)
- Search across all media types
- Artist albums and top tracks
- HTTP streaming with quality selection (320kbps MP3 / FLAC)
- Token-based authentication

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Yandex Music provider config entries

Remove duplicate token field that was causing the Save button to remain
disabled during provider setup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Yandex Music provider based on PR review feedback

- Move SUPPORTED_FEATURES to __init__.py and pass to setup()
- Remove code duplication in get_config_entries
- Remove label_instructions (use documentation instead)
- Replace RuntimeError with ProviderUnavailableError
- Replace generic Exception catches with InvalidDataError
- Remove non-existent AlbumType.PODCAST
- Fix audio_format to respect quality config setting (MP3/FLAC)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add yandex-music to requirements_all.txt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update manifest.json

* Improve Yandex Music provider based on PR review

- Use ContentType.UNKNOWN for unknown codecs instead of assuming MP3
- Add PLAYLIST_ID_SPLITTER constant and remove unused cache TTL constants
- Optimize search to use specific type when only one media type requested
- Remove unused imports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix mypy type errors in Yandex Music provider

- Add cast for users_playlists_list return type in api_client.py
- Add type annotations for _select_best_quality method in streaming.py
- Convert quality config value to str | None before passing to method
- Remove non-existent MediaItemMetadata attributes (track_count, has_lyrics)
- Use spread operator instead of append for Sequence types in search results

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Enable CI workflow for feature branches

Allow test workflow to run on feature/* branches to validate changes
before pushing to upstream PR.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix ruff TC006 error - use list() instead of cast()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove CI workflow changes unrelated to Yandex Music provider

Reverts changes to .github/workflows/test.yml as requested in PR review.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Yandex Music provider icons

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update music_assistant/providers/yandex_music/manifest.json

Co-authored-by: OzGav <gavnosp@hotmail.com>
---------

Co-authored-by: Михаил Невский <renso@MacBook-Pro-Mihail.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
Co-authored-by: OzGav <gavnosp@hotmail.com>
2 months agoFix IndexError when Deezer track has no media available (#3038)
Dionysis Fortis [Wed, 28 Jan 2026 08:11:39 +0000 (10:11 +0200)]
Fix IndexError when Deezer track has no media available (#3038)

Raise MediaNotFoundError instead of crashing with IndexError when the
Deezer API returns an empty media list. This allows the player queue
to gracefully skip unavailable tracks instead of stopping playback.

Fixes music-assistant/support#4860

2 months agoAdd JWT-based authentication with backward compatibility (#2891)
Ztripez [Wed, 28 Jan 2026 07:58:34 +0000 (08:58 +0100)]
Add JWT-based authentication with backward compatibility (#2891)

* Add JWT-based authentication with backward compatibility

Migrates from hash-based tokens to JWT (JSON Web Tokens) while maintaining
full backward compatibility with existing tokens. This enables stateless
authentication with embedded user claims for better integration with
external systems and OAuth2/OIDC compliance.

JWT Claims Structure:
- Standard claims: sub (user_id), jti (token_id), iat, exp
- Custom claims: username, role, player_filter, provider_filter,
  token_name, is_long_lived

Token Types:
- Short-lived (30 days, auto-renewing on use, sliding window)
- Long-lived (10 years, no auto-renewal for API integrations)

Implementation:
- JWTHelper class for encoding/decoding with HS256 algorithm
- JWT secret key generated and stored in auth.db settings table
- Token verification tries JWT first, falls back to legacy hash lookup
- Database still stores tokens for revocation checking

Benefits:
- Stateless authentication (user info embedded in token)
- Permission scopes available without database lookup
- OAuth2/OIDC compatibility for external integrations
- Standard JWT format for third-party verification

Migration Strategy:
- Automatic: Old tokens work until expiration
- New logins get JWT tokens automatically
- No breaking changes for existing clients

* Fix JWT token expiration check to honor database expiration

Database expiration is the source of truth for token validity, not just
the JWT expiration claim. This ensures manual token expiration (via
database update) works correctly even when JWT exp is still valid.

* Add documentation for future OIDC support

Notes on consuming external OIDC vs acting as OIDC provider, and
refresh token requirements for the latter.

* Clean up JWT implementation: remove dead code and verbose comments

- Remove unused methods: refresh_short_lived_token(), get_user_from_token()
- Remove unused imports: timedelta, UserRole
- Move User import to TYPE_CHECKING block
- Remove TODO comment about refresh tokens (not implementing)
- Simplify inline comments and reduce verbosity
- All tests still passing (36/36)

* Remove player_filter and provider_filter from JWT claims

These values can be dynamically updated, so storing them in the token
would result in stale data. The current values are available from the
database lookup during token validation.

---------

Co-authored-by: Ztripez von Matérn <ztripez@bobby.se>
2 months agoRaise AudioException if ffmpeg encounters a demuxing error (#3035)
Fabian Munkes [Tue, 27 Jan 2026 19:39:45 +0000 (20:39 +0100)]
Raise AudioException if ffmpeg encounters a demuxing error (#3035)

2 months ago⬆️ Update music-assistant-frontend to 2.17.76 (#3036)
music-assistant-machine [Tue, 27 Jan 2026 19:35:14 +0000 (20:35 +0100)]
⬆️ Update music-assistant-frontend to 2.17.76 (#3036)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
2 months agoFix Sonos looping announcements (#3032)
Marvin Schenkel [Tue, 27 Jan 2026 14:42:39 +0000 (15:42 +0100)]
Fix Sonos looping announcements (#3032)

Fix Sonos looping announcements.

2 months ago⬆️ Update music-assistant-frontend to 2.17.75 (#3033)
music-assistant-machine [Tue, 27 Jan 2026 14:14:17 +0000 (15:14 +0100)]
⬆️ Update music-assistant-frontend to 2.17.75 (#3033)

Co-authored-by: MarvinSchenkel <17671719+MarvinSchenkel@users.noreply.github.com>
2 months agoDon't filter unavailable player configs by default (#3031)
Marcel van der Veldt [Tue, 27 Jan 2026 11:50:29 +0000 (12:50 +0100)]
Don't filter unavailable player configs by default (#3031)

* Don't filter unavailable player configs by default

* Add additional param to filter out disabled players

2 months agoFix player sources in Sonos S1 (#3030)
Marvin Schenkel [Tue, 27 Jan 2026 10:24:03 +0000 (11:24 +0100)]
Fix player sources in Sonos S1 (#3030)

* Set active source to Line in source when selected.

* Add PlayerSources for S1.

* Add PlayerSources for S1.

* Disable media when linein is active. Switch back to MA on stop.

2 months agoChore(deps): Bump alexapy from 1.29.14 to 1.29.15 (#3022)
dependabot[bot] [Tue, 27 Jan 2026 08:17:29 +0000 (08:17 +0000)]
Chore(deps): Bump alexapy from 1.29.14 to 1.29.15 (#3022)

* Chore(deps): Bump alexapy from 1.29.14 to 1.29.15

Bumps [alexapy](https://gitlab.com/keatontaylor/alexapy) from 1.29.14 to 1.29.15.
- [Release notes](https://gitlab.com/keatontaylor/alexapy/tags)
- [Changelog](https://gitlab.com/keatontaylor/alexapy/blob/dev/CHANGELOG.md)
- [Commits](https://gitlab.com/keatontaylor/alexapy/compare/v1.29.14...v1.29.15)

---
updated-dependencies:
- dependency-name: alexapy
  dependency-version: 1.29.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
* Also bump the provider manifest.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoChore(deps): Bump py-opensonic from 8.0.1 to 8.1.1 (#3023)
dependabot[bot] [Tue, 27 Jan 2026 08:16:46 +0000 (08:16 +0000)]
Chore(deps): Bump py-opensonic from 8.0.1 to 8.1.1 (#3023)

* Chore(deps): Bump py-opensonic from 8.0.1 to 8.1.1

Bumps [py-opensonic](https://github.com/khers/py-opensonic) from 8.0.1 to 8.1.1.
- [Release notes](https://github.com/khers/py-opensonic/releases)
- [Changelog](https://github.com/khers/py-opensonic/blob/master/CHANGELOG.md)
- [Commits](https://github.com/khers/py-opensonic/compare/v8.0.1...v8.1.1)

---
updated-dependencies:
- dependency-name: py-opensonic
  dependency-version: 8.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
* Also bump the provider manifest.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marvin Schenkel <marvinschenkel@gmail.com>
2 months agoYTMusic: Implement more aggressive caching (#3029)
Marvin Schenkel [Tue, 27 Jan 2026 08:04:06 +0000 (09:04 +0100)]
YTMusic: Implement more aggressive caching (#3029)

* Implement more aggresive caching for YTM.

* Cleanup.

2 months ago⬆️ Update music-assistant-frontend to 2.17.74 (#3028)
music-assistant-machine [Tue, 27 Jan 2026 00:31:02 +0000 (01:31 +0100)]
⬆️ Update music-assistant-frontend to 2.17.74 (#3028)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>