Skip to content

Add support for local files and package resources to MediaElement#2502

Draft
ne0rrmatrix wants to merge 70 commits intoCommunityToolkit:mainfrom
ne0rrmatrix:MetaDataSource
Draft

Add support for local files and package resources to MediaElement#2502
ne0rrmatrix wants to merge 70 commits intoCommunityToolkit:mainfrom
ne0rrmatrix:MetaDataSource

Conversation

@ne0rrmatrix
Copy link
Member

Description of Change

Add support for file and resource file types to MediaElement.MetadataArtworkSource. This will allow developers to set artwork using MediaSource class.

Linked Issues

PR Checklist

  • Has a linked Issue, and the Issue has been approved(bug) or Championed (feature/proposal)
  • Has tests (if omitted, state reason in description)
  • Has samples (if omitted, state reason in description)
  • Rebased on top of main at time of PR
  • Changes adhere to coding standard
  • Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pulls

Additional information

Summary

Add support for local files and package resources as a source for MediaElement.MetadataArtworkUrl. This will add the missing support for all types of files on all device for artwork images.

Motivation

Allow developer more options to add images from more locations to use as artwork for player.

Detailed Design

API Design:

/// <summary>
	/// Backing store for the <see cref="MetadataArtworkUrl"/> property.
	/// </summary>
	public static readonly BindableProperty MetadataArtworkUrlProperty = BindableProperty.Create(nameof(MetadataArtworkSource), typeof(MediaSource), typeof(MediaElement));
/// Gets or sets the Artwork Image Url of the media.
	/// This is a bindable property.
	/// </summary>
	[TypeConverter(typeof(MediaSourceConverter))]
	public MetadataArtworkSource? MetadataArtworkSource
	{
		get => (MediaSource)GetValue(MetadataArtworkUrlProperty);
		set => SetValue(MetadataArtworkUrlProperty, value);
	}

Usage Syntax

XAML:

 <toolkit:MediaElement
     x:Name="MediaElement"
     ShouldAutoPlay="True"
     Source="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
     MetadataArtworkSource="ebedded://robot.jpg"
     MetadataTitle="Big Buck Bunny"
     MetadataArtist="Blender Foundation"/>

Code Behind:

MediaElement.MetadataArtworkSource = MediaSource.FromResource("robot.jpg");  

Updated `PlatformUpdateSource` to be asynchronous, re-implemented `SetPoster` method for robustness, and replaced `MetadataArtworkUrl` with `MetadataArtworkSource` across the project.

- Replaced `MetadataArtworkUrl` with `MetadataArtworkSource` in `MediaElementPage.xaml` and related files.
- Introduced `loadCustomMediaSource` constant and `saveDirectory` string in `MediaElementPage.xaml.cs`.
- Updated `ChangeSourceClicked` method to handle new media source and artwork property.
- Added file handling methods: `Savefile`, `GetFileName`, and `PickAndShow`.
- Updated `IMediaElement` and `MediaElement` class to use `MetadataArtworkSource`.
- Modified `SetMetadata` in `Metadata.macios.cs` for new artwork property.
- Removed `Metadata` class from `Metadata.windows.cs`.
- Enhanced `MediaManager.android.cs` to handle new artwork property and fetch image data.
- Added `BlankByteArray` method and `PlaybackState` class in `MediaManager.android.cs`.
- Made `PlatformUpdateSource` in `MediaManager.macios.cs` asynchronous, updated `Dispose` method.
- Added `GetArtwork` struct for fetching artwork in `MediaManager.macios.cs`.
- Updated `MediaManager.windows.cs` to handle new artwork property and added `ArtworkUrl` method.
- Updated `MediaElementTests` for new artwork property.
Simplified `SetMetadata` in `Metadata.macios.cs` by removing a null check for `artwork` before checking if it is a `UIImage`. Cleaned up `Dispose` in `MediaManager.android.cs` by removing redundant empty lines. Streamlined `StopService` in `MediaManager.android.cs` by removing `HttpClient` usage and `GetBytesFromMetadataArtworkUrl` method. Updated `Dispose` in `MediaManager.macios.cs` to change `SetPoster` return type from `Task` to `ValueTask`. Refactored `UpdateMetadata` in `MediaManager.windows.cs` to handle different `MetadataArtworkSource` types explicitly and removed redundant `ArtworkUrl` method. Cleaned up `OnPlaybackSessionPlaybackStateChanged` in `MediaManager.windows.cs` by removing the now redundant `ArtworkUrl` method.
@dotnet-policy-service dotnet-policy-service bot added stale The author has not responded in over 30 days help wanted This proposal has been approved and is ready to be implemented labels May 1, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.

…os.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 18, 2026 22:49
ne0rrmatrix and others added 4 commits February 18, 2026 14:50
…oid.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…oid.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ios.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…oid.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 6 comments.

StartService now verifies isAndroidForegroundServiceEnabled before starting the foreground service, preventing unnecessary service startup when the feature is disabled.
Updated the logging statement to include the exception object
using the "{Ex}" placeholder, providing more detailed error
information when file picking fails.
Correct ToggleCommand null check, clarify NSData log message, and remove file path/asset stream handling in MediaManager.android.cs for simplified stream logic.
Copilot AI review requested due to automatic review settings February 18, 2026 23:11
SetMetadata is now called without a CancellationToken, reflecting a change in its method signature that no longer requires this parameter.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.

…MediaElementPage.xaml.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 18, 2026 23:21
…ios.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Removed null-conditional logging for Logger, assuming it is always non-null. Moved thumbnail and poster clearing logic outside the switch statement to ensure artwork is reset when source, stream, or uri are null. This improves UI consistency when no artwork is available.
Prevent possible NullReferenceException by adding a null-conditional check when accessing url.AbsoluteString after calling NSBundle.MainBundle.GetUrlForResource. This ensures the code safely handles cases where the returned url may be null.
Copilot AI review requested due to automatic review settings February 18, 2026 23:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 6 comments.

Comment on lines +539 to +560
AVAssetTrack? videoTrack = null;
if (PlayerItem.Asset is not null)
{
#if IOS || MACCATALYST
// Use the non-obsolete API for iOS 18+ and MacCatalyst 18+
if (OperatingSystem.IsIOSVersionAtLeast(18) || OperatingSystem.IsMacCatalystVersionAtLeast(18))
{
// On iOS 18+ and MacCatalyst 18+, AVAsset.TracksWithMediaType is obsolete.
// Instead, use the Tracks property and filter for video tracks.
videoTrack = PlayerItem.Asset.Tracks.FirstOrDefault(t => t.MediaType == AVMediaTypes.Video.GetConstant());
}
else
#endif
{
// For earlier versions, use the existing API, but check for null
var videoMediaType = AVMediaTypes.Video.GetConstant();
if (videoMediaType is not null)
{
videoTrack = PlayerItem.Asset.TracksWithMediaType(videoMediaType).FirstOrDefault();
}
}
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is code duplication between the SetPoster method (lines 539-560) and the GetTrack method (lines 505-516). Both implement version-specific logic for handling AVAsset.TracksWithMediaType obsolescence in iOS 18+/MacCatalyst 18+. However, the SetPoster implementation correctly uses the new Tracks API (line 548), while GetTrack simply returns null for newer OS versions. Either the GetTrack method should be fixed to match SetPoster's implementation, or SetPoster should call GetTrack instead of duplicating the logic.

Copilot uses AI. Check for mistakes.
Comment on lines +265 to +273
System.Diagnostics.Trace.WriteLine($"Failed to open app package file: '{resource}' - stream is null.");
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.");
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using System.Diagnostics.Trace.WriteLine for debug logging violates the codebase's logging convention. According to the project's coding standards, Trace.WriteLine() should be used instead of Debug.WriteLine(), but more importantly, this file should use structured logging via the ILogger instance that exists in other platform managers. The Windows and Android implementations consistently use Logger.LogWarning() and Logger.LogInformation() for similar scenarios. This inconsistency makes debugging harder and prevents proper log filtering and categorization.

Copilot generated this review using guidance from repository custom instructions.
Player.Play();
}

await SetPoster();
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SetPoster method can throw exceptions when loading images (UIImage.LoadFromData or UIImage.FromFile might fail), and it's called from PlatformUpdateSource without a try-catch block at line 276. If image loading fails, the entire source update operation could fail. Consider wrapping the SetPoster call in a try-catch block to ensure that media playback isn't prevented by artwork loading failures.

Suggested change
await SetPoster();
try
{
await SetPoster();
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to set poster artwork.");
}

Copilot uses AI. Check for mistakes.
{
if (!File.Exists(resource))
{
System.Diagnostics.Trace.WriteLine($"Metadata artwork file not found: '{resource}'.");
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using System.Diagnostics.Trace.WriteLine for debug logging violates the codebase's logging convention. According to the project's coding standards, Trace.WriteLine() should be used instead of Debug.WriteLine(), but more importantly, this file should use structured logging via the ILogger instance that exists in other platform managers. The Windows and Android implementations consistently use Logger.LogWarning() and Logger.LogInformation() for similar scenarios. This inconsistency makes debugging harder and prevents proper log filtering and categorization.

Copilot generated this review using guidance from repository custom instructions.
Converted HttpClient from static to instance member in MediaManager. Updated GetArtworkFromMediasource and GetBytesFromMetadataArtworkUrl to instance methods, ensuring each MediaManager has its own HttpClient.
Added validation to ensure the artwork file path is not null or empty before loading the bitmap. Logs a trace message and returns null if the path is invalid, preventing potential errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

help wanted This proposal has been approved and is ready to be implemented stale The author has not responded in over 30 days

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants