Shiny.Music
1.3.1
Prefix Reserved
See the version list below for details.
dotnet add package Shiny.Music --version 1.3.1
NuGet\Install-Package Shiny.Music -Version 1.3.1
<PackageReference Include="Shiny.Music" Version="1.3.1" />
<PackageVersion Include="Shiny.Music" Version="1.3.1" />
<PackageReference Include="Shiny.Music" />
paket add Shiny.Music --version 1.3.1
#r "nuget: Shiny.Music, 1.3.1"
#:package Shiny.Music@1.3.1
#addin nuget:?package=Shiny.Music&version=1.3.1
#tool nuget:?package=Shiny.Music&version=1.3.1
Shiny.Music
A .NET library for accessing the device music library on Android and iOS. Provides a unified API for:
- 🔐 Requesting permissions to access music
- 🎵 Querying metadata about music on the device
- 🔎 Filtering tracks by genre, year, decade, and search text
- 📊 Browsing genres, years, and decades with track counts
- 📋 Browsing playlists and their tracks
- ▶️ Playing music files from the device library
- 🎧 Streaming Apple Music subscription tracks via
MPMusicPlayerController(iOS) - 📁 Copying music files (where permitted)
- 🔍 Checking for active streaming subscriptions
Installation
Add a project reference to Shiny.Music from your .NET MAUI or platform-specific app.
Quick Start
// Register in MauiProgram.cs
builder.Services.AddShinyMusic();
// Use via dependency injection
public class MyPage
{
readonly IMediaLibrary _library;
readonly IMusicPlayer _player;
public MyPage(IMediaLibrary library, IMusicPlayer player)
{
_library = library;
_player = player;
}
async Task Example()
{
// 1. Request permission
var status = await _library.RequestPermissionAsync();
if (status != PermissionStatus.Granted) return;
// 2. Get all tracks
var tracks = await _library.GetAllTracksAsync();
// 3. Play a track
await _player.PlayAsync(tracks[0]);
// 4. Browse genres with counts
var genres = await _library.GetGenresAsync();
// 5. Browse decades with counts
var decades = await _library.GetDecadesAsync();
// 6. Filter: Rock tracks from the 1990s
var filtered = await _library.GetTracksAsync(new MusicFilter
{
Genre = "Rock",
Decade = 1990
});
// 7. Cross-query: genres within the 2000s
var genresIn2000s = await _library.GetGenresAsync(new MusicFilter { Decade = 2000 });
// 8. Browse playlists
var playlists = await _library.GetPlaylistsAsync();
// 9. Get tracks in a playlist
var playlistTracks = await _library.GetPlaylistTracksAsync(playlists[0].Id);
// 10. Copy a track
var dest = Path.Combine(FileSystem.AppDataDirectory, "copy.m4a");
var success = await _library.CopyTrackAsync(tracks[0], dest);
}
}
Platform Configuration
Android
Required Permissions
Add these to your AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
Notes
- Minimum API Level: 24 (Android 7.0)
- Target API 33+: Uses
READ_MEDIA_AUDIOgranular media permission - Target API < 33: Falls back to
READ_EXTERNAL_STORAGE - The library requests runtime permissions via the MAUI Permissions API
- Music is queried through
MediaStore.Audio.Media; playlists throughMediaStore.Audio.Playlists - Playback uses
Android.Media.MediaPlayerwith content URIs HasStreamingSubscriptionAsync()always returnsfalse- Copy: Reads from the
ContentResolverinput stream. Works for all locally stored music files.
iOS
Required Info.plist Entry
<key>NSAppleMusicUsageDescription</key>
<string>This app needs access to your music library to browse and play your music.</string>
⚠️ This is mandatory. Your app will crash on launch if you attempt to access the music library without this key.
Notes
- Minimum iOS Version: 15.0
- Permission is requested via
MPMediaLibrary.RequestAuthorization - Music metadata is queried using
MPMediaQueryfrom theMediaPlayerframework; playlists viaMPMediaQuery.PlaylistsQuery - Local playback uses
AVAudioPlayerfromAVFoundationwith the item'sAssetURL - Streaming playback uses
MPMusicPlayerController.SystemMusicPlayerfor Apple Music subscription tracks with aStoreId HasStreamingSubscriptionAsync()checksSKCloudServiceControllerfor theMusicCatalogPlaybackcapability- Copy Limitations:
- ✅ Locally synced / purchased (non-DRM) tracks can be exported via
AVAssetExportSession - ❌ Apple Music subscription (DRM-protected) tracks cannot be copied. The
AssetURLis empty for these items, and iOS does not provide filesystem access to DRM content. - The
CopyTrackAsyncmethod returnsfalsefor tracks that cannot be exported. - Exported format is Apple M4A (
.m4a)
- ✅ Locally synced / purchased (non-DRM) tracks can be exported via
Entitlements
No special entitlements are required beyond the Info.plist usage description. The MediaPlayer and AVFoundation frameworks are standard iOS frameworks.
API Reference
IMediaLibrary
| Method | Description |
|---|---|
RequestPermissionAsync() |
Prompts the user for music library access |
CheckPermissionAsync() |
Checks current permission status without prompting |
GetAllTracksAsync() |
Returns all music tracks on the device |
SearchTracksAsync(query) |
Searches tracks by title, artist, or album |
GetTracksAsync(filter) |
Returns tracks matching a MusicFilter (genre, year, decade, search — combined with AND logic) |
GetGenresAsync(filter?) |
Returns distinct genres with track counts; optionally filtered by year/decade/search |
GetYearsAsync(filter?) |
Returns distinct release years with track counts; optionally filtered by genre/decade/search |
GetDecadesAsync(filter?) |
Returns distinct decades with track counts; optionally filtered by genre/year/search |
GetPlaylistsAsync() |
Returns all playlists with song counts, sorted alphabetically |
GetPlaylistTracksAsync(playlistId) |
Returns all tracks in the specified playlist, in playlist order |
CopyTrackAsync(track, destPath) |
Copies a track to the specified path; returns false if not possible |
HasStreamingSubscriptionAsync() |
Checks for an active streaming subscription (iOS: Apple Music; Android: always false) |
MusicFilter
All properties are optional and combined with AND logic. Pass to GetTracksAsync, GetGenresAsync, GetYearsAsync, or GetDecadesAsync.
| Property | Type | Description |
|---|---|---|
Genre |
string? |
Filter by genre name (case-insensitive) |
Year |
int? |
Filter by exact release year (takes precedence over Decade) |
Decade |
int? |
Filter by decade start year (e.g., 1990 for the 1990s) |
SearchQuery |
string? |
Text search across title, artist, and album |
GroupedCount<T>
Returned by GetGenresAsync, GetYearsAsync, and GetDecadesAsync.
| Property | Type | Description |
|---|---|---|
Value |
T |
The grouped value (string for genres, int for years/decades) |
Count |
int |
Number of tracks in this group |
PlaylistInfo
| Property | Type | Description |
|---|---|---|
Id |
string |
Platform-specific unique identifier for the playlist |
Name |
string |
The display name of the playlist |
SongCount |
int |
The number of tracks in the playlist |
IMusicPlayer
| Member | Description |
|---|---|
PlayAsync(track) |
Loads and plays the specified track (uses AVAudioPlayer or MPMusicPlayerController on iOS based on available URIs) |
Pause() |
Pauses current playback |
Resume() |
Resumes after pausing |
Stop() |
Stops playback and releases the track |
Seek(position) |
Seeks to a position in the track |
State |
Current PlaybackState (Stopped/Playing/Paused) |
CurrentTrack |
The currently loaded MusicMetadata |
Position / Duration |
Current position and total duration |
StateChanged |
Event fired when playback state changes |
PlaybackCompleted |
Event fired when a track finishes |
MusicMetadata
| Property | Type | Description |
|---|---|---|
Id |
string |
Platform-specific unique identifier |
Title |
string? |
Track title |
Artist |
string? |
Artist name |
Album |
string? |
Album name |
Genre |
string? |
Genre (may be null) |
Duration |
TimeSpan |
Track duration |
AlbumArtUri |
string? |
Album art URI (Android only; null on iOS) |
IsExplicit |
bool? |
Whether the track is marked as explicit content. iOS only via MPMediaItem.IsExplicitItem; always null on Android. |
ContentUri |
string |
URI used for playback and file operations |
StoreId |
string? |
Apple Music catalog ID for streaming playback via MPMusicPlayerController (iOS only; null on Android) |
Year |
int? |
Release year of the track, or null if not available. Android: MediaStore.Audio.Media.YEAR; iOS: derived from MPMediaItem.ReleaseDate. |
Sample App
The sample/MusicSample project is a .NET MAUI app that demonstrates all library features:
- Permission Request — Tap "Request Permission" to prompt for music access
- Browse — Tap "Load All" to list all music on the device
- Search — Type a query and tap "Search" to filter by title/artist/album
- Play/Pause/Stop — Select a track and use the playback controls
- Copy — Select a track and tap "Copy" to export it to app storage
- Genres — Switch to the "Genres" tab to view all distinct genres with track counts
- Decades — Switch to the "Decades" tab to view decades with track counts
- Years — Switch to the "Years" tab to view release years with track counts
- Playlists — Switch to the "Playlists" tab to view playlists and their tracks
Running the Sample
# Android
dotnet build sample/MusicSample -f net9.0-android -t:Run
# iOS (requires Mac with Xcode)
dotnet build sample/MusicSample -f net9.0-ios -t:Run
Note: Music library access requires a physical device. Simulators/emulators typically have no music content.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-android36.0 is compatible. net10.0-browser was computed. net10.0-ios was computed. net10.0-ios26.0 is compatible. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Maui.Essentials (>= 10.0.41)
-
net10.0-android36.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Maui.Essentials (>= 10.0.41)
-
net10.0-ios26.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Maui.Essentials (>= 10.0.41)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Shiny.Music:
| Package | Downloads |
|---|---|
|
Shiny.Music.Sqlite
Package Description |
|
|
Shiny.Music.Extensions.AI
Microsoft.Extensions.AI tool surface for Shiny.Music — exposes the device music library and player as AIFunction tools for LLM tool-calling. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0-beta-0004 | 0 | 7/9/2026 |
| 4.0.0-beta-0003 | 0 | 7/8/2026 |
| 4.0.0-beta-0002 | 0 | 7/8/2026 |
| 4.0.0-beta-0001 | 0 | 7/8/2026 |
| 4.0.0-beta | 0 | 7/8/2026 |
| 3.2.0 | 41 | 7/7/2026 |
| 3.1.0 | 44 | 7/7/2026 |
| 3.1.0-beta-0005 | 60 | 7/7/2026 |
| 3.1.0-beta-0004 | 76 | 7/7/2026 |
| 3.1.0-beta-0003 | 76 | 7/6/2026 |
| 3.1.0-beta-0002 | 76 | 7/5/2026 |
| 3.1.0-beta-0001 | 79 | 7/5/2026 |
| 3.0.1 | 123 | 5/17/2026 |
| 3.0.1-beta-0004 | 102 | 5/17/2026 |
| 3.0.1-beta-0003 | 105 | 5/17/2026 |
| 3.0.1-beta-0002 | 96 | 5/16/2026 |
| 3.0.1-beta-0001 | 96 | 5/16/2026 |
| 3.0.0 | 115 | 5/3/2026 |
| 3.0.0-beta-0007 | 96 | 5/3/2026 |
| 1.3.1 | 113 | 4/1/2026 |