Compare commits

...

14 Commits

Author SHA1 Message Date
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
66 changed files with 856 additions and 472 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.91.0" />
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -59,10 +59,12 @@
<converter:ShortcutToStringConverter x:Key="ShortcutToStringConverter" />
<converter:BoolNegationToVisibilityConverter x:Key="BoolNegationToVisibilityConverter" />
<converter:BoolToOpacityConverter x:Key="BoolToOpacityConverter" />
<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" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
@@ -331,6 +333,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" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -66,6 +66,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Media" 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" />
@@ -86,7 +87,6 @@
<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" />
@@ -112,12 +112,14 @@
<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\AMLLPlayer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Assets\AppleMusic.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -142,6 +144,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 +186,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 +316,9 @@
<Generator></Generator>
</PRIResource>
</ItemGroup>
<ItemGroup>
<Folder Include="TemplateSelector\" />
</ItemGroup>
<!-- Publish Properties -->
<PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>

View File

@@ -41,7 +41,7 @@
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEA3A;}">
<local:ExtendedSlider
Default="12"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -51,7 +51,7 @@
<controls: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}" />

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

@@ -18,7 +18,7 @@
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE790;}">
<controls: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" />
@@ -26,18 +26,14 @@
</ComboBox>
</controls:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPagePureLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEB42;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<controls:SettingsExpander x:Uid="SettingsPagePureLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsPureColorOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -47,18 +43,14 @@
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageAlbumArtLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE93C;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<controls:SettingsExpander x:Uid="SettingsPageAlbumArtLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -68,7 +60,7 @@
<controls:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="50"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -78,7 +70,7 @@
<controls: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}" />
@@ -87,7 +79,7 @@
<controls: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}" />
@@ -96,18 +88,14 @@
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageFluidLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEDA8;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<controls:SettingsExpander x:Uid="SettingsPageFluidLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsFluidOverlayEnabled, Mode=OneWay}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -124,14 +112,24 @@
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsExpander
x:Uid="SettingsPageSpectrumLayer"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF61F;}"
IsExpanded="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander x:Uid="SettingsPageSnowFlakeLayer" IsExpanded="{x:Bind LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAmount">
<uc:ExtendedSlider
Maximum="100"
Minimum="10"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageSpectrumLayer">
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -27,7 +27,7 @@
<local:ExtendedSlider
x:Uid="SettingsPageLyricsVerticalEdgeOpacitySlider"
Default="0"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -38,7 +38,7 @@
<local:ExtendedSlider
x:Uid="SettingsPageLyricsBlurAmountExtendedSlider"
Default="5"
Frequency="1"
Maximum="10"
Minimum="0"
Value="{x:Bind LyricsEffectSettings.LyricsBlurAmount, Mode=TwoWay}" />
@@ -111,7 +111,7 @@
<controls: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}" />
@@ -137,7 +137,7 @@
<controls: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}" />
@@ -156,7 +156,7 @@
<controls: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}" />
@@ -175,7 +175,7 @@
<controls:SettingsCard IsEnabled="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
@@ -196,7 +196,7 @@
<controls:SettingsCard Header="X" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
@@ -205,7 +205,7 @@
<controls:SettingsCard Header="Y" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"
@@ -214,7 +214,7 @@
<controls:SettingsCard Header="Z" IsEnabled="{x:Bind LyricsEffectSettings.Is3DLyricsEnabled, Mode=OneWay}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="90"
Minimum="-90"
Unit="°"

View File

@@ -52,6 +52,19 @@
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsSearchControlAlbum" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<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>
</controls:SettingsCard>
<controls:SettingsCard>
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
</controls:SettingsCard>
@@ -78,6 +91,7 @@
x:Name="SearchedTitle"
RelativePanel.AlignLeftWithPanel="True"
Text="{Binding Title}"
TextWrapping="Wrap"
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Name="SearchedArtists"
@@ -85,6 +99,14 @@
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.Below="SearchedTitle"
Text="{Binding Artist}"
TextWrapping="Wrap"
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Name="SearchedAlbum"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
RelativePanel.Below="SearchedArtists"
Text="{Binding Album}"
TextWrapping="Wrap"
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
x:Name="SearchedProvider"

View File

@@ -58,7 +58,6 @@
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB42;}">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
@@ -68,8 +67,7 @@
<controls: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>
@@ -184,6 +182,7 @@
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- 字体大小 -->
<controls:SettingsExpander x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageAutoAdjust">
@@ -192,7 +191,7 @@
<controls: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}" />
@@ -200,7 +199,7 @@
<controls: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}" />
@@ -208,7 +207,7 @@
<controls: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}" />

View File

@@ -26,11 +26,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}" />
@@ -213,7 +211,6 @@
<controls: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"
@@ -227,6 +224,18 @@
</ComboBox>
</controls:SettingsCard>
<controls: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>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
@@ -251,18 +260,6 @@
</controls:SettingsExpander.Items>
</controls: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
x:Uid="SettingsPageWindowBounds"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},

View File

@@ -322,7 +322,7 @@
<!-- LX music server -->
<TextBlock x:Uid="SettingsPageLXMusicServer" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard Header="SettingsPageServerAddress">
<controls:SettingsCard x:Uid="SettingsPageServerAddress">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"

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,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

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

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

@@ -37,9 +37,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 +57,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);
}

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

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public class PhoneticHelper
public static class PhoneticHelper
{
public const string PinyinCode = "zh-pinyin";
public const string JyutpingCode = "zh-jyutping";
@@ -34,7 +34,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 +

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,22 @@
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 ?? "";
}
}
}

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

@@ -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

@@ -9,7 +9,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; }

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

@@ -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);

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;
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;
}
@@ -485,6 +501,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
FileHelper.WriteLyricsCache(
title,
artist,
album,
translated,
LyricsFormat.Lrc,
PathHelper.QQTranslationCacheDirectory
@@ -492,8 +509,6 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}
lyricsSearchResult.Raw = original;
lyricsSearchResult.Title = qqResult.Title;
lyricsSearchResult.Artist = qqResult.Artists.Join(" | ");
}
else if (result is NeteaseSearchResult neteaseResult)
{
@@ -505,6 +520,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
FileHelper.WriteLyricsCache(
title,
artist,
album,
translated,
LyricsFormat.Lrc,
PathHelper.NeteaseTranslationCacheDirectory
@@ -512,47 +528,53 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}
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();
}
var candidate = response?.Candidates.FirstOrDefault();
if (candidate != null)
{
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
if (candidate.TransId != null)
if (original != null)
{
string? translated = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.TransId, candidate.AccessKey);
if (!string.IsNullOrEmpty(translated))
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
if (parsedList != null)
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
);
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.Title = result?.Title;
lyricsSearchResult.Artist = result?.Artists.Join(" | ");
lyricsSearchResult.Album = result?.Album;
return lyricsSearchResult;
}
@@ -570,6 +592,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);

View File

@@ -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
@@ -177,7 +177,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider?.ToString() ?? "null");
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

@@ -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,7 +16,6 @@ using Windows.Globalization;
namespace BetterLyrics.WinUI3.Services.SettingsService
{
// TODO 初始化时从文件读取到对象,后续独写操作先操纵对象,写入用 Debounce 写入文件
// 新建一个 AppSettings 类
public partial class SettingsService : BaseViewModel, ISettingsService
{
@@ -46,6 +44,9 @@ 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.Version = MetadataHelper.AppVersion;
EnsureMediaSourceProvidersInfo();
@@ -132,7 +133,6 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
/// <returns></returns>
public bool ImportSettings(string importPath)
{
// TODO 导入有问题
if (!File.Exists(importPath))
return false;

View File

@@ -249,6 +249,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>Translation provider</value>
</data>
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
<value>Album</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>Artist</value>
</data>
@@ -431,7 +434,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<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 +442,18 @@ 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="MusicGalleryPageTitle" xml:space="preserve">
<value>Music gallery - BetterLyrics</value>
</data>
@@ -1192,6 +1201,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>

View File

@@ -249,6 +249,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻訳プロバイダー</value>
</data>
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
<value>アルバム</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>アーティスト</value>
</data>
@@ -431,7 +434,7 @@
<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 +442,18 @@
<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="MusicGalleryPageTitle" xml:space="preserve">
<value>音楽ギャラリー - BetterLyrics</value>
</data>
@@ -1192,6 +1201,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>

View File

@@ -249,6 +249,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>번역 제공자</value>
</data>
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
<value>앨범</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>아티스트</value>
</data>
@@ -431,7 +434,7 @@
<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 +442,18 @@
<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="MusicGalleryPageTitle" xml:space="preserve">
<value>음악 갤러리 - BetterLyrics</value>
</data>
@@ -1192,6 +1201,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>

View File

@@ -249,6 +249,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻译来源</value>
</data>
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
<value>专辑</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>艺术家</value>
</data>
@@ -431,7 +434,7 @@
<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 +442,18 @@
<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="MusicGalleryPageTitle" xml:space="preserve">
<value>音乐库 - BetterLyrics</value>
</data>
@@ -1192,6 +1201,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>

View File

@@ -249,6 +249,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻譯來源</value>
</data>
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
<value>專輯</value>
</data>
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
<value>藝術家</value>
</data>
@@ -431,7 +434,7 @@
<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 +442,18 @@
<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="MusicGalleryPageTitle" xml:space="preserve">
<value>音樂庫 - BetterLyrics</value>
</data>
@@ -1192,6 +1201,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>

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

@@ -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

@@ -115,7 +115,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;
@@ -133,6 +133,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
points[i] = new Vector2(x, y);
}
// 限制最高点高度
var minY = points.OrderBy(p => p.Y).FirstOrDefault().Y;
var limitY = _canvasHeight * (1 - 0.1f);
if (minY < limitY)
{
var num = (float)(limitY / minY);
points = points.Select(p => new Vector2(p.X, p.Y * num)).ToArray();
}
// 防止越过画布边界
points = points.Select(p => new Vector2(p.X, (float)(Math.Min(_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.8f, 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;
}
}
}

View File

@@ -56,6 +56,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isAlbumArtSizeChanged = false;
private bool _isSpectrumOverlayEnabledChanged = true;
private bool _isFluidOverlayEnabledChanged = true;
private bool _isLyrics3DMatrixChanged = true;
@@ -76,11 +77,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
//_effect?.Properties["iTime"] = Convert.ToSingle(TotalTime.TotalSeconds);
if (_fluidEffect == null)
if (_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);
@@ -710,11 +721,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

@@ -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

@@ -44,6 +44,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; }
@@ -93,8 +96,9 @@ namespace BetterLyrics.WinUI3.ViewModels
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty));
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, false, CommonSongProperty.Title, string.Empty));
RefreshSongs();
@@ -319,6 +323,9 @@ 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;
default:
break;
}
@@ -338,7 +345,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 +371,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;
}
}

View File

@@ -9,6 +9,7 @@
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"
@@ -25,7 +26,8 @@
SizeChanged="RootGrid_SizeChanged">
<!-- Lyrics area -->
<renderer:LyricsRenderer />
<!--<Image Source="/Assets/Cover.jpg" />-->
<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}}" />
<!-- No music playing placeholder -->
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
@@ -36,9 +38,6 @@
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 />
@@ -137,11 +136,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

@@ -13,7 +13,6 @@
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"
Unloaded="Page_Unloaded"
mc:Ignorable="d">
@@ -93,111 +92,193 @@
</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>
<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>
<StackPanel Spacing="6">
<StackPanel VerticalAlignment="Top" Orientation="Horizontal">
<Button x:Uid="MusicGalleryPageStarredPlaylist">
<Button.Flyout>
<Flyout FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}">
<Grid>
<ListView
x:Name="StarredPlaylistsListView"
ItemsSource="{x:Bind ViewModel.AppSettings.StarredPlaylists, Mode=OneWay}"
SelectionMode="None">
<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>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="N/A">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.StarredPlaylists.Count, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.StarredPlaylists.Count, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBlock>
</Grid>
</Flyout>
</Button.Flyout>
</Button>
<Pivot ItemsSource="{x:Bind ViewModel.SongsTabInfoList, Mode=OneWay}" SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="models:SongsTabInfo">
<Grid ColumnSpacing="3">
<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}">
<Button.Content>
<Grid>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="10"
Glyph="&#xE734;"
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolNegationToOpacityConverter}, Mode=OneWay}">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
</FontIcon>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="10"
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=10,
FontFamily={StaticResource IconFontFamily},
Glyph=&#xE711;}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource GhostButtonStyle}"
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate />
</Pivot.ItemTemplate>
</Pivot>
</StackPanel>
<Grid Margin="0,56,0,0" VerticalAlignment="Top">
<AutoSuggestBox
x:Name="SongSearchBox"
x:Uid="MusicGalleryPageSongSearchBox"
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"
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>
<Grid>
<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>
</Grid>
<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>
<SemanticZoom Margin="0,140,0,0">
</StackPanel>
<SemanticZoom Margin="0,120,0,0">
<SemanticZoom.ZoomedInView>
<ListView
x:Name="SongListView"
@@ -214,46 +295,43 @@
ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<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" />
<!-- 基本信息 -->
<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"
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}">
<HyperlinkButton Grid.Column="2" Click="AlbumHyperlibkButton_Click">
<TextBlock Text="{Binding Album}" TextWrapping="Wrap" />
</HyperlinkButton>
<!-- 年份 -->
<TextBlock
Grid.Column="4"
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
@@ -261,12 +339,19 @@
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<HyperlinkButton
Grid.Column="5"
VerticalAlignment="Center"
Click="PathHyperlibkButton_Click"
Content="{Binding Path, Converter={StaticResource PathToParentFolderConverter}}" />
</Grid>
</Grid>
</DataTemplate>
@@ -310,7 +395,7 @@
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
<Grid Margin="0,140,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid Margin="0,120,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"

View File

@@ -142,15 +142,22 @@ namespace BetterLyrics.WinUI3.Views
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);
}
@@ -182,5 +189,29 @@ namespace BetterLyrics.WinUI3.Views
{
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);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 372 KiB

View File

@@ -1,13 +1,23 @@
![](Promotion/banner.png)
<div align=center>
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64">
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="96">
</div>
<h2 align=center>
BetterLyrics
</h2>
<h4 align="center">
Your dynamic lyrics display tool, built with WinUI 3 and Win2D, works with in-app playback and other players
</h4>
<div align="center">
[**_Click here to view wiki_**](https://github.com/jayfunc/BetterLyrics/wiki)
</div>
<div align=center>
![Static Badge](https://img.shields.io/badge/Language-C%23-purple) ![Static Badge](https://img.shields.io/badge/License-MIT-red) ![Static Badge](https://img.shields.io/badge/IDE-Visual%20Studio-purple) ![Static Badge](https://img.shields.io/badge/Framework-WinUI%203-blue)
@@ -20,17 +30,6 @@ BetterLyrics
</div>
<div align="center">
[**_Click here to view wiki_**](https://github.com/jayfunc/BetterLyrics/wiki)
</div>
<h4 align="center">
Your dynamic lyrics display tool, built with WinUI 3 and Win2D, works with in-app playback and other players
</h4>
## 🎉 This project was featured by SSPAI!
Check out the article: [BetterLyrics An immersive and smooth lyrics display tool designed for Windows](https://sspai.com/post/101028)
@@ -42,7 +41,12 @@ Check out the article: [BetterLyrics An immersive and smooth lyrics display
## 🌟 Highlighted features
- 🌠 **Pleasing User Interface**
- Fluent animations and effects
- Smooth and highly personalized style, animations and effects
- Immersive fluid background
- Perspective/fan-shaped lyrics
- Snowflake effect
- Multiple lyrics scrolling functions
- ... (and more)
- ↔️ **Strong Lyrics Translation**
- Offline machine translation (supporting 30+ languages)
- Auto-reading local lyrics files for embedded translation
@@ -81,26 +85,18 @@ Check out the article: [BetterLyrics An immersive and smooth lyrics display
## Screenshots
![](Screenshots/fs2.png)
![](Screenshots/std.png)
![](Screenshots/narrow.png)
![](Screenshots/Snipaste_2025-10-31_19-23-17.png)
![](Screenshots/Snipaste_2025-10-31_19-27-34.png)
![](Screenshots/dock.png)
![](Screenshots/desktop.png)
> ⚠️ Due to GIF format and frame rate limitations, the displayed effect is for preview only. Please refer to the actual device for the actual effect.
<div style="display: flex; justify-content: space-around; align-items: flex-start;">
<img src="Screenshots/std.png" width="49%" >
<img src="Screenshots/std-narrow.png" width="49%">
</div>
<div style="display: flex; justify-content: space-around; align-items: flex-start;">
<img src="Screenshots/PixPin_2025-10-24_18-13-44.gif" width="49%" >
<img src="Screenshots/std-fullscreen.png" width="49%">
</div>
<div style="display: flex; justify-content: space-around; align-items: flex-start;">
<img src="Screenshots/dock.png" width="49%" >
<img src="Screenshots/desktop.png" width="49%">
</div>
<div style="display: flex; justify-content: space-around; align-items: flex-start;">
<img src="Screenshots/PixPin_2025-10-24_18-17-17.gif" width="49%" >
</div>
![](Screenshots/PixPin_2025-10-24_18-13-44.gif)
![](Screenshots/PixPin_2025-10-24_18-17-17.gif)
## Demonstration
@@ -136,6 +132,7 @@ Before you build, make sure that you have already replaced `BetterLyrics\BetterL
| [LibreTranslate](https://github.com/LibreTranslate/LibreTranslate) | Provide the ability for offline lyrics translation |
| [Isolation](https://github.com/Storyteller-Studios/Isolation) | Dynamic fluid background implementation |
| [SpectrumVisualization](https://github.com/Johnwikix/SpectrumVisualization) | Audio visualization reference |
| [DevWinUI](https://github.com/ghost1372/DevWinUI) | Provide many out-of-the-box features for building WinUI 3 applications |
| ... | ... |
### Tutorials/Blogs/etc.
@@ -163,7 +160,7 @@ Visit https://crowdin.com/project/betterlyrics/invite?h=c9bfb28fce061484883c0891
## Star history
<div style="display: flex; justify-content: space-around; align-items: flex-start;">
<img src="https://api.star-history.com/svg?repos=jayfunc/BetterLyrics&type=Date)](https://www.star-history.com/#jayfunc/BetterLyrics&Date" width="49%" >
<img src="https://api.star-history.com/svg?repos=jayfunc/BetterLyrics&type=Date)](https://www.star-history.com/#jayfunc/BetterLyrics&Date" width="100%" >
</div>
## Any issues and PRs are welcome

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

View File

Before

Width:  |  Height:  |  Size: 642 KiB

After

Width:  |  Height:  |  Size: 642 KiB

BIN
Screenshots/fs2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

BIN
Screenshots/narrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 KiB

After

Width:  |  Height:  |  Size: 578 KiB