From cccf0b57a50062e89fa0e28349547dd4d1646fcc Mon Sep 17 00:00:00 2001 From: Brian Bicknell Date: Sun, 16 Feb 2025 19:07:07 -0500 Subject: [PATCH] Added playlist logic. --- Harmonia.Core/Events/PlaylistUpdateAction.cs | 21 +++ .../Events/PlaylistUpdatedEventArgs.cs | 12 ++ .../Extensions/EnumerableExtensions.cs | 34 ++++ Harmonia.Core/Models/Playlist.cs | 162 +++++++++++++++++- 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 Harmonia.Core/Events/PlaylistUpdateAction.cs create mode 100644 Harmonia.Core/Events/PlaylistUpdatedEventArgs.cs create mode 100644 Harmonia.Core/Extensions/EnumerableExtensions.cs diff --git a/Harmonia.Core/Events/PlaylistUpdateAction.cs b/Harmonia.Core/Events/PlaylistUpdateAction.cs new file mode 100644 index 0000000..695fd89 --- /dev/null +++ b/Harmonia.Core/Events/PlaylistUpdateAction.cs @@ -0,0 +1,21 @@ +namespace Harmonia.Core.Events; + +public enum PlaylistUpdateAction +{ + /// + /// An item was added to the collection. + /// + Add, + /// + /// An item was removed from the collection. + /// + Remove, + /// + /// An item was moved within the collection. + /// + Move, + /// + /// The contents of the collection changed dramatically. + /// + Reset +} \ No newline at end of file diff --git a/Harmonia.Core/Events/PlaylistUpdatedEventArgs.cs b/Harmonia.Core/Events/PlaylistUpdatedEventArgs.cs new file mode 100644 index 0000000..45e3fdb --- /dev/null +++ b/Harmonia.Core/Events/PlaylistUpdatedEventArgs.cs @@ -0,0 +1,12 @@ +using Harmonia.Core.Models; + +namespace Harmonia.Core.Events; + +public class PlaylistUpdatedEventArgs : EventArgs +{ + public required PlaylistUpdateAction Action { get; init; } + public required int Index { get; init; } + public int? NewIndex { get; init; } = -1; + public required int Count { get; init; } + public required PlaylistSong[] Songs { get; init; } = []; +} \ No newline at end of file diff --git a/Harmonia.Core/Extensions/EnumerableExtensions.cs b/Harmonia.Core/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..fb699cf --- /dev/null +++ b/Harmonia.Core/Extensions/EnumerableExtensions.cs @@ -0,0 +1,34 @@ +using Harmonia.Core.Models; +using System.ComponentModel; +using System.Reflection; + +namespace Harmonia.Core.Extensions; + +public static class EnumerableExtensions +{ + public static IOrderedEnumerable SortBy(this IEnumerable source, SortOption[] sortOptions) + { + IOrderedEnumerable orderedQuery = source.OrderBy(x => 0); + Type type = typeof(T); + + foreach (SortOption sortOption in sortOptions) + { + PropertyInfo? property = type.GetProperty(sortOption.FieldName); + + if (property == null) + continue; + + switch (sortOption.Direction) + { + case ListSortDirection.Ascending: + orderedQuery = orderedQuery.ThenBy(x => property.GetValue(x)); + break; + case ListSortDirection.Descending: + orderedQuery = orderedQuery.ThenByDescending(x => property.GetValue(x)); + break; + } + } + + return orderedQuery; + } +} \ No newline at end of file diff --git a/Harmonia.Core/Models/Playlist.cs b/Harmonia.Core/Models/Playlist.cs index a886b4f..dafe6bd 100644 --- a/Harmonia.Core/Models/Playlist.cs +++ b/Harmonia.Core/Models/Playlist.cs @@ -1,4 +1,7 @@ -namespace Harmonia.Core.Models; +using Harmonia.Core.Events; +using Harmonia.Core.Extensions; + +namespace Harmonia.Core.Models; public class Playlist(string name) { @@ -8,4 +11,161 @@ public class Playlist(string name) public List GroupOptions { get; set; } = []; public List SortOptions { get; set; } = []; public bool IsLocked { get; set; } + + public event EventHandler? PlaylistUpdated; + + public void AddSong(Song song, int? index) + { + AddSongs([song], index); + } + + public void AddSongs(Song[] songs, int? index) + { + PlaylistSong[] playlistSongs = songs.Select(song => new PlaylistSong(song)).ToArray(); + + AddSongs(playlistSongs, index); + } + + private void AddSongs(PlaylistSong[] playlistSongs, int? index) + { + if (IsLocked) + return; + + int insertIndex = index ?? Songs.Count; + + Songs.InsertRange(insertIndex, playlistSongs); + + PlaylistUpdatedEventArgs eventArgs = new() + { + Action = PlaylistUpdateAction.Add, + Index = insertIndex, + Count = playlistSongs.Length, + Songs = playlistSongs + }; + + PlaylistUpdated?.Invoke(this, eventArgs); + } + + public void MoveSong(PlaylistSong playlistSong, int newIndex) + { + int currentIndex = Songs.IndexOf(playlistSong); + + MoveSong(currentIndex, newIndex); + } + + public void MoveSong(int oldIndex, int newIndex) + { + if (IsLocked) + return; + + if (oldIndex == newIndex) + return; + + PlaylistSong playlistSong = Songs[oldIndex]; + + Songs.Remove(playlistSong); + Songs.Insert(newIndex, playlistSong); + + PlaylistUpdatedEventArgs eventArgs = new() + { + Action = PlaylistUpdateAction.Move, + Index = oldIndex, + Count = 1, + Songs = [playlistSong], + NewIndex = newIndex + }; + + PlaylistUpdated?.Invoke(this, eventArgs); + } + + public void SortSongs(PlaylistSong[] playlistSongs, SortOption[] sortOptions) + { + Dictionary oldPlaylistSongs = playlistSongs + .OrderBy(Songs.IndexOf) + .ToDictionary(Songs.IndexOf, playlistSong => playlistSong); + + Song[] songs = playlistSongs.Select(playlistSong => playlistSong.Song).ToArray(); + Song[] sortedSongs = [.. songs.SortBy(sortOptions)]; + + int currentSortIndex = 0; + + foreach (int index in oldPlaylistSongs.Keys) + { + PlaylistSong playlistSong = oldPlaylistSongs[index]; + Song sortedSong = sortedSongs[currentSortIndex++]; + PlaylistSong newPlaylistSong = playlistSongs.First(ps => ps.Song == sortedSong); + + if (newPlaylistSong == playlistSong) + continue; + + Songs.Remove(playlistSong); + Songs.Insert(index, newPlaylistSong); + } + + PlaylistUpdatedEventArgs eventArgs = new() + { + Action = PlaylistUpdateAction.Reset, + Index = -1, + Count = playlistSongs.Length, + Songs = playlistSongs + }; + + PlaylistUpdated?.Invoke(this, eventArgs); + } + + public void RemoveSong(int index) + { + RemoveSongs(index, 1); + } + + public void RemoveSongs(int index, int count) + { + if (IsLocked) + return; + + PlaylistSong[] playlistSongs = [.. Songs.GetRange(index, count)]; + + Songs.RemoveRange(index, count); + + PlaylistUpdatedEventArgs eventArgs = new() + { + Action = PlaylistUpdateAction.Remove, + Index = index, + Count = count, + Songs = playlistSongs + }; + + PlaylistUpdated?.Invoke(this, eventArgs); + } + + public void RemoveSong(PlaylistSong playlistSong) + { + RemoveSongs([playlistSong]); + } + + public void RemoveSongs(PlaylistSong[] playlistSongs) + { + List removedSongs = []; + + foreach (PlaylistSong playlistSong in playlistSongs) + { + if (Songs.Remove(playlistSong)) + { + removedSongs.Add(playlistSong); + } + } + + if (removedSongs.Count == 0) + return; + + PlaylistUpdatedEventArgs eventArgs = new() + { + Action = PlaylistUpdateAction.Remove, + Index = -1, + Count = removedSongs.Count, + Songs = playlistSongs + }; + + PlaylistUpdated?.Invoke(this, eventArgs); + } } \ No newline at end of file