// Copyright (c) Arlo Godfrey. All Rights Reserved.
// Licensed under the GNU Lesser General Public License, Version 3.0 with additional terms.
// See the LICENSE, LICENSE.LESSER and LICENSE.ADDITIONAL files in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using OwlCore.Events;
using StrixMusic.Sdk.AdapterModels;
using StrixMusic.Sdk.AppModels;
using StrixMusic.Sdk.CoreModels;
using StrixMusic.Sdk.MediaPlayback;
using StrixMusic.Sdk.Plugins.Model;
namespace StrixMusic.Sdk.PluginModels;
///
/// Wraps an instance of with the provided plugins.
///
public class TrackPluginWrapper : ITrack, IPluginWrapper
{
private readonly ITrack _track;
private readonly SdkModelPlugin[] _plugins;
///
/// Initializes a new instance of the class.
///
/// The instance to wrap around and apply plugins to.
/// The plugins that are applied to items returned from or emitted by this collection.
internal TrackPluginWrapper(ITrack track, params SdkModelPlugin[] plugins)
{
foreach (var item in plugins)
ActivePlugins.Import(item);
ActivePlugins = GlobalModelPluginConnector.Create(ActivePlugins);
_track = ActivePlugins.Track.Execute(track);
_plugins = plugins;
AttachEvents(_track);
if (_track.Album is not null)
Album = new AlbumPluginWrapper(_track.Album, _plugins);
if (_track.RelatedItems is not null)
RelatedItems = new PlayableCollectionGroupPluginWrapper(_track.RelatedItems, _plugins);
}
///
public SdkModelPlugin ActivePlugins { get; } = new(PluginModelWrapperInfo.Metadata);
private void AttachEvents(ITrack track)
{
track.SourcesChanged += OnSourcesChanged;
track.ImagesCountChanged += OnImagesCountChanged;
track.UrlsCountChanged += OnUrlsCountChanged;
track.PlaybackStateChanged += OnPlaybackStateChanged;
track.NameChanged += OnNameChanged;
track.DescriptionChanged += OnDescriptionChanged;
track.DurationChanged += OnDurationChanged;
track.LastPlayedChanged += OnLastPlayedChanged;
track.IsChangeNameAsyncAvailableChanged += OnIsChangeNameAsyncAvailableChanged;
track.IsChangeDescriptionAsyncAvailableChanged += OnIsChangeDescriptionAsyncAvailableChanged;
track.IsChangeDurationAsyncAvailableChanged += OnIsChangeDurationAsyncAvailableChanged;
track.IsPlayArtistCollectionAsyncAvailableChanged += OnIsPlayArtistCollectionAsyncAvailableChanged;
track.IsPauseArtistCollectionAsyncAvailableChanged += OnIsPauseArtistCollectionAsyncAvailableChanged;
track.ArtistItemsCountChanged += OnArtistItemsCountChanged;
track.ImagesChanged += OnImagesChanged;
track.UrlsChanged += OnUrlsChanged;
track.DownloadInfoChanged += OnDownloadInfoChanged;
track.ArtistItemsChanged += OnArtistItemsChanged;
track.GenresCountChanged += OnGenresCountChanged;
track.TrackNumberChanged += OnTrackNumberChanged;
track.LanguageChanged += OnLanguageChanged;
track.IsExplicitChanged += OnIsExplicitChanged;
track.GenresChanged += OnGenresChanged;
track.LyricsChanged += OnLyricsChanged;
track.AlbumChanged += OnAlbumChanged;
}
private void DetachEvents(ITrack track)
{
track.SourcesChanged -= OnSourcesChanged;
track.ImagesCountChanged -= OnImagesCountChanged;
track.UrlsCountChanged -= OnUrlsCountChanged;
track.PlaybackStateChanged -= OnPlaybackStateChanged;
track.NameChanged -= OnNameChanged;
track.DescriptionChanged -= OnDescriptionChanged;
track.DurationChanged -= OnDurationChanged;
track.LastPlayedChanged -= OnLastPlayedChanged;
track.IsChangeNameAsyncAvailableChanged -= OnIsChangeNameAsyncAvailableChanged;
track.IsChangeDescriptionAsyncAvailableChanged -= OnIsChangeDescriptionAsyncAvailableChanged;
track.IsChangeDurationAsyncAvailableChanged -= OnIsChangeDurationAsyncAvailableChanged;
track.IsPlayArtistCollectionAsyncAvailableChanged -= OnIsPlayArtistCollectionAsyncAvailableChanged;
track.IsPauseArtistCollectionAsyncAvailableChanged -= OnIsPauseArtistCollectionAsyncAvailableChanged;
track.ArtistItemsCountChanged -= OnArtistItemsCountChanged;
track.ImagesChanged -= OnImagesChanged;
track.UrlsChanged -= OnUrlsChanged;
track.DownloadInfoChanged -= OnDownloadInfoChanged;
track.ArtistItemsChanged -= OnArtistItemsChanged;
track.GenresCountChanged -= OnGenresCountChanged;
track.TrackNumberChanged -= OnTrackNumberChanged;
track.LanguageChanged -= OnLanguageChanged;
track.IsExplicitChanged -= OnIsExplicitChanged;
track.GenresChanged -= OnGenresChanged;
track.LyricsChanged -= OnLyricsChanged;
track.AlbumChanged -= OnAlbumChanged;
}
private void OnSourcesChanged(object sender, EventArgs e) => SourcesChanged?.Invoke(sender, e);
private void OnArtistItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
var wrappedAdded = addedItems.Select(x => new CollectionChangedItem(Transform(x.Data), x.Index)).ToList();
var wrappedRemoved = removedItems.Select(x => new CollectionChangedItem(Transform(x.Data), x.Index)).ToList();
ArtistItemsChanged?.Invoke(sender, wrappedAdded, wrappedRemoved);
}
private void OnUrlsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
var wrappedAdded = addedItems.Select(x => new CollectionChangedItem(new UrlPluginWrapper(x.Data, _plugins), x.Index)).ToList();
var wrappedRemoved = removedItems.Select(x => new CollectionChangedItem(new UrlPluginWrapper(x.Data, _plugins), x.Index)).ToList();
UrlsChanged?.Invoke(sender, wrappedAdded, wrappedRemoved);
}
private void OnImagesChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
var wrappedAdded = addedItems.Select(x => new CollectionChangedItem(new ImagePluginWrapper(x.Data, _plugins), x.Index)).ToList();
var wrappedRemoved = removedItems.Select(x => new CollectionChangedItem(new ImagePluginWrapper(x.Data, _plugins), x.Index)).ToList();
ImagesChanged?.Invoke(sender, wrappedAdded, wrappedRemoved);
}
private void OnAlbumChanged(object sender, IAlbum? e)
{
if (e is not null)
Album = new AlbumPluginWrapper(e, _plugins);
else
{
if (Album is not null)
(Album as AlbumPluginWrapper)?.DisposeAsync();
Album = null;
}
}
private void OnDownloadInfoChanged(object sender, DownloadInfo e) => DownloadInfoChanged?.Invoke(sender, e);
private void OnArtistItemsCountChanged(object sender, int e) => ArtistItemsCountChanged?.Invoke(sender, e);
private void OnIsPauseArtistCollectionAsyncAvailableChanged(object sender, bool e) => IsPauseArtistCollectionAsyncAvailableChanged?.Invoke(sender, e);
private void OnIsPlayArtistCollectionAsyncAvailableChanged(object sender, bool e) => IsPlayArtistCollectionAsyncAvailableChanged?.Invoke(sender, e);
private void OnIsChangeDurationAsyncAvailableChanged(object sender, bool e) => IsChangeDurationAsyncAvailableChanged?.Invoke(sender, e);
private void OnIsChangeDescriptionAsyncAvailableChanged(object sender, bool e) => IsChangeDescriptionAsyncAvailableChanged?.Invoke(sender, e);
private void OnIsChangeNameAsyncAvailableChanged(object sender, bool e) => IsChangeNameAsyncAvailableChanged?.Invoke(sender, e);
private void OnLastPlayedChanged(object sender, DateTime? e) => LastPlayedChanged?.Invoke(sender, e);
private void OnDurationChanged(object sender, TimeSpan e) => DurationChanged?.Invoke(sender, e);
private void OnDescriptionChanged(object sender, string? e) => DescriptionChanged?.Invoke(sender, e);
private void OnNameChanged(object sender, string e) => NameChanged?.Invoke(sender, e);
private void OnPlaybackStateChanged(object sender, PlaybackState e) => PlaybackStateChanged?.Invoke(sender, e);
private void OnUrlsCountChanged(object sender, int e) => UrlsCountChanged?.Invoke(sender, e);
private void OnImagesCountChanged(object sender, int e) => ImagesCountChanged?.Invoke(sender, e);
private void OnLyricsChanged(object sender, ILyrics? e) => LyricsChanged?.Invoke(sender, e);
private void OnGenresChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
var wrappedAdded = addedItems.Select(x => new CollectionChangedItem(new GenrePluginWrapper(x.Data, _plugins), x.Index)).ToList();
var wrappedRemoved = removedItems.Select(x => new CollectionChangedItem(new GenrePluginWrapper(x.Data, _plugins), x.Index)).ToList();
GenresChanged?.Invoke(sender, wrappedAdded, wrappedRemoved);
}
private void OnIsExplicitChanged(object sender, bool e) => IsExplicitChanged?.Invoke(sender, e);
private void OnLanguageChanged(object sender, CultureInfo? e) => LanguageChanged?.Invoke(sender, e);
private void OnTrackNumberChanged(object sender, int? e) => TrackNumberChanged?.Invoke(sender, e);
private void OnGenresCountChanged(object sender, int e) => GenresCountChanged?.Invoke(sender, e);
///
public event EventHandler? SourcesChanged;
///
public event EventHandler? ImagesCountChanged;
///
public event EventHandler? UrlsCountChanged;
///
public event EventHandler? PlaybackStateChanged;
///
public event EventHandler? NameChanged;
///
public event EventHandler? DescriptionChanged;
///
public event EventHandler? DurationChanged;
///
public event EventHandler? LastPlayedChanged;
///
public event EventHandler? IsChangeNameAsyncAvailableChanged;
///
public event EventHandler? IsChangeDescriptionAsyncAvailableChanged;
///
public event EventHandler? IsChangeDurationAsyncAvailableChanged;
///
public event EventHandler? IsPlayArtistCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPauseArtistCollectionAsyncAvailableChanged;
///
public event EventHandler? ArtistItemsCountChanged;
///
public event CollectionChangedEventHandler? ImagesChanged;
///
public event CollectionChangedEventHandler? UrlsChanged;
///
public event EventHandler? DownloadInfoChanged;
///
public event CollectionChangedEventHandler? ArtistItemsChanged;
///
public event EventHandler? GenresCountChanged;
///
public event EventHandler? TrackNumberChanged;
///
public event EventHandler? LanguageChanged;
///
public event EventHandler? IsExplicitChanged;
///
public event CollectionChangedEventHandler? GenresChanged;
///
public event EventHandler? AlbumChanged;
///
public event EventHandler? LyricsChanged;
///
public int TotalImageCount => _track.TotalImageCount;
///
public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsAddImageAvailableAsync(index, cancellationToken);
///
public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsRemoveImageAvailableAsync(index, cancellationToken);
///
public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) => _track.RemoveImageAsync(index, cancellationToken);
///
public int TotalUrlCount => _track.TotalUrlCount;
///
public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) => _track.RemoveUrlAsync(index, cancellationToken);
///
public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsAddUrlAvailableAsync(index, cancellationToken);
///
public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsRemoveUrlAvailableAsync(index, cancellationToken);
///
public string Id => _track.Id;
///
public string Name => _track.Name;
///
public string? Description => _track.Description;
///
public DateTime? LastPlayed => _track.LastPlayed;
///
public PlaybackState PlaybackState => _track.PlaybackState;
///
public TimeSpan Duration => _track.Duration;
///
public bool IsChangeNameAsyncAvailable => _track.IsChangeNameAsyncAvailable;
///
public bool IsChangeDescriptionAsyncAvailable => _track.IsChangeDescriptionAsyncAvailable;
///
public bool IsChangeDurationAsyncAvailable => _track.IsChangeDurationAsyncAvailable;
///
public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default) => _track.ChangeNameAsync(name, cancellationToken);
///
public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default) => _track.ChangeDescriptionAsync(description, cancellationToken);
///
public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default) => _track.ChangeDurationAsync(duration, cancellationToken);
///
public DateTime? AddedAt => _track.AddedAt;
///
public int TotalArtistItemsCount => _track.TotalArtistItemsCount;
///
public bool IsPlayArtistCollectionAsyncAvailable => _track.IsPlayArtistCollectionAsyncAvailable;
///
public bool IsPauseArtistCollectionAsyncAvailable => _track.IsPauseArtistCollectionAsyncAvailable;
///
public Task PlayArtistCollectionAsync(CancellationToken cancellationToken = default) => _track.PlayArtistCollectionAsync(cancellationToken);
///
public Task PauseArtistCollectionAsync(CancellationToken cancellationToken = default) => _track.PauseArtistCollectionAsync(cancellationToken);
///
public Task RemoveArtistItemAsync(int index, CancellationToken cancellationToken = default) => _track.RemoveArtistItemAsync(index, cancellationToken);
///
public Task IsAddArtistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsAddArtistItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveArtistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsRemoveArtistItemAvailableAsync(index, cancellationToken);
///
public bool Equals(ICoreImageCollection other) => _track.Equals(other);
///
IReadOnlyList IMerged.Sources => ((IMerged)_track).Sources;
///
IReadOnlyList IMerged.Sources => ((IMerged)_track).Sources;
///
IReadOnlyList IMerged.Sources => ((IMerged)_track).Sources;
///
IReadOnlyList IMerged.Sources => ((IMerged)_track).Sources;
///
IReadOnlyList IMerged.Sources => ((IMerged)_track).Sources;
///
public IReadOnlyList Sources => ((IMerged)_track).Sources;
///
public IAsyncEnumerable GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default) => _track.GetImagesAsync(limit, offset, cancellationToken).Select(x => new ImagePluginWrapper(x, _plugins));
///
public Task AddImageAsync(IImage image, int index, CancellationToken cancellationToken = default) => _track.AddImageAsync(image, index, cancellationToken);
///
public bool Equals(ICoreUrlCollection other) => _track.Equals(other);
///
public IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _track.GetUrlsAsync(limit, offset, cancellationToken).Select(x => new UrlPluginWrapper(x, _plugins));
///
public Task AddUrlAsync(IUrl url, int index, CancellationToken cancellationToken = default) => _track.AddUrlAsync(url, index, cancellationToken);
///
public DownloadInfo DownloadInfo => _track.DownloadInfo;
///
public Task StartDownloadOperationAsync(DownloadOperation operation, CancellationToken cancellationToken = default) => _track.StartDownloadOperationAsync(operation, cancellationToken);
///
public bool Equals(ICoreArtistCollectionItem other) => _track.Equals(other);
///
public bool Equals(ICoreArtistCollection other) => _track.Equals(other);
///
public Task PlayArtistCollectionAsync(IArtistCollectionItem artistItem, CancellationToken cancellationToken = default) => _track.PlayArtistCollectionAsync(artistItem, cancellationToken);
///
public IAsyncEnumerable GetArtistItemsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _track.GetArtistItemsAsync(limit, offset, cancellationToken).Select(Transform);
///
public Task AddArtistItemAsync(IArtistCollectionItem artistItem, int index, CancellationToken cancellationToken = default) => _track.AddArtistItemAsync(artistItem, index, cancellationToken);
///
public bool Equals(ICoreTrack other) => _track.Equals(other);
///
public int TotalGenreCount => _track.TotalGenreCount;
///
public Task RemoveGenreAsync(int index, CancellationToken cancellationToken = default) => _track.RemoveGenreAsync(index, cancellationToken);
///
public Task IsAddGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsAddGenreAvailableAsync(index, cancellationToken);
///
public Task IsRemoveGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _track.IsRemoveGenreAvailableAsync(index, cancellationToken);
///
public TrackType Type => _track.Type;
///
public int? TrackNumber => _track.TrackNumber;
///
public int? DiscNumber => _track.DiscNumber;
///
public CultureInfo? Language => _track.Language;
///
public bool IsExplicit => _track.IsExplicit;
///
public bool IsChangeAlbumAsyncAvailable => _track.IsChangeAlbumAsyncAvailable;
///
public bool IsChangeTrackNumberAsyncAvailable => _track.IsChangeTrackNumberAsyncAvailable;
///
public bool IsChangeLanguageAsyncAvailable => _track.IsChangeLanguageAsyncAvailable;
///
public bool IsChangeLyricsAsyncAvailable => _track.IsChangeLyricsAsyncAvailable;
///
public bool IsChangeIsExplicitAsyncAvailable => _track.IsChangeIsExplicitAsyncAvailable;
///
public Task ChangeTrackNumberAsync(int? trackNumber, CancellationToken cancellationToken = default) => _track.ChangeTrackNumberAsync(trackNumber, cancellationToken);
///
public Task ChangeLanguageAsync(CultureInfo language, CancellationToken cancellationToken = default) => _track.ChangeLanguageAsync(language, cancellationToken);
///
public Task ChangeIsExplicitAsync(bool isExplicit, CancellationToken cancellationToken = default) => _track.ChangeIsExplicitAsync(isExplicit, cancellationToken);
///
public bool Equals(ICoreGenreCollection other) => _track.Equals(other);
///
public IAsyncEnumerable GetGenresAsync(int limit, int offset, CancellationToken cancellationToken = default) => _track.GetGenresAsync(limit, offset, cancellationToken).Select(x => new GenrePluginWrapper(x, _plugins));
///
public Task AddGenreAsync(IGenre genre, int index, CancellationToken cancellationToken = default) => _track.AddGenreAsync(genre, index, cancellationToken);
///
public IAlbum? Album { get; private set; }
///
public ILyrics? Lyrics => _track.Lyrics;
///
public IPlayableCollectionGroup? RelatedItems { get; }
///
public Task ChangeLyricsAsync(ILyrics? lyrics, CancellationToken cancellationToken = default) => _track.ChangeLyricsAsync(lyrics, cancellationToken);
///
public Task ChangeAlbumAsync(IAlbum? album, CancellationToken cancellationToken = default) => _track.ChangeAlbumAsync(album, cancellationToken);
///
public ValueTask DisposeAsync()
{
DetachEvents(_track);
return _track.DisposeAsync();
}
private IArtistCollectionItem Transform(IArtistCollectionItem item) => item switch
{
IArtist artist => new ArtistPluginWrapper(artist, _plugins),
IArtistCollection artistCollection => new ArtistCollectionPluginWrapper(artistCollection, _plugins),
_ => ThrowHelper.ThrowArgumentOutOfRangeException()
};
}