Compare commits

...

51 Commits

Author SHA1 Message Date
Zhe Fang
94b22552e5 update doc 2025-10-10 11:40:44 -04:00
Zhe Fang
6deb16f6cb update version code 2025-10-10 11:34:39 -04:00
Zhe Fang
e467ab9c73 替换 SourceAppUserModelId 为 PlayerId 并新增 SongId
将 SongInfo 中的 SourceAppUserModelId 属性替换为 PlayerId,新增可选属性 SongId 以支持更精确的歌曲标识。
更新了相关服务和方法的参数签名,增加对 SongId 的支持,包括 SearchSmartlyAsync 和 SearchSingleAsync 方法。
调整了 MediaSessionsService 的逻辑,支持不同播放器的 SongId 处理。
优化了代码的灵活性和可扩展性,便于处理多播放器和歌曲标识。
2025-10-10 11:29:50 -04:00
Zhe Fang
ea038c9c56 更新 README.md 2025-10-05 07:59:44 -04:00
Zhe Fang
560250ad30 更新 README.md 2025-10-05 07:57:35 -04:00
Zhe Fang
536acc69a5 更新 README.md 2025-10-05 07:56:00 -04:00
Zhe Fang
0bbb379912 update LICENSE.txt 2025-09-21 21:15:15 -04:00
Zhe Fang
1f4d29e6f2 更新 README.md 2025-09-13 06:49:20 -04:00
Zhe Fang
5d1d7476c9 update screenshots 2025-09-11 21:10:24 -04:00
Zhe Fang
e016baefe1 doc 2025-09-11 20:44:17 -04:00
Zhe Fang
70b6194788 Improve formatting and clarity in README.md 2025-09-11 20:32:26 -04:00
Zhe Fang
9f103b0ea3 Update README with QQ 音乐 details and grammar fixes 2025-09-10 09:30:39 -04:00
Zhe Fang
2924140f95 Refine README.md for grammar and clarity
Corrected grammar and punctuation throughout the README file for improved clarity and consistency.
2025-09-08 18:33:06 -04:00
Zhe Fang
3d3f168926 更新 README.md 2025-09-05 07:23:52 -04:00
Zhe Fang
63b2285a36 Update download link for app in README 2025-09-05 07:03:26 -04:00
Zhe Fang
780689fa05 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-09-04 15:19:36 -04:00
Zhe Fang
01384717c4 更新应用版本和图像处理逻辑
在 `Package.appxmanifest` 中,将版本号更新为 `1.0.73.0`,发布者显示名称更改为 `Zhe Fang`。
在 `ImageHelper.cs` 中新增 `DataUrlToByteArray` 和 `GetImageBytesFromUrlAsync` 方法,以支持数据 URL 和网络图片的处理。
在 `MediaSessionsService.cs` 中,更新了音乐专辑封面图像的下载逻辑,改用新的图像获取方法并记录 URL 信息。
2025-09-04 15:19:33 -04:00
Zhe Fang
e18d78170a Add Apple Music configuration instructions
Added instructions for configuring Apple Music in the README.
2025-09-04 09:38:31 -04:00
Zhe Fang
023bf77afc Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-09-04 09:19:18 -04:00
Zhe Fang
2877ac2101 更新版本并增强媒体处理功能
- 更新 `Package.appxmanifest` 版本号至 `1.0.72.0`。
- 修改 `LXMusic.cs` 中的 `QuerySuffix`,新增 `picUrl` 过滤器。
- 清理 `MediaSourceProviderToLogoUriConverter.cs` 中的 `using` 语句。
- 在 `ImageHelper.cs` 中添加异步方法 `DownloadImageAsByteArrayAsync`,用于下载图像。
- 在 `MediaSessionsService.cs` 中添加 `_lxMusicAlbumArtBytes` 字节数组以存储专辑封面。
- 更新媒体属性处理逻辑,以支持从 `picUrl` 下载专辑封面。
- 修改 SSE 消息接收逻辑,支持异步操作以处理图像下载和更新。
2025-09-04 09:19:16 -04:00
Zhe Fang
16d82109bb 更新 README.md 2025-09-03 12:19:09 -04:00
Zhe Fang
c703f04119 Revise README for Microsoft Store and download options 2025-09-02 18:30:31 -04:00
Zhe Fang
998853f9d2 更新 README.md 2025-09-02 13:40:45 -04:00
Zhe Fang
f560735da0 更新 README.md 2025-09-02 13:35:09 -04:00
Zhe Fang
ab9da73b49 更新 README.md 2025-09-02 13:00:38 -04:00
Zhe Fang
dc364edf75 更新 README.md 2025-09-02 12:59:27 -04:00
Zhe Fang
7fbc8fbfe7 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-09-02 07:38:47 -04:00
Zhe Fang
4a00bb2ddf 更新版本并添加对 Apple Music 的支持
- 将 `Package.appxmanifest` 的版本号更新为 `1.0.71.0`。
- 移除 `BetterLyrics.WinUI3.csproj` 中对 `TinyPinyin.Net` 的引用。
- 在 `PlaybackSettingsControl.xaml` 中更新目标语言选择,移除中文选项并添加多个新语言。
- 新增 Apple Music 令牌输入框和按钮,允许用户保存令牌。
- 在 `LyricsSearchProviderToDisplayNameConverter.cs` 和 `TranslationSearchProviderToDisplayNameConverter.cs` 中添加对 Apple Music 的支持。
- 在 `LyricsSearchProvider.cs` 中新增 `AppleMusic` 作为歌词搜索提供者,并添加相关缓存目录和格式。
- 更新 `LanguageHelper.cs` 中的目标语言列表。
- 将 `TranslationSettings.cs` 中的 `SelectedTargetLanguageIndex` 属性更改为 `SelectedTargetLanguageCode`。
- 在 `LyricsSearchService.cs` 中添加 Apple Music 的歌词搜索功能。
- 更新 `MediaSessionsService.cs` 中的翻译和歌词更新逻辑。
- 移除 `SettingsPageViewModel.cs` 中的库信息支持,添加对媒体会话服务的引用。
- 新增 `AppleMusic.cs` 文件,包含与 Apple Music API 交互的逻辑。
2025-09-02 07:38:45 -04:00
Zhe Fang
7472aa048f 更新 README.md 2025-08-27 22:07:14 -04:00
Zhe Fang
49b0f7a692 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-08-27 08:28:44 -04:00
Zhe Fang
4d0602ebef 更新版本号并优化歌词窗口边界计算
在 `Package.appxmanifest` 文件中,将应用程序版本号从 `1.0.66.0` 更新为 `1.0.67.0`。
在 `LiveStates.cs` 文件中,修改了 `DemoLyricsWindowBounds` 的计算方式,以更准确地反映歌词窗口在监视器上的位置。
2025-08-27 08:28:42 -04:00
Zhe Fang
626395d93a Update instructions from 'Clone' to 'Fork' 2025-08-26 22:34:18 -04:00
Zhe Fang
0ab5602569 Revise translation contribution instructions
Updated translation instructions for contributors.
2025-08-26 16:50:34 -04:00
Zhe Fang
37a7528762 更新 FAQ.md 2025-08-25 08:56:13 -04:00
Zhe Fang
3ca391a509 更新 FAQ.md 2025-08-25 08:54:43 -04:00
Zhe Fang
f5e542d2f3 更新 How2Install.md 2025-08-25 08:53:10 -04:00
Zhe Fang
107bdf8bee 更新 README.md 2025-08-25 08:51:19 -04:00
Zhe Fang
3e9e56f5cc ignore ttml x-roman span 2025-08-25 07:12:24 -04:00
Zhe Fang
b0fd43ead5 fix input issue in search panel; fix display issue in lyrics window manager 2025-08-24 16:29:28 -04:00
Zhe Fang
81c59495c0 improvement 2025-08-23 21:12:57 -04:00
Zhe Fang
7c5f1a804e Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-08-23 18:09:14 -04:00
Zhe Fang
13ee4227f7 fix #101 fix #100 2025-08-23 18:09:12 -04:00
Zhe Fang
c1360ac972 更新 README.md 2025-08-22 17:48:53 -04:00
Zhe Fang
c72aa8a58f 更新 FAQ.md 2025-08-22 15:33:46 -04:00
Zhe Fang
9545ed610b update readme 2025-08-22 15:26:51 -04:00
Zhe Fang
1c4515acb9 update readme 2025-08-22 15:18:40 -04:00
Zhe Fang
92a6fe46de update docs 2025-08-22 15:00:57 -04:00
Zhe Fang
2f9fa02214 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-08-21 17:56:24 -04:00
Zhe Fang
c174363c07 fix #99 ;add thumb for timeline slider; add margin for top command area 2025-08-21 17:56:13 -04:00
Zhe Fang
8506062c9a Update FUNDING.yml 2025-08-21 13:19:24 -04:00
Zhe Fang
52711cba1f showing ordered system font family list; fix issue that timeline change can not be invoked sometimes; add jump to specific lyrics line when clikcing in searching flyout 2025-08-21 11:01:39 -04:00
129 changed files with 2264 additions and 2041 deletions

2
.github/FUNDING.yml vendored
View File

@@ -12,4 +12,4 @@ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cl
polar: # Replace with a single Polar username
buy_me_a_coffee: founchoo
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://paypal.me/zhefangpay']

View File

@@ -12,13 +12,13 @@
<Identity
Name="37412.BetterLyrics"
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
Version="1.0.59.0" />
Version="1.0.75.0" />
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>BetterLyrics</DisplayName>
<PublisherDisplayName>founchoo</PublisherDisplayName>
<PublisherDisplayName>Zhe Fang</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>

View File

@@ -58,6 +58,7 @@
<converter:ShortcutToStringConverter x:Key="ShortcutToStringConverter" />
<converter:BoolNegationToVisibilityConverter x:Key="BoolNegationToVisibilityConverter" />
<converter:BoolToOpacityConverter x:Key="BoolToOpacityConverter" />
<converter:RectToMarginConverter x:Key="RectToMarginConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@@ -32,6 +32,7 @@
<None Remove="Controls\PlaybackSettingsControl.xaml" />
<None Remove="Controls\ShortcutTextBox.xaml" />
<None Remove="Controls\SystemTray.xaml" />
<None Remove="Views\LyricsSearchWindow.xaml" />
<None Remove="Views\MusicGalleryPage.xaml" />
<None Remove="Views\MusicGalleryWindow.xaml" />
<None Remove="Views\SettingsWindow.xaml" />
@@ -59,6 +60,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
@@ -71,6 +73,7 @@
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
<PackageReference Include="NTextCat" Version="0.3.65" />
<PackageReference Include="RomajiConverter.Core" Version="1.0.9" />
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.3-dev-02320" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
@@ -78,7 +81,6 @@
<PackageReference Include="System.Drawing.Common" Version="9.0.8" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="TinyPinyin.Net" Version="1.0.2" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="Vanara.PInvoke.CoreAudio" Version="4.1.6" />
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.1.6" />
@@ -112,9 +114,6 @@
<Content Update="Assets\Chrome.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Discord.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Edge.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -166,9 +165,6 @@
<Content Update="Assets\PotPlayer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\QQ.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\QQMusic.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -181,13 +177,15 @@
<Content Update="Assets\Spotify.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Telegram.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Wiki82.profile.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Page Update="Views\LyricsSearchWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\LyricsSearchControl.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Constants
{
public static class AppleMusic
{
public const string MediaUserTokenKey = "AppleMusicMediaUserToken";
}
}

View File

@@ -8,6 +8,6 @@ namespace BetterLyrics.WinUI3.Constants
{
public static class LXMusic
{
public const string QuerySuffix = "/subscribe-player-status?filter=progress,duration";
public const string QuerySuffix = "/subscribe-player-status?filter=progress,duration,picUrl";
}
}

View File

@@ -8,7 +8,8 @@ namespace BetterLyrics.WinUI3.Constants
{
public static class Link
{
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
public const string GitHubUrl = "https://github.com/jayfunc/BetterLyrics";
public const string FAQUrl = $"{GitHubUrl}/blob/dev/FAQ/FAQ.md";
public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv";
public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1";

View File

@@ -17,6 +17,7 @@ namespace BetterLyrics.WinUI3.Constants
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";

View File

@@ -16,6 +16,7 @@ namespace BetterLyrics.WinUI3.Constants
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 音乐";

View File

@@ -46,6 +46,15 @@
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<local:ExtendedSlider
Default="18"
Frequency="2"
Maximum="48"
Minimum="12"
Value="{x:Bind ViewModel.AppSettings.AlbumArtLayoutSettings.SongInfoFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -11,185 +11,375 @@
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SwitchPresenter Margin="0,72,0,0" Value="{Binding SelectedItem.Tag, ElementName=SegmentedControl}">
<!-- App appearance -->
<controls:Case Value="General">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<!-- App appearance -->
<controls:SettingsExpander
x:Uid="SettingsPageLanguage"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF2B7;}"
IsExpanded="True">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.Language, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageSystemLanguage" />
<ComboBoxItem x:Uid="SettingsPageEN" />
<ComboBoxItem x:Uid="SettingsPageSC" />
<ComboBoxItem x:Uid="SettingsPageTC" />
<ComboBoxItem x:Uid="SettingsPageJA" />
<ComboBoxItem x:Uid="SettingsPageKO" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Uid="SettingsPageLanguage"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF2B7;}"
IsExpanded="True">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.Language, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageSystemLanguage" />
<ComboBoxItem x:Uid="SettingsPageEN" />
<ComboBoxItem x:Uid="SettingsPageSC" />
<ComboBoxItem x:Uid="SettingsPageTC" />
<ComboBoxItem x:Uid="SettingsPageJA" />
<ComboBoxItem x:Uid="SettingsPageKO" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- App behavior -->
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF71C;}">
<ToggleSwitch
x:Name="AutoStartupToggleSwitch"
Loaded="AutoStartupToggleSwitch_Loaded"
Unloaded="AutoStartupToggleSwitch_Unloaded" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- App behavior -->
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE736;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.AutoStartWindowType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartPIPLyrics" />
</ComboBox>
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Name="LyricsWindowManagerExpander"
x:Uid="SettingsPageLyricsWindowManager"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE61F;}"
IsExpanded="True" />
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF71C;}">
<ToggleSwitch
x:Name="AutoStartupToggleSwitch"
Loaded="AutoStartupToggleSwitch_Loaded"
Unloaded="AutoStartupToggleSwitch_Unloaded" />
</controls:SettingsCard>
<Grid
Margin="0,-20,0,0"
Padding="60,16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4"
Visibility="{Binding ElementName=LyricsWindowManagerExpander, Path=IsExpanded, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel Spacing="16">
<TextBlock x:Uid="SettingsPageCurrentLyricsWindowStatus" />
<StackPanel HorizontalAlignment="Left" Spacing="4">
<Grid
Width="{x:Bind ViewModel.LiveStates.DemoLyricsWindowMonitorBounds.Width, Mode=OneWay}"
Height="{x:Bind ViewModel.LiveStates.DemoLyricsWindowMonitorBounds.Height, Mode=OneWay}"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
<Grid
Width="{x:Bind ViewModel.LiveStates.DemoLyricsWindowBounds.Width, Mode=OneWay}"
Height="{x:Bind ViewModel.LiveStates.DemoLyricsWindowBounds.Height, Mode=OneWay}"
Margin="{x:Bind ViewModel.LiveStates.DemoLyricsWindowBounds, Converter={StaticResource RectToMarginConverter}, Mode=OneWay}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{ThemeResource AccentFillColorDefaultBrush}"
CornerRadius="4" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraBlack"
Opacity="0.7"
Text="{x:Bind ViewModel.LiveStates.LyricsWindowMonitorName, Mode=OneWay}"
TextWrapping="Wrap" />
</Grid>
<Grid>
<Button
x:Uid="SettingsPageRecord"
HorizontalAlignment="Stretch"
Command="{x:Bind ViewModel.RecordCurrentWindowBoundsCommand}"
Style="{StaticResource AccentButtonStyle}" />
</Grid>
</StackPanel>
</StackPanel>
</Grid>
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE736;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.AutoStartWindowType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartPIPLyrics" />
</ComboBox>
</controls:SettingsCard>
<StackPanel
Margin="0,-3,0,0"
Padding="60,16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4"
Spacing="16"
Visibility="{Binding ElementName=LyricsWindowManagerExpander, Path=IsExpanded, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<TextBlock x:Uid="SettingsPageRecordedWindowStatus" />
<GridView
Margin="-2"
CornerRadius="4"
ItemsSource="{x:Bind ViewModel.AppSettings.WindowBoundsRecords, Mode=OneWay}"
SelectionMode="None">
<GridView.ItemContainerStyle>
<Style BasedOn="{StaticResource DefaultGridViewItemStyle}" TargetType="GridViewItem">
<Setter Property="Margin" Value="5,5,5,5" />
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Spacing="4">
<Grid
Width="{Binding DemoMonitorBounds.Width}"
Height="{Binding DemoMonitorBounds.Height}"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
<Grid
Width="{Binding DemoWindowBounds.Width}"
Height="{Binding DemoWindowBounds.Height}"
Margin="{Binding DemoWindowBounds, Converter={StaticResource RectToMarginConverter}}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{ThemeResource AccentFillColorDefaultBrush}"
CornerRadius="4" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraBlack"
Opacity="0.7"
Text="{Binding MonitorDeviceName}"
TextWrapping="Wrap" />
</Grid>
<Grid ColumnSpacing="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
x:Uid="SettingsPageDelete"
Grid.Column="0"
HorizontalAlignment="Stretch"
Click="DeleteWindowBoundsRecordButton_Click" />
<Button
x:Uid="SettingsPageApply"
Grid.Column="1"
HorizontalAlignment="Stretch"
Click="ApplyWindowBoundsRecordButton_Click"
Style="{StaticResource AccentButtonStyle}" />
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE967;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreFullscreenWindow, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE967;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreFullscreenWindow, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED1A;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.HideWindowWhenNotPlaying, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED1A;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.HideWindowWhenNotPlaying, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageGlobalDrag" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C2;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsDragEverywhereEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageShowHideHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E8;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageGlobalDrag" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C2;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsDragEverywhereEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Standard mode -->
<controls:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E8;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed, Mode=TwoWay}" />
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPageAppStandard" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<!-- Playback shortcut -->
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.StandardModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPagePlaybackShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<!-- Desktop mode -->
<controls:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PlayOrPauseShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPageAppDesktop" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageNextSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.NextSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DesktopModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPagePreviousSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PreviousSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DesktopModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAutoLock" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE755;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.DesktopModeSettings.AutoLockOnDesktopMode, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLockHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DesktopModeSettings.LockShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Dock mode -->
<TextBlock x:Uid="SettingsPageAppDock" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DockModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DockModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockMonitor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7F4;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.AppSettings.DockModeSettings.DockMonitorDeviceName, Mode=TwoWay}" />
<Button
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE72C;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</Grid>
</ScrollViewer>
<controls:SettingsCard x:Uid="SettingsPageDockWindowHeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED5E;}">
<local:ExtendedSlider
Default="64"
Frequency="1"
Maximum="200"
Minimum="64"
Unit="px"
Value="{x:Bind ViewModel.AppSettings.DockModeSettings.DockWindowHeight, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:Case>
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DockModeSettings.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
</ComboBox>
</controls:SettingsCard>
<controls:Case Value="Standard">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- Picture in picture mode -->
<!-- Standard mode -->
<TextBlock x:Uid="SettingsPageAppPictureInPicture" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.PictureInPictureModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.StandardModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.PictureInPictureModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAOT" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE718;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.StandardModeSettings.IsAlwaysOnTop, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Playback shortcut -->
</StackPanel>
</Grid>
</ScrollViewer>
</controls:Case>
<TextBlock x:Uid="SettingsPagePlaybackShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:Case Value="Desktop">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PlayOrPauseShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Desktop mode -->
<controls:SettingsCard x:Uid="SettingsPageNextSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.NextSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPagePreviousSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PreviousSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DesktopModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAOT" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE718;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.DesktopModeSettings.IsAlwaysOnTop, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DesktopModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAutoLock" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE755;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.DesktopModeSettings.AutoLockOnDesktopMode, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLockHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DesktopModeSettings.LockShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</controls:Case>
<controls:Case Value="Dock">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- Dock mode -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DockModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.DockModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockMonitor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7F4;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.AppSettings.DockModeSettings.DockMonitorDeviceName, Mode=TwoWay}" />
<Button
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE72C;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockWindowHeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED5E;}">
<local:ExtendedSlider
Default="64"
Frequency="1"
Maximum="200"
Minimum="64"
Unit="px"
Value="{x:Bind ViewModel.AppSettings.DockModeSettings.DockWindowHeight, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DockModeSettings.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
</ComboBox>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</controls:Case>
<controls:Case Value="PictureInPicture">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- Picture in picture mode -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageDisplayTypeSwitcher" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF246;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.PictureInPictureModeSettings.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageToggleHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.PictureInPictureModeSettings.ToggleShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</controls:Case>
</controls:SwitchPresenter>
<controls:Segmented
x:Name="SegmentedControl"
Margin="36,36,36,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
SelectedIndex="0">
<controls:SegmentedItem x:Uid="AppSettingsControlGeneral" Tag="General" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlStandard" Tag="Standard" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlDock" Tag="Dock" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlDesktop" Tag="Desktop" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlPictureInPicture" Tag="PictureInPicture" />
</controls:Segmented>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -1,5 +1,8 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -46,5 +49,33 @@ namespace BetterLyrics.WinUI3.Controls
{
AutoStartupToggleSwitch.Toggled -= AutoStartupToggleSwitch_Toggled;
}
private void DeleteWindowBoundsRecordButton_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button)
{
var data = button.DataContext as WindowBoundsRecord;
if (data != null)
{
ViewModel.AppSettings.WindowBoundsRecords.Remove(data);
}
}
}
private void ApplyWindowBoundsRecordButton_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button)
{
var data = button.DataContext as WindowBoundsRecord;
if (data != null)
{
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (lyricsWindow != null)
{
lyricsWindow.AppWindow.MoveAndResize(data.WindowBounds.ToRectInt32());
}
}
}
}
}
}

View File

@@ -3,6 +3,7 @@
x:Class="BetterLyrics.WinUI3.Controls.LyricsSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
@@ -12,106 +13,61 @@
<Grid Padding="16" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,0,0,6">
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SubtitleTextBlockStyle}" />
</Grid>
<Grid Grid.Row="1" ColumnSpacing="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="LyricsSearchControlTitle"
Grid.Column="0"
VerticalAlignment="Center"
Text="Title" />
<TextBox
Grid.Column="1"
IsReadOnly="True"
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}"
TextWrapping="Wrap" />
<TextBlock
x:Uid="LyricsSearchControlMappedAs"
Grid.Column="2"
VerticalAlignment="Center" />
<TextBox
Grid.Column="3"
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}"
TextWrapping="Wrap" />
</Grid>
<Grid Grid.Row="2" ColumnSpacing="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="LyricsSearchControlArtist"
Grid.Column="0"
VerticalAlignment="Center" />
<TextBox
Grid.Column="1"
IsReadOnly="True"
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}"
TextWrapping="Wrap" />
<TextBlock
x:Uid="LyricsSearchControlMappedAs"
Grid.Column="2"
VerticalAlignment="Center" />
<TextBox
Grid.Column="3"
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}"
TextWrapping="Wrap" />
</Grid>
<!-- 搜索及其他按钮 -->
<Grid Grid.Row="3">
<RelativePanel>
<CheckBox
x:Uid="LyricsSearchControlMarkAsPureMusic"
IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True" />
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True"
Style="{StaticResource AccentButtonStyle}" />
</RelativePanel>
</Grid>
<Grid Grid.Row="4">
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<TextBlock x:Uid="LyricsSearchControlTargetSearchProvider" Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsExpander x:Uid="LyricsSearchControlSongInfoMapping" IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="LyricsSearchControlTitle" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}" TextWrapping="Wrap" />
<Button
VerticalAlignment="Center"
Command="{x:Bind ViewModel.ResetMappedTitleCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsSearchControlArtist" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}" TextWrapping="Wrap" />
<Button
VerticalAlignment="Center"
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard>
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
<Grid Grid.Column="1">
<TextBlock
x:Uid="LyricsSearchControlLyricsPreview"
Margin="6,0,0,0"
Style="{StaticResource SubtitleTextBlockStyle}" />
</Grid>
</Grid>
<!-- 结果列表及原始歌词文件展示区域 -->
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowSpacing="12">
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
@@ -188,17 +144,17 @@
ShowPaused="False"
Visibility="{x:Bind ViewModel.IsSearching, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Grid>
<Grid Grid.Column="1">
<ListView ItemsSource="{x:Bind ViewModel.LyricsData.LyricsLines, Mode=OneWay}">
<Grid Grid.Column="2">
<ListView ItemsSource="{x:Bind ViewModel.LyricsData.LyricsLines, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsLine, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=OneWay}"
Binding="{x:Bind ViewModel.LyricsData, Mode=OneWay}"
ComparisonCondition="Equal"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=OneWay}"
Binding="{x:Bind ViewModel.LyricsData, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
@@ -226,13 +182,13 @@
<Image MaxWidth="100" Source="/Assets/Page.png" />
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=OneWay}"
Binding="{x:Bind ViewModel.LyricsData, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=OneWay}"
Binding="{x:Bind ViewModel.LyricsData, Mode=OneWay}"
ComparisonCondition="Equal"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
@@ -241,13 +197,13 @@
</StackPanel>
</Grid>
</Grid>
<Grid Grid.Row="6">
<Grid Grid.Row="1">
<RelativePanel>
<TextBlock
x:Uid="LyricsSearchControlHelp"
Margin="0,0,24,0"
FontSize="12"
Foreground="{StaticResource TextFillColorSecondaryBrush}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
RelativePanel.AlignVerticalCenterWithPanel="True"
RelativePanel.LeftOf="Reset"
TextWrapping="Wrap" />

View File

@@ -175,14 +175,25 @@
</interactivity:Interaction.Behaviors>
</ColorPicker>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.LyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPageLyricsFontSize"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8E9;}"
IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageDynamicLyricsFontSize">
<ToggleSwitch IsOn="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.LyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF579;}">
<local:ExtendedSlider

View File

@@ -231,42 +231,60 @@
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.TranslationSettings.SelectedTargetLanguageIndex, Mode=TwoWay}">
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind ViewModel.SelectedTargetLanguageIndex, Mode=TwoWay}">
<ComboBoxItem Content="العربية" Tag="ar" />
<ComboBoxItem Content="Azərbaycan dili" Tag="az" />
<ComboBoxItem Content="简体中文" Tag="zh-Hans" />
<ComboBoxItem Content="繁體中文" Tag="zh-Hant" />
<ComboBoxItem Content="Български" Tag="bg" />
<ComboBoxItem Content="বাংলা" Tag="bn" />
<ComboBoxItem Content="Català" Tag="ca" />
<ComboBoxItem Content="Čeština" Tag="cs" />
<ComboBoxItem Content="Dansk" Tag="da" />
<ComboBoxItem Content="Nederlands" Tag="nl" />
<ComboBoxItem Content="English" Tag="en" />
<ComboBoxItem Content="Esperanto" Tag="eo" />
<ComboBoxItem Content="Suomi" Tag="fi" />
<ComboBoxItem Content="Français" Tag="fr" />
<ComboBoxItem Content="Deutsch" Tag="de" />
<ComboBoxItem Content="Ελληνικά" Tag="el" />
<ComboBoxItem Content="English" Tag="en" />
<ComboBoxItem Content="Esperanto" Tag="eo" />
<ComboBoxItem Content="Español" Tag="es" />
<ComboBoxItem Content="Eesti" Tag="et" />
<ComboBoxItem Content="Euskara" Tag="eu" />
<ComboBoxItem Content="فارسی" Tag="fa" />
<ComboBoxItem Content="Suomi" Tag="fi" />
<ComboBoxItem Content="Français" Tag="fr" />
<ComboBoxItem Content="Gaeilge" Tag="ga" />
<ComboBoxItem Content="Galego" Tag="gl" />
<ComboBoxItem Content="עברית" Tag="he" />
<ComboBoxItem Content="हिन्दी" Tag="hi" />
<ComboBoxItem Content="Magyar" Tag="hu" />
<ComboBoxItem Content="Bahasa Indonesia" Tag="id" />
<ComboBoxItem Content="Gaeilge" Tag="ga" />
<ComboBoxItem Content="Italiano" Tag="it" />
<ComboBoxItem Content="日本語" Tag="ja" />
<ComboBoxItem Content="한국어" Tag="ko" />
<ComboBoxItem Content="فارسی" Tag="fa" />
<ComboBoxItem Content="Кыргызча" Tag="ky" />
<ComboBoxItem Content="Lietuvių" Tag="lt" />
<ComboBoxItem Content="Latviešu" Tag="lv" />
<ComboBoxItem Content="Bahasa Melayu" Tag="ms" />
<ComboBoxItem Content="Norsk bokmål" Tag="nb" />
<ComboBoxItem Content="Nederlands" Tag="nl" />
<ComboBoxItem Content="Português (Brasil)" Tag="pt-BR" />
<ComboBoxItem Content="Polski" Tag="pl" />
<ComboBoxItem Content="Português" Tag="pt" />
<ComboBoxItem Content="Română" Tag="ro" />
<ComboBoxItem Content="Русский" Tag="ru" />
<ComboBoxItem Content="Slovenčina" Tag="sk" />
<ComboBoxItem Content="Español" Tag="es" />
<ComboBoxItem Content="Slovenščina" Tag="sl" />
<ComboBoxItem Content="Shqip" Tag="sq" />
<ComboBoxItem Content="Српски" Tag="sr" />
<ComboBoxItem Content="Svenska" Tag="sv" />
<ComboBoxItem Content="ไทย" Tag="th" />
<ComboBoxItem Content="Filipino" Tag="tl" />
<ComboBoxItem Content="Türkçe" Tag="tr" />
<ComboBoxItem Content="Українська" Tag="uk" />
<ComboBoxItem Content="اردو" Tag="ur" />
<ComboBoxItem Content="Tiếng Việt" Tag="vi" />
<ComboBoxItem Content="中文" Tag="zh" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslationConfig">
<controls:SettingsCard x:Uid="SettingsPageTranslationConfig" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<controls:SettingsCard.Description>
<HyperlinkButton Margin="0,6,0,0" NavigateUri="https://github.com/LibreTranslate/LibreTranslate">
<TextBlock
@@ -295,6 +313,29 @@
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- Lyrics phonetic -->
<controls:SettingsExpander
x:Uid="SettingsPageChinese"
IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}"
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.TranslationSettings.ChineseRomanization, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}">
<ComboBoxItem x:Uid="SettingsPagePinyin" />
<ComboBoxItem x:Uid="SettingsPageJyutping" />
</ComboBox>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageJapanese" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- 中文简体繁体偏好 -->
<controls:SettingsCard x:Uid="SettingsPageChinesePreference">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTraditionalChineseEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Provider info -->
<controls:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
</controls:SettingsCard>
@@ -315,6 +356,26 @@
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
<!-- Apple Music token -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="media-user-token (for Apple Muisc)" />
<controls:SettingsCard
Background="{ThemeResource SystemFillColorCautionBackgroundBrush}"
Foreground="{ThemeResource SystemFillColorCautionBrush}"
Header="Use at your own risk">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox
MaxWidth="250"
PlaceholderText="media-user-token"
Text="{x:Bind ViewModel.AppleMusicMediaUserToken, Mode=TwoWay}"
TextWrapping="Wrap" />
<Button
Command="{x:Bind ViewModel.SaveAppleMusicMediaUserTokenCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE8FB;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Converter
LyricsSearchProvider.Netease => "网易云音乐",
LyricsSearchProvider.Kugou => "酷狗音乐",
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
LyricsSearchProvider.AppleMusic => "Apple Music",
LyricsSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
LyricsSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
LyricsSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),

View File

@@ -2,10 +2,6 @@
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
@@ -19,6 +15,7 @@ namespace BetterLyrics.WinUI3.Converter
{
PlayerID.Spotify => PathHelper.SpotifyLogoPath,
PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath,
PlayerID.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
PlayerID.iTunes => PathHelper.iTunesLogoPath,
PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath,
PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,

View File

@@ -0,0 +1,26 @@
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
public class RectToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Windows.Foundation.Rect rect)
{
return new Microsoft.UI.Xaml.Thickness(rect.X, rect.Y, 0, 0);
}
return new Microsoft.UI.Xaml.Thickness(0);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Converter
TranslationSearchProvider.Netease => "网易云音乐",
TranslationSearchProvider.Kugou => "酷狗音乐",
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
TranslationSearchProvider.AppleMusic => "Apple Music",
TranslationSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
TranslationSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
TranslationSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Enums
{
public enum ChineseRomanization
{
Pinyin,
Jyutping,
}
}

View File

@@ -15,6 +15,7 @@ namespace BetterLyrics.WinUI3.Enums
LocalLrcFile,
LocalEslrcFile,
LocalTtmlFile,
AppleMusic,
}
public static class LyricsSearchProviderExtensions
@@ -28,6 +29,7 @@ namespace BetterLyrics.WinUI3.Enums
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
_ => throw new System.ArgumentOutOfRangeException(nameof(provider)),
};
}
@@ -41,6 +43,7 @@ namespace BetterLyrics.WinUI3.Enums
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,
@@ -71,6 +74,7 @@ namespace BetterLyrics.WinUI3.Enums
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,

View File

@@ -8,7 +8,8 @@ namespace BetterLyrics.WinUI3.Enums
{
public enum ShortcutID
{
DesktopLock,
LyricsWindowShowOrHide,
DesktopLockOrUnlock,
DesktopToggle,
DockToggle,
PictureInPictureToggle,

View File

@@ -13,6 +13,7 @@ namespace BetterLyrics.WinUI3.Enums
Netease,
LrcLib,
AmllTtmlDb,
AppleMusic,
LocalMusicFile,
LocalLrcFile,
LocalEslrcFile,

View File

@@ -0,0 +1,126 @@
using BetterLyrics.WinUI3.Constants;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public class AppleMusic
{
private readonly HttpClient _client;
private string _accessToken = "";
private string _storefront = "";
private string _language = "";
public AppleMusic()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
_client.DefaultRequestHeaders.Add("Accept", "application/json");
_client.DefaultRequestHeaders.Add("Origin", "https://music.apple.com");
_client.DefaultRequestHeaders.Add("Referer", "https://music.apple.com/");
}
public async Task<bool> InitAsync()
{
await GetAccessTokenAsync();
await SetMediaUserTokenAsync();
return
!string.IsNullOrEmpty(_accessToken) &&
!string.IsNullOrEmpty(PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
}
private async Task GetAccessTokenAsync()
{
var resp = await _client.GetStringAsync("https://music.apple.com/us/browse");
var jsMatch = Regex.Match(resp, "(?<=index)(.*?)(?=\\.js\")");
if (!jsMatch.Success) throw new Exception("Failed to find index.js");
var jsUrl = $"https://music.apple.com/assets/index{jsMatch.Value}.js";
var jsResp = await _client.GetStringAsync(jsUrl);
var tokenMatch = Regex.Match(jsResp, "(?=eyJh)(.*?)(?=\")");
if (!tokenMatch.Success) throw new Exception("Failed to find access token");
_accessToken = tokenMatch.Value;
_client.DefaultRequestHeaders.Remove("Authorization");
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_accessToken}");
}
private async Task SetMediaUserTokenAsync()
{
_client.DefaultRequestHeaders.Remove("media-user-token");
_client.DefaultRequestHeaders.Add("media-user-token",
PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
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();
_language = json.GetProperty("data")[0].GetProperty("attributes").GetProperty("defaultLanguageTag").ToString();
_client.DefaultRequestHeaders.Remove("Accept-Language");
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
}
public async Task<string?> GetLyricsAsync(string title, string artist)
{
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);
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
var data = json.GetProperty("data");
if (data.GetArrayLength() == 0) return string.Empty;
var song = data[0];
if (!song.TryGetProperty("relationships", out var relationships))
return string.Empty;
if (relationships.TryGetProperty("syllable-lyrics", out var syllableLyrics) &&
syllableLyrics.GetProperty("data").GetArrayLength() > 0)
{
var syllableLyric = syllableLyrics.GetProperty("data")[0];
if (syllableLyric.TryGetProperty("attributes", out var attributes) &&
attributes.TryGetProperty("ttml", out var ttml))
{
string? raw = ttml.GetString();
if (raw != null && raw.Contains("begin=") && raw.Contains("end="))
{
return raw;
}
}
}
//if (relationships.TryGetProperty("lyrics", out var lyrics) &&
// lyrics.GetProperty("data").GetArrayLength() > 0)
//{
// var lyric = lyrics.GetProperty("data")[0];
// if (lyric.TryGetProperty("attributes", out var attributes) &&
// attributes.TryGetProperty("ttml", out var ttml))
// {
// return ttml.GetString();
// }
//}
return null;
}
private async Task<string> SearchSongInfoAsync(string artist, string title)
{
var query = $"{artist} {title}";
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/search";
var url = apiUrl + $"?term={WebUtility.UrlEncode(query)}&types=songs&limit=1&l={_language}";
var resp = await _client.GetStringAsync(url);
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
var results = json.GetProperty("results");
if (results.TryGetProperty("songs", out var songs) && songs.GetProperty("data").GetArrayLength() > 0)
{
var song = songs.GetProperty("data")[0];
return song.GetProperty("id").ToString();
}
return string.Empty;
}
}
}

View File

@@ -74,9 +74,17 @@ namespace BetterLyrics.WinUI3.Helper
using var ds = list.CreateDrawingSession();
if (strokeWidth > 0)
{
if (lyricsLine.TextGeometry == null)
{
return list;
}
ds.DrawGeometry(lyricsLine.TextGeometry, lyricsLine.Position, strokeColor, strokeWidth); // 描边
}
ds.FillGeometry(lyricsLine.TextGeometry, lyricsLine.Position, fontColor); // 填充
if (lyricsLine.CanvasTextLayout == null)
{
return list;
}
ds.DrawTextLayout(lyricsLine.CanvasTextLayout, lyricsLine.Position, fontColor); // 绘制文本(填充)
return list;
}

View File

@@ -94,6 +94,11 @@ namespace BetterLyrics.WinUI3.Helper
return Color.FromArgb(alpha, color.R, color.G, color.B);
}
public static Color WithOpacity(this Color color, float opacity)
{
return Color.FromArgb((byte)(opacity * 255), color.R, color.G, color.B);
}
public static Color WithBrightness(this Color color, double brightness)
{
// 确保亮度因子在合理范围内
@@ -116,15 +121,11 @@ namespace BetterLyrics.WinUI3.Helper
{
case WindowPixelSampleMode.BelowWindow:
{
int sampleHeight = 1;
int sampleY = myRect.Bottom + 1;
return GetAverageColorFromScreenRegion(myRect.Left, sampleY, screenWidth, sampleHeight);
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Bottom + 2, screenWidth, 1);
}
case WindowPixelSampleMode.AboveWindow:
{
int sampleHeight = 1;
int sampleY = myRect.Top - 1;
return GetAverageColorFromScreenRegion(myRect.Left, sampleY, screenWidth, sampleHeight);
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top - 3, screenWidth, 1);
}
case WindowPixelSampleMode.WindowArea:
{

View File

@@ -11,6 +11,6 @@ namespace BetterLyrics.WinUI3.Helper
{
public static class FontHelper
{
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies();
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
}
}

View File

@@ -14,6 +14,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
@@ -219,5 +220,68 @@ namespace BetterLyrics.WinUI3.Helper
}
return pixelData;
}
public static async Task<byte[]> DownloadImageAsByteArrayAsync(string url)
{
using var httpClient = new HttpClient();
return await httpClient.GetByteArrayAsync(url);
}
public static byte[]? DataUrlToByteArray(string dataUrl)
{
const string base64Marker = ";base64,";
int base64Index = dataUrl.IndexOf(base64Marker, StringComparison.OrdinalIgnoreCase);
if (base64Index >= 0)
{
string base64Data = dataUrl.Substring(base64Index + base64Marker.Length);
return Convert.FromBase64String(base64Data);
}
else
{
// 非 base64直接取逗号后内容并解码
int commaIndex = dataUrl.IndexOf(',');
if (commaIndex >= 0)
{
string rawData = dataUrl.Substring(commaIndex + 1);
return System.Text.Encoding.UTF8.GetBytes(Uri.UnescapeDataString(rawData));
}
else
{
return null;
}
}
}
public static async Task<byte[]?> GetImageBytesFromUrlAsync(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return null;
}
try
{
if (url.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
// data URL直接解析
return DataUrlToByteArray(url);
}
else if (Uri.TryCreate(url, UriKind.Absolute, out var uri) &&
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
{
// 普通网络图片,下载
return await DownloadImageAsByteArrayAsync(url);
}
else
{
// 其他类型暂不支持
return null;
}
}
catch (Exception)
{
return null;
}
}
}
}

View File

@@ -1,14 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers.General;
using NTextCat;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using TinyPinyin;
using Windows.Globalization;
namespace BetterLyrics.WinUI3.Services
@@ -18,49 +11,83 @@ namespace BetterLyrics.WinUI3.Services
private static readonly RankedLanguageIdentifierFactory _factory = new();
private static readonly RankedLanguageIdentifier _identifier;
public static List<Models.LanguageInfo> SupportedTargetLanguages =>
public static List<Models.LanguageInfo> SupportedTargetLanguages { get; set; } =
[
new Models.LanguageInfo("ar", "العربية"),
new Models.LanguageInfo("az", "Azərbaycan dili"),
new Models.LanguageInfo("zh-Hans", "简体中文"),
new Models.LanguageInfo("zh-Hant", "繁體中文"),
new Models.LanguageInfo("bg", "Български"),
new Models.LanguageInfo("bn", "বাংলা"),
new Models.LanguageInfo("ca", "Català"),
new Models.LanguageInfo("cs", "Čeština"),
new Models.LanguageInfo("da", "Dansk"),
new Models.LanguageInfo("nl", "Nederlands"),
new Models.LanguageInfo("de", "Deutsch"),
new Models.LanguageInfo("el", "Ελληνικά"),
new Models.LanguageInfo("en", "English"),
new Models.LanguageInfo("eo", "Esperanto"),
new Models.LanguageInfo("es", "Español"),
new Models.LanguageInfo("et", "Eesti"),
new Models.LanguageInfo("eu", "Euskara"),
new Models.LanguageInfo("fa", "فارسی"),
new Models.LanguageInfo("fi", "Suomi"),
new Models.LanguageInfo("fr", "Français"),
new Models.LanguageInfo("de", "Deutsch"),
new Models.LanguageInfo("el", "Ελληνικά"),
new Models.LanguageInfo("ga", "Gaeilge"),
new Models.LanguageInfo("gl", "Galego"),
new Models.LanguageInfo("he", "עברית"),
new Models.LanguageInfo("hi", "हिन्दी"),
new Models.LanguageInfo("hu", "Magyar"),
new Models.LanguageInfo("id", "Bahasa Indonesia"),
new Models.LanguageInfo("ga", "Gaeilge"),
new Models.LanguageInfo("it", "Italiano"),
new Models.LanguageInfo("ja", "日本語"),
new Models.LanguageInfo("ko", "한국어"),
new Models.LanguageInfo("fa", "فارسی"),
new Models.LanguageInfo("ky", "Кыргызча"),
new Models.LanguageInfo("lt", "Lietuvių"),
new Models.LanguageInfo("lv", "Latviešu"),
new Models.LanguageInfo("ms", "Bahasa Melayu"),
new Models.LanguageInfo("nb", "Norsk bokmål"),
new Models.LanguageInfo("nl", "Nederlands"),
new Models.LanguageInfo("pt-BR", "Português (Brasil)"),
new Models.LanguageInfo("pl", "Polski"),
new Models.LanguageInfo("pt", "Português"),
new Models.LanguageInfo("ro", "Română"),
new Models.LanguageInfo("ru", "Русский"),
new Models.LanguageInfo("sk", "Slovenčina"),
new Models.LanguageInfo("es", "Español"),
new Models.LanguageInfo("sl", "Slovenščina"),
new Models.LanguageInfo("sq", "Shqip"),
new Models.LanguageInfo("sr", "Српски"),
new Models.LanguageInfo("sv", "Svenska"),
new Models.LanguageInfo("th", "ไทย"),
new Models.LanguageInfo("tl", "Filipino"),
new Models.LanguageInfo("tr", "Türkçe"),
new Models.LanguageInfo("uk", "Українська"),
new Models.LanguageInfo("ur", "اردو"),
new Models.LanguageInfo("vi", "Tiếng Việt"),
new Models.LanguageInfo("zh", "中文"),
];
static LanguageHelper()
{
_identifier = _factory.Load(PathHelper.LanguageProfilePath);
}
private static string SimplifiedChineseOrTraditionalChinese(string text)
{
return text == ChineseConverter.ConvertToSimplifiedChinese(text) ? "zh-Hans" : "zh-Hant";
RomajiConverter.Core.Helpers.RomajiHelper.Init();
}
public static string? DetectLanguageCode(string? text)
@@ -72,9 +99,8 @@ namespace BetterLyrics.WinUI3.Services
code = code switch
{
"simple" => "en",
"zh_classical" => SimplifiedChineseOrTraditionalChinese(text),
"zh_yue" => SimplifiedChineseOrTraditionalChinese(text),
"zh" => SimplifiedChineseOrTraditionalChinese(text),
"zh_classical" => "zh",
"zh_yue" => "zh",
_ => code
};
return code;
@@ -89,26 +115,17 @@ namespace BetterLyrics.WinUI3.Services
};
}
public static string ConvertToCountryCode(string? languageCode)
public static string GetDefaultTargetLanguageCode()
{
if (languageCode == null) return "us";
return languageCode switch
var found = SupportedTargetLanguages.Find(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);
if (found == null)
{
"zh" => "cn",
"zh-Hans" => "cn",
"zh-Hant" => "tw",
"ja" => "jp",
"ko" => "kr",
_ => "us"
};
}
public static int GetDefaultTargetLanguageIndex()
{
int found = SupportedTargetLanguages.FindIndex(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);
if (found == -1) found = 7; // 默认使用英语
return found;
return "en";
}
else
{
return found.Code;
}
}
public static string GetOrderChar(string text)
@@ -118,12 +135,17 @@ namespace BetterLyrics.WinUI3.Services
if (char.IsLetter(c) && c < 128)
return char.ToUpper(c).ToString();
if (PinyinHelper.IsChinese(c))
if (Pinyin.Pinyin.Instance.IsHanzi(c.ToString()))
{
return PinyinHelper.GetPinyinInitials($"{c}");
return Pinyin.Pinyin.Instance.HanziToPinyin(c.ToString(), Pinyin.ManTone.Style.NORMAL).ToStr().ToUpper().FirstOrDefault().ToString();
}
return "#";
}
public static string ToRomaji(string text)
{
return string.Join(" ", RomajiConverter.Core.Helpers.RomajiHelper.SentenceToRomaji(text).Select(x => x.Romaji));
}
}
}

View File

@@ -3,14 +3,12 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services;
using Lyricify.Lyrics.Helpers.General;
using Lyricify.Lyrics.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Windows.Globalization.Fonts;
using LyricsData = BetterLyrics.WinUI3.Models.LyricsData;
namespace BetterLyrics.WinUI3.Helper
@@ -48,50 +46,67 @@ namespace BetterLyrics.WinUI3.Helper
break;
}
}
FillChineseLyricsData();
FillRomanizationLyricsData();
_lyricsDataArr.Add(new LyricsData()); // 为机翻预留
return _lyricsDataArr;
}
private void FillChineseLyricsData()
private void FillRomanizationLyricsData()
{
var simplifiedChinese = _lyricsDataArr.Where(x => x.LanguageCode == "zh-Hans").FirstOrDefault();
var traditionalChinese = _lyricsDataArr.Where(x => x.LanguageCode == "zh-Hant").FirstOrDefault();
if (simplifiedChinese != null && traditionalChinese == null)
var chinese = _lyricsDataArr.Where(x => x.LanguageCode == "zh").FirstOrDefault();
if (chinese != null)
{
// 如果没有繁体中文歌词,则将简体中文歌词转换为繁体中文
_lyricsDataArr.Add(new LyricsData
{
LyricsLines = simplifiedChinese.LyricsLines.Select(line => new LyricsLine
LanguageCode = "pinyin",
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
{
StartMs = line.StartMs,
EndMs = line.EndMs,
OriginalText = ChineseConverter.ConvertToTraditionalChinese(line.OriginalText),
OriginalText = Pinyin.Pinyin.Instance.HanziToPinyin(line.OriginalText).ToStr(),
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
{
StartMs = c.StartMs,
EndMs = c.EndMs,
Text = ChineseConverter.ConvertToTraditionalChinese(c.Text),
Text = Pinyin.Pinyin.Instance.HanziToPinyin(c.Text).ToStr(),
StartIndex = c.StartIndex
}).ToList()
}).ToList()
});
_lyricsDataArr.Add(new LyricsData
{
LanguageCode = "jyutping",
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
{
StartMs = line.StartMs,
EndMs = line.EndMs,
OriginalText = Pinyin.Jyutping.Instance.HanziToPinyin(line.OriginalText).ToStr(),
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
{
StartMs = c.StartMs,
EndMs = c.EndMs,
Text = Pinyin.Jyutping.Instance.HanziToPinyin(c.Text).ToStr(),
StartIndex = c.StartIndex
}).ToList()
}).ToList()
});
}
else if (traditionalChinese != null && simplifiedChinese == null)
var japanese = _lyricsDataArr.Where(x => x.LanguageCode == "ja").FirstOrDefault();
if (japanese != null)
{
// 如果没有简体中文歌词,则将繁体中文歌词转换为简体中文
_lyricsDataArr.Add(new LyricsData
{
LyricsLines = traditionalChinese.LyricsLines.Select(line => new LyricsLine
LanguageCode = "romaji",
LyricsLines = japanese.LyricsLines.Select(line => new LyricsLine
{
StartMs = line.StartMs,
EndMs = line.EndMs,
OriginalText = ChineseConverter.ConvertToSimplifiedChinese(line.OriginalText),
OriginalText = LanguageHelper.ToRomaji(line.OriginalText),
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
{
StartMs = c.StartMs,
EndMs = c.EndMs,
Text = ChineseConverter.ConvertToSimplifiedChinese(c.Text),
Text = LanguageHelper.ToRomaji(c.Text),
StartIndex = c.StartIndex
}).ToList()
}).ToList()
@@ -230,10 +245,11 @@ namespace BetterLyrics.WinUI3.Helper
int pStartMs = ParseTtmlTime(pBegin);
int pEndMs = ParseTtmlTime(pEnd);
// 只获取一级span且排除ttm:role="x-bg"span
// 只获取一级span且排除 ttm:role="x-bg"span 和 ttm:role="x-roman"
var spans = p.Elements()
.Where(s => s.Name.LocalName == "span" &&
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-bg")
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-bg" &&
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-roman")
.ToList();
// 原文和翻译分离

View File

@@ -1,4 +1,5 @@
using System;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
@@ -6,6 +7,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Vanara.PInvoke;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.Helper
{
@@ -60,5 +62,14 @@ namespace BetterLyrics.WinUI3.Helper
var primaryMonitorInfo = GetPrimaryMonitorInfoEx();
return primaryMonitorInfo.szDevice;
}
public static User32.MONITORINFOEX GetMonitorInfoExFromWindow(Window window)
{
var hwnd = WindowNative.GetWindowHandle(window);
var hMonitor = User32.MonitorFromWindow(hwnd, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
User32.MONITORINFOEX monitorInfoEx = new() { cbSize = (uint)Marshal.SizeOf<User32.MONITORINFOEX>() };
User32.GetMonitorInfo(hMonitor, ref monitorInfoEx);
return monitorInfoEx;
}
}
}

View File

@@ -46,6 +46,7 @@ namespace BetterLyrics.WinUI3.Helper
public static string QQLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "qq");
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 AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
@@ -53,6 +54,7 @@ namespace BetterLyrics.WinUI3.Helper
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");
@@ -69,9 +71,11 @@ namespace BetterLyrics.WinUI3.Helper
Directory.CreateDirectory(KugouLyricsCacheDirectory);
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
Directory.CreateDirectory(AppleMusicCacheDirectory);
Directory.CreateDirectory(QQTranslationCacheDirectory);
Directory.CreateDirectory(NeteaseTranslationCacheDirectory);
Directory.CreateDirectory(KugouTranslationCacheDirectory);
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
}

View File

@@ -59,6 +59,10 @@ namespace BetterLyrics.WinUI3.Helper
{
window = new MusicGalleryWindow();
}
else if (typeof(T) == typeof(LyricsSearchWindow))
{
window = new LyricsSearchWindow();
}
else
{
throw new ArgumentException("Unsupported window type", nameof(T));
@@ -73,6 +77,7 @@ namespace BetterLyrics.WinUI3.Helper
var lyricsWindow = (LyricsWindow)window;
lyricsWindow.ViewModel.InitShortcuts();
lyricsWindow.AutoSelectLyricsMode();
lyricsWindow.ViewModel.SetIsAlwaysOnTop();
}
}
else

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
@@ -6,34 +7,70 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Vanara.PInvoke;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Models
{
public partial class LiveStates : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsWindowMode CurrentLyricsWindowMode { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsDisplayType CurrentLyricsDisplayType { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsStyleSettings CurrentLyricsStyleSettings { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectSettings CurrentLyricsEffectSettings { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsWindowMode LyricsWindowMode { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsDisplayType LyricsDisplayType { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAlwaysOnTop { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsStyleSettings LyricsStyleSettings { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectSettings LyricsEffectSettings { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect LyricsWindowBounds { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect LyricsWindowMonitorBounds { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect DemoLyricsWindowBounds { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect DemoLyricsWindowMonitorBounds { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWindowMonitorName { get; set; }
public LiveStates(AppSettings appSettings)
{
CurrentLyricsWindowMode = LyricsWindowMode.StandardMode;
CurrentLyricsDisplayType = appSettings.StandardModeSettings.LyricsDisplayType;
CurrentLyricsStyleSettings = appSettings.StandardLyricsStyleSettings;
CurrentLyricsEffectSettings = appSettings.StandardLyricsEffectSettings;
LyricsWindowMode = LyricsWindowMode.StandardMode;
LyricsDisplayType = appSettings.StandardModeSettings.LyricsDisplayType;
LyricsStyleSettings = appSettings.StandardLyricsStyleSettings;
LyricsEffectSettings = appSettings.StandardLyricsEffectSettings;
IsAlwaysOnTop = false;
}
public void ToggleLyricsWindowMode(LyricsWindowMode mode)
{
if (CurrentLyricsWindowMode == mode)
if (LyricsWindowMode == mode)
{
CurrentLyricsWindowMode = LyricsWindowMode.StandardMode;
LyricsWindowMode = LyricsWindowMode.StandardMode;
}
else
{
CurrentLyricsWindowMode = mode;
LyricsWindowMode = mode;
}
}
partial void OnLyricsWindowBoundsChanged(Rect value)
{
double factor = 0.1;
var lyricsWindow = WindowHelper.GetWindowByWindowType<Views.LyricsWindow>();
if (lyricsWindow == null) return;
var mointor = MonitorHelper.GetMonitorInfoExFromWindow(lyricsWindow);
LyricsWindowMonitorName = mointor.szDevice;
LyricsWindowMonitorBounds = new Rect(
mointor.rcMonitor.Left,
mointor.rcMonitor.Top,
mointor.rcMonitor.Width,
mointor.rcMonitor.Height
);
DemoLyricsWindowBounds = new Rect(
(value.X - mointor.rcMonitor.Left) * factor,
(value.Y - mointor.rcMonitor.Top) * factor,
value.Width * factor,
value.Height * factor
);
DemoLyricsWindowMonitorBounds = new Rect(
mointor.rcMonitor.Left * factor,
mointor.rcMonitor.Top * factor,
mointor.rcMonitor.Width * factor,
mointor.rcMonitor.Height * factor
);
}
}
}

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services;
using Lyricify.Lyrics.Helpers.General;
using System;
@@ -13,7 +14,12 @@ namespace BetterLyrics.WinUI3.Models
public class LyricsData
{
public List<LyricsLine> LyricsLines { get; set; }
public string? LanguageCode => LanguageHelper.DetectLanguageCode(WrappedOriginalText);
private string? _languageCode;
public string? LanguageCode
{
get => _languageCode ?? LanguageHelper.DetectLanguageCode(WrappedOriginalText);
set => _languageCode = value;
}
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
public LyricsData()
@@ -120,5 +126,18 @@ namespace BetterLyrics.WinUI3.Models
},
]);
}
public LyricsLine? GetLyricsLine(double sec)
{
for (int i = 0; i < LyricsLines.Count; i++)
{
var line = LyricsLines[i];
if (line.StartMs > sec * 1000)
{
return LyricsLines.ElementAtOrDefault(i - 1);
}
}
return null;
}
}
}

View File

@@ -13,6 +13,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType SongInfoAlignmentType { get; set; } = TextAlignmentType.Left;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageRadius { get; set; } = 12; // 12 % of the cover image size
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageShadowAmount { get; set; } = 12;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SongInfoFontSize { get; set; } = 18;
public AlbumArtLayoutSettings() { }
}

View File

@@ -34,8 +34,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LocalMediaFolder> LocalMediaFolders { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MappedSongSearchQuery> MappedSongSearchQueries { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<WindowBoundsRecord> WindowBoundsRecords { get; set; } = [];
public AppSettings() { }
}

View File

@@ -15,6 +15,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoLockOnDesktopMode { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> LockShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "U" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ToggleShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "D" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAlwaysOnTop { get; set; } = true;
public DesktopModeSettings()
{

View File

@@ -15,6 +15,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IgnoreFullscreenWindow { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LXMusicServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool HideWindowWhenNotPlaying { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ShowOrHideLyricsWindowShortcut { get; set; } = new List<string> { "Ctrl", "Alt", "H" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsDragEverywhereEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsImmersiveMode { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ExitOnLyricsWindowClosed { get; set; } = true;

View File

@@ -13,6 +13,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class LyricsStyleSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsDynamicLyricsFontSize { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontSize { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsBgFontOpacity { get; set; } = 30; // 30% opacity

View File

@@ -14,6 +14,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect WindowBounds { get; set; } = new Rect(100, 100, 1000, 600);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMaximized { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAlwaysOnTop { get; set; } = false;
public StandardModeSettings()
{

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
@@ -14,7 +15,12 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LibreTranslateServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTranslationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowTranslationOnly { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SelectedTargetLanguageIndex { get; set; } = LanguageHelper.GetDefaultTargetLanguageIndex();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string SelectedTargetLanguageCode { get; set; } = LanguageHelper.GetDefaultTargetLanguageCode();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsJyutpingEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ChineseRomanization ChineseRomanization { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsChineseRomanizationEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsJapaneseRomanizationEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTraditionalChineseEnabled { get; set; } = false;
public TranslationSettings() { }
}

View File

@@ -21,11 +21,14 @@ namespace BetterLyrics.WinUI3.Models
public partial double? DurationMs { get; set; }
[ObservableProperty]
public partial string? SourceAppUserModelId { get; set; } = null;
public partial string? PlayerId { get; set; } = null;
[ObservableProperty]
public partial string Title { get; set; }
[ObservableProperty]
public partial string? SongId { get; set; } = null;
public SongInfo() { }
}
}

View File

@@ -0,0 +1,19 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Models
{
public partial class WindowBoundsRecord : ObservableObject
{
[ObservableProperty] public partial string MonitorDeviceName { get; set; } = string.Empty;
[ObservableProperty] public partial Rect WindowBounds { get; set; }
[ObservableProperty] public partial Rect DemoWindowBounds { get; set; }
[ObservableProperty] public partial Rect MonitorBounds { get; set; }
[ObservableProperty] public partial Rect DemoMonitorBounds { get; set; }
}
}

View File

@@ -15,7 +15,8 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
public class LiveStatesService : BaseViewModel, ILiveStatesService,
IRecipient<PropertyChangedMessage<LyricsWindowMode>>,
IRecipient<PropertyChangedMessage<LyricsDisplayType>>
IRecipient<PropertyChangedMessage<LyricsDisplayType>>,
IRecipient<PropertyChangedMessage<bool>>
{
private readonly ISettingsService _settingsService;
@@ -32,29 +33,33 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.CurrentLyricsWindowMode))
if (message.PropertyName == nameof(LiveStates.LyricsWindowMode))
{
switch (message.NewValue)
{
case LyricsWindowMode.StandardMode:
LiveStates.CurrentLyricsStyleSettings = _settingsService.AppSettings.StandardLyricsStyleSettings;
LiveStates.CurrentLyricsEffectSettings = _settingsService.AppSettings.StandardLyricsEffectSettings;
LiveStates.CurrentLyricsDisplayType = _settingsService.AppSettings.StandardModeSettings.LyricsDisplayType;
LiveStates.LyricsStyleSettings = _settingsService.AppSettings.StandardLyricsStyleSettings;
LiveStates.LyricsEffectSettings = _settingsService.AppSettings.StandardLyricsEffectSettings;
LiveStates.LyricsDisplayType = _settingsService.AppSettings.StandardModeSettings.LyricsDisplayType;
LiveStates.IsAlwaysOnTop = _settingsService.AppSettings.StandardModeSettings.IsAlwaysOnTop;
break;
case LyricsWindowMode.DockMode:
LiveStates.CurrentLyricsStyleSettings = _settingsService.AppSettings.DockLyricsStyleSettings;
LiveStates.CurrentLyricsEffectSettings = _settingsService.AppSettings.DockLyricsEffectSettings;
LiveStates.CurrentLyricsDisplayType = _settingsService.AppSettings.DockModeSettings.LyricsDisplayType;
LiveStates.LyricsStyleSettings = _settingsService.AppSettings.DockLyricsStyleSettings;
LiveStates.LyricsEffectSettings = _settingsService.AppSettings.DockLyricsEffectSettings;
LiveStates.LyricsDisplayType = _settingsService.AppSettings.DockModeSettings.LyricsDisplayType;
LiveStates.IsAlwaysOnTop = true;
break;
case LyricsWindowMode.DesktopMode:
LiveStates.CurrentLyricsStyleSettings = _settingsService.AppSettings.DesktopLyricsStyleSettings;
LiveStates.CurrentLyricsEffectSettings = _settingsService.AppSettings.DesktopLyricsEffectSettings;
LiveStates.CurrentLyricsDisplayType = _settingsService.AppSettings.DesktopModeSettings.LyricsDisplayType;
LiveStates.LyricsStyleSettings = _settingsService.AppSettings.DesktopLyricsStyleSettings;
LiveStates.LyricsEffectSettings = _settingsService.AppSettings.DesktopLyricsEffectSettings;
LiveStates.LyricsDisplayType = _settingsService.AppSettings.DesktopModeSettings.LyricsDisplayType;
LiveStates.IsAlwaysOnTop = _settingsService.AppSettings.DesktopModeSettings.IsAlwaysOnTop;
break;
case LyricsWindowMode.PictureInPictureMode:
LiveStates.CurrentLyricsStyleSettings = _settingsService.AppSettings.PictureInPictureLyricsStyleSettings;
LiveStates.CurrentLyricsEffectSettings = _settingsService.AppSettings.PictureInPictureLyricsEffectSettings;
LiveStates.CurrentLyricsDisplayType = _settingsService.AppSettings.PictureInPictureModeSettings.LyricsDisplayType;
LiveStates.LyricsStyleSettings = _settingsService.AppSettings.PictureInPictureLyricsStyleSettings;
LiveStates.LyricsEffectSettings = _settingsService.AppSettings.PictureInPictureLyricsEffectSettings;
LiveStates.LyricsDisplayType = _settingsService.AppSettings.PictureInPictureModeSettings.LyricsDisplayType;
// IsAlwaysOnTop 由系统托管
break;
default:
break;
@@ -69,9 +74,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
if (message.PropertyName == nameof(StandardModeSettings.LyricsDisplayType))
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.StandardMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.StandardMode)
{
LiveStates.CurrentLyricsDisplayType = message.NewValue;
LiveStates.LyricsDisplayType = message.NewValue;
}
}
}
@@ -79,9 +84,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
if (message.PropertyName == nameof(DockModeSettings.LyricsDisplayType))
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode)
{
LiveStates.CurrentLyricsDisplayType = message.NewValue;
LiveStates.LyricsDisplayType = message.NewValue;
}
}
}
@@ -89,9 +94,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
if (message.PropertyName == nameof(DesktopModeSettings.LyricsDisplayType))
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
LiveStates.CurrentLyricsDisplayType = message.NewValue;
LiveStates.LyricsDisplayType = message.NewValue;
}
}
}
@@ -99,9 +104,33 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
if (message.PropertyName == nameof(PictureInPictureModeSettings.LyricsDisplayType))
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.PictureInPictureMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.PictureInPictureMode)
{
LiveStates.CurrentLyricsDisplayType = message.NewValue;
LiveStates.LyricsDisplayType = message.NewValue;
}
}
}
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is StandardModeSettings)
{
if (message.PropertyName == nameof(StandardModeSettings.IsAlwaysOnTop))
{
if (LiveStates.LyricsWindowMode == LyricsWindowMode.StandardMode)
{
LiveStates.IsAlwaysOnTop = message.NewValue;
}
}
}
else if (message.Sender is DesktopModeSettings)
{
if (message.PropertyName == nameof(DesktopModeSettings.IsAlwaysOnTop))
{
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
LiveStates.IsAlwaysOnTop = message.NewValue;
}
}
}

View File

@@ -11,7 +11,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
public interface ILyricsSearchService
{
Task<LyricsSearchResult> SearchSmartlyAsync(string mediaSessionId, string title, string artist, string album, double durationMs, CancellationToken token);
Task<LyricsSearchResult> SearchSmartlyAsync(string mediaSessionId, string title, string artist, string album, double durationMs, string? songId, CancellationToken token);
Task<List<LyricsSearchResult>> SearchAllAsync(string title, string artist, string album, double durationMs, CancellationToken token);
}

View File

@@ -28,6 +28,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
private readonly HttpClient _amllTtmlDbHttpClient;
private readonly HttpClient _lrcLibHttpClient;
private readonly AppleMusic _appleMusic;
private readonly ISettingsService _settingsService;
private readonly ILogger _logger;
@@ -40,9 +41,10 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
_lrcLibHttpClient = new();
_lrcLibHttpClient.DefaultRequestHeaders.Add(
"User-Agent",
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GithubUrl})"
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GitHubUrl})"
);
_amllTtmlDbHttpClient = new();
_appleMusic = new AppleMusic();
}
private static bool IsAmllTtmlDbIndexInvalid()
@@ -89,7 +91,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}
}
public async Task<LyricsSearchResult> SearchSmartlyAsync(string mediaSessionId, string title, string artist, string album, double durationMs, CancellationToken token)
public async Task<LyricsSearchResult> SearchSmartlyAsync(string mediaSessionId, string title, string artist, string album, double durationMs, string? songId, CancellationToken token)
{
var lyricsSearchResult = new LyricsSearchResult();
@@ -121,7 +123,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
var targetProvider = found.LyricsSearchProvider;
if (targetProvider != null)
{
return await SearchSingleAsync(targetProvider.Value, overridenTitle, overridenArtist, album, durationMs, token);
return await SearchSingleAsync(targetProvider.Value, overridenTitle, overridenArtist, album, durationMs, songId, token);
}
}
@@ -132,7 +134,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
continue;
}
lyricsSearchResult = await SearchSingleAsync(provider.Provider, overridenTitle, overridenArtist, album, durationMs, token);
lyricsSearchResult = await SearchSingleAsync(provider.Provider, overridenTitle, overridenArtist, album, durationMs, null, token);
if (lyricsSearchResult.IsFound)
{
@@ -149,13 +151,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
var results = new List<LyricsSearchResult>();
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
{
var searchResult = await SearchSingleAsync(provider, title, artist, album, durationMs, token);
var searchResult = await SearchSingleAsync(provider, title, artist, album, durationMs, null, token);
results.Add(searchResult);
}
return results;
}
private async Task<LyricsSearchResult> SearchSingleAsync(LyricsSearchProvider provider, string title, string artist, string album, double durationMs, CancellationToken token)
private async Task<LyricsSearchResult> SearchSingleAsync(LyricsSearchProvider provider, string title, string artist, string album, double durationMs, string? songId, CancellationToken token)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -198,17 +200,20 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
lyricsSearchResult = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
break;
case LyricsSearchProvider.QQ:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
lyricsSearchResult = await SearchAmllTtmlDbAsync(title, artist);
break;
case LyricsSearchProvider.AppleMusic:
lyricsSearchResult = await SearchAppleMusicAsync(title, artist, album, (int)durationMs);
break;
default:
break;
}
@@ -431,7 +436,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private static async Task<LyricsSearchResult> SearchQQNeteaseKugouAsync(string title, string artist, string album, int durationMs, Searchers searchers)
private static async Task<LyricsSearchResult> SearchQQNeteaseKugouAsync(string title, string artist, string album, int durationMs, string? songId, Searchers searchers)
{
var lyricsSearchResult = new LyricsSearchResult();
@@ -452,15 +457,23 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
break;
}
var result = await SearchersHelper.GetSearcher(searchers).SearchForResult(
new Lyricify.Lyrics.Models.TrackMultiArtistMetadata()
{
DurationMs = durationMs,
Album = album,
Artists = [artist],
Title = title,
}
);
ISearchResult? result;
if (searchers == Searchers.Netease && songId != null)
{
result = new NeteaseSearchResult(title, [artist], album, null, durationMs, songId);
}
else
{
result = await SearchersHelper.GetSearcher(searchers).SearchForResult(
new Lyricify.Lyrics.Models.TrackMultiArtistMetadata()
{
DurationMs = durationMs,
Album = album,
Artists = [artist],
Title = title,
}
);
}
if (result is QQMusicSearchResult qqResult)
{
@@ -508,7 +521,21 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string? original = null;
if (response?.Candidates.FirstOrDefault() is SearchLyricsResponse.Candidate candidate)
{
original = Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyrics(candidate.Id, candidate.AccessKey);
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
if (candidate.TransId != null)
{
string? translated = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.TransId, candidate.AccessKey);
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
);
}
}
}
lyricsSearchResult.Raw = original;
@@ -518,5 +545,24 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchAppleMusicAsync(string title, string artist, string album, int durationMs)
{
var lyricsSearchResult = new LyricsSearchResult
{
Provider = LyricsSearchProvider.AppleMusic,
};
if (await _appleMusic.InitAsync())
{
var raw = await _appleMusic.GetLyricsAsync(title, artist);
_logger.LogInformation("Apple Music lyrics search result for {Title} - {Artist}: {Raw}", title, artist, raw ?? "null");
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
}
return lyricsSearchResult;
}
}
}

View File

@@ -27,9 +27,13 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo();
void UpdateLyrics();
void UpdateTranslations();
bool IsPlaying { get; }
SongInfo? SongInfo { get; }
TimeSpan Position { get; }
LyricsData? CurrentLyricsData { get; }
LyricsSearchProvider? LyricsSearchProvider { get; }
TranslationSearchProvider? TranslationSearchProvider { get; }

View File

@@ -34,7 +34,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
byte[]? bytes = await Task.Run(async () => await _albumArtSearchService.SearchAsync(
SongInfo?.SourceAppUserModelId ?? "",
SongInfo?.PlayerId ?? "",
_cachedSongInfo.Title,
_cachedSongInfo.Artist,
_cachedSongInfo?.Album ?? string.Empty,
@@ -58,7 +58,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
var decoder = await BitmapDecoder.CreateAsync(stream);
token.ThrowIfCancellationRequested();
var albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Premultiplied);
var albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
albumArtSwBitmap = SoftwareBitmap.Copy(albumArtSwBitmap);
token.ThrowIfCancellationRequested();
var albumArtLightAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, false).FirstOrDefault();

View File

@@ -3,6 +3,7 @@ using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using Lyricify.Lyrics.Helpers.General;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
@@ -22,7 +23,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private int _langIndex = 0;
private List<LyricsData> _lyricsDataArr = [];
private LyricsData? CurrentLyricsData => _lyricsDataArr.ElementAtOrDefault(_langIndex);
public LyricsData? CurrentLyricsData => _lyricsDataArr.ElementAtOrDefault(_langIndex);
public event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
@@ -57,7 +58,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
{
_logger.LogInformation("Showing translation for lyrics...");
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string targetLangCode = _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode;
_logger.LogInformation("Target language code: {TargetLangCode}", targetLangCode);
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
if (originalText == null) return;
@@ -65,6 +66,25 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
if (originalLangCode == "zh" && _settingsService.AppSettings.TranslationSettings.IsChineseRomanizationEnabled)
{
switch (_settingsService.AppSettings.TranslationSettings.ChineseRomanization)
{
case ChineseRomanization.Pinyin:
targetLangCode = "pinyin";
break;
case ChineseRomanization.Jyutping:
targetLangCode = "jyutping";
break;
default:
break;
}
}
else if (originalLangCode == "ja" && _settingsService.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled)
{
targetLangCode = "romaji";
}
if (originalLangCode == targetLangCode)
{
_logger.LogInformation("Original lyrics already in target language: {TargetLangCode}", targetLangCode);
@@ -74,7 +94,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
else
{
// Try get translation from itself first
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetLangCode);
if (found >= 0)
{
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
@@ -85,7 +105,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
else
{
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator, 50);
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _liveStatesService.LiveStates.LyricsStyleSettings.LyricsTranslationSeparator, 50);
_langIndex = 0;
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
}
@@ -108,7 +128,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
else
{
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator);
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _liveStatesService.LiveStates.LyricsStyleSettings.LyricsTranslationSeparator);
_langIndex = 0;
}
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;
@@ -136,19 +156,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
SongInfo.Title, SongInfo.Artist, SongInfo.Album, SongInfo.DurationMs);
var lyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(
SongInfo.SourceAppUserModelId ?? "",
SongInfo.PlayerId ?? "",
SongInfo.Title,
SongInfo.Artist,
SongInfo.Album ?? "",
SongInfo.DurationMs ?? 0,
SongInfo.SongId,
token
), token);
if (token.IsCancellationRequested) return;
LyricsSearchProvider = lyricsSearchResult?.Provider;
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider?.ToString() ?? "null");
_lyricsDataArr = new LyricsParser().Parse(lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs);
ApplyChinesePreference();
FillTranslationFromCache(LyricsSearchProvider);
}
else
@@ -161,10 +183,23 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
// This ensures that original lyrics are always shown while waiting for translations
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
ApplyChinesePreference();
UpdateTranslations();
}
private void ApplyChinesePreference()
{
var traditionalChinesePreferred = _settingsService.AppSettings.TranslationSettings.IsTraditionalChineseEnabled;
var found = _lyricsDataArr.FindIndex(x => x.LanguageCode == "zh");
if (found >= 0)
{
foreach (var item in _lyricsDataArr[found].LyricsLines)
{
item.OriginalText = traditionalChinesePreferred ? ChineseHelper.S2T(item.OriginalText) : ChineseHelper.T2S(item.OriginalText);
}
}
}
private void FillTranslationFromCache(LyricsSearchProvider? provider)
{
string? translationRaw = null;
@@ -174,6 +209,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
break;
case Enums.LyricsSearchProvider.Kugou:
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.KugouTranslationCacheDirectory);
break;
case Enums.LyricsSearchProvider.Netease:
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
@@ -214,12 +250,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
}
private void UpdateLyrics()
public void UpdateLyrics()
{
_refreshLyricsRunner.RunAsync(RefreshLyricsAsync);
}
private void UpdateTranslations()
public void UpdateTranslations()
{
_refreshTranslationRunner.RunAsync(RefreshTranslationAsync);
}

View File

@@ -7,7 +7,6 @@ using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
@@ -20,19 +19,12 @@ using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using EvtSource;
using Microsoft.Extensions.Logging;
using Microsoft.Graphics.Canvas;
using Microsoft.UI.Dispatching;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Media.Control;
using Windows.Storage.Streams;
using WindowsMediaController;
@@ -40,10 +32,10 @@ using WindowsMediaController;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<LyricsWindowMode>>,
IRecipient<PropertyChangedMessage<ChineseRomanization>>,
IRecipient<PropertyChangedMessage<List<string>>>
{
private readonly IAlbumArtSearchService _albumArtSearchService;
@@ -56,6 +48,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private double _lxMusicPositionSeconds = 0;
private double _lxMusicDurationSeconds = 0;
private byte[]? _lxMusicAlbumArtBytes = null;
private bool _cachedIsPlaying = false;
private TimeSpan _cachedPosition = TimeSpan.Zero;
@@ -291,21 +284,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
string id = mediaSession.Id;
string sessionId = mediaSession.Id;
var desiredSession = GetCurrentSession();
//RecordMediaSourceProviderInfo(mediaSession);
if (mediaSession != desiredSession) return;
if (!IsMediaSourceEnabled(id))
if (!IsMediaSourceEnabled(sessionId))
{
_cachedSongInfo = null;
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
if (id == Constants.PlayerID.LXMusic)
if (sessionId == Constants.PlayerID.LXMusic)
{
StopSSE();
}
@@ -320,21 +313,39 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
currentMediaSourceProviderInfo?.PositionOffset = 0;
}
string fixedArtist = mediaProperties.Artist;
string fixedAlbum = mediaProperties.AlbumTitle;
string? songId = null;
if (sessionId == Constants.PlayerID.AppleMusic || sessionId == Constants.PlayerID.AppleMusicAlternative)
{
fixedArtist = mediaProperties.Artist.Split(" — ").FirstOrDefault() ?? mediaProperties.Artist;
fixedAlbum = mediaProperties.Artist.Split(" — ").LastOrDefault() ?? mediaProperties.AlbumTitle;
}
else if (sessionId == Constants.PlayerID.NetEaseCloudMusic)
{
songId = mediaProperties.Genres.FirstOrDefault()?.Replace("NCM-", "");
if (songId != null && songId.Length != 10)
{
songId = null;
}
}
_cachedSongInfo = new SongInfo
{
Title = mediaProperties.Title,
Artist = mediaProperties.Artist,
Album = mediaProperties.AlbumTitle,
Artist = fixedArtist,
Album = fixedAlbum,
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
SourceAppUserModelId = id,
PlayerId = sessionId,
SongId = songId
};
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
if (id == Constants.PlayerID.LXMusic)
if (sessionId == Constants.PlayerID.LXMusic)
{
StartSSE();
}
@@ -343,7 +354,11 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
StopSSE();
}
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
if (sessionId == Constants.PlayerID.LXMusic && _lxMusicAlbumArtBytes != null)
{
_SMTCAlbumArtBytes = _lxMusicAlbumArtBytes;
}
else if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
{
_SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference);
}
@@ -447,6 +462,11 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void StartSSE()
{
if (_sse != null)
{
return;
}
try
{
_sse = new EventSourceReader(new Uri($"{_settingsService.AppSettings.GeneralSettings.LXMusicServer}{Constants.LXMusic.QuerySuffix}")).Start();
@@ -486,9 +506,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void Sse_MessageReceived(object sender, EventSourceMessageEventArgs e)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (_cachedSongInfo?.SourceAppUserModelId == Constants.PlayerID.LXMusic)
if (_cachedSongInfo?.PlayerId == Constants.PlayerID.LXMusic)
{
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
if (data.ValueKind == JsonValueKind.Number)
@@ -507,6 +527,20 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.FromSeconds(_lxMusicPositionSeconds), TimeSpan.FromSeconds(_lxMusicDurationSeconds)));
}
}
else if (data.ValueKind == JsonValueKind.String)
{
if (e.Event == "picUrl")
{
string? picUrl = data.GetString();
if (picUrl != null)
{
_logger.LogInformation("LX Music Album Art URL: {url}", picUrl);
_lxMusicAlbumArtBytes = await ImageHelper.GetImageBytesFromUrlAsync(picUrl);
_SMTCAlbumArtBytes = _lxMusicAlbumArtBytes;
UpdateAlbumArt();
}
}
}
}
});
}
@@ -579,6 +613,18 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
UpdateTranslations();
}
else if (message.PropertyName == nameof(TranslationSettings.IsChineseRomanizationEnabled))
{
UpdateTranslations();
}
else if (message.PropertyName == nameof(TranslationSettings.IsJapaneseRomanizationEnabled))
{
UpdateTranslations();
}
else if (message.PropertyName == nameof(TranslationSettings.IsTraditionalChineseEnabled))
{
UpdateLyrics();
}
}
}
@@ -601,18 +647,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageIndex))
{
_logger.LogInformation("Target language index changed: {Index}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex);
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<string> message)
{
if (message.Sender is LyricsStyleSettings)
@@ -622,13 +656,33 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
UpdateTranslations();
}
}
else if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageCode))
{
_logger.LogInformation("Target language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<LyricsWindowMode> message)
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.CurrentLyricsWindowMode))
if (message.PropertyName == nameof(LiveStates.LyricsWindowMode))
{
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<ChineseRomanization> message)
{
if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.ChineseRomanization))
{
UpdateTranslations();
}

View File

@@ -60,6 +60,9 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
AppSettings.MappedSongSearchQueries.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.MappedSongSearchQueries.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.WindowBoundsRecords.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.WindowBoundsRecords.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.Version = MetadataHelper.AppVersion;
EnsureMediaSourceProvidersInfo();

View File

@@ -12,6 +12,6 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
{
Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken token);
int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr);
int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr, string targetLangCode);
}
}

View File

@@ -58,9 +58,8 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
return result?.TranslatedText ?? string.Empty;
}
public int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr)
public int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr, string targetLangCode)
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
if (lyricsDataArr.Count > 1)
{
for (int i = 1; i < lyricsDataArr.Count; i++)

View File

@@ -135,8 +135,8 @@
<data name="AllLyricsSettingsControlStandard.Content" xml:space="preserve">
<value>Standard</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>Always on top</value>
<data name="AppSettingsControlGeneral.Content" xml:space="preserve">
<value>General</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>Press Esc to exit full screen mode</value>
@@ -252,15 +252,12 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>Translation provider</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>Artist</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="LyricsSearchControlLyricsPreview.Text" xml:space="preserve">
<value>Lyrics preview</value>
</data>
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
<value>mapped as</value>
</data>
@@ -279,15 +276,18 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>Search</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<value>Song info mapping</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Text" xml:space="preserve">
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>Target lyrics search provider</value>
</data>
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>Title</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>Lyrics search - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>Local .ESLRC files</value>
</data>
@@ -442,6 +442,15 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>Music gallery - BetterLyrics</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>Donation</value>
</data>
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>Feedback</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>If you like this project, please consider supporting it by donating. Your support will help keep the project alive and encourage further development.</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>About</value>
</data>
@@ -478,6 +487,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageAmount.Header" xml:space="preserve">
<value>Amount</value>
</data>
<data name="SettingsPageAOT.Header" xml:space="preserve">
<value>Always on top</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>App appearance and behavior</value>
</data>
@@ -487,17 +499,8 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>App behavior</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>Desktop mode</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>Dock mode</value>
</data>
<data name="SettingsPageAppPictureInPicture.Text" xml:space="preserve">
<value>Picture-in-picture mode</value>
</data>
<data name="SettingsPageAppStandard.Text" xml:space="preserve">
<value>Standard</value>
<data name="SettingsPageApply.Content" xml:space="preserve">
<value>Apply</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>Auto-lock when activating desktop mode</value>
@@ -535,22 +538,37 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>Cache</value>
</data>
<data name="SettingsPageChinese.Header" xml:space="preserve">
<value>Chinese pronunciation</value>
</data>
<data name="SettingsPageChinesePreference.Header" xml:space="preserve">
<value>Convert Chinese to Traditional Chinese</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>Compact</value>
</data>
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
<value>Current lyrics window status</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>Dark</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>Show debug overlay</value>
</data>
<data name="SettingsPageDelete.Content" xml:space="preserve">
<value>Delete</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>Desktop Acrylic</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>Advanced options</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<data name="SettingsPageDisclaimer.Text" xml:space="preserve">
<value>This project is provided as is without warranty of any kind. All lyrics, fonts, icons, and other third-party resources are the property of their respective copyright holders. The author of this project does not claim ownership of such resources. This project is non-commercial and should not be used to infringe any rights. Users are responsible for ensuring their own use complies with applicable laws and licenses.</value>
</data>
<data name="SettingsPageDiscord.Content" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageDisplayTypeSwitcher.Header" xml:space="preserve">
@@ -571,6 +589,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>Window height</value>
</data>
<data name="SettingsPageDynamicLyricsFontSize.Header" xml:space="preserve">
<value>Automatic adjustment</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>Easing animation type</value>
</data>
@@ -625,7 +646,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>Fan lyrics</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<data name="SettingsPageFAQ.Content" xml:space="preserve">
<value>Frequently asked questions</value>
</data>
<data name="SettingsPageFixedTimeStep.Header" xml:space="preserve">
@@ -637,15 +658,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageFPS.Header" xml:space="preserve">
<value>Rendering frame rate</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>Open in new window</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>See source code on GitHub</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>This is an open source app</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>Extend the title bar to the entire page so that the window can be dragged in any non-interactive area</value>
</data>
@@ -653,10 +665,10 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<value>Global drag</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>Automatically hide the window when no songs are playing in dock mode or desktop mode</value>
<value>In docking mode or desktop mode, when no song is playing, the window will be automatically hidden; otherwise, the window will be automatically displayed.</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>Auto-hide window</value>
<value>Automatically hide/show windows</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>Force this app to appear on top of full-screen apps when docked or desktop mode is enabled</value>
@@ -673,9 +685,15 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageJA.Content" xml:space="preserve">
<value>日本語</value>
</data>
<data name="SettingsPageJapanese.Header" xml:space="preserve">
<value>Japanese annotation</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>Join now</value>
</data>
<data name="SettingsPageJyutping.Content" xml:space="preserve">
<value>Cantonese pinyin</value>
</data>
<data name="SettingsPageKO.Content" xml:space="preserve">
<value>한국어</value>
</data>
@@ -895,6 +913,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>Top and bottom edge opacity</value>
</data>
<data name="SettingsPageLyricsWindowManager.Header" xml:space="preserve">
<value>Lyrics window manager</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>Media library</value>
</data>
@@ -940,6 +961,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>The path cannot be found on your computer</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>Pinyin of Mandarin</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>Playback sources</value>
</data>
@@ -958,9 +982,15 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPagePreviousSongHotKey.Header" xml:space="preserve">
<value>Shortcut keys for the previous track</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<data name="SettingsPageQQGroup.Content" xml:space="preserve">
<value>QQ feedback &amp; chat group</value>
</data>
<data name="SettingsPageRecord.Content" xml:space="preserve">
<value>Record</value>
</data>
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
<value>Recorded window status</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>Original files and folders in this path will not be deleted when removing it from this app</value>
</data>
@@ -973,6 +1003,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>Restart app to apply change</value>
</data>
<data name="SettingsPageRomaji.Header" xml:space="preserve">
<value>Japanese annotation</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
</data>
@@ -1015,6 +1048,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
<value>This hotkey has been successfully registered</value>
</data>
<data name="SettingsPageShowHideHotKey.Header" xml:space="preserve">
<value>Lyrics window display and hide shortcut keys</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>Current value: </value>
</data>
@@ -1042,7 +1078,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Header" xml:space="preserve">
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">

View File

@@ -135,8 +135,8 @@
<data name="AllLyricsSettingsControlStandard.Content" xml:space="preserve">
<value>標準モード</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>常にトップ</value>
<data name="AppSettingsControlGeneral.Content" xml:space="preserve">
<value>一般的な</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>ESCを押して、フルスクリーンモードを終了します</value>
@@ -252,15 +252,12 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻訳プロバイダー</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>アーティスト</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
<value>*保存変更はすぐに有効になります。その後、トラックの歌詞がマッピング情報とターゲット歌詞で取得されます。純粋な音楽としてのマーキングは、純粋な音楽プレースホルダーの歌詞に直接戻ります。元のデータで取得するためにリセット。</value>
</data>
<data name="LyricsSearchControlLyricsPreview.Text" xml:space="preserve">
<value>歌詞プレビュー</value>
</data>
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
<value>としてマッピングされました</value>
</data>
@@ -279,15 +276,18 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>検索する</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<value>曲情報マッピング</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Text" xml:space="preserve">
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>ターゲット歌詞検索プロバイダー</value>
</data>
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>タイトル</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌詞検索 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>ローカル.ESLRCファイル</value>
</data>
@@ -442,6 +442,15 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音楽ギャラリー - BetterLyrics</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>寄付</value>
</data>
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>フィードバック</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>このプロジェクトが気に入っている場合は、寄付してサポートすることを検討してください。あなたのサポートは、プロジェクトを生かし続け、さらなる開発を促進するのに役立ちます。</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>について</value>
</data>
@@ -478,6 +487,9 @@
<data name="SettingsPageAmount.Header" xml:space="preserve">
<value>量</value>
</data>
<data name="SettingsPageAOT.Header" xml:space="preserve">
<value>常にトップ</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>アプリの外観と動作</value>
</data>
@@ -487,17 +499,8 @@
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>アプリの動作</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>デスクトップモード</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>ドックモード</value>
</data>
<data name="SettingsPageAppPictureInPicture.Text" xml:space="preserve">
<value>ピクチャーインピクチャーモード</value>
</data>
<data name="SettingsPageAppStandard.Text" xml:space="preserve">
<value>標準モード</value>
<data name="SettingsPageApply.Content" xml:space="preserve">
<value>適用する</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>デスクトップモードをアクティブにするときの自動ロック</value>
@@ -535,22 +538,37 @@
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>キャッシュ</value>
</data>
<data name="SettingsPageChinese.Header" xml:space="preserve">
<value>中国の発音</value>
</data>
<data name="SettingsPageChinesePreference.Header" xml:space="preserve">
<value>中国人を伝統的な中国人に変換します</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>コンパクト</value>
</data>
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
<value>現在の歌詞ウィンドウステータス</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>暗い</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>デバッグオーバーレイを表示します</value>
</data>
<data name="SettingsPageDelete.Content" xml:space="preserve">
<value>消去</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>デスクトップアクリル</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>高度なオプション</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<data name="SettingsPageDisclaimer.Text" xml:space="preserve">
<value>このプロジェクトは、いかなる種類の保証もなく現状のまま提供されています。すべての歌詞、フォント、アイコン、およびその他のサードパーティのリソースは、それぞれの著作権所有者の財産です。このプロジェクトの作成者は、そのようなリソースの所有権を主張していません。このプロジェクトは非営利的であり、いかなる権利も侵害するために使用すべきではありません。ユーザーは、自らの使用が適用される法律およびライセンスに準拠していることを確認する責任があります。</value>
</data>
<data name="SettingsPageDiscord.Content" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageDisplayTypeSwitcher.Header" xml:space="preserve">
@@ -571,6 +589,9 @@
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>窓の高さ</value>
</data>
<data name="SettingsPageDynamicLyricsFontSize.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>アニメーションタイプを緩和します</value>
</data>
@@ -625,7 +646,7 @@
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>ファンの歌詞</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<data name="SettingsPageFAQ.Content" xml:space="preserve">
<value>よくある質問</value>
</data>
<data name="SettingsPageFixedTimeStep.Header" xml:space="preserve">
@@ -637,15 +658,6 @@
<data name="SettingsPageFPS.Header" xml:space="preserve">
<value>レンダリングフレームレート</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>新しいウィンドウで開きます</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>GitHubのソースコードを参照してください</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>これはオープンソースアプリです</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>タイトルバーをページ全体に拡張して、ウィンドウを非対話領域でドラッグできるようにします</value>
</data>
@@ -653,10 +665,10 @@
<value>グローバルドラッグ</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>ドックモードやデスクトップモードで再生されている曲がないときにウィンドウ自動的に非表示にする</value>
<value>ドッキングモードまたはデスクトップモードでは、曲が再生されていない場合、ウィンドウ自動的に非表示になります。それ以外の場合、ウィンドウが自動的に表示されます。</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>自動ハイドウィンドウ</value>
<value>Windowsを自動的に非表示/表示します</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>このアプリは、ドッキングまたはデスクトップモードが有効になっているときにフルスクリーンアプリの上に表示されます</value>
@@ -673,9 +685,15 @@
<data name="SettingsPageJA.Content" xml:space="preserve">
<value>日本語</value>
</data>
<data name="SettingsPageJapanese.Header" xml:space="preserve">
<value>日本の注釈</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>今すぐ参加してください</value>
</data>
<data name="SettingsPageJyutping.Content" xml:space="preserve">
<value>広東語のピンイン</value>
</data>
<data name="SettingsPageKO.Content" xml:space="preserve">
<value>한국어</value>
</data>
@@ -877,15 +895,15 @@
<data name="SettingsPageLyricsThin.Content" xml:space="preserve">
<value>薄い</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>歌詞のタイムラインを同期</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>歌詞の進行が不安定な場合は、このしきい値を増やしてみてください。この値を変更すると、歌詞の同期が逸脱する可能性があります</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>歌詞タイムライン同期しきい値</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>歌詞のタイムラインを同期</value>
</data>
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
<value>翻訳ハイライト</value>
</data>
@@ -895,6 +913,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上端と下端の不透明度</value>
</data>
<data name="SettingsPageLyricsWindowManager.Header" xml:space="preserve">
<value>歌詞ウィンドウマネージャー</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>メディアライブラリ</value>
</data>
@@ -940,6 +961,9 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>パスはコンピューターでは見つかりません</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>マンダリンのピンイン</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>再生ソース</value>
</data>
@@ -958,9 +982,15 @@
<data name="SettingsPagePreviousSongHotKey.Header" xml:space="preserve">
<value>前のトラックのショートカットキー</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<data name="SettingsPageQQGroup.Content" xml:space="preserve">
<value>QQフィードバックチャットグループ</value>
</data>
<data name="SettingsPageRecord.Content" xml:space="preserve">
<value>記録</value>
</data>
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
<value>記録されたウィンドウステータス</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>このパスの元のファイルとフォルダーは、このアプリから削除するときに削除されません</value>
</data>
@@ -973,6 +1003,9 @@
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>変更を適用するためのアプリを再起動します</value>
</data>
<data name="SettingsPageRomaji.Header" xml:space="preserve">
<value>日本の注釈</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
</data>
@@ -1015,6 +1048,9 @@
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
<value>このホットキーは正常に登録されています</value>
</data>
<data name="SettingsPageShowHideHotKey.Header" xml:space="preserve">
<value>歌詞ウィンドウディスプレイとショートカットキーを非表示にします</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>現在の値: </value>
</data>
@@ -1042,7 +1078,7 @@
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Header" xml:space="preserve">
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">

View File

@@ -135,8 +135,8 @@
<data name="AllLyricsSettingsControlStandard.Content" xml:space="preserve">
<value>표준 모드</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>항상 위에</value>
<data name="AppSettingsControlGeneral.Content" xml:space="preserve">
<value>일반적인</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>ESC를 눌러 전체 화면 모드를 종료하십시오</value>
@@ -252,15 +252,12 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>번역 제공자</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>아티스트</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
<value>* 변경 사항 저장 변경은 즉시 적용되며, 그 후 트랙 가사는 맵핑 정보와 대상 가사로 검색됩니다. 순수한 음악으로 표시하면 순수한 음악 자리 표시 자 가사로 직접 돌아갑니다. 원래 데이터로 검색하도록 재설정하십시오.</value>
</data>
<data name="LyricsSearchControlLyricsPreview.Text" xml:space="preserve">
<value>가사 미리보기</value>
</data>
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
<value>로 매핑됨</value>
</data>
@@ -279,15 +276,18 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>검색</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<value>노래 정보 매핑</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Text" xml:space="preserve">
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>가사 검색 공급자를 타겟팅하십시오</value>
</data>
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>제목</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>가사 검색 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>로컬 .ESLRC 파일</value>
</data>
@@ -442,6 +442,15 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>음악 갤러리 - BetterLyrics</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>기부</value>
</data>
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>피드백</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>이 프로젝트가 마음에 들면 기부하여 지원을 고려하십시오. 귀하의 지원은 프로젝트를 계속 유지하고 추가 개발을 장려하는 데 도움이 될 것입니다.</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>에 대한</value>
</data>
@@ -478,6 +487,9 @@
<data name="SettingsPageAmount.Header" xml:space="preserve">
<value>금액</value>
</data>
<data name="SettingsPageAOT.Header" xml:space="preserve">
<value>항상 위에</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>앱 외관과 행동</value>
</data>
@@ -487,17 +499,8 @@
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>앱 동작</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>데스크탑 모드</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>도크 모드</value>
</data>
<data name="SettingsPageAppPictureInPicture.Text" xml:space="preserve">
<value>사진 인당 모드</value>
</data>
<data name="SettingsPageAppStandard.Text" xml:space="preserve">
<value>표준 모드</value>
<data name="SettingsPageApply.Content" xml:space="preserve">
<value>적용하다</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>데스크탑 모드를 활성화 할 때 자동 잠금</value>
@@ -535,22 +538,37 @@
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>은닉처</value>
</data>
<data name="SettingsPageChinese.Header" xml:space="preserve">
<value>화음</value>
</data>
<data name="SettingsPageChinesePreference.Header" xml:space="preserve">
<value>중국어를 전통적인 중국어로 전환합니다</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>콤팩트</value>
</data>
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
<value>현재 가사 창 상태</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>어두운</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>디버그 오버레이를 표시하십시오</value>
</data>
<data name="SettingsPageDelete.Content" xml:space="preserve">
<value>삭제</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>데스크탑 아크릴</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>고급 옵션</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<data name="SettingsPageDisclaimer.Text" xml:space="preserve">
<value>이 프로젝트는 어떠한 종류의 보증도 없이 그대로 제공됩니다. 모든 가사, 글꼴, 아이콘 및 기타 제3자 리소스는 해당 저작권 소유자의 재산입니다. 이 프로젝트의 저자는 그러한 자원의 소유권을 주장하지 않습니다. 이 프로젝트는 비상업적이기 때문에 어떠한 권리도 침해해서는 안 됩니다. 사용자는 자신의 사용이 관련 법률 및 라이센스를 준수하는지 확인할 책임이 있습니다.</value>
</data>
<data name="SettingsPageDiscord.Content" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageDisplayTypeSwitcher.Header" xml:space="preserve">
@@ -571,6 +589,9 @@
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>창 높이</value>
</data>
<data name="SettingsPageDynamicLyricsFontSize.Header" xml:space="preserve">
<value>자동 조정</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>애니메이션 유형 완화</value>
</data>
@@ -625,7 +646,7 @@
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>팬 가사</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<data name="SettingsPageFAQ.Content" xml:space="preserve">
<value>자주 묻는 질문</value>
</data>
<data name="SettingsPageFixedTimeStep.Header" xml:space="preserve">
@@ -637,15 +658,6 @@
<data name="SettingsPageFPS.Header" xml:space="preserve">
<value>렌더링 프레임 속도</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>새 창에서 열립니다</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>GitHub의 소스 코드를 참조하십시오</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>이것은 오픈 소스 앱입니다</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>비 중과 영역에서 창을 드래그 할 수 있도록 제목 표시 줄을 전체 페이지로 확장하십시오.</value>
</data>
@@ -653,10 +665,10 @@
<value>글로벌 드래그</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>도 모드 또는 데스크탑 모드에서 재생되지 않을 때는 자동으로 숨기고 있습니다</value>
<value>도 모드 또는 데스크탑 모드에서는 노래가 재생되지 않으면 자동으로 숨겨집니다. 그렇지 않으면 창이 자동으로 표시됩니다.</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>자동 가죽 창</value>
<value>Windows를 자동으로 숨기고 표시합니다</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>도킹 또는 데스크탑 모드가 활성화 될 때이 앱이 전체 화면 앱 위에 나타나도록 강요</value>
@@ -673,9 +685,15 @@
<data name="SettingsPageJA.Content" xml:space="preserve">
<value>日本語</value>
</data>
<data name="SettingsPageJapanese.Header" xml:space="preserve">
<value>일본 주석</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>지금 가입하십시오</value>
</data>
<data name="SettingsPageJyutping.Content" xml:space="preserve">
<value>광동어 Pinyin</value>
</data>
<data name="SettingsPageKO.Content" xml:space="preserve">
<value>한국어</value>
</data>
@@ -877,15 +895,15 @@
<data name="SettingsPageLyricsThin.Content" xml:space="preserve">
<value>얇은</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>가사 타임라인 동기화</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>가사 진행 상황이 불안하다면이 임계 값을 높이십시오. 이 값을 변경하면 가사가 동기화 될 수 있습니다</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>가사 타임 라인 동기화 임계 값</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>가사 타임라인 동기화</value>
</data>
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
<value>번역 하이라이트</value>
</data>
@@ -895,6 +913,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>상단 및 하단 가장자리 불투명도</value>
</data>
<data name="SettingsPageLyricsWindowManager.Header" xml:space="preserve">
<value>가사 창 관리자</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>미디어 라이브러리</value>
</data>
@@ -940,6 +961,9 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>경로는 컴퓨터에서 찾을 수 없습니다</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>만다린의 피니 인</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>재생 소스</value>
</data>
@@ -958,9 +982,15 @@
<data name="SettingsPagePreviousSongHotKey.Header" xml:space="preserve">
<value>이전 트랙의 바로 가기 키</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<data name="SettingsPageQQGroup.Content" xml:space="preserve">
<value>QQ 피드백 및 채팅 그룹</value>
</data>
<data name="SettingsPageRecord.Content" xml:space="preserve">
<value>기록</value>
</data>
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
<value>기록 된 창 상태</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>이 경로의 원본 파일과 폴더는이 앱에서 제거 할 때 삭제되지 않습니다.</value>
</data>
@@ -973,6 +1003,9 @@
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>변경 사항을 적용하려면 앱을 다시 시작하십시오</value>
</data>
<data name="SettingsPageRomaji.Header" xml:space="preserve">
<value>일본 주석</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
</data>
@@ -1015,6 +1048,9 @@
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
<value>이 핫키는 성공적으로 등록되었습니다</value>
</data>
<data name="SettingsPageShowHideHotKey.Header" xml:space="preserve">
<value>가사 창 디스플레이 및 바로 가기 키 숨기기</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>현재 가치 : </value>
</data>
@@ -1042,7 +1078,7 @@
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Header" xml:space="preserve">
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">

View File

@@ -135,8 +135,8 @@
<data name="AllLyricsSettingsControlStandard.Content" xml:space="preserve">
<value>标准模式</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>将应用置于顶层</value>
<data name="AppSettingsControlGeneral.Content" xml:space="preserve">
<value>通用</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>按 Esc 退出全屏模式</value>
@@ -252,15 +252,12 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻译来源</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>艺术家</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
<value>* 保存更改立即生效,此后将以映射信息和目标歌词源检索该曲目歌词;标记为纯音乐将直接返回纯音乐占位歌词。重置以按原始数据检索。</value>
</data>
<data name="LyricsSearchControlLyricsPreview.Text" xml:space="preserve">
<value>歌词预览</value>
</data>
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
<value>映射为</value>
</data>
@@ -279,15 +276,18 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>搜索</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<value>歌曲信息映射</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Text" xml:space="preserve">
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>目标歌词搜索提供商</value>
</data>
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>标题</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌词搜索 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>本地 .ESLRC 文件</value>
</data>
@@ -442,6 +442,15 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音乐库 - BetterLyrics</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>捐贈</value>
</data>
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>反馈</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜欢这个项目,请考虑通过捐赠来支持它。您的支持将有助于保持项目的生命并鼓励进一步的发展。</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>关于</value>
</data>
@@ -478,6 +487,9 @@
<data name="SettingsPageAmount.Header" xml:space="preserve">
<value>量</value>
</data>
<data name="SettingsPageAOT.Header" xml:space="preserve">
<value>将应用置于顶层</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>应用外观与行为</value>
</data>
@@ -487,17 +499,8 @@
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>应用行为</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>桌面模式</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>停靠模式</value>
</data>
<data name="SettingsPageAppPictureInPicture.Text" xml:space="preserve">
<value>画中画模式</value>
</data>
<data name="SettingsPageAppStandard.Text" xml:space="preserve">
<value>标准模式</value>
<data name="SettingsPageApply.Content" xml:space="preserve">
<value>应用</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>启动桌面模式时随即锁定窗口</value>
@@ -535,22 +538,37 @@
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>缓存</value>
</data>
<data name="SettingsPageChinese.Header" xml:space="preserve">
<value>中文注音</value>
</data>
<data name="SettingsPageChinesePreference.Header" xml:space="preserve">
<value>中文转换为繁体</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>紧凑</value>
</data>
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
<value>当前歌词窗口状态</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>深色</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>显示调试覆盖层</value>
</data>
<data name="SettingsPageDelete.Content" xml:space="preserve">
<value>删除</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>亚克力(桌面)</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>高级选项</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<data name="SettingsPageDisclaimer.Text" xml:space="preserve">
<value>本项目按原样提供,不提供任何形式的保证。所有歌词、字体、图标和其他第三方资源均为其各自版权所有者的财产。本项目的作者不主张此类资源的所有权。本项目为非商业性项目,不得用于侵犯任何权利。用户有责任确保自己的使用符合适用的法律和许可。</value>
</data>
<data name="SettingsPageDiscord.Content" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageDisplayTypeSwitcher.Header" xml:space="preserve">
@@ -571,6 +589,9 @@
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>窗口高度</value>
</data>
<data name="SettingsPageDynamicLyricsFontSize.Header" xml:space="preserve">
<value>自动调整</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>缓动动画类型</value>
</data>
@@ -625,7 +646,7 @@
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>扇形歌词</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<data name="SettingsPageFAQ.Content" xml:space="preserve">
<value>常见问题与解答</value>
</data>
<data name="SettingsPageFixedTimeStep.Header" xml:space="preserve">
@@ -637,15 +658,6 @@
<data name="SettingsPageFPS.Header" xml:space="preserve">
<value>渲染帧率</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>在新窗口中打开</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>在 GitHub 上查看源代码</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>此应用已开源</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>将标题栏扩展至整个页面使得在任意非交互区域均可拖拽窗口</value>
</data>
@@ -653,10 +665,10 @@
<value>全局拖拽</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>停靠模式或桌面模式下,无歌曲正在播放时,自动隐藏窗口</value>
<value>停靠模式或桌面模式下,无歌曲正在播放时,自动隐藏窗口;反之自动显示窗口</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>自动隐藏窗口</value>
<value>自动隐藏/显示窗口</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>当启用停靠模式或桌面模式时强制将本应用显示在全屏应用的上方</value>
@@ -673,9 +685,15 @@
<data name="SettingsPageJA.Content" xml:space="preserve">
<value>日本語</value>
</data>
<data name="SettingsPageJapanese.Header" xml:space="preserve">
<value>日语注音</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>立即加入</value>
</data>
<data name="SettingsPageJyutping.Content" xml:space="preserve">
<value>广东话拼音</value>
</data>
<data name="SettingsPageKO.Content" xml:space="preserve">
<value>한국어</value>
</data>
@@ -877,15 +895,15 @@
<data name="SettingsPageLyricsThin.Content" xml:space="preserve">
<value>极细</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>歌词时间轴同步</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>当歌词进度抖动时,请尝试增加该阈值;更改此值会导致歌词同步有偏差</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>歌词时间轴同步阈值</value>
</data>
<data name="SettingsPageLyricsTimeline.Header" xml:space="preserve">
<value>歌词时间轴同步</value>
</data>
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
<value>翻译高亮</value>
</data>
@@ -895,6 +913,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上下边缘不透明度</value>
</data>
<data name="SettingsPageLyricsWindowManager.Header" xml:space="preserve">
<value>歌词窗口管理</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>媒体库</value>
</data>
@@ -940,6 +961,9 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>无法在您的计算机中找到该路径</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>普通话拼音</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>播放源</value>
</data>
@@ -958,9 +982,15 @@
<data name="SettingsPagePreviousSongHotKey.Header" xml:space="preserve">
<value>上一首曲目快捷键</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<data name="SettingsPageQQGroup.Content" xml:space="preserve">
<value>QQ 反馈交流群</value>
</data>
<data name="SettingsPageRecord.Content" xml:space="preserve">
<value>记录</value>
</data>
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
<value>记录的窗口状态</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>路径中的原始文件和文件夹不会被删除</value>
</data>
@@ -973,6 +1003,9 @@
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>重启应用以应用更改</value>
</data>
<data name="SettingsPageRomaji.Header" xml:space="preserve">
<value>日语注音</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
</data>
@@ -1015,6 +1048,9 @@
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
<value>该热键已成功注册</value>
</data>
<data name="SettingsPageShowHideHotKey.Header" xml:space="preserve">
<value>歌词窗口显示和隐藏快捷键</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>当前值: </value>
</data>
@@ -1042,7 +1078,7 @@
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Header" xml:space="preserve">
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">

View File

@@ -135,8 +135,8 @@
<data name="AllLyricsSettingsControlStandard.Content" xml:space="preserve">
<value>標準模式</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>將應用置於頂層</value>
<data name="AppSettingsControlGeneral.Content" xml:space="preserve">
<value>一般</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>按 Esc 退出全螢幕模式</value>
@@ -252,15 +252,12 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻譯來源</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>藝術家</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
<value>* 保存更改立即生效,此後將以映射信息和目標歌詞源檢索該曲目歌詞;標記為純音樂將直接返回純音樂佔位歌詞。重置以按原始數據檢索。</value>
</data>
<data name="LyricsSearchControlLyricsPreview.Text" xml:space="preserve">
<value>歌詞預覽</value>
</data>
<data name="LyricsSearchControlMappedAs.Text" xml:space="preserve">
<value>映射爲</value>
</data>
@@ -279,15 +276,18 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>搜索</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<value>歌曲信息映射</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Text" xml:space="preserve">
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>目標歌詞搜尋提供者</value>
</data>
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>標題</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌詞搜尋 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>本地 .ESLRC 文件</value>
</data>
@@ -442,6 +442,15 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音樂庫 - BetterLyrics</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>捐贈</value>
</data>
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>回饋</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜歡這個項目,請考慮通過捐贈來支持它。您的支持將有助於保持項目的生命並鼓勵進一步的發展。</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>關於</value>
</data>
@@ -478,6 +487,9 @@
<data name="SettingsPageAmount.Header" xml:space="preserve">
<value>量</value>
</data>
<data name="SettingsPageAOT.Header" xml:space="preserve">
<value>將應用置於頂層</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>應用外觀與行為</value>
</data>
@@ -487,17 +499,8 @@
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>應用行為</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>桌面模式</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>停靠模式</value>
</data>
<data name="SettingsPageAppPictureInPicture.Text" xml:space="preserve">
<value>畫中畫模式</value>
</data>
<data name="SettingsPageAppStandard.Text" xml:space="preserve">
<value>標準模式</value>
<data name="SettingsPageApply.Content" xml:space="preserve">
<value>應用</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>啟動桌面模式時隨即鎖定窗口</value>
@@ -535,22 +538,37 @@
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>快取</value>
</data>
<data name="SettingsPageChinese.Header" xml:space="preserve">
<value>中文注音</value>
</data>
<data name="SettingsPageChinesePreference.Header" xml:space="preserve">
<value>中文轉換為繁體</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>緊湊</value>
</data>
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
<value>當前歌詞窗口狀態</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>深色</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>顯示調試覆蓋層</value>
</data>
<data name="SettingsPageDelete.Content" xml:space="preserve">
<value>刪除</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>亞克力(桌面)</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>高級選項</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<data name="SettingsPageDisclaimer.Text" xml:space="preserve">
<value>本項目按原樣提供,不提供任何形式的保證。所有歌詞、字型、圖示和其他第三方資源均屬其各自的版權擁有者所有。此專案的作者並未聲明此類資源的所有權。此專案為非商業專案,不應用於侵犯任何權利。用戶有責任確保自己的使用符合適用的法律和許可證。</value>
</data>
<data name="SettingsPageDiscord.Content" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageDisplayTypeSwitcher.Header" xml:space="preserve">
@@ -571,6 +589,9 @@
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>窗口高度</value>
</data>
<data name="SettingsPageDynamicLyricsFontSize.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>緩動動畫類型</value>
</data>
@@ -625,7 +646,7 @@
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>扇形歌詞</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<data name="SettingsPageFAQ.Content" xml:space="preserve">
<value>常見問題與解答</value>
</data>
<data name="SettingsPageFixedTimeStep.Header" xml:space="preserve">
@@ -637,15 +658,6 @@
<data name="SettingsPageFPS.Header" xml:space="preserve">
<value>渲染幀率</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>在新視窗中開啟</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>在 GitHub 上查看原始碼</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>此應用程式已開源</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>將標題列擴展至整個頁面使得在任意非互動區域均可拖曳窗口</value>
</data>
@@ -653,10 +665,10 @@
<value>全域拖曳</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>停靠模式或桌面模式下,無歌曲正在播放時,自動隱藏窗口</value>
<value>停靠模式或桌面模式下,無歌曲正在播放時,自動隱藏窗口;反之自動顯示窗口</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>自動隱藏窗口</value>
<value>自動隱藏/顯示窗口</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>啟用停靠模式或桌面模式時強制將本應用程式顯示在全螢幕應用程式的上方</value>
@@ -673,9 +685,15 @@
<data name="SettingsPageJA.Content" xml:space="preserve">
<value>日本語</value>
</data>
<data name="SettingsPageJapanese.Header" xml:space="preserve">
<value>日語注音</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>立即加入</value>
</data>
<data name="SettingsPageJyutping.Content" xml:space="preserve">
<value>廣東話拼音</value>
</data>
<data name="SettingsPageKO.Content" xml:space="preserve">
<value>한국어</value>
</data>
@@ -895,6 +913,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上下邊緣不透明度</value>
</data>
<data name="SettingsPageLyricsWindowManager.Header" xml:space="preserve">
<value>歌詞視窗管理</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>媒體庫</value>
</data>
@@ -940,6 +961,9 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>無法在您的電腦中找到該路徑</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>普通話拼音</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>播放源</value>
</data>
@@ -958,9 +982,15 @@
<data name="SettingsPagePreviousSongHotKey.Header" xml:space="preserve">
<value>上一首曲目快捷鍵</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<data name="SettingsPageQQGroup.Content" xml:space="preserve">
<value>QQ 回饋交流群</value>
</data>
<data name="SettingsPageRecord.Content" xml:space="preserve">
<value>記錄</value>
</data>
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
<value>記錄的窗口狀態</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>路徑中的原始檔案和資料夾不會被刪除</value>
</data>
@@ -973,6 +1003,9 @@
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>重啟應用程式以應用更改</value>
</data>
<data name="SettingsPageRomaji.Header" xml:space="preserve">
<value>日語注音</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
</data>
@@ -1015,6 +1048,9 @@
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
<value>該熱鍵已成功註冊</value>
</data>
<data name="SettingsPageShowHideHotKey.Header" xml:space="preserve">
<value>歌詞窗口顯示和隱藏快捷鍵</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>目前值: </value>
</data>
@@ -1042,7 +1078,7 @@
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Header" xml:space="preserve">
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">

View File

@@ -36,7 +36,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.CurrentLyricsWindowMode))
if (message.PropertyName == nameof(LiveStates.LyricsWindowMode))
{
SelectedTabIndex = (int)message.NewValue;
}

View File

@@ -1,5 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
@@ -13,19 +15,25 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class AppSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
public partial LiveStates LiveStates { get; set; }
[ObservableProperty]
public partial ObservableCollection<string> MonitorDeviceNames { get; set; }
public AppSettingsControlViewModel(ISettingsService settingsService)
public AppSettingsControlViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
{
_settingsService = settingsService;
_liveStatesService = liveStatesService;
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
}
[RelayCommand]
@@ -41,6 +49,19 @@ namespace BetterLyrics.WinUI3.ViewModels
AppSettings.DockModeSettings.DockMonitorDeviceName = MonitorHelper.GetPrimaryMonitorDeviceName();
}
[RelayCommand]
private void RecordCurrentWindowBounds()
{
AppSettings.WindowBoundsRecords.Add(new WindowBoundsRecord
{
WindowBounds = LiveStates.LyricsWindowBounds,
MonitorDeviceName = LiveStates.LyricsWindowMonitorName,
MonitorBounds = LiveStates.LyricsWindowMonitorBounds,
DemoWindowBounds = LiveStates.DemoLyricsWindowBounds,
DemoMonitorBounds = LiveStates.DemoLyricsWindowMonitorBounds
});
}
public async Task<bool> ToggleAutoStartupAsync(bool target)
{
StartupTask startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);

View File

@@ -102,6 +102,15 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsSongPlaying { get; set; }
[ObservableProperty]
public partial float TimelineSliderThumbOpacity { get; set; } = 0f;
[ObservableProperty]
public partial LyricsLine? TimelineSliderThumbLyricsLine { get; set; }
[ObservableProperty]
public partial double TimelineSliderThumbSeconds { get; set; } = 0;
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is LyricsWindowViewModel)
@@ -157,6 +166,11 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
partial void OnTimelineSliderThumbSecondsChanged(double value)
{
TimelineSliderThumbLyricsLine = _mediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
}
//partial void OnVolumeChanged(int value)
//{
// SystemVolumeHelper.SetMasterVolume(value);

View File

@@ -33,7 +33,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var combined = new CanvasCommandList(control);
using var combinedDs = combined.CreateDrawingSession();
switch (_liveStatesService.LiveStates.CurrentLyricsWindowMode)
switch (_liveStatesService.LiveStates.LyricsWindowMode)
{
case LyricsWindowMode.DockMode:
FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f,
@@ -236,7 +236,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
_liveStatesService.LiveStates.LyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
combinedDs.DrawImage(backgroundEffect);
@@ -247,43 +247,43 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var charMask = CanvasHelper.CreateCharMask(control, line, charStartIndex, charLength, charProgress);
using var lineStartToCharMask = CanvasHelper.CreateLineStartToCharMask(control, line, charStartIndex, charLength, charProgress,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsLineFadeEnabled);
_liveStatesService.LiveStates.LyricsEffectSettings.IsLyricsLineFadeEnabled);
using var lineMask = CanvasHelper.CreateLineMask(control, line);
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontStrokeWidth, _fgFontColor);
_liveStatesService.LiveStates.LyricsStyleSettings.LyricsFontStrokeWidth, _fgFontColor);
using var effectLayer = new CanvasCommandList(control);
using var effectLayerDs = effectLayer.CreateDrawingSession();
if (line.OriginalText != line.DisplayedText && _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsTranslationHighlightAmount != 0)
if (line.OriginalText != line.DisplayedText && _liveStatesService.LiveStates.LyricsEffectSettings.LyricsTranslationHighlightAmount != 0)
{
using var translationHighlightMask = CanvasHelper.CreateTranslationHighlightMask(control, line);
using var foregroundTranslationHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, translationHighlightMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsTranslationHighlightAmount / 100.0);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsTranslationHighlightAmount / 100.0);
effectLayerDs.DrawImage(foregroundTranslationHighlightEffect);
}
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsShadowEnabled)
if (_liveStatesService.LiveStates.LyricsEffectSettings.IsLyricsShadowEnabled)
{
var shadowEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsShadowScope);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsShadowScope);
using var foregroundShadowEffect = CanvasHelper.CreateForegroundShadowEffect(foregroundFontEffect, shadowEffectMask,
_albumArtAccentColorTransition.Value, _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsShadowAmount);
_albumArtAccentColorTransition.Value, _liveStatesService.LiveStates.LyricsEffectSettings.LyricsShadowAmount);
effectLayerDs.DrawImage(foregroundShadowEffect);
}
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsGlowEffectEnabled)
if (_liveStatesService.LiveStates.LyricsEffectSettings.IsLyricsGlowEffectEnabled)
{
var blurEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsGlowEffectScope);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsGlowEffectScope);
using var foregroundBlurEffect = CanvasHelper.CreateForegroundBlurEffect(foregroundFontEffect, blurEffectMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsGlowEffectAmount);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsGlowEffectAmount);
effectLayerDs.DrawImage(foregroundBlurEffect);
}
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightAmount != 0)
if (_liveStatesService.LiveStates.LyricsEffectSettings.LyricsHighlightAmount != 0)
{
var highlightEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightScope);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsHighlightScope);
using var foregroundHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, highlightEffectMask,
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightAmount / 100.0);
_liveStatesService.LiveStates.LyricsEffectSettings.LyricsHighlightAmount / 100.0);
effectLayerDs.DrawImage(foregroundHighlightEffect);
}
@@ -295,7 +295,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (i == _playingLineIndex)
{
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsFloatAnimationEnabled)
if (_liveStatesService.LiveStates.LyricsEffectSettings.IsLyricsFloatAnimationEnabled)
{
ds.DrawImage(new DisplacementMapEffect
{
@@ -303,7 +303,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
Displacement = lineStartToCharMask,
XChannelSelect = EffectChannelSelect.Red,
YChannelSelect = EffectChannelSelect.Alpha,
Amount = _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsFloatAmount,
Amount = _liveStatesService.LiveStates.LyricsEffectSettings.LyricsFloatAmount,
});
}
else

View File

@@ -73,6 +73,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateIsLastFMTrackEnabled();
}
}
else if (message.Sender is LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.IsDynamicLyricsFontSize))
{
_isLayoutChanged = true;
}
}
}
public void Receive(PropertyChangedMessage<Color> message)
@@ -126,6 +133,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
_isAlbumArtShadowAmountChanged = true;
}
else if (message.PropertyName == nameof(AlbumArtLayoutSettings.SongInfoFontSize))
{
UpdateSongInfoFontSize();
}
}
else if (message.Sender is LyricsBackgroundSettings)
{
@@ -242,7 +253,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.CurrentLyricsDisplayType))
if (message.PropertyName == nameof(LiveStates.LyricsDisplayType))
{
_isDisplayTypeChanged = true;
}
@@ -316,7 +327,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.CurrentLyricsWindowMode))
if (message.PropertyName == nameof(LiveStates.LyricsWindowMode))
{
UpdateColorConfig();
UpdateImmersiveBackgroundOpacity();

View File

@@ -93,7 +93,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (_isCanvasWidthChanged)
{
if (_canvasWidth < 450)
if (_canvasWidth < 500)
{
_lyricsLayoutOrientation = LyricsLayoutOrientation.Vertical;
}
@@ -119,7 +119,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize * 1.05 - _titleTextFormat.FontSize - _artistTextFormat.FontSize) / 2.0, jumpTo);
_titleYTransition.StartTransition(_albumArtYTransition.TargetValue + _albumArtSize * 1.05, jumpTo);
_lyricsYTransition.StartTransition(0, jumpTo);
switch (_liveStatesService.LiveStates.CurrentLyricsDisplayType)
switch (_liveStatesService.LiveStates.LyricsDisplayType)
{
case LyricsDisplayType.AlbumArtOnly:
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
@@ -148,7 +148,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_lyricsXTransition.StartTransition(_leftMargin, jumpTo);
_albumArtXTransition.StartTransition(_leftMargin, jumpTo);
_titleXTransition.StartTransition(_leftMargin + _albumArtSize * 1.2, jumpTo);
switch (_liveStatesService.LiveStates.CurrentLyricsDisplayType)
switch (_liveStatesService.LiveStates.LyricsDisplayType)
{
case LyricsDisplayType.AlbumArtOnly:
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
@@ -326,12 +326,19 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (control == null)
return;
_lyricsTextFormat.FontSize = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontSize;
_lyricsTextFormat.FontWeight = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontWeight.ToFontWeight();
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontFamily;
if (_liveStatesService.LiveStates.LyricsStyleSettings.IsDynamicLyricsFontSize)
{
_lyricsTextFormat.FontSize = (float)Math.Clamp(Math.Min(_canvasHeight, _canvasWidth) / 10, 12, 72);
}
else
{
_lyricsTextFormat.FontSize = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsFontSize;
}
_lyricsTextFormat.FontWeight = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsFontWeight.ToFontWeight();
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsFontFamily;
_canvasYScrollTransition.SetDuration(_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollDuration / 1000.0);
_canvasYScrollTransition.SetEasingType(_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollEasingType);
_canvasYScrollTransition.SetDuration(_liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollDuration / 1000.0);
_canvasYScrollTransition.SetEasingType(_liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollEasingType);
double y = 0;
@@ -346,15 +353,15 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
line.Position = new Vector2(0, (float)y);
line.RecreateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsAlignmentType);
line.UpdateCenterPosition(_maxLyricsWidth, _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsAlignmentType);
line.RecreateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _liveStatesService.LiveStates.LyricsStyleSettings.LyricsAlignmentType);
line.UpdateCenterPosition(_maxLyricsWidth, _liveStatesService.LiveStates.LyricsStyleSettings.LyricsAlignmentType);
line.RecreateTextGeometry();
y +=
(double)line.CanvasTextLayout!.LayoutBounds.Height
/ line.CanvasTextLayout.LineCount
* (line.CanvasTextLayout.LineCount + _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsLineSpacingFactor);
* (line.CanvasTextLayout.LineCount + _liveStatesService.LiveStates.LyricsStyleSettings.LyricsLineSpacingFactor);
}
}
@@ -451,8 +458,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void UpdateColorConfig()
{
if (_liveStatesService.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode ||
_liveStatesService.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
if (_liveStatesService.LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode ||
_liveStatesService.LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode)
{
ThemeTypeSent = Helper.ColorHelper.GetElementThemeFromBackgroundColor(_environmentalColor);
}
@@ -488,8 +495,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_lyricsBgBrightnessTransition.StartTransition(brightness);
if (_liveStatesService.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode ||
_liveStatesService.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (_liveStatesService.LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode ||
_liveStatesService.LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
_adaptiveColoredFontColor = Helper.ColorHelper.GetForegroundColor(_environmentalColor);
}
@@ -505,7 +512,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
}
switch (_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsBgFontColorType)
switch (_liveStatesService.LiveStates.LyricsStyleSettings.LyricsBgFontColorType)
{
case LyricsFontColorType.AdaptiveGrayed:
_bgFontColor = _adaptiveGrayedFontColor;
@@ -514,13 +521,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_bgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
break;
case LyricsFontColorType.Custom:
_bgFontColor = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsCustomBgFontColor;
_bgFontColor = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsCustomBgFontColor;
break;
default:
break;
}
switch (_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFgFontColorType)
switch (_liveStatesService.LiveStates.LyricsStyleSettings.LyricsFgFontColorType)
{
case LyricsFontColorType.AdaptiveGrayed:
_fgFontColor = _adaptiveGrayedFontColor;
@@ -529,13 +536,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_fgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
break;
case LyricsFontColorType.Custom:
_fgFontColor = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsCustomFgFontColor;
_fgFontColor = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsCustomFgFontColor;
break;
default:
break;
}
switch (_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsStrokeFontColorType)
switch (_liveStatesService.LiveStates.LyricsStyleSettings.LyricsStrokeFontColorType)
{
case LyricsFontColorType.AdaptiveGrayed:
_strokeFontColor = _grayedEnvironmentalColor.WithBrightness(0.7);
@@ -544,7 +551,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_strokeFontColor = _environmentalColor.WithBrightness(0.7);
break;
case LyricsFontColorType.Custom:
_strokeFontColor = _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsCustomStrokeFontColor;
_strokeFontColor = _liveStatesService.LiveStates.LyricsStyleSettings.LyricsCustomStrokeFontColor;
break;
default:
break;
@@ -572,7 +579,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
double distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y);
double distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1);
line.AngleTransition.StartTransition(_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsFanLyricsEnabled
line.AngleTransition.StartTransition(_liveStatesService.LiveStates.LyricsEffectSettings.IsFanLyricsEnabled
? Math.PI
* (30.0 / 180.0)
* distanceFactor
@@ -580,12 +587,12 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
: 0
);
line.BlurAmountTransition.StartTransition(_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsBlurAmount * distanceFactor);
line.BlurAmountTransition.StartTransition(_liveStatesService.LiveStates.LyricsEffectSettings.LyricsBlurAmount * distanceFactor);
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
line.OpacityTransition.StartTransition(
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsBgFontOpacity / 100.0 -
distanceFactor * _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsBgFontOpacity / 100.0 *
(1 - _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsVerticalEdgeOpacity / 100.0));
_liveStatesService.LiveStates.LyricsStyleSettings.LyricsBgFontOpacity / 100.0 -
distanceFactor * _liveStatesService.LiveStates.LyricsStyleSettings.LyricsBgFontOpacity / 100.0 *
(1 - _liveStatesService.LiveStates.LyricsEffectSettings.LyricsVerticalEdgeOpacity / 100.0));
line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f);
double yScrollDuration;
@@ -595,8 +602,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
yScrollDuration =
_canvasYScrollTransition.DurationSeconds +
distanceFactor * (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollTopDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollTopDelay / 1000.0;
distanceFactor * (_liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollTopDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollTopDelay / 1000.0;
}
else if (lineCountDelta == 0)
{
@@ -607,8 +614,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
yScrollDuration =
_canvasYScrollTransition.DurationSeconds +
distanceFactor * (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollBottomDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollBottomDelay / 1000.0;
distanceFactor * (_liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollBottomDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.LyricsEffectSettings.LyricsScrollBottomDelay / 1000.0;
}
line.YOffsetTransition.SetEasingType(_canvasYScrollTransition.EasingType ?? EasingType.Linear);
@@ -629,7 +636,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void UpdateImmersiveBackgroundOpacity()
{
double targetOpacity;
if (_liveStatesService.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (_liveStatesService.LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
if (_isLyricsWindowLocked)
{
@@ -688,5 +695,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_isLastFMTrackEnabled = current?.IsLastFMTrackEnabled ?? false;
}
private void UpdateSongInfoFontSize()
{
_titleTextFormat.FontSize = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoFontSize;
_artistTextFormat.FontSize = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoFontSize - 2;
}
}
}

View File

@@ -190,6 +190,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
AppSettings = _settingsService.AppSettings;
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
UpdateSongInfoFontSize();
_timelineSyncThreshold = 0;

View File

@@ -5,6 +5,7 @@ using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
@@ -22,6 +23,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private readonly ILyricsSearchService _lyricsSearchService;
private readonly IMediaSessionsService _mediaSessionsService;
private readonly ISettingsService _settingsService;
private LatestOnlyTaskRunner _lyricsSearchRunner = new();
[ObservableProperty]
@@ -36,6 +38,9 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial LyricsData? LyricsData { get; set; }
[ObservableProperty]
public partial LyricsLine? SelectedLyricsLine { get; set; }
[ObservableProperty]
public partial MappedSongSearchQuery? MappedSongSearchQuery { get; set; }
@@ -63,6 +68,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private void InitMappedSongSearchQuery()
{
LyricsSearchResults.Clear();
LyricsData = null;
if (_mediaSessionsService.SongInfo != null)
{
var found = GetMappedSongSearchQueryFromSettings();
@@ -148,6 +154,18 @@ namespace BetterLyrics.WinUI3.ViewModels
SelectedLyricsSearchResult = null;
}
[RelayCommand]
private void ResetMappedTitle()
{
MappedSongSearchQuery?.MappedTitle = MappedSongSearchQuery?.OriginalTitle ?? string.Empty;
}
[RelayCommand]
private void ResetMappedArtist()
{
MappedSongSearchQuery?.MappedArtist = MappedSongSearchQuery?.OriginalArtist ?? string.Empty;
}
partial void OnSelectedLyricsSearchResultChanged(LyricsSearchResult? value)
{
MappedSongSearchQuery?.LyricsSearchProvider = value?.Provider;
@@ -162,5 +180,14 @@ namespace BetterLyrics.WinUI3.ViewModels
LyricsData = null;
}
}
partial void OnSelectedLyricsLineChanged(LyricsLine? value)
{
if (value?.StartMs == null)
{
return;
}
_mediaSessionsService.ChangePosition(value.StartMs / 1000.0);
}
}
}

View File

@@ -105,14 +105,12 @@ namespace BetterLyrics.WinUI3
[NotifyPropertyChangedRecipients]
public partial bool IsMouseWithinWindow { get; set; } = false;
[ObservableProperty] public partial Visibility AOTFlyoutItemVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial Visibility FullScreenFlyoutItemVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial Visibility LockButtonVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial Visibility DesktopFlyoutItemVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial Visibility PIPFlyoutItemVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial Visibility DockFlyoutItemVisibility { get; set; } = Visibility.Visible;
[ObservableProperty] public partial bool IsAOTFlyoutItemChecked { get; set; } = false;
[ObservableProperty] public partial bool IsFullScreenFlyoutItemChecked { get; set; } = false;
[ObservableProperty] public partial bool IsDesktopFlyoutItemChecked { get; set; } = false;
[ObservableProperty] public partial bool IsPIPFlyoutItemChecked { get; set; } = false;
@@ -126,11 +124,11 @@ namespace BetterLyrics.WinUI3
var hwnd = WindowNative.GetWindowHandle(window);
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode || LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode || LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
if (_hideWindowWhenNotPlaying && !_mediaSessionsService.IsPlaying)
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode)
{
DockModeHelper.UpdateAppBarHeight(hwnd, _dockMonitorDeviceName, 0, _dockPlacement);
}
@@ -138,7 +136,7 @@ namespace BetterLyrics.WinUI3
}
else
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode)
{
DockModeHelper.UpdateAppBarHeight(hwnd, _dockMonitorDeviceName, _dockWindowHeight, _dockPlacement);
}
@@ -176,6 +174,7 @@ namespace BetterLyrics.WinUI3
if (message.PropertyName == nameof(GeneralSettings.IgnoreFullscreenWindow))
{
_ignoreFullscreenWindow = message.NewValue;
SetIsAlwaysOnTop();
}
else if (message.PropertyName == nameof(GeneralSettings.HideWindowWhenNotPlaying))
{
@@ -183,6 +182,13 @@ namespace BetterLyrics.WinUI3
UpdateDockOrDesktopWindow();
}
}
else if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.IsAlwaysOnTop))
{
SetIsAlwaysOnTop();
}
}
}
public void Receive(PropertyChangedMessage<ElementTheme> message)
@@ -210,19 +216,40 @@ namespace BetterLyrics.WinUI3
public void InitShortcuts()
{
UpdateDesktopLockShortcut();
UpdateDesktopLockUnlockShortcut();
UpdateDesktopToggleShortcut();
UpdateDockToggleShortcut();
UpdatePictureInPictureToggleShortcut();
UpdateLyricsWindowShowHideShortcut();
}
private void UpdateDesktopLockShortcut()
private void UpdateLyricsWindowShowHideShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.DesktopLock,
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowShowOrHide,
_settingsService.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut,
() =>
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
if (window.Visible)
{
window.Hide();
}
else
{
WindowHelper.OpenWindow<LyricsWindow>();
}
}
);
}
private void UpdateDesktopLockUnlockShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.DesktopLockOrUnlock,
_settingsService.AppSettings.DesktopModeSettings.LockShortcut,
() =>
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
ToggleLockWindow();
}
@@ -236,8 +263,8 @@ namespace BetterLyrics.WinUI3
_settingsService.AppSettings.DesktopModeSettings.ToggleShortcut,
() =>
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode ||
LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.StandardMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode ||
LiveStates.LyricsWindowMode == LyricsWindowMode.StandardMode)
{
ToggleDesktopMode();
}
@@ -251,8 +278,8 @@ namespace BetterLyrics.WinUI3
_settingsService.AppSettings.DockModeSettings.ToggleShortcut,
() =>
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode ||
LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.StandardMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode ||
LiveStates.LyricsWindowMode == LyricsWindowMode.StandardMode)
{
ToggleDockMode();
}
@@ -266,8 +293,8 @@ namespace BetterLyrics.WinUI3
_settingsService.AppSettings.PictureInPictureModeSettings.ToggleShortcut,
() =>
{
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.PictureInPictureMode ||
LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.StandardMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.PictureInPictureMode ||
LiveStates.LyricsWindowMode == LyricsWindowMode.StandardMode)
{
TogglePictureInPictureMode();
}
@@ -277,14 +304,14 @@ namespace BetterLyrics.WinUI3
private void SetFullscreenTitleBarControlsStatus()
{
AOTFlyoutItemVisibility = LockButtonVisibility = DesktopFlyoutItemVisibility = PIPFlyoutItemVisibility = DockFlyoutItemVisibility = Visibility.Collapsed;
LockButtonVisibility = DesktopFlyoutItemVisibility = PIPFlyoutItemVisibility = DockFlyoutItemVisibility = Visibility.Collapsed;
IsFullScreenFlyoutItemChecked = true;
IsImmersiveMode = true;
}
private void SetPIPModeTitleBarControlsStatus()
{
AOTFlyoutItemVisibility = DesktopFlyoutItemVisibility = FullScreenFlyoutItemVisibility = DockFlyoutItemVisibility = LockButtonVisibility = Visibility.Collapsed;
DesktopFlyoutItemVisibility = FullScreenFlyoutItemVisibility = DockFlyoutItemVisibility = LockButtonVisibility = Visibility.Collapsed;
IsImmersiveMode = true;
IsPIPFlyoutItemChecked = true;
}
@@ -296,7 +323,7 @@ namespace BetterLyrics.WinUI3
var overlappedPresenter = (OverlappedPresenter)window.AppWindow.Presenter;
overlappedPresenter.IsMinimizable = overlappedPresenter.IsMaximizable = false;
AOTFlyoutItemVisibility = DesktopFlyoutItemVisibility = LockButtonVisibility = FullScreenFlyoutItemVisibility = PIPFlyoutItemVisibility = Visibility.Collapsed;
DesktopFlyoutItemVisibility = LockButtonVisibility = FullScreenFlyoutItemVisibility = PIPFlyoutItemVisibility = Visibility.Collapsed;
IsImmersiveMode = true;
IsDockFlyoutItemChecked = true;
}
@@ -308,7 +335,7 @@ namespace BetterLyrics.WinUI3
var overlappedPresenter = (OverlappedPresenter)window.AppWindow.Presenter;
overlappedPresenter.IsMinimizable = overlappedPresenter.IsMaximizable = false;
DockFlyoutItemVisibility = AOTFlyoutItemVisibility = FullScreenFlyoutItemVisibility = PIPFlyoutItemVisibility = Visibility.Collapsed;
DockFlyoutItemVisibility = FullScreenFlyoutItemVisibility = PIPFlyoutItemVisibility = Visibility.Collapsed;
LockButtonVisibility = Visibility.Visible;
IsDesktopFlyoutItemChecked = true;
}
@@ -320,10 +347,9 @@ namespace BetterLyrics.WinUI3
var overlappedPresenter = (OverlappedPresenter)window.AppWindow.Presenter;
overlappedPresenter.IsMinimizable = overlappedPresenter.IsMaximizable = true;
AOTFlyoutItemVisibility = DesktopFlyoutItemVisibility = DockFlyoutItemVisibility = PIPFlyoutItemVisibility = FullScreenFlyoutItemVisibility = Visibility.Visible;
DesktopFlyoutItemVisibility = DockFlyoutItemVisibility = PIPFlyoutItemVisibility = FullScreenFlyoutItemVisibility = Visibility.Visible;
LockButtonVisibility = Visibility.Collapsed;
IsFullScreenFlyoutItemChecked = IsDesktopFlyoutItemChecked = IsDockFlyoutItemChecked = IsPIPFlyoutItemChecked = false;
IsAOTFlyoutItemChecked = overlappedPresenter.IsAlwaysOnTop;
IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
}
@@ -339,7 +365,7 @@ namespace BetterLyrics.WinUI3
{
_dispatcherQueueTimer.Debounce(() =>
{
if ((LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode || LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode) && _ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
if ((LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode || LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode) && _ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsAlwaysOnTop = true;
}
@@ -359,7 +385,7 @@ namespace BetterLyrics.WinUI3
public void UpdateAccentColor(nint hwnd)
{
WindowPixelSampleMode mode = LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode();
WindowPixelSampleMode mode = LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode();
ActivatedWindowAccentColor = ColorHelper.GetAccentColor(hwnd, _settingsService.AppSettings.DockModeSettings.DockMonitorDeviceName, mode).ToColor();
}
@@ -405,7 +431,7 @@ namespace BetterLyrics.WinUI3
StopWatchWindowColorChange();
LiveStates.ToggleLyricsWindowMode(LyricsWindowMode.DesktopMode);
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DesktopMode)
{
DesktopModeHelper.Enable(window);
StartWatchWindowColorChange();
@@ -424,6 +450,7 @@ namespace BetterLyrics.WinUI3
DesktopModeHelper.Disable(window);
SetStandardModeTitleBarControlsStatus();
}
SetIsAlwaysOnTop();
}
public void ToggleDockMode()
@@ -434,7 +461,7 @@ namespace BetterLyrics.WinUI3
StopWatchWindowColorChange();
LiveStates.ToggleLyricsWindowMode(LyricsWindowMode.DockMode);
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.DockMode)
{
window.Restore();
DockModeHelper.Enable(window, _dockMonitorDeviceName, _dockWindowHeight, _dockPlacement);
@@ -448,6 +475,7 @@ namespace BetterLyrics.WinUI3
}
UpdateDockOrDesktopWindow();
SetIsAlwaysOnTop();
}
public void TogglePictureInPictureMode()
@@ -456,7 +484,7 @@ namespace BetterLyrics.WinUI3
if (window == null) return;
LiveStates.ToggleLyricsWindowMode(LyricsWindowMode.PictureInPictureMode);
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.PictureInPictureMode)
if (LiveStates.LyricsWindowMode == LyricsWindowMode.PictureInPictureMode)
{
window.AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
window.AppWindow.Move(AppSettings.PictureInPictureModeSettings.WindowPosition.ToPointInt32());
@@ -467,17 +495,17 @@ namespace BetterLyrics.WinUI3
window.AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
SetStandardModeTitleBarControlsStatus();
}
SetIsAlwaysOnTop();
}
public void ToggleAlwaysOnTop()
public void SetIsAlwaysOnTop()
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
if (window.AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsAlwaysOnTop = !presenter.IsAlwaysOnTop;
IsAOTFlyoutItemChecked = presenter.IsAlwaysOnTop;
presenter.IsAlwaysOnTop = _liveStatesService.LiveStates.IsAlwaysOnTop;
}
}
@@ -537,7 +565,7 @@ namespace BetterLyrics.WinUI3
{
if (message.PropertyName == nameof(DesktopModeSettings.LockShortcut))
{
UpdateDesktopLockShortcut();
UpdateDesktopLockUnlockShortcut();
}
}
else if (message.Sender is DockModeSettings)
@@ -561,6 +589,13 @@ namespace BetterLyrics.WinUI3
UpdateDesktopToggleShortcut();
}
}
else if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.ShowOrHideLyricsWindowShortcut))
{
UpdateLyricsWindowShowHideShortcut();
}
}
}
}
}

View File

@@ -56,7 +56,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
[ObservableProperty]
public partial int SelectedTargetLanguageIndex { get; set; }
[ObservableProperty]
public partial string AppleMusicMediaUserToken { get; set; }
public PlaybackSettingsControlViewModel(
ISettingsService settingsService,
@@ -77,6 +81,10 @@ namespace BetterLyrics.WinUI3.ViewModels
AppSettings = _settingsService.AppSettings;
AppSettings.MediaSourceProvidersInfo.CollectionChanged += MediaSourceProvidersInfo_CollectionChanged;
AppleMusicMediaUserToken = PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey) ?? "";
SelectedTargetLanguageIndex = LanguageHelper.SupportedTargetLanguages.ToList().FindIndex(x => x.Code == AppSettings.TranslationSettings.SelectedTargetLanguageCode);
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
LastFMUser = _lastFMService.User;
@@ -103,7 +111,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
var current = AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.SourceAppUserModelId)?.FirstOrDefault();
var current = AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.PlayerId)?.FirstOrDefault();
if (_mediaSessionsService.Position.TotalSeconds <= 1 && current?.ResetPositionOffsetOnSongChanged == true)
{
current.PositionOffset = 0;
@@ -123,8 +131,8 @@ namespace BetterLyrics.WinUI3.ViewModels
{
try
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, new System.Threading.CancellationToken());
string result = await _libreTranslateService.TranslateTextAsync(
"Hello, world!", AppSettings.TranslationSettings.SelectedTargetLanguageCode, new System.Threading.CancellationToken());
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
@@ -184,6 +192,13 @@ namespace BetterLyrics.WinUI3.ViewModels
});
}
[RelayCommand]
private void SaveAppleMusicMediaUserToken()
{
PasswordVaultHelper.Save(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey, AppleMusicMediaUserToken);
_mediaSessionsService.UpdateLyrics();
}
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
{
if (message.Sender is MediaSessionsService)
@@ -205,5 +220,10 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
partial void OnSelectedTargetLanguageIndexChanged(int value)
{
AppSettings.TranslationSettings.SelectedTargetLanguageCode = LanguageHelper.SupportedTargetLanguages[value].Code;
}
}
}

View File

@@ -18,6 +18,7 @@ using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -29,6 +30,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class SettingsPageViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly IMediaSessionsService _mediaSessionsService;
public string Version { get; set; } = MetadataHelper.AppVersion;
@@ -42,16 +44,17 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; } = "App";
public SettingsPageViewModel(ISettingsService settingsService)
public SettingsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
AppSettings = _settingsService.AppSettings;
}
[RelayCommand]
private async Task LaunchProjectGitHubPageAsync()
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GithubUrl));
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GitHubUrl));
}
[RelayCommand]

View File

@@ -29,8 +29,8 @@
x:Uid="MainPageNoMusicPlaying"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{x:Bind ViewModel.LiveStates.CurrentLyricsStyleSettings.LyricsFontFamily, Mode=OneWay}"
FontSize="{x:Bind ViewModel.LiveStates.CurrentLyricsStyleSettings.LyricsFontSize, Mode=OneWay}" />
FontFamily="{x:Bind ViewModel.LiveStates.LyricsStyleSettings.LyricsFontFamily, Mode=OneWay}"
FontSize="{x:Bind ViewModel.LiveStates.LyricsStyleSettings.LyricsFontSize, Mode=OneWay}" />
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
@@ -245,14 +245,6 @@
<ToolTipService.ToolTip>
<ToolTip x:Uid="LyricsPageLyricsSearchButtonToolTip" />
</ToolTipService.ToolTip>
<Button.ContextFlyout>
<Flyout
x:Name="LyricsSearchFlyout"
Closed="LyricsSearchFlyout_Closed"
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False" />
</Button.ContextFlyout>
</Button>
<!-- Playback shortcut settings -->
@@ -297,9 +289,40 @@
Maximum="{x:Bind ViewModel.SongDurationSeconds, Mode=OneWay}"
Minimum="0"
Style="{StaticResource GhostSliderStyle}"
Tapped="TimelineSliderOverlay_Tapped"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"
Value="{x:Bind ViewModel.TimelinePositionSeconds, Mode=OneWay}" />
<Grid
x:Name="TimelineSliderLyricsLineInfo"
Margin="0,-48,0,0"
Padding="8,4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{ThemeResource SolidBackgroundFillColorQuarternaryBrush}"
CornerRadius="6"
Opacity="{x:Bind ViewModel.TimelineSliderThumbOpacity, Mode=OneWay}">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<Grid.TranslationTransition>
<Vector3Transition />
</Grid.TranslationTransition>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock
Margin="0,0,0,2"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
<TextBlock Margin="0,0,0,2" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.DisplayedText, Mode=OneWay}" />
</StackPanel>
</Grid>
<Grid
Height="32"
Margin="0,-32,0,0"
VerticalAlignment="Top"
Background="Transparent"
PointerEntered="TimelineSliderOverlay_PointerEntered"
PointerExited="TimelineSliderOverlay_PointerExited"
PointerMoved="TimelineSliderOverlay_PointerMoved"
PointerPressed="TimelineSliderOverlay_PointerPressed" />
</Grid>
</Grid>
@@ -333,7 +356,7 @@
<Flyout x:Name="BottomCommandFlyout" ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="MinWidth" Value="450" />
<Setter Property="MinWidth" Value="500" />
<Setter Property="MinHeight" Value="100" />
<Setter Property="CornerRadius" Value="12" />
</Style>

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Controls;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
@@ -10,6 +11,7 @@ using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -60,7 +62,7 @@ namespace BetterLyrics.WinUI3.Views
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width < 450 || e.NewSize.Height < 100)
if (e.NewSize.Width < 500 || e.NewSize.Height < 100)
{
if (BottomCommandGrid.Children.Count != 0)
{
@@ -110,11 +112,6 @@ namespace BetterLyrics.WinUI3.Views
}
}
private void TimelineSliderOverlay_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
{
_mediaSessionsService.ChangePosition(TimelineSlider.Value);
}
private void PlaybackSettingsFlyout_Closed(object sender, object e)
{
PlaybackSettingsFlyout.Content = null;
@@ -137,17 +134,43 @@ namespace BetterLyrics.WinUI3.Views
private void LyricsSearchShortcutButton_Click(object sender, RoutedEventArgs e)
{
LyricsSearchFlyout.Content = new LyricsSearchControl
{
MaxHeight = 500,
MaxWidth = 850,
};
LyricsSearchFlyout.ShowAt(BottomRightCommandStackPanel);
WindowHelper.OpenWindow<LyricsSearchWindow>();
}
private void LyricsSearchFlyout_Closed(object sender, object e)
private void TimelineSliderOverlay_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
LyricsSearchFlyout.Content = null;
var grid = (Grid)sender;
var pos = e.GetCurrentPoint(grid).Position;
var ratio = pos.X / grid.ActualWidth;
_mediaSessionsService.ChangePosition(TimelineSlider.Maximum * ratio);
}
private void TimelineSliderOverlay_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
float targetX;
var grid = (Grid)sender;
var pos = e.GetCurrentPoint(grid).Position;
var ratio = pos.X / grid.ActualWidth;
ViewModel.TimelineSliderThumbSeconds = TimelineSlider.Maximum * ratio;
if (pos.X + TimelineSliderLyricsLineInfo.ActualWidth > grid.ActualWidth)
{
targetX = (float)(grid.ActualWidth - TimelineSliderLyricsLineInfo.ActualWidth);
}
else
{
targetX = (float)pos.X;
}
TimelineSliderLyricsLineInfo.Translation = new Vector3(targetX, 0, 0);
}
private void TimelineSliderOverlay_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
ViewModel.TimelineSliderThumbOpacity = 0.7f;
}
private void TimelineSliderOverlay_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
ViewModel.TimelineSliderThumbOpacity = 0f;
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="BetterLyrics.WinUI3.Views.LyricsSearchWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:BetterLyrics.WinUI3.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:CommunityToolkit.WinUI.Media"
mc:Ignorable="d">
<Grid Background="{ThemeResource SystemControlAcrylicWindowBrush}" Opacity="0.85">
<controls:LyricsSearchControl />
</Grid>
</Window>

View File

@@ -0,0 +1,47 @@
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Views
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class LyricsSearchWindow : Window
{
public LyricsSearchWindow()
{
InitializeComponent();
Title = App.ResourceLoader?.GetString("LyricsSearchPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();
AppWindow.Closing += AppWindow_Closing;
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(Enums.BackdropType.Transparent);
}
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHelper.CloseWindow<LyricsSearchWindow>();
}
}
}

View File

@@ -25,6 +25,7 @@
<!-- Top command -->
<Grid
x:Name="TopCommandGrid"
Margin="6"
VerticalAlignment="Top"
Background="Transparent"
Opacity="{x:Bind ViewModel.TopCommandGridOpacity, Mode=OneWay}"
@@ -75,14 +76,6 @@
</ToolTipService.ToolTip>
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem
x:Name="AOTFlyoutItem"
x:Uid="BaseWindowAOTFlyoutItem"
Click="AOTFlyoutItem_Click"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE718;}"
IsChecked="{x:Bind ViewModel.IsAOTFlyoutItemChecked, Mode=OneWay}"
Visibility="{x:Bind ViewModel.AOTFlyoutItemVisibility, Mode=OneWay}" />
<ToggleMenuFlyoutItem
x:Name="FullScreenFlyoutItem"
x:Uid="BaseWindowFullScreenFlyoutItem"

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
@@ -19,6 +20,7 @@ namespace BetterLyrics.WinUI3.Views
public sealed partial class LyricsWindow : Window
{
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private readonly WindowMessageMonitor _wmm;
private bool _autoSelectLyricsModeOnRunning = true;
@@ -101,11 +103,11 @@ namespace BetterLyrics.WinUI3.Views
break;
}
_autoSelectLyricsModeOnRunning = false;
}
private void AOTFlyoutItem_Click(object sender, RoutedEventArgs e)
{
ViewModel.ToggleAlwaysOnTop();
var size = AppWindow.Size;
var rect = AppWindow.Position;
_liveStatesService.LiveStates.LyricsWindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
}
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
@@ -117,13 +119,15 @@ namespace BetterLyrics.WinUI3.Views
var size = AppWindow.Size;
var rect = AppWindow.Position;
_liveStatesService.LiveStates.LyricsWindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
if (rect.X < 0 && rect.Y < 0 && rect.X + size.Width < 0 && rect.Y + size.Height < 0)
{
return;
}
else
{
switch (ViewModel.LiveStates.CurrentLyricsWindowMode)
switch (ViewModel.LiveStates.LyricsWindowMode)
{
case LyricsWindowMode.StandardMode:
if (AppWindow.Presenter is OverlappedPresenter overlappedPresenter)

View File

@@ -354,7 +354,7 @@
<StackPanel>
<TextBlock Text="{Binding Track.Title}" TextWrapping="Wrap" />
<TextBlock
Foreground="{StaticResource TextFillColorSecondaryBrush}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Track.Artist}"
TextWrapping="Wrap" />
</StackPanel>

View File

@@ -3,6 +3,7 @@
x:Class="BetterLyrics.WinUI3.Views.SettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:const="using:BetterLyrics.WinUI3.Constants"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
@@ -120,41 +121,104 @@
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard Header="BetterLyrics" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Logo.png}">
<controls:SettingsCard.Description>
<RichTextBlock>
<Paragraph>
<Run x:Uid="SettingsPageVersion" />
<Run Text="{x:Bind ViewModel.Version, Mode=OneWay}" />
</Paragraph>
</RichTextBlock>
</controls:SettingsCard.Description>
</controls:SettingsCard>
<controls:SettingsExpander
Header="BetterLyrics"
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Logo.png}"
IsExpanded="True">
<controls:SettingsExpander.Description>
<HyperlinkButton Content="by Zhe Fang" NavigateUri="https://github.com/jayfunc" />
</controls:SettingsExpander.Description>
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}">
<Paragraph>
<Run x:Uid="SettingsPageVersion" />
<Run Text="{x:Bind ViewModel.Version, Mode=OneWay}" />
</Paragraph>
</RichTextBlock>
<controls:SettingsExpander.Items>
<controls:SettingsCard
x:Uid="SettingsPageGitHub"
ActionIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8A7;}"
Command="{x:Bind ViewModel.LaunchProjectGitHubPageCommand}"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE943;}"
IsClickEnabled="True" />
<controls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHubUrl}" />
<HyperlinkButton x:Uid="SettingsPageFAQ" NavigateUri="{x:Bind const:Link.FAQUrl}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageFAQ" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE897;}">
<HyperlinkButton Content="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md" NavigateUri="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md" />
</controls:SettingsCard>
<controls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
<StackPanel Spacing="6">
<TextBlock x:Uid="SetingsPageFeedback" />
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroupUrl}" />
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.DiscordUrl}" />
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.TelegramUrl}" />
</StackPanel>
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageQQGroup" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/QQ.png}">
<Button x:Uid="SettingsPageJoinNowButton" Click="QQGroupButton_Click" />
</controls:SettingsCard>
<controls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
<StackPanel Spacing="6">
<TextBlock x:Uid="SetingsPageDonation" />
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="https://buymeacoffee.com/founchoo" />
<HyperlinkButton Content="PayPal" NavigateUri="https://paypal.me/zhefangpay" />
<Button
Content="支付宝"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="0" />
</Style>
</Flyout.FlyoutPresenterStyle>
<Image Height="300" Source="/Assets/Alipay.jpg" />
</Flyout>
</Button.Flyout>
</Button>
<Button
Content="微信"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="0" />
</Style>
</Flyout.FlyoutPresenterStyle>
<Image Height="300" Source="/Assets/WeChatReward.png" />
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="*" />
<TextBlock
x:Uid="SetingsPageThanks"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDiscord" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Discord.png}">
<Button x:Uid="SettingsPageJoinNowButton" Click="DiscodGroupButton_Click" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTelegram" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Telegram.png}">
<Button x:Uid="SettingsPageJoinNowButton" Click="TelegramGroupButton_Click" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
<controls:SettingsExpander.ItemsHeader>
<InfoBar
BorderThickness="0"
CornerRadius="0"
IsClosable="False"
IsOpen="True"
Severity="Warning">
<TextBlock
x:Uid="SettingsPageDisclaimer"
Margin="12"
Foreground="{ThemeResource SystemFillColorCautionBrush}"
TextWrapping="Wrap" />
</InfoBar>
</controls:SettingsExpander.ItemsHeader>
</controls:SettingsExpander>
</StackPanel>
</Grid>

View File

@@ -29,20 +29,5 @@ namespace BetterLyrics.WinUI3.Views
{
ViewModel.NavViewSelectedItemTag = (args.SelectedItem as NavigationViewItem)!.Tag;
}
private void QQGroupButton_Click(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri(Constants.Link.QQGroupUrl));
}
private void DiscodGroupButton_Click(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri(Constants.Link.DiscordUrl));
}
private void TelegramGroupButton_Click(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri(Constants.Link.TelegramUrl));
}
}
}

BIN
Donate/Alipay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
Donate/WeChatReward.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@@ -1,14 +1,34 @@
## FAQ has been moved
[Click here to visit](https://github.com/jayfunc/BetterLyrics?tab=readme-ov-file#faq)
### Where I can find the logs?
`C:\Users\%USERNAME%\AppData\Local\Packages\37412.BetterLyrics_rd1g0rsrrtxw8\LocalCache\logs`
## FAQ 现已迁移
[点此访问](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-CN.md#faq)
### How to install ".msixbundle" package? (for test package only)
[See this doc](https://github.com/jayfunc/BetterLyrics/blob/dev/How2Install/How2Install.md)
## FAQ 已移至新位置
[點此前往](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-TW.md#faq)
### Lyrics are moving back and forth constantly, how to fix it?
![](Snipaste_2025-08-22_14-59-53.png)
## FAQ は移動しました
[こちらをクリック](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ja.md#faq)
Go to Settings > Playback sources > Disable "Lyrics timeline sync" or increase "Lyrics timeline sync threshold"
## FAQ가 이동되었습니다
[여기를 클릭하세요](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ko.md#faq)
### Wrong lyrics are shown, how to fix it?
![](Snipaste_2025-08-22_14-47-21.png)
Open search panel to manually search for the correct lyrics.
### Playback control panel is not showing in dock mode, how to fix it?
![](Snipaste_2025-08-22_14-50-16.png)
Hover over the bottom of the lyrics window and click on the white line to show the playback control panel.
### How to lock/unlock the lyrics window in desktop mode?
![](Snipaste_2025-08-22_14-52-53.png)
![](Snipaste_2025-08-22_14-53-21.png)
Alternatively, you can also use the shortcut `Ctrl+Alt+U` (default) to toggle lock/unlock.
![](Snipaste_2025-08-22_14-53-58.png)
You can change the shortcut in Settings > App appearance and behavior > Unlock and lock shortcut keys
### How to enable/disable immersive mode?
![](Snipaste_2025-08-22_14-58-44.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 KiB

Some files were not shown because too many files have changed in this diff Show More