diff --git a/.editorconfig b/.editorconfig index fd05618..fa67d48 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,7 +45,7 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false #Formatting - wrapping options #leave code block on separate lines -csharp_preserve_single_line_blocks = false +csharp_preserve_single_line_blocks = true #Style - Code block preferences diff --git a/OnlineMusicApi/OnlineMusicApi.csproj b/OnlineMusicApi/OnlineMusicApi.csproj deleted file mode 100644 index e2f49b7..0000000 --- a/OnlineMusicApi/OnlineMusicApi.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - OnlineMusicApi - OnlineMusicApi - 在线音乐API - Copyright © 2019 Binaryify / Wwh - 3.25.3.0 - net9.0 - true - ../OnlineMusicApi.snk - False - IDE1006;CS0436;CS3021 - true - ..\bin\$(Configuration) - OnlineMusicApi - Binaryify / Wwh - $(Version) - https://github.com/wwh1004/OnlineMusicApi - MIT - true - true - snupkg - preview - Exe - - - - - - diff --git a/OnlineMusicApi/QueryCollection.cs b/OnlineMusicApi/QueryCollection.cs deleted file mode 100644 index bddfa1e..0000000 --- a/OnlineMusicApi/QueryCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace NeteaseCloudMusicApi; - -internal sealed class QueryCollection : List> -{ - public void Add(string key, string value) => Add(new KeyValuePair(key, value)); -} diff --git a/The Untamed Music Player.sln b/The Untamed Music Player.sln index 52904be..271b8ef 100644 --- a/The Untamed Music Player.sln +++ b/The Untamed Music Player.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.10.35027.167 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "The Untamed Music Player", "The Untamed Music Player\The Untamed Music Player.csproj", "{FAF574F3-DB0A-4B33-BF19-45CF6396C9F7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnlineMusicApi", "OnlineMusicApi\OnlineMusicApi.csproj", "{BA37573B-D0FC-4FF6-8D06-5F59F348B90B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,22 +41,6 @@ Global {FAF574F3-DB0A-4B33-BF19-45CF6396C9F7}.Release|x86.ActiveCfg = Release|x86 {FAF574F3-DB0A-4B33-BF19-45CF6396C9F7}.Release|x86.Build.0 = Release|x86 {FAF574F3-DB0A-4B33-BF19-45CF6396C9F7}.Release|x86.Deploy.0 = Release|x86 - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|arm64.ActiveCfg = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|arm64.Build.0 = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|x64.ActiveCfg = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|x64.Build.0 = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|x86.ActiveCfg = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Debug|x86.Build.0 = Debug|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|Any CPU.Build.0 = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|arm64.ActiveCfg = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|arm64.Build.0 = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|x64.ActiveCfg = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|x64.Build.0 = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|x86.ActiveCfg = Release|Any CPU - {BA37573B-D0FC-4FF6-8D06-5F59F348B90B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/The Untamed Music Player/App.xaml.cs b/The Untamed Music Player/App.xaml.cs index 1f37907..945f19a 100644 --- a/The Untamed Music Player/App.xaml.cs +++ b/The Untamed Music Player/App.xaml.cs @@ -85,6 +85,7 @@ public partial class App : Application services.AddSingleton(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); // Configuration diff --git a/The Untamed Music Player/Contracts/Models/IMusicInfoBase.cs b/The Untamed Music Player/Contracts/Models/IMusicInfoBase.cs new file mode 100644 index 0000000..dec4732 --- /dev/null +++ b/The Untamed Music Player/Contracts/Models/IMusicInfoBase.cs @@ -0,0 +1,90 @@ +using Microsoft.UI; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; + +namespace The_Untamed_Music_Player.Contracts.Models; +public interface IBriefMusicInfoBase +{ + string Path { get; set; } + string Title { get; set; } + string Album { get; set; } + string ArtistsStr { get; set; } + string DurationStr { get; set; } + string YearStr { get; set; } + + /// + /// 获取参与创作的艺术家名字符串 + /// + /// + /// + static string GetArtistsStr(string[] artists) => string.Join(", ", artists); + + /// + /// 获取时长字符串 + /// + /// + static string GetDurationStr(TimeSpan duration) => duration.Hours > 0 ? $"{duration:hh\\:mm\\:ss}" : $"{duration:mm\\:ss}"; + + /// + /// 获取发行年份字符串 + /// + /// + /// + static string GetYearStr(ushort year) => year is 0 ? "" : year.ToString(); + + /// + /// 获取文本前景色 + /// + /// + /// + /// 如果是当前播放歌曲, 返回主题色, 如果不是, 根据当前主题返回黑色或白色 + SolidColorBrush GetTextForeground(IDetailedMusicInfoBase currentMusic, bool isDarkTheme) + { + var isCurrentMusic = Path == currentMusic.Path; + if (isCurrentMusic) + { + var color = isDarkTheme ? ColorHelper.FromArgb(0xFF, 0x42, 0x9C, 0xE3) : ColorHelper.FromArgb(0xFF, 0x00, 0x5A, 0x9E); + return new SolidColorBrush(color); + } + return new SolidColorBrush(isDarkTheme ? Colors.White : Colors.Black); + } +} + +public interface IDetailedMusicInfoBase : IBriefMusicInfoBase +{ + bool IsOnline { get; set; } + string GenreStr { get; set; } + string ItemType { get; set; } + string AlbumArtistsStr { get; set; } + string ArtistAndAlbumStr { get; set; } + BitmapImage? Cover { get; set; } + string BitRate { get; set; } + string Track { get; set; } + string Lyric { get; set; } + + /// + /// 获取专辑艺术家字符串 + /// + /// + /// + static string GetAlbumArtistsStr(string[] albumArtists) => string.Join(", ", albumArtists); + + /// + /// 获取艺术家和专辑名字符串 + /// + /// + /// + /// + static string GetArtistAndAlbumStr(string album, string artistsStr) + { + if (string.IsNullOrEmpty(artistsStr)) + { + return album ?? ""; + } + if (string.IsNullOrEmpty(album)) + { + return artistsStr; + } + return $"{artistsStr} • {album}"; + } +} diff --git a/The Untamed Music Player/Contracts/Models/IOnlineMusicInfo.cs b/The Untamed Music Player/Contracts/Models/IOnlineMusicInfo.cs new file mode 100644 index 0000000..a82badf --- /dev/null +++ b/The Untamed Music Player/Contracts/Models/IOnlineMusicInfo.cs @@ -0,0 +1,11 @@ +namespace The_Untamed_Music_Player.Contracts.Models; +public interface IBriefOnlineMusicInfo : IBriefMusicInfoBase +{ + bool IsAvailable { get; set; } + long ID { get; set; } + long AlbumID { get; set; } +} + +public interface IDetailedOnlineMusicInfo : IBriefOnlineMusicInfo, IDetailedMusicInfoBase +{ +} \ No newline at end of file diff --git a/The Untamed Music Player/Contracts/Models/IOnlineMusicInfoList.cs b/The Untamed Music Player/Contracts/Models/IOnlineMusicInfoList.cs new file mode 100644 index 0000000..d280627 --- /dev/null +++ b/The Untamed Music Player/Contracts/Models/IOnlineMusicInfoList.cs @@ -0,0 +1,13 @@ +using System.Collections.ObjectModel; +using The_Untamed_Music_Player.Models; + +namespace The_Untamed_Music_Player.Contracts.Models; +public abstract class IBriefOnlineMusicInfoList : ObservableCollection +{ + protected string _keyWords = ""; + public bool HasAllLoaded { get; set; } = false; + + public abstract Task SearchAsync(string keyWords); + public abstract Task SearchMore(); + public abstract Task> GetSearchResultAsync(string keyWords); +} \ No newline at end of file diff --git a/The Untamed Music Player/Models/AlbumInfo.cs b/The Untamed Music Player/Models/AlbumInfo.cs index 2189857..2c6bcd2 100644 --- a/The Untamed Music Player/Models/AlbumInfo.cs +++ b/The Untamed Music Player/Models/AlbumInfo.cs @@ -7,7 +7,7 @@ public class BriefAlbumInfo(AlbumInfo albumInfo) public string Name { get; set; } = albumInfo.Name; public string YearStr { get; set; } = albumInfo.Year == 0 ? "AlbumInfo_UnknownYear".GetLocalized() : albumInfo.Year.ToString(); public BitmapImage? Cover { get; set; } = albumInfo.Cover; - public List SongList { get; set; } = [.. Data.MusicLibrary.GetMusicsByAlbum(albumInfo)]; + public List SongList { get; set; } = [.. Data.MusicLibrary.GetSongsByAlbum(albumInfo)]; } public class AlbumInfo diff --git a/The Untamed Music Player/Models/Data.cs b/The Untamed Music Player/Models/Data.cs index 0280e78..90030d9 100644 --- a/The Untamed Music Player/Models/Data.cs +++ b/The Untamed Music Player/Models/Data.cs @@ -11,14 +11,10 @@ public static class Data /// public static bool NotFirstUsed { get; set; } = false; - public static AlbumInfo? SelectedAlbum - { - get; set; - } - public static ArtistInfo? SelectedArtist - { - get; set; - } + public static bool HasMusicLibraryLoaded { get; set; } = false; + + public static AlbumInfo? SelectedAlbum { get; set; } + public static ArtistInfo? SelectedArtist { get; set; } /// /// 软件显示名称 @@ -43,63 +39,25 @@ public static class Data /// /// 是否显示歌词背景 /// - public static bool IsLyricBackgroundVisible = false; + public static bool IsLyricBackgroundVisible { get; set; } = false; - public static MusicPlayer MusicPlayer { get; set; } = new(); + public static OnlineMusicLibrary OnlineMusicLibrary { get; set; } = new(); public static MusicLibrary MusicLibrary { get; set; } = new(); - public static bool hasMusicLibraryLoaded { get; set; } = false; + public static MusicPlayer MusicPlayer { get; set; } = new(); + public static MainWindow? MainWindow { get; set; } + public static ShellPage? ShellPage { get; set; } + public static HomePage HomePage { get; set; } = null!; + public static MusicLibraryPage? MusicLibraryPage { get; set; } + public static LyricPage? LyricPage { get; set; } + public static RootPlayBarView? RootPlayBarView { get; set; } + public static DesktopLyricWindow? DesktopLyricWindow { get; set; } - public static MainWindow? MainWindow - { - get; set; - } - public static MainViewModel? MainViewModel - { - get; set; - } - public static ShellPage? ShellPage - { - get; set; - } - public static MusicLibraryPage? MusicLibraryPage - { - get; set; - } - public static LyricPage? LyricPage - { - get; set; - } - public static RootPlayBarView? RootPlayBarView - { - get; set; - } - public static DesktopLyricWindow? DesktopLyricWindow - { - get; set; - } - public static SettingsViewModel? SettingsViewModel - { - get; set; - } - public static ShellViewModel? ShellViewModel - { - get; set; - } - public static RootPlayBarViewModel? RootPlayBarViewModel - { - get; set; - } - public static HaveMusicViewModel? HaveMusicViewModel - { - get; set; - } - public static LocalSongsViewModel? LocalSongsViewModel - { - get; set; - } - public static LocalAlbumsViewModel? LocalAlbumsViewModel - { - get; set; - } -} + public static MainViewModel? MainViewModel { get; set; } + public static SettingsViewModel? SettingsViewModel { get; set; } + public static ShellViewModel? ShellViewModel { get; set; } + public static RootPlayBarViewModel? RootPlayBarViewModel { get; set; } + public static HaveMusicViewModel? HaveMusicViewModel { get; set; } + public static LocalSongsViewModel? LocalSongsViewModel { get; set; } + public static LocalAlbumsViewModel? LocalAlbumsViewModel { get; set; } +} \ No newline at end of file diff --git a/The Untamed Music Player/Models/MusicInfo.cs b/The Untamed Music Player/Models/MusicInfo.cs index d4bf0aa..0dcc55e 100644 --- a/The Untamed Music Player/Models/MusicInfo.cs +++ b/The Untamed Music Player/Models/MusicInfo.cs @@ -3,11 +3,12 @@ using System.Runtime.InteropServices.WindowsRuntime; using Microsoft.UI; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; +using The_Untamed_Music_Player.Contracts.Models; using The_Untamed_Music_Player.Helpers; using Windows.Storage.Streams; namespace The_Untamed_Music_Player.Models; -public class BriefMusicInfo +public class BriefMusicInfo : IBriefMusicInfoBase { /// /// 歌手分隔符 @@ -25,20 +26,15 @@ public class BriefMusicInfo public string Folder { get; set; } = ""; /// - /// 项目类型 + /// 歌曲名 /// - public string ItemType { get; set; } = ""; + public string Title { get; set; } = ""; /// /// 专辑名, 为空时返回"未知专辑" /// public virtual string Album { get; set; } = ""; - /// - /// 歌曲名 - /// - public string Title { get; set; } = ""; - /// /// 参与创作的艺术家数组 /// @@ -61,7 +57,7 @@ public class BriefMusicInfo public TimeSpan Duration { get; set; } = TimeSpan.Zero; /// - /// 时长字符串 + /// 时长字符串, 为空时返回00:00 /// public virtual string DurationStr { get; set; } = ""; @@ -71,17 +67,14 @@ public class BriefMusicInfo public ushort Year { get; set; } = 0; /// - /// 发行年份字符串, 为0时返回空字符串 + /// 发行年份字符串, 为0时返回"" /// public string YearStr { get; set; } = ""; /// /// 封面(可能为空) /// - public virtual BitmapImage? Cover - { - get; set; - } + public virtual BitmapImage? Cover { get; set; } /// /// 流派数组 @@ -98,9 +91,7 @@ public class BriefMusicInfo /// public long ModifiedDate { get; set; } = 0; - public BriefMusicInfo() - { - } + public BriefMusicInfo() { } /// /// 异步工厂方法 @@ -115,7 +106,6 @@ public class BriefMusicInfo Path = path, Folder = folder, ModifiedDate = new DateTimeOffset(new FileInfo(path).LastWriteTime).ToUnixTimeSeconds(), - ItemType = System.IO.Path.GetExtension(path).ToLower() }; Task? coverTask = null; @@ -131,14 +121,14 @@ public class BriefMusicInfo info.Title = string.IsNullOrEmpty(musicFile.Tag.Title) ? System.IO.Path.GetFileNameWithoutExtension(path) : musicFile.Tag.Title; string[] combinedArtists = [.. musicFile.Tag.AlbumArtists, .. musicFile.Tag.Performers]; info.Artists = combinedArtists.Length != 0 ? combinedArtists : ["MusicInfo_UnknownArtist".GetLocalized()]; - info.ArtistsStr = info.GetArtistsStr(); + info.ArtistsStr = IBriefMusicInfoBase.GetArtistsStr(info.Artists); info.Year = (ushort)musicFile.Tag.Year; - info.YearStr = info.GetYearStr(); + info.YearStr = IBriefMusicInfoBase.GetYearStr(info.Year); var genres = musicFile.Tag.Genres; info.Genre = genres.Length != 0 ? genres : ["MusicInfo_UnknownGenre".GetLocalized()]; - info.GenreStr = info.GetGenreStr(); + info.GenreStr = GetGenreStr(info.Genre); info.Duration = musicFile.Properties.Duration; - info.DurationStr = info.GetDurationStr(); + info.DurationStr = IBriefMusicInfoBase.GetDurationStr(info.Duration); // 等待 LoadCoverAsync 任务完成 if (coverTask != null) @@ -152,10 +142,10 @@ public class BriefMusicInfo info.Title = System.IO.Path.GetFileNameWithoutExtension(path); info.Album = "MusicInfo_UnknownAlbum".GetLocalized(); info.Artists = ["MusicInfo_UnknownArtist".GetLocalized()]; - info.ArtistsStr = info.GetArtistsStr(); + info.ArtistsStr = IBriefMusicInfoBase.GetArtistsStr(info.Artists); info.Genre = ["MusicInfo_UnknownGenre".GetLocalized()]; - info.GenreStr = info.GetGenreStr(); - info.DurationStr = info.GetDurationStr(); + info.GenreStr = GetGenreStr(info.Genre); + info.DurationStr = IBriefMusicInfoBase.GetDurationStr(info.Duration); } catch (Exception ex) { @@ -196,29 +186,11 @@ public class BriefMusicInfo return tcs.Task; } - /// - /// 获取参与创作的艺术家名字符串, 为空时返回"未知艺术家" - /// - /// - protected virtual string GetArtistsStr() => string.Join(", ", Artists); - - /// - /// 获取时长字符串 - /// - /// - protected virtual string GetDurationStr() => Duration.Hours > 0 ? $"{Duration:hh\\:mm\\:ss}" : $"{Duration:mm\\:ss}"; - /// /// 获取流派字符串 /// /// - protected virtual string GetGenreStr() => string.Join(", ", Genre); - - /// - /// 获取发行年份字符串 - /// - /// - protected string GetYearStr() => Year is 0 ? "" : Year.ToString(); + protected static string GetGenreStr(string[] genre) => string.Join(", ", genre); /// /// 获取文本前景色 @@ -226,7 +198,7 @@ public class BriefMusicInfo /// /// /// 如果是当前播放歌曲, 返回主题色, 如果不是, 根据当前主题返回黑色或白色 - public SolidColorBrush GetTextForeground(DetailedMusicInfo currentMusic, bool isDarkTheme) + public SolidColorBrush GetTextForeground(IDetailedMusicInfoBase currentMusic, bool isDarkTheme) { var isCurrentMusic = Path == currentMusic.Path; if (isCurrentMusic) @@ -238,8 +210,10 @@ public class BriefMusicInfo } } -public class DetailedMusicInfo : BriefMusicInfo +public class DetailedMusicInfo : BriefMusicInfo, IDetailedMusicInfoBase { + public bool IsOnline { get; set; } = false; + /// /// 专辑名, 为空时返回"" /// @@ -256,20 +230,15 @@ public class DetailedMusicInfo : BriefMusicInfo public override string GenreStr { get; set; } = ""; /// - /// 时长字符串 + /// 时长字符串, 为空时返回"" /// public override string DurationStr { get; set; } = ""; + /// - /// 专辑艺术家数组 + /// 项目类型, 为空时返回"" /// - private string[] AlbumArtists - { - get; - set => field = [.. value - .SelectMany(artist => artist.Split(_delimiters, StringSplitOptions.RemoveEmptyEntries)) - .Distinct()]; - } = []; + public string ItemType { get; set; } = ""; /// /// 专辑艺术家字符串, 为空时返回"" @@ -277,17 +246,14 @@ public class DetailedMusicInfo : BriefMusicInfo public string AlbumArtistsStr { get; set; } = ""; /// - /// 艺术家和专辑名字符串 + /// 艺术家和专辑名字符串, 为空时返回"" /// public string ArtistAndAlbumStr { get; set; } = ""; /// /// 清晰封面(可能为空) /// - public override BitmapImage? Cover - { - get; set; - } + public override BitmapImage? Cover { get; set; } /// /// 封面缓冲数据 @@ -295,24 +261,20 @@ public class DetailedMusicInfo : BriefMusicInfo public byte[] CoverBuffer { get; set; } = []; /// - /// 比特率 + /// 比特率, 为空时返回"" /// public string BitRate { get; set; } = ""; /// - /// 曲目 + /// 曲目, 为空时返回"" /// public string Track { get; set; } = ""; /// - /// 歌词 + /// 歌词, 为空时返回"" /// public string Lyric { get; set; } = ""; - public DetailedMusicInfo() - { - } - public DetailedMusicInfo(string path) { Path = path; @@ -337,16 +299,17 @@ public class DetailedMusicInfo : BriefMusicInfo Title = string.IsNullOrEmpty(musicFile.Tag.Title) ? System.IO.Path.GetFileNameWithoutExtension(path) : musicFile.Tag.Title; Album = musicFile.Tag.Album ?? ""; Artists = [.. musicFile.Tag.AlbumArtists, .. musicFile.Tag.Performers]; - ArtistsStr = GetArtistsStr(); - AlbumArtists = [.. musicFile.Tag.AlbumArtists]; - AlbumArtistsStr = GetAlbumArtistsStr(); - ArtistAndAlbumStr = GetArtistAndAlbumStr(ArtistsStr); + ArtistsStr = IBriefMusicInfoBase.GetArtistsStr(Artists); + AlbumArtistsStr = IDetailedMusicInfoBase.GetAlbumArtistsStr([.. musicFile.Tag.AlbumArtists + .SelectMany(artist => artist.Split(_delimiters, StringSplitOptions.RemoveEmptyEntries)) + .Distinct()]); + ArtistAndAlbumStr = IDetailedMusicInfoBase.GetArtistAndAlbumStr(Album, ArtistsStr); Year = (ushort)musicFile.Tag.Year; - YearStr = GetYearStr(); + YearStr = IBriefMusicInfoBase.GetYearStr(Year); Genre = musicFile.Tag.Genres; - GenreStr = GetGenreStr(); + GenreStr = GetGenreStr(Genre); Duration = musicFile.Properties.Duration; - DurationStr = GetDurationStr(); + DurationStr = IBriefMusicInfoBase.GetDurationStr(Duration); Track = musicFile.Tag.Track == 0 ? "" : musicFile.Tag.Track.ToString(); Lyric = musicFile.Tag.Lyrics ?? ""; BitRate = $"{musicFile.Properties.AudioBitrate} kbps"; @@ -360,45 +323,4 @@ public class DetailedMusicInfo : BriefMusicInfo Debug.WriteLine(ex.StackTrace); } } - - /// - /// 获取参与创作的艺术家名字符串, 为空时返回"" - /// - /// - protected override string GetArtistsStr() => string.Join(", ", Artists); - - /// - /// 获取时长字符串 - /// - /// - protected override string GetDurationStr() => Duration.Hours > 0 ? $"{Duration:hh\\:mm\\:ss}" : $"{Duration:mm\\:ss}"; - - /// - /// 获取流派字符串, 为空时返回"" - /// - /// - protected override string GetGenreStr() => string.Join(", ", Genre); - - /// - /// 获取专辑艺术家字符串, 为空时返回"" - /// - /// - protected string GetAlbumArtistsStr() => string.Join(", ", AlbumArtists); - - /// - /// 获取艺术家和专辑名字符串 - /// - /// - protected string GetArtistAndAlbumStr(string artistsStr) - { - if (string.IsNullOrEmpty(artistsStr)) - { - return Album ?? ""; - } - if (string.IsNullOrEmpty(Album)) - { - return artistsStr; - } - return $"{artistsStr} • {Album}"; - } } \ No newline at end of file diff --git a/The Untamed Music Player/Models/MusicLibrary.cs b/The Untamed Music Player/Models/MusicLibrary.cs index 54ee325..bc07fc4 100644 --- a/The Untamed Music Player/Models/MusicLibrary.cs +++ b/The Untamed Music Player/Models/MusicLibrary.cs @@ -8,7 +8,7 @@ using The_Untamed_Music_Player.ViewModels; using Windows.Storage; namespace The_Untamed_Music_Player.Models; -public partial class MusicLibrary : ObservableObject +public partial class MusicLibrary : ObservableRecipient { /// /// 调度器队列 @@ -105,7 +105,7 @@ public partial class MusicLibrary : ObservableObject await _librarySemaphore.WaitAsync(); // 等待信号量, 只允许一个线程访问此函数 try { - Data.hasMusicLibraryLoaded = true; + Data.HasMusicLibraryLoaded = true; var loadMusicTasks = new List(); if (Folders.Any()) { @@ -120,8 +120,8 @@ public partial class MusicLibrary : ObservableObject { OnPropertyChanged(nameof(HasMusics)); Genres = [.. _musicGenres.Keys - .Concat([ResourceExtensions.GetLocalized("MusicInfo_AllGenres")]) - .OrderBy(x => x, new GenreComparer())]; + .Concat([ResourceExtensions.GetLocalized("MusicInfo_AllGenres")]) + .OrderBy(x => x, new GenreComparer())]; _musicGenres.Clear(); }); await Task.Run(AddFolderWatcher); @@ -142,7 +142,7 @@ public partial class MusicLibrary : ObservableObject await _librarySemaphore.WaitAsync(); try { - Data.hasMusicLibraryLoaded = true; + Data.HasMusicLibraryLoaded = true; _dispatcherQueue.TryEnqueue(() => { IsProgressRingActive = true; @@ -165,8 +165,8 @@ public partial class MusicLibrary : ObservableObject { OnPropertyChanged(nameof(HasMusics)); Genres = new([.. _musicGenres.Keys - .Concat([ResourceExtensions.GetLocalized("MusicInfo_AllGenres")]) - .OrderBy(x => x, new GenreComparer())]); + .Concat([ResourceExtensions.GetLocalized("MusicInfo_AllGenres")]) + .OrderBy(x => x, new GenreComparer())]); OnPropertyChanged("LibraryReloaded"); _musicGenres.Clear(); }); @@ -340,33 +340,30 @@ public partial class MusicLibrary : ObservableObject } } - public IOrderedEnumerable GetMusicsByAlbum(AlbumInfo albumInfo) - { - var list = new List(); - var albumName = albumInfo.Name; + /// + /// 根据专辑信息获取歌曲列表 + /// + /// + /// + public IOrderedEnumerable GetSongsByAlbum(AlbumInfo albumInfo) => Songs + .Where(m => m.Album == albumInfo.Name) + .OrderBy(m => m.Title, new TitleComparer()); - foreach (var music in Songs) - { - if (music.Album == albumName) - { - list.Add(music); - } - } + /// + /// 根据艺术家信息获取专辑列表 + /// + /// + /// + public List GetAlbumsByArtist(ArtistInfo artistInfo) => [.. artistInfo.Albums + .Select(album => new BriefAlbumInfo(Albums[album])) + .OrderBy(m => m.Name, new AlbumTitleComparer())]; - return list.OrderBy(m => m.Title, new TitleComparer()); - } - - public List GetAlbumsByArtist(ArtistInfo artistInfo) - { - var list = new List(); - var albums = artistInfo.Albums; - - foreach (var album in albums) - { - var albumInfo = Albums[album]; - list.Add(new BriefAlbumInfo(albumInfo)); - } - - return [.. list.OrderBy(m => m.Name, new AlbumTitleComparer())]; - } + /// + /// 根据艺术家信息获取歌曲列表 + /// + /// + /// + public ObservableCollection GetSongsByArtist(ArtistInfo artistInfo) => [.. artistInfo.Albums + .OrderBy(album => album, new AlbumTitleComparer()) + .SelectMany(album => GetSongsByAlbum(Albums[album]))]; } diff --git a/The Untamed Music Player/Models/MusicPlayer.cs b/The Untamed Music Player/Models/MusicPlayer.cs index ed0cace..0e26fcf 100644 --- a/The Untamed Music Player/Models/MusicPlayer.cs +++ b/The Untamed Music Player/Models/MusicPlayer.cs @@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; +using The_Untamed_Music_Player.Contracts.Models; using The_Untamed_Music_Player.Contracts.Services; using Windows.Media; using Windows.Media.Core; @@ -41,7 +42,6 @@ public partial class MusicPlayer : ObservableRecipient /// private ThreadPoolTimer? _positionUpdateTimer; - /// /// 线程锁开启状态, true为开启, false为关闭 /// @@ -100,6 +100,11 @@ public partial class MusicPlayer : ObservableRecipient /// public MediaPlayer Player { get; set; } = new() { AudioCategory = MediaPlayerAudioCategory.Media }; + /// + /// 歌曲来源模式, 0为本地, 1为网易 + /// + public byte SourceMode { get; set; } = 0; + /// /// 随机播放模式, true为开启, false为关闭. /// @@ -135,8 +140,8 @@ public partial class MusicPlayer : ObservableRecipient /// 当前播放歌曲 /// [ObservableProperty] - public partial DetailedMusicInfo CurrentMusic { get; set; } = new(); - partial void OnCurrentMusicChanged(DetailedMusicInfo value) + public partial IDetailedMusicInfoBase CurrentMusic { get; set; } = null!; + partial void OnCurrentMusicChanged(IDetailedMusicInfoBase value) { SetSource(value.Path); CurrentLyric = LyricSlice.GetLyricSlices(value.Lyric); @@ -283,7 +288,7 @@ public partial class MusicPlayer : ObservableRecipient Player.Source = MediaSource.CreateFromStorageFile(mediaFile); Player.PlaybackSession.PlaybackRate = PlaySpeed; TotalPlayingTime = Player.PlaybackSession.NaturalDuration; - _displayUpdater.MusicProperties.Title = CurrentMusic.Title; + _displayUpdater.MusicProperties.Title = CurrentMusic!.Title; _displayUpdater.MusicProperties.Artist = CurrentMusic.ArtistsStr == "未知艺术家" ? "" : CurrentMusic.ArtistsStr; _positionUpdateTimer = ThreadPoolTimer.CreatePeriodicTimer(UpdateTimerHandler, TimeSpan.FromMilliseconds(250)); } @@ -293,14 +298,14 @@ public partial class MusicPlayer : ObservableRecipient } } - if (CurrentMusic.Cover != null && CurrentMusic.CoverBuffer.Length != 0) + if (CurrentMusic!.Cover != null && CurrentMusic is DetailedMusicInfo info && info.CoverBuffer.Length != 0) { try { var tempFolder = ApplicationData.Current.TemporaryFolder; var coverFileName = "Cover.jpg"; var coverFile = await tempFolder.CreateFileAsync(coverFileName, CreationCollisionOption.ReplaceExisting); - await FileIO.WriteBytesAsync(coverFile, CurrentMusic.CoverBuffer); + await FileIO.WriteBytesAsync(coverFile, info.CoverBuffer); _displayUpdater.Thumbnail = RandomAccessStreamReference.CreateFromFile(coverFile); } catch @@ -320,14 +325,14 @@ public partial class MusicPlayer : ObservableRecipient /// /// /// - public async void SetPlayList(string name, ObservableCollection list, byte sortmode = 0) + public async void SetPlayList(string name, IEnumerable list, byte sortmode = 0) { - if (PlayQueue.Count != list.Count || PlayQueueName != name || _sortMode != sortmode) + if (PlayQueue.Count != list.Count() || PlayQueueName != name || _sortMode != sortmode) { _sortMode = sortmode; PlayQueueName = name; PlayQueue = [.. list]; - _playQueueLength = list.Count; + _playQueueLength = list.Count(); var hasMusics = PlayQueue.Any(); if (Data.RootPlayBarViewModel != null) { @@ -462,7 +467,7 @@ public partial class MusicPlayer : ObservableRecipient { if (RepeatMode == 2) { - PlaySongByPath(CurrentMusic.Path); + PlaySongByPath(CurrentMusic!.Path); } else { @@ -636,7 +641,7 @@ public partial class MusicPlayer : ObservableRecipient ShuffledPlayQueue.Clear(); for (var i = 0; i < PlayQueue.Count; i++) { - if (PlayQueue[i].Path == CurrentMusic.Path) + if (PlayQueue[i].Path == CurrentMusic!.Path) { PlayQueueIndex = i; break; @@ -665,7 +670,7 @@ public partial class MusicPlayer : ObservableRecipient ShuffledPlayQueue = new ObservableCollection([.. PlayQueue.OrderBy(x => Guid.NewGuid())]); for (var i = 0; i < ShuffledPlayQueue.Count; i++) { - if (ShuffledPlayQueue[i].Path == CurrentMusic.Path) + if (ShuffledPlayQueue[i].Path == CurrentMusic!.Path) { PlayQueueIndex = i; break; @@ -879,7 +884,7 @@ public partial class MusicPlayer : ObservableRecipient /// public async void SaveCurrentStateAsync() { - await _localSettingsService.SaveSettingAsync("CurrentMusic", CurrentMusic.Path); + await _localSettingsService.SaveSettingAsync("CurrentMusic", CurrentMusic!.Path); /*var playqueuepaths = PlayQueue.Select(music => music.Path).ToList(); await _localSettingsService.SaveSettingAsync("PlayQueuePaths", playqueuepaths); var shuffledplayqueuepaths = ShuffledPlayQueue.Select(music => music.Path).ToList(); diff --git a/The Untamed Music Player/Models/OnlineMusicLibrary.cs b/The Untamed Music Player/Models/OnlineMusicLibrary.cs new file mode 100644 index 0000000..0e8e169 --- /dev/null +++ b/The Untamed Music Player/Models/OnlineMusicLibrary.cs @@ -0,0 +1,221 @@ +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using The_Untamed_Music_Player.Contracts.Models; +using The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; + +namespace The_Untamed_Music_Player.Models; +public partial class OnlineMusicLibrary : ObservableRecipient +{ + private bool _isSearchingMore = false; + + public byte PageIndex { get; set; } + public byte MusicLibraryIndex { get; set; } + public string KeyWords { get; set; } = null!; + [ObservableProperty] + public partial Visibility KeyWordsTextBlockVisibility { get; set; } = Visibility.Collapsed; + + [ObservableProperty] + public partial Visibility NetworkErrorVisibility { get; set; } = Visibility.Collapsed; + + [ObservableProperty] + public partial double ListViewOpacity { get; set; } = 0; + + /// + /// 是否显示加载进度环 + /// + [ObservableProperty] + public partial bool IsSearchProgressRingActive { get; set; } = false; + + /// + /// 是否显示加载更多进度环 + /// + [ObservableProperty] + public partial bool IsSearchMoreProgressRingActive { get; set; } = false; + + [ObservableProperty] + public partial IBriefOnlineMusicInfoList OnlineMusicInfoList { get; set; } = null!; + + [ObservableProperty] + public partial List SearchResultList { get; set; } = []; + + public async Task Search() + { + KeyWordsTextBlockVisibility = Visibility.Collapsed; + NetworkErrorVisibility = Visibility.Collapsed; + ListViewOpacity = 0; + if (!await IsInternetAvailableAsync()) + { + NetworkErrorVisibility = Visibility.Visible; + return; + } + IsSearchProgressRingActive = true; + try + { + if (PageIndex == 0) + { + if (MusicLibraryIndex == 0) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + await OnlineMusicInfoList.SearchAsync(KeyWords); + } + else if (MusicLibraryIndex == 1) + { } + else if (MusicLibraryIndex == 2) + { } + else if (MusicLibraryIndex == 3) + { } + else if (MusicLibraryIndex == 4) + { } + else + { } + } + OnPropertyChanged(nameof(KeyWords)); + KeyWordsTextBlockVisibility = Visibility.Visible; + ListViewOpacity = 1; + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + finally + { + IsSearchProgressRingActive = false; + } + } + + public async Task SearchMore() + { + if (!_isSearchingMore) + { + _isSearchingMore = true; + if (OnlineMusicInfoList.HasAllLoaded || !await IsInternetAvailableAsync()) + { + return; + } + IsSearchMoreProgressRingActive = true; + try + { + if (PageIndex == 0) + { + if (MusicLibraryIndex == 0) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + await OnlineMusicInfoList.SearchMore(); + } + else if (MusicLibraryIndex == 1) + { } + else if (MusicLibraryIndex == 2) + { } + else if (MusicLibraryIndex == 3) + { } + else if (MusicLibraryIndex == 4) + { } + else + { } + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + finally + { + _isSearchingMore = false; + IsSearchMoreProgressRingActive = false; + } + } + } + + public async Task UpdateSearchResult() + { + if (!string.IsNullOrWhiteSpace(KeyWords)) + { + if (MusicLibraryIndex == 0) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + // 待修改 + else if (MusicLibraryIndex == 1) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + else if (MusicLibraryIndex == 2) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + else if (MusicLibraryIndex == 3) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + else if (MusicLibraryIndex == 4) + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + else + { + if (OnlineMusicInfoList is not CloudBriefOnlineMusicInfoList) + { + OnlineMusicInfoList = new CloudBriefOnlineMusicInfoList(); + } + SearchResultList = await OnlineMusicInfoList.GetSearchResultAsync(KeyWords); + } + } + else + { + ClearSearchResult(); + } + } + + public void ClearSearchResult() + { + SearchResultList = []; + } + + public async void RetryButton_Click(object sender, RoutedEventArgs e) + { + await Search(); + } + + private static async Task IsInternetAvailableAsync() + { + try + { + using var client = new HttpClient(); + client.Timeout = TimeSpan.FromSeconds(5); + var response = await client.GetAsync("https://www.baidu.com"); + return response.IsSuccessStatusCode; + } + catch + { + return false; + } + } +} diff --git a/The Untamed Music Player/Models/SearchResult.cs b/The Untamed Music Player/Models/SearchResult.cs new file mode 100644 index 0000000..ce12534 --- /dev/null +++ b/The Untamed Music Player/Models/SearchResult.cs @@ -0,0 +1,11 @@ +namespace The_Untamed_Music_Player.Models; +public class SearchResult +{ + public string Icon { get; set; } = null!; + public string Label { get; set; } = null!; + + public override string ToString() + { + return Label; + } +} \ No newline at end of file diff --git a/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfo.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfo.cs new file mode 100644 index 0000000..4123be1 --- /dev/null +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfo.cs @@ -0,0 +1,188 @@ +using System; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml.Media.Imaging; +using Newtonsoft.Json.Linq; +using The_Untamed_Music_Player.Contracts.Models; +using Windows.Storage.Streams; + +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; +public class CloudBriefOnlineMusicInfo : IBriefOnlineMusicInfo +{ + public bool IsAvailable { get; set; } = false; + public string Path { get; set; } = ""; + public string Title { get; set; } = ""; + public long ID { get; set; } = 0; + public virtual string Album { get; set; } = ""; + public long AlbumID { get; set; } = 0; + public virtual string ArtistsStr { get; set; } = ""; + public virtual string DurationStr { get; set; } = ""; + public string YearStr { get; set; } = ""; + + public CloudBriefOnlineMusicInfo() { } + + public static async Task CreateAsync(JToken jInfo, NeteaseCloudMusicApi api) + { + var info = new CloudBriefOnlineMusicInfo(); + try + { + info.ID = (long)jInfo["id"]!; + var (isOK, songUrlResult) = await api.RequestAsync(CloudMusicApiProviders.SongUrl, new Dictionary { { "id", $"{info.ID}" } }); + var path = (string)songUrlResult["data"]![0]!["url"]!; + if (string.IsNullOrEmpty(path) || !isOK || path == "null") + { + info.IsAvailable = false; + return info; + } + else + { + info.Path = path; + info.Title = (string)jInfo["name"]!; + info.Album = (string)jInfo["album"]!["name"]!; + info.AlbumID = (long)jInfo["album"]!["id"]!; + string[] artists = [.. jInfo["artists"]! + .Select(t => (string)t["name"]!) + .Distinct()]; + info.ArtistsStr = IBriefMusicInfoBase.GetArtistsStr(artists); + info.DurationStr = IBriefMusicInfoBase.GetDurationStr(TimeSpan.FromMilliseconds((long)jInfo["duration"]!)); + info.YearStr = IBriefMusicInfoBase.GetYearStr((ushort)DateTimeOffset.FromUnixTimeMilliseconds((long)jInfo["album"]!["publishTime"]!).Year); + info.IsAvailable = true; + return info; + } + } + catch + { + info.IsAvailable = false; + return info; + } + } + + public class CloudDetailedOnlineMusicInfo : CloudBriefOnlineMusicInfo, IDetailedOnlineMusicInfo + { + public bool IsOnline { get; set; } = true; + public string GenreStr { get; set; } = ""; + public string ItemType { get; set; } = ""; + public string AlbumArtistsStr { get; set; } = ""; + public string ArtistAndAlbumStr { get; set; } = ""; + public BitmapImage? Cover { get; set; } + public string CoverUrl { get; set; } = ""; + public string BitRate { get; set; } = ""; + public string Track { get; set; } = ""; + public string Lyric { get; set; } = ""; + + public CloudDetailedOnlineMusicInfo() { } + + public CloudDetailedOnlineMusicInfo(IBriefOnlineMusicInfo info) + { + var api = new NeteaseCloudMusicApi(); + var songUrlTask = api.RequestAsync(CloudMusicApiProviders.SongUrl, new Dictionary { { "id", $"{info.ID}" } }); + var albumTask = api.RequestAsync(CloudMusicApiProviders.Album, new Dictionary { { "id", $"{info.AlbumID}" } }); + var lyricTask = api.RequestAsync(CloudMusicApiProviders.Lyric, new Dictionary { { "id", $"{info.ID}" } }); + songUrlTask.Wait(); + albumTask.Wait(); + lyricTask.Wait(); + var (isOK1, songUrlResult) = songUrlTask.Result; + var (isOK2, albumResult) = albumTask.Result; + var (isOK3, lyricResult) = lyricTask.Result; + ID = info.ID; + Path = info.Path; + Title = info.Title; + Album = info.Album; + AlbumID = info.AlbumID; + ArtistsStr = info.ArtistsStr; + DurationStr = info.DurationStr; + YearStr = info.YearStr; + ItemType = (string)songUrlResult["data"]![0]!["type"]!; + string[] albumArtists = [.. albumResult["album"]!["artists"]! + .Select(t => (string)t["name"]!) + .Distinct()]; + AlbumArtistsStr = IDetailedMusicInfoBase.GetAlbumArtistsStr(albumArtists); + ArtistAndAlbumStr = IDetailedMusicInfoBase.GetArtistAndAlbumStr(Album, ArtistsStr); + BitRate = $"{((int)songUrlResult["data"]![0]!["br"]!) / 1000} kbps"; + CoverUrl = (string)albumResult["album"]!["picUrl"]!; + if (!string.IsNullOrEmpty(CoverUrl)) + { + using var httpClient = new HttpClient(); + var imageBytes = httpClient.GetByteArrayAsync(CoverUrl).Result; + using var stream = new MemoryStream(imageBytes); + var bitmap = new BitmapImage(); + bitmap.SetSourceAsync(stream.AsRandomAccessStream()).AsTask().Wait(); + Cover = bitmap; + } + Lyric = (string)lyricResult["lrc"]!["lyric"]!; + IsAvailable = true; + } + + public static async Task CreateAsync(IBriefOnlineMusicInfo info) + { + var detailedInfo = new CloudDetailedOnlineMusicInfo + { + ID = info.ID, + Path = info.Path, + Title = info.Title, + Album = info.Album, + AlbumID = info.AlbumID, + ArtistsStr = info.ArtistsStr, + DurationStr = info.DurationStr, + YearStr = info.YearStr + }; + var api = new NeteaseCloudMusicApi(); + var songUrlTask = api.RequestAsync(CloudMusicApiProviders.SongUrl, new Dictionary { { "id", $"{info.ID}" } }); + var albumTask = api.RequestAsync(CloudMusicApiProviders.Album, new Dictionary { { "id", $"{info.AlbumID}" } }); + var lyricTask = api.RequestAsync(CloudMusicApiProviders.Lyric, new Dictionary { { "id", $"{info.ID}" } }); + await Task.WhenAll(songUrlTask, albumTask, lyricTask); + var (isOK1, songUrlResult) = songUrlTask.Result; + var (isOK2, albumResult) = albumTask.Result; + var (isOK3, lyricResult) = lyricTask.Result; + api.Dispose(); + detailedInfo.CoverUrl = (string)albumResult["album"]!["picUrl"]!; + Task? coverTask = null; + if (!string.IsNullOrEmpty(detailedInfo.CoverUrl)) + { + using var httpClient = new HttpClient(); + var coverBuffer = await httpClient.GetByteArrayAsync(detailedInfo.CoverUrl); + coverTask = LoadCoverAsync(coverBuffer, detailedInfo); + } + detailedInfo.ItemType = (string)songUrlResult["data"]![0]!["type"]!; + string[] albumArtists = [.. albumResult["album"]!["artists"]! + .Select(t => (string)t["name"]!) + .Distinct()]; + detailedInfo.AlbumArtistsStr = IDetailedMusicInfoBase.GetAlbumArtistsStr(albumArtists); + detailedInfo.ArtistAndAlbumStr = IDetailedMusicInfoBase.GetArtistAndAlbumStr(detailedInfo.Album, detailedInfo.ArtistsStr); + detailedInfo.BitRate = $"{((int)songUrlResult["data"]![0]!["br"]!) / 1000} kbps"; + detailedInfo.Lyric = (string)lyricResult["lrc"]!["lyric"]!; + detailedInfo.IsAvailable = true; + if (coverTask != null) + { + await coverTask; + } + return detailedInfo; + } + + private static Task LoadCoverAsync(byte[] coverBuffer, CloudDetailedOnlineMusicInfo info) + { + var tcs = new TaskCompletionSource(); + App.MainWindow?.DispatcherQueue.TryEnqueue(async () => + { + try + { + using var stream = new InMemoryRandomAccessStream(); + await stream.WriteAsync(coverBuffer.AsBuffer()); + stream.Seek(0); + var bitmap = new BitmapImage + { + DecodePixelWidth = 400, + DecodePixelHeight = 400 + }; + await bitmap.SetSourceAsync(stream); + info.Cover = bitmap; + tcs.SetResult(true); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + return tcs.Task; + } + } +} diff --git a/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfoList.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfoList.cs new file mode 100644 index 0000000..f58efce --- /dev/null +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/CloudOnlineMusicInfoList.cs @@ -0,0 +1,160 @@ +using System.Diagnostics; +using Newtonsoft.Json.Linq; +using The_Untamed_Music_Player.Contracts.Models; +using The_Untamed_Music_Player.Models; + +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; +public partial class CloudBriefOnlineMusicInfoList : IBriefOnlineMusicInfoList +{ + private readonly NeteaseCloudMusicApi _api = new(); + private const byte _limit = 30; + private ushort _offset = 0; + private int _songCount = 0; + private int _listCount = 0; + + public CloudBriefOnlineMusicInfoList() + { + } + + public async override Task SearchAsync(string keyWords) + { + _offset = 0; + _listCount = 0; + HasAllLoaded = false; + Clear(); + _keyWords = keyWords; + var (isOk, result) = await _api.RequestAsync(CloudMusicApiProviders.Search, new Dictionary + { + { "keywords", keyWords }, + { "limit", _limit.ToString() }, + { "offset", _offset.ToString() } + }); + if (!isOk) + { + throw new Exception(); + } + try + { + _songCount = (int)result["result"]!["songCount"]!; + if (_songCount == 0) + { + HasAllLoaded = true; + return; + } + await ProcessSongsAsync(result["result"]!["songs"]!); + _offset = 1; + } + catch + { + throw new Exception("搜索失败"); + } + } + + public async override Task SearchMore() + { + var (isOk, result) = await _api.RequestAsync(CloudMusicApiProviders.Search, new Dictionary + { + { "keywords", _keyWords }, + { "limit", _limit.ToString() }, + { "offset", _offset.ToString() } + }); + if (!isOk) + { + throw new Exception(); + } + try + { + await ProcessSongsAsync(result["result"]!["songs"]!); + _offset++; + } + catch + { + throw new Exception("搜索失败"); + } + } + + private async Task ProcessSongsAsync(JToken songs) + { + var actualCount = songs.Count(); + var infos = new CloudBriefOnlineMusicInfo[actualCount]; + var groupTasks = new List(); + + // 每组 8 首歌曲 + for (var i = 0; i < actualCount; i += 8) + { + var start = i; + var end = Math.Min(i + 8, actualCount); + // 使用 Task.Run 将每组放在一个线程中执行 + groupTasks.Add(Task.Run(async () => + { + for (var j = start; j < end; j++) + { + try + { + var info = await CloudBriefOnlineMusicInfo.CreateAsync(songs[j]!, _api); + infos[j] = info; + } + catch (Exception ex) + { + _listCount++; + Debug.WriteLine(ex.StackTrace); + } + } + })); + } + await Task.WhenAll(groupTasks); + foreach (var info in infos) + { + Add(info); + } + } + + protected new void Add(IBriefOnlineMusicInfo info) + { + _listCount++; + if (info.IsAvailable) + { + base.Add(info); + } + if (_listCount == _songCount) + { + HasAllLoaded = true; + } + } + + public async override Task> GetSearchResultAsync(string keyWords) + { + var (isOk, result) = await _api.RequestAsync(CloudMusicApiProviders.SearchSuggest, new Dictionary { { "keywords", $"{keyWords}" } }); + if (!isOk) + { + Debug.WriteLine("获取网易云音乐搜索建议失败"); + return []; + } + var songs = result["result"]!["songs"]? + .Select(t => (string)t["name"]!) + .Distinct() ?? []; + var albums = result["result"]!["albums"]? + .Select(t => (string)t["name"]!) + .Distinct() ?? []; + var artists = result["result"]!["artists"]? + .Select(t => (string)t["name"]!) + .Distinct() ?? []; + var playlists = result["result"]!["playlists"]? + .Select(t => (string)t["name"]!) + .Distinct() ?? []; + var list = new List(); + AddResults(songs, 5, "\uE8D6", list); + AddResults(albums, 3, "\uE93C", list); + AddResults(artists, 3, "\uE77B", list); + AddResults(playlists, 2, "\uE728", list); + return list; + } + + private static void AddResults(IEnumerable items, int limit, string icon, List list) + { + foreach (var item in items.Take(limit)) + { + list.Add(new SearchResult { Icon = icon, Label = item }); + } + } +} \ No newline at end of file diff --git a/OnlineMusicApi/Extensions.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/Extensions.cs similarity index 94% rename from OnlineMusicApi/Extensions.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/Extensions.cs index 46c215e..2b2206d 100644 --- a/OnlineMusicApi/Extensions.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/Extensions.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +#pragma warning disable + using System.Security.Cryptography; using System.Text; -namespace NeteaseCloudMusicApi; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; internal static class Extensions { private static readonly MD5 _md5 = MD5.Create(); diff --git a/OnlineMusicApi/NeteaseCloudMusicApi.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApi.cs similarity index 97% rename from OnlineMusicApi/NeteaseCloudMusicApi.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApi.cs index a825e3c..e95292e 100644 --- a/OnlineMusicApi/NeteaseCloudMusicApi.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApi.cs @@ -1,18 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Extensions; -using System.IO; -using System.Linq; +#pragma warning disable + using System.Net; -using System.Net.Http; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using NeteaseCloudMusicApi.util; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Extensions; +using The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.util; -namespace NeteaseCloudMusicApi; +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; /// /// 网易云音乐API /// diff --git a/OnlineMusicApi/NeteaseCloudMusicApiProvider.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApiProvider.cs similarity index 99% rename from OnlineMusicApi/NeteaseCloudMusicApiProvider.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApiProvider.cs index c04ef48..a964e19 100644 --- a/OnlineMusicApi/NeteaseCloudMusicApiProvider.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/NeteaseCloudMusicApiProvider.cs @@ -1,14 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text.RegularExpressions; -using NeteaseCloudMusicApi.util; -using Newtonsoft.Json; -using static NeteaseCloudMusicApi.NeteaseCloudMusicApiProvider; +#pragma warning disable -namespace NeteaseCloudMusicApi; +using System.Net; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.util; +using static The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.NeteaseCloudMusicApiProvider; + +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; /// /// 网易云音乐API相关信息提供者 /// @@ -114,7 +112,7 @@ public sealed class NeteaseCloudMusicApiProvider SpecialHandle } - internal sealed class ParameterInfo(string key, NeteaseCloudMusicApiProvider.ParameterType type, string defaultValue) + internal sealed class ParameterInfo(string key, ParameterType type, string defaultValue) { public string Key = key; public ParameterType Type = type; @@ -311,7 +309,7 @@ public static partial class CloudMusicApiProviders /// /// 发送/删除评论 /// - public static readonly NeteaseCloudMusicApiProvider Comment = new("/comment", HttpMethod.Post, q => $"https://music.163.com/weapi/resource/comments/{(q["t"] == "1" ? "add" : (q["t"] == "0" ? "delete" : "reply"))}", [], BuildOptions("weapi", [new("os", "pc")])) + public static readonly NeteaseCloudMusicApiProvider Comment = new("/comment", HttpMethod.Post, q => $"https://music.163.com/weapi/resource/comments/{(q["t"] == "1" ? "add" : q["t"] == "0" ? "delete" : "reply")}", [], BuildOptions("weapi", [new("os", "pc")])) { DataProvider = queries => { diff --git a/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/QueryCollection.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/QueryCollection.cs new file mode 100644 index 0000000..d5e5368 --- /dev/null +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/QueryCollection.cs @@ -0,0 +1,5 @@ +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI; +internal sealed partial class QueryCollection : List> +{ + public void Add(string key, string value) => Add(new KeyValuePair(key, value)); +} diff --git a/OnlineMusicApi/System/Extensions/ExceptionExtensions.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/ExceptionExtensions.cs similarity index 93% rename from OnlineMusicApi/System/Extensions/ExceptionExtensions.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/ExceptionExtensions.cs index 7312c36..f596766 100644 --- a/OnlineMusicApi/System/Extensions/ExceptionExtensions.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/ExceptionExtensions.cs @@ -1,7 +1,8 @@ +#pragma warning disable + using System.Text; -namespace System.Extensions; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Extensions; internal static class ExceptionExtensions { /// diff --git a/OnlineMusicApi/System/Extensions/HttpClientExtensions.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpClientExtensions.cs similarity index 96% rename from OnlineMusicApi/System/Extensions/HttpClientExtensions.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpClientExtensions.cs index 67ea94e..d40aef3 100644 --- a/OnlineMusicApi/System/Extensions/HttpClientExtensions.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpClientExtensions.cs @@ -1,10 +1,9 @@ -using System.Collections.Generic; -using System.Net.Http; +#pragma warning disable + using System.Net.Http.Headers; using System.Text; -using System.Threading.Tasks; -namespace System.Extensions; +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Extensions; internal static class HttpClientExtensions { diff --git a/OnlineMusicApi/System/Extensions/HttpExtensions.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpExtensions.cs similarity index 79% rename from OnlineMusicApi/System/Extensions/HttpExtensions.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpExtensions.cs index 1b7ea32..a189e16 100644 --- a/OnlineMusicApi/System/Extensions/HttpExtensions.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Extensions/HttpExtensions.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using System.Linq; - -namespace System.Extensions; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Extensions; internal static class HttpExtensions { public static string ToQueryString(this IEnumerable> queries) diff --git a/OnlineMusicApi/System/Numerics/BigInteger.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigInteger.cs similarity index 94% rename from OnlineMusicApi/System/Numerics/BigInteger.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigInteger.cs index 5d69403..12ae123 100644 --- a/OnlineMusicApi/System/Numerics/BigInteger.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigInteger.cs @@ -1,11 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +#pragma warning disable using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal readonly struct BigInteger { private const uint kuMaskHighBit = unchecked((uint)int.MinValue); @@ -133,14 +130,14 @@ internal readonly struct BigInteger { for (var i = 0; i < byteCount; i++) { - _sign = (_sign << 8) | value[i]; + _sign = _sign << 8 | value[i]; } } else { for (var i = byteCount - 1; i >= 0; i--) { - _sign = (_sign << 8) | value[i]; + _sign = _sign << 8 | value[i]; } } @@ -177,7 +174,7 @@ internal readonly struct BigInteger for (var byteInDword = 0; byteInDword < 4; byteInDword++) { var curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[curDword] = val[curDword] << 8 | curByteValue; curByte++; } @@ -192,7 +189,7 @@ internal readonly struct BigInteger for (var byteInDword = 0; byteInDword < 4; byteInDword++) { var curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[curDword] = val[curDword] << 8 | curByteValue; curByte--; } @@ -213,7 +210,7 @@ internal readonly struct BigInteger for (curByte = 0; curByte < unalignedBytes; curByte++) { var curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[curDword] = val[curDword] << 8 | curByteValue; } } else @@ -221,7 +218,7 @@ internal readonly struct BigInteger for (curByte = byteCountMinus1; curByte >= byteCount - unalignedBytes; curByte--) { var curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[curDword] = val[curDword] << 8 | curByteValue; } } } @@ -254,7 +251,7 @@ internal readonly struct BigInteger default: if (unchecked((int)val[0]) > 0) { - _sign = (-1) * ((int)val[0]); + _sign = -1 * (int)val[0]; _bits = null; AssertValid(); return; @@ -343,7 +340,7 @@ internal readonly struct BigInteger get { AssertValid(); - return (_sign >> (kcbitUint - 1)) - (-_sign >> (kcbitUint - 1)); + return (_sign >> kcbitUint - 1) - (-_sign >> kcbitUint - 1); } } @@ -432,7 +429,7 @@ internal readonly struct BigInteger var bits = _bits; if (bits == null) { - highByte = (byte)((sign < 0) ? 0xff : 0x00); + highByte = (byte)(sign < 0 ? 0xff : 0x00); highDword = unchecked((uint)sign); } else if (sign == -1) @@ -565,9 +562,9 @@ internal readonly struct BigInteger } // Assert we're big endian, or little endian consistency holds. - Debug.Assert(isBigEndian || (!needExtraByte && curByte == length - 1) || (needExtraByte && curByte == length - 2)); + Debug.Assert(isBigEndian || !needExtraByte && curByte == length - 1 || needExtraByte && curByte == length - 2); // Assert we're little endian, or big endian consistency holds. - Debug.Assert(!isBigEndian || (!needExtraByte && curByte == 0) || (needExtraByte && curByte == 1)); + Debug.Assert(!isBigEndian || !needExtraByte && curByte == 0 || needExtraByte && curByte == 1); if (needExtraByte) { diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.AddSub.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.AddSub.cs similarity index 94% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.AddSub.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.AddSub.cs index aa0072a..a9e4ac9 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -1,11 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { private static unsafe void Add(uint* left, int leftLength, diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.BitsBuffer.cs similarity index 90% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.BitsBuffer.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index d008e43..df0834c 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -1,15 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - -// ATTENTION: always pass BitsBuffer by reference, -// it's a structure for performance reasons. Furthermore -// it's a mutable one, so use it only with care! - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { internal struct BitsBuffer diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.DivRem.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.DivRem.cs similarity index 97% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.DivRem.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.DivRem.cs index 67d2a2e..b207cb3 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -1,11 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { public static uint Remainder(uint[] left, uint right) diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.FastReducer.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.FastReducer.cs similarity index 95% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.FastReducer.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.FastReducer.cs index a0f446d..6f8c709 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -1,11 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { internal readonly struct FastReducer diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.PowMod.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.PowMod.cs similarity index 97% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.PowMod.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.PowMod.cs index 5e7eb28..b6cf165 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -1,11 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { // Executes different exponentiation algorithms, which are diff --git a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.SquMul.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.SquMul.cs similarity index 98% rename from OnlineMusicApi/System/Numerics/BigIntegerCalculator.SquMul.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.SquMul.cs index 9600ff7..29c405a 100644 --- a/OnlineMusicApi/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -1,11 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using System.Diagnostics; -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static partial class BigIntegerCalculator { private static readonly int SquareThreshold = 32; diff --git a/OnlineMusicApi/System/Numerics/NumericsHelpers.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/NumericsHelpers.cs similarity index 76% rename from OnlineMusicApi/System/Numerics/NumericsHelpers.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/NumericsHelpers.cs index e25cca2..df0344e 100644 --- a/OnlineMusicApi/System/Numerics/NumericsHelpers.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/System/Numerics/NumericsHelpers.cs @@ -1,9 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Numerics; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics; internal static class NumericsHelpers { public static void DangerousMakeTwosComplement(uint[] d) diff --git a/OnlineMusicApi/util/crypto.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/crypto.cs similarity index 97% rename from OnlineMusicApi/util/crypto.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/crypto.cs index 9de4e25..917050f 100644 --- a/OnlineMusicApi/util/crypto.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/crypto.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Numerics; +#pragma warning disable + using System.Security.Cryptography; using System.Text; using Newtonsoft.Json; +using BigInteger = The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Numerics.BigInteger; -namespace NeteaseCloudMusicApi.util; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.util; internal static class crypto { private static readonly byte[] iv = Encoding.ASCII.GetBytes("0102030405060708"); diff --git a/OnlineMusicApi/util/options.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/options.cs similarity index 64% rename from OnlineMusicApi/util/options.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/options.cs index ee82c2e..e044b34 100644 --- a/OnlineMusicApi/util/options.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/options.cs @@ -1,7 +1,8 @@ +#pragma warning disable + using System.Net; -namespace NeteaseCloudMusicApi.util; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.util; internal sealed class options { public string crypto; diff --git a/OnlineMusicApi/util/request.cs b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/request.cs similarity index 98% rename from OnlineMusicApi/util/request.cs rename to The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/request.cs index 6495c1c..83d20f6 100644 --- a/OnlineMusicApi/util/request.cs +++ b/The Untamed Music Player/OnlineAPIs/CloudMusicAPI/util/request.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Extensions; -using System.IO; +#pragma warning disable + using System.IO.Compression; -using System.Linq; using System.Net; -using System.Net.Http; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.System.Extensions; -namespace NeteaseCloudMusicApi.util; - +namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI.util; internal static partial class request { private static readonly string[] userAgentList = [ diff --git a/The Untamed Music Player/Strings/en-us/Resources.resw b/The Untamed Music Player/Strings/en-us/Resources.resw index 8a61a16..e3560e9 100644 --- a/The Untamed Music Player/Strings/en-us/Resources.resw +++ b/The Untamed Music Player/Strings/en-us/Resources.resw @@ -216,6 +216,9 @@ Songs + + Playlists + Albums @@ -606,4 +609,28 @@ Unknown year + + Music library 1 + + + Music library 6 + + + Music library 5 + + + Music library 4 + + + Music library 3 + + + Music library 2 + + + Network error, please retry later + + + Retry + \ No newline at end of file diff --git a/The Untamed Music Player/Strings/zh-cn/Resources.resw b/The Untamed Music Player/Strings/zh-cn/Resources.resw index 4abffec..c9045e8 100644 --- a/The Untamed Music Player/Strings/zh-cn/Resources.resw +++ b/The Untamed Music Player/Strings/zh-cn/Resources.resw @@ -216,6 +216,9 @@ 歌曲 + + 歌单 + 专辑 @@ -606,4 +609,28 @@ 未知年份 + + 乐库1 + + + 乐库6 + + + 乐库5 + + + 乐库4 + + + 乐库3 + + + 乐库2 + + + 网络异常,请稍后再试 + + + 重试 + \ No newline at end of file diff --git a/The Untamed Music Player/The Untamed Music Player.csproj b/The Untamed Music Player/The Untamed Music Player.csproj index 3a2a086..0b4bcb4 100644 --- a/The Untamed Music Player/The Untamed Music Player.csproj +++ b/The Untamed Music Player/The Untamed Music Player.csproj @@ -37,6 +37,12 @@ + + + + + + @@ -45,16 +51,19 @@ + - - + + - - - + + + - + + + @@ -63,6 +72,18 @@ Always + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + MSBuild:Compile @@ -74,6 +95,8 @@ False + False + Dynamic @@ -83,30 +106,36 @@ full True + CS8981 full True + CS8981 full True + CS8981 full True + CS8981 full True + CS8981 full True + CS8981 diff --git a/The Untamed Music Player/ViewModels/AlbumDetailViewModel.cs b/The Untamed Music Player/ViewModels/AlbumDetailViewModel.cs index f6af740..a1c8209 100644 --- a/The Untamed Music Player/ViewModels/AlbumDetailViewModel.cs +++ b/The Untamed Music Player/ViewModels/AlbumDetailViewModel.cs @@ -16,19 +16,19 @@ public partial class AlbumDetailViewModel : ObservableRecipient public AlbumDetailViewModel() { - var tempList = Data.MusicLibrary.GetMusicsByAlbum(Album); + var tempList = Data.MusicLibrary.GetSongsByAlbum(Album); SongList = [.. tempList]; } public void PlayAllButton_Click(object sender, RoutedEventArgs e) { - Data.MusicPlayer.SetPlayList($"Songs:Album:{Album.Name}", SongList); + Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{Album.Name}", SongList); Data.MusicPlayer.PlaySongByPath(SongList[0].Path); } public void SongListView_ItemClick(object sender, ItemClickEventArgs e) { - Data.MusicPlayer.SetPlayList($"Songs:Album:{Album.Name}", SongList); + Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{Album.Name}", SongList); if (e.ClickedItem is BriefMusicInfo briefMusicInfo) { Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); @@ -37,7 +37,7 @@ public partial class AlbumDetailViewModel : ObservableRecipient public void PlayButton_Click(object sender, RoutedEventArgs e) { - Data.MusicPlayer.SetPlayList($"Songs:Album:{Album.Name}", SongList); + Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{Album.Name}", SongList); if (sender is Button button && button.DataContext is BriefMusicInfo briefMusicInfo) { Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); diff --git a/The Untamed Music Player/ViewModels/ArtistDetailViewModel.cs b/The Untamed Music Player/ViewModels/ArtistDetailViewModel.cs index ffc899f..3945144 100644 --- a/The Untamed Music Player/ViewModels/ArtistDetailViewModel.cs +++ b/The Untamed Music Player/ViewModels/ArtistDetailViewModel.cs @@ -1,4 +1,6 @@ +using System.Collections.ObjectModel; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using The_Untamed_Music_Player.Contracts.Services; using The_Untamed_Music_Player.Models; @@ -21,6 +23,41 @@ public class ArtistDetailViewModel public void PlayAllButton_Click(object sender, RoutedEventArgs e) { + Data.MusicPlayer.SetPlayList($"LocalSongs:Artist:{Artist.Name}", ConvertAllSongsToFlatList()); + Data.MusicPlayer.PlaySongByPath(AlbumList[0].SongList[0].Path); + } + + public void SongListView_ItemClick(object sender, ItemClickEventArgs e) + { + Data.MusicPlayer.SetPlayList($"LocalSongs:Artist:{Artist.Name}", ConvertAllSongsToFlatList()); + if (e.ClickedItem is BriefMusicInfo briefMusicInfo) + { + Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); + } + } + + public void SongListViewPlayButton_Click(object sender, RoutedEventArgs e) + { + Data.MusicPlayer.SetPlayList($"LocalSongs:Artist:{Artist.Name}", ConvertAllSongsToFlatList()); + if (sender is Button button && button.DataContext is BriefMusicInfo briefMusicInfo) + { + Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); + } + } + + public void AlbumGridViewPlayButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.DataContext is BriefAlbumInfo briefAlbumInfo) + { + var songList = new ObservableCollection(briefAlbumInfo.SongList); + Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{briefAlbumInfo.Name}", songList); + Data.MusicPlayer.PlaySongByPath(songList[0].Path); + } + } + + private ObservableCollection ConvertAllSongsToFlatList() + { + return [.. AlbumList.SelectMany(album => album.SongList)]; } public async Task LoadSelectionBarSelectedIndex() diff --git a/The Untamed Music Player/ViewModels/HomeViewModel.cs b/The Untamed Music Player/ViewModels/HomeViewModel.cs index 03c657f..6dc48f6 100644 --- a/The Untamed Music Player/ViewModels/HomeViewModel.cs +++ b/The Untamed Music Player/ViewModels/HomeViewModel.cs @@ -1,25 +1,132 @@ +using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.WinUI.Controls; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; +using The_Untamed_Music_Player.Contracts.Services; +using The_Untamed_Music_Player.Models; +using The_Untamed_Music_Player.Views; namespace The_Untamed_Music_Player.ViewModels; public partial class HomeViewModel : ObservableRecipient { + public byte PageIndex + { + get; + set + { + field = value; + Data.OnlineMusicLibrary.PageIndex = value; + } + } + + [ObservableProperty] + public partial byte MusicLibraryIndex { get; set; } + partial void OnMusicLibraryIndexChanged(byte value) + { + Data.OnlineMusicLibrary.MusicLibraryIndex = value; + SaveMusicLibraryIndex(); + } + + private readonly ILocalSettingsService _localSettingsService = App.GetService(); + public HomeViewModel() { + Initialize(); } - public void SuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) + private async void Initialize() { - + PageIndex = await LoadPageIndex(); + MusicLibraryIndex = await LoadMusicLibraryIndex(); } - public void SuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args) + public async void SuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { - + if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) + { + Data.OnlineMusicLibrary.KeyWords = sender.Text; + await Data.OnlineMusicLibrary.UpdateSearchResult(); + } } - public void SuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) + public async void SuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) { + if (args.ChosenSuggestion != null && args.ChosenSuggestion is SearchResult result) + { + var keyWords = result.Label; + Data.OnlineMusicLibrary.ClearSearchResult(); + var currentSelectedIndex = result.Icon switch + { + "\uE8D6" => 0, + "\uE93C" => 1, + "\uE77B" => 2, + "\uE728" => 3, + _ => 0 + }; + Data.OnlineMusicLibrary.KeyWords = keyWords; + Navigate(currentSelectedIndex); + await Data.OnlineMusicLibrary.Search(); + } + else + { + Data.OnlineMusicLibrary.KeyWords = args.QueryText; + Data.OnlineMusicLibrary.ClearSearchResult(); + await Data.OnlineMusicLibrary.Search(); + } + } + public void SelectorBar_Loaded(object sender, RoutedEventArgs e) + { + if (sender is SelectorBar selectorBar) + { + var selectedItem = selectorBar.Items[PageIndex]; + selectorBar.SelectedItem = selectedItem; + } + } + + public void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) + { + var selectedItem = sender.SelectedItem; + var currentSelectedIndex = sender.Items.IndexOf(selectedItem); + + Navigate(currentSelectedIndex); + } + + public void Navigate(int currentSelectedIndex, bool isFirstLoaded = false) + { + if (!isFirstLoaded && PageIndex == currentSelectedIndex) + { + return; + } + var page = currentSelectedIndex switch + { + 0 => typeof(OnlineSongsPage), + 1 => typeof(OnlineAlbumsPage), + 2 => typeof(OnlineArtistsPage), + 3 => typeof(OnlinePlayListsPage), + _ => typeof(OnlineSongsPage) + }; + var slideNavigationTransitionEffect = currentSelectedIndex - PageIndex > 0 ? SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft; + PageIndex = (byte)currentSelectedIndex; + Data.HomePage.GetFrame().Navigate(page, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect }); + } + + public async Task LoadPageIndex() + { + return await _localSettingsService.ReadSettingAsync("HomePageIndex"); + } + public async Task LoadMusicLibraryIndex() + { + return await _localSettingsService.ReadSettingAsync("HomeMusicLibraryIndex"); + } + public async void SavePageIndex() + { + await _localSettingsService.SaveSettingAsync("HomePageIndex", PageIndex); + } + public async void SaveMusicLibraryIndex() + { + await _localSettingsService.SaveSettingAsync("HomeMusicLibraryIndex", MusicLibraryIndex); } } diff --git a/The Untamed Music Player/ViewModels/LocalAlbumsViewModel.cs b/The Untamed Music Player/ViewModels/LocalAlbumsViewModel.cs index cea1e5c..34da885 100644 --- a/The Untamed Music Player/ViewModels/LocalAlbumsViewModel.cs +++ b/The Untamed Music Player/ViewModels/LocalAlbumsViewModel.cs @@ -123,9 +123,9 @@ public partial class LocalAlbumsViewModel : ObservableRecipient { if (sender is Button button && button.DataContext is AlbumInfo albumInfo) { - var tempList = Data.MusicLibrary.GetMusicsByAlbum(albumInfo); + var tempList = Data.MusicLibrary.GetSongsByAlbum(albumInfo); var songList = new ObservableCollection(tempList); - Data.MusicPlayer.SetPlayList($"Songs:Album:{albumInfo.Name}", songList); + Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{albumInfo.Name}", songList); Data.MusicPlayer.PlaySongByPath(songList[0].Path); } } diff --git a/The Untamed Music Player/ViewModels/LocalArtistsViewModel.cs b/The Untamed Music Player/ViewModels/LocalArtistsViewModel.cs index 7ddc49f..e754619 100644 --- a/The Untamed Music Player/ViewModels/LocalArtistsViewModel.cs +++ b/The Untamed Music Player/ViewModels/LocalArtistsViewModel.cs @@ -73,6 +73,16 @@ public partial class LocalArtistsViewModel : ObservableRecipient } } + public void PlayButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.DataContext is ArtistInfo artistInfo) + { + var songList = Data.MusicLibrary.GetSongsByArtist(artistInfo); + Data.MusicPlayer.SetPlayList($"LocalSongs:Artist:{artistInfo.Name}", songList); + Data.MusicPlayer.PlaySongByPath(songList[0].Path); + } + } + public async Task SortArtists() { var sortTask = SortMode switch diff --git a/The Untamed Music Player/ViewModels/LocalSongsViewModel.cs b/The Untamed Music Player/ViewModels/LocalSongsViewModel.cs index 6078012..6b81c68 100644 --- a/The Untamed Music Player/ViewModels/LocalSongsViewModel.cs +++ b/The Untamed Music Player/ViewModels/LocalSongsViewModel.cs @@ -160,7 +160,7 @@ public partial class LocalSongsViewModel : ObservableRecipient public void SongListView_ItemClick(object sender, ItemClickEventArgs e) { - Data.MusicPlayer.SetPlayList("Songs:All", ConvertGroupedToFlatList(), SortMode); + Data.MusicPlayer.SetPlayList("LocalSongs:All", ConvertGroupedToFlatList(), SortMode); if (e.ClickedItem is BriefMusicInfo briefMusicInfo) { Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); @@ -169,7 +169,7 @@ public partial class LocalSongsViewModel : ObservableRecipient public void PlayButton_Click(object sender, RoutedEventArgs e) { - Data.MusicPlayer.SetPlayList("Songs:All", ConvertGroupedToFlatList(), SortMode); + Data.MusicPlayer.SetPlayList("LocalSongs:All", ConvertGroupedToFlatList(), SortMode); if (sender is Button button && button.DataContext is BriefMusicInfo briefMusicInfo) { Data.MusicPlayer.PlaySongByPath(briefMusicInfo.Path); @@ -253,27 +253,11 @@ public partial class LocalSongsViewModel : ObservableRecipient await SortSongs(); } - public ObservableCollection ConvertGroupedToFlatList() + private ObservableCollection ConvertGroupedToFlatList() { - if (_isGrouped) - { - var flatList = new ObservableCollection(); - foreach (var group in GroupedSongList) - { - foreach (var item in group) - { - if (item is BriefMusicInfo musicInfo) - { - flatList.Add(musicInfo); - } - } - } - return flatList; - } - else - { - return NotGroupedSongList; - } + return _isGrouped + ? [.. GroupedSongList.SelectMany(group => group.OfType())] + : NotGroupedSongList; } public object GetSongListViewSource(ICollectionView grouped, ObservableCollection notgrouped) diff --git a/The Untamed Music Player/ViewModels/MusicLibraryViewModel.cs b/The Untamed Music Player/ViewModels/MusicLibraryViewModel.cs index 17670f7..016a416 100644 --- a/The Untamed Music Player/ViewModels/MusicLibraryViewModel.cs +++ b/The Untamed Music Player/ViewModels/MusicLibraryViewModel.cs @@ -25,7 +25,7 @@ public partial class MusicLibraryViewModel : ObservableRecipient private async Task InitializeLibraryAsync() { Data.MusicLibrary.PropertyChanged += MusicLibrary_PropertyChanged; - if (!Data.hasMusicLibraryLoaded) + if (!Data.HasMusicLibraryLoaded) { await Task.Run(Data.MusicLibrary.LoadLibraryAsync); } diff --git a/The Untamed Music Player/ViewModels/OnlineSongsViewModel.cs b/The Untamed Music Player/ViewModels/OnlineSongsViewModel.cs new file mode 100644 index 0000000..ba57798 --- /dev/null +++ b/The Untamed Music Player/ViewModels/OnlineSongsViewModel.cs @@ -0,0 +1,9 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using The_Untamed_Music_Player.Contracts.Models; +using The_Untamed_Music_Player.Models; + +namespace The_Untamed_Music_Player.ViewModels; + +public partial class OnlineSongsViewModel : ObservableRecipient +{ +} diff --git a/The Untamed Music Player/ViewModels/SettingsViewModel.cs b/The Untamed Music Player/ViewModels/SettingsViewModel.cs index 3b2f85c..6a8dc85 100644 --- a/The Untamed Music Player/ViewModels/SettingsViewModel.cs +++ b/The Untamed Music Player/ViewModels/SettingsViewModel.cs @@ -20,10 +20,7 @@ public partial class SettingsViewModel : ObservableRecipient private readonly IThemeSelectorService _themeSelectorService; private readonly ILocalSettingsService _localSettingsService; - public ICommand SwitchThemeCommand - { - get; - } + public ICommand SwitchThemeCommand { get; } /// /// 是否显示文件夹为空信息 @@ -69,19 +66,13 @@ public partial class SettingsViewModel : ObservableRecipient /// 深浅色主题 /// [ObservableProperty] - public partial ElementTheme ElementTheme - { - get; set; - } + public partial ElementTheme ElementTheme { get; set; } /// /// 版本信息 /// [ObservableProperty] - public partial string VersionDescription - { - get; set; - } + public partial string VersionDescription { get; set; } /// /// 选中的字体 diff --git a/The Untamed Music Player/Views/AlbumDetailPage.xaml.cs b/The Untamed Music Player/Views/AlbumDetailPage.xaml.cs index 861e425..9c785b2 100644 --- a/The Untamed Music Player/Views/AlbumDetailPage.xaml.cs +++ b/The Untamed Music Player/Views/AlbumDetailPage.xaml.cs @@ -57,7 +57,7 @@ public sealed partial class AlbumDetailPage : Page protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { base.OnNavigatingFrom(e); - if (e.NavigationMode == NavigationMode.Back) + if (e.NavigationMode == NavigationMode.Back && e.SourcePageType != typeof(ArtistDetailPage)) { ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("BackConnectedAnimation", CoverArt); } diff --git a/The Untamed Music Player/Views/ArtistDetailPage.xaml b/The Untamed Music Player/Views/ArtistDetailPage.xaml index f0759aa..d20e214 100644 --- a/The Untamed Music Player/Views/ArtistDetailPage.xaml +++ b/The Untamed Music Player/Views/ArtistDetailPage.xaml @@ -117,6 +117,7 @@ @@ -197,6 +198,7 @@ IsThreeState="False" Visibility="Collapsed"/> + + + diff --git a/The Untamed Music Player/Views/HomePage.xaml.cs b/The Untamed Music Player/Views/HomePage.xaml.cs index 1596e72..80e403a 100644 --- a/The Untamed Music Player/Views/HomePage.xaml.cs +++ b/The Untamed Music Player/Views/HomePage.xaml.cs @@ -1,5 +1,7 @@ +using CommunityToolkit.WinUI.Controls; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; - +using The_Untamed_Music_Player.Models; using The_Untamed_Music_Player.ViewModels; namespace The_Untamed_Music_Player.Views; @@ -15,10 +17,12 @@ public sealed partial class HomePage : Page { ViewModel = App.GetService(); InitializeComponent(); + Data.HomePage = this; + ViewModel.Navigate(ViewModel.PageIndex, true); } - private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) + public Frame GetFrame() { - + return SelectFrame; } } diff --git a/The Untamed Music Player/Views/LocalArtistsPage.xaml b/The Untamed Music Player/Views/LocalArtistsPage.xaml index f4ed608..6d8c21e 100644 --- a/The Untamed Music Player/Views/LocalArtistsPage.xaml +++ b/The Untamed Music Player/Views/LocalArtistsPage.xaml @@ -92,7 +92,7 @@ Width="32" Height="32" Margin="13,0,0,8" HorizontalAlignment="Left" VerticalAlignment="Bottom" - Canvas.ZIndex="2" + Canvas.ZIndex="2" Click="PlayButton_Click" Style="{StaticResource CircularButtonStyle}" Visibility="Collapsed"> diff --git a/The Untamed Music Player/Views/LocalArtistsPage.xaml.cs b/The Untamed Music Player/Views/LocalArtistsPage.xaml.cs index 7c2d752..9388db2 100644 --- a/The Untamed Music Player/Views/LocalArtistsPage.xaml.cs +++ b/The Untamed Music Player/Views/LocalArtistsPage.xaml.cs @@ -87,4 +87,9 @@ public sealed partial class LocalArtistsPage : Page gridView.Focus(FocusState.Programmatic); } } + + private void PlayButton_Click(object sender, RoutedEventArgs e) + { + ViewModel.PlayButton_Click(sender, e); + } } diff --git a/The Untamed Music Player/Views/LocalSongsPage.xaml b/The Untamed Music Player/Views/LocalSongsPage.xaml index d0ef5ee..3b30b53 100644 --- a/The Untamed Music Player/Views/LocalSongsPage.xaml +++ b/The Untamed Music Player/Views/LocalSongsPage.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helper="using:The_Untamed_Music_Player.Helpers" + xmlns:contract="using:The_Untamed_Music_Player.Contracts.Models" xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:model="using:The_Untamed_Music_Player.Models" diff --git a/The Untamed Music Player/Views/OnlineAlbumsPage.xaml b/The Untamed Music Player/Views/OnlineAlbumsPage.xaml new file mode 100644 index 0000000..f553de0 --- /dev/null +++ b/The Untamed Music Player/Views/OnlineAlbumsPage.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/The Untamed Music Player/Views/OnlineAlbumsPage.xaml.cs b/The Untamed Music Player/Views/OnlineAlbumsPage.xaml.cs new file mode 100644 index 0000000..163865e --- /dev/null +++ b/The Untamed Music Player/Views/OnlineAlbumsPage.xaml.cs @@ -0,0 +1,10 @@ +using Microsoft.UI.Xaml.Controls; + +namespace The_Untamed_Music_Player.Views; +public sealed partial class OnlineAlbumsPage : Page +{ + public OnlineAlbumsPage() + { + InitializeComponent(); + } +} diff --git a/The Untamed Music Player/Views/OnlineArtistsPage.xaml b/The Untamed Music Player/Views/OnlineArtistsPage.xaml new file mode 100644 index 0000000..889ae53 --- /dev/null +++ b/The Untamed Music Player/Views/OnlineArtistsPage.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/The Untamed Music Player/Views/OnlineArtistsPage.xaml.cs b/The Untamed Music Player/Views/OnlineArtistsPage.xaml.cs new file mode 100644 index 0000000..fdf19c8 --- /dev/null +++ b/The Untamed Music Player/Views/OnlineArtistsPage.xaml.cs @@ -0,0 +1,10 @@ +using Microsoft.UI.Xaml.Controls; + +namespace The_Untamed_Music_Player.Views; +public sealed partial class OnlineArtistsPage : Page +{ + public OnlineArtistsPage() + { + InitializeComponent(); + } +} diff --git a/The Untamed Music Player/Views/OnlinePlayListsPage.xaml b/The Untamed Music Player/Views/OnlinePlayListsPage.xaml new file mode 100644 index 0000000..09369d2 --- /dev/null +++ b/The Untamed Music Player/Views/OnlinePlayListsPage.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/The Untamed Music Player/Views/OnlinePlayListsPage.xaml.cs b/The Untamed Music Player/Views/OnlinePlayListsPage.xaml.cs new file mode 100644 index 0000000..7eb8a04 --- /dev/null +++ b/The Untamed Music Player/Views/OnlinePlayListsPage.xaml.cs @@ -0,0 +1,10 @@ +using Microsoft.UI.Xaml.Controls; + +namespace The_Untamed_Music_Player.Views; +public sealed partial class OnlinePlayListsPage : Page +{ + public OnlinePlayListsPage() + { + InitializeComponent(); + } +} diff --git a/The Untamed Music Player/Views/OnlineSongsPage.xaml b/The Untamed Music Player/Views/OnlineSongsPage.xaml new file mode 100644 index 0000000..c2a00ba --- /dev/null +++ b/The Untamed Music Player/Views/OnlineSongsPage.xaml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/The Untamed Music Player/Views/OnlineSongsPage.xaml.cs b/The Untamed Music Player/Views/OnlineSongsPage.xaml.cs new file mode 100644 index 0000000..2dcdc58 --- /dev/null +++ b/The Untamed Music Player/Views/OnlineSongsPage.xaml.cs @@ -0,0 +1,67 @@ +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using The_Untamed_Music_Player.Models; +using The_Untamed_Music_Player.ViewModels; + +namespace The_Untamed_Music_Player.Views; +public sealed partial class OnlineSongsPage : Page +{ + private ScrollViewer? _scrollViewer; + + public OnlineSongsViewModel ViewModel + { + get; set; + } + + public OnlineSongsPage() + { + ViewModel = App.GetService(); + InitializeComponent(); + } + + private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e) + { + var grid = sender as Grid; + var checkBox = grid?.FindName("ItemCheckBox") as CheckBox; + var playButton = grid?.FindName("PlayButton") as Button; + if (checkBox != null) + { + checkBox.Visibility = Visibility.Visible; + } + if (playButton != null) + { + playButton.Visibility = Visibility.Visible; + } + } + + private void Grid_PointerExited(object sender, PointerRoutedEventArgs e) + { + var grid = sender as Grid; + var checkBox = grid?.FindName("ItemCheckBox") as CheckBox; + var playButton = grid?.FindName("PlayButton") as Button; + if (checkBox != null) + { + checkBox.Visibility = Visibility.Collapsed; + } + if (playButton != null) + { + playButton.Visibility = Visibility.Collapsed; + } + } + + private void OnlineSongsPage_Loaded(object sender, RoutedEventArgs e) + { + _scrollViewer = SongListView.FindDescendant() ?? throw new Exception("Cannot find ScrollViewer in ListView"); // 检索 ListView 内部使用的 ScrollViewer + + _scrollViewer.ViewChanged += async (s, e) => + { + if (!Data.OnlineMusicLibrary.OnlineMusicInfoList.HasAllLoaded && _scrollViewer.VerticalOffset + _scrollViewer.ViewportHeight >= _scrollViewer.ExtentHeight - 50) + { + await Data.OnlineMusicLibrary.SearchMore(); + await Task.Delay(3000); + } + }; + } +} diff --git a/The Untamed Music Player/Views/PropertiesDialog.xaml.cs b/The Untamed Music Player/Views/PropertiesDialog.xaml.cs index 2fc501f..5d767f3 100644 --- a/The Untamed Music Player/Views/PropertiesDialog.xaml.cs +++ b/The Untamed Music Player/Views/PropertiesDialog.xaml.cs @@ -1,13 +1,14 @@ using System.Diagnostics; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using The_Untamed_Music_Player.Contracts.Models; using The_Untamed_Music_Player.Models; namespace The_Untamed_Music_Player.Views; public sealed partial class PropertiesDialog : ContentDialog { - public DetailedMusicInfo Music { get; set; } = Data.MusicPlayer.CurrentMusic; + public IDetailedMusicInfoBase Music { get; set; } = Data.MusicPlayer.CurrentMusic!; public PropertiesDialog() { diff --git a/The Untamed Music Player/Views/RootPlayBarView.xaml.cs b/The Untamed Music Player/Views/RootPlayBarView.xaml.cs index 6969c61..ccdf58c 100644 --- a/The Untamed Music Player/Views/RootPlayBarView.xaml.cs +++ b/The Untamed Music Player/Views/RootPlayBarView.xaml.cs @@ -1,5 +1,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using The_Untamed_Music_Player.Contracts.Models; using The_Untamed_Music_Player.Helpers; using The_Untamed_Music_Player.Models; using The_Untamed_Music_Player.ViewModels; @@ -90,7 +91,7 @@ public sealed partial class RootPlayBarView : Page }; } - public Visibility GetArtistAndAlbumStrVisibility(DetailedMusicInfo detailedmusicinfo) + public Visibility GetArtistAndAlbumStrVisibility(IDetailedMusicInfoBase detailedmusicinfo) { return detailedmusicinfo.ArtistAndAlbumStr == "" ? Visibility.Collapsed : Visibility.Visible; }