mirror of
https://github.com/LanZhan-Harmony/WindowsMusicPlayer-TheUntamedMusicPlayer.git
synced 2026-05-06 19:20:18 +08:00
2025/2/16
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Title>OnlineMusicApi</Title>
|
||||
<Product>OnlineMusicApi</Product>
|
||||
<Description>在线音乐API</Description>
|
||||
<Copyright>Copyright © 2019 Binaryify / Wwh</Copyright>
|
||||
<Version>3.25.3.0</Version>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyOriginatorKeyFile>../OnlineMusicApi.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<NoWarn>IDE1006;CS0436;CS3021</NoWarn>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputPath>..\bin\$(Configuration)</OutputPath>
|
||||
<PackageId>OnlineMusicApi</PackageId>
|
||||
<Authors>Binaryify / Wwh</Authors>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<PackageProjectUrl>https://github.com/wwh1004/OnlineMusicApi</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,8 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NeteaseCloudMusicApi;
|
||||
|
||||
internal sealed class QueryCollection : List<KeyValuePair<string, string>>
|
||||
{
|
||||
public void Add(string key, string value) => Add(new KeyValuePair<string, string>(key, value));
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -85,6 +85,7 @@ public partial class App : Application
|
||||
services.AddSingleton<LocalArtistsViewModel>();
|
||||
services.AddTransient<AlbumDetailViewModel>();
|
||||
services.AddTransient<ArtistDetailViewModel>();
|
||||
services.AddTransient<OnlineSongsViewModel>();
|
||||
services.AddTransient<DesktopLyricViewModel>();
|
||||
|
||||
// Configuration
|
||||
|
||||
90
The Untamed Music Player/Contracts/Models/IMusicInfoBase.cs
Normal file
90
The Untamed Music Player/Contracts/Models/IMusicInfoBase.cs
Normal file
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取参与创作的艺术家名字符串
|
||||
/// </summary>
|
||||
/// <param name="artists"></param>
|
||||
/// <returns></returns>
|
||||
static string GetArtistsStr(string[] artists) => string.Join(", ", artists);
|
||||
|
||||
/// <summary>
|
||||
/// 获取时长字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static string GetDurationStr(TimeSpan duration) => duration.Hours > 0 ? $"{duration:hh\\:mm\\:ss}" : $"{duration:mm\\:ss}";
|
||||
|
||||
/// <summary>
|
||||
/// 获取发行年份字符串
|
||||
/// </summary>
|
||||
/// <param name="year"></param>
|
||||
/// <returns></returns>
|
||||
static string GetYearStr(ushort year) => year is 0 ? "" : year.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 获取文本前景色
|
||||
/// </summary>
|
||||
/// <param name="currentMusic"></param>
|
||||
/// <param name="isDarkTheme"></param>
|
||||
/// <returns>如果是当前播放歌曲, 返回主题色, 如果不是, 根据当前主题返回黑色或白色</returns>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取专辑艺术家字符串
|
||||
/// </summary>
|
||||
/// <param name="albumArtists"></param>
|
||||
/// <returns></returns>
|
||||
static string GetAlbumArtistsStr(string[] albumArtists) => string.Join(", ", albumArtists);
|
||||
|
||||
/// <summary>
|
||||
/// 获取艺术家和专辑名字符串
|
||||
/// </summary>
|
||||
/// <param name="album"></param>
|
||||
/// <param name="artistsStr"></param>
|
||||
/// <returns></returns>
|
||||
static string GetArtistAndAlbumStr(string album, string artistsStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(artistsStr))
|
||||
{
|
||||
return album ?? "";
|
||||
}
|
||||
if (string.IsNullOrEmpty(album))
|
||||
{
|
||||
return artistsStr;
|
||||
}
|
||||
return $"{artistsStr} • {album}";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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<IBriefOnlineMusicInfo>
|
||||
{
|
||||
protected string _keyWords = "";
|
||||
public bool HasAllLoaded { get; set; } = false;
|
||||
|
||||
public abstract Task SearchAsync(string keyWords);
|
||||
public abstract Task SearchMore();
|
||||
public abstract Task<List<SearchResult>> GetSearchResultAsync(string keyWords);
|
||||
}
|
||||
@@ -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<BriefMusicInfo> SongList { get; set; } = [.. Data.MusicLibrary.GetMusicsByAlbum(albumInfo)];
|
||||
public List<BriefMusicInfo> SongList { get; set; } = [.. Data.MusicLibrary.GetSongsByAlbum(albumInfo)];
|
||||
}
|
||||
|
||||
public class AlbumInfo
|
||||
|
||||
@@ -11,14 +11,10 @@ public static class Data
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 软件显示名称
|
||||
@@ -43,63 +39,25 @@ public static class Data
|
||||
/// <summary>
|
||||
/// 是否显示歌词背景
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 歌手分隔符
|
||||
@@ -25,20 +26,15 @@ public class BriefMusicInfo
|
||||
public string Folder { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 项目类型
|
||||
/// 歌曲名
|
||||
/// </summary>
|
||||
public string ItemType { get; set; } = "";
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 专辑名, 为空时返回"未知专辑"
|
||||
/// </summary>
|
||||
public virtual string Album { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲名
|
||||
/// </summary>
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 参与创作的艺术家数组
|
||||
/// </summary>
|
||||
@@ -61,7 +57,7 @@ public class BriefMusicInfo
|
||||
public TimeSpan Duration { get; set; } = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// 时长字符串
|
||||
/// 时长字符串, 为空时返回00:00
|
||||
/// </summary>
|
||||
public virtual string DurationStr { get; set; } = "";
|
||||
|
||||
@@ -71,17 +67,14 @@ public class BriefMusicInfo
|
||||
public ushort Year { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 发行年份字符串, 为0时返回空字符串
|
||||
/// 发行年份字符串, 为0时返回""
|
||||
/// </summary>
|
||||
public string YearStr { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 封面(可能为空)
|
||||
/// </summary>
|
||||
public virtual BitmapImage? Cover
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public virtual BitmapImage? Cover { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 流派数组
|
||||
@@ -98,9 +91,7 @@ public class BriefMusicInfo
|
||||
/// </summary>
|
||||
public long ModifiedDate { get; set; } = 0;
|
||||
|
||||
public BriefMusicInfo()
|
||||
{
|
||||
}
|
||||
public BriefMusicInfo() { }
|
||||
|
||||
/// <summary>
|
||||
/// 异步工厂方法
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取参与创作的艺术家名字符串, 为空时返回"未知艺术家"
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetArtistsStr() => string.Join(", ", Artists);
|
||||
|
||||
/// <summary>
|
||||
/// 获取时长字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetDurationStr() => Duration.Hours > 0 ? $"{Duration:hh\\:mm\\:ss}" : $"{Duration:mm\\:ss}";
|
||||
|
||||
/// <summary>
|
||||
/// 获取流派字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetGenreStr() => string.Join(", ", Genre);
|
||||
|
||||
/// <summary>
|
||||
/// 获取发行年份字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetYearStr() => Year is 0 ? "" : Year.ToString();
|
||||
protected static string GetGenreStr(string[] genre) => string.Join(", ", genre);
|
||||
|
||||
/// <summary>
|
||||
/// 获取文本前景色
|
||||
@@ -226,7 +198,7 @@ public class BriefMusicInfo
|
||||
/// <param name="currentMusic"></param>
|
||||
/// <param name="isDarkTheme"></param>
|
||||
/// <returns>如果是当前播放歌曲, 返回主题色, 如果不是, 根据当前主题返回黑色或白色</returns>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 专辑名, 为空时返回""
|
||||
/// </summary>
|
||||
@@ -256,20 +230,15 @@ public class DetailedMusicInfo : BriefMusicInfo
|
||||
public override string GenreStr { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 时长字符串
|
||||
/// 时长字符串, 为空时返回""
|
||||
/// </summary>
|
||||
public override string DurationStr { get; set; } = "";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 专辑艺术家数组
|
||||
/// 项目类型, 为空时返回""
|
||||
/// </summary>
|
||||
private string[] AlbumArtists
|
||||
{
|
||||
get;
|
||||
set => field = [.. value
|
||||
.SelectMany(artist => artist.Split(_delimiters, StringSplitOptions.RemoveEmptyEntries))
|
||||
.Distinct()];
|
||||
} = [];
|
||||
public string ItemType { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 专辑艺术家字符串, 为空时返回""
|
||||
@@ -277,17 +246,14 @@ public class DetailedMusicInfo : BriefMusicInfo
|
||||
public string AlbumArtistsStr { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 艺术家和专辑名字符串
|
||||
/// 艺术家和专辑名字符串, 为空时返回""
|
||||
/// </summary>
|
||||
public string ArtistAndAlbumStr { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 清晰封面(可能为空)
|
||||
/// </summary>
|
||||
public override BitmapImage? Cover
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public override BitmapImage? Cover { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 封面缓冲数据
|
||||
@@ -295,24 +261,20 @@ public class DetailedMusicInfo : BriefMusicInfo
|
||||
public byte[] CoverBuffer { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 比特率
|
||||
/// 比特率, 为空时返回""
|
||||
/// </summary>
|
||||
public string BitRate { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 曲目
|
||||
/// 曲目, 为空时返回""
|
||||
/// </summary>
|
||||
public string Track { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 歌词
|
||||
/// 歌词, 为空时返回""
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取参与创作的艺术家名字符串, 为空时返回""
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override string GetArtistsStr() => string.Join(", ", Artists);
|
||||
|
||||
/// <summary>
|
||||
/// 获取时长字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override string GetDurationStr() => Duration.Hours > 0 ? $"{Duration:hh\\:mm\\:ss}" : $"{Duration:mm\\:ss}";
|
||||
|
||||
/// <summary>
|
||||
/// 获取流派字符串, 为空时返回""
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override string GetGenreStr() => string.Join(", ", Genre);
|
||||
|
||||
/// <summary>
|
||||
/// 获取专辑艺术家字符串, 为空时返回""
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetAlbumArtistsStr() => string.Join(", ", AlbumArtists);
|
||||
|
||||
/// <summary>
|
||||
/// 获取艺术家和专辑名字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetArtistAndAlbumStr(string artistsStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(artistsStr))
|
||||
{
|
||||
return Album ?? "";
|
||||
}
|
||||
if (string.IsNullOrEmpty(Album))
|
||||
{
|
||||
return artistsStr;
|
||||
}
|
||||
return $"{artistsStr} • {Album}";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 调度器队列
|
||||
@@ -105,7 +105,7 @@ public partial class MusicLibrary : ObservableObject
|
||||
await _librarySemaphore.WaitAsync(); // 等待信号量, 只允许一个线程访问此函数
|
||||
try
|
||||
{
|
||||
Data.hasMusicLibraryLoaded = true;
|
||||
Data.HasMusicLibraryLoaded = true;
|
||||
var loadMusicTasks = new List<Task>();
|
||||
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<BriefMusicInfo> GetMusicsByAlbum(AlbumInfo albumInfo)
|
||||
{
|
||||
var list = new List<BriefMusicInfo>();
|
||||
var albumName = albumInfo.Name;
|
||||
/// <summary>
|
||||
/// 根据专辑信息获取歌曲列表
|
||||
/// </summary>
|
||||
/// <param name="albumInfo"></param>
|
||||
/// <returns></returns>
|
||||
public IOrderedEnumerable<BriefMusicInfo> 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);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据艺术家信息获取专辑列表
|
||||
/// </summary>
|
||||
/// <param name="artistInfo"></param>
|
||||
/// <returns></returns>
|
||||
public List<BriefAlbumInfo> 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<BriefAlbumInfo> GetAlbumsByArtist(ArtistInfo artistInfo)
|
||||
{
|
||||
var list = new List<BriefAlbumInfo>();
|
||||
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())];
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据艺术家信息获取歌曲列表
|
||||
/// </summary>
|
||||
/// <param name="artistInfo"></param>
|
||||
/// <returns></returns>
|
||||
public ObservableCollection<BriefMusicInfo> GetSongsByArtist(ArtistInfo artistInfo) => [.. artistInfo.Albums
|
||||
.OrderBy(album => album, new AlbumTitleComparer())
|
||||
.SelectMany(album => GetSongsByAlbum(Albums[album]))];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
private ThreadPoolTimer? _positionUpdateTimer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 线程锁开启状态, true为开启, false为关闭
|
||||
/// </summary>
|
||||
@@ -100,6 +100,11 @@ public partial class MusicPlayer : ObservableRecipient
|
||||
/// </summary>
|
||||
public MediaPlayer Player { get; set; } = new() { AudioCategory = MediaPlayerAudioCategory.Media };
|
||||
|
||||
/// <summary>
|
||||
/// 歌曲来源模式, 0为本地, 1为网易
|
||||
/// </summary>
|
||||
public byte SourceMode { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 随机播放模式, true为开启, false为关闭.
|
||||
/// </summary>
|
||||
@@ -135,8 +140,8 @@ public partial class MusicPlayer : ObservableRecipient
|
||||
/// 当前播放歌曲
|
||||
/// </summary>
|
||||
[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
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="list"></param>
|
||||
public async void SetPlayList(string name, ObservableCollection<BriefMusicInfo> list, byte sortmode = 0)
|
||||
public async void SetPlayList(string name, IEnumerable<BriefMusicInfo> 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<BriefMusicInfo>([.. 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
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
221
The Untamed Music Player/Models/OnlineMusicLibrary.cs
Normal file
221
The Untamed Music Player/Models/OnlineMusicLibrary.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示加载进度环
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
public partial bool IsSearchProgressRingActive { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示加载更多进度环
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
public partial bool IsSearchMoreProgressRingActive { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial IBriefOnlineMusicInfoList OnlineMusicInfoList { get; set; } = null!;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial List<SearchResult> 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<bool> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
The Untamed Music Player/Models/SearchResult.cs
Normal file
11
The Untamed Music Player/Models/SearchResult.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<CloudBriefOnlineMusicInfo> 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<string, string> { { "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<string, string> { { "id", $"{info.ID}" } });
|
||||
var albumTask = api.RequestAsync(CloudMusicApiProviders.Album, new Dictionary<string, string> { { "id", $"{info.AlbumID}" } });
|
||||
var lyricTask = api.RequestAsync(CloudMusicApiProviders.Lyric, new Dictionary<string, string> { { "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<CloudDetailedOnlineMusicInfo> 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<string, string> { { "id", $"{info.ID}" } });
|
||||
var albumTask = api.RequestAsync(CloudMusicApiProviders.Album, new Dictionary<string, string> { { "id", $"{info.AlbumID}" } });
|
||||
var lyricTask = api.RequestAsync(CloudMusicApiProviders.Lyric, new Dictionary<string, string> { { "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<bool> LoadCoverAsync(byte[] coverBuffer, CloudDetailedOnlineMusicInfo info)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<string, string>
|
||||
{
|
||||
{ "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<string, string>
|
||||
{
|
||||
{ "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<Task>();
|
||||
|
||||
// 每组 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<List<SearchResult>> GetSearchResultAsync(string keyWords)
|
||||
{
|
||||
var (isOk, result) = await _api.RequestAsync(CloudMusicApiProviders.SearchSuggest, new Dictionary<string, string> { { "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<SearchResult>();
|
||||
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<string> items, int limit, string icon, List<SearchResult> list)
|
||||
{
|
||||
foreach (var item in items.Take(limit))
|
||||
{
|
||||
list.Add(new SearchResult { Icon = icon, Label = item });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
/// <summary>
|
||||
/// 网易云音乐API
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
/// <summary>
|
||||
/// 网易云音乐API相关信息提供者
|
||||
/// </summary>
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// 发送/删除评论
|
||||
/// </summary>
|
||||
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 =>
|
||||
{
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace The_Untamed_Music_Player.OnlineAPIs.CloudMusicAPI;
|
||||
internal sealed partial class QueryCollection : List<KeyValuePair<string, string>>
|
||||
{
|
||||
public void Add(string key, string value) => Add(new KeyValuePair<string, string>(key, value));
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -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
|
||||
{
|
||||
@@ -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<KeyValuePair<string, string>> queries)
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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,
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
@@ -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");
|
||||
@@ -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;
|
||||
@@ -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 = [
|
||||
@@ -216,6 +216,9 @@
|
||||
<data name="HaveMusic_Songs.Text" xml:space="preserve">
|
||||
<value>Songs</value>
|
||||
</data>
|
||||
<data name="Home_Playlists.Text" xml:space="preserve">
|
||||
<value>Playlists</value>
|
||||
</data>
|
||||
<data name="HaveMusic_Albums.Text" xml:space="preserve">
|
||||
<value>Albums</value>
|
||||
</data>
|
||||
@@ -606,4 +609,28 @@
|
||||
<data name="AlbumInfo_UnknownYear" xml:space="preserve">
|
||||
<value>Unknown year</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary1.Text" xml:space="preserve">
|
||||
<value>Music library 1</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary6.Text" xml:space="preserve">
|
||||
<value>Music library 6</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary5.Text" xml:space="preserve">
|
||||
<value>Music library 5</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary4.Text" xml:space="preserve">
|
||||
<value>Music library 4</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary3.Text" xml:space="preserve">
|
||||
<value>Music library 3</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary2.Text" xml:space="preserve">
|
||||
<value>Music library 2</value>
|
||||
</data>
|
||||
<data name="Home_NetworkError.Text" xml:space="preserve">
|
||||
<value>Network error, please retry later</value>
|
||||
</data>
|
||||
<data name="Home_Retry.Text" xml:space="preserve">
|
||||
<value>Retry</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -216,6 +216,9 @@
|
||||
<data name="HaveMusic_Songs.Text" xml:space="preserve">
|
||||
<value>歌曲</value>
|
||||
</data>
|
||||
<data name="Home_Playlists.Text" xml:space="preserve">
|
||||
<value>歌单</value>
|
||||
</data>
|
||||
<data name="HaveMusic_Albums.Text" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
@@ -606,4 +609,28 @@
|
||||
<data name="AlbumInfo_UnknownYear" xml:space="preserve">
|
||||
<value>未知年份</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary1.Text" xml:space="preserve">
|
||||
<value>乐库1</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary6.Text" xml:space="preserve">
|
||||
<value>乐库6</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary5.Text" xml:space="preserve">
|
||||
<value>乐库5</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary4.Text" xml:space="preserve">
|
||||
<value>乐库4</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary3.Text" xml:space="preserve">
|
||||
<value>乐库3</value>
|
||||
</data>
|
||||
<data name="Home_MusicLibrary2.Text" xml:space="preserve">
|
||||
<value>乐库2</value>
|
||||
</data>
|
||||
<data name="Home_NetworkError.Text" xml:space="preserve">
|
||||
<value>网络异常,请稍后再试</value>
|
||||
</data>
|
||||
<data name="Home_Retry.Text" xml:space="preserve">
|
||||
<value>重试</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -37,6 +37,12 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\**\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Views\OnlineAlbumsPage.xaml" />
|
||||
<None Remove="Views\OnlineArtistsPage.xaml" />
|
||||
<None Remove="Views\OnlinePlayListsPage.xaml" />
|
||||
<None Remove="Views\OnlineSongsPage.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
@@ -45,16 +51,19 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.250129-pull-636.2034" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250129-preview2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250129-preview2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.1.240916" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.241112-preview1" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250129-preview2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250129-preview2" />
|
||||
<PackageReference Include="FluentIcons.WinUI" Version="1.1.271" />
|
||||
<PackageReference Include="hyjiacan.pinyin4net" Version="4.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.1" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.2" />
|
||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="WinUIEx" Version="2.5.1" />
|
||||
</ItemGroup>
|
||||
@@ -63,6 +72,18 @@
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Page Update="Views\OnlinePlayListsPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="Views\OnlineArtistsPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="Views\OnlineAlbumsPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="Views\OnlineSongsPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="Views\DesktopLyricWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -74,6 +95,8 @@
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<WebView2EnableCsWinRTProjection>False</WebView2EnableCsWinRTProjection>
|
||||
<WebView2UseWinRT>False</WebView2UseWinRT>
|
||||
<WebView2LoaderPreference>Dynamic</WebView2LoaderPreference>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
@@ -83,30 +106,36 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<BriefMusicInfo>(briefAlbumInfo.SongList);
|
||||
Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{briefAlbumInfo.Name}", songList);
|
||||
Data.MusicPlayer.PlaySongByPath(songList[0].Path);
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<BriefMusicInfo> ConvertAllSongsToFlatList()
|
||||
{
|
||||
return [.. AlbumList.SelectMany(album => album.SongList)];
|
||||
}
|
||||
|
||||
public async Task<int> LoadSelectionBarSelectedIndex()
|
||||
|
||||
@@ -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<ILocalSettingsService>();
|
||||
|
||||
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<byte> LoadPageIndex()
|
||||
{
|
||||
return await _localSettingsService.ReadSettingAsync<byte>("HomePageIndex");
|
||||
}
|
||||
public async Task<byte> LoadMusicLibraryIndex()
|
||||
{
|
||||
return await _localSettingsService.ReadSettingAsync<byte>("HomeMusicLibraryIndex");
|
||||
}
|
||||
public async void SavePageIndex()
|
||||
{
|
||||
await _localSettingsService.SaveSettingAsync("HomePageIndex", PageIndex);
|
||||
}
|
||||
public async void SaveMusicLibraryIndex()
|
||||
{
|
||||
await _localSettingsService.SaveSettingAsync("HomeMusicLibraryIndex", MusicLibraryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<BriefMusicInfo>(tempList);
|
||||
Data.MusicPlayer.SetPlayList($"Songs:Album:{albumInfo.Name}", songList);
|
||||
Data.MusicPlayer.SetPlayList($"LocalSongs:Album:{albumInfo.Name}", songList);
|
||||
Data.MusicPlayer.PlaySongByPath(songList[0].Path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<BriefMusicInfo> ConvertGroupedToFlatList()
|
||||
private ObservableCollection<BriefMusicInfo> ConvertGroupedToFlatList()
|
||||
{
|
||||
if (_isGrouped)
|
||||
{
|
||||
var flatList = new ObservableCollection<BriefMusicInfo>();
|
||||
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<BriefMusicInfo>())]
|
||||
: NotGroupedSongList;
|
||||
}
|
||||
|
||||
public object GetSongListViewSource(ICollectionView grouped, ObservableCollection<BriefMusicInfo> notgrouped)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示文件夹为空信息
|
||||
@@ -69,19 +66,13 @@ public partial class SettingsViewModel : ObservableRecipient
|
||||
/// 深浅色主题
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
public partial ElementTheme ElementTheme
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public partial ElementTheme ElementTheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本信息
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
public partial string VersionDescription
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public partial string VersionDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的字体
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
<ListView x:Name="SongListView"
|
||||
helper:ListViewExtensions.ItemCornerRadius="8"
|
||||
helper:ListViewExtensions.ItemMargin="0,3,0,3" IsItemClickEnabled="True"
|
||||
ItemClick="SongListView_ItemClick"
|
||||
ItemTemplate="{StaticResource SongListViewTemplate}"
|
||||
ItemsSource="{x:Bind SongList}"
|
||||
SelectionMode="None">
|
||||
@@ -197,6 +198,7 @@
|
||||
IsThreeState="False" Visibility="Collapsed"/>
|
||||
<Button x:Name="PlayButton" x:Uid="LocalSongs_PlayButton"
|
||||
Grid.Column="1"
|
||||
Click="SongListViewPlayButton_Click"
|
||||
DataContext="{x:Bind}"
|
||||
Style="{StaticResource SmallPlayButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
@@ -307,6 +309,7 @@
|
||||
Width="32" Height="32"
|
||||
Margin="12,0,0,8" HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="AlbumGridViewPlayButton_Click"
|
||||
DataContext="{x:Bind}"
|
||||
Style="{StaticResource CircularButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
@@ -663,6 +666,7 @@
|
||||
</ListView>
|
||||
<GridView x:Name="AlbumGridView"
|
||||
helper:ListViewExtensions.ItemCornerRadius="8" IsItemClickEnabled="True"
|
||||
ItemClick="AlbumGridView_ItemClick"
|
||||
ItemTemplate="{StaticResource AlbumGridViewTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.AlbumList}"
|
||||
SelectionMode="None">
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using The_Untamed_Music_Player.Models;
|
||||
using The_Untamed_Music_Player.ViewModels;
|
||||
using Windows.Storage.Streams;
|
||||
using EF = CommunityToolkit.WinUI.Animations.Expressions.ExpressionFunctions;
|
||||
@@ -66,13 +67,24 @@ public sealed partial class ArtistDetailPage : Page
|
||||
SelectionBarSelectedIndex = await ViewModel.LoadSelectionBarSelectedIndex();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当页面导航到此页时,将调用此方法。
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("ForwardConnectedAnimation");
|
||||
animation?.TryStart(CoverArt);
|
||||
if (e.NavigationMode == NavigationMode.New)
|
||||
{
|
||||
var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("ForwardConnectedAnimation");
|
||||
animation?.TryStart(CoverArt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当页面从此页导航时,将调用此方法。
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
@@ -346,4 +358,51 @@ public sealed partial class ArtistDetailPage : Page
|
||||
}
|
||||
SelectionBarSelectedIndex = currentSelectedIndex;
|
||||
}
|
||||
|
||||
private void SongListViewPlayButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.SongListViewPlayButton_Click(sender, e);
|
||||
}
|
||||
|
||||
private void SongListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
ViewModel.SongListView_ItemClick(sender, e);
|
||||
}
|
||||
|
||||
private void AlbumGridViewPlayButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.AlbumGridViewPlayButton_Click(sender, e);
|
||||
}
|
||||
|
||||
private void AlbumGridView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is BriefAlbumInfo briefAlbumInfo)
|
||||
{
|
||||
var albumInfo = Data.MusicLibrary.Albums[briefAlbumInfo.Name];
|
||||
if (albumInfo != null)
|
||||
{
|
||||
var grid = (Grid)((ContentControl)AlbumGridView.ContainerFromItem(e.ClickedItem)).ContentTemplateRoot;
|
||||
var border = (Border)grid.Children[1];
|
||||
ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("ForwardConnectedAnimation", border);
|
||||
Data.SelectedAlbum = albumInfo;
|
||||
Data.ShellPage!.GetFrame().Navigate(typeof(AlbumDetailPage), null, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*private void AlbumGridView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Data.SelectedBriefAlbum != null && sender is GridView gridView)
|
||||
{
|
||||
gridView.ScrollIntoView(Data.SelectedBriefAlbum, ScrollIntoViewAlignment.Leading);
|
||||
gridView.UpdateLayout();
|
||||
var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("BackConnectedAnimation");
|
||||
if (animation != null)
|
||||
{
|
||||
animation.Configuration = new DirectConnectedAnimationConfiguration();
|
||||
await gridView.TryStartConnectedAnimationAsync(animation, Data.SelectedBriefAlbum, "CoverBorder");
|
||||
}
|
||||
gridView.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -1,15 +1,37 @@
|
||||
<Page x:Class="The_Untamed_Music_Player.Views.HomePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:The_Untamed_Music_Player.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:The_Untamed_Music_Player.Models"
|
||||
Background="Transparent"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<DataTemplate x:Key="SearchResultTemplate" x:DataType="model:SearchResult">
|
||||
<Grid AutomationProperties.Name="{x:Bind Label}" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon Grid.RowSpan="2"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="{x:Bind Icon}"/>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Label}"
|
||||
TextTrimming="WordEllipsis"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid x:Name="ContentArea">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
@@ -21,14 +43,34 @@
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TitleGrid.Margin" Value="16,36,16,0"/>
|
||||
<Setter Target="AutoSuggestBox.Width" Value="140"/>
|
||||
<Setter Target="Segmented.Margin" Value="16,24,16,0"/>
|
||||
<Setter Target="NetworkErrorFontIcon.FontSize" Value="75"/>
|
||||
<Setter Target="NetworkErrorStackPanel.Spacing" Value="18"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState x:Name="Medium">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="641"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TitleGrid.Margin" Value="56,36,56,0"/>
|
||||
<Setter Target="AutoSuggestBox.Width" Value="190"/>
|
||||
<Setter Target="Segmented.Margin" Value="56,24,56,0"/>
|
||||
<Setter Target="NetworkErrorFontIcon.FontSize" Value="130"/>
|
||||
<Setter Target="NetworkErrorStackPanel.Spacing" Value="38"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Large">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="930"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TitleGrid.Margin" Value="56,36,56,0"/>
|
||||
<Setter Target="AutoSuggestBox.Width" Value="400"/>
|
||||
<Setter Target="Segmented.Margin" Value="56,24,56,0"/>
|
||||
<Setter Target="NetworkErrorFontIcon.FontSize" Value="130"/>
|
||||
<Setter Target="NetworkErrorStackPanel.Spacing" Value="38"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -43,25 +85,84 @@
|
||||
Grid.Column="0"
|
||||
FontSize="40"
|
||||
FontWeight="{x:Bind helper:LanguageRelated.GetTitleFontWeight()}"/>
|
||||
<SelectorBar x:Name="SelectorBar"
|
||||
Grid.Column="1"
|
||||
Margin="18,0,0,0"
|
||||
SelectionChanged="SelectorBar_SelectionChanged">
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage1" x:Uid="HaveMusic_Songs"
|
||||
FontSize="18" IsSelected="True"/>
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage2" x:Uid="HaveMusic_Albums"
|
||||
FontSize="18"/>
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage3" x:Uid="HaveMusic_Artists"
|
||||
FontSize="18"/>
|
||||
</SelectorBar>
|
||||
<AutoSuggestBox x:Uid="Home_SearchOnlineSongs"
|
||||
<ScrollViewer x:Name="SelectorBar"
|
||||
Grid.Column="1"
|
||||
Margin="18,0,10,-9" Padding="0,0,0,9"
|
||||
HorizontalAlignment="Left"
|
||||
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled"
|
||||
VerticalScrollMode="Disabled">
|
||||
<SelectorBar Grid.Column="1"
|
||||
Loaded="{x:Bind ViewModel.SelectorBar_Loaded}"
|
||||
SelectionChanged="{x:Bind ViewModel.SelectorBar_SelectionChanged}">
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage1" x:Uid="HaveMusic_Songs"
|
||||
FontSize="18" IsSelected="True"/>
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage2" x:Uid="HaveMusic_Albums"
|
||||
FontSize="18"/>
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage3" x:Uid="HaveMusic_Artists"
|
||||
FontSize="18"/>
|
||||
<SelectorBarItem x:Name="SelectorBarItemPage4" x:Uid="Home_PlayLists"
|
||||
FontSize="18"/>
|
||||
</SelectorBar>
|
||||
</ScrollViewer>
|
||||
<AutoSuggestBox x:Name="AutoSuggestBox" x:Uid="Home_SearchOnlineSongs"
|
||||
Grid.Column="2"
|
||||
Width="400" Height="32"
|
||||
Margin="0,7,0,0" VerticalAlignment="Center"
|
||||
ItemTemplate="{StaticResource SearchResultTemplate}"
|
||||
ItemsSource="{x:Bind model:Data.OnlineMusicLibrary.SearchResultList, Mode=OneWay}"
|
||||
QueryIcon="Find"
|
||||
QuerySubmitted="{x:Bind ViewModel.SuggestBox_QuerySubmitted}"
|
||||
SuggestionChosen="{x:Bind ViewModel.SuggestBox_SuggestionChosen}"
|
||||
TextChanged="{x:Bind ViewModel.SuggestBox_TextChanged}"/>
|
||||
</Grid>
|
||||
<ScrollViewer x:Name="Segmented"
|
||||
Grid.Row="1"
|
||||
Padding="0,0,0,13"
|
||||
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled"
|
||||
VerticalScrollMode="Disabled">
|
||||
<controls:Segmented SelectedIndex="{x:Bind ViewModel.MusicLibraryIndex, Mode=TwoWay}" SelectionMode="Single">
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary1"
|
||||
Width="90"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary2"/>
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary3"/>
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary4"/>
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary5"/>
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem>
|
||||
<TextBlock x:Uid="Home_MusicLibrary6"/>
|
||||
</controls:SegmentedItem>
|
||||
</controls:Segmented>
|
||||
</ScrollViewer>
|
||||
<ProgressRing Grid.Row="2"
|
||||
Width="50" Height="50"
|
||||
Margin="0,24,0,0"
|
||||
Canvas.ZIndex="1"
|
||||
IsActive="{x:Bind model:Data.OnlineMusicLibrary.IsSearchProgressRingActive, Mode=OneWay}"/>
|
||||
<StackPanel x:Name="NetworkErrorStackPanel"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Canvas.ZIndex="1" Orientation="Horizontal"
|
||||
Visibility="{x:Bind model:Data.OnlineMusicLibrary.NetworkErrorVisibility, Mode=OneWay}">
|
||||
<FontIcon x:Name="NetworkErrorFontIcon" Glyph=""/>
|
||||
<StackPanel VerticalAlignment="Center" Spacing="8">
|
||||
<TextBlock x:Uid="Home_NetworkError" FontSize="29"/>
|
||||
<Button Click="{x:Bind model:Data.OnlineMusicLibrary.RetryButton_Click}" Style="{StaticResource AccentButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="12" Glyph=""/>
|
||||
<TextBlock x:Uid="Home_Retry"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Frame x:Name="SelectFrame" Grid.Row="2"/>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -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<HomeViewModel>();
|
||||
InitializeComponent();
|
||||
Data.HomePage = this;
|
||||
ViewModel.Navigate(ViewModel.PageIndex, true);
|
||||
}
|
||||
|
||||
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
|
||||
public Frame GetFrame()
|
||||
{
|
||||
|
||||
return SelectFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
<FontIcon FontSize="12" Glyph=""/>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
10
The Untamed Music Player/Views/OnlineAlbumsPage.xaml
Normal file
10
The Untamed Music Player/Views/OnlineAlbumsPage.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Page x:Class="The_Untamed_Music_Player.Views.OnlineAlbumsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:The_Untamed_Music_Player.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid/>
|
||||
</Page>
|
||||
10
The Untamed Music Player/Views/OnlineAlbumsPage.xaml.cs
Normal file
10
The Untamed Music Player/Views/OnlineAlbumsPage.xaml.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace The_Untamed_Music_Player.Views;
|
||||
public sealed partial class OnlineAlbumsPage : Page
|
||||
{
|
||||
public OnlineAlbumsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
10
The Untamed Music Player/Views/OnlineArtistsPage.xaml
Normal file
10
The Untamed Music Player/Views/OnlineArtistsPage.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Page x:Class="The_Untamed_Music_Player.Views.OnlineArtistsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:The_Untamed_Music_Player.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid/>
|
||||
</Page>
|
||||
10
The Untamed Music Player/Views/OnlineArtistsPage.xaml.cs
Normal file
10
The Untamed Music Player/Views/OnlineArtistsPage.xaml.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace The_Untamed_Music_Player.Views;
|
||||
public sealed partial class OnlineArtistsPage : Page
|
||||
{
|
||||
public OnlineArtistsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
10
The Untamed Music Player/Views/OnlinePlayListsPage.xaml
Normal file
10
The Untamed Music Player/Views/OnlinePlayListsPage.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Page x:Class="The_Untamed_Music_Player.Views.OnlinePlayListsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:The_Untamed_Music_Player.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid/>
|
||||
</Page>
|
||||
10
The Untamed Music Player/Views/OnlinePlayListsPage.xaml.cs
Normal file
10
The Untamed Music Player/Views/OnlinePlayListsPage.xaml.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace The_Untamed_Music_Player.Views;
|
||||
public sealed partial class OnlinePlayListsPage : Page
|
||||
{
|
||||
public OnlinePlayListsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
185
The Untamed Music Player/Views/OnlineSongsPage.xaml
Normal file
185
The Untamed Music Player/Views/OnlineSongsPage.xaml
Normal file
@@ -0,0 +1,185 @@
|
||||
<Page x:Class="The_Untamed_Music_Player.Views.OnlineSongsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:contract="using:The_Untamed_Music_Player.Contracts.Models"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:The_Untamed_Music_Player.Helpers"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:The_Untamed_Music_Player.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:The_Untamed_Music_Player.Models"
|
||||
Loaded="OnlineSongsPage_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<DataTemplate x:Key="SongListViewTemplate" x:DataType="contract:IBriefOnlineMusicInfo">
|
||||
<Grid Height="52"
|
||||
Background="Transparent" PointerEntered="Grid_PointerEntered"
|
||||
PointerExited="Grid_PointerExited">
|
||||
<Grid.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_Play" Width="216">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_PlayNext">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutSeparator/>
|
||||
<MenuFlyoutSubItem x:Uid="LocalSongs_AddTo">
|
||||
<MenuFlyoutSubItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutSubItem.Icon>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_AddTo_PlayQueue" Width="216">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutSeparator/>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_AddTo_NewPlaylist">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
</MenuFlyoutSubItem>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_Properties">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_ShowAlbum">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_ShowArtist">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutSeparator/>
|
||||
<MenuFlyoutItem x:Uid="LocalSongs_Select">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
</MenuFlyout>
|
||||
</Grid.ContextFlyout>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="32"/>
|
||||
<ColumnDefinition Width="2.2*"/>
|
||||
<ColumnDefinition Width="1.5*"/>
|
||||
<ColumnDefinition Width="1.3*"/>
|
||||
<ColumnDefinition Width="55"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="ItemCheckBox"
|
||||
Grid.Column="0"
|
||||
IsThreeState="False" Visibility="Collapsed"/>
|
||||
<Button x:Name="PlayButton" x:Uid="LocalSongs_PlayButton"
|
||||
Grid.Column="1"
|
||||
DataContext="{x:Bind}"
|
||||
Style="{StaticResource SmallPlayButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
<FontIcon FontSize="16"
|
||||
Foreground="{ThemeResource AccentTextFillColorTertiaryBrush}"
|
||||
Glyph=""/>
|
||||
</Button>
|
||||
<TextBlock Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{x:Bind GetTextForeground(model:Data.MusicPlayer.CurrentMusic, model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"
|
||||
Text="{x:Bind Title}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTipService.ToolTip="{x:Bind Title}"/>
|
||||
<Button Grid.Column="3"
|
||||
Background="Transparent" BorderBrush="Transparent">
|
||||
<TextBlock FontSize="12"
|
||||
Foreground="{x:Bind GetTextForeground(model:Data.MusicPlayer.CurrentMusic, model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"
|
||||
Text="{x:Bind ArtistsStr}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTipService.ToolTip="{x:Bind ArtistsStr}"/>
|
||||
</Button>
|
||||
<Button Grid.Column="4"
|
||||
Background="Transparent" BorderBrush="Transparent">
|
||||
<TextBlock FontSize="12"
|
||||
Foreground="{x:Bind GetTextForeground(model:Data.MusicPlayer.CurrentMusic, model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"
|
||||
Text="{x:Bind Album}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTipService.ToolTip="{x:Bind Album}"/>
|
||||
</Button>
|
||||
<TextBlock Grid.Column="5"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{x:Bind GetTextForeground(model:Data.MusicPlayer.CurrentMusic, model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"
|
||||
Text="{x:Bind YearStr}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTipService.ToolTip="{x:Bind YearStr}"/>
|
||||
<TextBlock Grid.Column="6"
|
||||
Margin="0,0,10,0" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{x:Bind GetTextForeground(model:Data.MusicPlayer.CurrentMusic, model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"
|
||||
Text="{x:Bind DurationStr}"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Narrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SongListView.Padding" Value="12,0,12,0"/>
|
||||
<Setter Target="KeyWordsTextBlock.Margin" Value="12,0,12,0"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="641"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SongListView.Padding" Value="52,0,52,0"/>
|
||||
<Setter Target="KeyWordsTextBlock.Margin" Value="52,0,52,0"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<TextBlock x:Name="KeyWordsTextBlock"
|
||||
Grid.Row="0"
|
||||
Visibility="{x:Bind model:Data.OnlineMusicLibrary.KeyWordsTextBlockVisibility, Mode=OneWay}">
|
||||
<Run FontSize="30" FontWeight="Bold"
|
||||
Text="{x:Bind model:Data.OnlineMusicLibrary.KeyWords, Mode=OneWay}"/>
|
||||
<Run FontSize="20" Text="的搜索结果"/>
|
||||
</TextBlock>
|
||||
<ListView x:Name="SongListView"
|
||||
Grid.Row="1"
|
||||
helper:ListViewExtensions.ItemCornerRadius="8"
|
||||
helper:ListViewExtensions.ItemMargin="0,3,0,3" CanDragItems="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemTemplate="{StaticResource SongListViewTemplate}"
|
||||
ItemsSource="{x:Bind model:Data.OnlineMusicLibrary.OnlineMusicInfoList, Mode=OneWay}"
|
||||
Opacity="{x:Bind model:Data.OnlineMusicLibrary.ListViewOpacity, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.Footer>
|
||||
<ProgressRing Width="25" Height="25"
|
||||
Margin="0,5,0,5"
|
||||
Visibility="{x:Bind model:Data.OnlineMusicLibrary.IsSearchMoreProgressRingActive, Mode=OneWay}"/>
|
||||
</ListView.Footer>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<helper:AlternatingListViewBehavior AlternateBackground="{x:Bind helper:AlternatingListViewBehavior.GetAlternateBackgroundBrush(model:Data.MainViewModel.IsDarkTheme), Mode=OneWay}"/>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Page>
|
||||
67
The Untamed Music Player/Views/OnlineSongsPage.xaml.cs
Normal file
67
The Untamed Music Player/Views/OnlineSongsPage.xaml.cs
Normal file
@@ -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<OnlineSongsViewModel>();
|
||||
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<ScrollViewer>() ?? 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user