Files
WindowsMusicPlayer-TheUntam…/The Untamed Music Player/ViewModels/PlayQueueViewModel.cs
2025-09-02 22:23:21 +08:00

396 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.ObjectModel;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using The_Untamed_Music_Player.Contracts.Models;
using The_Untamed_Music_Player.Helpers;
using The_Untamed_Music_Player.Models;
using The_Untamed_Music_Player.Views;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.Storage.Pickers;
using WinRT.Interop;
using ZLinq;
namespace The_Untamed_Music_Player.ViewModels;
public partial class PlayQueueViewModel : ObservableObject
{
private IndexedPlayQueueSong? _currentSong;
[ObservableProperty]
public partial ObservableCollection<IndexedPlayQueueSong> PlayQueue { get; set; } =
Data.MusicPlayer.ShuffleMode
? Data.MusicPlayer.ShuffledPlayQueue
: Data.MusicPlayer.PlayQueue;
[ObservableProperty]
public partial bool IsButtonEnabled { get; set; } = false;
public PlayQueueViewModel()
{
IsButtonEnabled = PlayQueue.Count > 0;
Data.MusicPlayer.PropertyChanged += MusicPlayer_PropertyChanged;
}
private void MusicPlayer_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (
e.PropertyName == nameof(Data.MusicPlayer.ShuffleMode)
|| e.PropertyName == nameof(Data.MusicPlayer.PlayQueue)
|| e.PropertyName == nameof(Data.MusicPlayer.ShuffledPlayQueue)
)
{
PlayQueue = Data.MusicPlayer.ShuffleMode
? Data.MusicPlayer.ShuffledPlayQueue
: Data.MusicPlayer.PlayQueue;
IsButtonEnabled = PlayQueue.Count > 0;
}
}
public async void AddToPlaylistFlyoutButton_Click(PlaylistInfo playlist)
{
var songList = PlayQueue.AsValueEnumerable().Select(song => song.Song).ToArray();
await Data.PlaylistLibrary.AddToPlaylist(playlist, songList);
}
public void AddToPlayQueueFlyoutButton_Click()
{
var songList = PlayQueue.AsValueEnumerable().Select(song => song.Song).ToArray();
Data.MusicPlayer.AddSongsToPlayQueue(songList);
}
public void PlayQueueListView_ItemClick(object sender, ItemClickEventArgs e)
{
if (e.ClickedItem is IndexedPlayQueueSong info)
{
Data.MusicPlayer.PlaySongByIndexedInfo(info);
}
}
public void PlayButton_Click(IndexedPlayQueueSong info)
{
Data.MusicPlayer.PlaySongByIndexedInfo(info);
}
public void PlayNextButton_Click(IBriefSongInfoBase info)
{
Data.MusicPlayer.AddSongToNextPlay(info);
}
public void AddToPlayQueueButton_Click(IBriefSongInfoBase info)
{
Data.MusicPlayer.AddSongToPlayQueue(info);
}
public async void AddToPlaylistButton_Click(IBriefSongInfoBase info, PlaylistInfo playlist)
{
await Data.PlaylistLibrary.AddToPlaylist(playlist, info);
}
public async void RemoveButton_Click(IndexedPlayQueueSong info)
{
await Data.MusicPlayer.RemoveSong(info);
}
public void MoveUpButton_Click(IndexedPlayQueueSong info)
{
Data.MusicPlayer.MoveUpSong(info);
}
public void MoveDownButton_Click(IndexedPlayQueueSong info)
{
Data.MusicPlayer.MoveDownSong(info);
}
public async void ShowAlbumButton_Click(IBriefSongInfoBase info)
{
if (info is BriefLocalSongInfo localInfo)
{
var localAlbumInfo = Data.MusicLibrary.GetAlbumInfoBySong(localInfo.Album);
if (localAlbumInfo is not null)
{
Data.SelectedLocalAlbum = localAlbumInfo;
Data.ShellPage!.Navigate(
nameof(LocalAlbumDetailPage),
"",
new SuppressNavigationTransitionInfo()
);
}
}
else if (info is IBriefOnlineSongInfo onlineInfo)
{
var onlineAlbumInfo = await IBriefOnlineAlbumInfo.CreateFromSongInfoAsync(onlineInfo);
if (onlineAlbumInfo is not null)
{
Data.SelectedOnlineAlbum = onlineAlbumInfo;
Data.ShellPage!.Navigate(
nameof(OnlineAlbumDetailPage),
"",
new SuppressNavigationTransitionInfo()
);
}
}
}
public async void ShowArtistButton_Click(IBriefSongInfoBase info)
{
if (info is BriefLocalSongInfo localInfo)
{
var localArtistInfo = Data.MusicLibrary.GetArtistInfoBySong(localInfo.Artists[0]);
if (localArtistInfo is not null)
{
Data.SelectedLocalArtist = localArtistInfo;
Data.ShellPage!.Navigate(
nameof(LocalArtistDetailPage),
"",
new SuppressNavigationTransitionInfo()
);
}
}
else if (info is IBriefOnlineSongInfo onlineInfo)
{
var onlineArtistInfo = await IBriefOnlineArtistInfo.CreateFromSongInfoAsync(onlineInfo);
if (onlineArtistInfo is not null)
{
Data.SelectedOnlineArtist = onlineArtistInfo;
Data.ShellPage!.Navigate(
nameof(OnlineArtistDetailPage),
"",
new SuppressNavigationTransitionInfo()
);
}
}
}
public void ClearButton_Click(object sender, RoutedEventArgs e)
{
Data.MusicPlayer.ClearPlayQueue();
}
public async Task AddFilesButton_Click()
{
var picker = new FileOpenPicker { SuggestedStartLocation = PickerLocationId.MusicLibrary };
foreach (var type in Data.SupportedAudioTypes)
{
picker.FileTypeFilter.Add(type);
}
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
InitializeWithWindow.Initialize(picker, hwnd);
var files = await picker.PickMultipleFilesAsync();
if (files.Count > 0)
{
await AddExternalFilesToPlayQueue([.. files], PlayQueue.Count);
}
}
public async Task AddFolderButton_Click()
{
var picker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.MusicLibrary,
FileTypeFilter = { "*" },
};
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
InitializeWithWindow.Initialize(picker, hwnd);
var folder = await picker.PickSingleFolderAsync();
if (folder is not null)
{
List<StorageFile>? musicFiles = null;
await Task.Run(async () =>
{
musicFiles = await GetMusicFilesFromFolderAsync(folder);
});
if (musicFiles?.Count > 0)
{
await AddExternalFilesToPlayQueue(musicFiles, PlayQueue.Count);
}
}
}
public void AddUrlButton_Click(string url)
{
var songInfo = new BriefUnknownSongInfo(new Uri(url));
if (!songInfo.IsPlayAvailable)
{
return;
}
if (PlayQueue.Count > 0)
{
Data.MusicPlayer.AddSongToPlayQueue(songInfo);
}
else
{
Data.MusicPlayer.SetPlayQueue("UnknownOnlineSongs:Part", [songInfo]);
Data.MusicPlayer.PlaySongByInfo(songInfo);
}
IsButtonEnabled = PlayQueue.Count > 0;
}
public void PlayQueueListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
_currentSong = PlayQueue[Data.MusicPlayer.PlayQueueIndex];
if (e.Items.Count > 0)
{
e.Data.RequestedOperation = DataPackageOperation.Move;
}
}
public void PlayQueueListView_DragOver(object sender, DragEventArgs e)
{
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
e.AcceptedOperation = DataPackageOperation.Copy;
e.DragUIOverride.Caption = "PlayQueue_AddToPlayQueue".GetLocalized();
e.DragUIOverride.IsCaptionVisible = true;
e.DragUIOverride.IsContentVisible = true;
e.DragUIOverride.IsGlyphVisible = false;
}
}
public void PlayQueueListView_DragItemsCompleted(
object sender,
DragItemsCompletedEventArgs args
)
{
// 检查是否是重排序操作Move操作且在同一个ListView内
if (args.DropResult == DataPackageOperation.Move && args.Items.Count > 0)
{
var songs = args.Items.AsValueEnumerable().Cast<IndexedPlayQueueSong>().ToArray();
if (songs.Length == 0)
{
return;
}
var oldIndex = songs[0].Index;
var newIndex = PlayQueue.IndexOf(songs[0]);
if (oldIndex == newIndex)
{
return;
}
for (var i = 0; i < PlayQueue.Count; i++)
{
PlayQueue[i].Index = i;
}
Data.MusicPlayer.PlayQueueIndex = _currentSong!.Index;
}
}
public async void PlayQueueListView_Drop(object sender, DragEventArgs e)
{
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var def = e.GetDeferral();
var items = await e.DataView.GetStorageItemsAsync();
var musicFiles = new List<StorageFile>();
await Task.Run(async () =>
{
foreach (var item in items)
{
if (item is StorageFile file)
{
var extension = Path.GetExtension(file.Path).ToLowerInvariant();
if (Data.SupportedAudioTypes.Contains(extension))
{
musicFiles.Add(file);
}
}
else if (item is StorageFolder folder)
{
var folderFiles = await GetMusicFilesFromFolderAsync(folder);
musicFiles.AddRange(folderFiles);
}
}
});
if (musicFiles.Count > 0)
{
var listView = (ListView)sender;
var position = e.GetPosition(listView.ItemsPanelRoot);
var index = 0;
if (listView.Items.Count > 0)
{
var sampleItem = (ListViewItem)listView.ContainerFromIndex(0);
var itemHeight =
sampleItem.ActualHeight + sampleItem.Margin.Top + sampleItem.Margin.Bottom;
if (itemHeight > 0)
{
var calculatedIndex = (int)(position.Y / itemHeight);
index =
calculatedIndex >= listView.Items.Count
? listView.Items.Count
: calculatedIndex;
index = Math.Min(listView.Items.Count, Math.Max(0, index));
}
}
await AddExternalFilesToPlayQueue(musicFiles, index);
}
def.Complete();
}
}
private static async Task<List<StorageFile>> GetMusicFilesFromFolderAsync(StorageFolder folder)
{
var musicFiles = new List<StorageFile>();
try
{
var files = await folder.GetFilesAsync();
foreach (var file in files)
{
var extension = Path.GetExtension(file.Path).ToLowerInvariant();
if (Data.SupportedAudioTypes.Contains(extension))
{
musicFiles.Add(file);
}
}
var subFolders = await folder.GetFoldersAsync();
foreach (var subFolder in subFolders)
{
var subFiles = await GetMusicFilesFromFolderAsync(subFolder);
musicFiles.AddRange(subFiles);
}
}
catch { }
return musicFiles;
}
public async Task AddExternalFilesToPlayQueue(List<StorageFile> files, int insertIndex)
{
var newSongs = new List<IBriefSongInfoBase>();
await Task.Run(() =>
{
foreach (var file in files)
{
try
{
var folder = Path.GetDirectoryName(file.Path) ?? "";
var songInfo = new BriefLocalSongInfo(file.Path, folder);
if (songInfo.IsPlayAvailable)
{
newSongs.Add(songInfo);
}
}
catch { }
}
});
if (newSongs.Count > 0)
{
if (PlayQueue.Count > 0)
{
Data.MusicPlayer.InsertSongsToPlayQueue(newSongs, insertIndex);
}
else
{
Data.MusicPlayer.SetPlayQueue("LocalSongs:Part", newSongs);
Data.MusicPlayer.PlaySongByInfo(newSongs[0]);
}
}
IsButtonEnabled = PlayQueue.Count > 0;
}
}