Implemented tag refresh on a playlist. Made audio file scanning truly asynchronous. Added basic playlist sorting functionality.
This commit is contained in:
@@ -15,13 +15,13 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
|
||||
using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer;
|
||||
@@ -103,22 +103,50 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedPlaylistSongs, value);
|
||||
NotifySelectionDependentCommands();
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand PlaySongCommand => new AsyncRelayCommand(PlaySongAsync, AreSongsSelected);
|
||||
public ICommand NewPlaylistCommand => new AsyncRelayCommand(NewPlaylistAsync);
|
||||
public ICommand AddFilesCommand => new AsyncRelayCommand(AddFilesAsync);
|
||||
public ICommand AddFolderCommand => new AsyncRelayCommand(AddFolderAsync);
|
||||
public ICommand RemoveSongsCommand => new RelayCommand(RemoveSongs, AreSongsSelected);
|
||||
public ICommand CutSongsCommand => new RelayCommand(CutSongs, AreSongsSelected);
|
||||
public ICommand CopySongsCommand => new RelayCommand(CopySongs, AreSongsSelected);
|
||||
public ICommand PasteSongsCommand => new AsyncRelayCommand(PasteSongsAsync, CanPasteSongs);
|
||||
public ICommand OpenFileLocationCommand => new RelayCommand(OpenFileLocation, AreSongsSelected);
|
||||
public ICommand RefreshTagsCommand => new RelayCommand(RefreshTags);
|
||||
public ICommand RemoveMissingSongsCommand => new RelayCommand(RemoveMissingSongs);
|
||||
public ICommand RemoveDuplicateSongsCommand => new RelayCommand(RemoveDuplicateSongs);
|
||||
public ICommand DeletePlaylistCommand => new AsyncRelayCommand(DeletePlaylistAsync);
|
||||
public SortItem[] SortItems { get; } =
|
||||
[
|
||||
new SortItem
|
||||
{
|
||||
Name = "Album - Disc # - Track # - Title - File Name",
|
||||
SortOptions =
|
||||
[
|
||||
new(nameof(Song.Album), ListSortDirection.Ascending),
|
||||
new(nameof(Song.DiscNumber), ListSortDirection.Ascending),
|
||||
new(nameof(Song.TrackNumber), ListSortDirection.Ascending),
|
||||
new(nameof(Song.Title), ListSortDirection.Ascending),
|
||||
new(nameof(Song.FileName), ListSortDirection.Ascending)
|
||||
]
|
||||
},
|
||||
new SortItem
|
||||
{
|
||||
Name = "Title - File Name",
|
||||
SortOptions =
|
||||
[
|
||||
new(nameof(Song.Title), ListSortDirection.Ascending),
|
||||
new(nameof(Song.FileName), ListSortDirection.Ascending)
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
public IAsyncRelayCommand PlaySongCommand { get; }
|
||||
public IAsyncRelayCommand NewPlaylistCommand { get; }
|
||||
public IAsyncRelayCommand AddFilesCommand { get; }
|
||||
public IAsyncRelayCommand AddFolderCommand { get; }
|
||||
public IRelayCommand RemoveSongsCommand { get; }
|
||||
public IRelayCommand CutSongsCommand { get; }
|
||||
public IRelayCommand CopySongsCommand { get; }
|
||||
public IAsyncRelayCommand PasteSongsCommand { get; }
|
||||
public IRelayCommand OpenFileLocationCommand { get; }
|
||||
public IAsyncRelayCommand RefreshTagsCommand { get; }
|
||||
public IRelayCommand RemoveMissingSongsCommand { get; }
|
||||
public IRelayCommand RemoveDuplicateSongsCommand { get; }
|
||||
public IAsyncRelayCommand DeletePlaylistCommand { get; }
|
||||
public IRelayCommand<SortItem> SortAllCommand { get; }
|
||||
public IRelayCommand<SortItem> SortSelectedCommand { get; }
|
||||
|
||||
public bool IsUserUpdating { get; set; }
|
||||
private bool _isUserInitiatingSongChange;
|
||||
@@ -152,12 +180,39 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
_messageService = messageService;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
PlaySongCommand = new AsyncRelayCommand(PlaySongAsync, AreSongsSelected);
|
||||
NewPlaylistCommand = new AsyncRelayCommand(NewPlaylistAsync);
|
||||
AddFilesCommand = new AsyncRelayCommand(AddFilesAsync);
|
||||
AddFolderCommand = new AsyncRelayCommand(AddFolderAsync);
|
||||
RemoveSongsCommand = new RelayCommand(RemoveSongs, AreSongsSelected);
|
||||
CutSongsCommand = new RelayCommand(CutSongs, AreSongsSelected);
|
||||
CopySongsCommand = new RelayCommand(CopySongs, AreSongsSelected);
|
||||
PasteSongsCommand = new AsyncRelayCommand(PasteSongsAsync, CanPasteSongs);
|
||||
OpenFileLocationCommand = new RelayCommand(OpenFileLocation, AreSongsSelected);
|
||||
RefreshTagsCommand = new AsyncRelayCommand(RefreshTagsAsync);
|
||||
RemoveMissingSongsCommand = new RelayCommand(RemoveMissingSongs);
|
||||
RemoveDuplicateSongsCommand = new RelayCommand(RemoveDuplicateSongs);
|
||||
DeletePlaylistCommand = new AsyncRelayCommand(DeletePlaylistAsync);
|
||||
SortAllCommand = new RelayCommand<SortItem>(SortAllSongs);
|
||||
SortSelectedCommand = new RelayCommand<SortItem>(SortSelectedSongs, _ => AreMultipleSongsSelected());
|
||||
|
||||
FilteredPlaylistSongs.CollectionChanged += OnFilteredPlaylistSongsCollectionChanged;
|
||||
|
||||
Playlist = _playlistManager.CurrentPlaylist; // Testing
|
||||
UpdateFilteredSongs();
|
||||
}
|
||||
|
||||
private void NotifySelectionDependentCommands()
|
||||
{
|
||||
PlaySongCommand.NotifyCanExecuteChanged();
|
||||
RemoveSongsCommand.NotifyCanExecuteChanged();
|
||||
CutSongsCommand.NotifyCanExecuteChanged();
|
||||
CopySongsCommand.NotifyCanExecuteChanged();
|
||||
PasteSongsCommand.NotifyCanExecuteChanged();
|
||||
OpenFileLocationCommand.NotifyCanExecuteChanged();
|
||||
SortSelectedCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void OnFilteredPlaylistSongsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (IsUserUpdating == false)
|
||||
@@ -212,6 +267,7 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
{
|
||||
case PlaylistUpdateAction.Add:
|
||||
case PlaylistUpdateAction.Remove:
|
||||
case PlaylistUpdateAction.Refresh:
|
||||
_dispatcherQueue.TryEnqueue(UpdateFilteredSongs);
|
||||
break;
|
||||
case PlaylistUpdateAction.Move:
|
||||
@@ -381,6 +437,11 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
return SelectedPlaylistSongs.Count > 0;
|
||||
}
|
||||
|
||||
private bool AreMultipleSongsSelected()
|
||||
{
|
||||
return SelectedPlaylistSongs.Count > 1;
|
||||
}
|
||||
|
||||
private async Task NewPlaylistAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Playlist playlist = await _playlistManager.AddPlaylistAsync();
|
||||
@@ -538,9 +599,23 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
Process.Start("explorer.exe", argument);
|
||||
}
|
||||
|
||||
private void RefreshTags()
|
||||
private async Task RefreshTagsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//Playlist?.RefreshTags();
|
||||
if (Playlist is null)
|
||||
return;
|
||||
|
||||
// TODO: Have different method for selected songs versus all songs in the playlist
|
||||
PlaylistSong[] targets = SelectedPlaylistSongs.Count > 0
|
||||
? [.. SelectedPlaylistSongs]
|
||||
: [.. Playlist.Songs];
|
||||
|
||||
string[] fileNames = [.. targets
|
||||
.Select(ps => ps.Song.FileName)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)];
|
||||
|
||||
Song[] songs = await _audioFileScanner.GetSongsAsync(fileNames, cancellationToken);
|
||||
|
||||
Playlist.ImportTags(songs);
|
||||
}
|
||||
|
||||
private void RemoveMissingSongs()
|
||||
@@ -578,5 +653,30 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
private void SortAllSongs(SortItem? sortItem)
|
||||
{
|
||||
if (Playlist == null || sortItem == null)
|
||||
return;
|
||||
|
||||
Playlist.SortSongs([.. Playlist.Songs], [.. sortItem.SortOptions]);
|
||||
}
|
||||
|
||||
private void SortSelectedSongs(SortItem? sortItem)
|
||||
{
|
||||
if (Playlist == null || sortItem == null)
|
||||
return;
|
||||
|
||||
if (SelectedPlaylistSongs.Count < 2)
|
||||
return;
|
||||
|
||||
Playlist.SortSongs([.. SelectedPlaylistSongs], [.. sortItem.SortOptions]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class SortItem
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public IReadOnlyCollection<SortOption> SortOptions { get; set; } = [];
|
||||
}
|
||||
Reference in New Issue
Block a user