chores: rollback media app icon and name fetch algo; add new lyrics search type: best match
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.130.0" />
|
||||
Version="1.0.132.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AIMP.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AMLLPlayer.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AppleMusic.png
Normal file
|
After Width: | Height: | Size: 836 B |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Chrome.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Edge.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/HyPlayer.png
Normal file
|
After Width: | Height: | Size: 830 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/KugouMusic.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LXMusic.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Listen1.png
Normal file
|
After Width: | Height: | Size: 879 B |
|
After Width: | Height: | Size: 260 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MoeKoeMusic.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MusicBee.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PlanetMusic.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PotPlayer.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/QQMusic.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Question.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Spotify.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/foobar2000.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/iTunes.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -67,6 +67,7 @@
|
||||
<PackageReference Include="DevWinUI.Controls" Version="9.5.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.0" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
|
||||
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
@@ -118,12 +119,27 @@
|
||||
<TrimmerRootAssembly Include="Vanara.Windows.Shell" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\AIMP.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AlbumArtPlaceholder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Alipay.jpg">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AMLLPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AppleMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Chrome.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Edge.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\FluidEffect.bin">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -136,24 +152,72 @@
|
||||
<Content Update="Assets\EmptyState.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\foobar2000.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\HyPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\iTunes.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\KugouMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\LastFM.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Leaf.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Listen1.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\LXMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\MediaPlayerWindows11.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\MoeKoeMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\MusicBee.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\NetEaseCloudMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Page.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\PlanetMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\PotPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\QQMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Question.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\SaltPlayerForWindows.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Segoe Fluent Icons.ttf">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Spotify.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\WeChatReward.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace BetterLyrics.WinUI3.Constants
|
||||
{
|
||||
public static class PlayerID
|
||||
{
|
||||
public const string LXMusic = "cn.toside.music.desktop";
|
||||
public const string LXMusicPortable = "lx-music-desktop.exe";
|
||||
public const string MediaPlayerWindows11 = "Microsoft.ZuneMusic_8wekyb3d8bbwe!Microsoft.ZuneMusic";
|
||||
public const string AIMP = "AIMP.exe";
|
||||
public const string Foobar2000 = "foobar2000.exe";
|
||||
public const string MusicBee = "MusicBee.exe";
|
||||
public const string PotPlayer = "PotPlayerMini64.exe";
|
||||
public const string Spotify = "Spotify.exe";
|
||||
public const string AppleMusic = "AppleInc.AppleMusicWin_nzyj5cx40ttqa!App";
|
||||
public const string AppleMusicAlternative = "AppleMusic.exe";
|
||||
public const string NetEaseCloudMusic = "cloudmusic.exe";
|
||||
public const string KugouMusic = "kugou";
|
||||
public const string QQMusic = "QQMusic.exe";
|
||||
public const string iTunes = "49586DaveAntoine.MediaControllerforiTunes_9bzempp7dntjg!App";
|
||||
public const string Chrome = "Chrome";
|
||||
public const string Edge = "MSEdge";
|
||||
public const string BetterLyrics = "37412.BetterLyrics_rd1g0rsrrtxw8!App";
|
||||
public const string BetterLyricsDebug = "37412.BetterLyrics_c8mj3v9sysxb4!App";
|
||||
public const string SaltPlayerForWindows = "Sakawish.SaltPlayerforWindows_q65q631pyh094!SaltPlayerforWindows";
|
||||
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
||||
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
||||
public const string Listen1 = "com.listen1.listen1";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Constants
|
||||
{
|
||||
public class PlayerName
|
||||
{
|
||||
public const string LXMusic = "LX Music";
|
||||
public const string LXMusicPortable = "LX Music (Portable)";
|
||||
public const string MediaPlayerWindows11 = "Media Player";
|
||||
public const string AIMP = "AIMP";
|
||||
public const string Foobar2000 = "foobar2000";
|
||||
public const string MusicBee = "MusicBee";
|
||||
public const string PotPlayer = "PotPlayer";
|
||||
public const string Spotify = "Spotify";
|
||||
public const string AppleMusic = "Apple Music";
|
||||
public const string AppleMusicAlternative = "Apple Music";
|
||||
public const string NetEaseCloudMusic = "网易云音乐";
|
||||
public const string KugouMusic = "酷狗音乐";
|
||||
public const string QQMusic = "QQ 音乐";
|
||||
public const string iTunes = "iTunes";
|
||||
public const string Chrome = "Google Chrome";
|
||||
public const string Edge = "Microsoft Edge";
|
||||
public const string BetterLyrics = "BetterLyrics";
|
||||
public const string BetterLyricsDebug = "BetterLyrics (Debug)";
|
||||
public const string SaltPlayerForWindows = "Salt Player for Windows";
|
||||
public const string MoeKoeMusic = "MoeKoe Music";
|
||||
public const string Listen1 = "Listen 1";
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace BetterLyrics.WinUI3.Constants
|
||||
{
|
||||
public static class SpecialHandlePlayerID
|
||||
{
|
||||
public const string LXMusic = "cn.toside.music.desktop";
|
||||
public const string LXMusicPortable = "lx-music-desktop.exe";
|
||||
public const string AppleMusic = "AppleInc.AppleMusicWin_nzyj5cx40ttqa!App";
|
||||
public const string AppleMusicAlternative = "AppleMusic.exe";
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,10 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowHideHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
@@ -137,21 +137,67 @@
|
||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||
<StackPanel Padding="3,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
||||
<TextBlock Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Text="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
<TextBlock
|
||||
Text="{x:Bind Title}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind DisplayArtists}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Album}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<HyperlinkButton
|
||||
Padding="0"
|
||||
Content="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
||||
NavigateUri="{x:Bind Reference, FallbackValue=about:blank}" />
|
||||
<!-- Title -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Artist -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Album -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Duration -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock x:Uid="LyricsSearchControlDurauion" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Duration}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="s" />
|
||||
</StackPanel>
|
||||
<!-- Match percentage -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind MatchPercentage}" />
|
||||
</StackPanel>
|
||||
<!-- NOT FOUND -->
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlNotFound"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
Width="16"
|
||||
Height="16"
|
||||
CornerRadius="4">
|
||||
<Image Source="{Binding Logo, Mode=OneWay}" />
|
||||
<ImageIcon Source="{Binding LogoPath}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
MaxWidth="200"
|
||||
@@ -182,6 +182,13 @@
|
||||
|
||||
<!-- 歌词源配置 -->
|
||||
<TextBlock x:Uid="SettingsPageLyricsSearchProvidersConfig" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsSearchType">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.SelectedMediaSourceProvider.LyricsSearchType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsSearchSequential" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsSearchBestMatch" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
AllowDrop="True"
|
||||
@@ -252,48 +259,89 @@
|
||||
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<dev:SettingsCard ContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<!-- Playback source -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPagePlaybackSource"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center" />
|
||||
<RichTextBlock
|
||||
Grid.Column="1"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="Wrap">
|
||||
<TextBlock x:Uid="SettingsPagePlaybackSource" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
||||
<Run Text="(" />
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<Run Text=")" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Playback source ID -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="SettingsPagePlaybackSourceID" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song title -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song artists -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song album -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Lyrics source -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock VerticalAlignment="Center" Text="NCM ID" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.SongId, Mode=OneWay, TargetNullValue=N/A}" />
|
||||
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" />
|
||||
<HyperlinkButton
|
||||
Padding="0"
|
||||
Content="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
||||
IsEnabled="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.IsFound, Mode=OneWay}"
|
||||
NavigateUri="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<!-- Translation source -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
<!-- Match percentage -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
@@ -11,72 +11,4 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
Krc,
|
||||
NotSpecified,
|
||||
}
|
||||
|
||||
public static class LyricsFormatExtensions
|
||||
{
|
||||
public static LyricsFormat? DetectFormat(this string content)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
return null;
|
||||
|
||||
// TTML: 检查 <tt ... xmlns="http://www.w3.org/ns/ttml"
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
content,
|
||||
@"<tt\b[^>]*\bxmlns\s*=\s*[""']http://www\.w3\.org/ns/ttml[""']",
|
||||
System.Text.RegularExpressions.RegexOptions.IgnoreCase))
|
||||
{
|
||||
return LyricsFormat.Ttml;
|
||||
}
|
||||
// KRC: 检测主内容格式 [start,duration]<offset,duration,0>字...
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
content,
|
||||
@"^\[\d+,\d+\](<\d+,\d+,0>.+)+",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Krc;
|
||||
}
|
||||
// QRC: 检测主内容格式 [start,duration]字(offset,duration)
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
content,
|
||||
@"^\[\d+,\d+\].*?\(\d+,\d+\)",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Qrc;
|
||||
}
|
||||
// 标准LRC和增强型LRC
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(content, @"\[\d{1,2}:\d{2}") ||
|
||||
System.Text.RegularExpressions.Regex.IsMatch(content, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
|
||||
{
|
||||
return LyricsFormat.Lrc;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToFileExtension(this LyricsFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
LyricsFormat.Lrc => ".lrc",
|
||||
LyricsFormat.Qrc => ".qrc",
|
||||
LyricsFormat.Krc => ".krc",
|
||||
LyricsFormat.Eslrc => ".eslrc",
|
||||
LyricsFormat.Ttml => ".ttml",
|
||||
_ => ".*",
|
||||
};
|
||||
}
|
||||
|
||||
public static LyricsSearchProvider? ToLyricsSearchProvider(this LyricsFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
LyricsFormat.Lrc => LyricsSearchProvider.LocalLrcFile,
|
||||
LyricsFormat.Eslrc => LyricsSearchProvider.LocalEslrcFile,
|
||||
LyricsFormat.Ttml => LyricsSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,70 +17,4 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
LocalTtmlFile,
|
||||
AppleMusic,
|
||||
}
|
||||
|
||||
public static class LyricsSearchProviderExtensions
|
||||
{
|
||||
public static string GetCacheDirectory(this LyricsSearchProvider provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => PathHelper.LrcLibLyricsCacheDirectory,
|
||||
LyricsSearchProvider.QQ => PathHelper.QQLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
||||
_ => throw new System.ArgumentOutOfRangeException(nameof(provider)),
|
||||
};
|
||||
}
|
||||
|
||||
public static LyricsFormat GetLyricsFormat(this LyricsSearchProvider provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.QQ => LyricsFormat.Qrc,
|
||||
LyricsSearchProvider.Kugou => LyricsFormat.Krc,
|
||||
LyricsSearchProvider.Netease => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.AmllTtmlDb => LyricsFormat.Ttml,
|
||||
LyricsSearchProvider.AppleMusic => LyricsFormat.Ttml,
|
||||
LyricsSearchProvider.LocalLrcFile => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.LocalEslrcFile => LyricsFormat.Eslrc,
|
||||
LyricsSearchProvider.LocalTtmlFile => LyricsFormat.Ttml,
|
||||
_ => LyricsFormat.NotSpecified,
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsLocal(this LyricsSearchProvider provider)
|
||||
{
|
||||
return provider
|
||||
is LyricsSearchProvider.LocalMusicFile
|
||||
or LyricsSearchProvider.LocalLrcFile
|
||||
or LyricsSearchProvider.LocalEslrcFile
|
||||
or LyricsSearchProvider.LocalTtmlFile;
|
||||
}
|
||||
|
||||
public static bool IsRemote(this LyricsSearchProvider provider)
|
||||
{
|
||||
return !provider.IsLocal();
|
||||
}
|
||||
|
||||
public static TranslationSearchProvider? ToTranslationSearchProvider(this LyricsSearchProvider? provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => TranslationSearchProvider.LrcLib,
|
||||
LyricsSearchProvider.QQ => TranslationSearchProvider.QQ,
|
||||
LyricsSearchProvider.Kugou => TranslationSearchProvider.Kugou,
|
||||
LyricsSearchProvider.Netease => TranslationSearchProvider.Netease,
|
||||
LyricsSearchProvider.AmllTtmlDb => TranslationSearchProvider.AmllTtmlDb,
|
||||
LyricsSearchProvider.AppleMusic => TranslationSearchProvider.AppleMusic,
|
||||
LyricsSearchProvider.LocalMusicFile => TranslationSearchProvider.LocalMusicFile,
|
||||
LyricsSearchProvider.LocalLrcFile => TranslationSearchProvider.LocalLrcFile,
|
||||
LyricsSearchProvider.LocalEslrcFile => TranslationSearchProvider.LocalEslrcFile,
|
||||
LyricsSearchProvider.LocalTtmlFile => TranslationSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsSearchType
|
||||
{
|
||||
Sequential,
|
||||
BestMatch
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class LyricsFormatExtensions
|
||||
{
|
||||
extension(LyricsFormat format)
|
||||
{
|
||||
public string ToFileExtension()
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
LyricsFormat.Lrc => ".lrc",
|
||||
LyricsFormat.Qrc => ".qrc",
|
||||
LyricsFormat.Krc => ".krc",
|
||||
LyricsFormat.Eslrc => ".eslrc",
|
||||
LyricsFormat.Ttml => ".ttml",
|
||||
_ => ".*",
|
||||
};
|
||||
}
|
||||
|
||||
public LyricsSearchProvider? ToLyricsSearchProvider()
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
LyricsFormat.Lrc => LyricsSearchProvider.LocalLrcFile,
|
||||
LyricsFormat.Eslrc => LyricsSearchProvider.LocalEslrcFile,
|
||||
LyricsFormat.Ttml => LyricsSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class LyricsSearchProviderExtensions
|
||||
{
|
||||
extension(LyricsSearchProvider provider)
|
||||
{
|
||||
public string GetCacheDirectory() => provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => PathHelper.LrcLibLyricsCacheDirectory,
|
||||
LyricsSearchProvider.QQ => PathHelper.QQLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(provider)),
|
||||
};
|
||||
|
||||
public LyricsFormat GetLyricsFormat() => provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.QQ => LyricsFormat.Qrc,
|
||||
LyricsSearchProvider.Kugou => LyricsFormat.Krc,
|
||||
LyricsSearchProvider.Netease => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.AmllTtmlDb => LyricsFormat.Ttml,
|
||||
LyricsSearchProvider.AppleMusic => LyricsFormat.Ttml,
|
||||
LyricsSearchProvider.LocalLrcFile => LyricsFormat.Lrc,
|
||||
LyricsSearchProvider.LocalEslrcFile => LyricsFormat.Eslrc,
|
||||
LyricsSearchProvider.LocalTtmlFile => LyricsFormat.Ttml,
|
||||
_ => LyricsFormat.NotSpecified,
|
||||
};
|
||||
|
||||
public bool IsLocal() => provider
|
||||
is LyricsSearchProvider.LocalMusicFile
|
||||
or LyricsSearchProvider.LocalLrcFile
|
||||
or LyricsSearchProvider.LocalEslrcFile
|
||||
or LyricsSearchProvider.LocalTtmlFile;
|
||||
|
||||
public bool IsRemote() => !provider.IsLocal();
|
||||
|
||||
public TranslationSearchProvider? ToTranslationSearchProvider() => provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => TranslationSearchProvider.LrcLib,
|
||||
LyricsSearchProvider.QQ => TranslationSearchProvider.QQ,
|
||||
LyricsSearchProvider.Kugou => TranslationSearchProvider.Kugou,
|
||||
LyricsSearchProvider.Netease => TranslationSearchProvider.Netease,
|
||||
LyricsSearchProvider.AmllTtmlDb => TranslationSearchProvider.AmllTtmlDb,
|
||||
LyricsSearchProvider.AppleMusic => TranslationSearchProvider.AppleMusic,
|
||||
LyricsSearchProvider.LocalMusicFile => TranslationSearchProvider.LocalMusicFile,
|
||||
LyricsSearchProvider.LocalLrcFile => TranslationSearchProvider.LocalLrcFile,
|
||||
LyricsSearchProvider.LocalEslrcFile => TranslationSearchProvider.LocalEslrcFile,
|
||||
LyricsSearchProvider.LocalTtmlFile => TranslationSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using NTextCat.Commons;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -37,6 +38,47 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
return [str];
|
||||
}
|
||||
}
|
||||
|
||||
public LyricsFormat? DetectFormat()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
return null;
|
||||
|
||||
// TTML: 检查 <tt ... xmlns="http://www.w3.org/ns/ttml"
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
str,
|
||||
@"<tt\b[^>]*\bxmlns\s*=\s*[""']http://www\.w3\.org/ns/ttml[""']",
|
||||
System.Text.RegularExpressions.RegexOptions.IgnoreCase))
|
||||
{
|
||||
return LyricsFormat.Ttml;
|
||||
}
|
||||
// KRC: 检测主内容格式 [start,duration]<offset,duration,0>字...
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
str,
|
||||
@"^\[\d+,\d+\](<\d+,\d+,0>.+)+",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Krc;
|
||||
}
|
||||
// QRC: 检测主内容格式 [start,duration]字(offset,duration)
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
str,
|
||||
@"^\[\d+,\d+\].*?\(\d+,\d+\)",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Qrc;
|
||||
}
|
||||
// 标准LRC和增强型LRC
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(str, @"\[\d{1,2}:\d{2}") ||
|
||||
System.Text.RegularExpressions.Regex.IsMatch(str, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
|
||||
{
|
||||
return LyricsFormat.Lrc;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@@ -36,12 +39,17 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string? ReadLyricsCache(SongInfo songInfo, LyricsFormat format, string cacheFolderPath)
|
||||
public static LyricsSearchResult? ReadLyricsCache(SongInfo songInfo, LyricsSearchProvider lyricsSearchProvider)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{songInfo.DisplayArtists} - {songInfo.Title} - {songInfo.Album}{format.ToFileExtension()}"));
|
||||
var cacheFilePath = Path.Combine(
|
||||
lyricsSearchProvider.GetCacheDirectory(),
|
||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
||||
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllText(cacheFilePath);
|
||||
var json = File.ReadAllText(cacheFilePath);
|
||||
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LyricsSearchResult);
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -56,10 +64,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void WriteLyricsCache(SongInfo songInfo, string lyrics, LyricsFormat format, string cacheFolderPath)
|
||||
public static void WriteLyricsCache(SongInfo songInfo, LyricsSearchResult lyricsSearchResult)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{songInfo.DisplayArtists} - {songInfo.Title} - {songInfo.Album}{format.ToFileExtension()}"));
|
||||
File.WriteAllText(cacheFilePath, lyrics);
|
||||
var cacheFilePath = Path.Combine(
|
||||
lyricsSearchResult.Provider.GetCacheDirectory(),
|
||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(lyricsSearchResult, SourceGenerationContext.Default.LyricsSearchResult);
|
||||
File.WriteAllText(cacheFilePath, json);
|
||||
}
|
||||
|
||||
public static void WriteAlbumArtCache(SongInfo songInfo, byte[] img, string format, string cacheFolderPath)
|
||||
@@ -68,17 +79,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
File.WriteAllBytes(cacheFilePath, img);
|
||||
}
|
||||
|
||||
public static bool IsSwitchableNormalizedMatch(string fileName, string q1, string q2)
|
||||
{
|
||||
var normFileName = StringHelper.Normalize(fileName);
|
||||
var normQ1 = StringHelper.Normalize(q1);
|
||||
var normQ2 = StringHelper.Normalize(q2);
|
||||
|
||||
// 常见两种顺序
|
||||
return normFileName == normQ1 + normQ2
|
||||
|| normFileName == normQ2 + normQ1;
|
||||
}
|
||||
|
||||
public static readonly string[] MusicExtensions = {
|
||||
".mp3", ".aac", ".m4a", ".ogg", ".opus", ".wma", ".amr",
|
||||
".flac", ".alac", ".ape", ".wv", ".tak",
|
||||
|
||||
@@ -17,98 +17,48 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public List<LyricsData> LyricsDataArr { get; private set; } = [];
|
||||
|
||||
public void Parse(List<MappedSongSearchQuery> mappedSongSearchQueries, Models.SongInfo songInfo, string? raw, LyricsSearchProvider? lyricsSearchProvider)
|
||||
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
var overridenTitle = songInfo.Title;
|
||||
var overridenArtist = songInfo.Artists;
|
||||
var overridenAlbum = songInfo.Album;
|
||||
|
||||
var found = mappedSongSearchQueries
|
||||
.FirstOrDefault(x =>
|
||||
x.OriginalTitle == overridenTitle &&
|
||||
x.OriginalArtist == overridenArtist.Join(ATL.Settings.DisplayValueSeparator.ToString()) &&
|
||||
x.OriginalAlbum == overridenAlbum);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
overridenTitle = found.MappedTitle;
|
||||
overridenArtist = found.MappedArtist.Split(ATL.Settings.DisplayValueSeparator);
|
||||
overridenAlbum = found.MappedAlbum;
|
||||
}
|
||||
|
||||
LyricsDataArr = [];
|
||||
if (raw == null)
|
||||
if (lyricsSearchResult?.Raw == null)
|
||||
{
|
||||
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder((int)songInfo.DurationMs));
|
||||
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder((int)(songInfo?.DurationMs ?? 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (raw.DetectFormat())
|
||||
switch (lyricsSearchResult.Raw.DetectFormat())
|
||||
{
|
||||
case LyricsFormat.Lrc:
|
||||
case LyricsFormat.Eslrc:
|
||||
ParseLrc(raw);
|
||||
ParseLrc(lyricsSearchResult.Raw);
|
||||
break;
|
||||
case LyricsFormat.Qrc:
|
||||
ParseQrcKrc(QrcParser.Parse(raw).Lines);
|
||||
ParseQrcKrc(QrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Krc:
|
||||
ParseQrcKrc(KrcParser.Parse(raw).Lines);
|
||||
ParseQrcKrc(KrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Ttml:
|
||||
ParseTtml(raw);
|
||||
ParseTtml(lyricsSearchResult.Raw);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
FillRomanizationLyricsData();
|
||||
FillTranslationFromCache(
|
||||
((SongInfo)songInfo.Clone())
|
||||
.WithTitle(overridenTitle)
|
||||
.WithArtist(overridenArtist)
|
||||
.WithAlbum(overridenAlbum),
|
||||
lyricsSearchProvider);
|
||||
FillTranslationFromCache(lyricsSearchResult);
|
||||
}
|
||||
|
||||
private void FillTranslationFromCache(SongInfo songInfo, LyricsSearchProvider? provider)
|
||||
private void FillTranslationFromCache(LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
string? translationRaw = null;
|
||||
switch (provider)
|
||||
if (lyricsSearchResult?.Translation != null)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
translationRaw = FileHelper.ReadLyricsCache(songInfo, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
translationRaw = FileHelper.ReadLyricsCache(songInfo, LyricsFormat.Lrc, PathHelper.KugouTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
translationRaw = FileHelper.ReadLyricsCache(songInfo, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalMusicFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalLrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalEslrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (translationRaw != null)
|
||||
{
|
||||
switch (provider)
|
||||
switch (lyricsSearchResult.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
case LyricsSearchProvider.Kugou:
|
||||
case LyricsSearchProvider.Netease:
|
||||
ParseLrc(translationRaw);
|
||||
ParseLrc(lyricsSearchResult.Translation);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using F23.StringSimilarity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class MetadataComparer
|
||||
{
|
||||
// 权重配置 (总和 1.0)
|
||||
private const double WeightTitle = 0.40;
|
||||
private const double WeightArtist = 0.40;
|
||||
private const double WeightAlbum = 0.10;
|
||||
private const double WeightDuration = 0.10;
|
||||
|
||||
// 实例化算法 (JaroWinkler 适合短字符串匹配)
|
||||
private static readonly JaroWinkler _algo = new JaroWinkler();
|
||||
|
||||
/// <summary>
|
||||
/// 计算 SongInfo 和 LyricsSearchResult 的相似度 (0-100)
|
||||
/// </summary>
|
||||
public static int CalculateScore(SongInfo local, LyricsSearchResult remote)
|
||||
{
|
||||
if (local == null || remote == null) return 0;
|
||||
|
||||
// 1. 标题相似度
|
||||
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
||||
|
||||
// 2. 艺术家相似度 (需要处理数组顺序)
|
||||
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
||||
|
||||
// 3. 专辑相似度
|
||||
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
||||
|
||||
// 4. 时长相似度 (基于毫秒 vs 秒的转换和容差)
|
||||
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
||||
|
||||
// 5. 加权汇总
|
||||
double totalScore = (titleScore * WeightTitle) +
|
||||
(artistScore * WeightArtist) +
|
||||
(albumScore * WeightAlbum) +
|
||||
(durationScore * WeightDuration);
|
||||
|
||||
return (int)Math.Round(totalScore * 100);
|
||||
}
|
||||
|
||||
private static double GetStringSimilarity(string? s1, string? s2)
|
||||
{
|
||||
// 归一化:转小写,去空白
|
||||
s1 = s1?.Trim().ToLowerInvariant() ?? "";
|
||||
s2 = s2?.Trim().ToLowerInvariant() ?? "";
|
||||
|
||||
if (string.IsNullOrEmpty(s1) && string.IsNullOrEmpty(s2)) return 1.0; // 都是空,视为匹配
|
||||
if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2)) return 0.0; // 其中一个为空
|
||||
|
||||
return _algo.Similarity(s1, s2);
|
||||
}
|
||||
|
||||
private static double GetArtistSimilarity(string[]? localArtists, string[]? remoteArtists)
|
||||
{
|
||||
if (localArtists == null || localArtists.Length == 0) return 0.0;
|
||||
if (remoteArtists == null || remoteArtists.Length == 0) return 0.0;
|
||||
|
||||
// 技巧:将艺术家数组排序并连接,避免顺序不同导致的不匹配
|
||||
// 例如: ["Jay-Z", "Linkin Park"] 和 ["Linkin Park", "Jay-Z"] 应该是一样的
|
||||
var s1 = string.Join(" ", localArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||
var s2 = string.Join(" ", remoteArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||
|
||||
return _algo.Similarity(s1, s2);
|
||||
}
|
||||
|
||||
private static double GetDurationSimilarity(double localMs, double? remoteSeconds)
|
||||
{
|
||||
if (remoteSeconds == null || remoteSeconds == 0) return 0.0; // 远程没有时长数据,不匹配
|
||||
|
||||
double localSeconds = localMs / 1000.0;
|
||||
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
||||
|
||||
// 容差逻辑:
|
||||
// 差距 <= 3秒:100% 相似
|
||||
// 差距 >= 20秒:0% 相似
|
||||
// 中间线性插值
|
||||
|
||||
const double PerfectTolerance = 3.0;
|
||||
const double MaxTolerance = 20.0;
|
||||
|
||||
if (diff <= PerfectTolerance) return 1.0;
|
||||
if (diff >= MaxTolerance) return 0.0;
|
||||
|
||||
// 线性递减公式
|
||||
return 1.0 - ((diff - PerfectTolerance) / (MaxTolerance - PerfectTolerance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,26 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string SettingsFilePath => Path.Combine(SettingsDirectory, "settings.json");
|
||||
|
||||
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml");
|
||||
|
||||
public static string AlbumArtPlaceholderPath => Path.Combine(AssetsFolder, "AlbumArtPlaceholder.png");
|
||||
public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico");
|
||||
public static string AlbumArtPlaceholderPath => Path.Combine(AssetsFolder, "AlbumArtPlaceholder.png");
|
||||
public static string AIMPLogoPath => Path.Combine(AssetsFolder, "AIMP.png");
|
||||
public static string Foobar2000LogoPath => Path.Combine(AssetsFolder, "foobar2000.png");
|
||||
public static string MusicBeeLogoPath => Path.Combine(AssetsFolder, "MusicBee.png");
|
||||
public static string SpotifyLogoPath => Path.Combine(AssetsFolder, "Spotify.png");
|
||||
public static string AppleMusicLogoPath => Path.Combine(AssetsFolder, "AppleMusic.png");
|
||||
public static string iTunesLogoPath => Path.Combine(AssetsFolder, "iTunes.png");
|
||||
public static string KugouMusicLogoPath => Path.Combine(AssetsFolder, "KugouMusic.png");
|
||||
public static string NetEaseCloudMusicLogoPath => Path.Combine(AssetsFolder, "NetEaseCloudMusic.png");
|
||||
public static string QQMusicLogoPath => Path.Combine(AssetsFolder, "QQMusic.png");
|
||||
public static string LXMusicLogoPath => Path.Combine(AssetsFolder, "LXMusic.png");
|
||||
public static string MediaPlayerWindows11LogoPath => Path.Combine(AssetsFolder, "MediaPlayerWindows11.png");
|
||||
public static string PotPlayerLogoPath => Path.Combine(AssetsFolder, "PotPlayer.png");
|
||||
public static string ChromeLogoPath => Path.Combine(AssetsFolder, "Chrome.png");
|
||||
public static string EdgeLogoPath => Path.Combine(AssetsFolder, "Edge.png");
|
||||
public static string SaltPlayerForWindowsLogoPath => Path.Combine(AssetsFolder, "SaltPlayerForWindows.png");
|
||||
public static string MoeKoeMusicLogoPath => Path.Combine(AssetsFolder, "MoeKoeMusic.png");
|
||||
public static string Listen1LogoPath => Path.Combine(AssetsFolder, "Listen1.png");
|
||||
public static string UnknownPlayerLogoPath => Path.Combine(AssetsFolder, "Question.png");
|
||||
|
||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||
@@ -28,14 +45,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
|
||||
public static string AppleMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "apple-music");
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.json");
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
||||
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
||||
|
||||
public static string TranslationCacheDirectory => Path.Combine(CacheFolder, "translations");
|
||||
public static string QQTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "qq");
|
||||
public static string NeteaseTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "netease");
|
||||
public static string KugouTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "kugou");
|
||||
|
||||
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
|
||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
|
||||
|
||||
@@ -54,10 +66,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AppleMusicCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(QQTranslationCacheDirectory);
|
||||
Directory.CreateDirectory(NeteaseTranslationCacheDirectory);
|
||||
Directory.CreateDirectory(KugouTranslationCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using BetterLyrics.WinUI3.Constants;
|
||||
using Lyricify.Lyrics.Providers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class PlayerIDHelper
|
||||
{
|
||||
private static readonly List<string> neteaseFamilyRegex =
|
||||
[
|
||||
"cloudmusic.exe", //NetEaseCloudMusic
|
||||
"^17588BrandonWong\\.LyricEase_", //LyricEase
|
||||
"^48848aaaaaaccd\\.HyPlayer_" //HyPlayer
|
||||
];
|
||||
|
||||
public static bool IsNeteaseFamily(string? id)
|
||||
{
|
||||
if (id is null) return false;
|
||||
|
||||
foreach (var regex in neteaseFamilyRegex)
|
||||
{
|
||||
var isMatch = Regex.IsMatch(id, regex);
|
||||
if (isMatch) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsLXMusic(string? id) => id is PlayerID.LXMusic or PlayerID.LXMusicPortable;
|
||||
|
||||
public static bool IsAppleMusic(string? id) => id is PlayerID.AppleMusic or PlayerID.AppleMusicAlternative;
|
||||
|
||||
public static string? GetDisplayName(string? id) => id switch
|
||||
{
|
||||
PlayerID.Spotify => PlayerName.Spotify,
|
||||
PlayerID.AppleMusic => PlayerName.AppleMusic,
|
||||
PlayerID.iTunes => PlayerName.iTunes,
|
||||
PlayerID.KugouMusic => PlayerName.KugouMusic,
|
||||
PlayerID.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic,
|
||||
PlayerID.QQMusic => PlayerName.QQMusic,
|
||||
PlayerID.LXMusic => PlayerName.LXMusic,
|
||||
PlayerID.LXMusicPortable => PlayerName.LXMusicPortable,
|
||||
PlayerID.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11,
|
||||
PlayerID.AIMP => PlayerName.AIMP,
|
||||
PlayerID.Foobar2000 => PlayerName.Foobar2000,
|
||||
PlayerID.MusicBee => PlayerName.MusicBee,
|
||||
PlayerID.PotPlayer => PlayerName.PotPlayer,
|
||||
PlayerID.Chrome => PlayerName.Chrome,
|
||||
PlayerID.Edge => PlayerName.Edge,
|
||||
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
|
||||
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
||||
PlayerID.SaltPlayerForWindows => PlayerName.SaltPlayerForWindows,
|
||||
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
||||
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
||||
PlayerID.Listen1 => PlayerName.Listen1,
|
||||
_ => id,
|
||||
};
|
||||
|
||||
public static string GetLogoPath(string? id) => id switch
|
||||
{
|
||||
PlayerID.Spotify => PathHelper.SpotifyLogoPath,
|
||||
PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath,
|
||||
PlayerID.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
|
||||
PlayerID.iTunes => PathHelper.iTunesLogoPath,
|
||||
PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath,
|
||||
PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,
|
||||
PlayerID.QQMusic => PathHelper.QQMusicLogoPath,
|
||||
PlayerID.LXMusic => PathHelper.LXMusicLogoPath,
|
||||
PlayerID.LXMusicPortable => PathHelper.LXMusicLogoPath,
|
||||
PlayerID.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath,
|
||||
PlayerID.AIMP => PathHelper.AIMPLogoPath,
|
||||
PlayerID.Foobar2000 => PathHelper.Foobar2000LogoPath,
|
||||
PlayerID.MusicBee => PathHelper.MusicBeeLogoPath,
|
||||
PlayerID.PotPlayer => PathHelper.PotPlayerLogoPath,
|
||||
PlayerID.Chrome => PathHelper.ChromeLogoPath,
|
||||
PlayerID.Edge => PathHelper.EdgeLogoPath,
|
||||
PlayerID.BetterLyrics => PathHelper.LogoPath,
|
||||
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
|
||||
PlayerID.SaltPlayerForWindows => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
||||
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
||||
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
|
||||
_ => PathHelper.UnknownPlayerLogoPath,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using BetterLyrics.WinUI3.Constants;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class PlayerIDMatcher
|
||||
{
|
||||
private static readonly List<string> neteaseFamilyRegex =
|
||||
[
|
||||
"cloudmusic.exe", //NetEaseCloudMusic
|
||||
"^17588BrandonWong\\.LyricEase_", //LyricEase
|
||||
"^48848aaaaaaccd\\.HyPlayer_" //HyPlayer
|
||||
];
|
||||
|
||||
public static bool IsNeteaseFamily(string? id)
|
||||
{
|
||||
if (id is null) return false;
|
||||
|
||||
foreach (var regex in neteaseFamilyRegex)
|
||||
{
|
||||
var isMatch = Regex.IsMatch(id, regex);
|
||||
if (isMatch) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsLXMusic(string? id) => id is SpecialHandlePlayerID.LXMusic or SpecialHandlePlayerID.LXMusicPortable;
|
||||
|
||||
public static bool IsAppleMusic(string? id) => id is SpecialHandlePlayerID.AppleMusic or SpecialHandlePlayerID.AppleMusicAlternative;
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
.ToArray())
|
||||
.ToLowerInvariant();
|
||||
public static string NewLine = "\n";
|
||||
|
||||
public static bool IsSwitchableNormalizedMatch(string source, string q1, string q2)
|
||||
{
|
||||
var normFileName = Normalize(source);
|
||||
var normQ1 = Normalize(q1);
|
||||
var normQ2 = Normalize(q2);
|
||||
|
||||
// 常见两种顺序
|
||||
return normFileName == normQ1 + normQ2
|
||||
|| normFileName == normQ2 + normQ1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
if (hIconCopy.IsNull)
|
||||
{
|
||||
User32.DestroyIcon(hIconCopy);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsSearchResult
|
||||
public partial class LyricsSearchResult : ObservableObject, ICloneable
|
||||
{
|
||||
public LyricsSearchProvider? Provider { get; set; }
|
||||
public LyricsSearchProvider Provider { get; set; }
|
||||
|
||||
public string? Raw { get; set; }
|
||||
public string? Translation { get; set; }
|
||||
|
||||
public string? Title { get; set; }
|
||||
public string[]? Artists { get; set; }
|
||||
public string? Album { get; set; }
|
||||
public double? Duration { get; set; }
|
||||
[ObservableProperty] public partial int MatchPercentage { get; set; } = -1;
|
||||
[ObservableProperty] public partial string Reference { get; set; } = "about:blank";
|
||||
|
||||
public bool IsFound => !string.IsNullOrEmpty(Raw);
|
||||
|
||||
@@ -19,6 +25,22 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
public string? DisplayArtists => Artists?.Join("; ");
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new LyricsSearchResult()
|
||||
{
|
||||
Album = this.Album,
|
||||
Duration = this.Duration,
|
||||
Raw = this.Raw,
|
||||
Translation = this.Translation,
|
||||
Title = this.Title,
|
||||
Artists = this.Artists,
|
||||
MatchPercentage = this.MatchPercentage,
|
||||
Provider = this.Provider,
|
||||
Reference = this.Reference
|
||||
};
|
||||
}
|
||||
|
||||
public void CopyFromSongInfo(SongInfo songInfo)
|
||||
{
|
||||
Title = songInfo.Title;
|
||||
|
||||
@@ -37,12 +37,13 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; } = [.. Enum.GetValues<LyricsSearchProvider>().Select(p => new LyricsSearchProviderInfo(p, true))];
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchType LyricsSearchType { get; set; } = LyricsSearchType.Sequential;
|
||||
|
||||
[ObservableProperty] public partial BitmapImage? Logo { get; private set; }
|
||||
public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
|
||||
|
||||
public bool IsLXMusic => PlayerIDMatcher.IsLXMusic(Provider);
|
||||
public string? DisplayName => PlayerIDHelper.GetDisplayName(Provider);
|
||||
|
||||
[ObservableProperty] public partial string? DisplayName { get; private set; }
|
||||
public bool IsLXMusic => PlayerIDHelper.IsLXMusic(Provider);
|
||||
|
||||
public MediaSourceProviderInfo()
|
||||
{
|
||||
@@ -56,7 +57,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
IsEnabled = isEnable;
|
||||
switch (provider)
|
||||
{
|
||||
case Constants.SpecialHandlePlayerID.AppleMusic:
|
||||
case Constants.PlayerID.AppleMusic:
|
||||
// Apple Music 的特性
|
||||
TimelineSyncThreshold = 1000;
|
||||
PositionOffset = 1000;
|
||||
@@ -112,43 +113,5 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
OnPropertyChanged(nameof(LyricsSearchProvidersInfo));
|
||||
}
|
||||
|
||||
partial void OnProviderChanged(string value)
|
||||
{
|
||||
var dispatcherQueue = App.Current.Resources.DispatcherQueue;
|
||||
|
||||
STATaskHelper.RunAsSTATask(() =>
|
||||
{
|
||||
var shellItem = AppHook.GetShellItem(Provider);
|
||||
if (shellItem != null)
|
||||
{
|
||||
var displayName = AppHook.GetDisplayName(shellItem);
|
||||
|
||||
dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
DisplayName = displayName;
|
||||
});
|
||||
|
||||
var icon = AppHook.GetIcon(shellItem);
|
||||
|
||||
shellItem.Dispose();
|
||||
|
||||
if (icon != null)
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
Logo = await AppHook.ToBitmapImageAsync(icon.Value);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
DisplayName = Provider;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ShowOrHideLyricsWindowShortcut { get; set; } = new List<string> { "Ctrl", "Alt", "H" };
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ExitOnLyricsWindowClosed { get; set; } = false;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ListenOnNewPlaybackSource { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IgnoreCacheWhenSearching { get; set; } = false;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> BorderlessShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "B" };
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ClickThroughShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "C" };
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> LyricsWindowSwitchShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "S" };
|
||||
|
||||
@@ -51,13 +51,18 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"Title: {Title}\n" +
|
||||
$"Artist: {Artists}\n" +
|
||||
$"Album: {Album}\n" +
|
||||
$"Duration: {Duration} sec\n" +
|
||||
$"Plauer ID: {PlayerId}\n" +
|
||||
$"Song ID: {SongId}\n" +
|
||||
$"Linked file name: {LinkedFileName}";
|
||||
$"Title: {Title}, " +
|
||||
$"Artist: {DisplayArtists}, " +
|
||||
$"Album: {Album}, " +
|
||||
$"Duration: {Duration} sec, " +
|
||||
$"Plauer ID: {PlayerId}, " +
|
||||
$"Song ID: {SongId}, " +
|
||||
$"Linked file name: {LinkedFileName}.";
|
||||
}
|
||||
|
||||
public string ToFileName()
|
||||
{
|
||||
return $"{DisplayArtists} - {Title} - {Album} - {Duration}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
private string _accessToken = "";
|
||||
private string _storefront = "";
|
||||
private string _language = "";
|
||||
private bool _isInited = false;
|
||||
|
||||
public AppleMusic()
|
||||
{
|
||||
@@ -26,11 +27,18 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
|
||||
public async Task<bool> InitAsync()
|
||||
{
|
||||
await GetAccessTokenAsync();
|
||||
await SetMediaUserTokenAsync();
|
||||
return
|
||||
!string.IsNullOrEmpty(_accessToken) &&
|
||||
!string.IsNullOrEmpty(PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
|
||||
if (!_isInited)
|
||||
{
|
||||
var mediaUserToken = PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey);
|
||||
if (!string.IsNullOrEmpty(mediaUserToken))
|
||||
{
|
||||
await GetAccessTokenAsync();
|
||||
await SetMediaUserTokenAsync(mediaUserToken);
|
||||
_isInited = !string.IsNullOrEmpty(_accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
return _isInited;
|
||||
}
|
||||
|
||||
private async Task GetAccessTokenAsync()
|
||||
@@ -47,11 +55,10 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_accessToken}");
|
||||
}
|
||||
|
||||
private async Task SetMediaUserTokenAsync()
|
||||
private async Task SetMediaUserTokenAsync(string token)
|
||||
{
|
||||
_client.DefaultRequestHeaders.Remove("media-user-token");
|
||||
_client.DefaultRequestHeaders.Add("media-user-token",
|
||||
PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
|
||||
_client.DefaultRequestHeaders.Add("media-user-token", token);
|
||||
var resp = await _client.GetStringAsync("https://amp-api.music.apple.com/v1/me/storefront");
|
||||
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
_storefront = json.GetProperty("data")[0].GetProperty("id").ToString();
|
||||
@@ -60,10 +67,8 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
|
||||
}
|
||||
|
||||
public async Task<string?> GetLyricsAsync(string title, string artist)
|
||||
public async Task<string?> GetLyricsAsync(string id)
|
||||
{
|
||||
string id = await SearchSongInfoAsync(artist, title);
|
||||
|
||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/songs/{id}";
|
||||
var url = apiUrl + $"?include[songs]=lyrics,syllable-lyrics&l={_language}";
|
||||
var resp = await _client.GetStringAsync(url);
|
||||
@@ -104,7 +109,7 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string> SearchSongInfoAsync(string artist, string title)
|
||||
public async Task<string> SearchSongInfoAsync(string artist, string title)
|
||||
{
|
||||
var query = $"{artist} {title}";
|
||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/search";
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace BetterLyrics.WinUI3.Serialization
|
||||
[JsonSerializable(typeof(TranslateResponse))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
[JsonSerializable(typeof(LyricsSearchResult))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
internal partial class SourceGenerationContext : JsonSerializerContext { }
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
|
||||
if (FileHelper.MusicExtensions.Contains(Path.GetExtension(file)))
|
||||
{
|
||||
Track track = new(file);
|
||||
if ((track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists) || FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.DisplayArtists, songInfo.Title))
|
||||
if ((track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists) || StringHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.DisplayArtists, songInfo.Title))
|
||||
{
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
if (bytes != null)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@@ -9,8 +10,8 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
public interface ILyricsSearchService
|
||||
{
|
||||
Task<LyricsSearchResult> SearchSmartlyAsync(SongInfo songInfo, CancellationToken token);
|
||||
Task<LyricsSearchResult?> SearchSmartlyAsync(SongInfo songInfo, bool checkCache, LyricsSearchType? lyricsSearchType, CancellationToken token);
|
||||
|
||||
Task<List<LyricsSearchResult>> SearchAllAsync(SongInfo songInfo, CancellationToken token);
|
||||
Task<List<LyricsSearchResult>> SearchAllAsync(SongInfo songInfo, bool checkCache, CancellationToken token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
@@ -91,8 +92,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<LyricsSearchResult> SearchSmartlyAsync(SongInfo songInfo, CancellationToken token)
|
||||
public async Task<LyricsSearchResult?> SearchSmartlyAsync(SongInfo songInfo, bool checkCache, LyricsSearchType? lyricsSearchType, CancellationToken token)
|
||||
{
|
||||
if (lyricsSearchType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var lyricsSearchResult = new LyricsSearchResult();
|
||||
|
||||
string overridenTitle = songInfo.Title;
|
||||
@@ -101,6 +107,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
_logger.LogInformation("SearchSmartlyAsync {SongInfo}", songInfo);
|
||||
|
||||
// 先检查该曲目是否已被用户映射
|
||||
var found = _settingsService.AppSettings.MappedSongSearchQueries
|
||||
.FirstOrDefault(x =>
|
||||
x.OriginalTitle == overridenTitle &&
|
||||
@@ -133,45 +140,63 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
.WithTitle(overridenTitle)
|
||||
.WithArtist(overridenArtists)
|
||||
.WithAlbum(overridenAlbum),
|
||||
targetProvider.Value, token);
|
||||
targetProvider.Value, checkCache, token);
|
||||
}
|
||||
}
|
||||
|
||||
List<LyricsSearchResult> lyricsSearchResults = [];
|
||||
|
||||
// 曲目没有被映射
|
||||
foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == songInfo.PlayerId)?.LyricsSearchProvidersInfo ?? [])
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
lyricsSearchResult = await SearchSingleAsync(
|
||||
((SongInfo)songInfo.Clone())
|
||||
.WithTitle(overridenTitle)
|
||||
.WithArtist(overridenArtists)
|
||||
.WithAlbum(overridenAlbum),
|
||||
provider.Provider, token);
|
||||
provider.Provider, checkCache, token);
|
||||
|
||||
if (lyricsSearchResult.IsFound)
|
||||
{
|
||||
return lyricsSearchResult;
|
||||
switch (lyricsSearchType)
|
||||
{
|
||||
case LyricsSearchType.Sequential:
|
||||
return lyricsSearchResult;
|
||||
case LyricsSearchType.BestMatch:
|
||||
lyricsSearchResults.Add((LyricsSearchResult)lyricsSearchResult.Clone());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
return lyricsSearchType switch
|
||||
{
|
||||
LyricsSearchType.Sequential => lyricsSearchResult,
|
||||
LyricsSearchType.BestMatch => lyricsSearchResults.OrderByDescending(x => x.MatchPercentage).FirstOrDefault(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<LyricsSearchResult>> SearchAllAsync(SongInfo songInfo, CancellationToken token)
|
||||
public async Task<List<LyricsSearchResult>> SearchAllAsync(SongInfo songInfo, bool checkCache, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("SearchAllAsync {SongInfo}", songInfo);
|
||||
var results = new List<LyricsSearchResult>();
|
||||
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
|
||||
{
|
||||
var searchResult = await SearchSingleAsync(songInfo, provider, token);
|
||||
var searchResult = await SearchSingleAsync(songInfo, provider, checkCache, token);
|
||||
results.Add(searchResult);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<LyricsSearchResult> SearchSingleAsync(SongInfo songInfo, LyricsSearchProvider provider, CancellationToken token)
|
||||
private async Task<LyricsSearchResult> SearchSingleAsync(SongInfo songInfo, LyricsSearchProvider provider, bool checkCache, CancellationToken token)
|
||||
{
|
||||
var lyricsSearchResult = new LyricsSearchResult
|
||||
{
|
||||
@@ -182,14 +207,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
LyricsFormat lyricsFormat = provider.GetLyricsFormat();
|
||||
|
||||
// Check cache first
|
||||
if (provider.IsRemote())
|
||||
// Check cache first if allowed
|
||||
if (checkCache && provider.IsRemote())
|
||||
{
|
||||
var cachedLyrics = FileHelper.ReadLyricsCache(songInfo, lyricsFormat, provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
var cached = FileHelper.ReadLyricsCache(songInfo, provider);
|
||||
if (cached != null)
|
||||
{
|
||||
lyricsSearchResult.Raw = cachedLyrics;
|
||||
lyricsSearchResult.CopyFromSongInfo(songInfo);
|
||||
lyricsSearchResult = cached;
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
}
|
||||
@@ -234,30 +258,36 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
if (lyricsSearchResult.IsFound)
|
||||
{
|
||||
if (provider.IsRemote())
|
||||
{
|
||||
FileHelper.WriteLyricsCache(songInfo, lyricsSearchResult.Raw!, lyricsFormat, provider.GetCacheDirectory());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
if (lyricsSearchResult.IsFound)
|
||||
{
|
||||
if (provider.IsRemote())
|
||||
{
|
||||
FileHelper.WriteLyricsCache(songInfo, lyricsSearchResult);
|
||||
}
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
private async Task<LyricsSearchResult> SearchFile(SongInfo songInfo, LyricsFormat format)
|
||||
{
|
||||
var lyricsSearchResult = new LyricsSearchResult
|
||||
var lyricsSearchResult = new LyricsSearchResult();
|
||||
|
||||
if (format.ToLyricsSearchProvider() is LyricsSearchProvider lyricsSearchProvider)
|
||||
{
|
||||
Provider = format.ToLyricsSearchProvider(),
|
||||
};
|
||||
lyricsSearchResult.Provider = lyricsSearchProvider;
|
||||
}
|
||||
|
||||
foreach (var folder in _settingsService.AppSettings.LocalMediaFolders)
|
||||
{
|
||||
@@ -268,13 +298,14 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
foreach (var file in DirectoryHelper.GetAllFiles(folder.Path, $"*{format.ToFileExtension()}"))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(file);
|
||||
if (FileHelper.IsSwitchableNormalizedMatch(fileName, songInfo.Title, songInfo.DisplayArtists) || songInfo.LinkedFileName == fileName)
|
||||
if (StringHelper.IsSwitchableNormalizedMatch(fileName, songInfo.Title, songInfo.DisplayArtists) || songInfo.LinkedFileName == fileName)
|
||||
{
|
||||
string? raw = await File.ReadAllTextAsync(file, FileHelper.GetEncoding(file));
|
||||
if (raw != null)
|
||||
{
|
||||
lyricsSearchResult.Raw = raw;
|
||||
lyricsSearchResult.CopyFromSongInfo(songInfo);
|
||||
lyricsSearchResult.Reference = file;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
@@ -307,13 +338,14 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
var track = new Track(file);
|
||||
if ((songInfo.Album != "" && track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists && track.Album == songInfo.Album)
|
||||
|| (songInfo.Album == "" && track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists)
|
||||
|| (songInfo.Album == "" && FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.Title, songInfo.DisplayArtists)))
|
||||
|| (songInfo.Album == "" && StringHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.Title, songInfo.DisplayArtists)))
|
||||
{
|
||||
var plain = track.GetRawLyrics();
|
||||
if (!plain.IsNullOrEmpty())
|
||||
{
|
||||
lyricsSearchResult.Raw = plain;
|
||||
lyricsSearchResult.CopyFromSongInfo(songInfo);
|
||||
lyricsSearchResult.Reference = file;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
@@ -344,6 +376,10 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
string? rawLyricFile = null;
|
||||
await foreach (var line in File.ReadLinesAsync(PathHelper.AmllTtmlDbIndexPath))
|
||||
{
|
||||
lyricsSearchResult.Title = null;
|
||||
lyricsSearchResult.Artists = null;
|
||||
lyricsSearchResult.Album = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
try
|
||||
@@ -352,8 +388,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
var root = doc.RootElement;
|
||||
if (!root.TryGetProperty("metadata", out var metadataArr))
|
||||
continue;
|
||||
string? musicName = null;
|
||||
string? artists = null;
|
||||
|
||||
foreach (var meta in metadataArr.EnumerateArray())
|
||||
{
|
||||
if (meta.GetArrayLength() != 2)
|
||||
@@ -361,14 +396,14 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
var key = meta[0].GetString();
|
||||
var valueArr = meta[1];
|
||||
if (key == "musicName" && valueArr.GetArrayLength() > 0)
|
||||
musicName = valueArr[0].GetString();
|
||||
lyricsSearchResult.Title = valueArr[0].GetString();
|
||||
if (key == "artists" && valueArr.GetArrayLength() > 0)
|
||||
artists = valueArr.EnumerateArray().Select(x=>x.GetString()).Join(ATL.Settings.DisplayValueSeparator.ToString());
|
||||
lyricsSearchResult.Artists = valueArr.EnumerateArray().Select(x => x.GetString() ?? "").ToArray();
|
||||
if (key == "album" && valueArr.GetArrayLength() > 0)
|
||||
lyricsSearchResult.Album = valueArr[0].GetString();
|
||||
}
|
||||
if (musicName == null || artists == null)
|
||||
continue;
|
||||
|
||||
if (FileHelper.IsSwitchableNormalizedMatch($"{artists} - {musicName}", songInfo.Title, songInfo.DisplayArtists))
|
||||
if (MetadataComparer.CalculateScore(songInfo, lyricsSearchResult) > 0)
|
||||
{
|
||||
if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp))
|
||||
{
|
||||
@@ -387,6 +422,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
// 下载歌词内容
|
||||
var url = $"{_settingsService.AppSettings.GeneralSettings.AmllTtmlDbBaseUrl}/{Constants.AmllTTmlDB.QueryPrefix}/{rawLyricFile}";
|
||||
lyricsSearchResult.Reference = url;
|
||||
try
|
||||
{
|
||||
using var response = await _amllTtmlDbHttpClient.GetAsync(url);
|
||||
@@ -397,9 +433,6 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
string lyrics = await response.Content.ReadAsStringAsync();
|
||||
|
||||
lyricsSearchResult.Raw = lyrics;
|
||||
lyricsSearchResult.Title = songInfo.Title;
|
||||
lyricsSearchResult.Artists = songInfo.Artists;
|
||||
lyricsSearchResult.Album = songInfo.Album;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -440,6 +473,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
string? searchedTitle = null;
|
||||
string? searchedArtist = null;
|
||||
string? searchedAlbum = null;
|
||||
double? searchedDuration = null;
|
||||
|
||||
if (jArr.ValueKind == JsonValueKind.Array && jArr.GetArrayLength() > 0)
|
||||
{
|
||||
@@ -448,12 +482,16 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
searchedTitle = first.GetProperty("trackName").GetString();
|
||||
searchedArtist = first.GetProperty("artistName").GetString();
|
||||
searchedAlbum = first.GetProperty("albumName").GetString();
|
||||
searchedDuration = first.GetProperty("duration").GetDouble();
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Title = searchedTitle;
|
||||
lyricsSearchResult.Artists = searchedArtist?.Split(ATL.Settings.DisplayValueSeparator);
|
||||
lyricsSearchResult.Artists = searchedArtist?.SplitByCommonSplitter();
|
||||
lyricsSearchResult.Album = searchedAlbum;
|
||||
lyricsSearchResult.Duration = searchedDuration;
|
||||
|
||||
lyricsSearchResult.Reference = url;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
@@ -493,7 +531,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
AlbumArtists = songInfo.Artists.ToList(),
|
||||
Artists = songInfo.Artists.ToList(),
|
||||
Title = songInfo.Title,
|
||||
}, searcher, Lyricify.Lyrics.Searchers.Helpers.CompareHelper.MatchType.Medium);
|
||||
}, searcher, Lyricify.Lyrics.Searchers.Helpers.CompareHelper.MatchType.NoMatch);
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
@@ -501,41 +539,24 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
songInfo,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Raw = response?.Lyrics;
|
||||
lyricsSearchResult.Translation = response?.Trans;
|
||||
lyricsSearchResult.Reference = $"https://y.qq.com/n/ryqq/songDetail/{qqResult.Mid}";
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var original = response?.Lrc?.Lyric;
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
songInfo,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Raw = response?.Lrc?.Lyric;
|
||||
lyricsSearchResult.Translation = response?.Tlyric?.Lyric;
|
||||
lyricsSearchResult.Reference = $"https://music.163.com/song?id={neteaseResult.Id}";
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
string? original = null;
|
||||
string? translated = null;
|
||||
var candidate = response?.Candidates.FirstOrDefault();
|
||||
if (candidate != null)
|
||||
{
|
||||
@@ -545,7 +566,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
|
||||
if (parsedList != null)
|
||||
{
|
||||
string translated = "";
|
||||
translated = "";
|
||||
foreach (var item in parsedList)
|
||||
{
|
||||
if (item is Lyricify.Lyrics.Models.FullSyllableLineInfo fullSyllableLineInfo)
|
||||
@@ -556,26 +577,20 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
translated += $"[{startTimeStr}]{chTranslation}\n";
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
songInfo,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.KugouTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
lyricsSearchResult.Reference = $"https://www.kugou.com/";
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Translation = translated;
|
||||
}
|
||||
}
|
||||
|
||||
lyricsSearchResult.Title = result?.Title;
|
||||
lyricsSearchResult.Artists = result?.Artists;
|
||||
lyricsSearchResult.Album = result?.Album;
|
||||
lyricsSearchResult.Duration = result?.DurationMs / 1000;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
@@ -589,12 +604,14 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
if (await _appleMusic.InitAsync())
|
||||
{
|
||||
var raw = await _appleMusic.GetLyricsAsync(songInfo.Title, songInfo.DisplayArtists);
|
||||
string id = await _appleMusic.SearchSongInfoAsync(songInfo.DisplayArtists, songInfo.Title);
|
||||
string? raw = await _appleMusic.GetLyricsAsync(id);
|
||||
_logger.LogInformation("SearchAppleMusicAsync");
|
||||
lyricsSearchResult.Raw = raw;
|
||||
lyricsSearchResult.Title = songInfo.Title;
|
||||
lyricsSearchResult.Artists = songInfo.Artists;
|
||||
lyricsSearchResult.Album = "";
|
||||
lyricsSearchResult.Reference = $"https://music.apple.com/song/{id}";
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
|
||||
@@ -158,13 +158,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
if (CurrentSongInfo != null)
|
||||
{
|
||||
CurrentLyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(CurrentSongInfo, token), token);
|
||||
CurrentLyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(
|
||||
CurrentSongInfo,
|
||||
!_settingsService.AppSettings.GeneralSettings.IgnoreCacheWhenSearching,
|
||||
CurrentMediaSourceProviderInfo?.LyricsSearchType,
|
||||
token),
|
||||
token);
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
var lyricsParser = new LyricsParser();
|
||||
lyricsParser.Parse(
|
||||
_settingsService.AppSettings.MappedSongSearchQueries.ToList(),
|
||||
CurrentSongInfo, CurrentLyricsSearchResult?.Raw, CurrentLyricsSearchResult?.Provider);
|
||||
lyricsParser.Parse(CurrentSongInfo, CurrentLyricsSearchResult);
|
||||
_lyricsDataArr = lyricsParser.LyricsDataArr;
|
||||
ApplyChinesePreference();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using DevWinUI;
|
||||
using EvtSource;
|
||||
using Lyricify.Lyrics.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
@@ -32,6 +33,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.Windows.Shell;
|
||||
using Windows.Media.Control;
|
||||
@@ -180,6 +182,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
case nameof(MediaSourceProviderInfo.LyricsSearchProvidersInfo):
|
||||
UpdateLyrics();
|
||||
break;
|
||||
case nameof(MediaSourceProviderInfo.LyricsSearchType):
|
||||
UpdateLyrics();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -300,7 +305,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
CurrentSongInfo = SongInfoExtensions.Placeholder;
|
||||
|
||||
if (PlayerIDMatcher.IsLXMusic(sessionId))
|
||||
if (PlayerIDHelper.IsLXMusic(sessionId))
|
||||
{
|
||||
StopSSE();
|
||||
}
|
||||
@@ -319,12 +324,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
|
||||
string? songId = null;
|
||||
|
||||
if (PlayerIDMatcher.IsAppleMusic(sessionId))
|
||||
if (PlayerIDHelper.IsAppleMusic(sessionId))
|
||||
{
|
||||
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
|
||||
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
|
||||
}
|
||||
else if (PlayerIDMatcher.IsNeteaseFamily(sessionId))
|
||||
else if (PlayerIDHelper.IsNeteaseFamily(sessionId))
|
||||
{
|
||||
songId = mediaProperties?.Genres
|
||||
.FirstOrDefault(x => x.StartsWith(ExtendedGenreFiled.NetEaseCloudMusicTrackID))?
|
||||
@@ -349,7 +354,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (PlayerIDMatcher.IsLXMusic(sessionId))
|
||||
if (PlayerIDHelper.IsLXMusic(sessionId))
|
||||
{
|
||||
StartSSE();
|
||||
}
|
||||
@@ -358,7 +363,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
StopSSE();
|
||||
}
|
||||
|
||||
if (PlayerIDMatcher.IsLXMusic(sessionId) && _lxMusicAlbumArtBytes != null)
|
||||
if (PlayerIDHelper.IsLXMusic(sessionId) && _lxMusicAlbumArtBytes != null)
|
||||
{
|
||||
_SMTCAlbumArtBuffer = _lxMusicAlbumArtBytes.AsBuffer();
|
||||
}
|
||||
@@ -551,7 +556,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
|
||||
{
|
||||
if (PlayerIDMatcher.IsLXMusic(CurrentSongInfo?.PlayerId))
|
||||
if (PlayerIDHelper.IsLXMusic(CurrentSongInfo?.PlayerId))
|
||||
{
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
if (data.ValueKind == JsonValueKind.Number)
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Lyrics Window Management Shortcut Settings</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<value>Match percentage</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Play source shortcut settings</value>
|
||||
</data>
|
||||
@@ -270,9 +273,15 @@
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
<value>* Save changes take effect immediately, after which the track lyrics will be retrieved with mapping information and target lyrics; marking as pure music will directly return to pure music placeholder lyrics. Reset to retrieve by original data.</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlIgnoreCache.Header" xml:space="preserve">
|
||||
<value>Ignore cache when searching</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
|
||||
<value>mapped as</value>
|
||||
</data>
|
||||
@@ -907,6 +916,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>Light</value>
|
||||
</data>
|
||||
<data name="SettingsPageLinkedFile.Text" xml:space="preserve">
|
||||
<value>Linked local file</value>
|
||||
</data>
|
||||
<data name="SettingsPageListenNewSession.Header" xml:space="preserve">
|
||||
<value>Enable monitoring for new playback sources</value>
|
||||
</data>
|
||||
@@ -1039,9 +1051,21 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchBestMatch.Content" xml:space="preserve">
|
||||
<value>Best match</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Text" xml:space="preserve">
|
||||
<value>Configure lyrics source</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchSequential.Content" xml:space="preserve">
|
||||
<value>Sequential</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Description" xml:space="preserve">
|
||||
<value>In order: Find according to the priority of the list below and return the first lyrics found; Best Match: Search through all enabled lyrics sources and automatically select the best match</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Header" xml:space="preserve">
|
||||
<value>Lyrics search strategy</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
|
||||
<value>Semi Bold</value>
|
||||
</data>
|
||||
@@ -1168,6 +1192,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<value>Playback source</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<value>Playback source ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>Play "Cut To The Feeling" on "soundcloud.com"</value>
|
||||
</data>
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞ウィンドウ管理ショートカット設定</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<value>マッチ率</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
<value>再生ソースクイック設定</value>
|
||||
</data>
|
||||
@@ -270,9 +273,15 @@
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<value>間隔</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
<value>*保存変更はすぐに有効になります。その後、トラックの歌詞がマッピング情報とターゲット歌詞で取得されます。純粋な音楽としてのマーキングは、純粋な音楽プレースホルダーの歌詞に直接戻ります。元のデータで取得するためにリセット。</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlIgnoreCache.Header" xml:space="preserve">
|
||||
<value>検索時にキャッシュを無視してください</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
|
||||
<value>としてマッピングされました</value>
|
||||
</data>
|
||||
@@ -907,6 +916,9 @@
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>ライト</value>
|
||||
</data>
|
||||
<data name="SettingsPageLinkedFile.Text" xml:space="preserve">
|
||||
<value>リンクされたローカルファイル</value>
|
||||
</data>
|
||||
<data name="SettingsPageListenNewSession.Header" xml:space="preserve">
|
||||
<value>新しい再生ソースの監視を有効にします</value>
|
||||
</data>
|
||||
@@ -1039,9 +1051,21 @@
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchBestMatch.Content" xml:space="preserve">
|
||||
<value>ベストマッチ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Text" xml:space="preserve">
|
||||
<value>歌詞ソースを構成します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchSequential.Content" xml:space="preserve">
|
||||
<value>順次処理</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Description" xml:space="preserve">
|
||||
<value>順番:以下のリストの優先順位に従って検索し、最初に見つかった歌詞を返します。ベストマッチ:有効になっているすべての歌詞ソースを検索し、自動的に最適なものを選択します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Header" xml:space="preserve">
|
||||
<value>歌詞検索戦略</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
|
||||
<value>セミボールド</value>
|
||||
</data>
|
||||
@@ -1168,6 +1192,9 @@
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<value>再生ソース</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<value>再生ソースID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>「SoundCloud.com」で「Cut to the Feeling」を再生する</value>
|
||||
</data>
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>가사 창 관리 바로 가기 설정</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<value>매치 퍼센트</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
<value>재생 소스 빠른 설정</value>
|
||||
</data>
|
||||
@@ -270,9 +273,15 @@
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<value>지속</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
<value>* 변경 사항 저장 변경은 즉시 적용되며, 그 후 트랙 가사는 맵핑 정보와 대상 가사로 검색됩니다. 순수한 음악으로 표시하면 순수한 음악 자리 표시 자 가사로 직접 돌아갑니다. 원래 데이터로 검색하도록 재설정하십시오.</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlIgnoreCache.Header" xml:space="preserve">
|
||||
<value>검색 시 캐시를 무시하세요</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
|
||||
<value>로 매핑됨</value>
|
||||
</data>
|
||||
@@ -907,6 +916,9 @@
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>빛</value>
|
||||
</data>
|
||||
<data name="SettingsPageLinkedFile.Text" xml:space="preserve">
|
||||
<value>연결된 로컬 파일</value>
|
||||
</data>
|
||||
<data name="SettingsPageListenNewSession.Header" xml:space="preserve">
|
||||
<value>새로운 재생 소스에 대한 모니터링을 활성화하십시오</value>
|
||||
</data>
|
||||
@@ -1039,9 +1051,21 @@
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>오른쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchBestMatch.Content" xml:space="preserve">
|
||||
<value>최상의 검색 결과</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Text" xml:space="preserve">
|
||||
<value>가사 소스를 구성하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchSequential.Content" xml:space="preserve">
|
||||
<value>수열</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Description" xml:space="preserve">
|
||||
<value>순서대로: 아래 목록의 우선 순위에 따라 찾아 발견된 첫 번째 가사를 반환합니다. 베스트 매치: 활성화된 모든 가사 소스를 검색하여 자동으로 가장 적합한 가사를 선택합니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Header" xml:space="preserve">
|
||||
<value>가사 검색 전략</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
|
||||
<value>반 대담한</value>
|
||||
</data>
|
||||
@@ -1168,6 +1192,9 @@
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<value>재생 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<value>재생 소스 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>"soundcloud.com"에서 "Fut to the Feeling"을 재생하십시오.</value>
|
||||
</data>
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌词窗口管理快捷设置</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<value>匹配百分比</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
<value>播放源快捷设置</value>
|
||||
</data>
|
||||
@@ -270,9 +273,15 @@
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<value>时长</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
<value>* 保存更改立即生效,此后将以映射信息和目标歌词源检索该曲目歌词;标记为纯音乐将直接返回纯音乐占位歌词。重置以按原始数据检索。</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlIgnoreCache.Header" xml:space="preserve">
|
||||
<value>搜索时忽略缓存</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
|
||||
<value>映射为</value>
|
||||
</data>
|
||||
@@ -907,6 +916,9 @@
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="SettingsPageLinkedFile.Text" xml:space="preserve">
|
||||
<value>链接的本地文件</value>
|
||||
</data>
|
||||
<data name="SettingsPageListenNewSession.Header" xml:space="preserve">
|
||||
<value>启用对新播放源的监听</value>
|
||||
</data>
|
||||
@@ -1039,9 +1051,21 @@
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchBestMatch.Content" xml:space="preserve">
|
||||
<value>最佳匹配</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Text" xml:space="preserve">
|
||||
<value>配置歌词源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchSequential.Content" xml:space="preserve">
|
||||
<value>按顺序</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Description" xml:space="preserve">
|
||||
<value>按顺序:依照下方列表的优先级依次查找,返回第一个找到的歌词; 最佳匹配:在所有已启用的歌词源中搜索,并自动选择匹配度最高的结果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Header" xml:space="preserve">
|
||||
<value>歌词搜索策略</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
|
||||
<value>次粗</value>
|
||||
</data>
|
||||
@@ -1168,6 +1192,9 @@
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<value>播放源 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
|
||||
</data>
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞視窗管理快捷設定</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<value>匹配百分比</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
<value>播放源快捷設置</value>
|
||||
</data>
|
||||
@@ -270,9 +273,15 @@
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<value>時長</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
<value>* 保存更改立即生效,此後將以映射信息和目標歌詞源檢索該曲目歌詞;標記為純音樂將直接返回純音樂佔位歌詞。重置以按原始數據檢索。</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlIgnoreCache.Header" xml:space="preserve">
|
||||
<value>搜尋時忽略快取</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
|
||||
<value>映射爲</value>
|
||||
</data>
|
||||
@@ -907,6 +916,9 @@
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>淺色</value>
|
||||
</data>
|
||||
<data name="SettingsPageLinkedFile.Text" xml:space="preserve">
|
||||
<value>連結的本機檔案</value>
|
||||
</data>
|
||||
<data name="SettingsPageListenNewSession.Header" xml:space="preserve">
|
||||
<value>啟用對新播放來源的監聽</value>
|
||||
</data>
|
||||
@@ -1039,9 +1051,21 @@
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchBestMatch.Content" xml:space="preserve">
|
||||
<value>最佳匹配</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Text" xml:space="preserve">
|
||||
<value>配置歌詞源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchSequential.Content" xml:space="preserve">
|
||||
<value>按順序</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Description" xml:space="preserve">
|
||||
<value>依序:依照下方清單的優先順序依序查找,傳回第一個找到的歌詞;最佳符合:在所有已啟用的歌詞來源中搜尋,並自動選擇符合度最高的結果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchType.Header" xml:space="preserve">
|
||||
<value>歌詞搜尋策略</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
|
||||
<value>次粗</value>
|
||||
</data>
|
||||
@@ -1168,6 +1192,9 @@
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<value>播放源 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
|
||||
</data>
|
||||
|
||||
@@ -93,11 +93,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.NeteaseLyricsCacheDirectory);
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.QQLyricsCacheDirectory);
|
||||
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.TranslationCacheDirectory);
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.KugouTranslationCacheDirectory);
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.NeteaseTranslationCacheDirectory);
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.QQTranslationCacheDirectory);
|
||||
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.iTunesAlbumArtCacheDirectory);
|
||||
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ActionCompleted"));
|
||||
|
||||
@@ -113,11 +113,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
LyricsSearchResults = [..await Task.Run(async () =>
|
||||
{
|
||||
return await _lyricsSearchService.SearchAllAsync(
|
||||
var result = await _lyricsSearchService.SearchAllAsync(
|
||||
((SongInfo?)_mediaSessionsService.CurrentSongInfo?.Clone() ?? new())
|
||||
.WithTitle(MappedSongSearchQuery.MappedTitle)
|
||||
.WithArtist(MappedSongSearchQuery.MappedArtist.SplitByCommonSplitter())
|
||||
.WithAlbum(MappedSongSearchQuery.MappedAlbum), token);
|
||||
.WithAlbum(MappedSongSearchQuery.MappedAlbum),
|
||||
!_settingsService.AppSettings.GeneralSettings.IgnoreCacheWhenSearching,
|
||||
token);
|
||||
return result;
|
||||
}, token)];
|
||||
IsSearching = false;
|
||||
});
|
||||
@@ -176,10 +179,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (value?.Raw != null)
|
||||
{
|
||||
var lyricsParser = new LyricsParser();
|
||||
lyricsParser.Parse(
|
||||
[MappedSongSearchQuery ?? new()],
|
||||
_mediaSessionsService.CurrentSongInfo,
|
||||
value?.Raw, value?.Provider);
|
||||
lyricsParser.Parse(_mediaSessionsService.CurrentSongInfo, value);
|
||||
LyricsDataArr = [.. lyricsParser.LyricsDataArr];
|
||||
}
|
||||
else
|
||||
|
||||