-
Notifications
You must be signed in to change notification settings - Fork 488
Add support for local files and package resources to MediaElement #2502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c370fe0
9c3ca06
dadb767
8d934c6
49ad676
9347a3a
5618e4d
193ac8a
a192ebd
0cd2546
9492160
8b5176d
6fcf06f
f7b18da
9bcccbb
4487557
81ad1a8
47979ef
00c5378
6837979
fb9ac11
db57d38
5bfea37
c82cd56
ba13d17
67600dd
2ed7d98
07219ca
296ddf2
ef1cb25
a83e7f8
63ceb1b
9866b61
df180ff
a38dfd8
b7c83bd
5653940
3a655dd
f13d6d6
48f192c
0eea2d7
1baf9b1
41ad3e3
9970721
aebcfc3
eed4cd9
2067ead
8faa0bf
1dcc96d
73ea461
96bc04c
cdcbd0c
d2e0dfe
2e1c4f6
6061f57
531a4d1
7d1224a
9b00703
dd89258
7cee939
d56063b
63a24d8
f2bfc77
f5104c2
de54a84
1d6d224
0d6ae86
08bcbe2
162679b
98b1fb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| using AVFoundation; | ||
| using CommunityToolkit.Maui.Views; | ||
| using CoreMedia; | ||
| using Foundation; | ||
| using MediaPlayer; | ||
|
|
@@ -69,42 +70,33 @@ public Metadata(PlatformMediaElement player) | |
| /// </summary> | ||
| /// <param name="playerItem"></param> | ||
| /// <param name="mediaElement"></param> | ||
| public void SetMetadata(AVPlayerItem? playerItem, IMediaElement? mediaElement) | ||
| /// <param name="cancellationToken"></param> | ||
| public async Task SetMetadata(AVPlayerItem? playerItem, IMediaElement? mediaElement, CancellationToken cancellationToken = default) | ||
| { | ||
| if (mediaElement is null) | ||
| { | ||
| Metadata.ClearNowPlaying(); | ||
| return; | ||
| } | ||
| ClearNowPlaying(); | ||
| var artwork = await MetadataArtworkMediaSource(mediaElement.MetadataArtworkSource, cancellationToken).ConfigureAwait(false); | ||
|
|
||
| var url = mediaElement.MetadataArtworkUrl; | ||
|
|
||
| if (artwork is UIImage image) | ||
| { | ||
| NowPlayingInfo.Artwork = new(boundsSize: new(320, 240), requestHandler: _ => image); | ||
| } | ||
| else | ||
| { | ||
| NowPlayingInfo.Artwork = new(boundsSize: new(0, 0), requestHandler: _ => defaultUIImage); | ||
| } | ||
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| NowPlayingInfo.Title = mediaElement.MetadataTitle; | ||
| NowPlayingInfo.Artist = mediaElement.MetadataArtist; | ||
| NowPlayingInfo.PlaybackDuration = playerItem?.Duration.Seconds ?? 0; | ||
| NowPlayingInfo.IsLiveStream = false; | ||
| NowPlayingInfo.PlaybackRate = mediaElement.Speed; | ||
| NowPlayingInfo.ElapsedPlaybackTime = playerItem?.CurrentTime.Seconds ?? 0; | ||
| NowPlayingInfo.Artwork = new(boundsSize: new(320, 240), requestHandler: _ => GetImage(url)); | ||
| MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = NowPlayingInfo; | ||
| } | ||
|
|
||
| static UIImage GetImage(string imageUri) | ||
| { | ||
| try | ||
| { | ||
| if (imageUri.StartsWith(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return UIImage.LoadFromData(NSData.FromUrl(new NSUrl(imageUri))) ?? defaultUIImage; | ||
| } | ||
| return defaultUIImage; | ||
| } | ||
| catch | ||
| { | ||
| return defaultUIImage; | ||
| } | ||
| } | ||
|
|
||
| MPRemoteCommandHandlerStatus SeekCommand(MPRemoteCommandEvent? commandEvent) | ||
| { | ||
| if (commandEvent is not MPChangePlaybackPositionCommandEvent eventArgs) | ||
|
|
@@ -181,4 +173,111 @@ MPRemoteCommandHandlerStatus ToggleCommand(MPRemoteCommandEvent? commandEvent) | |
|
|
||
| return MPRemoteCommandHandlerStatus.Success; | ||
| } | ||
|
|
||
| public static async Task<UIImage?> MetadataArtworkMediaSource(MediaSource? artworkUrl, CancellationToken cancellationToken = default) | ||
| { | ||
| switch(artworkUrl) | ||
| { | ||
| case UriMediaSource uriMediaSource: | ||
| var uri = uriMediaSource.Uri; | ||
| return GetBitmapFromUrl(uri?.AbsoluteUri); | ||
| case FileMediaSource fileMediaSource: | ||
| var uriFile = fileMediaSource.Path; | ||
| return await GetBitmapFromFile(uriFile, cancellationToken).ConfigureAwait(false); | ||
| case ResourceMediaSource resourceMediaSource: | ||
| var path = resourceMediaSource.Path; | ||
| return await GetBitmapFromResource(path, cancellationToken).ConfigureAwait(false); | ||
| case null: | ||
| return null; | ||
| } | ||
| return null; | ||
|
Comment on lines
+177
to
+193
|
||
| } | ||
|
|
||
| static async Task<UIImage?> GetBitmapFromFile(string? resource, CancellationToken cancellationToken = default) | ||
| { | ||
| if (string.IsNullOrEmpty(resource)) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine("Metadata artwork file path is null or empty."); | ||
| return null; | ||
| } | ||
| if (!File.Exists(resource)) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Metadata artwork file not found: '{resource}'."); | ||
|
||
| return null; | ||
| } | ||
| try | ||
| { | ||
| using var fileStream = File.OpenRead(resource); | ||
| using var memoryStream = new MemoryStream(); | ||
| await fileStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); | ||
| memoryStream.Position = 0; | ||
| NSData temp = NSData.FromStream(memoryStream) ?? new NSData(); | ||
| return UIImage.LoadFromData(temp); | ||
| } | ||
| catch (IOException ex) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Error reading metadata artwork file '{resource}': {ex}"); | ||
| return null; | ||
| } | ||
| catch (UnauthorizedAccessException ex) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Access denied reading metadata artwork file '{resource}': {ex}"); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| static UIImage? GetBitmapFromUrl(string? resource) | ||
| { | ||
| if (string.IsNullOrEmpty(resource)) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| var nsUrl = new NSUrl(resource); | ||
| NSData? data = NSData.FromUrl(nsUrl); | ||
| if (data is null) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Failed to load metadata artwork from URL: '{resource}' - NSData.FromUrl returned null."); | ||
| return null; | ||
| } | ||
|
|
||
| UIImage? image = UIImage.LoadFromData(data); | ||
| if (image is null) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Failed to create UIImage from URL: '{resource}' - UIImage.LoadFromData returned null."); | ||
| } | ||
|
|
||
| return image; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Error loading metadata artwork from URL '{resource}': {ex}"); | ||
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return null; | ||
| } | ||
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| static async Task<UIImage?> GetBitmapFromResource(string? resource, CancellationToken cancellationToken = default) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(resource)) | ||
| { | ||
| return null; | ||
| } | ||
| using var inputStream = await FileSystem.OpenAppPackageFileAsync(resource).ConfigureAwait(false); | ||
| using var memoryStream = new MemoryStream(); | ||
| if (inputStream is null) | ||
| { | ||
| System.Diagnostics.Trace.WriteLine($"Failed to open app package file: '{resource}' - stream is null."); | ||
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return null; | ||
| } | ||
| await inputStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); | ||
| memoryStream.Position = 0; | ||
| NSData? nsdata = NSData.FromStream(memoryStream); | ||
| if (nsdata is null) | ||
| { | ||
| System.Diagnostics.Trace.TraceInformation($"NSData create from stream: {nsdata} is null."); | ||
|
Comment on lines
+270
to
+278
|
||
| return null; | ||
| } | ||
| return UIImage.LoadFromData(nsdata); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.