chores: Add new rule for locally search lyrics (only compare file name)

This commit is contained in:
Zhe Fang
2025-11-16 08:04:22 -05:00
parent 4a389d5e33
commit c471f128c1
95 changed files with 981 additions and 807 deletions

View File

@@ -53,8 +53,6 @@
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
<converter:MillisecondsToFormattedTimeConverter x:Key="MillisecondsToFormattedTimeConverter" />
<converter:MediaSourceProviderToLogoUriConverter x:Key="MediaSourceProviderToLogoUriConverter" />
<converter:MediaSourceProviderToDisplayedNameConverter x:Key="MediaSourceProviderToDisplayedNameConverter" />
<converter:FPSToTimeSpanConverter x:Key="FPSToTimeSpanConverter" />
<converter:ShortcutToStringConverter x:Key="ShortcutToStringConverter" />
<converter:BoolNegationToVisibilityConverter x:Key="BoolNegationToVisibilityConverter" />

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.DiscordService;
using BetterLyrics.WinUI3.Services.LastFMService;
@@ -68,10 +69,10 @@ namespace BetterLyrics.WinUI3
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
WindowHook.OpenOrShowWindow<LyricsWindow>();
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
{
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
}

View File

@@ -61,6 +61,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.2.250402" />
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
<PackageReference Include="csharp-kana" Version="1.0.2" />
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
<PackageReference Include="DevWinUI.Controls" Version="9.5.0" />

View File

@@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace BetterLyrics.WinUI3.Extensions
namespace BetterLyrics.WinUI3.Collections
{
// https://stackoverflow.com/a/32013610/11048731
public class FullyObservableCollection<T> : ObservableCollection<T>

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Constants
{
public class ExtendedGenreFiled
{
public const string NetEaseCloudMusicTrackID = "NCM-";
public const string FileName = "FILENAME-";
}
}

View File

@@ -14,7 +14,6 @@
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
CornerRadius="12">
<FontIcon
Margin="20"

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -34,10 +35,10 @@ namespace BetterLyrics.WinUI3.Controls
private async Task HideAsync()
{
var lyricsWindowSwitchWindow = WindowHelper.GetWindowByWindowType<LyricsWindowSwitchWindow>();
var lyricsWindowSwitchWindow = WindowHook.GetWindowByWindowType<LyricsWindowSwitchWindow>();
lyricsWindowSwitchWindow?.ViewModel.RootGridOpacity = 0;
await Task.Delay(300);
WindowHelper.HideWindow<LyricsWindowSwitchWindow>();
WindowHook.HideWindow<LyricsWindowSwitchWindow>();
}
}
}

View File

@@ -68,10 +68,10 @@
FontFamily="Segoe UI Symbol"
FontSize="12"
Glyph="&#x283F;" />
<ImageIcon Height="16" Source="{Binding Provider, Converter={StaticResource MediaSourceProviderToLogoUriConverter}, Mode=OneWay}" />
<ImageIcon Height="16" Source="{Binding LogoPath, Mode=OneWay}" />
<TextBlock
MaxWidth="200"
Text="{Binding Provider, Converter={StaticResource MediaSourceProviderToDisplayedNameConverter}, Mode=OneWay}"
Text="{Binding DisplayName, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
@@ -242,18 +242,54 @@
<!-- Provider info -->
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.MediaSessionsService.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.OriginalLyricsRef, Mode=OneWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="LyricsPageTranslationProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.TranslatedLyricsRef, Mode=OneWay}" />
<dev:SettingsCard ContentAlignment="Left">
<StackPanel Spacing="6">
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="SettingsPagePlaybackSource"
Grid.Column="0"
VerticalAlignment="Center" />
<RichTextBlock
Grid.Column="1"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
TextWrapping="Wrap">
<Paragraph>
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
<Run Text="(" />
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, Mode=OneWay}" />
<Run Text=")" />
</Paragraph>
</RichTextBlock>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock VerticalAlignment="Center" Text="NCM ID" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.SongId, Mode=OneWay, TargetNullValue=N/A}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" VerticalAlignment="Center" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" VerticalAlignment="Center" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
</StackPanel>
</StackPanel>
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
</dev:SettingsCard>
@@ -276,12 +312,7 @@
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageTranslationConfig" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<dev:SettingsCard.Description>
<HyperlinkButton Margin="0,6,0,0" NavigateUri="https://github.com/LibreTranslate/LibreTranslate">
<TextBlock
x:Uid="SettingsPageTranslationInfoLink"
FontSize="14"
TextWrapping="Wrap" />
</HyperlinkButton>
<HyperlinkButton Content="https://github.com/LibreTranslate/LibreTranslate" NavigateUri="https://github.com/LibreTranslate/LibreTranslate" />
</dev:SettingsCard.Description>
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
</dev:SettingsCard>

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Input;
@@ -89,7 +90,7 @@ namespace BetterLyrics.WinUI3.Controls
private void CheckButton_Click(object sender, RoutedEventArgs e)
{
bool registered = GlobalHotKeyHelper.IsHotKeyRegistered(Shortcut);
bool registered = GlobalHotKeyHook.IsHotKeyRegistered(Shortcut);
if (registered)
{
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageShortcutRegSuccessInfo"));

View File

@@ -1,47 +0,0 @@
using BetterLyrics.WinUI3.Constants;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public class MediaSourceProviderToDisplayedNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string provider)
{
return provider switch
{
PlayerID.Spotify => PlayerName.Spotify,
PlayerID.AppleMusic => PlayerName.AppleMusic,
PlayerID.iTunes => PlayerName.iTunes,
PlayerID.KugouMusic => PlayerName.KugouMusic,
PlayerID.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic,
PlayerID.QQMusic => PlayerName.QQMusic,
PlayerID.LXMusic => PlayerName.LXMusic,
PlayerID.LXMusicPortable => PlayerName.LXMusicPortable,
PlayerID.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11,
PlayerID.AIMP => PlayerName.AIMP,
PlayerID.Foobar2000 => PlayerName.Foobar2000,
PlayerID.MusicBee => PlayerName.MusicBee,
PlayerID.PotPlayer => PlayerName.PotPlayer,
PlayerID.Chrome => PlayerName.Chrome,
PlayerID.Edge => PlayerName.Edge,
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
PlayerID.SaltPlayerForWindows => PlayerName.SaltPlayerForWindows,
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
PlayerID.Listen1 => PlayerName.Listen1,
_ => provider,
};
}
return value?.ToString() ?? "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,49 +0,0 @@
using BetterLyrics.WinUI3.Constants;
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public class MediaSourceProviderToLogoUriConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string provider)
{
return provider switch
{
PlayerID.Spotify => PathHelper.SpotifyLogoPath,
PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath,
PlayerID.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
PlayerID.iTunes => PathHelper.iTunesLogoPath,
PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath,
PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,
PlayerID.QQMusic => PathHelper.QQMusicLogoPath,
PlayerID.LXMusic => PathHelper.LXMusicLogoPath,
PlayerID.LXMusicPortable => PathHelper.LXMusicLogoPath,
PlayerID.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath,
PlayerID.AIMP => PathHelper.AIMPLogoPath,
PlayerID.Foobar2000 => PathHelper.Foobar2000LogoPath,
PlayerID.MusicBee => PathHelper.MusicBeeLogoPath,
PlayerID.PotPlayer => PathHelper.PotPlayerLogoPath,
PlayerID.Chrome => PathHelper.ChromeLogoPath,
PlayerID.Edge => PathHelper.EdgeLogoPath,
PlayerID.BetterLyrics => PathHelper.LogoPath,
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
PlayerID.SaltPlayerForWindows => PathHelper.SaltPlayerForWindowsLogoPath,
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
_ => PathHelper.UnknownPlayerLogoPath,
};
}
return PathHelper.UnknownPlayerLogoPath;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,4 +1,5 @@
using ATL;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Xaml.Data;
using System;
@@ -11,7 +12,7 @@ namespace BetterLyrics.WinUI3.Converter
{
if (value is Track track)
{
return track.GetLyrics();
return track.GetRawLyrics();
}
return "";
}

View File

@@ -1,24 +1,8 @@
using BetterLyrics.WinUI3.Helper;
using System;
namespace BetterLyrics.WinUI3.Enums
namespace BetterLyrics.WinUI3.Enums
{
public enum ChineseRomanization
{
Pinyin,
Jyutping,
}
public static class ChineseRomanizationExtensions
{
public static string ToPhoneticCode(this ChineseRomanization chineseRomanization)
{
return chineseRomanization switch
{
ChineseRomanization.Pinyin => PhoneticHelper.PinyinCode,
ChineseRomanization.Jyutping => PhoneticHelper.JyutpingCode,
_ => throw new ArgumentOutOfRangeException(nameof(chineseRomanization))
};
}
}
}

View File

@@ -1,11 +0,0 @@
// 2025/6/23 by Zhe Fang
using System;
namespace BetterLyrics.WinUI3.Events
{
public class IsPlayingChangedEventArgs(bool isPlaying) : EventArgs
{
public bool IsPlaying { get; set; } = isPlaying;
}
}

View File

@@ -1,12 +0,0 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Models;
using System;
namespace BetterLyrics.WinUI3.Events
{
public class SongInfoChangedEventArgs(SongInfo? songInfo) : EventArgs
{
public SongInfo? SongInfo { get; set; } = songInfo;
}
}

View File

@@ -1,12 +0,0 @@
// 2025/6/23 by Zhe Fang
using System;
namespace BetterLyrics.WinUI3.Events
{
public class TimelineChangedEventArgs(TimeSpan position, TimeSpan end) : EventArgs()
{
public TimeSpan Position { get; set; } = position;
public TimeSpan End { get; set; } = end;
}
}

View File

@@ -0,0 +1,18 @@
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Windowing;
namespace BetterLyrics.WinUI3.Extensions
{
public static class AppWindowExtensions
{
extension(AppWindow appWindow)
{
public void SetIcons()
{
appWindow.SetIcon(PathHelper.LogoPath);
appWindow.SetTaskbarIcon(PathHelper.LogoPath);
appWindow.SetTitleBarIcon(PathHelper.LogoPath);
}
}
}
}

View File

@@ -0,0 +1,22 @@
using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas.Text;
namespace BetterLyrics.WinUI3.Extensions
{
public static class CanvasTextLayoutExtensions
{
extension(CanvasTextLayout? canvasTextLayout)
{
public void SetFontFamily(string? text, string cjk, string latin)
{
if (canvasTextLayout == null) return;
if (text == null) return;
for (int i = 0; i < text.Length; i++)
{
canvasTextLayout.SetFontFamily(i, 1, LanguageHelper.IsCJK(text[i]) ? cjk : latin);
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using System;
namespace BetterLyrics.WinUI3.Extensions
{
public static class ChineseRomanizationExtensions
{
extension(ChineseRomanization chineseRomanization)
{
public string ToPhoneticCode() => chineseRomanization switch
{
ChineseRomanization.Pinyin => PhoneticHelper.PinyinCode,
ChineseRomanization.Jyutping => PhoneticHelper.JyutpingCode,
_ => throw new ArgumentOutOfRangeException(nameof(chineseRomanization))
};
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using Windows.UI;
namespace BetterLyrics.WinUI3.Extensions
{
public static class ColorExtensions
{
extension(Color color)
{
public Color WithAlpha(byte alpha)
{
return Color.FromArgb(alpha, color.R, color.G, color.B);
}
public Color WithOpacity(float opacity)
{
return Color.FromArgb((byte)(opacity * 255), color.R, color.G, color.B);
}
public Color WithBrightness(double brightness)
{
// 确保亮度因子在合理范围内
brightness = Math.Max(0, Math.Min(1, brightness));
var hsl = CommunityToolkit.WinUI.Helpers.ColorHelper.ToHsl(color);
double h = hsl.H;
double s = hsl.S;
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, brightness);
}
public Vector3 ToVector3RGB()
{
return new Vector3((float)color.R / 0xff, (float)color.G / 0xff, (float)color.B / 0xff);
}
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.IO;
using System.Reflection;
namespace BetterLyrics.WinUI3.Extensions
{
public static class DisposableObjectExtension
{
extension(IDisposable obj)
{
// Credit/Copyright to https://gist.github.com/tcartwright/dab50ebaff7c59f05013de0fb349cabd
public bool IsDisposed()
{
/*
TIM C: This hacky code is because MSFT does not provide a standard way to interrogate if an object is disposed or not.
I wrote this based upon streams, but it should work for many other types of MSFT objects (maybe).
*/
if (obj == null) { return true; }
var objType = obj.GetType();
//var foo = new System.IO.BufferedStream();
// the _disposed pattern should catch a lot of msft objects.... hopefully
var isDisposedField = objType.GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance) ??
objType.GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return Convert.ToBoolean(isDisposedField.GetValue(obj)); }
isDisposedField = objType.GetField("_isOpen", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); }
// Windows.Graphics.Imaging.SoftwareBitmap
isDisposedField = objType.GetField("_objRef_global__System_IDisposable", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); }
// System.IO.FileStream
var strategyField = objType.GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance);
if (strategyField != null)
{
var strategy = strategyField.GetValue(obj);
var isClosedField = strategy.GetType().GetProperty("IsClosed", BindingFlags.NonPublic | BindingFlags.Instance);
if (isClosedField != null) { return Convert.ToBoolean(isClosedField.GetValue(strategy)); }
}
// other streams that use this pattern to determine if they are disposed
if (obj is Stream stream) { return !stream.CanRead && !stream.CanWrite; }
return false;
}
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
namespace BetterLyrics.WinUI3.Extensions
{
public static class EnumExtensions
{
extension<T>(T value) where T : struct, Enum
{
public T GetNext()
{
T[] values = Enum.GetValues<T>();
int currentIndex = Array.IndexOf(values, value);
int nextIndex = (currentIndex + 1) % values.Length;
return values[nextIndex];
}
}
}
}

View File

@@ -0,0 +1,26 @@
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace BetterLyrics.WinUI3.Extensions
{
public static class EnumerableExtensions
{
extension<T>(IEnumerable<T> items)
{
public ObservableCollection<GroupInfoList> GetGroupedBy(Func<T, object> groupKeySelector, Func<object, object>? orderSelector = null)
{
var query = from item in items
group item by groupKeySelector(item) into g
orderby g.Key
select new GroupInfoList(g.Cast<object>(), orderSelector) { Key = g.Key };
return new ObservableCollection<GroupInfoList>(query);
}
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace BetterLyrics.WinUI3.Extensions
{
public static class ObservableCollectionExtensions
{
extension<T>(ObservableCollection<T> list)
{
public void InsertRange(int index, IEnumerable<T> items)
{
if (list == null) return;
if (items == null) return;
if (index < 0 || index > list.Count) return;
foreach (var item in items)
{
list.Insert(index++, item);
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Extensions
{
public static class PointExtensions
{
extension(Point point)
{
public PointInt32 ToPointInt32() => new((int)point.X, (int)point.Y);
}
}
}

View File

@@ -0,0 +1,46 @@
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Extensions
{
public static class RectExtensions
{
extension(Rect rect)
{
public RectInt32 ToRectInt32() => new(
(int)rect.X,
(int)rect.Y,
(int)rect.Width,
(int)rect.Height
);
public Rect WithHeight(double height) => new(
rect.X,
rect.Y,
rect.Width,
height
);
public Rect WithWidth(double width) => new(
rect.X,
rect.Y,
width,
rect.Height
);
public Rect WithX(double x) => new(
x,
rect.Y,
rect.Width,
rect.Height
);
public Rect WithY(double y) => new(
rect.X,
y,
rect.Width,
rect.Height
);
}
}
}

View File

@@ -0,0 +1,31 @@
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Extensions
{
public static class SongInfoExtensions
{
extension(SongInfo songInfo)
{
public SongInfo WithTitle(string value)
{
songInfo.Title = value;
return songInfo;
}
public SongInfo WithArtist(string value)
{
songInfo.Artist = value;
return songInfo;
}
public SongInfo WithAlbum(string value)
{
songInfo.Album = value;
return songInfo;
}
}
}
}

View File

@@ -0,0 +1,24 @@
using ATL;
using System.IO;
namespace BetterLyrics.WinUI3.Extensions
{
public static class TrackExtensions
{
extension(Track track)
{
public string GetParentFolderName() => Directory.GetParent(track.Path)?.Name ?? "";
public string GetParentFolderPath() => Directory.GetParent(track.Path)?.FullName ?? "";
public string GetRawLyrics()
{
if (track.Path is string path)
{
return TagLib.File.Create(path).Tag.Lyrics;
}
return "";
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Numerics;
namespace BetterLyrics.WinUI3.Extensions
{
public static class VectorExtensions
{
extension(Vector2 vector2)
{
public Vector2 WithX(float x)
{
return new Vector2(x, vector2.Y);
}
public Vector2 WithY(float y)
{
return new Vector2(vector2.X, y);
}
}
}
}

View File

@@ -0,0 +1,37 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Extensions
{
public static class WindowExtensions
{
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
extension(Window window)
{
public void Init(
string titleKey,
TitleBarHeightOption titleBarHeightOption = TitleBarHeightOption.Standard,
BackdropType backdropType = BackdropType.DesktopAcrylic)
{
window.Title = _resourceService.GetLocalizedString(titleKey);
window.AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
window.AppWindow.SetIcons();
window.ExtendsContentIntoTitleBar = true;
window.AppWindow.TitleBar.PreferredHeightOption = titleBarHeightOption;
window.SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(backdropType);
}
}
}
}

View File

@@ -1,14 +0,0 @@
using Microsoft.UI.Windowing;
namespace BetterLyrics.WinUI3.Helper
{
public static class AppWindowHelper
{
public static void SetIcons(this AppWindow appWindow)
{
appWindow.SetIcon(PathHelper.LogoPath);
appWindow.SetTaskbarIcon(PathHelper.LogoPath);
appWindow.SetTitleBarIcon(PathHelper.LogoPath);
}
}
}

View File

@@ -1,18 +0,0 @@
using Microsoft.Graphics.Canvas.Text;
namespace BetterLyrics.WinUI3.Helper
{
public static class CanvasTextLayoutExtensions
{
public static void SetFontFamily(this CanvasTextLayout? layout, string? text, string cjk, string latin)
{
if (layout == null) return;
if (text == null) return;
for (int i = 0; i < text.Length; i++)
{
layout.SetFontFamily(i, 1, LanguageHelper.IsCJK(text[i]) ? cjk : latin);
}
}
}
}

View File

@@ -1,46 +0,0 @@
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace BetterLyrics.WinUI3.Helper
{
public static class CollectionHelper
{
public static ObservableCollection<GroupInfoList> GetGroupedBy<T>(
this IEnumerable<T> items,
Func<T, object> groupKeySelector,
Func<object, object>? orderSelector = null)
{
var query = from item in items
group item by groupKeySelector(item) into g
orderby g.Key
select new GroupInfoList(g.Cast<object>(), orderSelector) { Key = g.Key };
return new ObservableCollection<GroupInfoList>(query);
}
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
if (collection == null) return;
if (items == null) return;
foreach (var item in items)
{
collection.Add(item);
}
}
public static void InsertRange<T>(this IList<T> list, int index, IEnumerable<T> items)
{
if (list == null) return;
if (items == null) return;
if (index < 0 || index > list.Count) return;
foreach (var item in items)
{
list.Insert(index++, item);
}
}
}
}

View File

@@ -1,6 +1,8 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Hooks;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
@@ -62,86 +64,28 @@ namespace BetterLyrics.WinUI3.Helper
);
}
public static Color ToColor(this int argb)
{
byte a = (byte)(argb >> 24);
byte r = (byte)(argb >> 16);
byte g = (byte)(argb >> 8);
byte b = (byte)argb;
// 还原非预乘分量
if (a == 0)
return Color.FromArgb(0, 0, 0, 0);
// 预乘解码
// 这里 a+1 是编码时的分母
int ap1 = a + 1;
r = (byte)Math.Min(255, (r * 255 + (ap1 / 2)) / ap1);
g = (byte)Math.Min(255, (g * 255 + (ap1 / 2)) / ap1);
b = (byte)Math.Min(255, (b * 255 + (ap1 / 2)) / ap1);
return Color.FromArgb(a, r, g, b);
}
public static Color ToColor(this System.Drawing.Color color)
{
return Color.FromArgb(color.A, color.R, color.G, color.B);
}
public static Color WithAlpha(this Color color, byte alpha)
{
return Color.FromArgb(alpha, color.R, color.G, color.B);
}
public static Color WithOpacity(this Color color, float opacity)
{
return Color.FromArgb((byte)(opacity * 255), color.R, color.G, color.B);
}
public static Color WithBrightness(this Color color, double brightness)
{
// 确保亮度因子在合理范围内
brightness = Math.Max(0, Math.Min(1, brightness));
var hsl = CommunityToolkit.WinUI.Helpers.ColorHelper.ToHsl(color);
double h = hsl.H;
double s = hsl.S;
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, brightness);
}
public static Vector3 ToVector3RGB(this Color color)
{
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)
public static Color GetAccentColor(IntPtr myHwnd, string monitorDeviceName, WindowPixelSampleMode mode)
{
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return System.Drawing.Color.Transparent;
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return Colors.Transparent;
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(monitorDeviceName);
var monitorInfo = MonitorHook.GetMonitorInfoExFromDeviceName(monitorDeviceName);
int screenWidth = monitorInfo.rcMonitor.Width;
switch (mode)
{
case WindowPixelSampleMode.BelowWindow:
{
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Bottom + 2, screenWidth, 1);
}
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Bottom + 2, screenWidth, 1);
case WindowPixelSampleMode.AboveWindow:
{
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top - 2, screenWidth, 1);
}
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top - 2, screenWidth, 1);
case WindowPixelSampleMode.WindowArea:
{
int width = myRect.Right - myRect.Left;
int height = myRect.Bottom - myRect.Top;
if (width <= 0 || height <= 0)
return System.Drawing.Color.Transparent;
if (width <= 0 || height <= 0) return Colors.Transparent;
// 采集窗口区域的平均色
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top, width, height);
}
@@ -150,10 +94,10 @@ namespace BetterLyrics.WinUI3.Helper
int width = myRect.Right - myRect.Left;
int height = myRect.Bottom - myRect.Top;
if (width <= 0 || height <= 0)
return System.Drawing.Color.Transparent;
return Colors.Transparent;
var edgeThickness = new Thickness(36, 36, 36, 36);
List<System.Drawing.Color> edgeColors = [];
List<Color> edgeColors = [];
// Top edge
if (edgeThickness.Top > 0)
@@ -169,30 +113,27 @@ namespace BetterLyrics.WinUI3.Helper
edgeColors.Add(GetAverageColorFromScreenRegion(myRect.Right, myRect.Top, (int)edgeThickness.Right, height));
// 合并四边平均色
if (edgeColors.Count == 0)
return System.Drawing.Color.Transparent;
long r = 0,
g = 0,
b = 0;
if (edgeColors.Count == 0) return Colors.Transparent;
long r = 0, g = 0, b = 0;
foreach (var c in edgeColors)
{
r += c.R;
g += c.G;
b += c.B;
}
return System.Drawing.Color.FromArgb(
return Color.FromArgb(
255,
(int)(r / edgeColors.Count),
(int)(g / edgeColors.Count),
(int)(b / edgeColors.Count)
(byte)(r / edgeColors.Count),
(byte)(g / edgeColors.Count),
(byte)(b / edgeColors.Count)
);
}
default:
return System.Drawing.Color.Transparent;
return Colors.Transparent;
}
}
private static System.Drawing.Color GetAverageColorFromScreenRegion(int x, int y, int width, int height)
private static Color GetAverageColorFromScreenRegion(int x, int y, int width, int height)
{
using Bitmap bmp = new(width, height, PixelFormat.Format32bppArgb);
using Graphics gDest = Graphics.FromImage(bmp);
@@ -208,7 +149,7 @@ namespace BetterLyrics.WinUI3.Helper
return ComputeAverageColor(bmp);
}
private static System.Drawing.Color ComputeAverageColor(Bitmap bmp)
private static Color ComputeAverageColor(Bitmap bmp)
{
long r = 0, g = 0, b = 0;
int count = 0;
@@ -225,8 +166,8 @@ namespace BetterLyrics.WinUI3.Helper
}
}
if (count == 0) return System.Drawing.Color.Transparent;
return System.Drawing.Color.FromArgb((int)(r / count), (int)(g / count), (int)(b / count));
if (count == 0) return Colors.Transparent;
return Color.FromArgb(255, (byte)(r / count), (byte)(g / count), (byte)(b / count));
}
}
}

View File

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

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using System;
using System.IO;
using System.Text;
@@ -55,9 +56,9 @@ namespace BetterLyrics.WinUI3.Helper
return null;
}
public static void WriteLyricsCache(string title, string artist, string album, string lyrics, LyricsFormat format, string cacheFolderPath)
public static void WriteLyricsCache(SongInfo songInfo, string lyrics, LyricsFormat format, string cacheFolderPath)
{
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title} - {album}{format.ToFileExtension()}"));
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{songInfo.Artist} - {songInfo.Title} - {songInfo.Album}{format.ToFileExtension()}"));
File.WriteAllText(cacheFilePath, lyrics);
}

View File

@@ -1,18 +1,12 @@
// 2025/6/23 by Zhe Fang
using CommunityToolkit.WinUI.Helpers;
using Windows.ApplicationModel;
namespace BetterLyrics.WinUI3.Helper
{
public static class MetadataHelper
{
public static string AppVersion
{
get
{
var version = Package.Current.Id.Version;
return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
}
}
public static string AppVersion => Package.Current.Id.Version.ToFormattedString();
}
}

View File

@@ -1,50 +0,0 @@
using System;
using System.IO;
using System.Reflection;
namespace BetterLyrics.WinUI3.Helper
{
public static class ObjectHelper
{
// Credit/Copyright to https://gist.github.com/tcartwright/dab50ebaff7c59f05013de0fb349cabd
public static bool IsDisposed(this IDisposable obj)
{
/*
TIM C: This hacky code is because MSFT does not provide a standard way to interrogate if an object is disposed or not.
I wrote this based upon streams, but it should work for many other types of MSFT objects (maybe).
*/
if (obj == null) { return true; }
var objType = obj.GetType();
//var foo = new System.IO.BufferedStream();
// the _disposed pattern should catch a lot of msft objects.... hopefully
var isDisposedField = objType.GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance) ??
objType.GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return Convert.ToBoolean(isDisposedField.GetValue(obj)); }
isDisposedField = objType.GetField("_isOpen", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); }
// Windows.Graphics.Imaging.SoftwareBitmap
isDisposedField = objType.GetField("_objRef_global__System_IDisposable", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); }
// System.IO.FileStream
var strategyField = objType.GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance);
if (strategyField != null)
{
var strategy = strategyField.GetValue(obj);
var isClosedField = strategy.GetType().GetProperty("IsClosed", BindingFlags.NonPublic | BindingFlags.Instance);
if (isClosedField != null) { return Convert.ToBoolean(isClosedField.GetValue(strategy)); }
}
// other streams that use this pattern to determine if they are disposed
if (obj is Stream stream) { return !stream.CanRead && !stream.CanWrite; }
return false;
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using BetterLyrics.WinUI3.Hooks;
using DevWinUI;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Storage;
@@ -10,7 +12,7 @@ namespace BetterLyrics.WinUI3.Helper
{
public static async Task<StorageFolder?> PickSingleFolderAsync<T>()
{
var window = WindowHelper.GetWindowByWindowType<T>();
var window = WindowHook.GetWindowByWindowType<T>();
if (window == null) return null;
var picker = new Windows.Storage.Pickers.FolderPicker();
@@ -26,7 +28,7 @@ namespace BetterLyrics.WinUI3.Helper
public static async Task<StorageFile?> PickSingleFileAsync<T>(string[] fileTypeFilter)
{
var window = WindowHelper.GetWindowByWindowType<T>();
var window = WindowHook.GetWindowByWindowType<T>();
if (window == null) return null;
var picker = new Windows.Storage.Pickers.FileOpenPicker();
@@ -42,7 +44,7 @@ namespace BetterLyrics.WinUI3.Helper
public static async Task<StorageFile?> PickSaveFileAsync<T>(IDictionary<string, IList<string>> fileTypeChoices)
{
var window = WindowHelper.GetWindowByWindowType<T>();
var window = WindowHook.GetWindowByWindowType<T>();
if (window == null) return null;
var picker = new Windows.Storage.Pickers.FileSavePicker();

View File

@@ -1,9 +1,10 @@
using System.Collections.Generic;
using BetterLyrics.WinUI3.Constants;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace BetterLyrics.WinUI3.Helper
{
public static class PlayerIdMatcher
public static class PlayerIDMatcher
{
private static readonly List<string> neteaseFamilyRegex =
[
@@ -12,8 +13,10 @@ namespace BetterLyrics.WinUI3.Helper
"^48848aaaaaaccd\\.HyPlayer_" //HyPlayer
];
public static bool IsNeteaseFamily(string id)
public static bool IsNeteaseFamily(string? id)
{
if (id is null) return false;
foreach (var regex in neteaseFamilyRegex)
{
var isMatch = Regex.IsMatch(id, regex);
@@ -22,9 +25,8 @@ namespace BetterLyrics.WinUI3.Helper
return false;
}
public static bool IsLXMusic(string? id)
{
return id == Constants.PlayerID.LXMusic || id == Constants.PlayerID.LXMusicPortable;
}
public static bool IsLXMusic(string? id) => id is PlayerID.LXMusic or PlayerID.LXMusicPortable;
public static bool IsAppleMusic(string? id) => id is PlayerID.AppleMusic or PlayerID.AppleMusicAlternative;
}
}

View File

@@ -1,13 +0,0 @@
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Helper
{
public static class PointHelper
{
public static PointInt32 ToPointInt32(this Point point)
{
return new PointInt32((int)point.X, (int)point.Y);
}
}
}

View File

@@ -1,57 +0,0 @@
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Helper
{
public static class RectHelper
{
public static RectInt32 ToRectInt32(this Windows.Foundation.Rect rect)
{
return new RectInt32(
(int)rect.X,
(int)rect.Y,
(int)rect.Width,
(int)rect.Height
);
}
public static Windows.Foundation.Rect WithHeight(this Windows.Foundation.Rect rect, double height)
{
return new Windows.Foundation.Rect(
rect.X,
rect.Y,
rect.Width,
height
);
}
public static Windows.Foundation.Rect WithWidth(this Windows.Foundation.Rect rect, double width)
{
return new Windows.Foundation.Rect(
rect.X,
rect.Y,
width,
rect.Height
);
}
public static Windows.Foundation.Rect WithX(this Windows.Foundation.Rect rect, double x)
{
return new Windows.Foundation.Rect(
x,
rect.Y,
rect.Width,
rect.Height
);
}
public static Windows.Foundation.Rect WithY(this Windows.Foundation.Rect rect, double y)
{
return new Windows.Foundation.Rect(
rect.X,
y,
rect.Width,
rect.Height
);
}
}
}

View File

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

View File

@@ -1,17 +0,0 @@
using System.Numerics;
namespace BetterLyrics.WinUI3.Helper
{
public static class VectorHelper
{
public static Vector2 WithX(this Vector2 source, float x)
{
return new Vector2(x, source.Y);
}
public static Vector2 WithY(this Vector2 source, float y)
{
return new Vector2(source.X, y);
}
}
}

View File

@@ -3,9 +3,9 @@ using System;
using System.Collections.Generic;
using Vanara.PInvoke;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Hooks
{
public class ForegroundWindowWatcher
public class ForegroundWindowHook
{
private readonly User32.WinEventProc _winEventDelegate;
private readonly List<User32.HWINEVENTHOOK> _hooks = new();
@@ -17,7 +17,7 @@ namespace BetterLyrics.WinUI3.Helper
private readonly DispatcherTimer _timer;
public ForegroundWindowWatcher(IntPtr selfHwnd, WindowChangedHandler onWindowChanged)
public ForegroundWindowHook(IntPtr selfHwnd, WindowChangedHandler onWindowChanged)
{
_selfHwnd = selfHwnd;
_onWindowChanged = onWindowChanged;

View File

@@ -5,9 +5,9 @@ using Vanara.PInvoke;
using Windows.System;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Hooks
{
public class GlobalHotKeyHelper
public class GlobalHotKeyHook
{
private static Dictionary<int, Action> _actions = [];
private static Dictionary<int, List<string>> _keys = [];
@@ -23,7 +23,7 @@ namespace BetterLyrics.WinUI3.Helper
{
if (keys.Count == 0) return;
var window = WindowHelper.GetWindowByWindowType<T>();
var window = WindowHook.GetWindowByWindowType<T>();
if (window == null) return;
HWND hwnd = WindowNative.GetWindowHandle(window);
@@ -62,7 +62,7 @@ namespace BetterLyrics.WinUI3.Helper
private static void UnregisterHotKey<T>(ShortcutID id)
{
var window = WindowHelper.GetWindowByWindowType<T>();
var window = WindowHook.GetWindowByWindowType<T>();
if (window == null) return;
HWND hwnd = WindowNative.GetWindowHandle(window);

View File

@@ -5,9 +5,9 @@ using System.Runtime.InteropServices;
using Vanara.PInvoke;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Hooks
{
public static class MonitorHelper
public static class MonitorHook
{
public static IEnumerable<string> GetAllMonitorDeviceNames()
{

View File

@@ -2,9 +2,9 @@
using NAudio.CoreAudioApi;
using System;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Hooks
{
public static class SystemVolumeHelper
public static class SystemVolumeHook
{
private static MMDeviceEnumerator? _deviceEnumerator;
private static MMDevice? _defaultDevice;
@@ -15,7 +15,7 @@ namespace BetterLyrics.WinUI3.Helper
/// </summary>
public static event EventHandler<int>? VolumeNotification;
static SystemVolumeHelper()
static SystemVolumeHook()
{
_deviceEnumerator = new MMDeviceEnumerator();
_defaultDevice = _deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Views;
@@ -18,9 +19,9 @@ using Windows.Foundation;
using WinRT.Interop;
using WinUIEx;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Hooks
{
public static class WindowHelper
public static class WindowHook
{
private static List<object> _activeWindows = [];
private static List<object> _workAreas = [];

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;

View File

@@ -4,7 +4,6 @@ namespace BetterLyrics.WinUI3.Models
{
public class LyricsSearchResult
{
public bool IsFound => !string.IsNullOrEmpty(Raw);
public LyricsSearchProvider? Provider { get; set; }
public string? Raw { get; set; }
@@ -12,5 +11,16 @@ namespace BetterLyrics.WinUI3.Models
public string? Title { get; set; }
public string? Artist { get; set; }
public string? Album { get; set; }
public bool IsFound => !string.IsNullOrEmpty(Raw);
public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
public void CopyFromSongInfo(SongInfo songInfo)
{
Title = songInfo.Title;
Artist = songInfo.Artist;
Album = songInfo.Album;
}
}
}

View File

@@ -1,5 +1,6 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Views;
@@ -104,10 +105,10 @@ namespace BetterLyrics.WinUI3.Models
public void UpdateMonitorNameAndBounds()
{
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
var lyricsWindow = WindowHook.GetWindowByWindowType<LyricsWindow>();
if (lyricsWindow == null) return;
var mointor = MonitorHelper.GetMonitorInfoExFromWindow(lyricsWindow);
var mointor = MonitorHook.GetMonitorInfoExFromWindow(lyricsWindow);
MonitorDeviceName = mointor.szDevice;
MonitorBounds = new Rect(
mointor.rcMonitor.Left,
@@ -119,7 +120,7 @@ namespace BetterLyrics.WinUI3.Models
public void UpdateMonitorBounds()
{
var mointor = MonitorHelper.GetMonitorInfoExFromDeviceName(MonitorDeviceName);
var mointor = MonitorHook.GetMonitorInfoExFromDeviceName(MonitorDeviceName);
MonitorBounds = new Rect(
mointor.rcMonitor.Left,
mointor.rcMonitor.Top,

View File

@@ -1,11 +1,12 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Collections;
using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Linq;
using BetterLyrics.WinUI3.Constants;
namespace BetterLyrics.WinUI3.Models
{
@@ -32,7 +33,60 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
public bool IsLXMusic => PlayerIdMatcher.IsLXMusic(Provider);
public bool IsLXMusic => PlayerIDMatcher.IsLXMusic(Provider);
public string DisplayName => Provider switch
{
PlayerID.Spotify => PlayerName.Spotify,
PlayerID.AppleMusic => PlayerName.AppleMusic,
PlayerID.iTunes => PlayerName.iTunes,
PlayerID.KugouMusic => PlayerName.KugouMusic,
PlayerID.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic,
PlayerID.QQMusic => PlayerName.QQMusic,
PlayerID.LXMusic => PlayerName.LXMusic,
PlayerID.LXMusicPortable => PlayerName.LXMusicPortable,
PlayerID.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11,
PlayerID.AIMP => PlayerName.AIMP,
PlayerID.Foobar2000 => PlayerName.Foobar2000,
PlayerID.MusicBee => PlayerName.MusicBee,
PlayerID.PotPlayer => PlayerName.PotPlayer,
PlayerID.Chrome => PlayerName.Chrome,
PlayerID.Edge => PlayerName.Edge,
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
PlayerID.SaltPlayerForWindows => PlayerName.SaltPlayerForWindows,
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
PlayerID.Listen1 => PlayerName.Listen1,
_ => Provider,
};
public string LogoPath => Provider switch
{
PlayerID.Spotify => PathHelper.SpotifyLogoPath,
PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath,
PlayerID.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
PlayerID.iTunes => PathHelper.iTunesLogoPath,
PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath,
PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,
PlayerID.QQMusic => PathHelper.QQMusicLogoPath,
PlayerID.LXMusic => PathHelper.LXMusicLogoPath,
PlayerID.LXMusicPortable => PathHelper.LXMusicLogoPath,
PlayerID.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath,
PlayerID.AIMP => PathHelper.AIMPLogoPath,
PlayerID.Foobar2000 => PathHelper.Foobar2000LogoPath,
PlayerID.MusicBee => PathHelper.MusicBeeLogoPath,
PlayerID.PotPlayer => PathHelper.PotPlayerLogoPath,
PlayerID.Chrome => PathHelper.ChromeLogoPath,
PlayerID.Edge => PathHelper.EdgeLogoPath,
PlayerID.BetterLyrics => PathHelper.LogoPath,
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
PlayerID.SaltPlayerForWindows => PathHelper.SaltPlayerForWindowsLogoPath,
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
_ => PathHelper.UnknownPlayerLogoPath,
};
public MediaSourceProviderInfo()
{

View File

@@ -1,4 +1,4 @@
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models.Settings

View File

@@ -1,10 +1,11 @@
// 2025/6/23 by Zhe Fang
using CommunityToolkit.Mvvm.ComponentModel;
using System;
namespace BetterLyrics.WinUI3.Models
{
public partial class SongInfo : ObservableObject
public partial class SongInfo : ObservableObject, ICloneable
{
[ObservableProperty]
public partial string Album { get; set; }
@@ -24,9 +25,37 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty]
public partial string? SongId { get; set; } = null;
public string? LinkedFileName { get; set; } = null;
public double Duration => DurationMs / 1000;
public SongInfo() { }
public object Clone()
{
return new SongInfo()
{
Title = this.Title,
Artist = this.Artist,
Album = this.Album,
DurationMs = this.DurationMs,
PlayerId = this.PlayerId,
SongId = this.SongId,
LinkedFileName = this.LinkedFileName,
};
}
public override string ToString()
{
return
$"Title: {Title}\n" +
$"Artist: {Artist}\n" +
$"Album: {Album}\n" +
$"Duration: {Duration} sec\n" +
$"Plauer ID: {PlayerId}\n" +
$"Song ID: {SongId}\n" +
$"Linked file name: {LinkedFileName}";
}
}
public static class SongInfoExtensions

View File

@@ -1,11 +1,12 @@
using System;
using BetterLyrics.WinUI3.Helper;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
namespace BetterLyrics.WinUI3.Providers
{
public class AppleMusic
{

View File

@@ -1,5 +1,6 @@
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
@@ -59,7 +60,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
public async Task AuthAsync()
{
var dialogXamlRoot = WindowHelper.GetWindowByWindowType<SettingsWindow>()?.Content.XamlRoot;
var dialogXamlRoot = WindowHook.GetWindowByWindowType<SettingsWindow>()?.Content.XamlRoot;
if (dialogXamlRoot == null)
{
return;
@@ -86,7 +87,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
public async Task UnAuthAsync()
{
var dialogXamlRoot = WindowHelper.GetWindowByWindowType<SettingsWindow>()?.Content.XamlRoot;
var dialogXamlRoot = WindowHook.GetWindowByWindowType<SettingsWindow>()?.Content.XamlRoot;
if (dialogXamlRoot == null)
{
return;

View File

@@ -24,7 +24,7 @@ namespace BetterLyrics.WinUI3.Services.LibWatcherService
UpdateWatchers();
}
private void LocalMediaFolders_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
private void LocalMediaFolders_ItemPropertyChanged(object? sender, Collections.ItemPropertyChangedEventArgs e)
{
UpdateWatchers();
}

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
@@ -44,21 +46,21 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
WindowHelper.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
WindowHook.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
WindowHelper.UpdateWorkArea<LyricsWindow>();
WindowHook.UpdateWorkArea<LyricsWindow>();
}
await Task.Delay(300);
WindowHelper.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
WindowHelper.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
WindowHook.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
WindowHook.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
WindowHelper.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
WindowHelper.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
WindowHook.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
WindowHook.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHook.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
// 下述代码可以删除,但是为了避免给用户造成操作上的疑虑,暂时保留
if (LiveStates.LyricsWindowStatus.IsWorkArea)
@@ -66,7 +68,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
LiveStates.LyricsWindowStatus.WindowBounds = LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea();
}
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds);
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds);
LiveStates.LyricsWindowStatus.WindowX = LiveStates.LyricsWindowStatus.WindowBounds.X;
LiveStates.LyricsWindowStatus.WindowY = LiveStates.LyricsWindowStatus.WindowBounds.Y;
LiveStates.LyricsWindowStatus.WindowWidth = LiveStates.LyricsWindowStatus.WindowBounds.Width;
@@ -83,11 +85,11 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
case nameof(LyricsWindowStatus.IsWorkArea):
LiveStates.IsLyricsWindowStatusRefreshing = true;
WindowHelper.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
WindowHook.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
LiveStates.IsLyricsWindowStatusRefreshing = false;
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
}
break;
case nameof(LyricsWindowStatus.DockHeight):
@@ -97,40 +99,40 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
LiveStates.IsLyricsWindowStatusRefreshing = true;
WindowHelper.UpdateWorkArea<LyricsWindow>();
WindowHook.UpdateWorkArea<LyricsWindow>();
LiveStates.IsLyricsWindowStatusRefreshing = false;
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
}
break;
case nameof(LyricsWindowStatus.IsShownInSwitchers):
WindowHelper.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
WindowHook.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
break;
case nameof(LyricsWindowStatus.IsAlwaysOnTop):
WindowHelper.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
WindowHook.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
break;
case nameof(LyricsWindowStatus.IsClickThrough):
WindowHelper.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
WindowHook.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
break;
case nameof(LyricsWindowStatus.IsBorderless):
WindowHelper.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
WindowHook.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
break;
case nameof(LyricsWindowStatus.WindowX):
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithX(LiveStates.LyricsWindowStatus.WindowX));
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithX(LiveStates.LyricsWindowStatus.WindowX));
break;
case nameof(LyricsWindowStatus.WindowY):
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithY(LiveStates.LyricsWindowStatus.WindowY));
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithY(LiveStates.LyricsWindowStatus.WindowY));
break;
case nameof(LyricsWindowStatus.WindowWidth):
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithWidth(LiveStates.LyricsWindowStatus.WindowWidth));
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithWidth(LiveStates.LyricsWindowStatus.WindowWidth));
break;
case nameof(LyricsWindowStatus.WindowHeight):
WindowHelper.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithHeight(LiveStates.LyricsWindowStatus.WindowHeight));
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithHeight(LiveStates.LyricsWindowStatus.WindowHeight));
break;
case nameof(LyricsWindowStatus.TitleBarArea):
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
WindowHook.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
break;
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
break;
default:
break;

View File

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

View File

@@ -2,9 +2,11 @@
using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Helper.BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Providers;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers;
@@ -89,15 +91,16 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}
}
public async Task<LyricsSearchResult> SearchSmartlyAsync(string mediaSessionId, string title, string artist, string album, double durationMs, string? songId, CancellationToken token)
public async Task<LyricsSearchResult> SearchSmartlyAsync(SongInfo songInfo, CancellationToken token)
{
var lyricsSearchResult = new LyricsSearchResult();
string overridenTitle = title;
string overridenArtist = artist;
string overridenAlbum = album;
string overridenTitle = songInfo.Title;
string overridenArtist = songInfo.Artist;
string overridenAlbum = songInfo.Album;
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
_logger.LogInformation("Searching lyrics for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)",
overridenTitle, overridenArtist, overridenAlbum, songInfo.DurationMs);
var found = _settingsService.AppSettings.MappedSongSearchQueries
.Where(x => x.OriginalTitle == overridenTitle && x.OriginalArtist == overridenArtist && x.OriginalAlbum == overridenAlbum)
@@ -124,18 +127,27 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
var targetProvider = found.LyricsSearchProvider;
if (targetProvider != null)
{
return await SearchSingleAsync(targetProvider.Value, overridenTitle, overridenArtist, overridenAlbum, durationMs, songId, token);
return await SearchSingleAsync(
((SongInfo)songInfo.Clone())
.WithTitle(overridenTitle)
.WithArtist(overridenArtist)
.WithAlbum(overridenAlbum),
targetProvider.Value, token);
}
}
foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == mediaSessionId).FirstOrDefault()?.LyricsSearchProvidersInfo ?? [])
foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == songInfo.PlayerId).FirstOrDefault()?.LyricsSearchProvidersInfo ?? [])
{
if (!provider.IsEnabled)
{
continue;
}
lyricsSearchResult = await SearchSingleAsync(provider.Provider, overridenTitle, overridenArtist, overridenAlbum, durationMs, null, token);
lyricsSearchResult = await SearchSingleAsync(
((SongInfo)songInfo.Clone())
.WithTitle(overridenTitle)
.WithArtist(overridenArtist)
.WithAlbum(overridenAlbum),
provider.Provider, token);
if (lyricsSearchResult.IsFound)
{
@@ -146,19 +158,19 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
public async Task<List<LyricsSearchResult>> SearchAllAsync(string title, string artist, string album, double durationMs, CancellationToken token)
public async Task<List<LyricsSearchResult>> SearchAllAsync(SongInfo songInfo, CancellationToken token)
{
_logger.LogInformation("Searching all lyrics for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
_logger.LogInformation("Searching all lyrics for: {SongInfo}", songInfo);
var results = new List<LyricsSearchResult>();
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
{
var searchResult = await SearchSingleAsync(provider, title, artist, album, durationMs, null, token);
var searchResult = await SearchSingleAsync(songInfo, provider, token);
results.Add(searchResult);
}
return results;
}
private async Task<LyricsSearchResult> SearchSingleAsync(LyricsSearchProvider provider, string title, string artist, string album, double durationMs, string? songId, CancellationToken token)
private async Task<LyricsSearchResult> SearchSingleAsync(SongInfo songInfo, LyricsSearchProvider provider, CancellationToken token)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -172,13 +184,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
// Check cache first
if (provider.IsRemote())
{
var cachedLyrics = FileHelper.ReadLyricsCache(title, artist, album, lyricsFormat, provider.GetCacheDirectory());
var cachedLyrics = FileHelper.ReadLyricsCache(songInfo.Title, songInfo.Artist, songInfo.Album, lyricsFormat, provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
{
lyricsSearchResult.Raw = cachedLyrics;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
lyricsSearchResult.CopyFromSongInfo(songInfo);
return lyricsSearchResult;
}
}
@@ -187,11 +197,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
if (provider == LyricsSearchProvider.LocalMusicFile)
{
lyricsSearchResult = SearchEmbedded(title, artist, album);
lyricsSearchResult = SearchEmbedded(songInfo);
}
else
{
lyricsSearchResult = await SearchFile(title, artist, album, lyricsFormat);
lyricsSearchResult = await SearchFile(songInfo, lyricsFormat);
}
}
else
@@ -199,22 +209,22 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
switch (provider)
{
case LyricsSearchProvider.LrcLib:
lyricsSearchResult = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
lyricsSearchResult = await SearchLrcLibAsync(songInfo);
break;
case LyricsSearchProvider.QQ:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.QQMusic);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.Kugou);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
lyricsSearchResult = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, songId, Searchers.Netease);
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
lyricsSearchResult = await SearchAmllTtmlDbAsync(title, artist, album);
lyricsSearchResult = await SearchAmllTtmlDbAsync(songInfo);
break;
case LyricsSearchProvider.AppleMusic:
lyricsSearchResult = await SearchAppleMusicAsync(title, artist, album, (int)durationMs);
lyricsSearchResult = await SearchAppleMusicAsync(songInfo);
break;
default:
break;
@@ -230,7 +240,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
if (provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, album, lyricsSearchResult.Raw!, lyricsFormat, provider.GetCacheDirectory());
FileHelper.WriteLyricsCache(songInfo, lyricsSearchResult.Raw!, lyricsFormat, provider.GetCacheDirectory());
}
}
}
@@ -241,7 +251,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchFile(string title, string artist, string album, LyricsFormat format)
private async Task<LyricsSearchResult> SearchFile(SongInfo songInfo, LyricsFormat format)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -256,15 +266,14 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
foreach (var file in DirectoryHelper.GetAllFiles(folder.Path, $"*{format.ToFileExtension()}"))
{
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
var fileName = Path.GetFileNameWithoutExtension(file);
if (FileHelper.IsSwitchableNormalizedMatch(fileName, songInfo.Title, songInfo.Artist) || songInfo.LinkedFileName == fileName)
{
string? raw = await File.ReadAllTextAsync(file, FileHelper.GetEncoding(file));
if (raw != null)
{
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
lyricsSearchResult.CopyFromSongInfo(songInfo);
return lyricsSearchResult;
}
@@ -279,7 +288,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private LyricsSearchResult SearchEmbedded(string title, string artist, string album)
private LyricsSearchResult SearchEmbedded(SongInfo songInfo)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -295,17 +304,15 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (FileHelper.MusicExtensions.Contains(Path.GetExtension(file)))
{
var track = new Track(file);
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)))
if ((songInfo.Album != "" && track.Title == songInfo.Title && track.Artist == songInfo.Artist && track.Album == songInfo.Album)
|| (songInfo.Album == "" && track.Title == songInfo.Title && track.Artist == songInfo.Artist)
|| (songInfo.Album == "" && FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.Title, songInfo.Artist)))
{
var plain = track.GetLyrics();
var plain = track.GetRawLyrics();
if (!plain.IsNullOrEmpty())
{
lyricsSearchResult.Raw = plain;
lyricsSearchResult.Title = track.Title;
lyricsSearchResult.Artist = track.Artist;
lyricsSearchResult.Album = track.Album;
lyricsSearchResult.CopyFromSongInfo(songInfo);
return lyricsSearchResult;
}
@@ -317,7 +324,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchAmllTtmlDbAsync(string title, string artist, string album)
private async Task<LyricsSearchResult> SearchAmllTtmlDbAsync(SongInfo songInfo)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -360,7 +367,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (musicName == null || artists == null)
continue;
if (FileHelper.IsSwitchableNormalizedMatch($"{artists} - {musicName}", title, artist))
if (FileHelper.IsSwitchableNormalizedMatch($"{artists} - {musicName}", songInfo.Title, songInfo.Artist))
{
if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp))
{
@@ -389,9 +396,9 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string lyrics = await response.Content.ReadAsStringAsync();
lyricsSearchResult.Raw = lyrics;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Album = album;
lyricsSearchResult.Title = songInfo.Title;
lyricsSearchResult.Artist = songInfo.Artist;
lyricsSearchResult.Album = songInfo.Album;
}
catch
{
@@ -400,7 +407,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchLrcLibAsync(string title, string artist, string album, int duration)
private async Task<LyricsSearchResult> SearchLrcLibAsync(SongInfo songInfo)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -410,10 +417,10 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
// Build API query URL
var url =
$"https://lrclib.net/api/search?" +
$"track_name={Uri.EscapeDataString(title)}&" +
$"artist_name={Uri.EscapeDataString(artist)}&" +
$"&album_name={Uri.EscapeDataString(album)}" +
$"&durationMs={Uri.EscapeDataString(duration.ToString())}";
$"track_name={Uri.EscapeDataString(songInfo.Title)}&" +
$"artist_name={Uri.EscapeDataString(songInfo.Artist)}&" +
$"&album_name={Uri.EscapeDataString(songInfo.Album)}" +
$"&durationMs={Uri.EscapeDataString(songInfo.DurationMs.ToString())}";
using var response = await _lrcLibHttpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
@@ -450,7 +457,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private static async Task<LyricsSearchResult> SearchQQNeteaseKugouAsync(string title, string artist, string album, int durationMs, string? songId, Searchers searcher)
private static async Task<LyricsSearchResult> SearchQQNeteaseKugouAsync(SongInfo songInfo, Searchers searcher)
{
var lyricsSearchResult = new LyricsSearchResult();
@@ -472,19 +479,19 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
}
ISearchResult? result;
if (searcher == Searchers.Netease && songId != null)
if (searcher == Searchers.Netease && songInfo.SongId != null)
{
result = new NeteaseSearchResult(title, [artist], album, null, durationMs, songId);
result = new NeteaseSearchResult(songInfo.Title, [songInfo.Artist], songInfo.Album, null, (int)songInfo.DurationMs, songInfo.SongId);
}
else
{
result = await SearchHelper.Search(new Lyricify.Lyrics.Models.TrackMultiArtistMetadata()
{
DurationMs = durationMs,
Album = album,
AlbumArtists = [artist],
Artists = [artist],
Title = title,
DurationMs = (int)songInfo.DurationMs,
Album = songInfo.Album,
AlbumArtists = [songInfo.Artist],
Artists = [songInfo.Artist],
Title = songInfo.Title,
}, searcher);
}
@@ -498,9 +505,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
album,
songInfo,
translated,
LyricsFormat.Lrc,
PathHelper.QQTranslationCacheDirectory
@@ -517,9 +522,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
album,
songInfo,
translated,
LyricsFormat.Lrc,
PathHelper.NeteaseTranslationCacheDirectory
@@ -555,9 +558,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
album,
songInfo,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
@@ -578,7 +579,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchAppleMusicAsync(string title, string artist, string album, int durationMs)
private async Task<LyricsSearchResult> SearchAppleMusicAsync(SongInfo songInfo)
{
var lyricsSearchResult = new LyricsSearchResult
{
@@ -587,11 +588,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
if (await _appleMusic.InitAsync())
{
var raw = await _appleMusic.GetLyricsAsync(title, artist);
_logger.LogInformation("Apple Music lyrics search result for {Title} - {Artist}: {Raw}", title, artist, raw ?? "null");
var raw = await _appleMusic.GetLyricsAsync(songInfo.Title, songInfo.Artist);
_logger.LogInformation("Apple Music lyrics search result for {SongInfo}: {Raw}", songInfo, raw);
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
lyricsSearchResult.Title = songInfo.Title;
lyricsSearchResult.Artist = songInfo.Artist;
lyricsSearchResult.Album = "";
}

View File

@@ -32,7 +32,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
TimeSpan CurrentPosition { get; }
LyricsData? CurrentLyricsData { get; }
LyricsSearchProvider? LyricsSearchProvider { get; }
TranslationSearchProvider? TranslationSearchProvider { get; }
LyricsSearchResult? CurrentLyricsSearchResult { get; }
}
}

View File

@@ -1,5 +1,6 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -26,7 +27,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider? LyricsSearchProvider { get; private set; }
[ObservableProperty] public partial LyricsSearchResult? CurrentLyricsSearchResult { get; private set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSearchProvider? TranslationSearchProvider { get; private set; }
@@ -84,7 +85,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_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();
TranslationSearchProvider = CurrentLyricsSearchResult?.Provider.ToTranslationSearchProvider();
}
else if (_settingsService.AppSettings.TranslationSettings.IsLibreTranslateEnabled)
{
@@ -147,7 +148,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_logger.LogInformation("Refreshing lyrics...");
LyricsSearchProvider = null;
CurrentLyricsSearchResult = null;
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
@@ -160,24 +161,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_logger.LogInformation("Searching lyrics for: Title={Title}, Artist={Artist}, Album={Album}, DurationMs={DurationMs}",
CurrentSongInfo.Title, CurrentSongInfo.Artist, CurrentSongInfo.Album, CurrentSongInfo.DurationMs);
var lyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(
CurrentSongInfo.PlayerId ?? "",
CurrentSongInfo.Title,
CurrentSongInfo.Artist,
CurrentSongInfo.Album,
CurrentSongInfo.DurationMs,
CurrentSongInfo.SongId,
token
), token);
CurrentLyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(CurrentSongInfo, token), token);
if (token.IsCancellationRequested) return;
LyricsSearchProvider = lyricsSearchResult?.Provider;
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsSearchResult?.IsFound, LyricsSearchProvider);
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", CurrentLyricsSearchResult?.IsFound, CurrentLyricsSearchResult?.Provider);
var lyricsParser = new LyricsParser();
lyricsParser.Parse(
_settingsService.AppSettings.MappedSongSearchQueries.ToList(),
CurrentSongInfo.Title, CurrentSongInfo.Artist, CurrentSongInfo.Album, lyricsSearchResult?.Raw, (int?)CurrentSongInfo?.DurationMs, LyricsSearchProvider);
CurrentSongInfo.Title, CurrentSongInfo.Artist, CurrentSongInfo.Album, CurrentLyricsSearchResult?.Raw, (int?)CurrentSongInfo?.DurationMs, CurrentLyricsSearchResult?.Provider);
_lyricsDataArr = lyricsParser.LyricsDataArr;
ApplyChinesePreference();
}

View File

@@ -2,7 +2,7 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Collections;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
@@ -32,6 +32,8 @@ using System.Threading.Tasks;
using Windows.Media.Control;
using Windows.Storage.Streams;
using WindowsMediaController;
using BetterLyrics.WinUI3.Constants;
using BetterLyrics.WinUI3.Hooks;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
@@ -64,7 +66,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TimeSpan CurrentPosition { get; private set; } = TimeSpan.Zero;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial SongInfo? CurrentSongInfo { get; private set; }
public MediaSourceProviderInfo? CurrentMediaSourceProviderInfo { get; set; }
[ObservableProperty] public partial MediaSourceProviderInfo? CurrentMediaSourceProviderInfo { get; set; }
public MediaSessionsService(
ISettingsService settingsService,
@@ -119,7 +121,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void UpdatePlayOrPauseSongShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
{
if (CurrentIsPlaying)
{
@@ -134,7 +136,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void UpdatePreviousSongShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.PreviousSong, _settingsService.AppSettings.GeneralSettings.PreviousSongShortcut, () =>
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.PreviousSong, _settingsService.AppSettings.GeneralSettings.PreviousSongShortcut, () =>
{
_ = PreviousAsync();
});
@@ -142,7 +144,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void UpdateNextSongShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.NextSong, _settingsService.AppSettings.GeneralSettings.NextSongShortcut, () =>
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.NextSong, _settingsService.AppSettings.GeneralSettings.NextSongShortcut, () =>
{
_ = NextAsync();
});
@@ -291,7 +293,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
if (PlayerIdMatcher.IsLXMusic(sessionId))
if (PlayerIDMatcher.IsLXMusic(sessionId))
{
StopSSE();
}
@@ -310,16 +312,22 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
string? songId = null;
if (sessionId == Constants.PlayerID.AppleMusic || sessionId == Constants.PlayerID.AppleMusicAlternative)
if (PlayerIDMatcher.IsAppleMusic(sessionId))
{
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
}
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId ?? ""))
else if (PlayerIDMatcher.IsNeteaseFamily(sessionId))
{
songId = mediaProperties?.Genres.FirstOrDefault()?.Replace("NCM-", "");
songId = mediaProperties?.Genres
.Where(x => x.StartsWith(ExtendedGenreFiled.NetEaseCloudMusicTrackID))?.FirstOrDefault()?
.Replace(ExtendedGenreFiled.NetEaseCloudMusicTrackID, "");
}
var linkedFileName = mediaProperties?.Genres
.Where(x => x.StartsWith(ExtendedGenreFiled.FileName))?.FirstOrDefault()?
.Replace(ExtendedGenreFiled.FileName, "");
CurrentSongInfo = new SongInfo
{
Title = mediaProperties?.Title ?? "N/A",
@@ -327,13 +335,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
Album = fixedAlbum,
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds ?? 0,
PlayerId = sessionId,
SongId = songId
SongId = songId,
LinkedFileName = linkedFileName
};
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
if (PlayerIdMatcher.IsLXMusic(sessionId))
if (PlayerIDMatcher.IsLXMusic(sessionId))
{
StartSSE();
}
@@ -342,7 +351,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
StopSSE();
}
if (PlayerIdMatcher.IsLXMusic(sessionId) && _lxMusicAlbumArtBytes != null)
if (PlayerIDMatcher.IsLXMusic(sessionId) && _lxMusicAlbumArtBytes != null)
{
_SMTCAlbumArtBuffer = _lxMusicAlbumArtBytes.AsBuffer();
}
@@ -532,7 +541,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (PlayerIdMatcher.IsLXMusic(CurrentSongInfo?.PlayerId))
if (PlayerIDMatcher.IsLXMusic(CurrentSongInfo?.PlayerId))
{
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
if (data.ValueKind == JsonValueKind.Number)

View File

@@ -1,7 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Collections;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;

View File

@@ -0,0 +1,59 @@
using ComputeSharp;
using ComputeSharp.D2D1;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Shaders
{
[D2DInputCount(0)]
[D2DShaderProfile(D2D1ShaderProfile.PixelShader50)]
[D2DGeneratedPixelShaderDescriptor]
[D2DRequiresScenePosition]
public readonly partial struct SnowEffect(float time, Float2 resolution) : ID2D1PixelShader
{
public Float4 Execute()
{
Float2 fragCoord = D2D.GetScenePosition().XY;
float snow = 0.0f;
for (int k = 0; k < 6; k++)
{
for (int i = 0; i < 12; i++)
{
float cellSize = 2.0f + ((float)i * 3.0f);
float downSpeed = 0.3f + (Hlsl.Sin(time * 0.4f + (float)(k + i * 20)) + 1.0f) * 0.00008f;
downSpeed /= 10;
downSpeed *= -1;
Float2 uv = (fragCoord / resolution.X) +
new Float2(
0.01f * Hlsl.Sin((time + (float)(k * 6185)) * 0.6f + (float)i) * (5.0f * i),
downSpeed * (time + (float)(k * 1352)) * i
);
Float2 uvStep = (Hlsl.Ceil(uv * cellSize - new Float2(0.5f, 0.5f)) / cellSize);
float x = Hlsl.Frac(Hlsl.Sin(Hlsl.Dot(uvStep, new Float2(12.9898f + (float)k * 12.0f, 78.233f + (float)k * 315.156f))) * 43758.5453f + (float)k * 12.0f) - 0.5f;
float y = Hlsl.Frac(Hlsl.Sin(Hlsl.Dot(uvStep, new Float2(62.2364f + (float)k * 23.0f, 94.674f + (float)k * 95.0f))) * 62159.8432f + (float)k * 12.0f) - 0.5f;
float randomMagnitude1 = Hlsl.Sin(time * 2.5f) * 0.7f / cellSize;
float randomMagnitude2 = Hlsl.Cos(time * 2.5f) * 0.7f / cellSize;
float d = 5.0f * Hlsl.Distance((uvStep + new Float2(x * Hlsl.Sin(y), y) * randomMagnitude1 + new Float2(y, x) * randomMagnitude2), uv);
float omiVal = Hlsl.Frac(Hlsl.Sin(Hlsl.Dot(uvStep, new Float2(32.4691f, 94.615f))) * 31572.1684f);
if (omiVal < 0.08f)
{
float newd = (x + 1.0f) * 0.4f * Hlsl.Clamp(1.9f - d * (15.0f + (x * 6.3f)) * (cellSize / 1.4f), 0.0f, 1.0f);
snow += newd;
}
}
}
return (Float4)snow;
}
}
}

View File

@@ -225,7 +225,7 @@
<data name="LyricsNotFound" xml:space="preserve">
<value>Lyrics not found</value>
</data>
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
<value>Lyrics provider</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
@@ -258,7 +258,7 @@
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>Show translation only</value>
</data>
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
<value>Translation provider</value>
</data>
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
@@ -333,6 +333,9 @@
<data name="LyricsWindowSwitchWindowHelp.Text" xml:space="preserve">
<value>Go to Settings to add more modes</value>
</data>
<data name="LyricsWindowSwitchWindowTitle" xml:space="preserve">
<value>Lyrics window switcher</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>Album art area only</value>
</data>
@@ -1150,6 +1153,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>Play</value>
</data>
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
<value>Playback source</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>Play "Cut To The Feeling" on "soundcloud.com"</value>
</data>

View File

@@ -225,7 +225,7 @@
<data name="LyricsNotFound" xml:space="preserve">
<value>歌詞が見つかりません</value>
</data>
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
<value>歌詞プロバイダー</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
@@ -258,7 +258,7 @@
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>翻訳のみを表示します</value>
</data>
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
<value>翻訳プロバイダー</value>
</data>
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
@@ -333,6 +333,9 @@
<data name="LyricsWindowSwitchWindowHelp.Text" xml:space="preserve">
<value>設定に移動して、さらにモードを追加してください</value>
</data>
<data name="LyricsWindowSwitchWindowTitle" xml:space="preserve">
<value>歌詞ウィンドウスイッチャー</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>アルバムアートエリアのみ</value>
</data>
@@ -1150,6 +1153,9 @@
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>遊ぶ</value>
</data>
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
<value>再生ソース</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>「SoundCloud.com」で「Cut to the Feeling」を再生する</value>
</data>

View File

@@ -225,7 +225,7 @@
<data name="LyricsNotFound" xml:space="preserve">
<value>가사를 찾을 수 없습니다</value>
</data>
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
<value>가사 제공자</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
@@ -258,7 +258,7 @@
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>번역 만 표시하십시오</value>
</data>
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
<value>번역 제공자</value>
</data>
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
@@ -333,6 +333,9 @@
<data name="LyricsWindowSwitchWindowHelp.Text" xml:space="preserve">
<value>설정으로 이동하여 모드를 더 추가하세요</value>
</data>
<data name="LyricsWindowSwitchWindowTitle" xml:space="preserve">
<value>가사 창 전환기</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>앨범 아트 영역만</value>
</data>
@@ -1150,6 +1153,9 @@
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>놀다</value>
</data>
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
<value>재생 소스</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>"soundcloud.com"에서 "Fut to the Feeling"을 재생하십시오.</value>
</data>

View File

@@ -225,7 +225,7 @@
<data name="LyricsNotFound" xml:space="preserve">
<value>未找到歌词</value>
</data>
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
<value>歌词来源</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
@@ -258,7 +258,7 @@
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>仅显示翻译</value>
</data>
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
<value>翻译来源</value>
</data>
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
@@ -333,6 +333,9 @@
<data name="LyricsWindowSwitchWindowHelp.Text" xml:space="preserve">
<value>转到“设置”以添加更多模式</value>
</data>
<data name="LyricsWindowSwitchWindowTitle" xml:space="preserve">
<value>歌词窗口切换器</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>仅显示专辑区域</value>
</data>
@@ -1150,6 +1153,9 @@
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>播放</value>
</data>
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
<value>播放源</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
</data>

View File

@@ -225,7 +225,7 @@
<data name="LyricsNotFound" xml:space="preserve">
<value>找不到歌詞</value>
</data>
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
<value>歌詞來源</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
@@ -258,7 +258,7 @@
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>僅顯示翻譯</value>
</data>
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
<value>翻譯來源</value>
</data>
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
@@ -333,6 +333,9 @@
<data name="LyricsWindowSwitchWindowHelp.Text" xml:space="preserve">
<value>前往設定以新增更多模式</value>
</data>
<data name="LyricsWindowSwitchWindowTitle" xml:space="preserve">
<value>歌詞視窗切換器</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>僅限相簿藝術區域</value>
</data>
@@ -1150,6 +1153,9 @@
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>播放</value>
</data>
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
<value>播放源</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
</data>

View File

@@ -1,5 +1,6 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Helper.BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
@@ -59,7 +60,7 @@ namespace BetterLyrics.WinUI3.ViewModels
var succeed = _settingsService.ImportSettings(file.Path);
if (succeed)
{
WindowHelper.RestartApp();
WindowHook.RestartApp();
}
else
{

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -25,7 +26,7 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private static void RestartApp()
{
WindowHelper.RestartApp();
WindowHook.RestartApp();
}
public async Task<bool> ToggleAutoStartupAsync(bool target)

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
@@ -31,8 +32,8 @@ namespace BetterLyrics.WinUI3.ViewModels
LiveStates = _liveStatesService.LiveStates;
Volume = SystemVolumeHelper.MasterVolume;
SystemVolumeHelper.VolumeNotification += SystemVolumeHelper_VolumeNotification;
Volume = SystemVolumeHook.MasterVolume;
SystemVolumeHook.VolumeNotification += SystemVolumeHelper_VolumeNotification;
}
private void SystemVolumeHelper_VolumeNotification(object? sender, int e)
@@ -68,7 +69,7 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private static void OpenSettingsWindow()
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
[RelayCommand]

View File

@@ -1,6 +1,9 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Shaders;
using CommunityToolkit.WinUI;
using ComputeSharp.D2D1.WinUI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Effects;
@@ -68,6 +71,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
DrawAlbumArt(control, ds);
DrawSongInfo(control, ds);
DrawSnowEffect(control, ds);
if (_isDebugOverlayEnabled)
{
_drawFrameCount++;
@@ -400,6 +405,20 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
}
private void DrawSnowEffect(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled)
{
_snowEffect ??= new PixelShaderEffect<SnowEffect>();
var width = (float)control.ConvertDipsToPixels((float)control.Size.Width, CanvasDpiRounding.Round);
var height = (float)control.ConvertDipsToPixels((float)control.Size.Height, CanvasDpiRounding.Round);
_snowEffect?.ConstantBuffer = new SnowEffect((float)TotalTime.TotalSeconds, new(width, height));
ds.DrawImage(_snowEffect);
}
}
private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, double radius, double opacity)
{
ds.FillRoundedRectangle(

View File

@@ -1,4 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Shaders;
using ComputeSharp.D2D1.WinUI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI.Xaml;
@@ -17,6 +20,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private OpacityEffect? _albumArtBgEffect;
private CanvasCommandList? _albumArtEffect;
private PixelShaderEffect? _fluidEffect;
private PixelShaderEffect<SnowEffect>? _snowEffect;
private OpacityEffect CreateBgImageEffect(CanvasBitmap canvasBitmap, double opacity)
{

View File

@@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using BetterLyrics.WinUI3.Extensions;
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
@@ -117,9 +118,12 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (_fluidEffect != null)
{
var effectTime = Convert.ToSingle(_fluidEffect.Properties["iTime"]);
effectTime += Convert.ToSingle(_elapsedTime.TotalSeconds);
_fluidEffect.Properties["iTime"] = effectTime;
if (_mediaSessionsService.CurrentIsPlaying)
{
var effectTime = Convert.ToSingle(_fluidEffect.Properties["iTime"]);
effectTime += Convert.ToSingle(_elapsedTime.TotalSeconds);
_fluidEffect.Properties["iTime"] = effectTime;
}
if (_albumArtAccentColor1Transition.IsTransitioning)
{
@@ -168,7 +172,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isDebugOverlayEnabledChanged = false;
}
if (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed > 0)
if (_mediaSessionsService.CurrentIsPlaying && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed > 0)
{
_rotateAngle += _coverRotateBaseSpeed * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
_rotateAngle %= Math.PI * 2;

View File

@@ -33,7 +33,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
private bool _isLastFMTrackEnabled = false;
private bool _isLastFMTracked = false;
/// <summary>
@@ -46,10 +45,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
/// </summary>
private TimeSpan _elapsedTime = TimeSpan.Zero;
//public TimeSpan ScrollDeltaTime { get; set; } = TimeSpan.Zero;
//private TimeSpan _scrollTime = TimeSpan.Zero;
//public bool IsScrolling { get; set; } = false;
/// <summary>
/// Get or set the current time position of the song.
/// </summary>

View File

@@ -111,10 +111,12 @@ namespace BetterLyrics.WinUI3.ViewModels
LyricsSearchResults = [..await Task.Run(async () =>
{
return await _lyricsSearchService.SearchAllAsync(
MappedSongSearchQuery.MappedTitle,
MappedSongSearchQuery.MappedArtist,
MappedSongSearchQuery.MappedAlbum,
_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0, token);
new SongInfo {
Title = MappedSongSearchQuery.MappedTitle,
Artist = MappedSongSearchQuery.MappedArtist,
Album = MappedSongSearchQuery.MappedAlbum,
DurationMs = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0
}, token);
}, token)];
IsSearching = false;
});

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
@@ -37,13 +38,13 @@ namespace BetterLyrics.WinUI3.ViewModels
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
}
[RelayCommand]
private void RefreshMonitorDeviceNames()
{
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
LiveStates.LyricsWindowStatus?.MonitorDeviceName = MonitorDeviceNames.FirstOrDefault() ?? "";
}

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
@@ -33,7 +34,7 @@ namespace BetterLyrics.WinUI3
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
private ForegroundWindowWatcher? _fgWindowWatcher = null;
private ForegroundWindowHook? _fgWindowWatcher = null;
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
public LyricsWindowViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
@@ -72,11 +73,11 @@ namespace BetterLyrics.WinUI3
private void UpdateLyricsWindowShowHideShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowShowOrHide,
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowShowOrHide,
_settingsService.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut,
() =>
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
var window = WindowHook.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
if (window.Visible)
@@ -85,7 +86,7 @@ namespace BetterLyrics.WinUI3
}
else
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
WindowHook.OpenOrShowWindow<LyricsWindow>();
}
}
);
@@ -93,7 +94,7 @@ namespace BetterLyrics.WinUI3
private void UpdateLyricsWindowBorderlessShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.Borderless,
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.Borderless,
_settingsService.AppSettings.GeneralSettings.BorderlessShortcut,
() =>
{
@@ -104,7 +105,7 @@ namespace BetterLyrics.WinUI3
private void UpdateLyricsWindowClickThroughShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.ClickThrough,
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.ClickThrough,
_settingsService.AppSettings.GeneralSettings.ClickThroughShortcut,
() =>
{
@@ -115,24 +116,24 @@ namespace BetterLyrics.WinUI3
private void UpdateLyricsWindowSwitchShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowSwitch,
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowSwitch,
_settingsService.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut,
() =>
{
WindowHelper.OpenOrShowWindow<LyricsWindowSwitchWindow>();
WindowHook.OpenOrShowWindow<LyricsWindowSwitchWindow>();
}
);
}
public void InitFgWindowWatcher()
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
var window = WindowHook.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
_fgWindowWatcherTimer = _dispatcherQueue.CreateTimer();
_fgWindowWatcher = new ForegroundWindowWatcher(
_fgWindowWatcher = new ForegroundWindowHook(
hwnd,
fgHwnd =>
{
@@ -160,18 +161,18 @@ namespace BetterLyrics.WinUI3
BackdropAccentColor = ColorHelper.GetAccentColor(
hwnd,
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorDeviceName,
_liveStatesService.LiveStates.LyricsWindowStatus.EnvironmentSampleMode).ToColor();
_liveStatesService.LiveStates.LyricsWindowStatus.EnvironmentSampleMode);
}
public void ExitOrClose()
{
if (_settingsService.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed)
{
WindowHelper.ExitApp();
WindowHook.ExitApp();
}
else
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
var window = WindowHook.GetWindowByWindowType<LyricsWindow>();
window?.Hide();
}
}
@@ -225,7 +226,7 @@ namespace BetterLyrics.WinUI3
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentIsPlaying))
{
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
}
}
}

View File

@@ -1,6 +1,6 @@
using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Collections;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Helper.BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
@@ -24,6 +24,7 @@ using Windows.Media;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using BetterLyrics.WinUI3.Extensions;
namespace BetterLyrics.WinUI3.ViewModels
{
@@ -446,6 +447,8 @@ namespace BetterLyrics.WinUI3.ViewModels
var storageFile = await StorageFile.GetFileFromPathAsync(track.Path);
await updater.CopyFromFileAsync(MediaPlaybackType.Music, storageFile);
updater.MusicProperties.AlbumTitle = track.Album;
updater.MusicProperties.Genres.Add($"FILENAME-{Path.GetFileNameWithoutExtension(track.Path)}");
updater.Update();
}
}

View File

@@ -45,12 +45,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsLXMusicServerTesting { get; set; } = false;
[ObservableProperty]
public partial string OriginalLyricsRef { get; set; } = "about:blank";
[ObservableProperty]
public partial string TranslatedLyricsRef { get; set; } = "about:blank";
[ObservableProperty]
public partial int SelectedTargetLanguageIndex { get; set; }

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -15,50 +16,50 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private static void ExitApp()
{
WindowHelper.ExitApp();
WindowHook.ExitApp();
}
[RelayCommand]
private static void RestartApp()
{
WindowHelper.RestartApp();
WindowHook.RestartApp();
}
[RelayCommand]
private static void ResetWindowPosition()
{
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
var lyricsWindow = WindowHook.GetWindowByWindowType<LyricsWindow>();
lyricsWindow?.MoveAndResize(100, 100, 800, 500);
}
[RelayCommand]
private static void OpenSettings()
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
[RelayCommand]
private static void OpenMusicGallery()
{
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
[RelayCommand]
private static void OpenLyrics()
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
WindowHook.OpenOrShowWindow<LyricsWindow>();
}
[RelayCommand]
private static void OpenLyricsWindowSwitch()
{
WindowHelper.OpenOrShowWindow<LyricsWindowSwitchWindow>();
WindowHook.OpenOrShowWindow<LyricsWindowSwitchWindow>();
}
[RelayCommand]
private static void OpenLyricsSearchWindow()
{
WindowHelper.OpenOrShowWindow<LyricsSearchWindow>();
WindowHook.OpenOrShowWindow<LyricsSearchWindow>();
}
}
}

View File

@@ -27,8 +27,6 @@
<!-- Lyrics area -->
<renderer:LyricsRenderer />
<dev:SnowFlakeEffect FlakeCount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=OneWay}" Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
<StackPanel
Margin="40"
@@ -36,8 +34,6 @@
dev:Growl.GrowlParent="True" />
</ScrollViewer>
<!--<Grid x:Name="ScrollGrid" PointerWheelChanged="ScrollGrid_PointerWheelChanged" Background="{ThemeResource AccentAcrylicBackgroundFillColorDefaultBrush}" Opacity="0.3"/>-->
<!-- Bottom command area -->
<Grid
x:Name="BottomCommandGrid"

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Controls;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
@@ -131,7 +132,7 @@ namespace BetterLyrics.WinUI3.Views
private void LyricsSearchShortcutButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<LyricsSearchWindow>();
WindowHook.OpenOrShowWindow<LyricsSearchWindow>();
}
private void TimelineSliderOverlay_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
@@ -180,7 +181,7 @@ namespace BetterLyrics.WinUI3.Views
private void ExtendedSlider_ValueChangedByUser(object sender, Events.ExtendedSliderValueChangedByUserEventArgs e)
{
SystemVolumeHelper.MasterVolume = ViewModel.Volume;
SystemVolumeHook.MasterVolume = ViewModel.Volume;
}
//private void ScrollGrid_PointerWheelChanged(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)

View File

@@ -1,4 +1,8 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
@@ -14,26 +18,18 @@ namespace BetterLyrics.WinUI3.Views
/// </summary>
public sealed partial class LyricsSearchWindow : Window
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public LyricsSearchWindow()
{
InitializeComponent();
Title = _resourceService.GetLocalizedString("LyricsSearchPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();
ExtendsContentIntoTitleBar = true;
this.Init("LyricsSearchPageTitle", backdropType: BackdropType.Transparent);
AppWindow.Closing += AppWindow_Closing;
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(Enums.BackdropType.Transparent);
}
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHelper.CloseWindow<LyricsSearchWindow>();
WindowHook.CloseWindow<LyricsSearchWindow>();
}
}

View File

@@ -1,7 +1,9 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -16,7 +18,6 @@ namespace BetterLyrics.WinUI3.Views
public sealed partial class LyricsWindow : Window
{
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
private readonly WindowMessageMonitor _wmm;
@@ -26,18 +27,12 @@ namespace BetterLyrics.WinUI3.Views
{
this.InitializeComponent();
AppWindow.SetIcons();
AppWindow.Changed += AppWindow_Changed;
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
Title = _resourceService.GetLocalizedString("LyricsPageTitle");
_wmm = new WindowMessageMonitor(this);
_wmm.WindowMessageReceived += Wmm_WindowMessageReceived;
this.Init("LyricsPageTitle", TitleBarHeightOption.Collapsed, BackdropType.Transparent);
AppWindow.Changed += AppWindow_Changed;
AppWindow.Closing += AppWindow_Closing;
}
@@ -70,7 +65,7 @@ namespace BetterLyrics.WinUI3.Views
if (e.Message.MessageId == (uint)User32.WindowMessage.WM_HOTKEY)
{
int id = (int)e.Message.WParam;
GlobalHotKeyHelper.TryInvokeAction(id);
GlobalHotKeyHook.TryInvokeAction(id);
}
}
@@ -109,7 +104,7 @@ namespace BetterLyrics.WinUI3.Views
private void MusicGalleryButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
@@ -119,12 +114,12 @@ namespace BetterLyrics.WinUI3.Views
private void LyricsWindowSwitchButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<LyricsWindowSwitchWindow>();
WindowHook.OpenOrShowWindow<LyricsWindowSwitchWindow>();
}
private void SettingsWindowButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
}
}

View File

@@ -1,6 +1,10 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using WinUIEx;
@@ -20,10 +24,9 @@ namespace BetterLyrics.WinUI3.Views
{
InitializeComponent();
AppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = Microsoft.UI.Windowing.TitleBarHeightOption.Collapsed;
this.Init("LyricsWindowSwitchWindowTitle", TitleBarHeightOption.Collapsed, BackdropType.Transparent);
this.CenterOnScreen();
this.SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(Enums.BackdropType.Transparent);
this.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
AppWindow.IsShownInSwitchers = false;
this.SetIsAlwaysOnTop(true);
@@ -32,7 +35,7 @@ namespace BetterLyrics.WinUI3.Views
AppWindow.Changed += AppWindow_Changed;
}
private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidVisibilityChange)
{

View File

@@ -7,6 +7,7 @@
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dev="using:DevWinUI"
xmlns:ext="using:BetterLyrics.WinUI3.Extensions"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
xmlns:local="using:BetterLyrics.WinUI3.Views"

View File

@@ -1,11 +1,13 @@
using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using DevWinUI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;

View File

@@ -9,10 +9,6 @@
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Window.SystemBackdrop>
<DesktopAcrylicBackdrop />
</Window.SystemBackdrop>
<Grid>
<local:MusicGalleryPage Margin="0,40,0,0" />

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
@@ -14,34 +16,28 @@ namespace BetterLyrics.WinUI3.Views
/// </summary>
public sealed partial class MusicGalleryWindow : Window
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public MusicGalleryWindow()
{
InitializeComponent();
Title = _resourceService.GetLocalizedString("MusicGalleryPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();
ExtendsContentIntoTitleBar = true;
this.Init("MusicGalleryPageTitle");
AppWindow.Closing += AppWindow_Closing;
}
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHelper.CloseWindow<MusicGalleryWindow>();
WindowHook.CloseWindow<MusicGalleryWindow>();
}
private void LyricsWindowButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
WindowHook.OpenOrShowWindow<LyricsWindow>();
}
private void SettingsWindowButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
}
}

View File

@@ -9,10 +9,6 @@
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Window.SystemBackdrop>
<DesktopAcrylicBackdrop />
</Window.SystemBackdrop>
<Grid x:Name="RootGrid">
<Frame x:Name="RootFrame" Margin="0,40,0,0" />

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -16,11 +18,8 @@ namespace BetterLyrics.WinUI3.Views
public SettingsWindow()
{
InitializeComponent();
Title = _resourceService.GetLocalizedString("SettingsPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();
ExtendsContentIntoTitleBar = true;
this.Init("SettingsPageTitle");
AppWindow.Closing += AppWindow_Closing;
@@ -29,17 +28,17 @@ namespace BetterLyrics.WinUI3.Views
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHelper.CloseWindow<SettingsWindow>();
WindowHook.CloseWindow<SettingsWindow>();
}
private void LyricsWindowButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
WindowHook.OpenOrShowWindow<LyricsWindow>();
}
private void MusicGalleryButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<MusicGalleryWindow>();
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
}
}