Skip to content

Commit

Permalink
Add: 为搜索添加歌曲列表显示,调整搜索逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
Miaoyww committed Nov 4, 2023
1 parent a2b179b commit 5b03a58
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 83 deletions.
5 changes: 4 additions & 1 deletion NonsPlayer.Core/Contracts/Models/INonsModel.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace NonsPlayer.Core.Contracts.Models;
using System.Security.Cryptography;

namespace NonsPlayer.Core.Contracts.Models;

public class INonsModel
{
public long Id { get; set; }
public string Md5 { get; set; }
public string Name { get; set; }

public string AvatarUrl { get; set; }
Expand Down
16 changes: 16 additions & 0 deletions NonsPlayer.Core/Exceptions/SearchExceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NonsPlayer.Core.Exceptions;

public class SearchExceptions: Exception
{
public SearchExceptions()
{
}

public SearchExceptions(string message) : base(message)
{
}

public SearchExceptions(string message, Exception inner) : base(message, inner)
{
}
}
1 change: 1 addition & 0 deletions NonsPlayer.Core/Models/Artist.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class Artist : INonsModel
public List<Music> HotMusics;
public int MusicCount;
public int MvCount;
public string Trans;
public string MiddleAvatarUrl => AvatarUrl + "?param=500y500";

Check warning on line 12 in NonsPlayer.Core/Models/Artist.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

'Artist.MiddleAvatarUrl' hides inherited member 'INonsModel.MiddleAvatarUrl'. Use the new keyword if hiding was intended.

Check warning on line 12 in NonsPlayer.Core/Models/Artist.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x86)

'Artist.MiddleAvatarUrl' hides inherited member 'INonsModel.MiddleAvatarUrl'. Use the new keyword if hiding was intended.

Check warning on line 12 in NonsPlayer.Core/Models/Artist.cs

View workflow job for this annotation

GitHub Actions / build (Release, x86)

'Artist.MiddleAvatarUrl' hides inherited member 'INonsModel.MiddleAvatarUrl'. Use the new keyword if hiding was intended.

Check warning on line 12 in NonsPlayer.Core/Models/Artist.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

'Artist.MiddleAvatarUrl' hides inherited member 'INonsModel.MiddleAvatarUrl'. Use the new keyword if hiding was intended.
public string SmallAvaterUrl => AvatarUrl + "?param=100y100";

Expand Down
72 changes: 72 additions & 0 deletions NonsPlayer.Core/Models/SearchResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json.Linq;
using NonsPlayer.Core.Adapters;
using NonsPlayer.Core.Api;
using NonsPlayer.Core.Contracts.Models;
using NonsPlayer.Core.Exceptions;
using NonsPlayer.Core.Nons;

namespace NonsPlayer.Core.Models;

public class SearchResult : INonsModel
{
public readonly string KeyWords;

private SearchResult(string keyWords)
{
if (keyWords.Equals(string.Empty))
{
throw new SearchExceptions("搜索KeyWords不能为空");
}

KeyWords = keyWords;
}

public Playlist[] Playlists { get; set; }

public Artist[] Artists { get; set; }

public Music[] Musics { get; set; }

public static async Task<SearchResult> CreateSearchAsync(string keyWords)
{
var i = new SearchResult(keyWords);
Debug.WriteLine(i.GetMd5(keyWords));
return i;
}

public async Task<Music[]> SearchMusics(int limit = 20)
{
var result = await Apis.Search.Default(KeyWords, limit, 1, NonsCore.Instance);
var tasks = ((JArray)result["result"]["songs"])
.Select(x => MusicAdapters.CreateById(x["id"].ToObject<long>())).ToList();
Musics = await Task.WhenAll(tasks);
return Musics;
}

public async Task<Artist[]> SearchArtists(int limit = 2)
{
var result = await Apis.Search.Default(KeyWords, limit, 100, NonsCore.Instance);
Artists = ((JArray)result["result"]["artists"])
.Select(x => ArtistAdapters.CreateFromSearch((JObject)x)).ToArray();
return Artists;
}

public async Task<Playlist[]> SearchPlaylists(int limit = 2)
{
var result = await Apis.Search.Default(KeyWords, limit, 1000, NonsCore.Instance);
var tasks = ((JArray)result["result"]["playlists"])
.Select(x => PlaylistAdaptes.CreateById(x["id"].ToObject<long>())).ToList();
Playlists = await Task.WhenAll(tasks);
return Playlists;
}

/// <returns>Md5的b64形式</returns>
private string GetMd5(string keyWords)
{
Md5 = MD5.HashData(Encoding.UTF8.GetBytes(keyWords)).ToBase64String();
return Md5;
}
}
12 changes: 12 additions & 0 deletions NonsPlayer/Helpers/CacheHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,18 @@ public static Music GetMusic(string cacheId, string id)
await MusicAdapters.CreateById(long.Parse(id))).Result.Data;
}

public static async Task<SearchResult> GetSearchResultAsync(string cacheId, string keyWords)
{
return (await GetCacheItemAsync<SearchResult>(cacheId, async () =>
await SearchResult.CreateSearchAsync(keyWords))).Data;
}

public static SearchResult GetSearchResult(string cacheId, string keyWords)
{
return GetCacheItemAsync<SearchResult>(cacheId, async () =>
await SearchResult.CreateSearchAsync(keyWords)).Result.Data;
}

public static async Task<BitmapImage> GetImageStreamFromServer(string imageUrl)
{
using (var httpClient = new HttpClient())
Expand Down
5 changes: 1 addition & 4 deletions NonsPlayer/Helpers/StartUpHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

public class StartUpHelper
{
public static StartUpHelper Instance
{
get;
}=new ();
public static StartUpHelper Instance { get; } = new();

public async Task Main()
{
Expand Down
68 changes: 34 additions & 34 deletions NonsPlayer/NonsPlayer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,35 +32,35 @@
<AppInstallerUri>D:\Program Files\NonsPlayer</AppInstallerUri>
<RepositoryUrl></RepositoryUrl>
<IsPackable>false</IsPackable>
<Authors />
<Authors/>
<Company>Miaoyww</Company>
<PublicSign>false</PublicSign>
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
</PropertyGroup>

<ItemGroup>
<Manifest Include="$(ApplicationManifest)" />
<Manifest Include="$(ApplicationManifest)"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="F23.StringSimilarity" Version="5.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
<PackageReference Include="F23.StringSimilarity" Version="5.1.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1"/>
<PackageReference Include="Microsoft.Windows.CsWin32" PrivateAssets="all" Version="0.3.18-beta">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.3.230602002" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageReference Include="NAudio" Version="2.1.0" />
<PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="RestSharp" Version="106.15.0" />
<PackageReference Include="WinUIEx" Version="2.2.0" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.3.230602002"/>
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9"/>
<PackageReference Include="NAudio" Version="2.1.0"/>
<PackageReference Include="QRCoder" Version="1.4.3"/>
<PackageReference Include="RestSharp" Version="106.15.0"/>
<PackageReference Include="WinUIEx" Version="2.2.0"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NonsPlayer.Core\NonsPlayer.Core.csproj" />
<ProjectReference Include="..\NonsPlayer.Updater\NonsPlayer.Updater.csproj" />
<ProjectReference Include="..\NonsPlayer.Core\NonsPlayer.Core.csproj"/>
<ProjectReference Include="..\NonsPlayer.Updater\NonsPlayer.Updater.csproj"/>
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
Expand All @@ -85,7 +85,7 @@
</ItemGroup>

<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
<ProjectCapability Include="Msix"/>
</ItemGroup>

<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
Expand Down Expand Up @@ -118,38 +118,38 @@

<Target Name="_RemoveFrameworkReferences" BeforeTargets="_ConvertItems;_CalculateInputsForGenerateCurrentProjectAppxManifest">
<ItemGroup>
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="$([System.String]::Copy('%(FrameworkSdkReference.SDKName)').StartsWith('Microsoft.WindowsAppRuntime.'))" />
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="$([System.String]::Copy('%(FrameworkSdkReference.SDKName)').StartsWith('Microsoft.WindowsAppRuntime.'))"/>
</ItemGroup>
</Target>

<ItemGroup>
<PRIResource Remove="Strings\新文件夹\**" />
<PRIResource Remove="Strings\新文件夹\**"/>
</ItemGroup>

<ItemGroup>
<Content Remove="Assets\SplashScreen.scale-100.png" />
<Content Remove="Assets\SplashScreen.scale-400.png" />
<None Remove="Segoe Fluent Icons.ttf" />
<Content Remove="Assets\SplashScreen.scale-100.png"/>
<Content Remove="Assets\SplashScreen.scale-400.png"/>
<None Remove="Segoe Fluent Icons.ttf"/>
<Content Include="Segoe Fluent Icons.ttf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<None Remove="Assets\Icons\arrow-back.svg" />
<None Remove="Assets\Icons\arrow-next.svg" />
<None Remove="Assets\Icons\liked.svg" />
<None Remove="Assets\Icons\search.svg" />
<None Remove="Assets\Icons\unliked.svg" />
<None Remove="Assets\SplashScreen.png" />
<None Remove="Assets\UnknowResource.png" />
<None Remove="Assets\WindowIcon.ico" />
<None Remove="Components\Views\BestArtistCard.xaml" />
<None Remove="Components\Views\BestResultCard.xaml" />
<None Remove="Components\Views\FunctionBar.xaml" />
<None Remove="Components\Views\LyricBox.xaml" />
<None Remove="Components\Views\PlayerBar.xaml" />
<None Remove="Components\Views\UserPlaylistCard.xaml" />
<None Remove="Assets\Icons\arrow-back.svg"/>
<None Remove="Assets\Icons\arrow-next.svg"/>
<None Remove="Assets\Icons\liked.svg"/>
<None Remove="Assets\Icons\search.svg"/>
<None Remove="Assets\Icons\unliked.svg"/>
<None Remove="Assets\SplashScreen.png"/>
<None Remove="Assets\UnknowResource.png"/>
<None Remove="Assets\WindowIcon.ico"/>
<None Remove="Components\Views\BestArtistCard.xaml"/>
<None Remove="Components\Views\BestResultCard.xaml"/>
<None Remove="Components\Views\FunctionBar.xaml"/>
<None Remove="Components\Views\LyricBox.xaml"/>
<None Remove="Components\Views\PlayerBar.xaml"/>
<None Remove="Components\Views\UserPlaylistCard.xaml"/>
</ItemGroup>

<ItemGroup>
Expand Down
103 changes: 60 additions & 43 deletions NonsPlayer/ViewModels/SearchViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,77 +1,94 @@
using CommunityToolkit.Mvvm.ComponentModel;
using F23.StringSimilarity;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Newtonsoft.Json.Linq;
using NonsPlayer.Components.Models;
using NonsPlayer.Contracts.Services;
using NonsPlayer.Contracts.ViewModels;
using NonsPlayer.Core.Adapters;
using NonsPlayer.Core.Api;
using NonsPlayer.Core.Enums;
using NonsPlayer.Core.Contracts.Models;
using NonsPlayer.Core.Models;
using NonsPlayer.Core.Nons;
using NonsPlayer.Core.Nons.Player;
using NonsPlayer.Helpers;


namespace NonsPlayer.ViewModels;

public class SearchViewModel : ObservableRecipient, INavigationAware
public partial class SearchViewModel : ObservableRecipient, INavigationAware, INotifyPropertyChanged
{
private string queryKey;
public ObservableCollection<MusicItem> MusicItems = new();
[ObservableProperty] private Artist[] artists;
[ObservableProperty] private Playlist[] playlists;

public async void OnNavigatedTo(object parameter)
public void OnNavigatedTo(object parameter)
{
queryKey = (parameter as string).ToLower();
await Search(queryKey).ConfigureAwait(false);
Search(queryKey).ConfigureAwait(false);
}

public void OnNavigatedFrom()
{
}


private void QuickSortAlgorithm(List<Tuple<SearchDataType, Tuple<double, JObject>>> data, int low, int high)
public void DoubleClick(object sender, DoubleTappedRoutedEventArgs e)
{
if (low < high)
var listView = sender as ListView;
if (listView.SelectedItem is MusicItem item)
{
var pivotIndex = Partition(data, low, high);
QuickSortAlgorithm(data, low, pivotIndex - 1);
QuickSortAlgorithm(data, pivotIndex + 1, high);
PlayQueue.Instance.Play(item.Music);
}
}

private int Partition(List<Tuple<SearchDataType, Tuple<double, JObject>>> data, int low, int high)
{
var pivotValue = data[high].Item2.Item1;
var i = low - 1;

for (var j = low; j < high; j++)
if (data[j].Item2.Item1 < pivotValue)
{
i++;
Swap(data, i, j);
}

Swap(data, i + 1, high);
return i + 1;
}

private void Swap(List<Tuple<SearchDataType, Tuple<double, JObject>>> data, int i, int j)
public async Task Search(string key)
{
(data[i], data[j]) = (data[j], data[i]);
var searcher = await CacheHelper.GetSearchResultAsync(GetB64(key), key);
await Task.WhenAll(
searcher.SearchMusics(),
searcher.SearchArtists(),
searcher.SearchPlaylists());
SearchHelper.Instance.BestMusicResult = searcher.Musics[0];
Artists = searcher.Artists;
Playlists = searcher.Playlists;
for (var i = 0; i < searcher.Musics.Count(); i++)
{
var index = i;
if (index < App.GetService<ILocalSettingsService>().GetOptions().PlaylistTrackShowCount)
ServiceHelper.DispatcherQueue.TryEnqueue(() =>
{
MusicItems.Add(new MusicItem
{
Music = searcher.Musics[index],
Index = (index + 1).ToString("D2")
});
});
}
}


private Tuple<SearchDataType, Tuple<double, JObject>> ParseResult(SearchDataType type, string name,
JObject originalJObject)
public async void OnScrollViewerViewChanged(object? sender, ScrollViewerViewChangedEventArgs e)
{
var l = new Levenshtein();
return new Tuple<SearchDataType, Tuple<double, JObject>>
(
type,
new Tuple<double, JObject>(l.Distance(name, queryKey.ToLower()), originalJObject)
);
//
// if (sender is ScrollViewer scrollViewer)
// {
// var offset = scrollViewer.VerticalOffset;
//
// var height = scrollViewer.ScrollableHeight;
// if (height - offset <
// App.GetService<ILocalSettingsService>().GetOptions().PlaylistTrackShowCount &&
// currentItemGroupIndex < playListObject.MusicsCount - 1)
// await LoadMusicItemsByGroup();
// }
}

public async Task Search(string key)
private string GetB64(string kyw)
{
var result = await Apis.Search.Default(key, 1, 1, NonsCore.Instance);
SearchHelper.Instance.BestMusicResult =
await MusicAdapters.CreateById(result["result"]["songs"][0]["id"].ToObject<long>());
return Convert.ToBase64String(MD5.HashData(Encoding.UTF8.GetBytes(kyw)));
}
}
Loading

0 comments on commit 5b03a58

Please sign in to comment.