using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using OwlCore.Events;
using OwlCore.Extensions;
using StrixMusic.Cores.Files.Services;
using StrixMusic.Sdk.CoreModels;
using StrixMusic.Sdk.FileMetadata;
using StrixMusic.Sdk.FileMetadata.Models;
using StrixMusic.Sdk.MediaPlayback;
namespace StrixMusic.Cores.Files.Models
{
///
/// Wraps around to provide artist information extracted from a file to the Strix SDK.
///
public sealed class FilesCoreArtist : ICoreArtist
{
private readonly IFileMetadataManager? _fileMetadataManager;
private ArtistMetadata _artistMetadata;
///
/// Initializes a new instance of the class.
///
/// The source core.
/// The artist metadata to wrap around.
public FilesCoreArtist(ICore sourceCore, ArtistMetadata artistMetadata)
{
SourceCore = sourceCore;
_artistMetadata = artistMetadata;
_fileMetadataManager = sourceCore.Cast().FileMetadataManager;
AttachEvents();
}
private void AttachEvents()
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
_fileMetadataManager.Artists.MetadataUpdated += Artists_MetadataUpdated;
}
private void DetachEvents()
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
_fileMetadataManager.Artists.MetadataUpdated -= Artists_MetadataUpdated;
}
private void Artists_MetadataUpdated(object sender, IEnumerable e)
{
foreach (var metadata in e)
{
if (metadata.Id != Id)
return;
Guard.IsNotNull(metadata.AlbumIds, nameof(metadata.AlbumIds));
Guard.IsNotNull(metadata.TrackIds, nameof(metadata.TrackIds));
Guard.IsNotNull(metadata.ImageIds, nameof(metadata.ImageIds));
Guard.IsNotNull(metadata.Genres, nameof(metadata.Genres));
var previousData = _artistMetadata;
_artistMetadata = metadata;
Guard.IsNotNull(previousData.TrackIds, nameof(previousData.TrackIds));
Guard.IsNotNull(previousData.AlbumIds, nameof(previousData.AlbumIds));
Guard.IsNotNull(previousData.ImageIds, nameof(previousData.ImageIds));
Guard.IsNotNull(previousData.Genres, nameof(previousData.Genres));
if (metadata.Name != previousData.Name)
NameChanged?.Invoke(this, Name);
if (metadata.Description != previousData.Description)
DescriptionChanged?.Invoke(this, Description);
if (metadata.Genres.Count != previousData.Genres.Count)
GenresCountChanged?.Invoke(this, metadata.Genres.Count);
if (metadata.TrackIds.Count != previousData.TrackIds.Count)
TracksCountChanged?.Invoke(this, metadata.TrackIds.Count);
if (metadata.AlbumIds.Count != previousData.AlbumIds.Count)
AlbumItemsCountChanged?.Invoke(this, metadata.AlbumIds.Count);
_ = HandleImagesChanged(previousData.ImageIds, metadata.ImageIds);
_ = HandleTracksChanged(previousData.TrackIds, metadata.TrackIds);
_ = HandleAlbumsChanged(previousData.AlbumIds, metadata.AlbumIds);
}
}
private async Task HandleAlbumsChanged(HashSet oldAlbumIds, HashSet newAlbumIds)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
if (oldAlbumIds.OrderBy(s => s).SequenceEqual(newAlbumIds.OrderBy(s => s)))
{
// Lists have identical content, so no images have changed.
return;
}
var addedAlbums = newAlbumIds.Except(oldAlbumIds);
var removedAlbums = oldAlbumIds.Except(newAlbumIds);
if (oldAlbumIds.Count != newAlbumIds.Count)
{
AlbumItemsChanged?.Invoke(this, await TransformAsync(addedAlbums), await TransformAsync(removedAlbums));
AlbumItemsCountChanged?.Invoke(this, newAlbumIds.Count);
}
async Task>> TransformAsync(IEnumerable ids)
{
var idArray = ids as string[] ?? ids.ToArray();
var collectionChangedItems = new List>(idArray.Length);
foreach (var id in idArray)
{
var album = await _fileMetadataManager.Albums.GetByIdAsync(id);
Guard.IsNotNullOrWhiteSpace(album?.Id, nameof(album.Id));
collectionChangedItems.Add(new CollectionChangedItem(InstanceCache.Albums.GetOrCreate(album.Id, SourceCore, album), collectionChangedItems.Count));
}
return collectionChangedItems;
}
}
private async Task HandleTracksChanged(HashSet oldTrackIds, HashSet newTrackIds)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
if (oldTrackIds.OrderBy(s => s).SequenceEqual(newTrackIds.OrderBy(s => s)))
{
// Lists have identical content, so no images have changed.
return;
}
var addedImages = newTrackIds.Except(oldTrackIds);
var removedImages = oldTrackIds.Except(newTrackIds);
if (oldTrackIds.Count != newTrackIds.Count)
{
TracksChanged?.Invoke(this, await TransformAsync(addedImages), await TransformAsync(removedImages));
TracksCountChanged?.Invoke(this, newTrackIds.Count);
}
async Task>> TransformAsync(IEnumerable ids)
{
var idArray = ids as string[] ?? ids.ToArray();
var collectionChangedItems = new List>(idArray.Length);
foreach (var id in idArray)
{
var track = await _fileMetadataManager.Tracks.GetByIdAsync(id);
Guard.IsNotNullOrWhiteSpace(track?.Id, nameof(track.Id));
collectionChangedItems.Add(new CollectionChangedItem(InstanceCache.Tracks.GetOrCreate(track.Id, SourceCore, track), collectionChangedItems.Count));
}
return collectionChangedItems;
}
}
private async Task HandleImagesChanged(HashSet oldImageIds, HashSet newImageIds)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
if (oldImageIds.OrderBy(s => s).SequenceEqual(newImageIds.OrderBy(s => s)))
{
// Lists have identical content, so no images have changed.
return;
}
var addedImages = newImageIds.Except(oldImageIds);
var removedImages = oldImageIds.Except(newImageIds);
if (oldImageIds.Count != newImageIds.Count)
{
ImagesChanged?.Invoke(this, await TransformAsync(addedImages), await TransformAsync(removedImages));
ImagesCountChanged?.Invoke(this, newImageIds.Count);
}
async Task>> TransformAsync(IEnumerable ids)
{
var idArray = ids as string[] ?? ids.ToArray();
var collectionChangedItems = new List>(idArray.Length);
foreach (var id in idArray)
{
var image = await _fileMetadataManager.Images.GetByIdAsync(id);
Guard.IsNotNullOrWhiteSpace(image?.Id, nameof(image.Id));
collectionChangedItems.Add(new CollectionChangedItem(InstanceCache.Images.GetOrCreate(image.Id, SourceCore, image), collectionChangedItems.Count));
}
return collectionChangedItems;
}
}
///
public event EventHandler? PlaybackStateChanged;
///
public event EventHandler? NameChanged;
///
public event EventHandler? DescriptionChanged;
///
public event EventHandler? DurationChanged;
///
public event EventHandler? LastPlayedChanged;
///
public event EventHandler? IsPlayTrackCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPauseTrackCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPlayAlbumCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPauseAlbumCollectionAsyncAvailableChanged;
///
public event EventHandler? IsChangeNameAsyncAvailableChanged;
///
public event EventHandler? IsChangeDescriptionAsyncAvailableChanged;
///
public event EventHandler? IsChangeDurationAsyncAvailableChanged;
///
public event EventHandler? ImagesCountChanged;
///
public event EventHandler? AlbumItemsCountChanged;
///
public event EventHandler? TracksCountChanged;
///
public event CollectionChangedEventHandler? ImagesChanged;
///
public event CollectionChangedEventHandler? AlbumItemsChanged;
///
public event CollectionChangedEventHandler? TracksChanged;
///
public event CollectionChangedEventHandler? GenresChanged;
///
public event EventHandler? GenresCountChanged;
///
public event CollectionChangedEventHandler? UrlsChanged;
///
public event EventHandler? UrlsCountChanged;
///
public string Id => _artistMetadata.Id ?? string.Empty;
///
public int TotalTrackCount => _artistMetadata.TrackIds?.Count ?? 0;
///
public int TotalAlbumItemsCount => _artistMetadata.AlbumIds?.Count ?? 0;
///
public int TotalGenreCount => _artistMetadata.Genres?.Count ?? 0;
///
public int TotalImageCount => _artistMetadata.ImageIds?.Count ?? 0;
///
public int TotalUrlCount => 0;
///
public ICore SourceCore { get; }
///
public string Name => _artistMetadata.Name ?? string.Empty;
///
public string Description => _artistMetadata.Description ?? string.Empty;
///
public PlaybackState PlaybackState => PlaybackState.None;
///
public TimeSpan Duration => new TimeSpan(0, 0, 0);
///
public DateTime? LastPlayed { get; }
///
public DateTime? AddedAt { get; }
///
public ICorePlayableCollectionGroup? RelatedItems => null;
///
public bool IsPlayTrackCollectionAsyncAvailable => false;
///
public bool IsPauseTrackCollectionAsyncAvailable => false;
///
public bool IsPlayAlbumCollectionAsyncAvailable => false;
///
public bool IsPauseAlbumCollectionAsyncAvailable => false;
///
public bool IsChangeNameAsyncAvailable => false;
///
public bool IsChangeDescriptionAsyncAvailable => false;
///
public bool IsChangeDurationAsyncAvailable => false;
///
public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsAddTrackAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsAddAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsAddGenreAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsRemoveTrackAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsRemoveAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsRemoveGenreAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
///
public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// A cancellation token that may be used to cancel the ongoing task.
///
public Task PauseTrackCollectionAsync(CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// A cancellation token that may be used to cancel the ongoing task.
///
public Task PlayTrackCollectionAsync(CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// A cancellation token that may be used to cancel the ongoing task.
///
public Task PauseAlbumCollectionAsync(CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// A cancellation token that may be used to cancel the ongoing task.
///
public Task PlayAlbumCollectionAsync(CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task PlayTrackCollectionAsync(ICoreTrack track, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task PlayAlbumCollectionAsync(ICoreAlbumCollectionItem albumItem, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task AddTrackAsync(ICoreTrack track, int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task AddAlbumItemAsync(ICoreAlbumCollectionItem album, int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task RemoveTrackAsync(int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task RemoveAlbumItemAsync(int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task AddGenreAsync(ICoreGenre genre, int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task RemoveGenreAsync(int index, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
///
public Task AddImageAsync(ICoreImage image, int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task AddUrlAsync(ICoreUrl image, int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public async IAsyncEnumerable GetAlbumItemsAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var albums = await _fileMetadataManager.Albums.GetAlbumsByArtistId(Id, offset, limit);
foreach (var album in albums)
{
Guard.IsNotNull(album.Id, nameof(album.Id));
yield return InstanceCache.Albums.GetOrCreate(album.Id, SourceCore, album);
}
}
///
public async IAsyncEnumerable GetTracksAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var tracks = await _fileMetadataManager.Tracks.GetTracksByArtistId(Id, offset, limit);
foreach (var track in tracks)
{
Guard.IsNotNullOrWhiteSpace(track?.Id, nameof(track.Id));
yield return InstanceCache.Tracks.GetOrCreate(track.Id, SourceCore, track);
}
}
///
public async IAsyncEnumerable GetImagesAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
if (_artistMetadata.ImageIds == null)
yield break;
foreach (var imageId in _artistMetadata.ImageIds)
{
var image = await _fileMetadataManager.Images.GetByIdAsync(imageId);
Guard.IsNotNull(image, nameof(image));
yield return InstanceCache.Images.GetOrCreate(imageId, SourceCore, image);
}
}
///
public async IAsyncEnumerable GetUrlsAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await Task.CompletedTask;
yield break;
}
///
public async IAsyncEnumerable GetGenresAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_artistMetadata, nameof(_artistMetadata));
foreach (var genre in _artistMetadata.Genres ?? Enumerable.Empty())
yield return new FilesCoreGenre(SourceCore, genre);
await Task.CompletedTask;
}
///
public ValueTask DisposeAsync()
{
DetachEvents();
return default;
}
}
}