Skip to content

Commit a594283

Browse files
committed
Updates
1 parent 037397a commit a594283

4 files changed

Lines changed: 35 additions & 20 deletions

File tree

src/content/docs/client/music/index.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Shiny.Music provides a unified API for accessing the device music library on **A
1212
* Request and check music library permissions on both platforms
1313
* Query all music tracks or search by title, artist, or album
1414
* Play, pause, resume, stop, and seek within tracks
15+
* Stream Apple Music subscription tracks via `MPMusicPlayerController` on iOS
16+
* Check for active streaming subscriptions
1517
* Copy music files to app storage (non-DRM content only on iOS)
1618
* Event-driven playback state changes and completion notifications
1719

@@ -22,8 +24,10 @@ Shiny.Music provides a unified API for accessing the device music library on **A
2224
| Permission Request |||
2325
| Query Tracks |||
2426
| Search Tracks |||
25-
| Playback |||
27+
| Local Playback || ✅ (AVPlayer) |
28+
| Streaming Playback || ✅ (MPMusicPlayerController via StoreId) |
2629
| Copy Track || ✅ (non-DRM only) |
30+
| Streaming Subscription Check | ❌ (always `false`) | ✅ (SKCloudServiceController) |
2731
| Album Art URI || ❌ (use MPMediaItem.Artwork) |
2832
| Explicit Flag || ✅ (via MPMediaItem.IsExplicitItem) |
2933

src/content/docs/client/music/playback.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ title: Playback
44

55
The `IMusicPlayer` interface provides full playback control for music tracks from the device library.
66

7+
On iOS, the player supports two modes that are automatically selected based on the track's properties:
8+
- **Local playback** via `AVPlayer` when `ContentUri` is available (purchased/synced tracks)
9+
- **Streaming playback** via `MPMusicPlayerController.SystemMusicPlayer` when `StoreId` is available (Apple Music subscription tracks)
10+
711
## Playing a Track
812

913
```csharp
1014
var tracks = await _library.GetAllTracksAsync();
1115
await _player.PlayAsync(tracks[0]);
1216
```
1317

14-
Calling `PlayAsync` stops any currently playing track, loads the new one, and begins playback immediately.
18+
Calling `PlayAsync` stops any currently playing track, loads the new one, and begins playback immediately. The player automatically selects the appropriate playback engine based on whether `StoreId` or `ContentUri` is available.
1519

1620
:::caution
17-
`PlayAsync` will throw an `InvalidOperationException` if the track's `ContentUri` is empty (DRM-protected on iOS) or if the platform player fails to initialize. Always check `ContentUri` before playing.
21+
`PlayAsync` will throw an `InvalidOperationException` if both `ContentUri` and `StoreId` are empty, or if the platform player fails to initialize.
1822
:::
1923

2024
## Pause, Resume, and Stop
@@ -98,9 +102,8 @@ If you register the player as a singleton in DI, it will be disposed when the ap
98102
- Seeking uses millisecond precision.
99103

100104
### iOS
101-
- Playback uses `AVFoundation.AVAudioPlayer` with the track's `ipod-library://` asset URL.
102-
- The `AVAudioSession` category is set to `Playback` to support background audio (if configured).
103-
- Seeking uses second precision.
105+
- **Local tracks** (with `ContentUri`): Playback uses `AVPlayer` with the track's `ipod-library://` asset URL. The `AVAudioSession` category is set to `Playback` to support background audio (if configured). Seeking uses second precision.
106+
- **Streaming tracks** (with `StoreId`): Playback uses `MPMusicPlayerController.SystemMusicPlayer` with the Apple Music catalog ID. This enables playback of DRM-protected Apple Music subscription content. The system player manages its own audio session.
104107

105108
:::note
106109
Music library access requires a **physical device**. Simulators and emulators typically have no music content and cannot test playback.

src/content/docs/client/music/querying.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,40 +39,45 @@ Each track is represented by a `MusicMetadata` record with the following propert
3939
| Property | Type | Description |
4040
|---|---|---|
4141
| `Id` | `string` | Platform-specific unique identifier. On Android, this is the MediaStore row ID. On iOS, it is the `MPMediaItem` persistent ID. |
42-
| `Title` | `string` | The track title. |
43-
| `Artist` | `string` | The artist or performer. |
44-
| `Album` | `string` | The album name. |
42+
| `Title` | `string?` | The track title, or `null` if not available. |
43+
| `Artist` | `string?` | The artist or performer, or `null` if not available. |
44+
| `Album` | `string?` | The album name, or `null` if not available. |
4545
| `Genre` | `string?` | The genre, or `null` if unavailable. |
4646
| `Duration` | `TimeSpan` | The playback duration. |
4747
| `AlbumArtUri` | `string?` | URI to album artwork. Available on Android via MediaStore; `null` on iOS where artwork is accessed through `MPMediaItem.Artwork`. |
4848
| `IsExplicit` | `bool?` | Whether the track is marked as explicit content. iOS only via `MPMediaItem.IsExplicitItem`; always `null` on Android. |
4949
| `ContentUri` | `string` | URI used for playback and file operations. On Android, this is a `content://` URI. On iOS, this is an `ipod-library://` asset URL. **Empty for DRM-protected Apple Music subscription tracks.** |
50+
| `StoreId` | `string?` | Apple Music catalog ID (from `PlayParams.Id`). Enables streaming playback via `MPMusicPlayerController` on iOS. Always `null` on Android. |
5051

51-
## ContentUri and DRM
52+
## ContentUri, StoreId, and DRM
5253

53-
The `ContentUri` property is critical for understanding what operations are available for a track:
54+
The `ContentUri` and `StoreId` properties determine what operations are available for a track:
5455

5556
```csharp
5657
var tracks = await _library.GetAllTracksAsync();
5758

5859
foreach (var track in tracks)
5960
{
60-
if (string.IsNullOrEmpty(track.ContentUri))
61+
if (!string.IsNullOrEmpty(track.ContentUri))
6162
{
62-
// DRM-protected Apple Music track
63-
// Cannot be played via AVAudioPlayer or copied
64-
Console.WriteLine($"⚠️ {track.Title} - DRM protected, playback/copy unavailable");
63+
// Locally synced or purchased track — full access
64+
Console.WriteLine($"✅ {track.Title} - available for local playback and copy");
65+
}
66+
else if (!string.IsNullOrEmpty(track.StoreId))
67+
{
68+
// Apple Music subscription track with catalog ID — streaming playback only
69+
Console.WriteLine($"🎧 {track.Title} - available for streaming playback (no copy)");
6570
}
6671
else
6772
{
68-
// Locally synced or purchased track — full access
69-
Console.WriteLine($" {track.Title} - available for playback and copy");
73+
// No playback or copy available
74+
Console.WriteLine($"⚠️ {track.Title} - not playable or copyable");
7075
}
7176
}
7277
```
7378

7479
:::note
75-
On Android, `ContentUri` is always populated for all music files. The DRM limitation only applies to iOS Apple Music subscription tracks.
80+
On Android, `ContentUri` is always populated for all music files. `StoreId` is always `null` on Android. The DRM and streaming distinction only applies to iOS Apple Music subscription tracks.
7681
:::
7782

7883
## Platform Details

src/content/docs/client/music/release-notes.mdx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import RN from '/src/components/ReleaseNote.astro';
1313
<RN type="feature">Audio playback — play, pause, resume, and stop music files with state change and completion events</RN>
1414
<RN type="feature">File copying — copy music files to app-local storage (where DRM permits)</RN>
1515
<RN type="feature">Dependency injection — `AddShinyMusic()` extension method for `IServiceCollection` registration</RN>
16+
<RN type="feature">`HasStreamingSubscriptionAsync()` — check for active Apple Music subscription capability on iOS</RN>
17+
<RN type="feature">`StoreId` property on `MusicMetadata` — Apple Music catalog ID enabling streaming playback of subscription content</RN>
1618
<RN type="feature" platform="Android">Android implementation using `MediaStore.Audio.Media` and `Android.Media.MediaPlayer`</RN>
17-
<RN type="feature" platform="iOS">iOS implementation using `MPMediaQuery` and `AVFoundation.AVAudioPlayer`</RN>
18-
<RN type="feature" platform="iOS">DRM-aware — `ContentUri` is empty for Apple Music subscription tracks that cannot be played or copied</RN>
19+
<RN type="feature" platform="iOS">iOS implementation using `MPMediaQuery` and `AVPlayer`</RN>
20+
<RN type="feature" platform="iOS">Streaming playback of Apple Music subscription tracks via `MPMusicPlayerController.SystemMusicPlayer` using `StoreId`</RN>
21+
<RN type="feature" platform="iOS">DRM-aware — `ContentUri` is empty for Apple Music subscription tracks that cannot be copied, but streaming playback is available via `StoreId`</RN>
1922
<RN type="feature" platform="iOS">`IsExplicit` property on `MusicMetadata` — reports whether a track is marked as explicit content via `MPMediaItem.IsExplicitItem`</RN>

0 commit comments

Comments
 (0)