// 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() }; }