Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!
Music Releases
4.0 - TBD
Section titled “4.0 - TBD” Feature
Volume (get/set, 0.0–1.0) and VolumeChanged on IMusicPlayer control and observe the device-wide system media volume, independent of any active duck. Reading works on all platforms; observe changes from the hardware buttons, Control Center, or a successful set via VolumeChanged Feature
IsVolumeControlSupported on IMusicPlayer — reports whether the Volume setter is honored: true on Android, false on Apple. Guard writes with it, since setting volume on Apple throws NotSupportedException Feature Android
Android reads and writes the system
STREAM_MUSIC volume via AudioManager (IsVolumeControlSupported is true); VolumeChanged observes the system volume through a ContentObserver. The stream is integer-stepped, so a set value is quantized to the nearest step Feature iOS
Apple reads
AVAudioSession.OutputVolume reliably, but setting the volume is not supported — the setter throws NotSupportedException (IsVolumeControlSupported is false), as iOS/Mac Catalyst expose no supported API to change system volume. VolumeChanged fires from KVO on the session’s output volume; direct the user to the hardware buttons or an MPVolumeView to change volume Fix
Ducking now enforces a single active duck — calling
Duck() while a duck is already active returns a no-op scope instead of superseding the existing one (previously last-writer-wins). This prevents a superseded duck’s restore-fade from racing the new duck on the shared volume, which could leave the music stuck at a lowered level; the active duck restores normally when its scope is disposed BREAKING Feature Android
Android permission checks now run on Shiny.Core’s
AndroidPlatform (GetCurrentPermissionStatus / RequestAccess), replacing the built-in activity provider. Your app must be under Shiny hosting — call .UseShiny() in MAUI (or use ShinyAndroidApplication + ShinyAndroidActivity natively) — otherwise IMediaLibrary cannot resolve AndroidPlatform. This makes Shiny.Core a dependency on the Android target and fixes the config-change-during-dialog hang from the previous fragment-based implementation Feature iOS
SearchCatalogAsync(term, limit) on IMediaLibrary — searches the Apple Music streaming catalog (via MusicKit MusicCatalogSearchRequest), returning tracks that need not be in the user’s library; the first call prompts for MusicKit authorization and results come back empty without a subscription/authorization rather than throwing Feature iOS
CatalogId property on MusicMetadata — set on catalog search results; passing such a track to IMusicPlayer.PlayAsync streams it by catalog id via MPMusicPlayerStoreQueueDescriptor, so catalog tracks are playable without adding them to the library (active Apple Music subscription required). SearchCatalogAsync is Apple-only and throws PlatformNotSupportedException on Android Feature iOS
Shiny.Music.Extensions.AI — new opt-in AddCatalog() area exposing a search_catalog tool so an LLM agent can search the Apple Music streaming catalog and play results via play_track; Apple-only and deliberately excluded from AddAll(), so guard the registration with #if IOS || MACCATALYST3.2 - July 7, 2026
Section titled “3.2 - July 7, 2026” Feature
Shiny.Music.Extensions.AI — a new companion package that exposes the music library and player as Microsoft.Extensions.AI tool functions, so an LLM chat agent can search, browse, play, and manage playlists through natural language; register with AddMusicAITools(...) and pass the resolved MusicAITools.Tools to any IChatClient Feature
Opt-in tool areas via the
AddMusicAITools builder — AddLibrary() (search/browse/genres/playlists/lyrics), AddPlayback() (play/pause/resume/stop/seek/now-playing), AddPlaylistManagement() (create/modify/delete custom playlists), or AddAll(); anything not added stays invisible to the model Feature
browse_tracks tool filters the library by genre, year, decade, and free text — the natural path for “pick a song for my mood” or era-based requests Enhancement
AI tool package is AOT-compatible (hand-authored JSON schemas, no reflection) and depends only on
Microsoft.Extensions.AI.Abstractions3.1 - July 7, 2026
Section titled “3.1 - July 7, 2026” Feature
Audio ducking —
Duck(DuckOptions?) on IMusicPlayer lowers the currently playing music so an announcement can be heard over top, returning an IAsyncDisposable scope that restores full volume when disposed; ideal for TTS prompts and navigation callouts Feature
DuckOptions record — control the ducked Level (0.0-1.0) plus FadeIn/FadeOut ramp durations; defaults to 20% with 200ms fades Feature
IsDucked property on IMusicPlayer — reports whether a duck scope is currently active Enhancement
Last-writer-wins duck semantics — a newer duck supersedes an older one; disposing the active scope restores volume while disposing a superseded scope is a no-op, and ducking with nothing playing returns a no-op scope
Feature Android
Android ducking lowers the player’s own track, honoring
DuckOptions.Level and the fade durations exactly Feature iOS
Apple ducking activates
AVAudioSession with DuckOthers (level and fades are advisory — the OS controls duck depth and ramp); announcement audio played through the app audio session plays at full volume over the ducked music Feature
GetTrackByIdAsync(trackId) on IMediaLibrary — resolve a single track by its identifier, returns null if not found Feature
GetTracksByIdsAsync(trackIds) on IMediaLibrary — resolve multiple tracks in a single query, ordered to match the input; ideal for rehydrating a saved queue or favorites list Feature
GetPlaylistByIdAsync(playlistId) on IMediaLibrary — resolve a single playlist (with song count) by identifier, including custom playlists; returns null if not found3.0 - May 3, 2026
Section titled “3.0 - May 3, 2026” Feature
Mac Catalyst support — all features now work on macOS via Mac Catalyst in addition to iOS and Android
Feature
Removed
Microsoft.Maui.Essentials dependency — the library no longer requires .NET MAUI, enabling use in plain .NET for iOS/Android projects Feature
Custom playlist CRUD on
IMediaLibrary — CreatePlaylistAsync, RemovePlaylistAsync, AddTrackToPlaylistAsync, RemoveTrackFromPlaylistAsync for app-managed playlists stored locally as JSON Feature
PlayCount property on MusicMetadata — number of times a track has been played Feature iOS
Play counts from
MPMediaItem.PlayCount on Apple platforms Feature Android
Play counts tracked locally and auto-incremented on
PlayAsync on Android Feature Android
Android runtime permission handling via
ActivityProvider — no longer requires MAUI Permissions API Feature iOS
Streaming subscription check now uses MusicKit
MusicSubscription instead of SKCloudServiceControllerRemoved
Volume property from IMusicPlayerRemoved
IMusicIdentifier and ShazamKit song identification2.0 - April 22, 2026
Section titled “2.0 - April 22, 2026” Feature
ILyricsProvider interface — fetch lyrics for any music track via dependency injection Feature
LyricsResult record — contains PlainLyrics (plain text) and SyncedLyrics (synchronized LRC format with timestamps) Feature
LrcLibLyricsProvider — default cross-platform lyrics provider using the free LRCLIB service, with AOT-compatible source-generated JSON serialization Feature
GetAlbumArtPathAsync(trackId) on IMediaLibrary — retrieve album artwork as a file path for use as an image source Feature
Volume property on IMusicPlayer — get/set playback volume from 0.0 (silent) to 1.0 (full), with automatic clamping Feature Android
Android lyrics via LRCLIB — matches tracks by artist, title, and duration
Feature Android
Android album art via MediaStore content URI
Feature Android
Android volume control via
MediaPlayer.SetVolume Feature iOS
iOS lyrics via LRCLIB (default) and
MPMediaItem.Lyrics (native, via IMediaLibrary cast) Feature iOS
iOS album art via
MPMediaItem.Artwork — exported as cached JPEG (600x600, 85% quality) Feature iOS
iOS volume control via
AVPlayer.Volume1.3 - March 13, 2026
Section titled “1.3 - March 13, 2026” Feature
GetPlaylistsAsync() on IMediaLibrary — returns all playlists from the device music library with song counts, sorted alphabetically Feature
GetPlaylistTracksAsync(playlistId) on IMediaLibrary — returns all tracks in a playlist, in playlist order Feature
PlaylistInfo record — contains playlist Id, Name, and SongCount Feature Android
Android playlist querying via
MediaStore.Audio.Playlists and MediaStore.Audio.Playlists.Members Feature iOS
iOS playlist querying via
MPMediaQuery.PlaylistsQuery and MPMediaPlaylist1.2 - March 13, 2026
Section titled “1.2 - March 13, 2026” Feature
MusicFilter class — combine Genre, Year, Decade, and SearchQuery criteria with AND logic to filter tracks Feature
GetTracksAsync(MusicFilter) on IMediaLibrary — query tracks matching any combination of filter criteria Feature
GetYearsAsync() on IMediaLibrary — returns distinct release years with track counts, with optional MusicFilter parameter Feature
GetDecadesAsync() on IMediaLibrary — returns distinct decades (e.g., 1990s) with track counts, with optional MusicFilter parameter Feature
GroupedCount<T> record — generic type pairing a grouped value with its track count, used by all grouping methods Feature
Year property on MusicMetadata — the release year of the track (nullable) Feature
GetGenresAsync now accepts an optional MusicFilter and returns GroupedCount<string> with track counts Feature
Cross-dimensional browsing — get genres by decade, decades by genre, years by genre, etc.
Feature Android
Year metadata read from
MediaStore.Audio.Media.YEAR Feature iOS
Year metadata extracted from
MPMediaItem.ReleaseDate1.1 - March 12, 2026
Section titled “1.1 - March 12, 2026” Feature
GetGenresAsync() on IMediaLibrary — returns all distinct genre names from the user’s music library, sorted alphabetically Feature Android
Android genre querying via
MediaStore.Audio.Genres Feature iOS
iOS genre querying via
MPMediaQuery.GenresQuery1.0 - March 6, 2026
Section titled “1.0 - March 6, 2026” Feature
Permission management — request and check music library access using platform-native APIs
Feature
Music metadata querying — retrieve all tracks or search by title, artist, or album
Feature
Audio playback — play, pause, resume, and stop music files with state change and completion events
Feature
File copying — copy music files to app-local storage (where DRM permits)
Feature
Dependency injection —
AddShinyMusic() extension method for IServiceCollection registration Feature
HasStreamingSubscriptionAsync() — check for active Apple Music subscription capability on iOS Feature
StoreId property on MusicMetadata — Apple Music catalog ID enabling streaming playback of subscription content Feature Android
Android implementation using
MediaStore.Audio.Media and Android.Media.MediaPlayer Feature iOS
iOS implementation using
MPMediaQuery and AVAudioPlayer Feature iOS
Streaming playback of Apple Music subscription tracks via
MPMusicPlayerController.SystemMusicPlayer using StoreId Feature iOS
DRM-aware —
ContentUri is empty for Apple Music subscription tracks that cannot be copied, but streaming playback is available via StoreId Feature iOS
IsExplicit property on MusicMetadata — reports whether a track is marked as explicit content via MPMediaItem.IsExplicitItem