Compare commits

...

41 Commits

Author SHA1 Message Date
Zhe Fang
1eb1d2d72b feat: track stop; chores: ui/ux adjustments, bump version code. 2025-11-08 11:41:59 -05:00
Zhe Fang
252b7e4b25 chores: bump version code 2025-11-08 08:45:27 -05:00
Zhe Fang
bebcfa7819 fix: thread access issue related to ISettingsService 2025-11-08 08:21:31 -05:00
Zhe Fang
073ddfcaee fix: player fail to pass thumbnails 2025-11-07 21:06:37 -05:00
Zhe Fang
1775d947f9 chores: bump version code 2025-11-07 16:26:05 -05:00
Zhe Fang
a5d1831717 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-11-07 15:51:23 -05:00
Zhe Fang
d4a924accf feat: support presistent play queue and index, add settings item: auto open music gallery and auto play music; fix: auto scroll to playing track in play queue; 2025-11-07 15:51:21 -05:00
Zhe Fang
5d65314522 更新 README.md 2025-11-07 07:00:25 -05:00
Zhe Fang
0684069e52 更新 README.CN.md 2025-11-07 06:59:24 -05:00
Zhe Fang
fea617ff98 chores: edit readme 2025-11-06 17:17:44 -05:00
Zhe Fang
4b33776340 chores: update readme 2025-11-06 17:12:56 -05:00
Zhe Fang
ea73d7ed3b chores: add all deps link in readme 2025-11-06 14:23:40 -05:00
Zhe Fang
b8fd51cbe8 chores: bump version 2025-11-06 11:45:51 -05:00
Zhe Fang
004079a7ee feat: support create and import playlist; fix text in textbox overflow issue in search window; change in-app push style 2025-11-06 11:27:59 -05:00
Zhe Fang
30d1ccf67f chores: fix ui, add lyrics search window entry in system tray 2025-11-04 09:33:01 -05:00
Zhe Fang
a24885c277 chores: update readme 2025-11-03 19:37:46 -05:00
Zhe Fang
a207224bbf chores: add zh edition 2025-11-03 12:36:16 -05:00
Zhe Fang
2c80e0815a Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-11-03 11:31:09 -05:00
Zhe Fang
7f65b4addb fix: handle device lost event 2025-11-03 11:31:07 -05:00
Zhe Fang
210df4dd61 Update README.md 2025-11-02 21:33:45 -05:00
Zhe Fang
3c82908890 Update README.md 2025-11-02 21:01:01 -05:00
Zhe Fang
597cc1ec9b Update README.md 2025-11-02 20:59:29 -05:00
Zhe Fang
e354e29ec1 chores: update version code 2025-11-02 19:10:46 -05:00
Zhe Fang
dd5fb91764 chores: add delay when automatically show and hide window 2025-11-02 17:16:40 -05:00
Zhe Fang
041459e564 chores: remove gain 2025-11-02 11:26:27 -05:00
Zhe Fang
7de813b645 fix: audio graph 2025-11-02 11:05:13 -05:00
Zhe Fang
838d663834 chores: add null check in SearchQQNeteaseKugouAsync; fix search page result display issue 2025-11-02 08:47:49 -05:00
Zhe Fang
9add190383 chores: add logo for some music players 2025-11-01 21:20:47 -04:00
Zhe Fang
fa58360884 chores: add pic in faq 2025-11-01 19:27:08 -04:00
Zhe Fang
4dd032655c fix: translation issue caused by mapping 2025-11-01 19:13:10 -04:00
Zhe Fang
80984218ec add: folder filter in music gallery page; chores: add pic screenshoots; 2025-11-01 15:53:15 -04:00
Zhe Fang
df45026638 chores: update readme 2025-10-31 21:04:29 -04:00
Zhe Fang
aafc5c9fb5 chores: update readme 2025-10-31 20:59:24 -04:00
Zhe Fang
c5fccfbb63 Update README.md 2025-10-31 20:44:57 -04:00
Zhe Fang
42f03b4291 chores: relayout pics in readme 2025-10-31 19:30:25 -04:00
Zhe Fang
3339bb0d41 chores: add screenshots in readme 2025-10-31 19:26:47 -04:00
Zhe Fang
7d60f29845 chores: fix kugou translation issue; fix audio graph issue; fix lyrics status switching between different monitors issue 2025-10-31 17:10:10 -04:00
Zhe Fang
bd7e93f3e6 chores: fix i18n issue 2025-10-30 21:29:08 -04:00
Zhe Fang
c37c6ab489 Update README.md 2025-10-30 21:24:50 -04:00
Zhe Fang
125794c44f Update README.md 2025-10-30 21:22:42 -04:00
Zhe Fang
f5bdb3a431 update readme and add snow flake effect 2025-10-30 21:20:06 -04:00
118 changed files with 3132 additions and 1630 deletions

View File

@@ -12,7 +12,7 @@
<Identity
Name="37412.BetterLyrics"
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
Version="1.0.89.0" />
Version="1.0.99.0" />
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -13,8 +13,8 @@
<ResourceDictionary.MergedDictionaries>
<!-- Merged dictionaries here -->
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.SettingsControls/SettingsExpander/SettingsExpander.xaml" />
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
<ResourceDictionary Source="ms-appx:///DevWinUI.Controls/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Theme -->
@@ -59,14 +59,21 @@
<converter:ShortcutToStringConverter x:Key="ShortcutToStringConverter" />
<converter:BoolNegationToVisibilityConverter x:Key="BoolNegationToVisibilityConverter" />
<converter:BoolToOpacityConverter x:Key="BoolToOpacityConverter" />
<converter:BoolToPartialOpacityConverter x:Key="BoolToPartialOpacityConverter" />
<converter:BoolNegationToOpacityConverter x:Key="BoolNegationToOpacityConverter" />
<converter:RectToMarginConverter x:Key="RectToMarginConverter" />
<converter:LanguageCodeToDisplayedNameConverter x:Key="LanguageCodeToDisplayedNameConverter" />
<converter:ByteArrayToImageConverter x:Key="ByteArrayToImageConverter" />
<converter:DisplayLanguageCodeToIndexConverter x:Key="DisplayLanguageCodeToIndexConverter" />
<converter:PathToParentFolderConverter x:Key="PathToParentFolderConverter" />
<converter:TrackToLyricsConverter x:Key="TrackToLyricsConverter" />
<converter:IntToBoolConverter x:Key="IntToBoolConverter" />
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
<converters:CollectionVisibilityConverter x:Key="CollectionVisibilityConverter" />
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
@@ -302,8 +309,12 @@
</Setter>
</Style>
<Style x:Key="ListViewStretchedItemContainerStyle" TargetType="ListViewItem">
<Style
x:Key="ListViewStretchedItemContainerStyle"
BasedOn="{StaticResource DefaultListViewItemStyle}"
TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
@@ -331,6 +342,16 @@
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
</Style>
<Style
x:Key="FlyoutGhostStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
</Style>
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />

View File

@@ -9,6 +9,7 @@ using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.ViewModels;
@@ -22,7 +23,6 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.ApplicationModel.Resources;
using Serilog;
using ShadowViewer.Controls;
using System;
using System.Diagnostics;
using System.Linq;
@@ -39,12 +39,6 @@ namespace BetterLyrics.WinUI3
private readonly ILogger<App> _logger;
public static new App Current => (App)Application.Current;
public static DispatcherQueue? DispatcherQueue { get; private set; }
public static DispatcherQueueTimer? DispatcherQueueTimer { get; private set; }
public static ResourceLoader? ResourceLoader { get; private set; }
public NotificationPanel? LyricsWindowNotificationPanel { get; set; }
public NotificationPanel? SettingsWindowNotificationPanel { get; set; }
private static Mutex? _instanceMutex;
@@ -52,10 +46,6 @@ namespace BetterLyrics.WinUI3
{
this.InitializeComponent();
DispatcherQueue = DispatcherQueue.GetForCurrentThread();
DispatcherQueueTimer = DispatcherQueue.CreateTimer();
ResourceLoader = new ResourceLoader();
EnsureSingleInstance();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@@ -72,12 +62,11 @@ namespace BetterLyrics.WinUI3
private void EnsureSingleInstance()
{
bool createdNew;
_instanceMutex = new Mutex(true, Constants.App.AppName, out createdNew);
_instanceMutex = new Mutex(true, Constants.App.AppName, out bool createdNew);
if (!createdNew)
{
User32.MessageBox(HWND.NULL, ResourceLoader!.GetString("TryRunMultipleInstance"), null, User32.MB_FLAGS.MB_APPLMODAL);
User32.MessageBox(HWND.NULL, new ResourceLoader().GetString("TryRunMultipleInstance"), null, User32.MB_FLAGS.MB_APPLMODAL);
Environment.Exit(0);
}
}
@@ -85,6 +74,10 @@ namespace BetterLyrics.WinUI3
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
{
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
}
}
private static void ConfigureServices()
@@ -111,6 +104,7 @@ namespace BetterLyrics.WinUI3
.AddSingleton<ILibWatcherService, LibWatcherService>()
.AddSingleton<ITranslateService, TranslateService>()
.AddSingleton<ILastFMService, LastFMService>()
.AddSingleton<IResourceService, ResourceService>()
// ViewModels
.AddSingleton<AppSettingsControlViewModel>()
.AddSingleton<PlaybackSettingsControlViewModel>()

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -50,22 +50,19 @@
<ItemGroup>
<PackageReference Include="3v.EvtSource" Version="2.0.0" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251021-build.2365" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250703-build.2173" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250703-build.2173" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.MetadataControl" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
<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="CommunityToolkit.WinUI.Triggers" Version="8.2.250402" />
<PackageReference Include="csharp-kana" Version="1.0.2" />
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
<PackageReference Include="DevWinUI.Controls" Version="9.4.2" />
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
@@ -73,7 +70,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251003001" />
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
@@ -81,18 +78,16 @@
<PackageReference Include="NTextCat" Version="0.3.65" />
<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" />
<PackageReference Include="System.Drawing.Common" Version="9.0.10" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.10" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="Vanara.PInvoke.CoreAudio" Version="4.2.1" />
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.2.1" />
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="4.2.1" />
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.2.1" />
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
<PackageReference Include="WinUIEx" Version="2.9.0" />
<PackageReference Include="z440.atl.core" Version="7.5.0" />
<PackageReference Include="z440.atl.core" Version="7.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ColorThief.WinUI3\ColorThief.WinUI3.csproj" />
@@ -112,12 +107,17 @@
<ItemGroup>
<TrimmerRootAssembly Include="TagLibSharp" />
<TrimmerRootAssembly Include="NAudio.Wasapi" />
<TrimmerRootAssembly Include="Vanara.PInvoke.CoreAudio" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\AIMP.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\AlbumArtPlaceholder.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\AMLLPlayer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\AppleMusic.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -142,6 +142,9 @@
<Content Update="Assets\foobar2000.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\HyPlayer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\iTunes.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -181,6 +184,9 @@
<Content Update="Assets\Page.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\PlanetMusic.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\PotPlayer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -308,6 +314,9 @@
<Generator></Generator>
</PRIResource>
</ItemGroup>
<ItemGroup>
<Folder Include="TemplateSelector\" />
</ItemGroup>
<!-- Publish Properties -->
<PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>

View File

@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Constants
{
public const string GitHubUrl = "https://github.com/jayfunc/BetterLyrics";
public const string WikiUrl = "https://github.com/jayfunc/BetterLyrics/wiki";
public const string AppleMusicCfgUrl = $"{WikiUrl}/Lyrics-provider-configuration#apple-music";
public const string AppleMusicCfgUrl = $"{WikiUrl}/%5BEN%5D-Lyrics-provider-configuration#apple-music";
public const string FAQUrl = $"{GitHubUrl}/blob/dev/FAQ/index.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";

View File

@@ -7,6 +7,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dev="using:DevWinUI"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
@@ -17,16 +18,16 @@
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageAlbumArtSize"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE744;}"
IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAutoAdjust">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.AutoAlbumArtSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.AutoAlbumArtSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.AutoAlbumArtSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Frequency="2"
Maximum="800"
@@ -34,49 +35,49 @@
ResetButtonVisibility="Collapsed"
Unit="px"
Value="{x:Bind AlbumArtLayoutSettings.AlbumArtSize, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEA3A;}">
<dev:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEA3A;}">
<local:ExtendedSlider
Default="12"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF5EF;}">
<dev:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF5EF;}">
<local:ExtendedSlider
Default="12"
Frequency="1"
Maximum="64"
Minimum="0"
Value="{x:Bind AlbumArtLayoutSettings.CoverImageShadowAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<dev:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageSongInfoLeft" />
<ComboBoxItem x:Uid="SettingsPageSongInfoCenter" />
<ComboBoxItem x:Uid="SettingsPageSongInfoRight" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLyricsFontSize"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8E9;}"
IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAutoAdjust">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.IsAutoSongInfoFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard>
</dev:SettingsCard>
<dev:SettingsCard>
<local:ExtendedSlider
Default="18"
Frequency="2"
@@ -84,22 +85,22 @@
Maximum="72"
Minimum="8"
Value="{x:Bind AlbumArtLayoutSettings.SongInfoFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageShowTitle"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEF3D;}"
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageShowArtists">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageShowArtists">
<ToggleSwitch IsEnabled="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=OneWay}" IsOn="{x:Bind AlbumArtLayoutSettings.ShowArtists, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
</StackPanel>
</Grid>

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:globalization="using:Windows.Globalization"
xmlns:helper="using:BetterLyrics.WinUI3.Helper"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
@@ -22,7 +23,7 @@
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLanguage"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF2B7;}"
@@ -36,63 +37,71 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<dev:SettingsExpander.Items>
<dev:SettingsCard>
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- App behavior -->
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF71C;}">
<dev:SettingsCard x:Uid="SettingsPageAutoStart">
<ToggleSwitch
x:Name="AutoStartupToggleSwitch"
Loaded="AutoStartupToggleSwitch_Loaded"
Unloaded="AutoStartupToggleSwitch_Unloaded" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E8;}">
<dev:SettingsCard x:Uid="SettingsPageAutoOpenMusicGalleryWindow">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoOpen, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAutoPlayWhenOpenMusicGalleryWindow">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoPlay, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageListenNewSession" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF270;}">
<dev:SettingsCard x:Uid="SettingsPageListenNewSession">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageShowHideHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPageShowHideHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageBorderlessHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPageBorderlessHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.BorderlessShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageClickThroughHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPageClickThroughHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ClickThroughShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsWindowSwitchHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsWindowSwitchHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- Playback shortcut -->
<TextBlock x:Uid="SettingsPagePlaybackShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PlayOrPauseShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageNextSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPageNextSongHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.NextSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPagePreviousSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<dev:SettingsCard x:Uid="SettingsPagePreviousSongHotKey">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PreviousSongShortcut, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -13,13 +13,14 @@
<StackPanel>
<Slider
IsEnabled="{x:Bind IsSliderEnabled, Mode=OneWay}"
Maximum="{x:Bind Maximum, Mode=OneWay}"
Minimum="{x:Bind Minimum, Mode=OneWay}"
SnapsTo="Ticks"
StepFrequency="{x:Bind Frequency, Mode=OneWay}"
Tapped="Slider_Tapped"
TickFrequency="{x:Bind Frequency, Mode=OneWay}"
TickPlacement="None"
ValueChanged="Slider_ValueChanged"
Value="{x:Bind Value, Mode=TwoWay}" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">

View File

@@ -1,3 +1,4 @@
using BetterLyrics.WinUI3.Events;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -21,18 +22,13 @@ namespace BetterLyrics.WinUI3.Controls
{
public sealed partial class ExtendedSlider : UserControl
{
public event EventHandler<RangeBaseValueChangedEventArgs> ValueChanged;
public event EventHandler<ExtendedSliderValueChangedByUserEventArgs>? ValueChangedByUser;
public ExtendedSlider()
{
InitializeComponent();
}
private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
ValueChanged?.Invoke(sender, e);
}
private void Subtract()
{
if (Value - Frequency < Minimum)
@@ -68,7 +64,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty FrequencyProperty =
DependencyProperty.Register(nameof(Frequency), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
DependencyProperty.Register(nameof(Frequency), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(1.0));
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(nameof(Minimum), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty MaximumProperty =
@@ -83,6 +79,8 @@ namespace BetterLyrics.WinUI3.Controls
DependencyProperty.Register(nameof(ResetButtonVisibility), typeof(Visibility), typeof(ExtendedSlider), new PropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty UnitProperty =
DependencyProperty.Register(nameof(Unit), typeof(string), typeof(ExtendedSlider), new PropertyMetadata(""));
public static readonly DependencyProperty IsSliderEnabledProperty =
DependencyProperty.Register(nameof(IsSliderEnabled), typeof(bool), typeof(ExtendedSlider), new PropertyMetadata(true));
public double Frequency
{
@@ -128,20 +126,33 @@ namespace BetterLyrics.WinUI3.Controls
get => (string)GetValue(UnitProperty);
set => SetValue(UnitProperty, value);
}
public bool IsSliderEnabled
{
get => (bool)GetValue(IsSliderEnabledProperty);
set => SetValue(IsSliderEnabledProperty, value);
}
private void ResetButton_Click(object sender, RoutedEventArgs e)
{
Value = Default;
ValueChangedByUser?.Invoke(this, new ExtendedSliderValueChangedByUserEventArgs(Value));
}
private void SubtractButton_Click(object sender, RoutedEventArgs e)
{
Subtract();
ValueChangedByUser?.Invoke(this, new ExtendedSliderValueChangedByUserEventArgs(Value));
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
Add();
ValueChangedByUser?.Invoke(this, new ExtendedSliderValueChangedByUserEventArgs(Value));
}
private void Slider_Tapped(object sender, TappedRoutedEventArgs e)
{
ValueChangedByUser?.Invoke(this, new ExtendedSliderValueChangedByUserEventArgs(Value));
}
}
}

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:enums="using:BetterLyrics.WinUI3.Enums"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
@@ -18,119 +19,110 @@
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE790;}">
<dev:SettingsCard x:Uid="SettingsPageTheme">
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageFollowSystem" />
<ComboBoxItem x:Uid="SettingsPageLight" />
<ComboBoxItem x:Uid="SettingsPageDark" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPagePureLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEB42;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<dev:SettingsExpander x:Uid="SettingsPagePureLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<dev:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsBackgroundSettings.PureColorOverlayOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageAlbumArtLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE93C;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<dev:SettingsExpander x:Uid="SettingsPageAlbumArtLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<dev:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="50"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsBackgroundSettings.CoverOverlaySpeed, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageBlurAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageBlurAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayBlurAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageBackgroundAcrylicEffectAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageBackgroundAcrylicEffectAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="0"
Frequency="1"
Maximum="10"
Minimum="0"
Value="{x:Bind LyricsBackgroundSettings.CoverAcrylicEffectAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageFluidLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEDA8;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<dev:SettingsExpander x:Uid="SettingsPageFluidLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<dev:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsBackgroundSettings.FluidOverlayOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPagePaletteGeneratorType">
<dev:SettingsCard x:Uid="SettingsPagePaletteGeneratorType">
<ComboBox SelectedIndex="{x:Bind LyricsBackgroundSettings.PaletteGeneratorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageMedianCut" />
<ComboBoxItem x:Uid="SettingsPageOctTree" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageSpectrumLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF61F;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=OneWay}">
<dev:SettingsExpander x:Uid="SettingsPageSnowFlakeLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=TwoWay}" />
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAmount">
<uc:ExtendedSlider
Maximum="100"
Minimum="10"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<dev:SettingsCard x:Uid="SettingsPageSpectrumLayer">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=TwoWay}" />
</controls:SettingsExpander>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -23,36 +24,34 @@
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="Effect" />
<controls:SettingsCard x:Uid="SettingsPageLyricsVerticalEdgeOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB42;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsVerticalEdgeOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB42;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsVerticalEdgeOpacitySlider"
Default="0"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsEffectSettings.LyricsVerticalEdgeOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE727;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE727;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsBlurAmountExtendedSlider"
Default="5"
Frequency="1"
Maximum="10"
Minimum="0"
Value="{x:Bind LyricsEffectSettings.LyricsBlurAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsLineFade" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED3A;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsLineFade" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED3A;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsLineFadeEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- 高亮 -->
<controls:SettingsExpander x:Uid="SettingsPageLyricsHighlight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E6;}">
<controls:SettingsExpander.Items>
<dev:SettingsExpander x:Uid="SettingsPageLyricsHighlight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E6;}">
<dev:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPagePhoneticText">
<dev:SettingsCard x:Uid="SettingsPagePhoneticText">
<local:ExtendedSlider
Default="60"
Frequency="5"
@@ -60,17 +59,17 @@
Minimum="0"
Unit="%"
Value="{x:Bind LyricsEffectSettings.PhoneticLyricsHighlightAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsHighlightScope">
<dev:SettingsCard x:Uid="SettingsPageLyricsHighlightScope">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.OriginalLyricsHighlightScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageOriginalText">
<dev:SettingsCard x:Uid="SettingsPageOriginalText">
<local:ExtendedSlider
Default="60"
Frequency="5"
@@ -78,9 +77,9 @@
Minimum="0"
Unit="%"
Value="{x:Bind LyricsEffectSettings.OriginalLyricsHighlightAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslatedText">
<dev:SettingsCard x:Uid="SettingsPageTranslatedText">
<local:ExtendedSlider
Default="60"
Frequency="5"
@@ -88,139 +87,132 @@
Minimum="0"
Unit="%"
Value="{x:Bind LyricsEffectSettings.TranslatedLyricsHighlightAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 阴影 -->
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLyricsShadow"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF5EF;}"
IsExpanded="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsShadowScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="8"
Frequency="1"
Maximum="20"
Minimum="1"
Value="{x:Bind LyricsEffectSettings.LyricsShadowAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 辉光效果 -->
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLyricsGlowEffect"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE9A9;}"
IsExpanded="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="8"
Frequency="1"
Maximum="20"
Minimum="1"
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 浮动动画 -->
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLyricsFloatAnimation"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8C5;}"
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="1"
Frequency="1"
Maximum="4"
Minimum="1"
Value="{x:Bind LyricsEffectSettings.LyricsFloatAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 扇形歌词 -->
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageFan"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEBC5;}"
IsExpanded="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard IsEnabled="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard IsEnabled="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
Value="{x:Bind LyricsEffectSettings.FanLyricsAngle, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 3D 歌词 -->
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPage3DLyrics"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE914;}"
IsExpanded="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard Header="X" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard Header="X" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
Value="{x:Bind LyricsEffectSettings.Lyrics3DXAngle, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard Header="Y" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard Header="Y" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
Value="{x:Bind LyricsEffectSettings.Lyrics3DYAngle, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard Header="Z" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard Header="Z" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
Value="{x:Bind LyricsEffectSettings.Lyrics3DZAngle, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPage3DLyricsDepth" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPage3DLyricsDepth" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="1000"
Frequency="10"
@@ -228,12 +220,12 @@
Minimum="100"
Unit="°"
Value="{x:Bind LyricsEffectSettings.Lyrics3DDepth, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 滚动动画 -->
<controls:SettingsExpander x:Uid="SettingsPageScrollEasing" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xECE7;}">
<dev:SettingsExpander x:Uid="SettingsPageScrollEasing" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xECE7;}">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeSmoothStep" />
@@ -248,8 +240,8 @@
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutElastic" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutBounce" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageScrollTopDuration">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageScrollTopDuration">
<local:ExtendedSlider
Default="500"
Frequency="50"
@@ -257,8 +249,8 @@
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDuration, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollDuration">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageScrollDuration">
<local:ExtendedSlider
Default="500"
Frequency="50"
@@ -266,8 +258,8 @@
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollDuration, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollBottomDuration">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageScrollBottomDuration">
<local:ExtendedSlider
Default="500"
Frequency="50"
@@ -275,8 +267,8 @@
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollTopDelay">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageScrollTopDelay">
<local:ExtendedSlider
Default="0"
Frequency="50"
@@ -284,8 +276,8 @@
Minimum="0"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDelay, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollBottomDelay">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageScrollBottomDelay">
<local:ExtendedSlider
Default="0"
Frequency="50"
@@ -293,9 +285,9 @@
Minimum="0"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDelay, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
</StackPanel>
</Grid>

View File

@@ -5,9 +5,11 @@
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:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
@@ -23,81 +25,127 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<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>
<ScrollViewer>
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
<Grid
Padding="16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4">
<StackPanel Spacing="6">
<TextBlock x:Uid="LyricsSearchControlTitle" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<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>
</Grid>
<Grid
Padding="16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4">
<StackPanel Spacing="6">
<TextBlock x:Uid="LyricsSearchControlArtist" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}"
TextWrapping="Wrap" />
<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>
</Grid>
<Grid
Padding="16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4">
<StackPanel Spacing="6">
<TextBlock x:Uid="LyricsSearchControlAlbum" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}"
TextWrapping="Wrap" />
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedAlbum, Mode=TwoWay}" TextWrapping="Wrap" />
<Button
VerticalAlignment="Center"
Command="{x:Bind ViewModel.ResetMappedAlbumCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</Grid>
<dev:SettingsCard>
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
Style="{StaticResource AccentButtonStyle}" />
</dev:SettingsCard>
</StackPanel>
</ScrollViewer>
</Grid>
<Grid Grid.Column="1">
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem IsEnabled="{Binding IsFound}">
<Grid Opacity="{Binding IsFound, Converter={StaticResource BoolToOpacityConverter}}">
<RelativePanel Padding="0,6">
<TextBlock
x:Name="SearchedTitle"
RelativePanel.AlignLeftWithPanel="True"
Text="{Binding Title}"
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Name="SearchedArtists"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.Below="SearchedTitle"
Text="{Binding Artist}"
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Name="SearchedProvider"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
</RelativePanel>
<DataTemplate x:DataType="models:LyricsSearchResult">
<ListViewItem IsEnabled="{x:Bind IsFound}">
<StackPanel Padding="3,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
<TextBlock Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Text="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
<TextBlock
Text="{x:Bind Title}"
TextWrapping="Wrap"
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind Artist}"
TextWrapping="Wrap"
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind Album}"
TextWrapping="Wrap"
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Uid="LyricsSearchControlNotFound"
VerticalAlignment="Center"
Text="Not found"
Visibility="{Binding IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
</Grid>
Visibility="{x:Bind IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
</StackPanel>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -20,26 +21,26 @@
<TextBlock x:Uid="SettingsPageLyricsStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsLeft" />
<ComboBoxItem x:Uid="SettingsPageLyricsCenter" />
<ComboBoxItem x:Uid="SettingsPageLyricsRight" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D2;}">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageCJK">
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D2;}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageCJK">
<local:FontFamilyAutoSuggestBox SelectedFontFamily="{x:Bind LyricsStyleSettings.LyricsCJKFontFamily, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageWesternChar">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageWesternChar">
<local:FontFamilyAutoSuggestBox SelectedFontFamily="{x:Bind LyricsStyleSettings.LyricsWesternFontFamily, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontWeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8DD;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsFontWeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8DD;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsFontWeight, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsThin" />
<ComboBoxItem x:Uid="SettingsPageLyricsExtraLight" />
@@ -53,39 +54,37 @@
<ComboBoxItem x:Uid="SettingsPageLyricsBlack" />
<ComboBoxItem x:Uid="SettingsPageLyricsExtraBlack" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB42;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB42;}">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsStyleSettings.LyricsBgFontOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC12;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC12;}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="5"
Maximum="10"
Minimum="0"
Value="{x:Bind LyricsStyleSettings.LyricsFontStrokeWidth, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- 字体颜色 -->
<controls:SettingsExpander x:Uid="SettingsPageFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D3;}">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageStrokeFontColor">
<dev:SettingsExpander x:Uid="SettingsPageFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D3;}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageStrokeFontColor">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsStrokeFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard HorizontalContentAlignment="Left">
<dev:SettingsCard HorizontalContentAlignment="Left">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsStrokeFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
@@ -110,17 +109,17 @@
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomStrokeFontColor, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontColor">
<dev:SettingsCard x:Uid="SettingsPageLyricsBgFontColor">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsBgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard>
<dev:SettingsCard>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsBgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
@@ -145,17 +144,17 @@
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomBgFontColor, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFgFontColor">
<dev:SettingsCard x:Uid="SettingsPageLyricsFgFontColor">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsFgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard>
<dev:SettingsCard>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsFgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
@@ -180,43 +179,44 @@
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomFgFontColor, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAutoAdjust">
<!-- 字体大小 -->
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPagePhoneticText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPagePhoneticText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Maximum="256"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.PhoneticLyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageOriginalText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageOriginalText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Maximum="256"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.OriginalLyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslatedText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageTranslatedText" IsEnabled="{x:Bind LyricsStyleSettings.IsDynamicLyricsFontSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Maximum="256"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.TranslatedLyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF579;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF579;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
Default="0.5"
@@ -225,14 +225,14 @@
Minimum="0"
Unit="x"
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsTranslationSeparator" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF464;}">
<dev:SettingsCard x:Uid="SettingsPageLyricsTranslationSeparator" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF464;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox AcceptsReturn="True" Text="{x:Bind LyricsStyleSettings.LyricsTranslationSeparator, Mode=TwoWay}" />
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=&#xE8FB;}" Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
@@ -26,11 +27,9 @@
x:Uid="SettingsPageRecordedWindowStatus"
RelativePanel.AlignLeftWithPanel="True"
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<Button x:Uid="SettingsPageCreateFromCurrent" Command="{x:Bind ViewModel.CopyLyricsWindowStatusCommand}" />
<Button
x:Uid="SettingsPageCreateFromTemplates"
VerticalAlignment="Center"
RelativePanel.AlignRightWithPanel="True">
<Button x:Uid="SettingsPageCreateFromTemplates">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="SettingsPageStandardMode" Command="{x:Bind ViewModel.CreateStandardLyricsWindowStatusCommand}" />
@@ -173,7 +172,7 @@
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8AC;}">
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8AC;}">
<StackPanel
Margin="0,6,0,0"
Orientation="Horizontal"
@@ -181,9 +180,9 @@
<TextBox Text="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=&#xE8FB;}" Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageDisplayTypeSwitcher"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF246;}"
@@ -193,51 +192,62 @@
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageLayoutOrientation">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageLayoutOrientation">
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsLayoutOrientation, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationHorizontal" />
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationVertical" />
</ComboBox>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageWorkArea"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE78B;}"
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<uc:ExtendedSlider
Default="64"
Frequency="1"
Maximum="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
Minimum="64"
Unit="px"
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
<Button
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE72C;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</dev:SettingsCard>
<controls:SettingsExpander
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<dev:SettingsExpander
x:Uid="SettingsPageAdaptEnvColor"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE88F;}"
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard
<dev:SettingsExpander.Items>
<dev:SettingsCard
x:Uid="SettingsPageEnvColorSample"
Header="Environment color sample mode"
IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
@@ -247,71 +257,59 @@
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
</ComboBox>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<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.LiveStates.LyricsWindowStatus.MonitorDeviceName, 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:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageWindowBounds"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF16B;}"
IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard Header="X">
<dev:SettingsExpander.Items>
<dev:SettingsCard Header="X">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowX, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard Header="Y">
</dev:SettingsCard>
<dev:SettingsCard Header="Y">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowY, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageWidth">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageWidth">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageHeight">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageHeight">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageAOT"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE718;}"
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED1A;}">
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED1A;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>
@@ -344,25 +342,25 @@
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C4;}">
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C4;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C9;}">
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C9;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B2;}">
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B2;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB41;}">
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB41;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
</ComboBox>
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -5,6 +5,7 @@
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:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -18,7 +19,7 @@
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageMusicLib" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B7;}" />
<dev:SettingsCard x:Uid="SettingsPageMusicLib" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B7;}" />
<InfoBar
x:Uid="SettingsPageRemoveInfo"
@@ -49,38 +50,38 @@
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<controls:SettingsExpander>
<controls:SettingsExpander.Header>
<dev:SettingsExpander>
<dev:SettingsExpander.Header>
<HyperlinkButton
Click="LocalFolderHyperlinkButton_Click"
Content="{Binding Path, Mode=OneWay}"
Tag="{Binding Path, Mode=OneWay}" />
</controls:SettingsExpander.Header>
</dev:SettingsExpander.Header>
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<controls:SettingsCard.Header>
<dev:SettingsExpander.Items>
<dev:SettingsCard>
<dev:SettingsCard.Header>
<HyperlinkButton
x:Uid="SettingsPageRemovePath"
Click="SettingsPageRemovePathButton_Click"
Tag="{Binding}" />
</controls:SettingsCard.Header>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageMusicLibRealTimeWatch">
</dev:SettingsCard.Header>
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageMusicLibRealTimeWatch">
<ToggleSwitch IsOn="{Binding IsRealTimeWatchEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<controls:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
<dev:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
<Button
x:Uid="SettingsPageAddFolderButton"
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
CommandParameter="{Binding ElementName=RootGrid}" />
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -6,6 +6,7 @@
xmlns:constants="using:BetterLyrics.WinUI3.Constants"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dev="using:DevWinUI"
xmlns:globalization="using:Windows.Globalization"
xmlns:helper="using:BetterLyrics.WinUI3.Helper"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
@@ -25,19 +26,19 @@
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig">
<dev:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMTrack" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<dev:SettingsCard x:Uid="SettingsPageLastFMTrack" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsLastFMTrackEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- 时间轴相关配置 -->
<controls:SettingsExpander x:Uid="SettingsPageLyricsTimeline" IsExpanded="True">
<dev:SettingsExpander x:Uid="SettingsPageLyricsTimeline" IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsTimelineSyncEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold" IsEnabled="{x:Bind ViewModel.SelectedMediaSourceProvider.IsTimelineSyncEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold" IsEnabled="{x:Bind ViewModel.SelectedMediaSourceProvider.IsTimelineSyncEnabled, Mode=OneWay}">
<local:ExtendedSlider
Frequency="100"
Maximum="1000"
@@ -45,10 +46,10 @@
ResetButtonVisibility="Collapsed"
Unit="ms"
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.TimelineSyncThreshold, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsExpander x:Uid="MainPagePositionOffsetSlider" IsExpanded="True">
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<dev:SettingsExpander x:Uid="MainPagePositionOffsetSlider" IsExpanded="True">
<local:ExtendedSlider
x:Uid="SettingsPagePositionOffsetReset"
Default="0"
@@ -57,12 +58,12 @@
Minimum="-5000"
Unit="ms"
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.PositionOffset, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="LyricsPagePositionOffsetHint">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="LyricsPagePositionOffsetHint">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.ResetPositionOffsetOnSongChanged, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 专辑封面源配置 -->
<TextBlock x:Uid="SettingsPageAlbumArtSearchProvidersConfig" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
@@ -80,12 +81,12 @@
</ListView.OpacityTransition>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:AlbumArtSearchProviderInfo">
<controls:SettingsCard Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<controls:SettingsCard.HeaderIcon>
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<dev:SettingsCard.HeaderIcon>
<FontIcon FontFamily="Segoe UI Symbol" Glyph="&#x283F;" />
</controls:SettingsCard.HeaderIcon>
</dev:SettingsCard.HeaderIcon>
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
@@ -112,12 +113,12 @@
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
<controls:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<controls:SettingsCard.HeaderIcon>
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<dev:SettingsCard.HeaderIcon>
<FontIcon FontFamily="Segoe UI Symbol" Glyph="&#x283F;" />
</controls:SettingsCard.HeaderIcon>
</dev:SettingsCard.HeaderIcon>
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
@@ -209,25 +210,25 @@
<!-- Provider info -->
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<dev:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.OriginalLyricsRef, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsPageTranslationProviderPrefix">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="LyricsPageTranslationProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.TranslatedLyricsRef, Mode=OneWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- Lyrics translation -->
<TextBlock x:Uid="SettingsPageTranslation" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander x:Uid="LyricsPageTranslationEnabled" IsExpanded="True">
<dev:SettingsExpander x:Uid="LyricsPageTranslationEnabled" IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageTargetLanguage" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<ComboBox ItemsSource="{x:Bind helper:LanguageHelper.SupportedTranslationTargetLanguages}" SelectedIndex="{x:Bind ViewModel.SelectedTargetLanguageIndex, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:ExtendedLanguage">
@@ -237,19 +238,19 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslationConfig" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<controls:SettingsCard.Description>
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageTranslationConfig" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<dev:SettingsCard.Description>
<HyperlinkButton Margin="0,6,0,0" NavigateUri="https://github.com/LibreTranslate/LibreTranslate">
<TextBlock
x:Uid="SettingsPageTranslationInfoLink"
FontSize="14"
TextWrapping="Wrap" />
</HyperlinkButton>
</controls:SettingsCard.Description>
</dev:SettingsCard.Description>
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBox
x:Name="LibreTranslateServerTextBox"
@@ -261,36 +262,36 @@
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- Lyrics phonetic -->
<TextBlock x:Uid="SettingsPagePhonetic" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander x:Uid="SettingsPageChinese" IsExpanded="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=OneWay}">
<dev:SettingsExpander x:Uid="SettingsPageChinese" IsExpanded="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsChineseRomanizationEnabled, Mode=OneWay}">
<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">
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<dev:SettingsCard x:Uid="SettingsPageJapanese">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- 中文简体繁体偏好 -->
<TextBlock x:Uid="SettingsPageChineseLyrics" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageChinesePreference">
<dev:SettingsCard x:Uid="SettingsPageChinesePreference">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTraditionalChineseEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</dev:SettingsCard>
<!-- Last.fm -->
<TextBlock x:Uid="SettingsPageLastFM" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
<dev:SettingsExpander
x:Uid="SettingsPageLastFMManager"
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
@@ -304,25 +305,25 @@
Command="{x:Bind ViewModel.LastFMUnAuthCommand}"
IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}" />
</StackPanel>
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageLastFMUsername" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageLastFMUsername" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<HyperlinkButton Content="{x:Bind ViewModel.LastFMUser.Name, Mode=OneWay}" NavigateUri="{x:Bind ViewModel.LastFMUser.Url, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMPlaycount" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLastFMPlaycount" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Playcount, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMRegistered" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLastFMRegistered" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Registered.ToLongDateString(), Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
</dev:SettingsCard>
<dev:SettingsCard IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<Button x:Uid="SettingsPageLastFMRefresh" Command="{x:Bind ViewModel.LastFMRefreshCommand}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- LX music server -->
<TextBlock x:Uid="SettingsPageLXMusicServer" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard Header="SettingsPageServerAddress">
<dev:SettingsCard x:Uid="SettingsPageServerAddress">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
@@ -333,11 +334,11 @@
Command="{x:Bind ViewModel.LXMusicServerTestCommand}"
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
</dev:SettingsCard>
<!-- Apple Music token -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="Apple Muisc media-user-token" />
<controls:SettingsCard
<dev:SettingsCard
Background="{ThemeResource SystemFillColorCautionBackgroundBrush}"
Description="Use at your own risk"
Foreground="{ThemeResource SystemFillColorCautionBrush}"
@@ -356,7 +357,7 @@
Glyph=&#xE8FB;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</dev:SettingsCard>
</StackPanel>
</Grid>

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -23,6 +25,8 @@ namespace BetterLyrics.WinUI3.Controls
{
public sealed partial class ShortcutTextBox : UserControl
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public ShortcutTextBox()
{
InitializeComponent();
@@ -98,15 +102,11 @@ namespace BetterLyrics.WinUI3.Controls
bool registered = GlobalHotKeyHelper.IsHotKeyRegistered(Shortcut);
if (registered)
{
App.Current.SettingsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("SettingsPageShortcutRegSuccessInfo"),
InfoBarSeverity.Success);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageShortcutRegSuccessInfo"));
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("SettingsPageShortcutRegFailInfo"),
InfoBarSeverity.Error);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageShortcutRegFailInfo"));
}
}
}

View File

@@ -34,6 +34,11 @@
Command="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8AB;}" />
<MenuFlyoutItem
x:Uid="SystemTraySearch"
Command="{x:Bind ViewModel.OpenLyricsSearchWindowCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE721;}" />
<MenuFlyoutItem
x:Uid="SystemTrayLyrics"
Command="{x:Bind ViewModel.OpenLyricsCommand}"

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
@@ -10,14 +12,16 @@ namespace BetterLyrics.WinUI3.Converter
{
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is AlbumArtSearchProvider provider)
{
return provider switch
{
AlbumArtSearchProvider.Local => App.ResourceLoader!.GetString("AlbumArtSearchLocalProvider"),
AlbumArtSearchProvider.SMTC => App.ResourceLoader!.GetString("AlbumArtSearchSMTCProvider"),
AlbumArtSearchProvider.Local => _resourceService.GetLocalizedString("AlbumArtSearchLocalProvider"),
AlbumArtSearchProvider.SMTC => _resourceService.GetLocalizedString("AlbumArtSearchSMTCProvider"),
AlbumArtSearchProvider.iTunes => "iTunes",
_ => throw new Exception($"Unknown AlbumArtSearchProvider: {provider}"),
};

View File

@@ -0,0 +1,27 @@
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 partial class BoolNegationToOpacityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolValue)
{
return boolValue ? 0.0 : 1.0;
}
return 0.0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -7,13 +7,13 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
public class BoolToOpacityConverter : IValueConverter
public partial class BoolToOpacityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolValue)
{
return boolValue ? 1.0 : 0.3;
return boolValue ? 1.0 : 0.0;
}
return 1.0;
}

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 BoolToPartialOpacityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolValue)
{
return boolValue ? 1.0 : 0.3;
}
return 1.0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,4 +1,5 @@
using Microsoft.UI.Xaml.Data;
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Generic;
@@ -29,11 +30,11 @@ namespace BetterLyrics.WinUI3.Converter
}
catch
{
return null;
return PathHelper.AlbumArtPlaceholderPath;
}
}
return null;
return PathHelper.AlbumArtPlaceholderPath;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)

View File

@@ -0,0 +1,27 @@
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 partial class IndexToDisplayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
int display = 0;
if (value is int index)
{
display = index + 1;
}
return display.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

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 partial class IntToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is int intValue)
{
return intValue != 0;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,13 +1,17 @@
// 2025/6/23 by Zhe Fang
using System;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public partial class LyricsSearchProviderToDisplayNameConverter : IValueConverter
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is LyricsSearchProvider provider)
@@ -20,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
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"),
LyricsSearchProvider.LocalTtmlFile => App.ResourceLoader!.GetString("LyricsSearchProviderTtmlFile"),
LyricsSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
LyricsSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
LyricsSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
LyricsSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
_ => "N/A",
};
}

View File

@@ -1,18 +1,22 @@
// 2025/6/23 by Zhe Fang
using System;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public partial class MatchedLocalFilesPathToVisibilityConverter : IValueConverter
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string path)
{
if (path == App.ResourceLoader!.GetString("MainPageNoLocalFilesMatched"))
if (path == _resourceService.GetLocalizedString("MainPageNoLocalFilesMatched"))
{
return Visibility.Collapsed;
}

View File

@@ -0,0 +1,27 @@
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
public partial class PathToParentFolderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string path)
{
return Directory.GetParent(path)?.Name ?? "";
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -11,15 +11,27 @@ namespace BetterLyrics.WinUI3.Converter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
TimeSpan timeSpan = TimeSpan.Zero;
if (value is double seconds)
{
return TimeSpan.FromSeconds(seconds).ToString(@"mm\:ss");
timeSpan = TimeSpan.FromSeconds(seconds);
}
else if (value is int secondsInt)
{
return TimeSpan.FromSeconds(secondsInt).ToString(@"mm\:ss");
timeSpan = TimeSpan.FromSeconds(secondsInt);
}
if (timeSpan.Days > 0)
{
return timeSpan.ToString(@"dd\.hh\:mm\:ss");
}
else if (timeSpan.Hours > 0)
{
return timeSpan.ToString(@"hh\:mm\:ss");
}
else
{
return timeSpan.ToString(@"mm\:ss");
}
return value?.ToString() ?? "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)

View File

@@ -0,0 +1,28 @@
using ATL;
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
{
public partial class TrackToLyricsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Track track)
{
return track.GetLyrics();
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,13 +1,17 @@
// 2025/6/23 by Zhe Fang
using System;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public partial class TranslationSearchProviderToDisplayNameConverter : IValueConverter
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TranslationSearchProvider provider)
@@ -20,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
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"),
TranslationSearchProvider.LocalTtmlFile => App.ResourceLoader!.GetString("LyricsSearchProviderTtmlFile"),
TranslationSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
TranslationSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
TranslationSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
TranslationSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
TranslationSearchProvider.LibreTranslate => "LibreTranslate",
_ => "N/A",
};

View File

@@ -10,6 +10,8 @@ namespace BetterLyrics.WinUI3.Enums
{
Title,
Album,
Artist
Artist,
Folder,
M3UFilePath
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Events
{
public class ExtendedSliderValueChangedByUserEventArgs : EventArgs
{
public double Value { get; set; }
public ExtendedSliderValueChangedByUserEventArgs(double value)
{
Value = value;
}
}
}

View File

@@ -117,6 +117,11 @@ namespace BetterLyrics.WinUI3.Helper
return new Vector3((float)color.R / 0xff, (float)color.G / 0xff, (float)color.B / 0xff);
}
public static Color GetRandomColor()
{
return Color.FromArgb(255, (byte)Random.Shared.Next(0, 256), (byte)Random.Shared.Next(0, 256), (byte)Random.Shared.Next(0, 256));
}
public static System.Drawing.Color GetAccentColor(IntPtr myHwnd, string monitorDeviceName, WindowPixelSampleMode mode)
{
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return System.Drawing.Color.Transparent;

View File

@@ -0,0 +1,21 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public static class EnumExtensions
{
public static T GetNext<T>(this T value) where T : struct, Enum
{
T[] values = Enum.GetValues<T>();
int currentIndex = Array.IndexOf(values, value);
int nextIndex = (currentIndex + 1) % values.Length;
return values[nextIndex];
}
}
}

View File

@@ -1,6 +1,8 @@
// 2025/6/23 by Zhe Fang
using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.IO;
@@ -37,9 +39,9 @@ namespace BetterLyrics.WinUI3.Helper
return sb.ToString();
}
public static string? ReadLyricsCache(string title, string artist, LyricsFormat format, string cacheFolderPath)
public static string? ReadLyricsCache(string title, string artist, string album, LyricsFormat format, string cacheFolderPath)
{
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title}{format.ToFileExtension()}"));
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title} - {album}{format.ToFileExtension()}"));
if (File.Exists(cacheFilePath))
{
return File.ReadAllText(cacheFilePath);
@@ -57,9 +59,9 @@ namespace BetterLyrics.WinUI3.Helper
return null;
}
public static void WriteLyricsCache(string title, string artist, string lyrics, LyricsFormat format, string cacheFolderPath)
public static void WriteLyricsCache(string title, string artist, string album, string lyrics, LyricsFormat format, string cacheFolderPath)
{
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title}{format.ToFileExtension()}"));
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title} - {album}{format.ToFileExtension()}"));
File.WriteAllText(cacheFilePath, lyrics);
}
@@ -86,7 +88,5 @@ namespace BetterLyrics.WinUI3.Helper
".wav", ".aiff", ".aif", ".pcm", ".cda", ".dsf", ".dff", ".au", ".snd",
".mid", ".midi", ".mod", ".xm", ".it", ".s3m"
};
public static string MusicSearchPattern => string.Join("|", MusicExtensions.Select(x => $"*{x}"));
}
}

View File

@@ -18,6 +18,7 @@ using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI;
using static Vanara.PInvoke.Ole32;
@@ -46,43 +47,13 @@ namespace BetterLyrics.WinUI3.Helper
return RandomAccessStreamReference.CreateFromStream(stream);
}
public static async Task<IRandomAccessStream> CreateTextPlaceholderBytesAsync(int width, int height)
public static async Task<IRandomAccessStream> GetAlbumArtPlaceholderAsync()
{
using var device = CanvasDevice.GetSharedDevice();
using var renderTarget = new CanvasRenderTarget(device, width, height, 96);
// 随机生成渐变色
Windows.UI.Color RandomColor()
{
var rand = new Random(Guid.NewGuid().GetHashCode());
double h = rand.NextDouble() * 360;
double s = 0.35 + rand.NextDouble() * 0.3; // 0.35~0.65,适中饱和度
double l = 0.5 + rand.NextDouble() * 0.3; // 0.5~0.8,明亮
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, l);
}
Windows.UI.Color color1 = RandomColor();
Windows.UI.Color color2 = RandomColor();
using (var ds = renderTarget.CreateDrawingSession())
{
// 绘制线性渐变背景
using var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
{
StartPoint = new Vector2(0, 0),
EndPoint = new Vector2(width, height)
};
ds.FillRectangle(0, 0, width, height, gradientBrush);
}
// 保存为 PNG 并转为 byte[]
var stream = new InMemoryRandomAccessStream();
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
stream.Seek(0);
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(PathHelper.AlbumArtPlaceholderPath));
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
return stream;
}
public static Task<ThemeColorResult> GetAccentColorAsync(BitmapDecoder decoder, PaletteGeneratorType generatorType)
{
return generatorType switch

View File

@@ -1,5 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using NTextCat;
using NTextCat.Commons;
using System.Collections.Generic;
@@ -13,6 +15,7 @@ namespace BetterLyrics.WinUI3.Helper
{
private static readonly RankedLanguageIdentifierFactory _factory = new();
private static readonly RankedLanguageIdentifier _identifier;
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public static List<ExtendedLanguage> SupportedTranslationTargetLanguages { get; set; } =
[
@@ -89,7 +92,7 @@ namespace BetterLyrics.WinUI3.Helper
public static List<ExtendedLanguage> SupportedDisplayLanguages { get; set; } =
[
new ExtendedLanguage("", App.ResourceLoader!.GetString("SettingsPageSystemLanguage")),
new ExtendedLanguage("", _resourceService.GetLocalizedString("SettingsPageSystemLanguage")),
new ExtendedLanguage("en-US", "English"),
new ExtendedLanguage("ja-JP"),
new ExtendedLanguage("ko-KR"),

View File

@@ -9,7 +9,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using Vanara.PInvoke;
using LyricsData = BetterLyrics.WinUI3.Models.LyricsData;
namespace BetterLyrics.WinUI3.Helper
@@ -18,10 +20,23 @@ namespace BetterLyrics.WinUI3.Helper
{
public List<LyricsData> LyricsDataArr { get; private set; } = [];
public LyricsData? LibreTranslationLyricsData => LyricsDataArr.LastOrDefault();
public void Parse(string title, string artist, string? raw, int? durationMs, LyricsSearchProvider? lyricsSearchProvider)
public void Parse(List<MappedSongSearchQuery> mappedSongSearchQueries, string title, string artist, string album, string? raw, int? durationMs, LyricsSearchProvider? lyricsSearchProvider)
{
var overridenTitle = title;
var overridenArtist = artist;
var overridenAlbum = album;
var found = mappedSongSearchQueries
.Where(x => x.OriginalTitle == overridenTitle && x.OriginalArtist == overridenArtist && x.OriginalAlbum == overridenAlbum)
.FirstOrDefault();
if (found != null)
{
overridenTitle = found.MappedTitle;
overridenArtist = found.MappedArtist;
overridenAlbum = found.MappedAlbum;
}
LyricsDataArr = [];
durationMs ??= (int)TimeSpan.FromMinutes(99).TotalMilliseconds;
if (raw == null)
@@ -50,22 +65,22 @@ namespace BetterLyrics.WinUI3.Helper
}
}
FillRomanizationLyricsData();
FillTranslationFromCache(title, artist, lyricsSearchProvider);
FillTranslationFromCache(overridenTitle, overridenArtist, overridenAlbum, lyricsSearchProvider);
}
private void FillTranslationFromCache(string title, string artist, LyricsSearchProvider? provider)
private void FillTranslationFromCache(string title, string artist, string album, LyricsSearchProvider? provider)
{
string? translationRaw = null;
switch (provider)
{
case LyricsSearchProvider.QQ:
translationRaw = FileHelper.ReadLyricsCache(title, artist, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
translationRaw = FileHelper.ReadLyricsCache(title, artist, album, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
break;
case LyricsSearchProvider.Kugou:
translationRaw = FileHelper.ReadLyricsCache(title, artist, LyricsFormat.Lrc, PathHelper.KugouTranslationCacheDirectory);
translationRaw = FileHelper.ReadLyricsCache(title, artist, album, LyricsFormat.Lrc, PathHelper.KugouTranslationCacheDirectory);
break;
case LyricsSearchProvider.Netease:
translationRaw = FileHelper.ReadLyricsCache(title, artist, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
translationRaw = FileHelper.ReadLyricsCache(title, artist, album, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
break;
case LyricsSearchProvider.LrcLib:
break;

View File

@@ -20,6 +20,7 @@ namespace BetterLyrics.WinUI3.Helper
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml");
public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico");
public static string AlbumArtPlaceholderPath => "ms-appx:///Assets/AlbumArtPlaceholder.png";
public static string AIMPLogoPath => Path.Combine(AssetsFolder, "AIMP.png");
public static string Foobar2000LogoPath => Path.Combine(AssetsFolder, "foobar2000.png");
public static string MusicBeeLogoPath => Path.Combine(AssetsFolder, "MusicBee.png");
@@ -37,14 +38,12 @@ namespace BetterLyrics.WinUI3.Helper
public static string SaltPlayerForWindowsLogoPath => Path.Combine(AssetsFolder, "SaltPlayerForWindows.png");
public static string MoeKoeMusicLogoPath => Path.Combine(AssetsFolder, "MoeKoeMusic.png");
public static string Listen1LogoPath => Path.Combine(AssetsFolder, "Listen1.png");
public static string UnknownPlayerLogoPath => Path.Combine(AssetsFolder, "Question.png");
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
public static string LyricsCacheDirectory => Path.Combine(CacheFolder, "lyrics");
public static string LrcLibLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "lrclib");
public static string NeteaseLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "netease");
public static string QQLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "qq");
@@ -55,15 +54,15 @@ namespace BetterLyrics.WinUI3.Helper
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
public static string TranslationCacheDirectory => Path.Combine(CacheFolder, "translations");
public static string QQTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "qq");
public static string NeteaseTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "netease");
public static string KugouTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "kugou");
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
public static string PlayQueuePath => Path.Combine(CacheFolder, "play-queue.m3u");
public static void EnsureDirectories()
{
Directory.CreateDirectory(SettingsDirectory);

View File

@@ -1,4 +1,6 @@
using System;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -6,8 +8,10 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public class PhoneticHelper
public static class PhoneticHelper
{
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public const string PinyinCode = "zh-pinyin";
public const string JyutpingCode = "zh-jyutping";
public const string RomajiCode = "ja-romaji";
@@ -22,11 +26,11 @@ namespace BetterLyrics.WinUI3.Helper
switch (code)
{
case PinyinCode:
return App.ResourceLoader!.GetString("Pinyin");
return _resourceService.GetLocalizedString("Pinyin");
case JyutpingCode:
return App.ResourceLoader!.GetString("Jyutping");
return _resourceService.GetLocalizedString("Jyutping");
case RomajiCode:
return App.ResourceLoader!.GetString("Romaji");
return _resourceService.GetLocalizedString("Romaji");
default:
throw new ArgumentOutOfRangeException(nameof(code));
}
@@ -34,7 +38,7 @@ namespace BetterLyrics.WinUI3.Helper
public static string ToRomaji(string text)
{
return Kana.Kana.KanaToRomaji(text).ToStr();
return Kana.Kana.KanaToRomaji(text, Kana.Error.Ignore).ToStr();
}
public static string ToPinyin(string text, Pinyin.ManTone.Style style = Pinyin.ManTone.Style.TONE)

View File

@@ -1,37 +1,35 @@
using BetterLyrics.WinUI3.Models.Settings;
using NAudio.Dsp;
using NAudio.Dsp;
using NAudio.Wave;
using System;
using System.Diagnostics;
namespace BetterLyrics.WinUI3.Helper
{
public class SpectrumAnalyzer : IDisposable
public partial class SpectrumAnalyzer : IDisposable
{
private WasapiLoopbackCapture _capture;
private WasapiLoopbackCapture? _capture;
private int _sampleRate = 48000;
private readonly int _fftLength = 2048;
private readonly float[] _fftLeftBuffer;
private readonly float[] _fftRightBuffer;
private readonly Complex[] _fftLeftData;
private readonly Complex[] _fftRightData;
private float[] _spectrumLeftData;
private float[] _spectrumRightData;
private float[] _spectrumData;
private float[]? _spectrumLeftData;
private float[]? _spectrumRightData;
private float[]? _spectrumData;
private bool _disposed = false;
private double[] _hammingWindow;
private float[] _currentSpectrum;
public float[] SmoothSpectrum { get; private set; }
public int BarCount { get; set; } = 32;
public int Sensitivity { get; set; } = 10;
private float[]? _currentSpectrum;
public float[]? SmoothSpectrum { get; private set; }
public int BarCount { get; set; } = 16;
public int Sensitivity { get; set; } = 100;
public float SmoothingFactor { get; set; } = 0.95f;
public bool IsCapturing { get; private set; } = false;
@@ -51,12 +49,13 @@ namespace BetterLyrics.WinUI3.Helper
public void StartCapture()
{
_currentSpectrum = new float[BarCount];
SmoothSpectrum = new float[BarCount];
try
{
_capture = new WasapiLoopbackCapture();
_currentSpectrum = new float[BarCount];
SmoothSpectrum = new float[BarCount];
_capture = new();
_sampleRate = _capture.WaveFormat.SampleRate;
_spectrumLeftData = new float[(int)(24000.0f / _sampleRate * _fftLength) / 2];
_spectrumRightData = new float[(int)(24000.0f / _sampleRate * _fftLength) / 2];
@@ -67,9 +66,7 @@ namespace BetterLyrics.WinUI3.Helper
IsCapturing = true;
}
catch (Exception ex)
{
}
catch (Exception) { }
}
public void StopCapture()
@@ -100,10 +97,15 @@ namespace BetterLyrics.WinUI3.Helper
_fftRightData[i].Y = 0;
}
if (_spectrumData == null || _spectrumRightData == null || _currentSpectrum == null)
{
return;
}
// FFT
FastFourierTransform.FFT(true, (int)Math.Log(_fftLength, 2), _fftLeftData);
FastFourierTransform.FFT(true, (int)Math.Log(_fftLength, 2), _fftRightData);
for (int i = 0; i < _spectrumLeftData.Length; i++)
for (int i = 0; i < _spectrumLeftData?.Length; i++)
{
float real = (float)_fftLeftData[i].X;
float imaginary = (float)_fftLeftData[i].Y;
@@ -132,6 +134,11 @@ namespace BetterLyrics.WinUI3.Helper
public void UpdateSmoothSpectrum()
{
if (SmoothSpectrum == null || _currentSpectrum == null)
{
return;
}
for (int i = 0; i < BarCount; i++)
{
SmoothSpectrum[i] = SmoothSpectrum[i] * SmoothingFactor +
@@ -143,7 +150,8 @@ namespace BetterLyrics.WinUI3.Helper
{
// 补偿曲线
float[] frequencies = { 20, 50, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 20000 };
float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
//float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
float[] gains = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
if (freq <= frequencies[0])
{
return gains[0];

View File

@@ -1,74 +1,62 @@
using Microsoft.UI.Dispatching;
using NAudio.CoreAudioApi;
using System;
using Vanara.Extensions;
using Vanara.PInvoke;
using static Vanara.PInvoke.CoreAudio;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public static class SystemVolumeHelper
{
private readonly static IMMDeviceEnumerator _deviceEnumerator = new();
private static IAudioEndpointVolume? _endpointVolume = null;
private static VolumeCallbackImpl? _callbackImpl;
private static int _masterVolume = 0;
private static MMDeviceEnumerator? _deviceEnumerator;
private static MMDevice? _defaultDevice;
private static DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
public static event Action<int>? VolumeChanged;
/// <summary>
/// 当系统音量或静音状态改变时触发。
/// </summary>
public static event EventHandler<int>? VolumeNotification;
static SystemVolumeHelper()
{
var device = _deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
if (device != null)
_deviceEnumerator = new MMDeviceEnumerator();
_defaultDevice = _deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
if (_defaultDevice != null)
{
device.Activate(typeof(IAudioEndpointVolume).GUID, 0, null, out var obj);
if (obj is IAudioEndpointVolume endpointVolume)
{
_endpointVolume = endpointVolume;
_callbackImpl = new VolumeCallbackImpl();
_endpointVolume.RegisterControlChangeNotify(_callbackImpl);
}
_defaultDevice.AudioEndpointVolume.OnVolumeNotification += AudioEndpointVolume_OnVolumeNotification;
}
}
/// <summary>
/// 获取当前系统主音量0~100
/// </summary>
public static int GetMasterVolume()
private static void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
{
if (_endpointVolume != null)
_dispatcherQueue?.TryEnqueue(() =>
{
double level = _endpointVolume.GetMasterVolumeLevelScalar();
_masterVolume = (int)(level * 100);
VolumeNotification?.Invoke(null, (int)(data.MasterVolume * 100));
});
}
/// <summary>
/// 获取或设置系统主音量 (0 到 100)。
/// </summary>
public static int MasterVolume
{
get
{
if (_defaultDevice == null)
return 0;
return (int)(_defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
}
return _masterVolume;
}
/// <summary>
/// 设置当前系统主音量0~100
/// </summary>
public static void SetMasterVolume(int volume)
{
if (_masterVolume == volume) return;
_masterVolume = volume;
_endpointVolume?.SetMasterVolumeLevelScalar(_masterVolume / 100f, Guid.Empty);
}
// 内部回调实现
private class VolumeCallbackImpl : IAudioEndpointVolumeCallback
{
HRESULT IAudioEndpointVolumeCallback.OnNotify(nint pNotify)
set
{
var data = pNotify.ToStructure<AUDIO_VOLUME_NOTIFICATION_DATA>();
_masterVolume = (int)(data.fMasterVolume * 100);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
VolumeChanged?.Invoke(_masterVolume);
});
return HRESULT.S_OK;
if (_defaultDevice == null)
return;
_defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar = value / 100f;
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public static class TrackHelper
{
public static string GetParentFolderName(this ATL.Track track)
{
return Directory.GetParent(track.Path)?.Name ?? "";
}
public static string GetParentFolderPath(this ATL.Track track)
{
return Directory.GetParent(track.Path)?.FullName ?? "";
}
public static string GetLyrics(this ATL.Track track)
{
if (track.Path is string path)
{
return TagLib.File.Create(path).Tag.Lyrics;
}
return "";
}
}
}

View File

@@ -6,6 +6,7 @@ using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System;
@@ -31,6 +32,8 @@ namespace BetterLyrics.WinUI3.Helper
private static readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private static readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
private static DispatcherQueueTimer? _setLyricsWindowVisibilityByPlayingStatusTimer;
public static void HideWindow<T>()
{
var window = _activeWindows.Find(w => w is T);
@@ -346,27 +349,36 @@ namespace BetterLyrics.WinUI3.Helper
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
}
public static void SetLyricsWindowVisibilityByPlayingStatus()
/// <summary>
///
/// </summary>
/// <param name="dispatcherQueue">请确保此参数指向同一个对象,建议传值 BaseViewModel._dispatcherQueue</param>
public static void SetLyricsWindowVisibilityByPlayingStatus(DispatcherQueue dispatcherQueue)
{
var window = GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
_setLyricsWindowVisibilityByPlayingStatusTimer ??= dispatcherQueue.CreateTimer();
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
var window = GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
{
SetIsWorkArea<LyricsWindow>(false);
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
{
SetIsWorkArea<LyricsWindow>(false);
}
HideWindow<LyricsWindow>();
}
HideWindow<LyricsWindow>();
}
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
{
SetIsWorkArea<LyricsWindow>(true);
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
{
SetIsWorkArea<LyricsWindow>(true);
}
OpenOrShowWindow<LyricsWindow>();
}
OpenOrShowWindow<LyricsWindow>();
}
}, Constants.Time.DebounceTimeout);
}
}

View File

@@ -1,6 +1,8 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers.General;
using System;
using System.Collections.Generic;
@@ -13,6 +15,8 @@ namespace BetterLyrics.WinUI3.Models
{
public class LyricsData
{
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public List<LyricsLine> LyricsLines { get; set; }
private string? _languageCode;
public string? LanguageCode
@@ -138,7 +142,7 @@ namespace BetterLyrics.WinUI3.Models
{
StartMs = 0,
EndMs = durationMs,
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
OriginalText = _resourceService.GetLocalizedString("LyricsNotFound"),
LyricsChars = [],
}]);
}

View File

@@ -16,5 +16,6 @@ namespace BetterLyrics.WinUI3.Models
public string? Title { get; set; }
public string? Artist { get; set; }
public string? Album { get; set; }
}
}

View File

@@ -1,8 +1,10 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
using System;
using Windows.Foundation;
@@ -91,11 +93,6 @@ namespace BetterLyrics.WinUI3.Models
this.OnPropertyChanged(nameof(AlbumArtLayoutSettings));
}
partial void OnAutoShowOrHideWindowChanged(bool value)
{
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
}
public void UpdateMonitorNameAndBounds()
{
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
@@ -176,11 +173,13 @@ namespace BetterLyrics.WinUI3.Models
public static class LyricsWindowStatusExtensions
{
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public static LyricsWindowStatus DesktopMode()
{
return new LyricsWindowStatus
{
Name = App.ResourceLoader!.GetString("DesktopMode"),
Name = _resourceService.GetLocalizedString("DesktopMode"),
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
WindowBounds = new Rect(100, 100, 600, 250),
IsAlwaysOnTop = true,
@@ -206,7 +205,7 @@ namespace BetterLyrics.WinUI3.Models
{
return new LyricsWindowStatus
{
Name = App.ResourceLoader!.GetString("DockedMode"),
Name = _resourceService.GetLocalizedString("DockedMode"),
IsWorkArea = true,
IsAlwaysOnTop = true,
IsAlwaysOnTopPolling = true,
@@ -233,7 +232,7 @@ namespace BetterLyrics.WinUI3.Models
{
return new LyricsWindowStatus
{
Name = App.ResourceLoader!.GetString("FullscreenMode"),
Name = _resourceService.GetLocalizedString("FullscreenMode"),
WindowBounds = monitorBounds,
IsAlwaysOnTop = true,
IsBorderless = true,
@@ -258,7 +257,7 @@ namespace BetterLyrics.WinUI3.Models
{
return new LyricsWindowStatus
{
Name = App.ResourceLoader!.GetString("StandardMode"),
Name = _resourceService.GetLocalizedString("StandardMode"),
};
}
@@ -266,7 +265,7 @@ namespace BetterLyrics.WinUI3.Models
{
return new LyricsWindowStatus
{
Name = App.ResourceLoader!.GetString("NarrowMode"),
Name = _resourceService.GetLocalizedString("NarrowMode"),
WindowBounds = new Rect(100, 100, 400, 800),
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
};

View File

@@ -12,9 +12,11 @@ namespace BetterLyrics.WinUI3.Models
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalTitle { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalArtist { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalAlbum { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string MappedTitle { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string MappedArtist { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string MappedAlbum { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMarkedAsPureMusic { get; set; } = false;
@@ -26,8 +28,10 @@ namespace BetterLyrics.WinUI3.Models
{
OriginalTitle = this.OriginalTitle,
OriginalArtist = this.OriginalArtist,
OriginalAlbum = this.OriginalAlbum,
MappedTitle = this.MappedTitle,
MappedArtist = this.MappedArtist,
MappedAlbum = this.MappedAlbum,
IsMarkedAsPureMusic = this.IsMarkedAsPureMusic,
LyricsSearchProvider = this.LyricsSearchProvider
};

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MappedSongSearchQuery> MappedSongSearchQueries { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LyricsWindowStatus> WindowBoundsRecords { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<SongsTabInfo> StarredPlaylists { get; set; } = [];
public AppSettings() { }
}

View File

@@ -29,6 +29,9 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSpectrumOverlayEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSnowFlakeOverlayEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SnowFlakeOverlayAmount { get; set; } = 50;
public LyricsBackgroundSettings() { }

View File

@@ -2,6 +2,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -11,6 +12,10 @@ namespace BetterLyrics.WinUI3.Models.Settings
public partial class MusicGallerySettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial PlaybackOrder PlaybackOrder { get; set; } = PlaybackOrder.RepeatAll;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ObservableCollection<string> PlayQueuePaths { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayQueueIndex { get; set; } = -1;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoOpen { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoPlay { get; set; } = false;
public MusicGallerySettings() { }
}

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using Windows.Graphics.Imaging;
using Windows.UI;
@@ -9,7 +10,7 @@ namespace BetterLyrics.WinUI3.Models
public partial class SongInfo : ObservableObject
{
[ObservableProperty]
public partial string? Album { get; set; }
public partial string Album { get; set; }
[ObservableProperty]
public partial string Artist { get; set; }
@@ -31,4 +32,14 @@ namespace BetterLyrics.WinUI3.Models
public SongInfo() { }
}
public static class SongInfoExtensions
{
public static SongInfo Placeholder => new()
{
Title = "N/A",
Album = "N/A",
Artist = "N/A",
};
}
}

View File

@@ -1,8 +1,10 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models
{
public class SongsTabInfo
public partial class SongsTabInfo : BaseViewModel
{
public string Name { get; set; }
@@ -10,6 +12,9 @@ namespace BetterLyrics.WinUI3.Models
public bool IsClosable { get; set; }
[ObservableProperty]
public partial bool IsStarred { get; set; }
public CommonSongProperty FilterProperty { get; set; }
public string FilterValue { get; set; }
@@ -19,15 +24,17 @@ namespace BetterLyrics.WinUI3.Models
Name = string.Empty;
Icon = string.Empty;
IsClosable = true;
IsStarred = false;
FilterProperty = CommonSongProperty.Title;
FilterValue = string.Empty;
}
public SongsTabInfo(string name, string icon, bool isClosable, CommonSongProperty filterProperty, string filterValue)
public SongsTabInfo(string name, string icon, bool isClosable, bool isStarred, CommonSongProperty filterProperty, string filterValue)
{
Name = name;
Icon = icon;
IsClosable = isClosable;
IsStarred = isStarred;
FilterProperty = filterProperty;
FilterValue = filterValue;
}

View File

@@ -14,6 +14,7 @@
<Grid>
<canvas:CanvasAnimatedControl
x:Name="LyricsCanvas"
CreateResources="LyricsCanvas_CreateResources"
Draw="LyricsCanvas_Draw"
Update="LyricsCanvas_Update" />
</Grid>

View File

@@ -35,5 +35,10 @@ namespace BetterLyrics.WinUI3.Renderer
LyricsCanvas.RemoveFromVisualTree();
LyricsCanvas = null;
}
private void LyricsCanvas_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{
ViewModel.CreateResources(sender, args);
}
}
}

View File

@@ -1,10 +1,12 @@
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
@@ -25,6 +27,8 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
public partial class LastFMService : ILastFMService
{
private readonly ISettingsService _settingsService;
private readonly IResourceService _resourceService;
private readonly LastfmClient _client;
public event EventHandler<LastFMUserChangedEventArgs>? UserChanged;
@@ -34,9 +38,10 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
public bool IsAuthenticated { get; private set; }
public LastFMService(ISettingsService settingsService)
public LastFMService(ISettingsService settingsService, IResourceService resourceService)
{
_settingsService = settingsService;
_resourceService = resourceService;
_client = new LastfmClient(Constants.LastFM.ApiKey, Constants.LastFM.SharedSecret);
_client.Session.SessionKey = PasswordVaultHelper.Get(Constants.App.AppName, Constants.LastFM.SessionKeyCredentialKey) ?? string.Empty;
@@ -53,7 +58,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
}
catch (Exception)
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LastFMAuthFailed") ?? "", InfoBarSeverity.Error);
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("LastFMAuthFailed") ?? "");
}
}
@@ -74,10 +79,10 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
var dialog = new ContentDialog
{
Title = App.ResourceLoader?.GetString("LastFMRequestAuthTitle") ?? "",
Content = App.ResourceLoader?.GetString("LastFMRequestAuthDesc") ?? "",
PrimaryButtonText = App.ResourceLoader?.GetString("LastFMRequestAuthConfirm") ?? "",
CloseButtonText = App.ResourceLoader?.GetString("Cancel") ?? "",
Title = _resourceService.GetLocalizedString("LastFMRequestAuthTitle") ?? "",
Content = _resourceService.GetLocalizedString("LastFMRequestAuthDesc") ?? "",
PrimaryButtonText = _resourceService.GetLocalizedString("LastFMRequestAuthConfirm") ?? "",
CloseButtonText = _resourceService.GetLocalizedString("Cancel") ?? "",
DefaultButton = ContentDialogButton.Close,
XamlRoot = dialogXamlRoot,
};
@@ -101,10 +106,10 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
var dialog = new ContentDialog
{
Title = App.ResourceLoader?.GetString("LastFMRequestUnAuthTitle") ?? "",
Content = App.ResourceLoader?.GetString("LastFMRequestUnAuthDesc") ?? "",
PrimaryButtonText = App.ResourceLoader?.GetString("LastFMRequestUnAuthConfirm") ?? "",
CloseButtonText = App.ResourceLoader?.GetString("Cancel") ?? "",
Title = _resourceService.GetLocalizedString("LastFMRequestUnAuthTitle") ?? "",
Content = _resourceService.GetLocalizedString("LastFMRequestUnAuthDesc") ?? "",
PrimaryButtonText = _resourceService.GetLocalizedString("LastFMRequestUnAuthConfirm") ?? "",
CloseButtonText = _resourceService.GetLocalizedString("Cancel") ?? "",
DefaultButton = ContentDialogButton.Close,
XamlRoot = dialogXamlRoot,
};

View File

@@ -49,16 +49,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
break;
case nameof(LyricsWindowStatus.DockHeight):
case nameof(LyricsWindowStatus.DockPlacement):
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
WindowHelper.UpdateWorkArea<LyricsWindow>();
await Task.Delay(300);
WindowHelper.MoveAndResize<LyricsWindow>(GetWindowBoundsWhenWorkArea());
}
break;
case nameof(LyricsWindowStatus.MonitorDeviceName):
// 记录切换前的窗口状态是否沾满屏幕
bool isStretchedToMonitor = LiveStates.LyricsWindowStatus.WindowBounds == LiveStates.LyricsWindowStatus.MonitorBounds;
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
@@ -66,22 +57,6 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
await Task.Delay(300);
WindowHelper.MoveAndResize<LyricsWindow>(GetWindowBoundsWhenWorkArea());
}
else
{
if (isStretchedToMonitor)
{
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.MonitorBounds);
}
else
{
WindowHelper.MoveAndResize<LyricsWindow>(new Rect(
LiveStates.LyricsWindowStatus.MonitorBounds.X,
LiveStates.LyricsWindowStatus.MonitorBounds.Y,
Math.Min(LiveStates.LyricsWindowStatus.MonitorBounds.Width, LiveStates.LyricsWindowStatus.WindowBounds.Width),
Math.Min(LiveStates.LyricsWindowStatus.MonitorBounds.Height, LiveStates.LyricsWindowStatus.WindowBounds.Height)
));
}
}
break;
case nameof(LyricsWindowStatus.IsShownInSwitchers):
WindowHelper.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
@@ -118,6 +93,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
case nameof(LyricsWindowStatus.TitleBarArea):
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
break;
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
break;
default:
break;
}
@@ -159,7 +137,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
WindowHelper.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
WindowHelper.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
WindowHelper.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
if (LiveStates.LyricsWindowStatus.IsWorkArea)

View File

@@ -98,17 +98,19 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string overridenTitle = title;
string overridenArtist = artist;
string overridenAlbum = album;
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
var found = _settingsService.AppSettings.MappedSongSearchQueries
.Where(x => x.OriginalTitle == title && x.OriginalArtist == artist)
.Where(x => x.OriginalTitle == overridenTitle && x.OriginalArtist == overridenArtist && x.OriginalAlbum == overridenAlbum)
.FirstOrDefault();
if (found != null)
{
overridenTitle = found.MappedTitle;
overridenArtist = found.MappedArtist;
overridenAlbum = found.MappedAlbum;
_logger.LogInformation("Found mapped song search query: {MappedSongSearchQuery}", found);
@@ -117,6 +119,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
lyricsSearchResult.Title = overridenTitle;
lyricsSearchResult.Artist = overridenArtist;
lyricsSearchResult.Album = overridenAlbum;
lyricsSearchResult.Raw = "[99:00.000]🎶🎶🎶";
return lyricsSearchResult;
}
@@ -124,7 +127,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
var targetProvider = found.LyricsSearchProvider;
if (targetProvider != null)
{
return await SearchSingleAsync(targetProvider.Value, overridenTitle, overridenArtist, album, durationMs, songId, token);
return await SearchSingleAsync(targetProvider.Value, overridenTitle, overridenArtist, overridenAlbum, durationMs, songId, token);
}
}
@@ -135,7 +138,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
continue;
}
lyricsSearchResult = await SearchSingleAsync(provider.Provider, overridenTitle, overridenArtist, album, durationMs, null, token);
lyricsSearchResult = await SearchSingleAsync(provider.Provider, overridenTitle, overridenArtist, overridenAlbum, durationMs, null, token);
if (lyricsSearchResult.IsFound)
{
@@ -172,12 +175,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
// Check cache first
if (provider.IsRemote())
{
var cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.GetCacheDirectory());
var cachedLyrics = FileHelper.ReadLyricsCache(title, artist, album, lyricsFormat, provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
{
lyricsSearchResult.Raw = cachedLyrics;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
return lyricsSearchResult;
}
}
@@ -186,11 +190,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
if (provider == LyricsSearchProvider.LocalMusicFile)
{
lyricsSearchResult = SearchEmbedded(title, artist);
lyricsSearchResult = SearchEmbedded(title, artist, album);
}
else
{
lyricsSearchResult = await SearchFile(title, artist, lyricsFormat);
lyricsSearchResult = await SearchFile(title, artist, album, lyricsFormat);
}
}
else
@@ -210,7 +214,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
lyricsSearchResult = await SearchAmllTtmlDbAsync(title, artist);
lyricsSearchResult = await SearchAmllTtmlDbAsync(title, artist, album);
break;
case LyricsSearchProvider.AppleMusic:
lyricsSearchResult = await SearchAppleMusicAsync(title, artist, album, (int)durationMs);
@@ -229,7 +233,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
if (provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, lyricsSearchResult.Raw!, lyricsFormat, provider.GetCacheDirectory());
FileHelper.WriteLyricsCache(title, artist, album, lyricsSearchResult.Raw!, lyricsFormat, provider.GetCacheDirectory());
}
}
}
@@ -240,7 +244,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchFile(string title, string artist, LyricsFormat format)
private async Task<LyricsSearchResult> SearchFile(string title, string artist, string album, LyricsFormat format)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -263,6 +267,9 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
return lyricsSearchResult;
}
}
}
@@ -275,7 +282,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private LyricsSearchResult SearchEmbedded(string title, string artist)
private LyricsSearchResult SearchEmbedded(string title, string artist, string album)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -291,14 +298,19 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (FileHelper.MusicExtensions.Contains(Path.GetExtension(file)))
{
var track = new Track(file);
if ((track.Title == title && track.Artist == artist) || FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
if ((album != "" && track.Title == title && track.Artist == artist && track.Album == album)
|| (album == "" && track.Title == title && track.Artist == artist)
|| (album == "" && FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist)))
{
var plain = TagLib.File.Create(file).Tag.Lyrics;
var plain = track.GetLyrics();
if (!plain.IsNullOrEmpty())
{
lyricsSearchResult.Raw = plain;
lyricsSearchResult.Title = track.Title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Artist = track.Artist;
lyricsSearchResult.Album = track.Album;
return lyricsSearchResult;
}
}
}
@@ -308,7 +320,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchAmllTtmlDbAsync(string title, string artist)
private async Task<LyricsSearchResult> SearchAmllTtmlDbAsync(string title, string artist, string album)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -382,6 +394,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
lyricsSearchResult.Raw = lyrics;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
}
catch
{
@@ -421,6 +434,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string? original = null;
string? searchedTitle = null;
string? searchedArtist = null;
string? searchedAlbum = null;
if (jArr.ValueKind == JsonValueKind.Array && jArr.GetArrayLength() > 0)
{
@@ -428,11 +442,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
original = first.GetProperty("syncedLyrics").GetString();
searchedTitle = first.GetProperty("trackName").GetString();
searchedArtist = first.GetProperty("artistName").GetString();
searchedAlbum = first.GetProperty("albumName").GetString();
}
lyricsSearchResult.Raw = original;
lyricsSearchResult.Title = searchedTitle;
lyricsSearchResult.Artist = searchedArtist;
lyricsSearchResult.Album = searchedAlbum;
return lyricsSearchResult;
}
@@ -475,84 +491,93 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}, searcher);
}
if (result is QQMusicSearchResult qqResult)
if (result != null)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
var original = response?.Lyrics;
var translated = response?.Trans;
if (!string.IsNullOrEmpty(translated))
if (result is QQMusicSearchResult qqResult)
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.QQTranslationCacheDirectory
);
}
lyricsSearchResult.Raw = original;
lyricsSearchResult.Title = qqResult.Title;
lyricsSearchResult.Artist = qqResult.Artists.Join(" | ");
}
else if (result is NeteaseSearchResult neteaseResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
var original = response?.Lrc?.Lyric;
var translated = response?.Tlyric?.Lyric;
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.NeteaseTranslationCacheDirectory
);
}
lyricsSearchResult.Raw = original;
lyricsSearchResult.Title = neteaseResult.Title;
lyricsSearchResult.Artist = neteaseResult.Artists.Join(" | ");
}
else if (result is KugouSearchResult kugouResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
string? original = null;
var candidateWithTranslation = response?.Candidates.Where(x => x.TransId != null).FirstOrDefault();
SearchLyricsResponse.Candidate? candidate;
if (candidateWithTranslation != null)
{
candidate = candidateWithTranslation;
}
else
{
candidate = response?.Candidates.FirstOrDefault();
}
if (candidate != null)
{
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
if (candidate.TransId != null)
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
var original = response?.Lyrics;
var translated = response?.Trans;
if (!string.IsNullOrEmpty(translated))
{
string? translated = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.TransId, candidate.AccessKey);
if (!string.IsNullOrEmpty(translated))
FileHelper.WriteLyricsCache(
title,
artist,
album,
translated,
LyricsFormat.Lrc,
PathHelper.QQTranslationCacheDirectory
);
}
lyricsSearchResult.Raw = original;
}
else if (result is NeteaseSearchResult neteaseResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
var original = response?.Lrc?.Lyric;
var translated = response?.Tlyric?.Lyric;
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
album,
translated,
LyricsFormat.Lrc,
PathHelper.NeteaseTranslationCacheDirectory
);
}
lyricsSearchResult.Raw = original;
}
else if (result is KugouSearchResult kugouResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
string? original = null;
var candidate = response?.Candidates.FirstOrDefault();
if (candidate != null)
{
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
if (original != null)
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
);
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
if (parsedList != null)
{
string translated = "";
foreach (var item in parsedList)
{
if (item is Lyricify.Lyrics.Models.FullSyllableLineInfo fullSyllableLineInfo)
{
var startTimeSpan = TimeSpan.FromMilliseconds(fullSyllableLineInfo.StartTime ?? 0);
string startTimeStr = startTimeSpan.ToString(@"mm\:ss\.ff");
string chTranslation = fullSyllableLineInfo.Translations.GetValueOrDefault("zh") ?? "";
translated += $"[{startTimeStr}]{chTranslation}\n";
}
}
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
album,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
);
}
}
}
}
}
lyricsSearchResult.Raw = original;
lyricsSearchResult.Title = kugouResult.Title;
lyricsSearchResult.Artist = kugouResult.Artists.Join(" | ");
lyricsSearchResult.Raw = original;
}
}
lyricsSearchResult.Title = result?.Title;
lyricsSearchResult.Artist = result?.Artists.Join(" | ");
lyricsSearchResult.Album = result?.Album;
return lyricsSearchResult;
}
@@ -570,6 +595,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = "";
}
return lyricsSearchResult;

View File

@@ -37,7 +37,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
SongInfo?.PlayerId ?? "",
_cachedSongInfo.Title,
_cachedSongInfo.Artist,
_cachedSongInfo?.Album ?? string.Empty,
_cachedSongInfo.Album,
_SMTCAlbumArtBuffer,
token
), token);
@@ -46,7 +46,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (buffer == null)
{
using var placeHolderStream = await ImageHelper.CreateTextPlaceholderBytesAsync(500, 500);
using var placeHolderStream = await ImageHelper.GetAlbumArtPlaceholderAsync();
var tempBuffer = new Windows.Storage.Streams.Buffer((uint)placeHolderStream.Size);
await placeHolderStream.ReadAsync(tempBuffer, (uint)placeHolderStream.Size, InputStreamOptions.None);
buffer = tempBuffer;

View File

@@ -69,7 +69,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (originalText == null) return;
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode);
if (originalLangCode == targetLangCode)
{
@@ -83,7 +83,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetLangCode);
if (found >= 0)
{
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
_logger.LogInformation("Found translated text in lyrics data at index {FoundIndex}", found);
_lyricsDataArr[0].SetTranslatedText(_lyricsDataArr[found], _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsTranslationSeparator, 50);
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
@@ -104,7 +104,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
catch (Exception)
{
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LibreTranslateFailed")!, Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("LibreTranslateFailed")!);
}
}
}
@@ -119,7 +119,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (originalText == null) return;
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
_logger.LogInformation("Original phonetic code: {OriginalLangCode}", originalLangCode);
if (originalLangCode == "zh" && _settingsService.AppSettings.TranslationSettings.IsChineseRomanizationEnabled)
{
@@ -139,7 +139,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetPhoneticCode);
if (found >= 0)
{
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
_logger.LogInformation("Found phonetic text in lyrics data at index {FoundIndex}", found);
_lyricsDataArr[0].SetPhoneticText(_lyricsDataArr[found], _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsTranslationSeparator, 50);
}
@@ -166,7 +166,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
SongInfo.PlayerId ?? "",
SongInfo.Title,
SongInfo.Artist,
SongInfo.Album ?? "",
SongInfo.Album,
SongInfo.DurationMs ?? 0,
SongInfo.SongId,
token
@@ -174,10 +174,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (token.IsCancellationRequested) return;
LyricsSearchProvider = lyricsSearchResult?.Provider;
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider?.ToString() ?? "null");
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider);
var lyricsParser = new LyricsParser();
lyricsParser.Parse(SongInfo.Title, SongInfo.Artist, lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs, LyricsSearchProvider);
lyricsParser.Parse(
_settingsService.AppSettings.MappedSongSearchQueries.ToList(),
SongInfo.Title, SongInfo.Artist, SongInfo.Album, lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs, LyricsSearchProvider);
_lyricsDataArr = lyricsParser.LyricsDataArr;
ApplyChinesePreference();
}

View File

@@ -10,6 +10,7 @@ using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.ViewModels;
@@ -46,6 +47,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private readonly ISettingsService _settingsService;
private readonly ILibWatcherService _libWatcherService;
private readonly ILiveStatesService _liveStatesService;
private readonly IResourceService _resourceService;
private readonly ILogger<MediaSessionsService> _logger;
private double _lxMusicPositionSeconds = 0;
@@ -77,7 +79,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
ILyricsSearchService musicSearchService,
ILibWatcherService libWatcherService,
ILiveStatesService liveStatesService,
ITranslateService libreTranslateService)
ITranslateService libreTranslateService,
IResourceService resourceService)
{
_settingsService = settingsService;
_albumArtSearchService = albumArtSearchService;
@@ -85,6 +88,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_libWatcherService = libWatcherService;
_translateService = libreTranslateService;
_liveStatesService = liveStatesService;
_resourceService = resourceService;
_logger = Ioc.Default.GetRequiredService<ILogger<MediaSessionsService>>();
_settingsService.AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += MediaSourceProvidersInfo_ItemPropertyChanged;
@@ -213,14 +217,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
}
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
private async void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
{
if (!_mediaManager.IsStarted) return;
SendFocusedMessagesAsync();
await SendFocusedMessagesAsync();
}
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties? timelineProperties)
{
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
@@ -241,16 +245,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
if (IsMediaSourceTimelineSyncEnabled(mediaSession.Id))
{
_cachedPosition = timelineProperties.Position;
_cachedPosition = timelineProperties?.Position ?? TimeSpan.Zero;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties.EndTime));
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties?.EndTime ?? TimeSpan.Zero));
});
}
}
}
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo? playbackInfo)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
@@ -268,7 +272,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
else
{
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
_cachedIsPlaying = playbackInfo?.PlaybackStatus switch
{
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
_ => false,
@@ -279,26 +283,28 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
});
}
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProperties)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
if (mediaSession == null)
{
_cachedSongInfo = SongInfoExtensions.Placeholder;
}
string sessionId = mediaSession.Id;
string? sessionId = mediaSession?.Id;
var desiredSession = GetCurrentSession();
//RecordMediaSourceProviderInfo(mediaSession);
if (mediaSession != desiredSession) return;
if (!IsMediaSourceEnabled(sessionId))
if (sessionId != null && !IsMediaSourceEnabled(sessionId))
{
_cachedSongInfo = null;
_cachedSongInfo = SongInfoExtensions.Placeholder;
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
if (sessionId == Constants.PlayerID.LXMusic)
{
@@ -315,33 +321,33 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
currentMediaSourceProviderInfo?.PositionOffset = 0;
}
string fixedArtist = mediaProperties.Artist;
string fixedAlbum = mediaProperties.AlbumTitle;
string fixedArtist = mediaProperties?.Artist ?? "N/A";
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
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;
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
}
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId))
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId ?? ""))
{
songId = mediaProperties.Genres.FirstOrDefault()?.Replace("NCM-", "");
songId = mediaProperties?.Genres.FirstOrDefault()?.Replace("NCM-", "");
}
_cachedSongInfo = new SongInfo
{
Title = mediaProperties.Title,
Title = mediaProperties?.Title ?? "N/A",
Artist = fixedArtist,
Album = fixedAlbum,
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds,
PlayerId = sessionId,
SongId = songId
};
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
_cachedSongInfo.Duration = (int)((_cachedSongInfo.DurationMs ?? 0) / 1000f);
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
if (sessionId == Constants.PlayerID.LXMusic)
{
@@ -356,7 +362,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_SMTCAlbumArtBuffer = _lxMusicAlbumArtBytes.AsBuffer();
}
else if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
else if (mediaProperties?.Thumbnail is IRandomAccessStreamReference streamReference)
{
_SMTCAlbumArtBuffer = await ImageHelper.ToBufferAsync(streamReference);
}
@@ -438,7 +444,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
_cachedSongInfo = null;
_cachedSongInfo = SongInfoExtensions.Placeholder;
_cachedIsPlaying = false;
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
@@ -448,14 +454,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private async Task SendFocusedMessagesAsync()
{
var desiredSession = GetCurrentSession();
if (desiredSession == null || desiredSession.ControlSession == null) return;
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null;
var mediaProps = await desiredSession.ControlSession.TryGetMediaPropertiesAsync();
if (desiredSession == null || desiredSession.ControlSession == null) return;
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession.ControlSession.GetTimelineProperties());
var desiredSession = GetCurrentSession();
//if (desiredSession == null || desiredSession.ControlSession == null) return;
try
{
mediaProps = await desiredSession?.ControlSession?.TryGetMediaPropertiesAsync();
}
catch (Exception) { }
//if (desiredSession == null || desiredSession.ControlSession == null) return;
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession?.ControlSession?.GetTimelineProperties());
MediaManager_OnAnyMediaPropertyChanged(desiredSession, mediaProps);
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession.ControlSession.GetPlaybackInfo());
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession?.ControlSession?.GetPlaybackInfo());
}
private void StartSSE()
@@ -476,7 +489,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_logger.LogError("Failed to start SSE connection for LX Music.");
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("FailToStartLXMusicServer"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("FailToStartLXMusicServer"));
});
StopSSE();
}
@@ -654,7 +667,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageCode))
{
_logger.LogInformation("Target language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
_logger.LogInformation("Target LibreTranslate language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
UpdateTranslations();
}
}

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.Services.ResourceService
{
public interface IResourceService
{
string GetLocalizedString(string id);
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.Windows.ApplicationModel.Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services.ResourceService
{
public class ResourceService : IResourceService
{
private readonly ResourceLoader _resourceLoader = new();
public string GetLocalizedString(string id)
{
return _resourceLoader.GetString(id);
}
}
}

View File

@@ -7,7 +7,6 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Serialization;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using System;
@@ -17,14 +16,17 @@ using Windows.Globalization;
namespace BetterLyrics.WinUI3.Services.SettingsService
{
// TODO 初始化时从文件读取到对象,后续独写操作先操纵对象,写入用 Debounce 写入文件
// 新建一个 AppSettings 类
public partial class SettingsService : BaseViewModel, ISettingsService
{
private DispatcherQueueTimer _writeAppSettingsTimer;
public AppSettings AppSettings { get; set; }
public SettingsService()
{
_writeAppSettingsTimer = _dispatcherQueue.CreateTimer();
AppSettings = ReadAppSettings();
AppSettings.PropertyChanged += AppSettings_PropertyChanged;
@@ -46,6 +48,11 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
AppSettings.WindowBoundsRecords.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.WindowBoundsRecords.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.StarredPlaylists.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.StarredPlaylists.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.MusicGallerySettings.PlayQueuePaths.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.Version = MetadataHelper.AppVersion;
EnsureMediaSourceProvidersInfo();
@@ -93,12 +100,12 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
private void AppSettings_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
{
WriteAppSettingsDebounce();
WriteAppSettings();
}
private void AppSettings_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
WriteAppSettingsDebounce();
WriteAppSettings();
}
private void AppSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
@@ -111,7 +118,7 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
default:
break;
}
WriteAppSettingsDebounce();
WriteAppSettings();
}
/// <summary>
@@ -132,7 +139,6 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
/// <returns></returns>
public bool ImportSettings(string importPath)
{
// TODO 导入有问题
if (!File.Exists(importPath))
return false;
@@ -161,9 +167,9 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
return data;
}
private void WriteAppSettingsDebounce()
private void WriteAppSettings()
{
_dispatcherQueueTimer.Debounce(() =>
_writeAppSettingsTimer.Debounce(() =>
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{

View File

@@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services.TranslateService
{
public class TranslateService : BaseViewModel, ITranslateService
public partial class TranslateService : BaseViewModel, ITranslateService
{
private readonly ISettingsService _settingsService;
private readonly HttpClient _httpClient;

View File

@@ -141,6 +141,9 @@
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
<value>Playlist was created successfully</value>
</data>
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>Default</value>
</data>
@@ -174,9 +177,12 @@
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
<value>Music gallery</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
<value>Settings</value>
</data>
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
<value>Playlist was imported successfully</value>
</data>
<data name="ImportSettingsFailed" xml:space="preserve">
<value>Settings file import failed, application settings remain unchanged</value>
</data>
@@ -249,7 +255,10 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>Translation provider</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
<value>Album</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<value>Artist</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
@@ -273,13 +282,13 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>Search</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<value>Song info mapping</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>Target lyrics search provider</value>
</data>
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<value>Title</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
@@ -343,7 +352,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>Welcome to BetterLyrics</value>
</data>
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToCustomList.Text" xml:space="preserve">
<value>Add to playlist</value>
</data>
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
@@ -352,13 +361,13 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
<value>Next items</value>
</data>
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
<value>Add to play queue</value>
</data>
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
<value>All songs</value>
</data>
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
<value>Clear play queue</value>
</data>
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
@@ -385,6 +394,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
<value>Format</value>
</data>
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
<value>Lyrics</value>
</data>
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
<value>Path</value>
</data>
@@ -400,6 +412,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
<value>No songs were found in the media library</value>
</data>
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
<value>Import from file</value>
</data>
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
<value>Create a playlist</value>
</data>
@@ -412,26 +427,32 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
<value>Play queue is empty</value>
</data>
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
<value>Playlists</value>
</data>
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
<value>List loop</value>
</data>
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
<data name="MusicGalleryPageQueueRandom.Text" xml:space="preserve">
<value>Random</value>
</data>
<data name="MusicGalleryPageRemoveFromCustomList.Text" xml:space="preserve">
<value>Remove from playlists</value>
</data>
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
<value>Remove from play queue</value>
</data>
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
<data name="MusicGalleryPageScrollToPlayingItem.Text" xml:space="preserve">
<value>Scroll to playing item</value>
</data>
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
<value>Select all</value>
</data>
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
<data name="MusicGalleryPageSingleLoop.Text" xml:space="preserve">
<value>Single loop</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>Search songs</value>
<value>Search for song title, artist, album or folder path</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>Album</value>
@@ -439,12 +460,21 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
<value>Artist</value>
</data>
<data name="MusicGalleryPageSortByFolder.Content" xml:space="preserve">
<value>Folder</value>
</data>
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
<value>Title</value>
</data>
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
<value>Sort type</value>
</data>
<data name="MusicGalleryPageStarredPlaylist.Content" xml:space="preserve">
<value>Starred playlists</value>
</data>
<data name="MusicGalleryPageStopTrack.Text" xml:space="preserve">
<value>Stop</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>Music gallery - BetterLyrics</value>
</data>
@@ -457,6 +487,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="Pinyin" xml:space="preserve">
<value>Mandarin pinyin</value>
</data>
<data name="PlaylistViewFailed" xml:space="preserve">
<value>Unable to view tracks since the path is not existed</value>
</data>
<data name="Romaji" xml:space="preserve">
<value>Romaji</value>
</data>
@@ -550,6 +583,12 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>Automatic adjustment</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>Open the music library window when the app starts</value>
</data>
<data name="SettingsPageAutoPlayWhenOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>Automatically continue playing when the music library window is opened</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>Automatic startup</value>
</data>
@@ -1192,6 +1231,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>Current value: </value>
</data>
<data name="SettingsPageSnowFlakeLayer.Header" xml:space="preserve">
<value>Snow flake layer</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>Song title &amp; artist</value>
</data>
@@ -1309,12 +1351,21 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SystemTrayRestart.Text" xml:space="preserve">
<value>Restart</value>
</data>
<data name="SystemTraySearch.Text" xml:space="preserve">
<value>Lyrics search window</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>Settings</value>
</data>
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>Lyrics window switcher</value>
</data>
<data name="TracksAddToPlaylistFailed" xml:space="preserve">
<value>Unable to add tracks since the path is not existed</value>
</data>
<data name="TracksAddToPlaylistSuccessfully" xml:space="preserve">
<value>Selected tracks have been added to this playlist</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>Translate server is not set, please configure it in settings first</value>
</data>

View File

@@ -141,6 +141,9 @@
<data name="Cancel" xml:space="preserve">
<value>キャンセル</value>
</data>
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
<value>正常に作成されました</value>
</data>
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>デフォルト</value>
</data>
@@ -174,9 +177,12 @@
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
<value>ミュージックギャラリー</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
<value>設定</value>
</data>
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
<value>プレイリストが正常にインポートされました</value>
</data>
<data name="ImportSettingsFailed" xml:space="preserve">
<value>設定ファイルのインポートに失敗し、アプリケーション設定は変更されません</value>
</data>
@@ -249,7 +255,10 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻訳プロバイダー</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
<value>アルバム</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<value>アーティスト</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
@@ -273,13 +282,13 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>検索する</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<value>曲情報マッピング</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>ターゲット歌詞検索プロバイダー</value>
</data>
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<value>タイトル</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
@@ -343,8 +352,8 @@
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>BetterLyrics へようこそ</value>
</data>
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
<value>プレイリストに追加します</value>
<data name="MusicGalleryPageAddToCustomList.Text" xml:space="preserve">
<value>プレイリストに追加</value>
</data>
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
<value>リストの終わり</value>
@@ -352,13 +361,13 @@
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
<value>次のアイテム</value>
</data>
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
<value>キューを再生するために追加します</value>
</data>
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
<value>すべての曲</value>
</data>
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
<value>クリアプレイキュー</value>
</data>
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
@@ -385,6 +394,9 @@
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
<value>形式</value>
</data>
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
<value>歌詞</value>
</data>
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
<value>パス</value>
</data>
@@ -400,8 +412,11 @@
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
<value>メディアライブラリには歌が見つかりませんでした</value>
</data>
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
<value>ファイルからのインポート</value>
</data>
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
<value>プレイリストを作成しま</value>
<value>プレイリストを作成しましょう</value>
</data>
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
<value>すべてを再生します</value>
@@ -412,26 +427,32 @@
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
<value>キューを再生するのは空です</value>
</data>
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
<value>プレイリスト</value>
</data>
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
<value>ループをリストします</value>
</data>
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
<data name="MusicGalleryPageQueueRandom.Text" xml:space="preserve">
<value>ランダム</value>
</data>
<data name="MusicGalleryPageRemoveFromCustomList.Text" xml:space="preserve">
<value>プレイリストから削除</value>
</data>
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
<value>プレイリストから取り外します</value>
</data>
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
<data name="MusicGalleryPageScrollToPlayingItem.Text" xml:space="preserve">
<value>アイテムを再生するための位置</value>
</data>
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
<value>すべてを選択します</value>
</data>
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
<data name="MusicGalleryPageSingleLoop.Text" xml:space="preserve">
<value>シングルループ</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>曲を検索します</value>
<value>曲名、アーティスト、アルバム、フォルダパスを検索してください</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>アルバム</value>
@@ -439,12 +460,21 @@
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
<value>アーティスト</value>
</data>
<data name="MusicGalleryPageSortByFolder.Content" xml:space="preserve">
<value>フォルダー</value>
</data>
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
<value>タイトル</value>
</data>
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
<value>並べ替えタイプ</value>
</data>
<data name="MusicGalleryPageStarredPlaylist.Content" xml:space="preserve">
<value>スター付きプレイリスト</value>
</data>
<data name="MusicGalleryPageStopTrack.Text" xml:space="preserve">
<value>立ち止まる</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音楽ギャラリー - BetterLyrics</value>
</data>
@@ -457,6 +487,9 @@
<data name="Pinyin" xml:space="preserve">
<value>マンダリンのピンイン</value>
</data>
<data name="PlaylistViewFailed" xml:space="preserve">
<value>パスが存在しないため、トラックを表示できません</value>
</data>
<data name="Romaji" xml:space="preserve">
<value>ローマン</value>
</data>
@@ -550,6 +583,12 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>アプリが起動したら、音楽ライブラリウィンドウを開きます</value>
</data>
<data name="SettingsPageAutoPlayWhenOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>音楽ライブラリウィンドウを開くと、自動的に再生が続行されます</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>自動起動</value>
</data>
@@ -1192,6 +1231,9 @@
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>現在の値: </value>
</data>
<data name="SettingsPageSnowFlakeLayer.Header" xml:space="preserve">
<value>スノーフレークレイヤー</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>曲のタイトル&アーティスト</value>
</data>
@@ -1309,12 +1351,21 @@
<data name="SystemTrayRestart.Text" xml:space="preserve">
<value>再起動</value>
</data>
<data name="SystemTraySearch.Text" xml:space="preserve">
<value>歌詞検索ウィンドウ</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>設定を開く</value>
</data>
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌詞ウィンドウスイッチャー</value>
</data>
<data name="TracksAddToPlaylistFailed" xml:space="preserve">
<value>パスが存在しないため、トラックを追加できません</value>
</data>
<data name="TracksAddToPlaylistSuccessfully" xml:space="preserve">
<value>選択したトラックがこのプレイリストに追加されました</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>翻訳サーバーは設定されていません。最初に設定で構成してください</value>
</data>

View File

@@ -141,6 +141,9 @@
<data name="Cancel" xml:space="preserve">
<value>취소</value>
</data>
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
<value>재생 목록이 성공적으로 생성되었습니다</value>
</data>
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>기본</value>
</data>
@@ -174,9 +177,12 @@
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
<value>음악 갤러리</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
<value>설정</value>
</data>
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
<value>재생 목록을 성공적으로 가져왔습니다</value>
</data>
<data name="ImportSettingsFailed" xml:space="preserve">
<value>설정 파일 가져 오기 실패, 응용 프로그램 설정은 변경되지 않았습니다</value>
</data>
@@ -249,7 +255,10 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>번역 제공자</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
<value>앨범</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<value>아티스트</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
@@ -273,13 +282,13 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>검색</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<value>노래 정보 매핑</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>가사 검색 공급자를 타겟팅하십시오</value>
</data>
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<value>제목</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
@@ -343,8 +352,8 @@
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>Betterlyrics에 오신 것을 환영합니다</value>
</data>
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
<value>재생 목록에 추가하십시오</value>
<data name="MusicGalleryPageAddToCustomList.Text" xml:space="preserve">
<value>재생 목록에 추가</value>
</data>
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
<value>목록의 끝</value>
@@ -352,13 +361,13 @@
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
<value>다음 항목</value>
</data>
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
<value>재생 큐에 추가하십시오</value>
</data>
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
<value>모든 노래</value>
</data>
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
<value>플레이 대기열을 클리어합니다</value>
</data>
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
@@ -385,6 +394,9 @@
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
<value>체재</value>
</data>
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
<value>가사</value>
</data>
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
<value>길</value>
</data>
@@ -400,8 +412,11 @@
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
<value>미디어 라이브러리에는 노래가 없습니다</value>
</data>
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
<value>파일에서 가져오기</value>
</data>
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
<value>재생 목록을 만듭니다</value>
<value>재생목록을 만드세요</value>
</data>
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
<value>모두 재생하십시오</value>
@@ -412,26 +427,32 @@
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
<value>플레이 대기열이 비어 있습니다</value>
</data>
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
<value>재생 목록</value>
</data>
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
<value>목록 루프</value>
</data>
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
<data name="MusicGalleryPageQueueRandom.Text" xml:space="preserve">
<value>무작위의</value>
</data>
<data name="MusicGalleryPageRemoveFromCustomList.Text" xml:space="preserve">
<value>재생 목록에서 제거</value>
</data>
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
<value>재생 목록에서 제거하십시오</value>
</data>
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
<data name="MusicGalleryPageScrollToPlayingItem.Text" xml:space="preserve">
<value>아이템을 재생하기위한 포지셔닝</value>
</data>
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
<value>모두를 선택하십시오</value>
</data>
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
<data name="MusicGalleryPageSingleLoop.Text" xml:space="preserve">
<value>단일 루프</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>노래 검색</value>
<value>노래 제목, 아티스트, 앨범 또는 폴더 경로를 검색하세요</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>앨범</value>
@@ -439,12 +460,21 @@
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
<value>아티스트</value>
</data>
<data name="MusicGalleryPageSortByFolder.Content" xml:space="preserve">
<value>폴더</value>
</data>
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
<value>제목</value>
</data>
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
<value>정렬 유형</value>
</data>
<data name="MusicGalleryPageStarredPlaylist.Content" xml:space="preserve">
<value>별표 표시된 재생 목록</value>
</data>
<data name="MusicGalleryPageStopTrack.Text" xml:space="preserve">
<value>멈추기</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>음악 갤러리 - BetterLyrics</value>
</data>
@@ -457,6 +487,9 @@
<data name="Pinyin" xml:space="preserve">
<value>만다린의 피니 인</value>
</data>
<data name="PlaylistViewFailed" xml:space="preserve">
<value>경로가 존재하지 않으므로 트랙을 볼 수 없습니다</value>
</data>
<data name="Romaji" xml:space="preserve">
<value>로만</value>
</data>
@@ -550,6 +583,12 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>자동 조정</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>앱이 시작되면 음악 라이브러리 창을 여세요</value>
</data>
<data name="SettingsPageAutoPlayWhenOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>음악 라이브러리 창이 열리면 자동으로 계속 재생됩니다</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>자동 시작</value>
</data>
@@ -1192,6 +1231,9 @@
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>현재 가치 : </value>
</data>
<data name="SettingsPageSnowFlakeLayer.Header" xml:space="preserve">
<value>스노우 플레이크 레이어</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>노래 제목 및 아티스트</value>
</data>
@@ -1309,12 +1351,21 @@
<data name="SystemTrayRestart.Text" xml:space="preserve">
<value>다시 시작</value>
</data>
<data name="SystemTraySearch.Text" xml:space="preserve">
<value>가사 검색 창</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>열기 설정</value>
</data>
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>가사 창 전환기</value>
</data>
<data name="TracksAddToPlaylistFailed" xml:space="preserve">
<value>경로가 존재하지 않으므로 트랙을 추가할 수 없습니다</value>
</data>
<data name="TracksAddToPlaylistSuccessfully" xml:space="preserve">
<value>선택한 트랙이 이 재생 목록에 추가되었습니다</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>번역 서버가 설정되지 않았습니다. 먼저 설정으로 구성하십시오.</value>
</data>

View File

@@ -141,6 +141,9 @@
<data name="Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
<value>播放列表创建成功</value>
</data>
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>默认</value>
</data>
@@ -174,9 +177,12 @@
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
<value>音乐库</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
<value>设置</value>
</data>
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
<value>播放列表已成功导入</value>
</data>
<data name="ImportSettingsFailed" xml:space="preserve">
<value>设置文件导入失败,应用程序设置保持不变</value>
</data>
@@ -249,7 +255,10 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻译来源</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
<value>专辑</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<value>艺术家</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
@@ -273,13 +282,13 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>搜索</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<value>歌曲信息映射</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>目标歌词搜索提供商</value>
</data>
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<value>标题</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
@@ -343,8 +352,8 @@
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>欢迎使用 BetterLyrics</value>
</data>
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
<value>添加到歌单</value>
<data name="MusicGalleryPageAddToCustomList.Text" xml:space="preserve">
<value>添加到播放列表</value>
</data>
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
<value>列表的结尾</value>
@@ -352,13 +361,13 @@
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
<value>下一个项目</value>
</data>
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
<value>添加到播放队列</value>
</data>
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
<value>所有歌曲</value>
</data>
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
<value>清除播放队列</value>
</data>
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
@@ -385,6 +394,9 @@
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
<value>格式</value>
</data>
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
<value>歌词</value>
</data>
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
<value>路径</value>
</data>
@@ -400,8 +412,11 @@
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
<value>未在媒体库内找到任何歌曲</value>
</data>
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
<value>从文件导入</value>
</data>
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
<value>创建歌单</value>
<value>创建播放列表</value>
</data>
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
<value>播放全部</value>
@@ -412,26 +427,32 @@
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
<value>播放队列是空的</value>
</data>
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
<value>播放列表</value>
</data>
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
<value>列表循环</value>
</data>
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
<data name="MusicGalleryPageQueueRandom.Text" xml:space="preserve">
<value>随机</value>
</data>
<data name="MusicGalleryPageRemoveFromCustomList.Text" xml:space="preserve">
<value>从播放列表中删除</value>
</data>
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
<value>从播放列表移除</value>
</data>
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
<data name="MusicGalleryPageScrollToPlayingItem.Text" xml:space="preserve">
<value>定位到播放项</value>
</data>
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
<value>选择全部</value>
</data>
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
<data name="MusicGalleryPageSingleLoop.Text" xml:space="preserve">
<value>单曲循环</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>搜索歌曲</value>
<value>搜索歌曲名、艺术家、专辑或所在文件夹路径</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>专辑</value>
@@ -439,12 +460,21 @@
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
<value>艺术家</value>
</data>
<data name="MusicGalleryPageSortByFolder.Content" xml:space="preserve">
<value>文件夹</value>
</data>
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
<value>标题</value>
</data>
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
<value>排序类型</value>
</data>
<data name="MusicGalleryPageStarredPlaylist.Content" xml:space="preserve">
<value>已加星标的歌单</value>
</data>
<data name="MusicGalleryPageStopTrack.Text" xml:space="preserve">
<value>停止</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音乐库 - BetterLyrics</value>
</data>
@@ -457,6 +487,9 @@
<data name="Pinyin" xml:space="preserve">
<value>普通话拼音</value>
</data>
<data name="PlaylistViewFailed" xml:space="preserve">
<value>无法查看曲目,因为路径不存在</value>
</data>
<data name="Romaji" xml:space="preserve">
<value>罗马音</value>
</data>
@@ -550,6 +583,12 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自动调整</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>应用程序启动时打开音乐库窗口</value>
</data>
<data name="SettingsPageAutoPlayWhenOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>打开音乐库窗口时自动继续播放</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>自动启动</value>
</data>
@@ -1192,6 +1231,9 @@
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>当前值: </value>
</data>
<data name="SettingsPageSnowFlakeLayer.Header" xml:space="preserve">
<value>雪花层</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>歌曲标题和艺术家</value>
</data>
@@ -1309,12 +1351,21 @@
<data name="SystemTrayRestart.Text" xml:space="preserve">
<value>重新启动</value>
</data>
<data name="SystemTraySearch.Text" xml:space="preserve">
<value>歌词搜索窗口</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>打开设置</value>
</data>
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌词窗口切换器</value>
</data>
<data name="TracksAddToPlaylistFailed" xml:space="preserve">
<value>由于路径不存在,无法添加曲目</value>
</data>
<data name="TracksAddToPlaylistSuccessfully" xml:space="preserve">
<value>所选曲目已添加到此播放列表</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>未设置Translate服务器请先在设置中进行配置</value>
</data>

View File

@@ -141,6 +141,9 @@
<data name="Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
<value>已成功建立播放清單</value>
</data>
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>預設</value>
</data>
@@ -174,9 +177,12 @@
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
<value>音樂庫</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
<value>設定</value>
</data>
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
<value>已成功匯入播放清單</value>
</data>
<data name="ImportSettingsFailed" xml:space="preserve">
<value>設置文件導入失敗,應用程序設置保持不變</value>
</data>
@@ -249,7 +255,10 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻譯來源</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
<value>專輯</value>
</data>
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
<value>藝術家</value>
</data>
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
@@ -273,13 +282,13 @@
<data name="LyricsSearchControlSearch.Content" xml:space="preserve">
<value>搜索</value>
</data>
<data name="LyricsSearchControlSongInfoMapping.Header" xml:space="preserve">
<data name="LyricsSearchControlSongInfoMapping.Text" xml:space="preserve">
<value>歌曲信息映射</value>
</data>
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
<value>目標歌詞搜尋提供者</value>
</data>
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
<value>標題</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
@@ -343,8 +352,8 @@
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>歡迎使用 BetterLyrics</value>
</data>
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
<value>添加到歌單</value>
<data name="MusicGalleryPageAddToCustomList.Text" xml:space="preserve">
<value>新增到播放清單</value>
</data>
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
<value>列表的結尾</value>
@@ -352,13 +361,13 @@
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
<value>下一個項目</value>
</data>
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
<value>新增到播放隊列</value>
</data>
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
<value>所有歌曲</value>
</data>
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
<value>清除播放隊列</value>
</data>
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
@@ -385,6 +394,9 @@
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
<value>格式</value>
</data>
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
<value>歌词</value>
</data>
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
<value>路徑</value>
</data>
@@ -400,8 +412,11 @@
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
<value>未在媒體庫內找到任何歌曲</value>
</data>
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
<value>從檔案匯入</value>
</data>
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
<value>建立單</value>
<value>建立播放清單</value>
</data>
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
<value>播放全部</value>
@@ -412,26 +427,32 @@
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
<value>播放隊列是空的</value>
</data>
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
<value>播放清單</value>
</data>
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
<value>列表循環</value>
</data>
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
<data name="MusicGalleryPageQueueRandom.Text" xml:space="preserve">
<value>隨機</value>
</data>
<data name="MusicGalleryPageRemoveFromCustomList.Text" xml:space="preserve">
<value>從播放列表中刪除</value>
</data>
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
<value>從播放列表移除</value>
</data>
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
<data name="MusicGalleryPageScrollToPlayingItem.Text" xml:space="preserve">
<value>定位到播放項</value>
</data>
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
<value>選擇全部</value>
</data>
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
<data name="MusicGalleryPageSingleLoop.Text" xml:space="preserve">
<value>單曲循環</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>搜歌曲</value>
<value>搜歌曲名稱、藝人、專輯或所在資料夾路徑</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>專輯</value>
@@ -439,12 +460,21 @@
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
<value>藝術家</value>
</data>
<data name="MusicGalleryPageSortByFolder.Content" xml:space="preserve">
<value>文件夹</value>
</data>
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
<value>標題</value>
</data>
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
<value>排序類型</value>
</data>
<data name="MusicGalleryPageStarredPlaylist.Content" xml:space="preserve">
<value>已加星號的歌单</value>
</data>
<data name="MusicGalleryPageStopTrack.Text" xml:space="preserve">
<value>停止</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音樂庫 - BetterLyrics</value>
</data>
@@ -457,6 +487,9 @@
<data name="Pinyin" xml:space="preserve">
<value>普通話拼音</value>
</data>
<data name="PlaylistViewFailed" xml:space="preserve">
<value>由於路徑不存在,因此無法檢視曲目</value>
</data>
<data name="Romaji" xml:space="preserve">
<value>羅馬音</value>
</data>
@@ -550,6 +583,12 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>應用程式啟動時開啟音樂庫視窗</value>
</data>
<data name="SettingsPageAutoPlayWhenOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>打開音樂庫視窗時自動繼續播放</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>自動啟動</value>
</data>
@@ -1192,6 +1231,9 @@
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>目前值: </value>
</data>
<data name="SettingsPageSnowFlakeLayer.Header" xml:space="preserve">
<value>雪花層</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>歌曲標題和藝術家</value>
</data>
@@ -1309,12 +1351,21 @@
<data name="SystemTrayRestart.Text" xml:space="preserve">
<value>重新啟動</value>
</data>
<data name="SystemTraySearch.Text" xml:space="preserve">
<value>歌詞搜尋視窗</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>打開設置</value>
</data>
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌詞視窗切換器</value>
</data>
<data name="TracksAddToPlaylistFailed" xml:space="preserve">
<value>路徑不存在,無法新增曲目</value>
</data>
<data name="TracksAddToPlaylistSuccessfully" xml:space="preserve">
<value>所選曲目已新增到此播放列表</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>未設定翻譯伺服器,請先在設定中進行配置</value>
</data>

View File

@@ -1,25 +0,0 @@
using BetterLyrics.WinUI3.Enums;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace BetterLyrics.WinUI3.TemplateSelector;
public class SongOrderTemplateSelector : DataTemplateSelector
{
public DataTemplate ByTitleTemplate { get; set; }
public DataTemplate ByAlbumTemplate { get; set; }
public DataTemplate ByArtistTemplate { get; set; }
public CommonSongProperty SongOrderType { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
return SongOrderType switch
{
CommonSongProperty.Title => ByTitleTemplate,
CommonSongProperty.Album => ByAlbumTemplate,
CommonSongProperty.Artist => ByArtistTemplate,
_ => ByTitleTemplate
};
}
}

View File

@@ -11,14 +11,12 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class BaseViewModel : ObservableRecipient
{
private protected readonly DispatcherQueue _dispatcherQueue;
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
public BaseViewModel()
{
IsActive = true;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
}
}
}

View File

@@ -38,8 +38,8 @@ namespace BetterLyrics.WinUI3.ViewModels
LiveStates = _liveStatesService.LiveStates;
Volume = SystemVolumeHelper.GetMasterVolume();
SystemVolumeHelper.VolumeChanged += SystemVolumeHelper_VolumeChanged;
Volume = SystemVolumeHelper.MasterVolume;
SystemVolumeHelper.VolumeNotification += SystemVolumeHelper_VolumeNotification;
_mediaSessionsService = mediaSessionsService;
_mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged;
@@ -49,16 +49,16 @@ namespace BetterLyrics.WinUI3.ViewModels
IsSongPlaying = _mediaSessionsService.IsPlaying;
}
private void SystemVolumeHelper_VolumeNotification(object? sender, int e)
{
Volume = e;
}
private void PlaybackService_TimelineChanged(object? sender, Events.TimelineChangedEventArgs e)
{
SongDurationSeconds = (int)e.End.TotalSeconds;
}
private void SystemVolumeHelper_VolumeChanged(int volume)
{
Volume = volume;
}
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
IsSongPlaying = e.IsPlaying;
@@ -139,11 +139,6 @@ namespace BetterLyrics.WinUI3.ViewModels
TimelineSliderThumbLyricsLine = _mediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
}
partial void OnVolumeChanged(int value)
{
SystemVolumeHelper.SetMasterVolume(value);
}
public void Receive(PropertyChangedMessage<TimeSpan> message)
{
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)

View File

@@ -0,0 +1,30 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
public partial class LyricsRendererViewModel
{
public void CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{
_logger.LogInformation("Creating resources... Reason: {Reason}", args.Reason);
switch (args.Reason)
{
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.FirstTime:
_isDeviceChanged = true;
break;
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.NewDevice:
_isDeviceChanged = true;
break;
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.DpiChanged:
break;
default:
break;
}
}
}
}

View File

@@ -18,6 +18,7 @@ using System.Numerics;
using Windows.Foundation;
using Windows.Graphics.Effects;
using Windows.UI;
using static Vanara.PInvoke.Shell32;
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
@@ -115,7 +116,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void DrawSpectrum(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
if (_spectrumAnalyzer != null && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSpectrumOverlayEnabled)
if (_spectrumAnalyzer != null && _spectrumAnalyzer.SmoothSpectrum != null && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSpectrumOverlayEnabled)
{
var points = new Vector2[_spectrumAnalyzer.BarCount];
float pointSpacing = 0;
@@ -128,11 +129,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
for (int i = 0; i < _spectrumAnalyzer.BarCount; i++)
{
float x = i * pointSpacing;
float amplitude = _spectrumAnalyzer.SmoothSpectrum.Average() * 10 + _spectrumAnalyzer.SmoothSpectrum[i] * 0.5f;
float y = (float)_canvasHeight - amplitude;
float y = _spectrumAnalyzer.SmoothSpectrum[i];
points[i] = new Vector2(x, y);
}
// 限制最高点高度
var maxY = points.OrderByDescending(p => p.Y).FirstOrDefault().Y;
var limitY = _canvasHeight * 0.2f;
if (maxY > limitY)
{
var num = (float)(limitY / maxY);
points = points.Select(p => new Vector2(p.X, p.Y * num)).ToArray();
}
points = points.Select(p => new Vector2(p.X, (float)(_canvasHeight - p.Y))).ToArray();
// 用于填充的闭合路径
using var pathBuilder = new CanvasPathBuilder(ds);
pathBuilder.BeginFigure(points[0]);
@@ -164,21 +175,25 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var geometry = CanvasGeometry.CreatePath(pathBuilder);
var gradientStops = new CanvasGradientStop[]
{
new() { Position = 0.0f, Color = _albumArtAccentColor1Transition.Value },
new() { Position = 1.0f, Color = Colors.Transparent }
new() { Position = 0.0f, Color = Colors.Transparent },
new() { Position = 0.7f, Color = Colors.Transparent },
new() { Position = 1.0f, Color = _adaptiveColoredFontColor ?? _albumArtAccentColor1Transition.Value }
};
using var gradientBrush = new CanvasLinearGradientBrush(ds, gradientStops);
gradientBrush.StartPoint = new Vector2((float)_canvasWidth / 2, (float)_canvasHeight);
gradientBrush.EndPoint = new Vector2((float)_canvasWidth / 2, points.Select(p => p.Y).Min());
gradientBrush.StartPoint = new Vector2(0, 0);
gradientBrush.EndPoint = new Vector2(0, (float)_canvasHeight);
// 使用渐变画刷填充
ds.FillGeometry(geometry, gradientBrush);
// 纯色
//ds.FillGeometry(geometry, _adaptiveColoredFontColor ?? _albumArtAccentColor1Transition.Value);
// 绘制轮廓线
// var lineColor = Colors.SkyBlue;
// float strokeWidth = 2f;
// session.DrawGeometry(geometry, lineColor, strokeWidth);
//var lineColor = Colors.SkyBlue;
//float strokeWidth = 2f;
//ds.DrawGeometry(geometry, _albumArtAccentColor4Transition.Value, strokeWidth);
}
}
@@ -340,7 +355,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var combinedDs = combined.CreateDrawingSession();
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
@@ -356,7 +371,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings.IsLyricsLineFadeEnabled);
using var lineMask = CanvasHelper.CreateLineMask(control, line);
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _fgFontColor);
using var effectLayer = new CanvasCommandList(control);

View File

@@ -257,8 +257,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_fluidEffect = null;
}
private async void UpdateFluidEffect(ICanvasAnimatedControl control)
private async void RecreateFluidEffect(ICanvasAnimatedControl control)
{
DisposeFluidEffect();
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/FluidEffect.bin"));
IBuffer buffer = await FileIO.ReadBufferAsync(file);
var bytes = buffer.ToArray();

View File

@@ -100,6 +100,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
_isSpectrumOverlayEnabledChanged = true;
}
else if (message.PropertyName == nameof(LyricsBackgroundSettings.IsFluidOverlayEnabled))
{
_isFluidOverlayEnabledChanged = true;
}
}
}
@@ -404,6 +408,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateSongInfoFontSize();
_isLayoutChanged = true;
// 模拟设备变更,执行全局强制刷新以避免旧值被误用
_isDeviceChanged = true;
}
}
}

View File

@@ -26,6 +26,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
public partial class LyricsRendererViewModel
{
private bool _isLayoutChanged = true;
private bool _isCanvasWidthChanged = false;
private bool _isCanvasHeightChanged = false;
@@ -56,9 +58,12 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isAlbumArtSizeChanged = false;
private bool _isSpectrumOverlayEnabledChanged = true;
private bool _isFluidOverlayEnabledChanged = true;
private bool _isLyrics3DMatrixChanged = true;
private bool _isDeviceChanged = true;
public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args)
{
_elapsedTime = args.Timing.ElapsedTime;
@@ -76,11 +81,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
//_effect?.Properties["iTime"] = Convert.ToSingle(TotalTime.TotalSeconds);
if (_fluidEffect == null)
if (_isDeviceChanged || _isFluidOverlayEnabledChanged)
{
UpdateFluidEffect(control);
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsFluidOverlayEnabled)
{
RecreateFluidEffect(control);
}
else
{
DisposeFluidEffect();
}
_isFluidOverlayEnabledChanged = false;
}
else
if (_fluidEffect != null)
{
var effectTime = Convert.ToSingle(_fluidEffect.Properties["iTime"]);
effectTime += Convert.ToSingle(_elapsedTime.TotalSeconds);
@@ -131,8 +146,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isDebugOverlayEnabledChanged = false;
}
_rotateAngle += _coverRotateBaseSpeed * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
_rotateAngle %= Math.PI * 2;
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed > 0)
{
_rotateAngle += _coverRotateBaseSpeed * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
_rotateAngle %= Math.PI * 2;
}
if (_isSpectrumOverlayEnabledChanged)
{
@@ -165,7 +183,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// }
//}
if (_isCanvasWidthChanged || _isCanvasHeightChanged)
if (_isDeviceChanged || _isCanvasWidthChanged || _isCanvasHeightChanged)
{
UpdateSongInfoFontSize();
@@ -174,6 +192,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_fluidEffect?.Properties["Width"] = (float)control.ConvertDipsToPixels((float)_canvasWidth, CanvasDpiRounding.Round);
_fluidEffect?.Properties["Height"] = (float)control.ConvertDipsToPixels((float)_canvasHeight, CanvasDpiRounding.Round);
//_topMargin = _bottomMargin = _leftMargin = _middleMargin = _rightMargin = Math.Max(_canvasWidth, _canvasHeight) / 30.0;
}
if (_isSongInfoFontSizeChanged || _isSongTitleVisibilityChanged || _isSongArtistsVisibilityChanged)
@@ -189,7 +209,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
}
if (_isDisplayTypeChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
if (_isDeviceChanged || _isDisplayTypeChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
_isSongInfoFontSizeChanged || _isSongTitleVisibilityChanged || _isSongArtistsVisibilityChanged ||
_isCanvasWidthChanged || _isCanvasHeightChanged ||
_isAlbumArtSizeChanged)
@@ -309,20 +329,18 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// 将当前背景图放到 _lastAlbumArtSwBitmap 中 并设置不透明度为 1
// 将新的背景图放到 _albumArtSwBitmap 中 并设置不透明度为 0
// 这样可以实现背景图的连贯渐变效果
if (_albumArtChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
if (_isDeviceChanged || _albumArtChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
_isCanvasHeightChanged || _isCanvasWidthChanged ||
_lyricsBgBrightnessTransition.IsTransitioning ||
_albumArtBgTransition.IsTransitioning)
{
// 必须先在此处重置动画
if (_albumArtChanged)
if (_isDeviceChanged || _albumArtChanged)
{
// 必须先在此处重置动画
_albumArtBgTransition.Reset(0f);
_albumArtBgTransition.StartTransition(1f);
}
// 更新 last 和 current
if (_albumArtChanged)
{
// 更新 last 和 current
if (_lastAlbumArtSwBitmap != null)
{
_lastAlbumArtCanvasBitmap?.Dispose();
@@ -349,50 +367,37 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLyricsLayoutOrientationChanged = false;
_isAlbumArtSizeChanged = false;
if (_isCoverAcrylicEffectAmountChanged)
if (_isDeviceChanged || _isAlbumArtBgOpacityChanged || _isAlbumArtBgBlurAmountChanged || _isCoverAcrylicEffectAmountChanged)
{
UpdateCoverAcrylicOverlay(control);
if (_isDeviceChanged || _isCoverAcrylicEffectAmountChanged)
{
UpdateCoverAcrylicOverlay(control);
}
DisposeAlbumArtBgRenderTarget();
UpdateAlbumArtBgEffect(control);
_isAlbumArtBgEffectChanged = true;
_isCoverAcrylicEffectAmountChanged = false;
}
if (_isAlbumArtBgOpacityChanged)
{
DisposeAlbumArtBgRenderTarget();
UpdateAlbumArtBgEffect(control);
_isAlbumArtBgEffectChanged = true;
_isAlbumArtBgOpacityChanged = false;
}
if (_isAlbumArtBgBlurAmountChanged)
{
DisposeAlbumArtBgRenderTarget();
UpdateAlbumArtBgEffect(control);
_isAlbumArtBgEffectChanged = true;
_isAlbumArtBgBlurAmountChanged = false;
_isCoverAcrylicEffectAmountChanged = false;
}
_albumArtChanged = false;
if (!_isAlbumArtEffectChanged && _albumArtEffect != null)
if (_isDeviceChanged || (!_isAlbumArtEffectChanged && _albumArtEffect != null))
{
UpdateAlbumArtRenderTarget(control);
DisposeAlbumArtEffect();
}
if (!_isAlbumArtBgEffectChanged && _albumArtBgEffect != null)
if (_isDeviceChanged || (!_isAlbumArtBgEffectChanged && _albumArtBgEffect != null))
{
UpdateAlbumArtBgRenderTarget(control);
DisposeAlbumArtBgEffect();
}
if (_isCanvasHeightChanged || _isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
if (_isDeviceChanged || _isCanvasHeightChanged || _isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
{
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
_maxLyricsWidth = Math.Max(_maxLyricsWidth, 0);
@@ -447,6 +452,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_lyricsBgBrightnessTransition.Update(_elapsedTime);
_songInfoOpacityTransition.Update(_elapsedTime);
_canvasYScrollTransition.Update(_elapsedTime);
_isDeviceChanged = false;
}
private string AutoSelectFontFamily(string text)
@@ -710,11 +717,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
if (isLight)
{
_adaptiveColoredFontColor = _albumArtDarkAccentColors.FirstOrDefault();
_adaptiveColoredFontColor = _albumArtDarkAccentColors.ElementAtOrDefault(0);
}
else
{
_adaptiveColoredFontColor = _albumArtLightAccentColors.FirstOrDefault();
_adaptiveColoredFontColor = _albumArtLightAccentColors.ElementAtOrDefault(0);
}
}

View File

@@ -97,17 +97,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private readonly ILiveStatesService _liveStatesService;
private readonly ILogger _logger;
private readonly double _leftMargin = 36f;
private readonly double _middleMargin = 36f;
private readonly double _rightMargin = 36f;
private readonly double _topMargin = 36f;
private readonly double _bottomMargin = 36f;
private double _leftMargin = 36f;
private double _middleMargin = 36f;
private double _rightMargin = 36f;
private double _topMargin = 36f;
private double _bottomMargin = 36f;
private Color _adaptiveGrayedFontColor = Colors.Transparent;
private Color? _adaptiveColoredFontColor = null;
private List<Color> _albumArtLightAccentColors = Enumerable.Repeat(Colors.Transparent, 4).ToList();
private List<Color> _albumArtDarkAccentColors = Enumerable.Repeat(Colors.Transparent, 4).ToList();
private List<Color> _albumArtLightAccentColors = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _albumArtDarkAccentColors = Enumerable.Repeat(Colors.Black, 4).ToList();
private Color _environmentalColor = Colors.Transparent;
private Color _grayedEnvironmentalColor = Colors.Transparent;
@@ -130,8 +130,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
[ObservableProperty]
public partial bool IsPlaying { get; set; } = false;
private bool _isLayoutChanged = true;
private int _timelineSyncThreshold = 0;
private int _phoneticLyricsFontSize = 18;

View File

@@ -78,8 +78,10 @@ namespace BetterLyrics.WinUI3.ViewModels
{
OriginalTitle = _mediaSessionsService.SongInfo.Title,
OriginalArtist = _mediaSessionsService.SongInfo.Artist,
OriginalAlbum = _mediaSessionsService.SongInfo.Album,
MappedTitle = _mediaSessionsService.SongInfo.Title,
MappedArtist = _mediaSessionsService.SongInfo.Artist,
MappedAlbum = _mediaSessionsService.SongInfo.Album,
};
}
else
@@ -95,9 +97,11 @@ namespace BetterLyrics.WinUI3.ViewModels
{
return null;
}
var found = AppSettings.MappedSongSearchQueries
.Where(x => x.OriginalTitle == _mediaSessionsService.SongInfo.Title && x.OriginalArtist == _mediaSessionsService.SongInfo.Artist).FirstOrDefault();
return found;
.Where(x => x.OriginalTitle == _mediaSessionsService.SongInfo.Title && x.OriginalArtist == _mediaSessionsService.SongInfo.Artist && x.OriginalAlbum == _mediaSessionsService.SongInfo.Album);
return found.FirstOrDefault();
}
[RelayCommand]
@@ -118,7 +122,7 @@ namespace BetterLyrics.WinUI3.ViewModels
return await _lyricsSearchService.SearchAllAsync(
MappedSongSearchQuery.MappedTitle,
MappedSongSearchQuery.MappedArtist,
_mediaSessionsService.SongInfo?.Album ?? "",
MappedSongSearchQuery.MappedAlbum,
_mediaSessionsService.SongInfo?.DurationMs ?? 0, token);
}, token)];
IsSearching = false;
@@ -166,6 +170,12 @@ namespace BetterLyrics.WinUI3.ViewModels
MappedSongSearchQuery?.MappedArtist = MappedSongSearchQuery?.OriginalArtist ?? string.Empty;
}
[RelayCommand]
private void ResetMappedAlbum()
{
MappedSongSearchQuery?.MappedAlbum = MappedSongSearchQuery?.OriginalAlbum ?? string.Empty;
}
partial void OnSelectedLyricsSearchResultChanged(LyricsSearchResult? value)
{
MappedSongSearchQuery?.LyricsSearchProvider = value?.Provider;
@@ -173,8 +183,10 @@ namespace BetterLyrics.WinUI3.ViewModels
{
var lyricsParser = new LyricsParser();
lyricsParser.Parse(
MappedSongSearchQuery?.MappedTitle ?? "",
MappedSongSearchQuery?.MappedArtist ?? "",
AppSettings.MappedSongSearchQueries.ToList(),
MappedSongSearchQuery?.OriginalTitle ?? "",
MappedSongSearchQuery?.OriginalArtist ?? "",
MappedSongSearchQuery?.OriginalAlbum ?? "",
value?.Raw, (int?)_mediaSessionsService.SongInfo?.DurationMs, value?.Provider);
LyricsDataArr = [.. lyricsParser.LyricsDataArr];
}

View File

@@ -16,13 +16,13 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.Generic;
using Vanara.PInvoke;
using Windows.Foundation;
using Windows.System;
using Windows.UI;
using WinRT.Interop;
using WinUIEx;
@@ -39,6 +39,7 @@ namespace BetterLyrics.WinUI3
private readonly ILiveStatesService _liveStatesService;
private ForegroundWindowWatcher? _fgWindowWatcher = null;
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
public LyricsWindowViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
{
@@ -54,7 +55,7 @@ namespace BetterLyrics.WinUI3
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
}
[ObservableProperty] public partial AppSettings AppSettings { get; set; }
@@ -142,11 +143,13 @@ namespace BetterLyrics.WinUI3
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
_fgWindowWatcherTimer = _dispatcherQueue.CreateTimer();
_fgWindowWatcher = new ForegroundWindowWatcher(
hwnd,
fgHwnd =>
{
_dispatcherQueueTimer.Debounce(() =>
_fgWindowWatcherTimer.Debounce(() =>
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTop &&
_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling &&

View File

@@ -2,9 +2,11 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -21,13 +23,15 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class MediaSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly IResourceService _resourceService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public MediaSettingsControlViewModel(ISettingsService settingsService)
public MediaSettingsControlViewModel(ISettingsService settingsService, IResourceService resourceService)
{
_settingsService = settingsService;
_resourceService = resourceService;
AppSettings = _settingsService.AppSettings;
}
@@ -57,18 +61,18 @@ namespace BetterLyrics.WinUI3.ViewModels
if (AppSettings.LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo"), InfoBarSeverity.Warning);
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathExistedInfo"));
}
else if (AppSettings.LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
{
// 添加的文件夹是现有文件夹的子文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo"), InfoBarSeverity.Warning);
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathBeIncludedInfo"));
}
else if (AppSettings.LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
)
{
// 添加的文件夹是现有文件夹的父文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo"), InfoBarSeverity.Warning);
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathIncludingOthersInfo"));
}
else
{

View File

@@ -7,8 +7,11 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
@@ -25,6 +28,8 @@ using Windows.ApplicationModel;
using Windows.Media;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.ViewModels
{
@@ -32,11 +37,14 @@ namespace BetterLyrics.WinUI3.ViewModels
{
private readonly ILibWatcherService _libWatcherService;
private readonly ISettingsService _settingsService;
private readonly IResourceService _resourceService;
private readonly MediaPlayer _mediaPlayer = new();
private readonly MediaTimelineController _timelineController = new();
private readonly SystemMediaTransportControls _smtc;
private readonly DispatcherQueueTimer _refreshSongsTimer;
// All songs
private List<Track> _tracks = [];
// Songs in current playlist
@@ -44,6 +52,9 @@ namespace BetterLyrics.WinUI3.ViewModels
// Filtered songs based on search query for current playlist
private List<Track> _filteredTracks = [];
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
public partial bool IsLocalMediaNotFound { get; set; }
@@ -57,12 +68,12 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial List<Track> SelectedTracks { get; set; } = [];
[ObservableProperty]
public partial ObservableCollection<PlayQueueItem> TrackPlayingQueue { get; set; } = [];
public PlayQueueItem? PlayingQueueItem => TrackPlayingQueue.ElementAtOrDefault(PlayingSongIndex);
public partial int SelectedTracksTotalDuration { get; set; } = 0;
[ObservableProperty]
public partial PlaybackOrder PlaybackOrder { get; set; }
public partial ObservableCollection<PlayQueueItem> TrackPlayingQueue { get; set; }
public PlayQueueItem? PlayingQueueItem => TrackPlayingQueue.ElementAtOrDefault(AppSettings.MusicGallerySettings.PlayQueueIndex);
[ObservableProperty]
public partial CommonSongProperty SongOrderType { get; set; } = CommonSongProperty.Title;
@@ -81,34 +92,35 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial Track TrackRightTapped { get; set; } = new();
[ObservableProperty]
public partial int PlayingSongIndex { get; set; } = -1;
[ObservableProperty]
public partial int DisplayedPlayingSongIndex { get; set; } = 0;
[ObservableProperty]
public partial string SongSearchQuery { get; set; } = string.Empty;
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IResourceService resourceService)
{
_settingsService = settingsService;
_refreshSongsTimer = _dispatcherQueue.CreateTimer();
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty));
_settingsService = settingsService;
_resourceService = resourceService;
AppSettings = _settingsService.AppSettings;
TrackPlayingQueue = [.. AppSettings.MusicGallerySettings.PlayQueuePaths.Select(x => new PlayQueueItem(new Track(x)))];
TrackPlayingQueue.CollectionChanged += TrackPlayingQueue_CollectionChanged;
SongsTabInfoList.Add(new SongsTabInfo(_resourceService.GetLocalizedString("MusicGalleryPageAllSongs"), "\uE8A9", false, false, CommonSongProperty.Title, string.Empty));
RefreshSongs();
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
_settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
PlaybackOrder = _settingsService.AppSettings.MusicGallerySettings.PlaybackOrder;
_mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
_mediaPlayer.CommandManager.IsEnabled = false;
_timelineController = _mediaPlayer.TimelineController = new();
_timelineController.PositionChanged += TimelineController_PositionChanged;
_smtc = _mediaPlayer.SystemMediaTransportControls;
_mediaPlayer.CommandManager.IsEnabled = false;
_smtc.IsPlayEnabled = true;
_smtc.IsPauseEnabled = true;
_smtc.IsNextEnabled = true;
@@ -118,6 +130,16 @@ namespace BetterLyrics.WinUI3.ViewModels
_libWatcherService = libWatcherService;
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
if (AppSettings.MusicGallerySettings.AutoPlay)
{
_ = PlayTrackAtAsync(AppSettings.MusicGallerySettings.PlayQueueIndex);
}
}
private void TrackPlayingQueue_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
AppSettings.MusicGallerySettings.PlayQueuePaths = [.. TrackPlayingQueue.Select(x => x.Track.Path)];
}
private void LocalMediaFolders_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
@@ -137,33 +159,33 @@ namespace BetterLyrics.WinUI3.ViewModels
public void PlayNextTrack()
{
switch (PlaybackOrder)
switch (AppSettings.MusicGallerySettings.PlaybackOrder)
{
case PlaybackOrder.RepeatAll:
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (PlayingSongIndex < TrackPlayingQueue.Count - 1)
if (AppSettings.MusicGallerySettings.PlayQueueIndex < TrackPlayingQueue.Count - 1)
{
PlayingSongIndex++;
AppSettings.MusicGallerySettings.PlayQueueIndex++;
}
else
{
PlayingSongIndex = 0;
AppSettings.MusicGallerySettings.PlayQueueIndex = 0;
}
PlayTrack(PlayingQueueItem);
await PlayTrackAsync(PlayingQueueItem);
});
break;
case PlaybackOrder.RepeatOne:
_timelineController.Position = TimeSpan.Zero;
break;
case PlaybackOrder.Shuffle:
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (TrackPlayingQueue.Count > 0)
{
PlayingSongIndex = new Random().Next(0, TrackPlayingQueue.Count);
AppSettings.MusicGallerySettings.PlayQueueIndex = new Random().Next(0, TrackPlayingQueue.Count);
}
PlayTrack(PlayingQueueItem);
await PlayTrackAsync(PlayingQueueItem);
});
break;
default:
@@ -173,33 +195,33 @@ namespace BetterLyrics.WinUI3.ViewModels
private void PlayPreviousTrack()
{
switch (PlaybackOrder)
switch (AppSettings.MusicGallerySettings.PlaybackOrder)
{
case PlaybackOrder.RepeatAll:
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (PlayingSongIndex > 0)
if (AppSettings.MusicGallerySettings.PlayQueueIndex > 0)
{
PlayingSongIndex--;
AppSettings.MusicGallerySettings.PlayQueueIndex--;
}
else
{
PlayingSongIndex = TrackPlayingQueue.Count - 1;
AppSettings.MusicGallerySettings.PlayQueueIndex = TrackPlayingQueue.Count - 1;
}
PlayTrack(PlayingQueueItem);
await PlayTrackAsync(PlayingQueueItem);
});
break;
case PlaybackOrder.RepeatOne:
_timelineController.Position = TimeSpan.Zero;
break;
case PlaybackOrder.Shuffle:
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (TrackPlayingQueue.Count > 0)
{
PlayingSongIndex = new Random().Next(0, TrackPlayingQueue.Count);
AppSettings.MusicGallerySettings.PlayQueueIndex = new Random().Next(0, TrackPlayingQueue.Count);
}
PlayTrack(PlayingQueueItem);
await PlayTrackAsync(PlayingQueueItem);
});
break;
default:
@@ -259,7 +281,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public void RefreshSongs()
{
_dispatcherQueueTimer.Debounce(() =>
_refreshSongsTimer.Debounce(() =>
{
IsDataLoading = true;
_tracks.Clear();
@@ -319,6 +341,24 @@ namespace BetterLyrics.WinUI3.ViewModels
case CommonSongProperty.Artist:
_playlistTracks = _tracks.Where(t => t.Artist.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case CommonSongProperty.Folder:
_playlistTracks = _tracks.Where(t => t.GetParentFolderPath().Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
break;
case CommonSongProperty.M3UFilePath:
if (SelectedSongsTabInfo.FilterValue is string path)
{
if (File.Exists(path))
{
var m3uFileContent = File.ReadAllText(path);
_playlistTracks = _tracks.Where(t => m3uFileContent.Contains(t.Path)).ToList();
}
else
{
_playlistTracks = [];
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("PlaylistViewFailed"), path);
}
}
break;
default:
break;
}
@@ -338,7 +378,8 @@ namespace BetterLyrics.WinUI3.ViewModels
_filteredTracks = _playlistTracks.Where(t =>
t.Title.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
t.Artist.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
t.Album.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase)).ToList();
t.Album.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
t.GetParentFolderPath().Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase)).ToList();
}
private void ApplySongOrderType()
@@ -363,6 +404,12 @@ namespace BetterLyrics.WinUI3.ViewModels
o => ((Track)o).Album
);
break;
case CommonSongProperty.Folder:
GroupedTracks = _filteredTracks.GetGroupedBy(
t => LanguageHelper.GetOrderChar(t.GetParentFolderName()),
o => ((Track)o).Album
);
break;
}
}
@@ -382,12 +429,12 @@ namespace BetterLyrics.WinUI3.ViewModels
ApplyPlaylist();
}
public void PlayTrackAt(int index)
public async Task PlayTrackAtAsync(int index)
{
PlayTrack(TrackPlayingQueue.ElementAtOrDefault(index));
await PlayTrackAsync(TrackPlayingQueue.ElementAtOrDefault(index));
}
public void PlayTrack(PlayQueueItem? playQueueItem)
public async Task PlayTrackAsync(PlayQueueItem? playQueueItem)
{
_timelineController.Pause();
_mediaPlayer.Source = null;
@@ -402,18 +449,9 @@ namespace BetterLyrics.WinUI3.ViewModels
_smtc.IsEnabled = true;
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));
updater.AppMediaId = Package.Current.Id.FullName;
updater.Type = MediaPlaybackType.Music;
updater.MusicProperties.Title = track.Title;
updater.MusicProperties.Artist = track.Artist;
updater.MusicProperties.AlbumTitle = track.Album;
if (track.EmbeddedPictures.FirstOrDefault()?.PictureData is byte[] pictureData)
{
updater.Thumbnail = ImageHelper.ByteArrayToRandomAccessStreamReference(pictureData);
}
else
{
updater.Thumbnail = null;
}
var storageFile = await StorageFile.GetFileFromPathAsync(track.Path);
await updater.CopyFromFileAsync(MediaPlaybackType.Music, storageFile);
updater.Update();
}
}
@@ -431,14 +469,71 @@ namespace BetterLyrics.WinUI3.ViewModels
ApplySongOrderType();
}
partial void OnPlayingSongIndexChanged(int value)
private void AddFileToStarredPlaylists(StorageFile file)
{
DisplayedPlayingSongIndex = value + 1;
AppSettings.StarredPlaylists.Add(new SongsTabInfo
{
FilterProperty = CommonSongProperty.M3UFilePath,
FilterValue = file.Path,
Icon = "\uE7BC",
IsStarred = true,
IsClosable = true,
Name = file.Name
});
}
partial void OnPlaybackOrderChanged(PlaybackOrder value)
[RelayCommand]
private async Task CreatePlaylistAsync()
{
_settingsService.AppSettings.MusicGallerySettings.PlaybackOrder = value;
var window = WindowHelper.GetWindowByWindowType<MusicGalleryWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FileSavePicker();
picker.FileTypeChoices.Add("M3U", new List<string>() { ".m3u" });
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var file = await picker.PickSaveFileAsync();
if (file != null)
{
AddFileToStarredPlaylists(file);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("CreatePlaylistSuccessfully"), file.Path);
}
}
[RelayCommand]
private async Task ImportPlaylistAsync()
{
var window = WindowHelper.GetWindowByWindowType<MusicGalleryWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add(".m3u");
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var file = await picker.PickSingleFileAsync();
if (file != null)
{
AddFileToStarredPlaylists(file);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ImportPlaylistSuccessfully"), file.Path);
}
}
[RelayCommand]
private void SwitchPlaybackOrder()
{
AppSettings.MusicGallerySettings.PlaybackOrder = AppSettings.MusicGallerySettings.PlaybackOrder.GetNext();
}
[RelayCommand]
private async Task StopTrackAsync()
{
await PlayTrackAtAsync(-1);
}
}
}

View File

@@ -6,6 +6,7 @@ using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -33,6 +34,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private readonly ITranslateService _libreTranslateService;
private readonly ILastFMService _lastFMService;
private readonly ISettingsService _settingsService;
private readonly IResourceService _resourceService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
@@ -74,11 +76,13 @@ namespace BetterLyrics.WinUI3.ViewModels
ISettingsService settingsService,
IMediaSessionsService mediaSessionsService,
ITranslateService libreTranslateService,
ILastFMService lastFMService)
ILastFMService lastFMService,
IResourceService resourceService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
_libreTranslateService = libreTranslateService;
_resourceService = resourceService;
_lastFMService = lastFMService;
_lastFMService.UserChanged += LastFMService_UserChanged;
@@ -143,14 +147,14 @@ namespace BetterLyrics.WinUI3.ViewModels
"Hello, world!", AppSettings.TranslationSettings.SelectedTargetLanguageCode, new System.Threading.CancellationToken());
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageServerTestSuccessInfo"));
});
}
catch (Exception)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
});
}
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
@@ -189,11 +193,11 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (testResult)
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageServerTestSuccessInfo"));
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
}
IsLXMusicServerTesting = false;
});

View File

@@ -9,6 +9,7 @@ using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.Views;
@@ -34,6 +35,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
private readonly ISettingsService _settingsService;
private readonly IMediaSessionsService _mediaSessionsService;
private readonly IResourceService _resourceService;
public string Version { get; set; } = MetadataHelper.AppVersion;
@@ -47,10 +49,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; } = "App";
public SettingsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService)
public SettingsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, IResourceService resourceService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
_resourceService = resourceService;
AppSettings = _settingsService.AppSettings;
}
@@ -101,7 +104,7 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("ImportSettingsFailed") ?? "");
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("ImportSettingsFailed") ?? "");
}
}
}
@@ -123,7 +126,7 @@ namespace BetterLyrics.WinUI3.ViewModels
if (folder != null)
{
_settingsService.ExportSettings(folder.Path);
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("ExportSettingsSuccess") ?? "", InfoBarSeverity.Success);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ExportSettingsSuccess") ?? "");
}
}
@@ -146,7 +149,7 @@ namespace BetterLyrics.WinUI3.ViewModels
DirectoryHelper.DeleteAllFiles(PathHelper.iTunesAlbumArtCacheDirectory);
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("ActionCompleted"), InfoBarSeverity.Success);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ActionCompleted"));
}
}
}

View File

@@ -58,5 +58,11 @@ namespace BetterLyrics.WinUI3.ViewModels
{
WindowHelper.OpenOrShowWindow<LyricsWindowSwitchWindow>();
}
[RelayCommand]
private static void OpenLyricsSearchWindow()
{
WindowHelper.OpenOrShowWindow<LyricsSearchWindow>();
}
}
}

View File

@@ -9,8 +9,8 @@
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:labs="using:CommunityToolkit.Labs.WinUI.MarqueeTextRns"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:CommunityToolkit.WinUI.Media"
@@ -25,39 +25,15 @@
SizeChanged="RootGrid_SizeChanged">
<!-- Lyrics area -->
<renderer:LyricsRenderer />
<!--<Image Source="/Assets/Cover.jpg" />-->
<!-- No music playing placeholder -->
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock
x:Uid="MainPageNoMusicPlaying"
HorizontalAlignment="Center"
FontFamily="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsCJKFontFamily, Mode=OneWay}"
FontSize="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.OriginalLyricsFontSize, Mode=OneWay}"
TextWrapping="Wrap" />
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<HyperlinkButton x:Uid="SettingsPageFAQ" NavigateUri="{x:Bind const:Link.FAQUrl}" />
</StackPanel>
</StackPanel>
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
ComparisonCondition="Equal"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="{x:Null}">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Grid>
<dev:SnowFlakeEffect FlakeCount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=OneWay}" Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
<StackPanel
Margin="20"
VerticalAlignment="Bottom"
dev:Growl.GrowlParent="True" />
</ScrollViewer>
<!-- Bottom command area -->
<Grid
@@ -137,11 +113,13 @@
<Button.ContextFlyout>
<Flyout x:Name="VolumeFlyout" ShouldConstrainToRootBounds="False">
<uc:ExtendedSlider
Frequency="1"
Frequency="10"
IsSliderEnabled="False"
Maximum="100"
Minimum="0"
ResetButtonVisibility="Collapsed"
Unit="%"
ValueChangedByUser="ExtendedSlider_ValueChangedByUser"
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
</Flyout>
</Button.ContextFlyout>

View File

@@ -180,5 +180,10 @@ namespace BetterLyrics.WinUI3.Views
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
}
}
private void ExtendedSlider_ValueChangedByUser(object sender, Events.ExtendedSliderValueChangedByUserEventArgs e)
{
SystemVolumeHelper.MasterVolume = ViewModel.Volume;
}
}
}

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -25,11 +27,13 @@ namespace BetterLyrics.WinUI3.Views
/// </summary>
public sealed partial class LyricsSearchWindow : Window
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public LyricsSearchWindow()
{
InitializeComponent();
Title = App.ResourceLoader?.GetString("LyricsSearchPageTitle");
Title = _resourceService.GetLocalizedString("LyricsSearchPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();

View File

@@ -8,7 +8,6 @@
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:CommunityToolkit.WinUI.Media"
xmlns:scontrols="using:ShadowViewer.Controls"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
@@ -57,14 +56,20 @@
</ToolTipService.ToolTip>
</Button>
<!-- Settings -->
<Button
x:Name="SettingsWindowButton"
Click="SettingsWindowButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE713;}"
Style="{StaticResource TitleBarButtonStyle}" />
Style="{StaticResource TitleBarButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="HostWindowSettingsButtonToolTip" />
</ToolTipService.ToolTip>
</Button>
<!-- Lyrics window switcher -->
<Button Click="LyricsWindowSwitchButton_Click" Style="{StaticResource TitleBarButtonStyle}">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
@@ -104,16 +109,5 @@
</Grid>
<scontrols:NotificationPanel
x:Name="TipContainerCenter"
Margin="0,0,0,52"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Canvas.ZIndex="1"
FlowDirection="RightToLeft"
Loaded="TipContainerCenter_Loaded"
Orientation="Vertical"
Visibility="Collapsed" />
</Grid>
</Window>

View File

@@ -4,6 +4,7 @@ using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
@@ -24,6 +25,8 @@ namespace BetterLyrics.WinUI3.Views
public sealed partial class LyricsWindow : Window
{
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
private readonly WindowMessageMonitor _wmm;
public LyricsWindowViewModel ViewModel { get; private set; } = Ioc.Default.GetRequiredService<LyricsWindowViewModel>();
@@ -39,7 +42,7 @@ namespace BetterLyrics.WinUI3.Views
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
Title = App.ResourceLoader!.GetString("LyricsPageTitle");
Title = _resourceService.GetLocalizedString("LyricsPageTitle");
_wmm = new WindowMessageMonitor(this);
_wmm.WindowMessageReceived += Wmm_WindowMessageReceived;
@@ -118,11 +121,6 @@ namespace BetterLyrics.WinUI3.Views
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
}
private void TipContainerCenter_Loaded(object sender, RoutedEventArgs e)
{
App.Current.LyricsWindowNotificationPanel = TipContainerCenter;
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.ExitOrClose();

View File

@@ -6,6 +6,7 @@
xmlns:atl="using:ATL"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dev="using:DevWinUI"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
xmlns:local="using:BetterLyrics.WinUI3.Views"
@@ -13,8 +14,8 @@
xmlns:media="using:CommunityToolkit.WinUI.Media"
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:muxm="using:Microsoft.UI.Xaml.Media"
xmlns:templateselector="using:BetterLyrics.WinUI3.TemplateSelector"
xmlns:ui="using:CommunityToolkit.WinUI"
Loaded="Page_Loaded"
Unloaded="Page_Unloaded"
mc:Ignorable="d">
<Page.Resources>
@@ -25,249 +26,517 @@
</Page.Resources>
<Grid>
<Grid>
<Grid Padding="12,0,12,0" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
x:Name="SongViewer"
Grid.Column="0"
Padding="12">
<Grid x:Name="SongViewer" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Tag>
<Flyout
x:Name="SongFileInfoFlyout"
FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}"
Placement="Bottom"
ShouldConstrainToRootBounds="False">
<ScrollViewer Width="300" Height="300">
<StackPanel Spacing="12">
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoTitle" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}" />
<ScrollViewer MaxWidth="300" MaxHeight="600">
<Grid>
<controls:OpacityMaskView MaxHeight="300" VerticalAlignment="Top">
<controls:OpacityMaskView.OpacityMask>
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="1" Color="Transparent" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</controls:OpacityMaskView.OpacityMask>
<FlipView ItemsSource="{x:Bind ViewModel.TrackRightTapped.EmbeddedPictures, Mode=OneWay}">
<FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding PictureData, Converter={StaticResource ByteArrayToImageConverter}}" />
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</controls:OpacityMaskView>
<StackPanel
Margin="0,200,0,0"
Padding="12"
Spacing="12">
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoTitle" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileArtist" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileAlbum" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoYear" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoDuration" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoBitrate" TextWrapping="Wrap" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoSampleRate" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoBitDepth" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoFormat" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoEncoder" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoPath" />
<HyperlinkButton Padding="0" Click="SongPathHyperlinkButton_Click">
<HyperlinkButton.Content>
<TextBlock
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}"
TextWrapping="Wrap" />
</HyperlinkButton.Content>
</HyperlinkButton>
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoLyrics" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.TrackRightTapped, Converter={StaticResource TrackToLyricsConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileArtist" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileAlbum" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoYear" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoDuration" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoBitrate" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoSampleRate" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoBitDepth" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoFormat" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoEncoder" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock x:Uid="MusicGalleryPageFileInfoPath" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<HyperlinkButton
Margin="-12,0,0,0"
Click="SongPathHyperlinkButton_Click"
Content="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}" />
</StackPanel>
</StackPanel>
</Grid>
</ScrollViewer>
</Flyout>
</Grid.Tag>
<Pivot
VerticalAlignment="Top"
ItemsSource="{x:Bind ViewModel.SongsTabInfoList, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="models:SongsTabInfo">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid
Grid.Column="0"
Background="Transparent"
ColumnSpacing="6"
Tapped="PlaylistGrid_Tapped">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Spacing="6">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
x:Name="PlaylistButton"
Grid.Column="0"
Style="{StaticResource GhostButtonStyle}">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon
Grid.Column="0"
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="{x:Bind Icon}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind Name}" />
</Grid>
<Button
Grid.Column="1"
Click="PlaylistCloseButton_Click"
Content="{ui:FontIcon FontSize=10,
FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8BB;}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource GhostButtonStyle}"
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate />
</Pivot.ItemTemplate>
</Pivot>
Glyph="&#xE728;" />
<TextBlock x:Uid="MusicGalleryPagePlaylist" />
</StackPanel>
</Button.Content>
<Button.Flyout>
<Flyout FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Margin="4"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Bind ViewModel.CreatePlaylistCommand}">
<Button.Content>
<StackPanel
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="6">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE710;" />
<TextBlock x:Uid="MusicGalleryPageNewPlaylist" />
</StackPanel>
</Button.Content>
</Button>
<Button
Grid.Row="1"
Margin="4,1,4,2"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Bind ViewModel.ImportPlaylistCommand}">
<Button.Content>
<StackPanel
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="6">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE74B;" />
<TextBlock x:Uid="MusicGalleryPageImportFromFile" />
</StackPanel>
</Button.Content>
</Button>
<ListView
x:Name="StarredPlaylistsListView"
Grid.Row="2"
ItemsSource="{x:Bind ViewModel.AppSettings.StarredPlaylists, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:SongsTabInfo">
<Grid Tapped="StarredPlaylistsListViewItemGrid_Tapped">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="{x:Bind Icon}" />
<TextBlock
Margin="0,0,0,2"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind Name}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Flyout>
</Button.Flyout>
</Button>
<ListView
Grid.Column="1"
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
ItemsSource="{x:Bind ViewModel.SongsTabInfoList, Mode=OneWay}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:SongsTabInfo">
<Grid Padding="12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
Click="PlaylistFavButton_Click"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource GhostButtonStyle}"
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<ToolTipService.ToolTip>
<Grid>
<TextBlock x:Uid="MusicGalleryPageAddToCustomList" Visibility="{x:Bind IsStarred, Converter={StaticResource BoolNegationToVisibilityConverter}, Mode=OneWay}" />
<TextBlock x:Uid="MusicGalleryPageRemoveFromCustomList" Visibility="{x:Bind IsStarred, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Grid>
</ToolTipService.ToolTip>
<Button.Content>
<Grid>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE734;"
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolNegationToOpacityConverter}, Mode=OneWay}">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE735;"
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolToOpacityConverter}, Mode=OneWay}">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
</Grid>
</Button.Content>
</Button>
<Grid
Grid.Column="1"
Background="Transparent"
ColumnSpacing="6"
Tapped="PlaylistGrid_Tapped">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Column="0"
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="{x:Bind Icon}" />
<TextBlock
Grid.Column="1"
Margin="0,0,0,2"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind Name}" />
</Grid>
<Button
Grid.Column="2"
Click="PlaylistCloseButton_Click"
Content="{ui:FontIcon FontSize=16,
FontFamily={StaticResource IconFontFamily},
Glyph=&#xE711;}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource GhostButtonStyle}"
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<Grid Margin="0,56,0,0" VerticalAlignment="Top">
<AutoSuggestBox
x:Name="SongSearchBox"
x:Uid="MusicGalleryPageSongSearchBox"
Margin="0,-8,0,0"
HorizontalAlignment="Stretch"
QueryIcon="Find"
Text="{x:Bind ViewModel.SongSearchQuery, Mode=TwoWay}" />
</Grid>
<Grid Margin="0,96,0,0" VerticalAlignment="Top">
<StackPanel
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="6">
<ToggleButton
x:Name="SelectAllToggleButton"
x:Uid="MusicGalleryPageSelectAll"
Click="SelectAllToggleButton_Click" />
<Button x:Uid="MusicGalleryPagePlayAll" Click="PlayAllButton_Click" />
<Button x:Uid="MusicGalleryPageAddToPlayingQueue">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToNext" Click="AddSongToQueueNextMenuFlyoutItem_Click" />
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToEnd" Click="AddSongToQueueEndMenuFlyoutItem_Click" />
</MenuFlyout>
</Button.Flyout>
</Button>
<Button x:Uid="MusicGalleryPageAddToCustomList" Visibility="Collapsed">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="MusicGalleryPageNewPlaylist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE710;}" />
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
<StackPanel
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="6">
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock
x:Uid="MusicGalleryPageSortType"
<Grid>
<StackPanel
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="6">
<CheckBox
x:Name="SelectAllCheckBox"
MinWidth="20"
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<controls:Segmented
x:Name="Segmented"
SelectedIndex="{x:Bind ViewModel.SongOrderType, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}"
SelectionMode="Single">
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByTitle" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC4F;}" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByAlbum" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE93C;}" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByArtist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
</controls:Segmented>
</StackPanel>
</StackPanel>
</Grid>
Checked="SelectAllCheckBox_Checked"
Unchecked="SelectAllCheckBox_Unchecked"
Visibility="{Binding ElementName=SongListViewSelectionModeToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
<SemanticZoom Margin="0,140,0,0">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.SelectedTracks.Count, Mode=OneWay}" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
<TextBlock Text="{x:Bind GroupedTracksCVS.View.Count, Mode=OneWay}" />
</StackPanel>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Text="{x:Bind ViewModel.SelectedTracksTotalDuration, Mode=OneWay, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
</StackPanel>
</StackPanel>
<StackPanel
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="6">
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock
x:Uid="MusicGalleryPageSortType"
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<controls:Segmented
x:Name="Segmented"
SelectedIndex="{x:Bind ViewModel.SongOrderType, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}"
SelectionMode="Single">
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByTitle" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC4F;}" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByAlbum" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE93C;}" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByArtist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByFolder" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B7;}" />
</controls:Segmented>
</StackPanel>
</StackPanel>
</Grid>
</StackPanel>
<SemanticZoom Grid.Row="1">
<SemanticZoom.ZoomedInView>
<ListView
x:Name="SongListView"
ItemsSource="{x:Bind GroupedTracksCVS.View, Mode=OneWay}"
SelectionChanged="SongListView_SelectionChanged"
SelectionMode="Multiple">
<ListView.ContextFlyout>
<MenuBarItemFlyout>
<MenuFlyoutSubItem x:Uid="MusicGalleryPageAddToPlayingQueue" IsEnabled="{x:Bind ViewModel.SelectedTracks.Count, Mode=OneWay, Converter={StaticResource IntToBoolConverter}}">
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToNext" Click="AddSongToQueueNextMenuFlyoutItem_Click" />
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToEnd" Click="AddSongToQueueEndMenuFlyoutItem_Click" />
</MenuFlyoutSubItem>
<MenuFlyoutItem
x:Uid="MusicGalleryPageAddToCustomList"
Click="AddToPlaylistMenuFlyoutItem_Click"
IsEnabled="{x:Bind ViewModel.SelectedTracks.Count, Mode=OneWay, Converter={StaticResource IntToBoolConverter}}">
<MenuFlyoutItem.ContextFlyout>
<Flyout FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}" Placement="Bottom">
<ListView ItemsSource="{x:Bind ViewModel.AppSettings.StarredPlaylists, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:SongsTabInfo">
<Grid Tapped="ToBeAddedPlaylistsListViewItemGrid_Tapped">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="{x:Bind Icon}" />
<TextBlock
Margin="0,0,0,2"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind Name}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</MenuFlyoutItem.ContextFlyout>
</MenuFlyoutItem>
</MenuBarItemFlyout>
</ListView.ContextFlyout>
<ListView.ItemTemplate>
<DataTemplate x:DataType="atl:Track">
<Grid RightTapped="SongListViewItemGrid_RightTapped">
<Grid
Padding="12"
ColumnSpacing="12"
DoubleTapped="SongListViewItem_DoubleTapped">
<Grid
x:Name="SongListViewItemContentGrid"
Padding="12"
ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" CornerRadius="6">
<Image Source="{Binding EmbeddedPictures[0].PictureData, Mode=OneWay, Converter={StaticResource ByteArrayToImageConverter}}" Stretch="UniformToFill" />
</Grid>
<!-- 歌曲名 -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding Title}"
TextWrapping="Wrap" />
<!-- 歌手名 -->
<HyperlinkButton
Grid.Column="2"
VerticalAlignment="Center"
Click="ArtistHyperlibkButton_Click"
Tag="{Binding Artist}">
<TextBlock Text="{Binding Artist}" TextWrapping="Wrap" />
</HyperlinkButton>
<!-- 专辑名 -->
<HyperlinkButton
Grid.Column="3"
VerticalAlignment="Center"
Click="AlbumHyperlibkButton_Click"
Tag="{Binding Album}">
<TextBlock Text="{Binding Album}" TextWrapping="Wrap" />
</HyperlinkButton>
<!-- 年份 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
<Grid Grid.Column="0" CornerRadius="6">
<Image Source="{Binding EmbeddedPictures[0].PictureData, Mode=OneWay, Converter={StaticResource ByteArrayToImageConverter}}" Stretch="Uniform" />
</Grid>
<!-- 基本信息 -->
<Grid Grid.Column="1">
<StackPanel VerticalAlignment="Center" Spacing="6">
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
<StackPanel Orientation="Horizontal" Spacing="6">
<Grid Background="{ThemeResource AccentAcrylicBackgroundFillColorBaseBrush}" CornerRadius="4">
<TextBlock
Margin="4,2"
FontSize="12"
Text="{Binding AudioFormat.ShortName}" />
</Grid>
<HyperlinkButton Padding="0" Click="ArtistHyperlibkButton_Click">
<TextBlock Text="{Binding Artist}" TextWrapping="Wrap" />
</HyperlinkButton>
</StackPanel>
</StackPanel>
</Grid>
<HyperlinkButton Grid.Column="2" Click="AlbumHyperlibkButton_Click">
<TextBlock Text="{Binding Album}" TextWrapping="Wrap" />
</HyperlinkButton>
<!-- 年份 -->
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<HyperlinkButton
Grid.Column="5"
VerticalAlignment="Center"
Click="PathHyperlibkButton_Click"
Content="{Binding Path, Converter={StaticResource PathToParentFolderConverter}}" />
<Button
Grid.Column="6"
Click="SongListViewItemMoreButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE712;}"
Style="{StaticResource GhostButtonStyle}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
@@ -310,7 +579,7 @@
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
<Grid Margin="0,140,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
@@ -325,38 +594,140 @@
</Grid>
<Grid
x:Name="PlayQueue"
Grid.Column="1"
Margin="0,0,12,0">
<Grid x:Name="PlayQueue" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Margin="0,10,0,0" Spacing="6">
<Grid Margin="0,6,0,0" VerticalAlignment="Top">
<TextBlock x:Uid="MusicGalleryPagePlayingQueue" Style="{StaticResource BodyStrongTextBlockStyle}" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.DisplayedPlayingSongIndex, Mode=OneWay}" />
<StackPanel Grid.Row="0" Spacing="6">
<Grid ColumnSpacing="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="MusicGalleryPagePlayingQueue"
Grid.Column="0"
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<StackPanel
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=OneWay, Converter={StaticResource IndexToDisplayConverter}}" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
<TextBlock Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
</StackPanel>
<!-- Stop media session -->
<Button
Grid.Column="2"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.StopTrackCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=16,
Glyph=&#xE71A;}"
Style="{StaticResource GhostButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="MusicGalleryPageStopTrack" />
</ToolTipService.ToolTip>
</Button>
<!-- Playback order -->
<Button
Grid.Column="3"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.SwitchPlaybackOrderCommand}"
Style="{StaticResource GhostButtonStyle}">
<ToolTipService.ToolTip>
<ToolTip>
<ToolTip.Content>
<Grid>
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
</Grid>
</ToolTip.Content>
</ToolTip>
</ToolTipService.ToolTip>
<Button.Content>
<Grid>
<!-- Repeat all -->
<FontIcon
x:Name="PlaybackRepeatAll"
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE8EE;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
<!-- Repeat one -->
<FontIcon
x:Name="PlaybackRepeatOne"
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE8ED;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
<!-- Shuffle -->
<FontIcon
x:Name="PlaybackShuffle"
FontFamily="{StaticResource IconFontFamily}"
FontSize="16"
Glyph="&#xE8B1;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
</Grid>
</Button.Content>
</Button>
<!-- Scroll to playing item -->
<Button
Grid.Column="4"
HorizontalAlignment="Right"
Click="ScrollToPlayingItemButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=16,
Glyph=&#xE7B7;}"
Style="{StaticResource GhostButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="MusicGalleryPageScrollToPlayingItem" />
</ToolTipService.ToolTip>
</Button>
<!-- Empty play queue -->
<Button
Grid.Column="5"
HorizontalAlignment="Right"
Click="EmptyPlayingQueueButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=16,
Glyph=&#xE738;}"
Style="{StaticResource GhostButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="MusicGalleryPageEmptyPlayingQueue" />
</ToolTipService.ToolTip>
</Button>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="6">
<Button x:Uid="MusicGalleryPageEmptyPlayingQueue" Click="EmptyPlayingQueueButton_Click" />
<Button x:Uid="MusicGalleryPageScrollToPlayingItem" Click="ScrollToPlayingItemButton_Click" />
</StackPanel>
<controls:Segmented HorizontalAlignment="Stretch" SelectedIndex="{x:Bind ViewModel.PlaybackOrder, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}">
<controls:Segmented.Items>
<controls:SegmentedItem x:Uid="MusicGalleryPageQueueLoop" />
<controls:SegmentedItem x:Uid="MusicGalleryPageSingleLoop" />
<controls:SegmentedItem x:Uid="MusicGalleryPageQueueRandom" />
</controls:Segmented.Items>
</controls:Segmented>
</StackPanel>
<ListView
x:Name="PlayingQueueListView"
Margin="0,136,0,0"
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.TrackPlayingQueue, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.PlayingSongIndex, Mode=TwoWay}">
SelectedIndex="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,6">
@@ -386,7 +757,7 @@
</ListView.ItemTemplate>
</ListView>
<Grid Margin="0,136,0,0">
<Grid Grid.Row="1">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
@@ -433,5 +804,65 @@
</Grid>
<ProgressRing IsActive="{x:Bind ViewModel.IsDataLoading, Mode=OneWay}" />
</Grid>
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
<StackPanel
Margin="20"
VerticalAlignment="Bottom"
dev:Growl.GrowlParent="True" />
</ScrollViewer>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlaybackOrderState">
<VisualState x:Name="RepeatAll">
<VisualState.StateTriggers>
<ui:CompareStateTrigger
Comparison="Equal"
Value="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
To="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PlaybackRepeatAll.Opacity" Value="1" />
<Setter Target="PlaybackRepeatOne.Opacity" Value="0" />
<Setter Target="PlaybackShuffle.Opacity" Value="0" />
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Visible" />
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Collapsed" />
<Setter Target="PlaybackShuffleHint.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="RepeatOne">
<VisualState.StateTriggers>
<ui:CompareStateTrigger
Comparison="Equal"
Value="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
To="1" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PlaybackRepeatAll.Opacity" Value="0" />
<Setter Target="PlaybackRepeatOne.Opacity" Value="1" />
<Setter Target="PlaybackShuffle.Opacity" Value="0" />
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Collapsed" />
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Visible" />
<Setter Target="PlaybackShuffleHint.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Shuffle">
<VisualState.StateTriggers>
<ui:CompareStateTrigger
Comparison="Equal"
Value="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
To="2" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PlaybackRepeatAll.Opacity" Value="0" />
<Setter Target="PlaybackRepeatOne.Opacity" Value="0" />
<Setter Target="PlaybackShuffle.Opacity" Value="1" />
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Collapsed" />
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Collapsed" />
<Setter Target="PlaybackShuffleHint.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -2,6 +2,8 @@ using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
@@ -31,46 +33,57 @@ namespace BetterLyrics.WinUI3.Views
/// </summary>
public sealed partial class MusicGalleryPage : Page
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public MusicGalleryViewModel ViewModel => (MusicGalleryViewModel)DataContext;
public MusicGalleryPage()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<MusicGalleryViewModel>();
ViewModel.AppSettings.MusicGallerySettings.PropertyChanged += MusicGallerySettings_PropertyChanged;
}
private void SongListViewItemGrid_RightTapped(object sender, RightTappedRoutedEventArgs e)
private void ScrollToPlayingItem()
{
ViewModel.TrackRightTapped = (Track)((FrameworkElement)sender).DataContext;
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
if (ViewModel.PlayingQueueItem == null) return;
if (PlayingQueueListView == null) return;
PlayingQueueListView.ScrollIntoView(ViewModel.PlayingQueueItem);
}
private void MusicGallerySettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(MusicGallerySettings.PlayQueueIndex))
{
ScrollToPlayingItem();
}
}
private async void SongPathHyperlinkButton_Click(object sender, RoutedEventArgs e)
{
await LauncherHelper.SelectAndShowFile($"{((HyperlinkButton)sender).Content}");
await LauncherHelper.SelectAndShowFile(((Track)((HyperlinkButton)sender).DataContext).Path);
}
private void PlayingQueueListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
private async void PlayingQueueListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
{
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
ViewModel.PlayTrack(item);
await ViewModel.PlayTrackAsync(item);
PlayingQueueListView.ScrollIntoView(item);
}
private void EmptyPlayingQueueButton_Click(object sender, RoutedEventArgs e)
private async void EmptyPlayingQueueButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.TrackPlayingQueue.Clear();
ViewModel.PlayingSongIndex = -1;
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = -1;
await ViewModel.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
}
private void ScrollToPlayingItemButton_Click(object sender, RoutedEventArgs e)
{
if (ViewModel.PlayingQueueItem == null) return;
PlayingQueueListView.ScrollIntoView(ViewModel.PlayingQueueItem);
ScrollToPlayingItem();
}
private void RemoveFromPlayingQueueButton_Click(object sender, RoutedEventArgs e)
private async void RemoveFromPlayingQueueButton_Click(object sender, RoutedEventArgs e)
{
bool playNext = false;
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
@@ -90,67 +103,68 @@ namespace BetterLyrics.WinUI3.Views
{
index = ViewModel.TrackPlayingQueue.Count - 1;
}
ViewModel.PlayingSongIndex = index;
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = index;
await ViewModel.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
}
}
private void SongFileInfoMenuFlyoutSubItem_Tapped(object sender, TappedRoutedEventArgs e)
{
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
}
private void AddSongToQueueNextMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
private async void AddSongToQueueNextMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
bool startPlaying = ViewModel.TrackPlayingQueue.Count == 0;
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.PlayingSongIndex + 1, SongListView.SelectedItems.Cast<Track>().Select(x => new PlayQueueItem(x)));
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex + 1, SongListView.SelectedItems.Cast<Track>().Select(x => new PlayQueueItem(x)));
if (startPlaying)
{
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex + 1;
await ViewModel.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
}
}
private void AddSongToQueueEndMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
private async void AddSongToQueueEndMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
bool startPlaying = ViewModel.TrackPlayingQueue.Count == 0;
ViewModel.TrackPlayingQueue.AddRange(SongListView.SelectedItems.Cast<Track>().Select(x => new PlayQueueItem(x)));
if (startPlaying)
{
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex + 1;
await ViewModel.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
}
}
private void SongListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.SelectedTracks = SongListView.SelectedItems.Cast<Track>().ToList();
SelectAllToggleButton.IsChecked = SongListView.SelectedItems.Count == SongListView.Items.Count;
}
private void SelectAllToggleButton_Click(object sender, RoutedEventArgs e)
{
if (SelectAllToggleButton.IsChecked == true)
ViewModel.SelectedTracksTotalDuration = ViewModel.SelectedTracks.Select(x => x.Duration).Sum();
if (SelectAllCheckBox != null)
{
SongListView.SelectAll();
}
else
{
SongListView.SelectedItems.Clear();
if (SongListView.SelectedItems.Count == SongListView.Items.Count)
{
SelectAllCheckBox.IsChecked = true;
}
else if (SongListView.SelectedItems.Count == 0)
{
SelectAllCheckBox.IsChecked = false;
}
}
}
private void ArtistHyperlibkButton_Click(object sender, RoutedEventArgs e)
{
var artist = (string)((HyperlinkButton)sender).Tag;
var playlist = new SongsTabInfo(artist, "\uEFA9", true, CommonSongProperty.Artist, artist);
var artist = ((Track)((FrameworkElement)sender).DataContext).Artist;
var playlist = new SongsTabInfo(artist, "\uEFA9", true, false, CommonSongProperty.Artist, artist);
ViewModel.UpdateSelectedPlaylist(playlist);
}
private void AlbumHyperlibkButton_Click(object sender, RoutedEventArgs e)
{
var album = (string)((HyperlinkButton)sender).Tag;
var playlist = new SongsTabInfo(album, "\uE93C", true, CommonSongProperty.Album, album);
var album = ((Track)((FrameworkElement)sender).DataContext).Album;
var playlist = new SongsTabInfo(album, "\uE93C", true, false, CommonSongProperty.Album, album);
ViewModel.UpdateSelectedPlaylist(playlist);
}
private void PathHyperlibkButton_Click(object sender, RoutedEventArgs e)
{
var track = ((Track)((FrameworkElement)sender).DataContext);
var playlist = new SongsTabInfo(track.GetParentFolderName(), "\uE8B7", true, false, CommonSongProperty.Folder, track.GetParentFolderPath());
ViewModel.UpdateSelectedPlaylist(playlist);
}
@@ -168,19 +182,102 @@ namespace BetterLyrics.WinUI3.Views
ViewModel.ApplyPlaylist();
}
private void PlayAllButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.TrackPlayingQueue.Clear();
ViewModel.PlayingSongIndex = -1;
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.PlayingSongIndex + 1, SongListView.Items.Cast<Track>().Select(x => new PlayQueueItem(x)));
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
ViewModel.CancelRefreshSongs();
}
private void PlaylistFavButton_Click(object sender, RoutedEventArgs e)
{
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
var targetStatus = !playlist.IsStarred;
if (targetStatus)
{
ViewModel.AppSettings.StarredPlaylists.Add(playlist);
}
else
{
ViewModel.AppSettings.StarredPlaylists.Remove(playlist);
}
playlist.IsStarred = targetStatus;
}
private void StarredPlaylistsListViewItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
{
var songsTabInfo = ((SongsTabInfo)((FrameworkElement)sender).DataContext);
if (!ViewModel.SongsTabInfoList.Contains(songsTabInfo))
{
ViewModel.SongsTabInfoList.Add(songsTabInfo);
}
}
private void SongListViewItemMoreButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.TrackRightTapped = (Track)((FrameworkElement)sender).DataContext;
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
}
private void SelectAllCheckBox_Checked(object sender, RoutedEventArgs e)
{
SongListView.SelectAll();
}
private void SelectAllCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
SongListView.SelectedItems.Clear();
}
private void AddToPlaylistMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
((MenuFlyoutItem)sender).ContextFlyout.ShowAt(PlaylistButton);
}
private void ToBeAddedPlaylistsListViewItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
{
var songsTabInfo = ((SongsTabInfo)((FrameworkElement)sender).DataContext);
if (songsTabInfo.FilterProperty == CommonSongProperty.M3UFilePath)
{
if (songsTabInfo.FilterValue is string path)
{
if (File.Exists(path))
{
var content = File.ReadAllText(path);
foreach (var item in ViewModel.SelectedTracks.Select(x => x.Path).ToList())
{
if (!content.Contains(item))
{
content += Environment.NewLine;
content += item;
}
}
File.WriteAllText(path, content);
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("TracksAddToPlaylistSuccessfully"), path);
}
else
{
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("TracksAddToPlaylistFailed"), path);
}
}
}
}
private async void SongListViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
var displayedTracks = SongListView.Items.Cast<Track>();
var track = (Track)((FrameworkElement)sender).DataContext;
// Play all the songs
ViewModel.TrackPlayingQueue.Clear();
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = -1;
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex + 1, displayedTracks.Select(x => new PlayQueueItem(x)));
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = displayedTracks.ToList().IndexOf(track);
await ViewModel.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
ScrollToPlayingItem();
}
}
}

View File

@@ -24,14 +24,22 @@
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE90B;}"
Style="{StaticResource TitleBarButtonStyle}" />
Style="{StaticResource TitleBarButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="SystemTrayLyrics" />
</ToolTipService.ToolTip>
</Button>
<Button
x:Name="SettingsWindowButton"
Click="SettingsWindowButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE713;}"
Style="{StaticResource TitleBarButtonStyle}" />
Style="{StaticResource TitleBarButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="HostWindowSettingsButtonToolTip" />
</ToolTipService.ToolTip>
</Button>
</StackPanel>
</Grid>

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