Skip to content
Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!

Music Releases

Feature
Volume (get/set, 0.01.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 || MACCATALYST
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.Abstractions
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 found
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 IMediaLibraryCreatePlaylistAsync, 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 SKCloudServiceController
Removed Volume property from IMusicPlayer
Removed IMusicIdentifier and ShazamKit song identification
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.Volume
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 MPMediaPlaylist
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.ReleaseDate
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.GenresQuery
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