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.Sdk.CoreModels; using StrixMusic.Sdk.Extensions; using StrixMusic.Sdk.FileMetadata; using StrixMusic.Sdk.FileMetadata.Models; using StrixMusic.Sdk.MediaPlayback; namespace StrixMusic.Cores.Files.Models { /// /// Wraps around to provide playlist information extracted from a file to the Strix SDK. /// public sealed class LocalFilesCorePlaylist : ICorePlaylist { private readonly IFileMetadataManager? _fileMetadataManager; private PlaylistMetadata _playlistMetadata; /// /// Creates a new instance of /// public LocalFilesCorePlaylist(ICore sourceCore, PlaylistMetadata playlistMetadata) { SourceCore = sourceCore; _playlistMetadata = playlistMetadata; Guard.IsNotNull(playlistMetadata, nameof(playlistMetadata)); Guard.IsNotNullOrWhiteSpace(playlistMetadata.Id, nameof(playlistMetadata.Id)); Id = playlistMetadata.Id; _fileMetadataManager = sourceCore.Cast().FileMetadataManager; Duration = playlistMetadata.Duration ?? default; AttachEvents(); } private void AttachEvents() { Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager)); _fileMetadataManager.Playlists.MetadataUpdated += Albums_MetadataUpdated; } private void DetachEvents() { Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager)); _fileMetadataManager.Playlists.MetadataUpdated -= Albums_MetadataUpdated; } /// public event EventHandler? IsPlayTrackCollectionAsyncAvailableChanged; /// public event EventHandler? IsPauseTrackCollectionAsyncAvailableChanged; /// public event EventHandler? TracksCountChanged; /// 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 CollectionChangedEventHandler? TracksChanged; /// public event CollectionChangedEventHandler? ImagesChanged; /// public event CollectionChangedEventHandler? UrlsChanged; /// public ICore SourceCore { get; } /// public string Id { get; set; } /// public string Name => _playlistMetadata.Title ?? string.Empty; /// public string? Description => _playlistMetadata.Description; /// public DateTime? LastPlayed { get; } /// public PlaybackState PlaybackState { get; } /// public TimeSpan Duration { get; private set; } /// public bool IsChangeNameAsyncAvailable => false; /// public bool IsChangeDescriptionAsyncAvailable => false; /// public bool IsChangeDurationAsyncAvailable => false; /// public ICoreUserProfile? Owner { get; } /// public ICorePlayableCollectionGroup? RelatedItems { get; } /// public DateTime? AddedAt { get; } /// public int TotalTrackCount => _playlistMetadata.TrackIds?.Count ?? 0; /// public int TotalImageCount { get; } /// public int TotalUrlCount { get; } /// public bool IsPlayTrackCollectionAsyncAvailable => false; /// public bool IsPauseTrackCollectionAsyncAvailable => false; /// public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsAddTrackAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsRemoveTrackAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task AddImageAsync(ICoreImage image, int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task AddUrlAsync(ICoreUrl image, int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task AddTrackAsync(ICoreTrack track, int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task RemoveTrackAsync(int index, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task PlayTrackCollectionAsync(ICoreTrack track, 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 PauseTrackCollectionAsync(CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public async IAsyncEnumerable GetTracksAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default) { Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager)); var trackIds = _playlistMetadata.TrackIds; Guard.IsNotNull(trackIds, nameof(trackIds)); foreach (var id in trackIds) { cancellationToken.ThrowIfCancellationRequested(); var track = await _fileMetadataManager.Tracks.GetByIdAsync(id); Guard.IsNotNull(track, nameof(track)); yield return new FilesCoreTrack(SourceCore, track); } } /// public IAsyncEnumerable GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// public IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); } private void Albums_MetadataUpdated(object sender, IEnumerable e) { foreach (var metadata in e) { if (metadata.Id != Id) return; var previousData = _playlistMetadata; _playlistMetadata = metadata; if (metadata.Title != previousData.Title) NameChanged?.Invoke(this, Name); if (metadata.Description != previousData.Description) DescriptionChanged?.Invoke(this, Description); if (metadata.Duration != previousData.Duration) DurationChanged?.Invoke(this, Duration); if (metadata.TrackIds?.Count != previousData.TrackIds?.Count) TracksCountChanged?.Invoke(this, metadata.TrackIds?.Count ?? 0); } } /// public ValueTask DisposeAsync() { DetachEvents(); return default; } } }