Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a207224bbf | ||
|
|
2c80e0815a | ||
|
|
7f65b4addb | ||
|
|
210df4dd61 | ||
|
|
3c82908890 | ||
|
|
597cc1ec9b | ||
|
|
e354e29ec1 | ||
|
|
dd5fb91764 | ||
|
|
041459e564 | ||
|
|
7de813b645 | ||
|
|
838d663834 | ||
|
|
9add190383 | ||
|
|
fa58360884 | ||
|
|
4dd032655c | ||
|
|
80984218ec | ||
|
|
df45026638 | ||
|
|
aafc5c9fb5 | ||
|
|
c5fccfbb63 | ||
|
|
42f03b4291 | ||
|
|
3339bb0d41 | ||
|
|
7d60f29845 | ||
|
|
bd7e93f3e6 | ||
|
|
c37c6ab489 | ||
|
|
125794c44f | ||
|
|
f5bdb3a431 |
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.89.0" />
|
||||
Version="1.0.94.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AMLLPlayer.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 84 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/HyPlayer.png
Normal file
|
After Width: | Height: | Size: 830 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PlanetMusic.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
@@ -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,17 @@
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
<TrimmerRootAssembly Include="NAudio.Wasapi" />
|
||||
<TrimmerRootAssembly Include="Vanara.PInvoke.CoreAudio" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\AIMP.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AlbumArtPlaceholder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AMLLPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AppleMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -142,6 +147,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 +189,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 +319,9 @@
|
||||
<Generator></Generator>
|
||||
</PRIResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="TemplateSelector\" />
|
||||
</ItemGroup>
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<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=}">
|
||||
<local:ExtendedSlider
|
||||
Default="12"
|
||||
Frequency="1"
|
||||
|
||||
Maximum="64"
|
||||
Minimum="0"
|
||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageShadowAmount, Mode=TwoWay}" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<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=}"
|
||||
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=}"
|
||||
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=}"
|
||||
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=}"
|
||||
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>
|
||||
|
||||
@@ -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="°"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -52,6 +53,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=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard>
|
||||
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
@@ -70,33 +84,42 @@
|
||||
<Grid Grid.Column="1">
|
||||
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ListViewItem IsEnabled="{Binding IsFound}">
|
||||
<Grid Opacity="{Binding IsFound, Converter={StaticResource BoolToOpacityConverter}}">
|
||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||
<Grid Opacity="{x:Bind IsFound, Converter={StaticResource BoolToOpacityConverter}}">
|
||||
<RelativePanel Padding="0,6">
|
||||
<TextBlock
|
||||
x:Name="SearchedTitle"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
Text="{Binding Title}"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
Text="{x:Bind Title}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedArtists"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="SearchedTitle"
|
||||
Text="{Binding Artist}"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
Text="{x:Bind Artist}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedAlbum"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
RelativePanel.Below="SearchedArtists"
|
||||
Text="{x:Bind Album}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedProvider"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
Text="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
</RelativePanel>
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlNotFound"
|
||||
VerticalAlignment="Center"
|
||||
Text="Not found"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<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=}">
|
||||
<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=}">
|
||||
<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}" />
|
||||
|
||||
@@ -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=}"
|
||||
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=}">
|
||||
<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=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageWindowBounds"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
Title,
|
||||
Album,
|
||||
Artist
|
||||
Artist,
|
||||
Folder
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI;
|
||||
using static Vanara.PInvoke.Ole32;
|
||||
@@ -46,43 +47,14 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return RandomAccessStreamReference.CreateFromStream(stream);
|
||||
}
|
||||
|
||||
public static async Task<IRandomAccessStream> CreateTextPlaceholderBytesAsync(int width, int height)
|
||||
public static async Task<IRandomAccessStream> GetAlbumArtPlaceholderAsync()
|
||||
{
|
||||
using var device = CanvasDevice.GetSharedDevice();
|
||||
using var renderTarget = new CanvasRenderTarget(device, width, height, 96);
|
||||
|
||||
// 随机生成渐变色
|
||||
Windows.UI.Color RandomColor()
|
||||
{
|
||||
var rand = new Random(Guid.NewGuid().GetHashCode());
|
||||
double h = rand.NextDouble() * 360;
|
||||
double s = 0.35 + rand.NextDouble() * 0.3; // 0.35~0.65,适中饱和度
|
||||
double l = 0.5 + rand.NextDouble() * 0.3; // 0.5~0.8,明亮
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, l);
|
||||
}
|
||||
|
||||
Windows.UI.Color color1 = RandomColor();
|
||||
Windows.UI.Color color2 = RandomColor();
|
||||
|
||||
using (var ds = renderTarget.CreateDrawingSession())
|
||||
{
|
||||
// 绘制线性渐变背景
|
||||
using var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
|
||||
{
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(width, height)
|
||||
};
|
||||
ds.FillRectangle(0, 0, width, height, gradientBrush);
|
||||
}
|
||||
|
||||
// 保存为 PNG 并转为 byte[]
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
|
||||
stream.Seek(0);
|
||||
Uri uri = new Uri($"ms-appx:///Assets/AlbumArtPlaceholder.png");
|
||||
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
|
||||
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
public static Task<ThemeColorResult> GetAccentColorAsync(BitmapDecoder decoder, PaletteGeneratorType generatorType)
|
||||
{
|
||||
return generatorType switch
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using NAudio.Dsp;
|
||||
using NAudio.Dsp;
|
||||
using NAudio.Wave;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class SpectrumAnalyzer : IDisposable
|
||||
public partial class SpectrumAnalyzer : IDisposable
|
||||
{
|
||||
private WasapiLoopbackCapture _capture;
|
||||
|
||||
private WasapiLoopbackCapture? _capture;
|
||||
|
||||
private int _sampleRate = 48000;
|
||||
private readonly int _fftLength = 2048;
|
||||
|
||||
|
||||
private readonly float[] _fftLeftBuffer;
|
||||
private readonly float[] _fftRightBuffer;
|
||||
|
||||
private readonly Complex[] _fftLeftData;
|
||||
private readonly Complex[] _fftRightData;
|
||||
|
||||
private float[] _spectrumLeftData;
|
||||
private float[] _spectrumRightData;
|
||||
private float[] _spectrumData;
|
||||
|
||||
private float[]? _spectrumLeftData;
|
||||
private float[]? _spectrumRightData;
|
||||
private float[]? _spectrumData;
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
|
||||
private double[] _hammingWindow;
|
||||
|
||||
private float[] _currentSpectrum;
|
||||
public float[] SmoothSpectrum { get; private set; }
|
||||
|
||||
public int BarCount { get; set; } = 32;
|
||||
public int Sensitivity { get; set; } = 10;
|
||||
private float[]? _currentSpectrum;
|
||||
public float[]? SmoothSpectrum { get; private set; }
|
||||
|
||||
public int BarCount { get; set; } = 16;
|
||||
public int Sensitivity { get; set; } = 100;
|
||||
public float SmoothingFactor { get; set; } = 0.95f;
|
||||
public bool IsCapturing { get; private set; } = false;
|
||||
|
||||
@@ -51,12 +49,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public void StartCapture()
|
||||
{
|
||||
_currentSpectrum = new float[BarCount];
|
||||
SmoothSpectrum = new float[BarCount];
|
||||
|
||||
try
|
||||
{
|
||||
_capture = new WasapiLoopbackCapture();
|
||||
_currentSpectrum = new float[BarCount];
|
||||
SmoothSpectrum = new float[BarCount];
|
||||
|
||||
_capture = new();
|
||||
|
||||
_sampleRate = _capture.WaveFormat.SampleRate;
|
||||
_spectrumLeftData = new float[(int)(24000.0f / _sampleRate * _fftLength) / 2];
|
||||
_spectrumRightData = new float[(int)(24000.0f / _sampleRate * _fftLength) / 2];
|
||||
@@ -67,9 +66,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
IsCapturing = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public void StopCapture()
|
||||
@@ -100,10 +97,15 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_fftRightData[i].Y = 0;
|
||||
}
|
||||
|
||||
if (_spectrumData == null || _spectrumRightData == null || _currentSpectrum == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// FFT
|
||||
FastFourierTransform.FFT(true, (int)Math.Log(_fftLength, 2), _fftLeftData);
|
||||
FastFourierTransform.FFT(true, (int)Math.Log(_fftLength, 2), _fftRightData);
|
||||
for (int i = 0; i < _spectrumLeftData.Length; i++)
|
||||
for (int i = 0; i < _spectrumLeftData?.Length; i++)
|
||||
{
|
||||
float real = (float)_fftLeftData[i].X;
|
||||
float imaginary = (float)_fftLeftData[i].Y;
|
||||
@@ -132,6 +134,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public void UpdateSmoothSpectrum()
|
||||
{
|
||||
if (SmoothSpectrum == null || _currentSpectrum == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BarCount; i++)
|
||||
{
|
||||
SmoothSpectrum[i] = SmoothSpectrum[i] * SmoothingFactor +
|
||||
@@ -143,7 +150,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
// 补偿曲线
|
||||
float[] frequencies = { 20, 50, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 20000 };
|
||||
float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
|
||||
//float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
|
||||
float[] gains = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
if (freq <= frequencies[0])
|
||||
{
|
||||
return gains[0];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
@@ -31,6 +32,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
private static readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
|
||||
private static DispatcherQueueTimer? _setLyricsWindowVisibilityByPlayingStatusTimer;
|
||||
|
||||
public static void HideWindow<T>()
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
@@ -346,27 +349,36 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
}
|
||||
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus()
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dispatcherQueue">请确保此参数指向同一个对象,建议传值 BaseViewModel._dispatcherQueue</param>
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus(DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
var window = GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer ??= dispatcherQueue.CreateTimer();
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
var window = GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
}
|
||||
HideWindow<LyricsWindow>();
|
||||
}
|
||||
HideWindow<LyricsWindow>();
|
||||
}
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(true);
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(true);
|
||||
}
|
||||
OpenOrShowWindow<LyricsWindow>();
|
||||
}
|
||||
OpenOrShowWindow<LyricsWindow>();
|
||||
}
|
||||
}, Constants.Time.DebounceTimeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
public string? Title { get; set; }
|
||||
public string? Artist { get; set; }
|
||||
public string? Album { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ namespace BetterLyrics.WinUI3.Models
|
||||
this.OnPropertyChanged(nameof(AlbumArtLayoutSettings));
|
||||
}
|
||||
|
||||
partial void OnAutoShowOrHideWindowChanged(bool value)
|
||||
{
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
}
|
||||
|
||||
public void UpdateMonitorNameAndBounds()
|
||||
{
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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() { }
|
||||
}
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -9,7 +10,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public partial class SongInfo : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial string? Album { get; set; }
|
||||
public partial string Album { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string Artist { get; set; }
|
||||
@@ -31,4 +32,14 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
public SongInfo() { }
|
||||
}
|
||||
|
||||
public static class SongInfoExtensions
|
||||
{
|
||||
public static SongInfo Placeholder => new()
|
||||
{
|
||||
Title = "N/A",
|
||||
Album = "N/A",
|
||||
Artist = "N/A",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<Grid>
|
||||
<canvas:CanvasAnimatedControl
|
||||
x:Name="LyricsCanvas"
|
||||
CreateResources="LyricsCanvas_CreateResources"
|
||||
Draw="LyricsCanvas_Draw"
|
||||
Update="LyricsCanvas_Update" />
|
||||
</Grid>
|
||||
|
||||
@@ -35,5 +35,10 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
LyricsCanvas.RemoveFromVisualTree();
|
||||
LyricsCanvas = null;
|
||||
}
|
||||
|
||||
private void LyricsCanvas_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
||||
{
|
||||
ViewModel.CreateResources(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,16 +49,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.DockHeight):
|
||||
case nameof(LyricsWindowStatus.DockPlacement):
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
WindowHelper.UpdateWorkArea<LyricsWindow>();
|
||||
await Task.Delay(300);
|
||||
WindowHelper.MoveAndResize<LyricsWindow>(GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.MonitorDeviceName):
|
||||
// 记录切换前的窗口状态是否沾满屏幕
|
||||
bool isStretchedToMonitor = LiveStates.LyricsWindowStatus.WindowBounds == LiveStates.LyricsWindowStatus.MonitorBounds;
|
||||
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
@@ -66,22 +57,6 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
await Task.Delay(300);
|
||||
WindowHelper.MoveAndResize<LyricsWindow>(GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isStretchedToMonitor)
|
||||
{
|
||||
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.MonitorBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowHelper.MoveAndResize<LyricsWindow>(new Rect(
|
||||
LiveStates.LyricsWindowStatus.MonitorBounds.X,
|
||||
LiveStates.LyricsWindowStatus.MonitorBounds.Y,
|
||||
Math.Min(LiveStates.LyricsWindowStatus.MonitorBounds.Width, LiveStates.LyricsWindowStatus.WindowBounds.Width),
|
||||
Math.Min(LiveStates.LyricsWindowStatus.MonitorBounds.Height, LiveStates.LyricsWindowStatus.WindowBounds.Height)
|
||||
));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.IsShownInSwitchers):
|
||||
WindowHelper.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
|
||||
@@ -118,6 +93,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
case nameof(LyricsWindowStatus.TitleBarArea):
|
||||
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -159,7 +137,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
WindowHelper.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
WindowHelper.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
WindowHelper.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -475,84 +491,93 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}, searcher);
|
||||
}
|
||||
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
if (result != null)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Title = qqResult.Title;
|
||||
lyricsSearchResult.Artist = qqResult.Artists.Join(" | ");
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var original = response?.Lrc?.Lyric;
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Title = neteaseResult.Title;
|
||||
lyricsSearchResult.Artist = neteaseResult.Artists.Join(" | ");
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
string? original = null;
|
||||
var candidateWithTranslation = response?.Candidates.Where(x => x.TransId != null).FirstOrDefault();
|
||||
SearchLyricsResponse.Candidate? candidate;
|
||||
if (candidateWithTranslation != null)
|
||||
{
|
||||
candidate = candidateWithTranslation;
|
||||
}
|
||||
else
|
||||
{
|
||||
candidate = response?.Candidates.FirstOrDefault();
|
||||
}
|
||||
if (candidate != null)
|
||||
{
|
||||
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
|
||||
if (candidate.TransId != null)
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
string? translated = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.TransId, candidate.AccessKey);
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var original = response?.Lrc?.Lyric;
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
string? original = null;
|
||||
var candidate = response?.Candidates.FirstOrDefault();
|
||||
if (candidate != null)
|
||||
{
|
||||
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
|
||||
if (original != null)
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.KugouTranslationCacheDirectory
|
||||
);
|
||||
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
|
||||
if (parsedList != null)
|
||||
{
|
||||
string translated = "";
|
||||
foreach (var item in parsedList)
|
||||
{
|
||||
if (item is Lyricify.Lyrics.Models.FullSyllableLineInfo fullSyllableLineInfo)
|
||||
{
|
||||
var startTimeSpan = TimeSpan.FromMilliseconds(fullSyllableLineInfo.StartTime ?? 0);
|
||||
string startTimeStr = startTimeSpan.ToString(@"mm\:ss\.ff");
|
||||
string chTranslation = fullSyllableLineInfo.Translations.GetValueOrDefault("zh") ?? "";
|
||||
translated += $"[{startTimeStr}]{chTranslation}\n";
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.KugouTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Title = kugouResult.Title;
|
||||
lyricsSearchResult.Artist = kugouResult.Artists.Join(" | ");
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
}
|
||||
|
||||
lyricsSearchResult.Title = result?.Title;
|
||||
lyricsSearchResult.Artist = result?.Artists.Join(" | ");
|
||||
lyricsSearchResult.Album = result?.Album;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
@@ -570,6 +595,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
lyricsSearchResult.Raw = raw;
|
||||
lyricsSearchResult.Title = title;
|
||||
lyricsSearchResult.Artist = artist;
|
||||
lyricsSearchResult.Album = "";
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
SongInfo?.PlayerId ?? "",
|
||||
_cachedSongInfo.Title,
|
||||
_cachedSongInfo.Artist,
|
||||
_cachedSongInfo?.Album ?? string.Empty,
|
||||
_cachedSongInfo.Album,
|
||||
_SMTCAlbumArtBuffer,
|
||||
token
|
||||
), token);
|
||||
@@ -46,7 +46,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
using var placeHolderStream = await ImageHelper.CreateTextPlaceholderBytesAsync(500, 500);
|
||||
using var placeHolderStream = await ImageHelper.GetAlbumArtPlaceholderAsync();
|
||||
var tempBuffer = new Windows.Storage.Streams.Buffer((uint)placeHolderStream.Size);
|
||||
await placeHolderStream.ReadAsync(tempBuffer, (uint)placeHolderStream.Size, InputStreamOptions.None);
|
||||
buffer = tempBuffer;
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
if (originalText == null) return;
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
|
||||
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode);
|
||||
|
||||
if (originalLangCode == targetLangCode)
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetLangCode);
|
||||
if (found >= 0)
|
||||
{
|
||||
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
|
||||
_logger.LogInformation("Found translated text in lyrics data at index {FoundIndex}", found);
|
||||
|
||||
_lyricsDataArr[0].SetTranslatedText(_lyricsDataArr[found], _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsTranslationSeparator, 50);
|
||||
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
|
||||
@@ -119,7 +119,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
if (originalText == null) return;
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
|
||||
_logger.LogInformation("Original phonetic code: {OriginalLangCode}", originalLangCode);
|
||||
|
||||
if (originalLangCode == "zh" && _settingsService.AppSettings.TranslationSettings.IsChineseRomanizationEnabled)
|
||||
{
|
||||
@@ -139,7 +139,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetPhoneticCode);
|
||||
if (found >= 0)
|
||||
{
|
||||
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
|
||||
_logger.LogInformation("Found phonetic text in lyrics data at index {FoundIndex}", found);
|
||||
_lyricsDataArr[0].SetPhoneticText(_lyricsDataArr[found], _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsTranslationSeparator, 50);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
SongInfo.PlayerId ?? "",
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.Album,
|
||||
SongInfo.DurationMs ?? 0,
|
||||
SongInfo.SongId,
|
||||
token
|
||||
@@ -174,10 +174,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
if (token.IsCancellationRequested) return;
|
||||
LyricsSearchProvider = lyricsSearchResult?.Provider;
|
||||
|
||||
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider?.ToString() ?? "null");
|
||||
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider);
|
||||
|
||||
var lyricsParser = new LyricsParser();
|
||||
lyricsParser.Parse(SongInfo.Title, SongInfo.Artist, lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs, LyricsSearchProvider);
|
||||
lyricsParser.Parse(
|
||||
_settingsService.AppSettings.MappedSongSearchQueries.ToList(),
|
||||
SongInfo.Title, SongInfo.Artist, SongInfo.Album, lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs, LyricsSearchProvider);
|
||||
_lyricsDataArr = lyricsParser.LyricsDataArr;
|
||||
ApplyChinesePreference();
|
||||
}
|
||||
|
||||
@@ -213,14 +213,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
|
||||
}
|
||||
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
private async void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
|
||||
SendFocusedMessagesAsync();
|
||||
await SendFocusedMessagesAsync();
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties? timelineProperties)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
@@ -241,16 +241,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
if (IsMediaSourceTimelineSyncEnabled(mediaSession.Id))
|
||||
{
|
||||
_cachedPosition = timelineProperties.Position;
|
||||
_cachedPosition = timelineProperties?.Position ?? TimeSpan.Zero;
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties.EndTime));
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties?.EndTime ?? TimeSpan.Zero));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo? playbackInfo)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
@@ -268,7 +268,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
_cachedIsPlaying = playbackInfo?.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
@@ -279,26 +279,28 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProperties)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
if (mediaSession == null)
|
||||
{
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
}
|
||||
|
||||
string sessionId = mediaSession.Id;
|
||||
string? sessionId = mediaSession?.Id;
|
||||
|
||||
var desiredSession = GetCurrentSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != desiredSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(sessionId))
|
||||
if (sessionId != null && !IsMediaSourceEnabled(sessionId))
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (sessionId == Constants.PlayerID.LXMusic)
|
||||
{
|
||||
@@ -315,33 +317,33 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
currentMediaSourceProviderInfo?.PositionOffset = 0;
|
||||
}
|
||||
|
||||
string fixedArtist = mediaProperties.Artist;
|
||||
string fixedAlbum = mediaProperties.AlbumTitle;
|
||||
string fixedArtist = mediaProperties?.Artist ?? "N/A";
|
||||
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
|
||||
string? songId = null;
|
||||
|
||||
if (sessionId == Constants.PlayerID.AppleMusic || sessionId == Constants.PlayerID.AppleMusicAlternative)
|
||||
{
|
||||
fixedArtist = mediaProperties.Artist.Split(" — ").FirstOrDefault() ?? mediaProperties.Artist;
|
||||
fixedAlbum = mediaProperties.Artist.Split(" — ").LastOrDefault() ?? mediaProperties.AlbumTitle;
|
||||
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
|
||||
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
|
||||
}
|
||||
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId))
|
||||
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId ?? ""))
|
||||
{
|
||||
songId = mediaProperties.Genres.FirstOrDefault()?.Replace("NCM-", "");
|
||||
songId = mediaProperties?.Genres.FirstOrDefault()?.Replace("NCM-", "");
|
||||
}
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Title = mediaProperties?.Title ?? "N/A",
|
||||
Artist = fixedArtist,
|
||||
Album = fixedAlbum,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
PlayerId = sessionId,
|
||||
SongId = songId
|
||||
};
|
||||
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
|
||||
_cachedSongInfo.Duration = (int)((_cachedSongInfo.DurationMs ?? 0) / 1000f);
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (sessionId == Constants.PlayerID.LXMusic)
|
||||
{
|
||||
@@ -356,7 +358,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_SMTCAlbumArtBuffer = _lxMusicAlbumArtBytes.AsBuffer();
|
||||
}
|
||||
else if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
else if (mediaProperties?.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
_SMTCAlbumArtBuffer = await ImageHelper.ToBufferAsync(streamReference);
|
||||
}
|
||||
@@ -438,7 +440,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
_cachedIsPlaying = false;
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
@@ -448,14 +450,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private async Task SendFocusedMessagesAsync()
|
||||
{
|
||||
var desiredSession = GetCurrentSession();
|
||||
if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null;
|
||||
|
||||
var mediaProps = await desiredSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession.ControlSession.GetTimelineProperties());
|
||||
var desiredSession = GetCurrentSession();
|
||||
//if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
mediaProps = await desiredSession?.ControlSession?.TryGetMediaPropertiesAsync();
|
||||
}
|
||||
catch (Exception) { }
|
||||
//if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
|
||||
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession?.ControlSession?.GetTimelineProperties());
|
||||
MediaManager_OnAnyMediaPropertyChanged(desiredSession, mediaProps);
|
||||
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession.ControlSession.GetPlaybackInfo());
|
||||
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession?.ControlSession?.GetPlaybackInfo());
|
||||
}
|
||||
|
||||
private void StartSSE()
|
||||
@@ -654,7 +663,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageCode))
|
||||
{
|
||||
_logger.LogInformation("Target language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
|
||||
_logger.LogInformation("Target LibreTranslate language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
@@ -17,14 +16,17 @@ using Windows.Globalization;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
{
|
||||
// TODO 初始化时从文件读取到对象,后续独写操作先操纵对象,写入用 Debounce 写入文件
|
||||
// 新建一个 AppSettings 类
|
||||
public partial class SettingsService : BaseViewModel, ISettingsService
|
||||
{
|
||||
private DispatcherQueueTimer _writeAppSettingsTimer;
|
||||
|
||||
public AppSettings AppSettings { get; set; }
|
||||
|
||||
public SettingsService()
|
||||
{
|
||||
_writeAppSettingsTimer = _dispatcherQueue.CreateTimer();
|
||||
|
||||
AppSettings = ReadAppSettings();
|
||||
|
||||
AppSettings.PropertyChanged += AppSettings_PropertyChanged;
|
||||
@@ -46,6 +48,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();
|
||||
@@ -93,12 +98,12 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
|
||||
private void AppSettings_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
private void AppSettings_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
private void AppSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
@@ -111,7 +116,7 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,7 +137,6 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
/// <returns></returns>
|
||||
public bool ImportSettings(string importPath)
|
||||
{
|
||||
// TODO 导入有问题
|
||||
if (!File.Exists(importPath))
|
||||
return false;
|
||||
|
||||
@@ -161,9 +165,9 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
return data;
|
||||
}
|
||||
|
||||
private void WriteAppSettingsDebounce()
|
||||
private void WriteAppSettings()
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_writeAppSettingsTimer.Debounce(() =>
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
|
||||
@@ -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 & artist</value>
|
||||
</data>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial class BaseViewModel : ObservableRecipient
|
||||
{
|
||||
private protected readonly DispatcherQueue _dispatcherQueue;
|
||||
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
|
||||
|
||||
public BaseViewModel()
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
public void CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
||||
{
|
||||
_logger.LogInformation("Creating resources... Reason: {Reason}", args.Reason);
|
||||
switch (args.Reason)
|
||||
{
|
||||
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.FirstTime:
|
||||
_isDeviceChanged = true;
|
||||
break;
|
||||
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.NewDevice:
|
||||
_isDeviceChanged = true;
|
||||
break;
|
||||
case Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesReason.DpiChanged:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Effects;
|
||||
using Windows.UI;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
@@ -115,7 +116,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
public void DrawSpectrum(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
if (_spectrumAnalyzer != null && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSpectrumOverlayEnabled)
|
||||
if (_spectrumAnalyzer != null && _spectrumAnalyzer.SmoothSpectrum != null && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSpectrumOverlayEnabled)
|
||||
{
|
||||
var points = new Vector2[_spectrumAnalyzer.BarCount];
|
||||
float pointSpacing = 0;
|
||||
@@ -128,11 +129,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
for (int i = 0; i < _spectrumAnalyzer.BarCount; i++)
|
||||
{
|
||||
float x = i * pointSpacing;
|
||||
float amplitude = _spectrumAnalyzer.SmoothSpectrum.Average() * 10 + _spectrumAnalyzer.SmoothSpectrum[i] * 0.5f;
|
||||
float y = (float)_canvasHeight - amplitude;
|
||||
float y = _spectrumAnalyzer.SmoothSpectrum[i];
|
||||
points[i] = new Vector2(x, y);
|
||||
}
|
||||
|
||||
// 限制最高点高度
|
||||
var maxY = points.OrderByDescending(p => p.Y).FirstOrDefault().Y;
|
||||
var limitY = _canvasHeight * 0.2f;
|
||||
if (maxY > limitY)
|
||||
{
|
||||
var num = (float)(limitY / maxY);
|
||||
points = points.Select(p => new Vector2(p.X, p.Y * num)).ToArray();
|
||||
}
|
||||
|
||||
points = points.Select(p => new Vector2(p.X, (float)(_canvasHeight - p.Y))).ToArray();
|
||||
|
||||
// 用于填充的闭合路径
|
||||
using var pathBuilder = new CanvasPathBuilder(ds);
|
||||
pathBuilder.BeginFigure(points[0]);
|
||||
@@ -164,21 +175,25 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
using var geometry = CanvasGeometry.CreatePath(pathBuilder);
|
||||
var gradientStops = new CanvasGradientStop[]
|
||||
{
|
||||
new() { Position = 0.0f, Color = _albumArtAccentColor1Transition.Value },
|
||||
new() { Position = 1.0f, Color = Colors.Transparent }
|
||||
new() { Position = 0.0f, Color = Colors.Transparent },
|
||||
new() { Position = 0.7f, Color = Colors.Transparent },
|
||||
new() { Position = 1.0f, Color = _adaptiveColoredFontColor ?? _albumArtAccentColor1Transition.Value }
|
||||
};
|
||||
|
||||
using var gradientBrush = new CanvasLinearGradientBrush(ds, gradientStops);
|
||||
gradientBrush.StartPoint = new Vector2((float)_canvasWidth / 2, (float)_canvasHeight);
|
||||
gradientBrush.EndPoint = new Vector2((float)_canvasWidth / 2, points.Select(p => p.Y).Min());
|
||||
gradientBrush.StartPoint = new Vector2(0, 0);
|
||||
gradientBrush.EndPoint = new Vector2(0, (float)_canvasHeight);
|
||||
|
||||
// 使用渐变画刷填充
|
||||
ds.FillGeometry(geometry, gradientBrush);
|
||||
|
||||
// 纯色
|
||||
//ds.FillGeometry(geometry, _adaptiveColoredFontColor ?? _albumArtAccentColor1Transition.Value);
|
||||
|
||||
// 绘制轮廓线
|
||||
// var lineColor = Colors.SkyBlue;
|
||||
// float strokeWidth = 2f;
|
||||
// session.DrawGeometry(geometry, lineColor, strokeWidth);
|
||||
//var lineColor = Colors.SkyBlue;
|
||||
//float strokeWidth = 2f;
|
||||
//ds.DrawGeometry(geometry, _albumArtAccentColor4Transition.Value, strokeWidth);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -340,7 +355,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
|
||||
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
|
||||
|
||||
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
|
||||
@@ -356,7 +371,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings.IsLyricsLineFadeEnabled);
|
||||
using var lineMask = CanvasHelper.CreateLineMask(control, line);
|
||||
|
||||
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _fgFontColor);
|
||||
|
||||
using var effectLayer = new CanvasCommandList(control);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -100,6 +100,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
_isSpectrumOverlayEnabledChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsBackgroundSettings.IsFluidOverlayEnabled))
|
||||
{
|
||||
_isFluidOverlayEnabledChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private bool _isCanvasWidthChanged = false;
|
||||
private bool _isCanvasHeightChanged = false;
|
||||
|
||||
@@ -56,9 +58,12 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
private bool _isAlbumArtSizeChanged = false;
|
||||
|
||||
private bool _isSpectrumOverlayEnabledChanged = true;
|
||||
private bool _isFluidOverlayEnabledChanged = true;
|
||||
|
||||
private bool _isLyrics3DMatrixChanged = true;
|
||||
|
||||
private bool _isDeviceChanged = true;
|
||||
|
||||
public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args)
|
||||
{
|
||||
_elapsedTime = args.Timing.ElapsedTime;
|
||||
@@ -76,11 +81,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
//_effect?.Properties["iTime"] = Convert.ToSingle(TotalTime.TotalSeconds);
|
||||
|
||||
if (_fluidEffect == null)
|
||||
if (_isDeviceChanged || _isFluidOverlayEnabledChanged)
|
||||
{
|
||||
UpdateFluidEffect(control);
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsFluidOverlayEnabled)
|
||||
{
|
||||
RecreateFluidEffect(control);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisposeFluidEffect();
|
||||
}
|
||||
|
||||
_isFluidOverlayEnabledChanged = false;
|
||||
}
|
||||
else
|
||||
|
||||
if (_fluidEffect != null)
|
||||
{
|
||||
var effectTime = Convert.ToSingle(_fluidEffect.Properties["iTime"]);
|
||||
effectTime += Convert.ToSingle(_elapsedTime.TotalSeconds);
|
||||
@@ -131,8 +146,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_isDebugOverlayEnabledChanged = false;
|
||||
}
|
||||
|
||||
_rotateAngle += _coverRotateBaseSpeed * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
|
||||
_rotateAngle %= Math.PI * 2;
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed > 0)
|
||||
{
|
||||
_rotateAngle += _coverRotateBaseSpeed * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
|
||||
_rotateAngle %= Math.PI * 2;
|
||||
}
|
||||
|
||||
if (_isSpectrumOverlayEnabledChanged)
|
||||
{
|
||||
@@ -165,7 +183,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
// }
|
||||
//}
|
||||
|
||||
if (_isCanvasWidthChanged || _isCanvasHeightChanged)
|
||||
if (_isDeviceChanged || _isCanvasWidthChanged || _isCanvasHeightChanged)
|
||||
{
|
||||
UpdateSongInfoFontSize();
|
||||
|
||||
@@ -189,7 +207,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
}
|
||||
}
|
||||
|
||||
if (_isDisplayTypeChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
|
||||
if (_isDeviceChanged || _isDisplayTypeChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
|
||||
_isSongInfoFontSizeChanged || _isSongTitleVisibilityChanged || _isSongArtistsVisibilityChanged ||
|
||||
_isCanvasWidthChanged || _isCanvasHeightChanged ||
|
||||
_isAlbumArtSizeChanged)
|
||||
@@ -309,20 +327,18 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
// 将当前背景图放到 _lastAlbumArtSwBitmap 中 并设置不透明度为 1
|
||||
// 将新的背景图放到 _albumArtSwBitmap 中 并设置不透明度为 0
|
||||
// 这样可以实现背景图的连贯渐变效果
|
||||
if (_albumArtChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
|
||||
if (_isDeviceChanged || _albumArtChanged || _isLyricsLayoutOrientationChanged || _isAlbumArtSizeChanged ||
|
||||
_isCanvasHeightChanged || _isCanvasWidthChanged ||
|
||||
_lyricsBgBrightnessTransition.IsTransitioning ||
|
||||
_albumArtBgTransition.IsTransitioning)
|
||||
{
|
||||
// 必须先在此处重置动画
|
||||
if (_albumArtChanged)
|
||||
if (_isDeviceChanged || _albumArtChanged)
|
||||
{
|
||||
// 必须先在此处重置动画
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
}
|
||||
// 更新 last 和 current
|
||||
if (_albumArtChanged)
|
||||
{
|
||||
|
||||
// 更新 last 和 current
|
||||
if (_lastAlbumArtSwBitmap != null)
|
||||
{
|
||||
_lastAlbumArtCanvasBitmap?.Dispose();
|
||||
@@ -349,50 +365,37 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_isLyricsLayoutOrientationChanged = false;
|
||||
_isAlbumArtSizeChanged = false;
|
||||
|
||||
if (_isCoverAcrylicEffectAmountChanged)
|
||||
if (_isDeviceChanged || _isAlbumArtBgOpacityChanged || _isAlbumArtBgBlurAmountChanged || _isCoverAcrylicEffectAmountChanged)
|
||||
{
|
||||
UpdateCoverAcrylicOverlay(control);
|
||||
if (_isDeviceChanged || _isCoverAcrylicEffectAmountChanged)
|
||||
{
|
||||
UpdateCoverAcrylicOverlay(control);
|
||||
}
|
||||
|
||||
DisposeAlbumArtBgRenderTarget();
|
||||
UpdateAlbumArtBgEffect(control);
|
||||
_isAlbumArtBgEffectChanged = true;
|
||||
|
||||
_isCoverAcrylicEffectAmountChanged = false;
|
||||
}
|
||||
|
||||
if (_isAlbumArtBgOpacityChanged)
|
||||
{
|
||||
DisposeAlbumArtBgRenderTarget();
|
||||
UpdateAlbumArtBgEffect(control);
|
||||
_isAlbumArtBgEffectChanged = true;
|
||||
|
||||
_isAlbumArtBgOpacityChanged = false;
|
||||
}
|
||||
|
||||
if (_isAlbumArtBgBlurAmountChanged)
|
||||
{
|
||||
DisposeAlbumArtBgRenderTarget();
|
||||
UpdateAlbumArtBgEffect(control);
|
||||
_isAlbumArtBgEffectChanged = true;
|
||||
|
||||
_isAlbumArtBgBlurAmountChanged = false;
|
||||
_isCoverAcrylicEffectAmountChanged = false;
|
||||
}
|
||||
|
||||
_albumArtChanged = false;
|
||||
|
||||
if (!_isAlbumArtEffectChanged && _albumArtEffect != null)
|
||||
if (_isDeviceChanged || (!_isAlbumArtEffectChanged && _albumArtEffect != null))
|
||||
{
|
||||
UpdateAlbumArtRenderTarget(control);
|
||||
DisposeAlbumArtEffect();
|
||||
}
|
||||
|
||||
if (!_isAlbumArtBgEffectChanged && _albumArtBgEffect != null)
|
||||
if (_isDeviceChanged || (!_isAlbumArtBgEffectChanged && _albumArtBgEffect != null))
|
||||
{
|
||||
UpdateAlbumArtBgRenderTarget(control);
|
||||
DisposeAlbumArtBgEffect();
|
||||
}
|
||||
|
||||
if (_isCanvasHeightChanged || _isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
|
||||
if (_isDeviceChanged || _isCanvasHeightChanged || _isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
|
||||
{
|
||||
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
|
||||
_maxLyricsWidth = Math.Max(_maxLyricsWidth, 0);
|
||||
@@ -447,6 +450,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_lyricsBgBrightnessTransition.Update(_elapsedTime);
|
||||
_songInfoOpacityTransition.Update(_elapsedTime);
|
||||
_canvasYScrollTransition.Update(_elapsedTime);
|
||||
|
||||
_isDeviceChanged = false;
|
||||
}
|
||||
|
||||
private string AutoSelectFontFamily(string text)
|
||||
@@ -710,11 +715,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
if (isLight)
|
||||
{
|
||||
_adaptiveColoredFontColor = _albumArtDarkAccentColors.FirstOrDefault();
|
||||
_adaptiveColoredFontColor = _albumArtDarkAccentColors.ElementAtOrDefault(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_adaptiveColoredFontColor = _albumArtLightAccentColors.FirstOrDefault();
|
||||
_adaptiveColoredFontColor = _albumArtLightAccentColors.ElementAtOrDefault(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,8 +130,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
[ObservableProperty]
|
||||
public partial bool IsPlaying { get; set; } = false;
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private int _timelineSyncThreshold = 0;
|
||||
|
||||
private int _phoneticLyricsFontSize = 18;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.Generic;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.Foundation;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
@@ -39,6 +39,7 @@ namespace BetterLyrics.WinUI3
|
||||
private readonly ILiveStatesService _liveStatesService;
|
||||
|
||||
private ForegroundWindowWatcher? _fgWindowWatcher = null;
|
||||
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
|
||||
{
|
||||
@@ -54,7 +55,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
|
||||
{
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
}
|
||||
|
||||
[ObservableProperty] public partial AppSettings AppSettings { get; set; }
|
||||
@@ -142,11 +143,13 @@ namespace BetterLyrics.WinUI3
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
_fgWindowWatcherTimer = _dispatcherQueue.CreateTimer();
|
||||
_fgWindowWatcher = new ForegroundWindowWatcher(
|
||||
hwnd,
|
||||
fgHwnd =>
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_fgWindowWatcherTimer.Debounce(() =>
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTop &&
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling &&
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private readonly MediaTimelineController _timelineController = new();
|
||||
private readonly SystemMediaTransportControls _smtc;
|
||||
|
||||
private readonly DispatcherQueueTimer _refreshSongsTimer;
|
||||
|
||||
// All songs
|
||||
private List<Track> _tracks = [];
|
||||
// Songs in current playlist
|
||||
@@ -44,6 +46,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; }
|
||||
|
||||
@@ -92,9 +97,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_refreshSongsTimer = _dispatcherQueue.CreateTimer();
|
||||
|
||||
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty));
|
||||
_settingsService = settingsService;
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
|
||||
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, false, CommonSongProperty.Title, string.Empty));
|
||||
|
||||
RefreshSongs();
|
||||
|
||||
@@ -259,7 +267,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public void RefreshSongs()
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_refreshSongsTimer.Debounce(() =>
|
||||
{
|
||||
IsDataLoading = true;
|
||||
_tracks.Clear();
|
||||
@@ -319,6 +327,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 +349,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 +375,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,39 +26,8 @@
|
||||
SizeChanged="RootGrid_SizeChanged">
|
||||
<!-- Lyrics area -->
|
||||
<renderer:LyricsRenderer />
|
||||
<!--<Image Source="/Assets/Cover.jpg" />-->
|
||||
|
||||
<!-- No music playing placeholder -->
|
||||
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
HorizontalAlignment="Center"
|
||||
FontFamily="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsCJKFontFamily, Mode=OneWay}"
|
||||
FontSize="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.OriginalLyricsFontSize, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<HyperlinkButton x:Uid="SettingsPageFAQ" NavigateUri="{x:Bind const:Link.FAQUrl}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Grid>
|
||||
<dev:SnowFlakeEffect FlakeCount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=OneWay}" Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
@@ -137,11 +107,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>
|
||||
|
||||
@@ -180,5 +180,10 @@ namespace BetterLyrics.WinUI3.Views
|
||||
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtendedSlider_ValueChangedByUser(object sender, Events.ExtendedSliderValueChangedByUserEventArgs e)
|
||||
{
|
||||
SystemVolumeHelper.MasterVolume = ViewModel.Volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=}"
|
||||
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=""
|
||||
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolNegationToOpacityConverter}, Mode=OneWay}">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="10"
|
||||
Glyph=""
|
||||
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=}"
|
||||
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=}" />
|
||||
</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=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByAlbum" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByArtist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
</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=}" />
|
||||
</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=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByAlbum" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByArtist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByFolder" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
</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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
FAQ/Snipaste_2025-11-01_12-51-44.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
FAQ/Snipaste_2025-11-01_19-25-56.png
Normal file
|
After Width: | Height: | Size: 543 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 372 KiB |
202
README.CN.md
@@ -1 +1,201 @@
|
||||
[原页面已更改,点此返回项目主页](https://github.com/jayfunc/BetterLyrics)
|
||||

|
||||
|
||||
<div align=center>
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="96">
|
||||
</div>
|
||||
|
||||
<h2 align=center>
|
||||
BetterLyrics
|
||||
</h2>
|
||||
|
||||
<h4 align="center">
|
||||
🤩 一款优雅且高度自定义的歌词/播放器应用,基于 WinUI3/Win2D 构建
|
||||
</h4>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[**_📖 点按此处浏览软件操作说明_**](https://github.com/jayfunc/BetterLyrics/wiki)
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/stargazers)
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<mark>**_💞 BetterLyrics 的发展离不开每一位贡献者、反馈者和用户的全力支持。_**</mark>
|
||||
|
||||
</div>
|
||||
|
||||
## 🎉 该项目入选少数派推荐文章!
|
||||
|
||||
文章链接:[BetterLyrics - 一款专为 Windows 打造的沉浸式流畅歌词显示软件](https://sspai.com/post/101028)
|
||||
|
||||
## 🔈 反馈交流群
|
||||
|
||||
[QQ 群](https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info) (1054700388) | [Discord Server](https://discord.gg/5yAQPnyCKv) | [Telegram Group](https://t.me/+svhSLZ7awPsxNGY1)
|
||||
|
||||
## 🌟 特色功能
|
||||
|
||||
- 🌠 **精美的用户界面**
|
||||
- 流畅、高度自定义的样式、动画、动效
|
||||
- 沉浸式流体背景
|
||||
- 透视/扇形歌词
|
||||
- 雪花效果
|
||||
- 多种歌词滚动函数
|
||||
- ...
|
||||
- ↔️ **强大的歌词翻译**
|
||||
- 本地机器翻译 (支持 30 多种语言)
|
||||
- 自动读取本地音乐文件内嵌歌词
|
||||
- 🧩 **多种歌词源**
|
||||
- 💾 本地源
|
||||
- 音乐文件 (内嵌歌词)
|
||||
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) 文件 (传统格式、增强格式)
|
||||
- [.eslrc](https://github.com/ESLyric/release) 文件
|
||||
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) 文件
|
||||
- ☁️ 在线源
|
||||
- QQ 音乐
|
||||
- 网易云音乐
|
||||
- 酷狗音乐
|
||||
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
|
||||
- [LRCLIB](https://lrclib.net/)
|
||||
- <details><summary>⚠️ Apple Music (需要额外配置)</summary>
|
||||
|
||||
- 浏览器打开 Apple Music,打开开发者工具。刷新网页,回到开发者工具窗口,筛选出 Fetch/XHR,选择一个请求,在请求标头中找到 media-user-token 并复制其值。
|
||||
- 打开 BetterLyrics 转到播放源设置。在 Media-User-Token (for Apple Music) 中粘贴复制的值并点按右侧对勾。
|
||||
|
||||
- 🎶 **支持众多音乐播放器**
|
||||
|
||||
- 点击 [此处](https://github.com/jayfunc/BetterLyrics/wiki/Known-supported-music-players-(configuration-guidance)) 查看详细信息
|
||||
|
||||
- 🪟 **多种显示模式**
|
||||
- **标准模式**
|
||||
- 标准的歌词窗口样式,沉浸式的音乐歌词体验。
|
||||
- **停靠模式**
|
||||
- 停靠在屏幕上/下边缘的轻量歌词窗口,工作休闲互不打扰。
|
||||
- **桌面模式**
|
||||
- 悬浮在所有应用上层,不能被选中,但能直击你的使用需求。
|
||||
- **更多模式...**
|
||||
- 等你来发现...
|
||||
- 🧠 **智能化行为**
|
||||
- 根据歌曲播放状态自动显隐歌词窗口
|
||||
|
||||
## 屏幕截图
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> ⚠️ 由于 GIF 格式帧率限制,效果仅作展示。请以实机效果为准。
|
||||
|
||||

|
||||

|
||||
|
||||
## 演示
|
||||
|
||||
在 [哔哩哔哩](https://www.bilibili.com/video/BV1QRstz1EGt/) 上观看于 2025 年 10 月 21 日上传的演示视频
|
||||
|
||||
## 即刻体验
|
||||
|
||||
<a href="https://apps.microsoft.com/detail/9P1WCD1P597R?referrer=appbadge&mode=direct">
|
||||
<img src="https://get.microsoft.com/images/zh-cn%20dark.svg" width="200"/>
|
||||
</a>
|
||||
|
||||
**无限期**免费试用版和付费版**无任何区别**
|
||||
|
||||
☕ 如果喜欢该软件,请考虑 [捐赠](#捐赠) 或在 **Microsoft Store** 🧧 购买, 感谢您的支持! 🥰
|
||||
|
||||
无法从 Microsoft Store 下载?点按 [此处](https://github.com/jayfunc/BetterLyrics/wiki/Alternative-way-to-download-and-install) 查看其他下载安装方式
|
||||
|
||||
## 构建
|
||||
|
||||
在构建之前确保替换文件 `BetterLyrics\BetterLyrics.WinUI3\BetterLyrics.WinUI3\Constants\LastFMTemplate` 为 `BetterLyrics\BetterLyrics.WinUI3\BetterLyrics.WinUI3\Constants\LastFM.cs`
|
||||
|
||||
## 💖 感谢
|
||||
|
||||
| 项目/包 | 描述 |
|
||||
| :--- | :--- |
|
||||
| [Lyricify-Lyrics-Helper](https://github.com/WXRIW/Lyricify-Lyrics-Helper) | 为 QQ、网易、酷狗在线歌词源提供歌词抓取、解密、解析等一系列方法 |
|
||||
| [lrclib](https://github.com/tranxuanthang/lrclib) | LRCLIB 歌词 API |
|
||||
| [Manzana-Apple-Music-Lyrics](https://github.com/dropcreations/Manzana-Apple-Music-Lyrics) | Apple Music 歌词抓取(Python 实现) |
|
||||
| [Audio Tools Library (ATL) for .NET](https://github.com/Zeugma440/atldotnet) | 从音乐文件提取图片 |
|
||||
| [WinUIEx](https://github.com/dotMorten/WinUIEx) | 提供有关窗口的开箱即用的 Win32 API |
|
||||
| [TagLib#](https://github.com/mono/taglib-sharp) | 读取音乐文件内嵌的原始歌词内容 |
|
||||
| [Vanara](https://github.com/dahall/Vanara) | 提供开箱即用的 Win32 API |
|
||||
| [LibreTranslate](https://github.com/LibreTranslate/LibreTranslate) | 离线翻译核心 |
|
||||
| [Isolation](https://github.com/Storyteller-Studios/Isolation) | 动态流体背景 |
|
||||
| [SpectrumVisualization](https://github.com/Johnwikix/SpectrumVisualization) | 频谱图 |
|
||||
| [DevWinUI](https://github.com/ghost1372/DevWinUI) | 为 WinUI3 提供众多开箱即用的功能 |
|
||||
| ... | ... |
|
||||
|
||||
### 教程、博客等
|
||||
|
||||
- [Stackoverflow - How to animate Margin property in WPF](https://stackoverflow.com/a/21542882/11048731)
|
||||
- [Bilibili -【WinUI3】SystemBackdropController:定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
|
||||
- [cnblogs - .NET App 与 Windows 系统媒体控制(SMTC)交互](https://www.cnblogs.com/TwilightLemon/p/18279496)
|
||||
- [Win2D 中的游戏循环:CanvasAnimatedControl](https://www.cnblogs.com/walterlv/p/10236395.html)
|
||||
- [r2d2rigo/Win2D-Samples](https://github.com/r2d2rigo/Win2D-Samples/blob/master/IrisBlurWin2D/IrisBlurWin2D/MainPage.xaml.cs)
|
||||
- [CommunityToolkit - 从入门到精通](https://mvvm.coldwind.top/)
|
||||
|
||||
## 💡 灵感来源
|
||||
|
||||
- [refined-now-playing-netease](https://github.com/solstice23/refined-now-playing-netease)
|
||||
- [Lyricify-App](https://github.com/WXRIW/Lyricify-App)
|
||||
- [椒盐音乐 Salt Player](https://moriafly.com/program/salt-player)
|
||||
- [MyToolBar](https://github.com/TwilightLemon/MyToolBar)
|
||||
|
||||
## ✍️ 协助翻译
|
||||
|
||||
找不到你的语言?有更好的翻译?没关系!😆
|
||||
|
||||
现在访问 https://crowdin.com/project/betterlyrics/invite?h=c9bfb28fce061484883c0891e7a26f9b2592556 即刻为本应用提供翻译,成为贡献者!
|
||||
|
||||
## 星标记录
|
||||
|
||||
<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="100%" >
|
||||
</div>
|
||||
|
||||
## 欢迎反馈问题、提交代码
|
||||
|
||||
如果发现 Bug 请在 Issues 内提出,同时也欢迎任何想法、建议。
|
||||
|
||||
## 捐赠
|
||||
|
||||
如果你喜欢本应用,请考虑捐赠支持开发者。这将有助于本应用的长远发展。
|
||||
|
||||
通过以下途径捐赠:
|
||||
- [PayPal](https://paypal.me/zhefangpay)
|
||||
- [Buy Me a Coffee](https://buymeacoffee.com/founchoo)
|
||||
- <details><summary>支付宝</summary>
|
||||
|
||||

|
||||
|
||||
</detais>
|
||||
|
||||
- <details><summary>微信</summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
本项目按“原样”提供,不提供任何形式的担保。
|
||||
所有歌词、字体、图标及其他第三方资源均为其各自版权所有者的财产。
|
||||
本项目作者不主张对这些资源的所有权。
|
||||
本项目为非商业用途,不得用于侵犯任何权利。
|
||||
用户有责任确保其使用符合适用的法律和许可协议。
|
||||
67
README.md
@@ -1,15 +1,25 @@
|
||||

|
||||
|
||||
<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>
|
||||
|
||||
<div align=center>
|
||||
<h4 align="center">
|
||||
🤩 An elegant and deeply customizable lyrics & player app, built with WinUI3/Win2D
|
||||
</h4>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[**_📖 Click here to view wiki_**](https://github.com/jayfunc/BetterLyrics/wiki)
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
|
||||
</div>
|
||||
@@ -20,16 +30,17 @@ BetterLyrics
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<mark>**_💞 BetterLyrics is made possible by all its contributors, bug reporters and users._**</mark>
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[**_Click here to view wiki_**](https://github.com/jayfunc/BetterLyrics/wiki)
|
||||
|
||||
</div>
|
||||
**_[中文版 README 请点按此处](https://github.com/jayfunc/BetterLyrics/blob/dev/README.CN.md)_**
|
||||
|
||||
<h4 align="center">
|
||||
Your dynamic lyrics display tool, built with WinUI 3 and Win2D, works with in-app playback and other players
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
## 🎉 This project was featured by SSPAI!
|
||||
|
||||
@@ -42,7 +53,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 +97,18 @@ Check out the article: [BetterLyrics – An immersive and smooth lyrics display
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> ⚠️ 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>
|
||||

|
||||

|
||||
|
||||
## Demonstration
|
||||
|
||||
@@ -136,6 +144,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.
|
||||
@@ -147,7 +156,7 @@ Before you build, make sure that you have already replaced `BetterLyrics\BetterL
|
||||
- [r2d2rigo/Win2D-Samples](https://github.com/r2d2rigo/Win2D-Samples/blob/master/IrisBlurWin2D/IrisBlurWin2D/MainPage.xaml.cs)
|
||||
- [CommunityToolkit - 从入门到精通](https://mvvm.coldwind.top/)
|
||||
|
||||
## Inspired by
|
||||
## 💡 Inspired by
|
||||
|
||||
- [refined-now-playing-netease](https://github.com/solstice23/refined-now-playing-netease)
|
||||
- [Lyricify-App](https://github.com/WXRIW/Lyricify-App)
|
||||
@@ -163,7 +172,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
|
||||
|
||||
BIN
Screenshots/Snipaste_2025-10-31_19-23-17.png
Normal file
|
After Width: | Height: | Size: 620 KiB |
BIN
Screenshots/Snipaste_2025-10-31_19-27-34.png
Normal file
|
After Width: | Height: | Size: 629 KiB |
|
Before Width: | Height: | Size: 642 KiB After Width: | Height: | Size: 642 KiB |
BIN
Screenshots/fs2.png
Normal file
|
After Width: | Height: | Size: 466 KiB |
BIN
Screenshots/narrow.png
Normal file
|
After Width: | Height: | Size: 530 KiB |
|
Before Width: | Height: | Size: 847 KiB |
|
Before Width: | Height: | Size: 823 KiB After Width: | Height: | Size: 578 KiB |