// 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.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Diagnostics; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using OwlCore; using OwlCore.Events; using OwlCore.Extensions; using StrixMusic.Sdk.AdapterModels; using StrixMusic.Sdk.AppModels; using StrixMusic.Sdk.CoreModels; using StrixMusic.Sdk.Extensions; using StrixMusic.Sdk.MediaPlayback; using StrixMusic.Sdk.ViewModels.Helpers; namespace StrixMusic.Sdk.ViewModels { /// /// A ViewModel for . /// public sealed class TrackViewModel : ObservableObject, ISdkViewModel, ITrack, IArtistCollectionViewModel, IImageCollectionViewModel, IGenreCollectionViewModel { private readonly SemaphoreSlim _populateArtistsMutex = new(1, 1); private readonly SemaphoreSlim _populateImagesMutex = new(1, 1); private readonly SemaphoreSlim _populateGenresMutex = new(1, 1); private readonly SemaphoreSlim _populateUrlsMutex = new(1, 1); private readonly SynchronizationContext _syncContext; private readonly ITrack _model; /// /// Creates a new instance of . /// /// The to wrap. public TrackViewModel(ITrack track) { _syncContext = SynchronizationContext.Current; _model = track; if (_model.Album != null) Album = new AlbumViewModel(_model.Album); if (_model.RelatedItems != null) RelatedItems = new PlayableCollectionGroupViewModel(_model.RelatedItems); Artists = new ObservableCollection(); UnsortedArtists = new ObservableCollection(); Images = new ObservableCollection(); Urls = new ObservableCollection(); Genres = new ObservableCollection(); PlayArtistCollectionAsyncCommand = new AsyncRelayCommand(PlayArtistCollectionAsync); PauseArtistCollectionAsyncCommand = new AsyncRelayCommand(PauseArtistCollectionAsync); PlayArtistAsyncCommand = new AsyncRelayCommand((x, y) => _model.PlayArtistCollectionAsync(x ?? ThrowHelper.ThrowArgumentNullException(), y)); ChangeNameAsyncCommand = new AsyncRelayCommand(ChangeNameInternalAsync); ChangeDescriptionAsyncCommand = new AsyncRelayCommand(ChangeDescriptionAsync); ChangeDurationAsyncCommand = new AsyncRelayCommand(ChangeDurationAsync); PopulateMoreArtistsCommand = new AsyncRelayCommand(PopulateMoreArtistsAsync); PopulateMoreImagesCommand = new AsyncRelayCommand(PopulateMoreImagesAsync); PopulateMoreGenresCommand = new AsyncRelayCommand(PopulateMoreGenresAsync); PopulateMoreUrlsCommand = new AsyncRelayCommand(PopulateMoreUrlsAsync); InitArtistCollectionAsyncCommand = new AsyncRelayCommand(InitArtistCollectionAsync); InitGenreCollectionAsyncCommand = new AsyncRelayCommand(InitGenreCollectionAsync); InitImageCollectionAsyncCommand = new AsyncRelayCommand(InitImageCollectionAsync); ChangeArtistCollectionSortingTypeCommand = new RelayCommand(x => SortArtistCollection(x, CurrentArtistSortingDirection)); ChangeArtistCollectionSortingDirectionCommand = new RelayCommand(x => SortArtistCollection(CurrentArtistSortingType, x)); AttachEvents(); } private void AttachEvents() { AlbumChanged += Track_AlbumChanged; DescriptionChanged += Track_DescriptionChanged; IsExplicitChanged += Track_IsExplicitChanged; LanguageChanged += Track_LanguageChanged; LyricsChanged += Track_LyricsChanged; NameChanged += Track_NameChanged; PlaybackStateChanged += Track_PlaybackStateChanged; TrackNumberChanged += Track_TrackNumberChanged; LastPlayedChanged += OnLastPlayedChanged; DownloadInfoChanged += OnDownloadInfoChanged; IsPlayArtistCollectionAsyncAvailableChanged += OnIsPlayArtistCollectionAsyncAvailableChanged; IsPauseArtistCollectionAsyncAvailableChanged += OnIsPauseArtistCollectionAsyncAvailableChanged; IsChangeNameAsyncAvailableChanged += OnIsChangeNameAsyncAvailableChanged; IsChangeDurationAsyncAvailableChanged += OnIsChangeDurationAsyncAvailableChanged; IsChangeDescriptionAsyncAvailableChanged += OnIsChangeDescriptionAsyncAvailableChanged; ArtistItemsCountChanged += OnArtistItemsCountChanged; ArtistItemsChanged += TrackViewModel_ArtistItemsChanged; ImagesCountChanged += OnImagesCountChanged; ImagesChanged += TrackViewModel_ImagesChanged; GenresChanged += TrackViewModel_GenresChanged; GenresCountChanged += OnGenresCountChanged; UrlsChanged += TrackViewModel_UrlsChanged; UrlsCountChanged += OnUrlsCountChanged; _model.PlaybackStateChanged += OnPlaybackStateChanged; } private void DetachEvents() { AlbumChanged -= Track_AlbumChanged; DescriptionChanged -= Track_DescriptionChanged; IsExplicitChanged -= Track_IsExplicitChanged; LanguageChanged -= Track_LanguageChanged; LyricsChanged -= Track_LyricsChanged; NameChanged -= Track_NameChanged; PlaybackStateChanged -= Track_PlaybackStateChanged; TrackNumberChanged -= Track_TrackNumberChanged; LastPlayedChanged -= OnLastPlayedChanged; DownloadInfoChanged -= OnDownloadInfoChanged; IsPlayArtistCollectionAsyncAvailableChanged -= OnIsPlayArtistCollectionAsyncAvailableChanged; IsPauseArtistCollectionAsyncAvailableChanged -= OnIsPauseArtistCollectionAsyncAvailableChanged; IsChangeNameAsyncAvailableChanged -= OnIsChangeNameAsyncAvailableChanged; IsChangeDurationAsyncAvailableChanged -= OnIsChangeDurationAsyncAvailableChanged; IsChangeDescriptionAsyncAvailableChanged -= OnIsChangeDescriptionAsyncAvailableChanged; ArtistItemsCountChanged -= OnArtistItemsCountChanged; ArtistItemsChanged -= TrackViewModel_ArtistItemsChanged; ImagesCountChanged -= OnImagesCountChanged; ImagesChanged -= TrackViewModel_ImagesChanged; GenresChanged -= TrackViewModel_GenresChanged; GenresCountChanged -= OnGenresCountChanged; UrlsChanged -= TrackViewModel_UrlsChanged; UrlsCountChanged -= OnUrlsCountChanged; _model.PlaybackStateChanged -= OnPlaybackStateChanged; } /// public event EventHandler? SourcesChanged { add => _model.SourcesChanged += value; remove => _model.SourcesChanged -= value; } /// public event EventHandler? PlaybackStateChanged; /// public event EventHandler? DownloadInfoChanged { add => _model.DownloadInfoChanged += value; remove => _model.DownloadInfoChanged -= value; } /// public event EventHandler? AlbumChanged { add => _model.AlbumChanged += value; remove => _model.AlbumChanged -= value; } /// public event EventHandler? TrackNumberChanged { add => _model.TrackNumberChanged += value; remove => _model.TrackNumberChanged -= value; } /// public event EventHandler? LanguageChanged { add => _model.LanguageChanged += value; remove => _model.LanguageChanged -= value; } /// public event EventHandler? LyricsChanged { add => _model.LyricsChanged += value; remove => _model.LyricsChanged -= value; } /// public event EventHandler? IsExplicitChanged { add => _model.IsExplicitChanged += value; remove => _model.IsExplicitChanged -= value; } /// public event EventHandler? NameChanged { add => _model.NameChanged += value; remove => _model.NameChanged -= value; } /// public event EventHandler? DescriptionChanged { add => _model.DescriptionChanged += value; remove => _model.DescriptionChanged -= value; } /// public event EventHandler? DurationChanged { add => _model.DurationChanged += value; remove => _model.DurationChanged -= value; } /// public event EventHandler? LastPlayedChanged { add => _model.LastPlayedChanged += value; remove => _model.LastPlayedChanged -= value; } /// public event EventHandler? IsPlayArtistCollectionAsyncAvailableChanged { add => _model.IsPlayArtistCollectionAsyncAvailableChanged += value; remove => _model.IsPlayArtistCollectionAsyncAvailableChanged -= value; } /// public event EventHandler? IsPauseArtistCollectionAsyncAvailableChanged { add => _model.IsPauseArtistCollectionAsyncAvailableChanged += value; remove => _model.IsPauseArtistCollectionAsyncAvailableChanged -= value; } /// public event EventHandler? IsChangeNameAsyncAvailableChanged { add => _model.IsChangeNameAsyncAvailableChanged += value; remove => _model.IsChangeNameAsyncAvailableChanged -= value; } /// public event EventHandler? IsChangeDescriptionAsyncAvailableChanged { add => _model.IsChangeDescriptionAsyncAvailableChanged += value; remove => _model.IsChangeDescriptionAsyncAvailableChanged -= value; } /// public event EventHandler? IsChangeDurationAsyncAvailableChanged { add => _model.IsChangeDurationAsyncAvailableChanged += value; remove => _model.IsChangeDurationAsyncAvailableChanged -= value; } /// public event EventHandler? ArtistItemsCountChanged { add => _model.ArtistItemsCountChanged += value; remove => _model.ArtistItemsCountChanged -= value; } /// public event CollectionChangedEventHandler? ArtistItemsChanged { add => _model.ArtistItemsChanged += value; remove => _model.ArtistItemsChanged -= value; } /// public event EventHandler? ImagesCountChanged { add => _model.ImagesCountChanged += value; remove => _model.ImagesCountChanged -= value; } /// public event CollectionChangedEventHandler? ImagesChanged { add => _model.ImagesChanged += value; remove => _model.ImagesChanged -= value; } /// public event EventHandler? GenresCountChanged { add => _model.GenresCountChanged += value; remove => _model.GenresCountChanged -= value; } /// public event CollectionChangedEventHandler? GenresChanged { add => _model.GenresChanged += value; remove => _model.GenresChanged -= value; } /// public event EventHandler? UrlsCountChanged { add => _model.UrlsCountChanged += value; remove => _model.UrlsCountChanged -= value; } /// public event CollectionChangedEventHandler? UrlsChanged { add => _model.UrlsChanged += value; remove => _model.UrlsChanged -= value; } private void Track_TrackNumberChanged(object sender, int? e) => _syncContext.Post(_ => OnPropertyChanged(nameof(TrackNumber)), null); private void Track_PlaybackStateChanged(object sender, PlaybackState e) => _syncContext.Post(_ => OnPropertyChanged(nameof(PlaybackState)), null); private void OnDownloadInfoChanged(object sender, DownloadInfo e) => _syncContext.Post(_ => OnPropertyChanged(nameof(DownloadInfo)), null); private void Track_NameChanged(object sender, string e) => _syncContext.Post(_ => OnPropertyChanged(nameof(Name)), null); private void Track_LyricsChanged(object sender, ILyrics? e) => _syncContext.Post(_ => OnPropertyChanged(nameof(Lyrics)), null); private void Track_LanguageChanged(object sender, CultureInfo? e) => _syncContext.Post(_ => OnPropertyChanged(nameof(Language)), null); private void Track_IsExplicitChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsExplicit)), null); private void Track_DescriptionChanged(object sender, string? e) => _syncContext.Post(_ => OnPropertyChanged(nameof(Description)), null); private void OnLastPlayedChanged(object sender, DateTime? e) => _syncContext.Post(_ => OnPropertyChanged(nameof(LastPlayed)), null); private void OnIsChangeDescriptionAsyncAvailableChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsChangeDescriptionAsyncAvailable)), null); private void OnIsChangeDurationAsyncAvailableChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsChangeDurationAsyncAvailable)), null); private void OnIsChangeNameAsyncAvailableChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsChangeNameAsyncAvailable)), null); private void OnIsPauseArtistCollectionAsyncAvailableChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsPauseArtistCollectionAsyncAvailable)), null); private void OnIsPlayArtistCollectionAsyncAvailableChanged(object sender, bool e) => _syncContext.Post(_ => OnPropertyChanged(nameof(IsPlayArtistCollectionAsyncAvailable)), null); private void Track_AlbumChanged(object sender, IAlbum? e) { Album = e is null ? null : new AlbumViewModel(e); _syncContext.Post(_ => OnPropertyChanged(nameof(Album)), null); } private void OnArtistItemsCountChanged(object sender, int e) => _syncContext.Post(_ => OnPropertyChanged(nameof(TotalArtistItemsCount)), null); private void OnImagesCountChanged(object sender, int e) => _syncContext.Post(_ => OnPropertyChanged(nameof(TotalImageCount)), null); private void OnGenresCountChanged(object sender, int e) => _syncContext.Post(_ => OnPropertyChanged(nameof(TotalGenreCount)), null); private void OnUrlsCountChanged(object sender, int e) => _syncContext.Post(_ => OnPropertyChanged(nameof(TotalUrlCount)), null); private void TrackViewModel_ImagesChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) => _syncContext.Post(_ => { Images.ChangeCollection(addedItems, removedItems); }, null); private void TrackViewModel_GenresChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) => _syncContext.Post(_ => { Genres.ChangeCollection(addedItems, removedItems); }, null); private void TrackViewModel_UrlsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) => _syncContext.Post(_ => { Urls.ChangeCollection(addedItems, removedItems); }, null); private void TrackViewModel_ArtistItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) => _syncContext.Post(_ => { if (CurrentArtistSortingType == ArtistSortingType.Unsorted) { Artists.ChangeCollection(addedItems, removedItems, item => item.Data switch { IArtist artist => new ArtistViewModel(artist), IArtistCollection collection => new ArtistCollectionViewModel(collection), _ => ThrowHelper.ThrowNotSupportedException( $"{item.Data.GetType()} not supported for adding to {GetType()}") }); } else { // Preventing index issues during artists emission from the core, also making sure that unordered artists updated. UnsortedArtists.ChangeCollection(addedItems, removedItems, item => item.Data switch { IArtist artist => new ArtistViewModel(artist), IArtistCollection collection => new ArtistCollectionViewModel(collection), _ => ThrowHelper.ThrowNotSupportedException( $"{item.Data.GetType()} not supported for adding to {GetType()}") }); // Avoiding direct assignment to prevent effect on UI. foreach (var item in UnsortedArtists) { if (!Artists.Contains(item)) Artists.Add(item); } foreach (var item in Artists) { if (!UnsortedArtists.Contains(item)) Artists.Remove(item); } SortArtistCollection(CurrentArtistSortingType, CurrentArtistSortingDirection); } }, null); private void OnPlaybackStateChanged(object sender, PlaybackState e) => _syncContext.Post(_ => { PlaybackStateChanged?.Invoke(this, PlaybackState); OnPropertyChanged(nameof(PlaybackState)); }, null); /// public void SortArtistCollection(ArtistSortingType artistSorting, SortDirection sortDirection) { CurrentArtistSortingType = artistSorting; CurrentArtistSortingDirection = sortDirection; CollectionSorting.SortArtists(Artists, artistSorting, sortDirection, UnsortedArtists); } /// /// The merged sources for this model. /// public IReadOnlyList Sources => _model.GetSources(); /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// /// The artists for this track. /// public ObservableCollection Artists { get; } /// public ObservableCollection UnsortedArtists { get; } /// public ObservableCollection Images { get; } /// public ObservableCollection Genres { get; } /// public ObservableCollection Urls { get; } /// public ArtistSortingType CurrentArtistSortingType { get; private set; } /// public SortDirection CurrentArtistSortingDirection { get; private set; } /// public TrackType Type => _model.Type; /// public TimeSpan Duration => _model.Duration; /// public DateTime? AddedAt => _model.AddedAt; /// public DateTime? LastPlayed => _model.LastPlayed; /// public IPlayableCollectionGroup? RelatedItems { get; } /// public string Id => _model.Id; /// public string Name => _model.Name; /// public int TotalArtistItemsCount => _model.TotalArtistItemsCount; /// public int TotalImageCount => _model.TotalImageCount; /// public int TotalGenreCount => _model.TotalGenreCount; /// public int TotalUrlCount => _model.TotalUrlCount; /// public AlbumViewModel? Album { get; private set; } /// IAlbum? ITrack.Album => _model.Album; /// public int? TrackNumber => _model.TrackNumber; /// public int? DiscNumber => _model.DiscNumber; /// public CultureInfo? Language => _model.Language; /// public ILyrics? Lyrics => _model.Lyrics; /// public bool IsExplicit => _model.IsExplicit; /// public string? Description => _model.Description; /// public PlaybackState PlaybackState => _model.PlaybackState; /// public DownloadInfo DownloadInfo => _model.DownloadInfo; /// public bool IsPlayArtistCollectionAsyncAvailable => _model.IsPlayArtistCollectionAsyncAvailable; /// public bool IsPauseArtistCollectionAsyncAvailable => _model.IsPauseArtistCollectionAsyncAvailable; /// public bool IsChangeNameAsyncAvailable => _model.IsChangeNameAsyncAvailable; /// public bool IsChangeDescriptionAsyncAvailable => _model.IsChangeDescriptionAsyncAvailable; /// public bool IsChangeDurationAsyncAvailable => _model.IsChangeDurationAsyncAvailable; /// public bool IsChangeAlbumAsyncAvailable => _model.IsChangeAlbumAsyncAvailable; /// public bool IsChangeTrackNumberAsyncAvailable => _model.IsChangeTrackNumberAsyncAvailable; /// public bool IsChangeLanguageAsyncAvailable => _model.IsChangeLanguageAsyncAvailable; /// public bool IsChangeLyricsAsyncAvailable => _model.IsChangeLyricsAsyncAvailable; /// public bool IsChangeIsExplicitAsyncAvailable => _model.IsChangeIsExplicitAsyncAvailable; /// public Task IsAddArtistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsAddArtistItemAvailableAsync(index, cancellationToken); /// public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsAddImageAvailableAsync(index, cancellationToken); /// public Task IsAddGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsAddGenreAvailableAsync(index, cancellationToken); /// public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsAddUrlAvailableAsync(index, cancellationToken); /// public Task IsRemoveArtistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsRemoveArtistItemAvailableAsync(index, cancellationToken); /// public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsRemoveImageAvailableAsync(index, cancellationToken); /// public Task IsRemoveGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsRemoveGenreAvailableAsync(index, cancellationToken); /// public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _model.IsRemoveUrlAvailableAsync(index, cancellationToken); /// public Task ChangeAlbumAsync(IAlbum? album, CancellationToken cancellationToken = default) => _model.ChangeAlbumAsync(album, cancellationToken); /// public Task ChangeTrackNumberAsync(int? trackNumber, CancellationToken cancellationToken = default) => _model.ChangeTrackNumberAsync(trackNumber, cancellationToken); /// public Task ChangeLanguageAsync(CultureInfo language, CancellationToken cancellationToken = default) => _model.ChangeLanguageAsync(language, cancellationToken); /// public Task ChangeLyricsAsync(ILyrics? lyrics, CancellationToken cancellationToken = default) => _model.ChangeLyricsAsync(lyrics, cancellationToken); /// public Task ChangeIsExplicitAsync(bool isExplicit, CancellationToken cancellationToken = default) => _model.ChangeIsExplicitAsync(isExplicit, cancellationToken); /// public Task PauseArtistCollectionAsync(CancellationToken cancellationToken = default) => _model.PauseArtistCollectionAsync(cancellationToken); /// public Task StartDownloadOperationAsync(DownloadOperation operation, CancellationToken cancellationToken = default) => _model.StartDownloadOperationAsync(operation, cancellationToken); /// public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default) => ChangeNameInternalAsync(name, cancellationToken); /// public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default) => _model.ChangeDescriptionAsync(description, cancellationToken); /// public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default) => _model.ChangeDurationAsync(duration, cancellationToken); /// public Task AddArtistItemAsync(IArtistCollectionItem artistItem, int index, CancellationToken cancellationToken = default) => _model.AddArtistItemAsync(artistItem, index, cancellationToken); /// public Task AddImageAsync(IImage image, int index, CancellationToken cancellationToken = default) => _model.AddImageAsync(image, index, cancellationToken); /// public Task AddGenreAsync(IGenre genre, int index, CancellationToken cancellationToken = default) => _model.AddGenreAsync(genre, index, cancellationToken); /// public Task AddUrlAsync(IUrl genre, int index, CancellationToken cancellationToken = default) => _model.AddUrlAsync(genre, index, cancellationToken); /// public Task RemoveArtistItemAsync(int index, CancellationToken cancellationToken = default) => _model.RemoveArtistItemAsync(index, cancellationToken); /// public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) => _model.RemoveImageAsync(index, cancellationToken); /// public Task RemoveGenreAsync(int index, CancellationToken cancellationToken = default) => _model.RemoveGenreAsync(index, cancellationToken); /// public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) => _model.RemoveUrlAsync(index, cancellationToken); /// public IAsyncEnumerable GetArtistItemsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _model.GetArtistItemsAsync(limit, offset, cancellationToken); /// public IAsyncEnumerable GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default) => _model.GetImagesAsync(limit, offset, cancellationToken); /// public IAsyncEnumerable GetGenresAsync(int limit, int offset, CancellationToken cancellationToken = default) => _model.GetGenresAsync(limit, offset, cancellationToken); /// public IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _model.GetUrlsAsync(limit, offset, cancellationToken); /// public Task PlayArtistCollectionAsync(IArtistCollectionItem artistItem, CancellationToken cancellationToken = default) => _model.PlayArtistCollectionAsync(artistItem, cancellationToken); /// public Task PlayArtistCollectionAsync(CancellationToken cancellationToken = default) => _model.PlayArtistCollectionAsync(cancellationToken); /// public async Task PopulateMoreArtistsAsync(int limit, CancellationToken cancellationToken = default) { using (await Flow.EasySemaphore(_populateArtistsMutex)) { using var releaseReg = cancellationToken.Register(() => _populateArtistsMutex.Release()); _syncContext.Post(async _ => { await foreach (var item in GetArtistItemsAsync(limit, Artists.Count, cancellationToken)) { switch (item) { case IArtist artist: var avm = new ArtistViewModel(artist); Artists.Add(avm); UnsortedArtists.Add(avm); break; case IArtistCollection collection: var acvm = new ArtistCollectionViewModel(collection); Artists.Add(acvm); UnsortedArtists.Add(acvm); break; } } }, null); } } /// public async Task PopulateMoreImagesAsync(int limit, CancellationToken cancellationToken = default) { using (await Flow.EasySemaphore(_populateImagesMutex)) { using var releaseReg = cancellationToken.Register(() => _populateImagesMutex.Release()); _syncContext.Post(async _ => { await foreach (var item in GetImagesAsync(limit, Images.Count, cancellationToken)) Images.Add(item); }, null); } } /// public async Task PopulateMoreGenresAsync(int limit, CancellationToken cancellationToken = default) { using (await Flow.EasySemaphore(_populateGenresMutex)) { using var releaseReg = cancellationToken.Register(() => _populateGenresMutex.Release()); _syncContext.Post(async _ => { await foreach (var item in GetGenresAsync(limit, Genres.Count, cancellationToken)) Genres.Add(item); }, null); } } /// public async Task PopulateMoreUrlsAsync(int limit, CancellationToken cancellationToken = default) { using (await Flow.EasySemaphore(_populateUrlsMutex)) { using var releaseReg = cancellationToken.Register(() => _populateUrlsMutex.Release()); _syncContext.Post(async _ => { await foreach (var item in GetUrlsAsync(limit, Urls.Count, cancellationToken)) Urls.Add(item); }, null); } } /// public IAsyncRelayCommand PopulateMoreArtistsCommand { get; } /// public IAsyncRelayCommand PlayArtistCollectionAsyncCommand { get; } /// public IRelayCommand ChangeArtistCollectionSortingTypeCommand { get; } /// public IRelayCommand ChangeArtistCollectionSortingDirectionCommand { get; } /// public IAsyncRelayCommand PlayArtistAsyncCommand { get; } /// public IAsyncRelayCommand PauseArtistCollectionAsyncCommand { get; } /// public IAsyncRelayCommand PopulateMoreImagesCommand { get; } /// public IAsyncRelayCommand PopulateMoreGenresCommand { get; } /// public IAsyncRelayCommand PopulateMoreUrlsCommand { get; } /// /// Command to change the name. It triggers . /// public IAsyncRelayCommand ChangeNameAsyncCommand { get; } /// /// Command to change the description. It triggers . /// public IAsyncRelayCommand ChangeDescriptionAsyncCommand { get; } /// /// Command to change the duration. It triggers . /// public IAsyncRelayCommand ChangeDurationAsyncCommand { get; } /// public IAsyncRelayCommand InitImageCollectionAsyncCommand { get; } /// public Task InitImageCollectionAsync(CancellationToken cancellationToken = default) => CollectionInit.ImageCollection(this, cancellationToken); /// public Task InitArtistCollectionAsync(CancellationToken cancellationToken = default) => CollectionInit.ArtistCollection(this, cancellationToken); /// public Task InitGenreCollectionAsync(CancellationToken cancellationToken = default) => CollectionInit.GenreCollection(this, cancellationToken); /// public IAsyncRelayCommand InitArtistCollectionAsyncCommand { get; } /// public IAsyncRelayCommand InitGenreCollectionAsyncCommand { get; } /// public bool Equals(ICoreArtistCollectionItem other) => _model.Equals(other); /// public bool Equals(ICoreImageCollection other) => _model.Equals(other); /// public bool Equals(ICoreArtistCollection other) => _model.Equals(other); /// public bool Equals(ICoreGenreCollection other) => _model.Equals(other); /// public bool Equals(ICoreUrlCollection other) => _model.Equals(other); /// public bool Equals(ICoreTrack other) => _model.Equals(other); private Task ChangeNameInternalAsync(string? name, CancellationToken cancellationToken = default) { Guard.IsNotNull(name, nameof(name)); return _model.ChangeNameAsync(name, cancellationToken); } /// public ValueTask DisposeAsync() { DetachEvents(); return _model.DisposeAsync(); } /// public async Task InitAsync(CancellationToken cancellationToken = default) { if (!IsInitialized) return; await Task.WhenAll(InitImageCollectionAsync(cancellationToken), InitArtistCollectionAsync(cancellationToken), InitGenreCollectionAsync(cancellationToken)); IsInitialized = true; } /// public bool IsInitialized { get; private set; } } }