mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
change language detection model
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.9.0" />
|
||||
Version="1.0.10.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</Style>
|
||||
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="16,9,16,11" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
|
||||
@@ -37,15 +37,12 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
var test = LanguageDetectionHelper.DetectLanguageCode("一隻烏龜");
|
||||
|
||||
|
||||
DispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
DispatcherQueueTimer = DispatcherQueue.CreateTimer();
|
||||
ResourceLoader = new ResourceLoader();
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
AppInfo.EnsureDirectories();
|
||||
PathHelper.EnsureDirectories();
|
||||
ConfigureServices();
|
||||
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<App>>();
|
||||
@@ -62,16 +59,6 @@ namespace BetterLyrics.WinUI3
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow == null) return;
|
||||
|
||||
string[] commandLineArguments = Environment.GetCommandLineArgs();
|
||||
if (commandLineArguments.Length > 1)
|
||||
{
|
||||
commandLineArguments = commandLineArguments.Skip(1).ToArray();
|
||||
if (commandLineArguments.First() == AppInfo.UnlockWindowTag)
|
||||
{
|
||||
lyricsWindow.AutoSelectLyricsMode(AutoStartWindowType.DesktopMode, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
lyricsWindow.AutoSelectLyricsMode();
|
||||
}
|
||||
|
||||
@@ -79,7 +66,7 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Verbose)
|
||||
.WriteTo.File(AppInfo.LogFilePattern, rollingInterval: RollingInterval.Day)
|
||||
.WriteTo.File(PathHelper.LogFilePattern, rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
// Register services
|
||||
@@ -93,9 +80,10 @@ namespace BetterLyrics.WinUI3
|
||||
// Services
|
||||
.AddSingleton<ISettingsService, SettingsService>()
|
||||
.AddSingleton<IPlaybackService, PlaybackService>()
|
||||
.AddSingleton<IMusicSearchService, MusicSearchService>()
|
||||
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
|
||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ILibreTranslateService, LibreTranslateService>()
|
||||
.AddSingleton<ITranslateService, TranslateService>()
|
||||
// ViewModels
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
|
||||
56094
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Core14.profile.xml
Normal file
56094
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Core14.profile.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@
|
||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Core14.profile.xml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Views\SettingsWindow.xaml" />
|
||||
</ItemGroup>
|
||||
@@ -42,7 +43,6 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="LanguageDetection.NETStandard" Version="1.3.1" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||
@@ -50,6 +50,7 @@
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
|
||||
@@ -23,11 +23,11 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => AppInfo.LrcLibLyricsCacheDirectory,
|
||||
LyricsSearchProvider.QQ => AppInfo.QQLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Netease => AppInfo.NeteaseLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Kugou => AppInfo.KugouLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AmllTtmlDb => AppInfo.AmllTtmlDbLyricsCacheDirectory,
|
||||
LyricsSearchProvider.LrcLib => PathHelper.LrcLibLyricsCacheDirectory,
|
||||
LyricsSearchProvider.QQ => PathHelper.QQLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
|
||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
||||
_ => throw new System.ArgumentOutOfRangeException(nameof(provider)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum MusicSearchMatchMode
|
||||
{
|
||||
TitleAndArtist,
|
||||
TitleArtistAlbumAndDuration,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum WindowColorSampleMode
|
||||
public enum WindowPixelSampleMode
|
||||
{
|
||||
BelowWindow,
|
||||
WindowArea,
|
||||
@@ -1,116 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.FileProperties;
|
||||
|
||||
public static class AppInfo
|
||||
{
|
||||
public const string AppAuthor = "Zhe Fang";
|
||||
public const string AppDisplayName = "Better Lyrics";
|
||||
public const string AppName = "BetterLyrics";
|
||||
public static string AppVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = Package.Current.Id.Version;
|
||||
return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
||||
}
|
||||
}
|
||||
|
||||
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||
|
||||
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||
|
||||
|
||||
public const string UnlockWindowTag = "UnlockWindow";
|
||||
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(CacheFolder, "amll-ttml-db-index.json");
|
||||
|
||||
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
|
||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||
|
||||
public static string LrcLibLyricsCacheDirectory => Path.Combine(CacheFolder, "lrclib-lyrics");
|
||||
public static string NeteaseLyricsCacheDirectory => Path.Combine(CacheFolder, "netease-lyrics");
|
||||
public static string QQLyricsCacheDirectory => Path.Combine(CacheFolder, "qq-lyrics");
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(CacheFolder, "kugou-lyrics");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(CacheFolder, "amll-ttml-db-lyrics");
|
||||
|
||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(CacheFolder, "itunes-album-art");
|
||||
|
||||
|
||||
private static string LocalFolder => ApplicationData.Current.LocalFolder.Path;
|
||||
|
||||
public static void EnsureDirectories()
|
||||
{
|
||||
Directory.CreateDirectory(LocalFolder);
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
|
||||
Directory.CreateDirectory(LrcLibLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(QQLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(KugouLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
|
||||
public static async Task<DateTime> GetBuildDate()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var filePath = assembly.Location;
|
||||
if (!File.Exists(filePath))
|
||||
return DateTime.MinValue;
|
||||
|
||||
StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
// 获取文件基本属性
|
||||
BasicProperties props = await file.GetBasicPropertiesAsync();
|
||||
// 返回修改日期
|
||||
return props.DateModified.DateTime;
|
||||
}
|
||||
|
||||
public static List<LanguageInfo> TranslationLanguagesInfo =>
|
||||
[
|
||||
new LanguageInfo("ar", "العربية"),
|
||||
new LanguageInfo("az", "Azərbaycan dili"),
|
||||
new LanguageInfo("zh-Hans", "简体中文"),
|
||||
new LanguageInfo("zh-Hant", "繁體中文"),
|
||||
new LanguageInfo("cs", "Čeština"),
|
||||
new LanguageInfo("da", "Dansk"),
|
||||
new LanguageInfo("nl", "Nederlands"),
|
||||
new LanguageInfo("en", "English"),
|
||||
new LanguageInfo("eo", "Esperanto"),
|
||||
new LanguageInfo("fi", "Suomi"),
|
||||
new LanguageInfo("fr", "Français"),
|
||||
new LanguageInfo("de", "Deutsch"),
|
||||
new LanguageInfo("el", "Ελληνικά"),
|
||||
new LanguageInfo("he", "עברית"),
|
||||
new LanguageInfo("hi", "हिन्दी"),
|
||||
new LanguageInfo("hu", "Magyar"),
|
||||
new LanguageInfo("id", "Bahasa Indonesia"),
|
||||
new LanguageInfo("ga", "Gaeilge"),
|
||||
new LanguageInfo("it", "Italiano"),
|
||||
new LanguageInfo("ja", "日本語"),
|
||||
new LanguageInfo("ko", "한국어"),
|
||||
new LanguageInfo("fa", "فارسی"),
|
||||
new LanguageInfo("pl", "Polski"),
|
||||
new LanguageInfo("pt", "Português"),
|
||||
new LanguageInfo("ru", "Русский"),
|
||||
new LanguageInfo("sk", "Slovenčina"),
|
||||
new LanguageInfo("es", "Español"),
|
||||
new LanguageInfo("sv", "Svenska"),
|
||||
new LanguageInfo("tr", "Türkçe"),
|
||||
new LanguageInfo("uk", "Українська"),
|
||||
new LanguageInfo("vi", "Tiếng Việt"),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.UI;
|
||||
|
||||
using Color = Windows.UI.Color;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class ColorHelper
|
||||
@@ -98,5 +105,140 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, brightness);
|
||||
}
|
||||
|
||||
public static System.Drawing.Color GetAccentColor(IntPtr myHwnd, WindowPixelSampleMode mode)
|
||||
{
|
||||
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return System.Drawing.Color.Transparent;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case WindowPixelSampleMode.BelowWindow:
|
||||
{
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int sampleHeight = 1;
|
||||
int sampleY = myRect.Bottom + 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
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;
|
||||
// 采集窗口区域的平均色
|
||||
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top, width, height);
|
||||
}
|
||||
case WindowPixelSampleMode.WindowEdge:
|
||||
{
|
||||
int width = myRect.Right - myRect.Left;
|
||||
int height = myRect.Bottom - myRect.Top;
|
||||
if (width <= 0 || height <= 0)
|
||||
return System.Drawing.Color.Transparent;
|
||||
|
||||
var edgeThickness = new Thickness(36, 0, 36, 0);
|
||||
List<System.Drawing.Color> edgeColors = [];
|
||||
|
||||
// Top edge
|
||||
if (edgeThickness.Top > 0 && edgeThickness.Top < height)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Top,
|
||||
width,
|
||||
(int)edgeThickness.Top
|
||||
)
|
||||
);
|
||||
// Bottom edge
|
||||
if (edgeThickness.Bottom > 0 && edgeThickness.Bottom < height)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Bottom - (int)edgeThickness.Bottom,
|
||||
width,
|
||||
(int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
// Left edge
|
||||
if (edgeThickness.Left > 0 && edgeThickness.Left < width)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Top + (int)edgeThickness.Top,
|
||||
(int)edgeThickness.Left,
|
||||
height - (int)edgeThickness.Top - (int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
// Right edge
|
||||
if (edgeThickness.Right > 0 && edgeThickness.Right < width)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Right - (int)edgeThickness.Right,
|
||||
myRect.Top + (int)edgeThickness.Top,
|
||||
(int)edgeThickness.Right,
|
||||
height - (int)edgeThickness.Top - (int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
|
||||
// 合并四边平均色
|
||||
if (edgeColors.Count == 0)
|
||||
return System.Drawing.Color.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(
|
||||
255,
|
||||
(int)(r / edgeColors.Count),
|
||||
(int)(g / edgeColors.Count),
|
||||
(int)(b / edgeColors.Count)
|
||||
);
|
||||
}
|
||||
default:
|
||||
return System.Drawing.Color.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
private static System.Drawing.Color GetAverageColorFromScreenRegion(int x, int y, int width, int height)
|
||||
{
|
||||
using Bitmap bmp = new(width, height, PixelFormat.Format32bppArgb);
|
||||
using Graphics gDest = Graphics.FromImage(bmp);
|
||||
|
||||
IntPtr hdcDest = gDest.GetHdc();
|
||||
IntPtr hdcSrc = (nint)User32.GetDC(IntPtr.Zero); // Entire screen
|
||||
|
||||
Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, Gdi32.RasterOperationMode.SRCCOPY);
|
||||
|
||||
gDest.ReleaseHdc(hdcDest);
|
||||
User32.ReleaseDC(IntPtr.Zero, hdcSrc);
|
||||
|
||||
return ComputeAverageColor(bmp);
|
||||
}
|
||||
|
||||
private static System.Drawing.Color ComputeAverageColor(Bitmap bmp)
|
||||
{
|
||||
long r = 0, g = 0, b = 0;
|
||||
int count = 0;
|
||||
|
||||
for (int y = 0; y < bmp.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bmp.Width; x++)
|
||||
{
|
||||
System.Drawing.Color pixel = bmp.GetPixel(x, y);
|
||||
r += pixel.R;
|
||||
g += pixel.G;
|
||||
b += pixel.B;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) return System.Drawing.Color.Transparent;
|
||||
return System.Drawing.Color.FromArgb((int)(r / count), (int)(g / count), (int)(b / count));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,7 +16,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
private static readonly Dictionary<IntPtr, bool> _clickThroughStates = [];
|
||||
private static readonly Dictionary<IntPtr, bool> _originalTopmostStates = [];
|
||||
private static readonly Dictionary<IntPtr, (double X, double Y, double Width, double Height)> _originalWindowBounds = [];
|
||||
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyles = [];
|
||||
@@ -95,17 +95,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
window.SetIsAlwaysOnTop(true);
|
||||
|
||||
window.SetIsShownInSwitchers(false);
|
||||
}
|
||||
|
||||
public static void Lock(Window window)
|
||||
{
|
||||
window.SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ޱ߿<DEB1><DFBF><EFBFBD><EFBFBD><CDB8>
|
||||
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
|
||||
SetClickThrough(window, true);
|
||||
}
|
||||
|
||||
public static void SetClickThrough(Window window, bool enable)
|
||||
@@ -115,30 +106,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
if (enable)
|
||||
{
|
||||
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle | (int)User32.WindowStylesEx.WS_EX_TRANSPARENT | (int)User32.WindowStylesEx.WS_EX_LAYERED);
|
||||
_clickThroughStates[hwnd] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle & ~(int)User32.WindowStylesEx.WS_EX_TRANSPARENT);
|
||||
_clickThroughStates[hwnd] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unlock(Window window)
|
||||
{
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
// <20>ָ<EFBFBD><D6B8><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD> Disable ʱ<><CAB1><EFBFBD>Ƴ<EFBFBD><C6B3><EFBFBD>
|
||||
if (_originalWindowStyles.TryGetValue(hwnd, out var style))
|
||||
{
|
||||
window.SetWindowStyle(style);
|
||||
}
|
||||
window.ExtendsContentIntoTitleBar = true;
|
||||
|
||||
SetClickThrough(window, false);
|
||||
|
||||
// To recover the system backdrop, we need to reopen the window
|
||||
WindowHelper.RestartApp(AppInfo.UnlockWindowTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Ude;
|
||||
@@ -21,5 +23,60 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
return Encoding.GetEncoding(encoding);
|
||||
}
|
||||
|
||||
public static string SanitizeFileName(string fileName, char replacement = '_')
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(fileName.Length);
|
||||
foreach (var c in fileName)
|
||||
{
|
||||
sb.Append(Array.IndexOf(invalidChars, c) >= 0 ? replacement : c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string? ReadLyricsCache(string title, string artist, LyricsFormat format, string cacheFolderPath)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title}{format.ToFileExtension()}"));
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllText(cacheFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[]? ReadAlbumArtCache(string album, string artist, string format, string cacheFolderPath)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {album}{format}"));
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllBytes(cacheFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void WriteLyricsCache(string title, string artist, string lyrics, LyricsFormat format, string cacheFolderPath)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {title}{format.ToFileExtension()}"));
|
||||
File.WriteAllText(cacheFilePath, lyrics);
|
||||
}
|
||||
|
||||
public static void WriteAlbumArtCache(string album, string artist, byte[] img, string format, string cacheFolderPath)
|
||||
{
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {album}{format}"));
|
||||
File.WriteAllBytes(cacheFilePath, img);
|
||||
}
|
||||
|
||||
public static bool IsSwitchableNormalizedMatch(string fileName, string q1, string q2)
|
||||
{
|
||||
var normFileName = fileName.Normalize();
|
||||
var normQ1 = q1.Normalize();
|
||||
var normQ2 = q2.Normalize();
|
||||
|
||||
// 常见两种顺序
|
||||
return normFileName == normQ1 + normQ2
|
||||
|| normFileName == normQ2 + normQ1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ForegroundWindowWatcherHelper
|
||||
public class ForegroundWindowWatcher
|
||||
{
|
||||
private readonly User32.WinEventProc _winEventDelegate;
|
||||
private readonly List<User32.HWINEVENTHOOK> _hooks = new();
|
||||
@@ -20,11 +20,17 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public delegate void WindowChangedHandler(HWND hwnd);
|
||||
private readonly WindowChangedHandler _onWindowChanged;
|
||||
|
||||
public ForegroundWindowWatcherHelper(IntPtr selfHwnd, WindowChangedHandler onWindowChanged)
|
||||
private readonly DispatcherTimer _timer;
|
||||
|
||||
public ForegroundWindowWatcher(IntPtr selfHwnd, WindowChangedHandler onWindowChanged)
|
||||
{
|
||||
_selfHwnd = selfHwnd;
|
||||
_onWindowChanged = onWindowChanged;
|
||||
_winEventDelegate = new User32.WinEventProc(WinEventProc);
|
||||
|
||||
_timer = new DispatcherTimer();
|
||||
_timer.Interval = TimeSpan.FromSeconds(1);
|
||||
_timer.Tick += Timer_Tick;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -54,6 +60,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
User32.WINEVENT.WINEVENT_OUTOFCONTEXT
|
||||
)
|
||||
);
|
||||
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -62,6 +70,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
User32.UnhookWinEvent(hook);
|
||||
|
||||
_hooks.Clear();
|
||||
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
private void Timer_Tick(object? sender, object e)
|
||||
{
|
||||
if (_currentForeground != HWND.NULL)
|
||||
{
|
||||
_onWindowChanged?.Invoke(_currentForeground);
|
||||
}
|
||||
}
|
||||
|
||||
private void WinEventProc(
|
||||
@@ -19,7 +19,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ImageHelper
|
||||
{
|
||||
public const int AccentColorCount = 3;
|
||||
private const int _accentColorCount = 1;
|
||||
|
||||
public static async Task<InMemoryRandomAccessStream> ByteArrayToStream(byte[] bytes)
|
||||
{
|
||||
@@ -127,7 +127,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
// 按出现次数排序,取前 AccentColorCount 个
|
||||
var topColors = colorCount
|
||||
.OrderByDescending(kv => kv.Value)
|
||||
.Take(AccentColorCount)
|
||||
.Take(_accentColorCount)
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
|
||||
@@ -138,31 +138,31 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
|
||||
|
||||
public static async Task<BitmapImage> GetBitmapImageFromBytesAsync(byte[] imageBytes)
|
||||
{
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(imageBytes.AsBuffer());
|
||||
stream.Seek(0);
|
||||
//public static async Task<BitmapImage> GetBitmapImageFromBytesAsync(byte[] imageBytes)
|
||||
//{
|
||||
// var stream = new InMemoryRandomAccessStream();
|
||||
// await stream.WriteAsync(imageBytes.AsBuffer());
|
||||
// stream.Seek(0);
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
await bitmapImage.SetSourceAsync(stream);
|
||||
// var bitmapImage = new BitmapImage();
|
||||
// await bitmapImage.SetSourceAsync(stream);
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
// return bitmapImage;
|
||||
//}
|
||||
|
||||
public static async Task<BitmapDecoder> GetDecoderFromByte(byte[] bytes) =>
|
||||
await BitmapDecoder.CreateAsync(await ByteArrayToStream(bytes));
|
||||
//public static async Task<BitmapDecoder> GetDecoderFromByte(byte[] bytes) =>
|
||||
// await BitmapDecoder.CreateAsync(await ByteArrayToStream(bytes));
|
||||
|
||||
public static async Task<InMemoryRandomAccessStream> GetStreamFromBytesAsync(byte[] imageBytes)
|
||||
{
|
||||
if (imageBytes == null || imageBytes.Length == 0)
|
||||
return null;
|
||||
//public static async Task<InMemoryRandomAccessStream> GetStreamFromBytesAsync(byte[] imageBytes)
|
||||
//{
|
||||
// if (imageBytes == null || imageBytes.Length == 0)
|
||||
// return null;
|
||||
|
||||
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(imageBytes.AsBuffer());
|
||||
// InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
|
||||
// await stream.WriteAsync(imageBytes.AsBuffer());
|
||||
|
||||
return stream;
|
||||
}
|
||||
// return stream;
|
||||
//}
|
||||
|
||||
public static async Task<byte[]> ToByteArrayAsync(IRandomAccessStreamReference streamRef)
|
||||
{
|
||||
@@ -172,21 +172,21 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
public static float GetAverageLuminance(CanvasBitmap bitmap)
|
||||
{
|
||||
var pixels = bitmap.GetPixelBytes();
|
||||
double sum = 0;
|
||||
for (int i = 0; i < pixels.Length; i += 4)
|
||||
{
|
||||
// BGRA
|
||||
byte b = pixels[i];
|
||||
byte g = pixels[i + 1];
|
||||
byte r = pixels[i + 2];
|
||||
// 忽略A
|
||||
double y = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
sum += y / 255.0;
|
||||
}
|
||||
return (float)(sum / (pixels.Length / 4));
|
||||
}
|
||||
//public static float GetAverageLuminance(CanvasBitmap bitmap)
|
||||
//{
|
||||
// var pixels = bitmap.GetPixelBytes();
|
||||
// double sum = 0;
|
||||
// for (int i = 0; i < pixels.Length; i += 4)
|
||||
// {
|
||||
// // BGRA
|
||||
// byte b = pixels[i];
|
||||
// byte g = pixels[i + 1];
|
||||
// byte r = pixels[i + 2];
|
||||
// // 忽略A
|
||||
// double y = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
// sum += y / 255.0;
|
||||
// }
|
||||
// return (float)(sum / (pixels.Length / 4));
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
using LanguageDetection;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class LanguageDetectionHelper
|
||||
{
|
||||
private static readonly LanguageDetector _detector = new();
|
||||
|
||||
static LanguageDetectionHelper()
|
||||
{
|
||||
_detector.AddAllLanguages();
|
||||
}
|
||||
|
||||
private static string? ThreeLetterToTwoLetter(string threeLetterCode)
|
||||
{
|
||||
foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
|
||||
{
|
||||
if (string.Equals(ci.ThreeLetterISOLanguageName, threeLetterCode, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ci.TwoLetterISOLanguageName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
string? code = ThreeLetterToTwoLetter(_detector.Detect(text));
|
||||
if (code != null && code == "zh")
|
||||
{
|
||||
if (ChineseConverter.ConvertToTraditionalChinese(text) == text)
|
||||
{
|
||||
return "zh-Hant";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "zh-Hans";
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
public static bool IsCJK(string text)
|
||||
{
|
||||
return DetectLanguageCode(text) switch
|
||||
{
|
||||
"zh" or "ja" or "ko" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
110
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs
Normal file
110
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using NTextCat;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class LanguageHelper
|
||||
{
|
||||
private static readonly RankedLanguageIdentifierFactory _factory = new();
|
||||
private static readonly RankedLanguageIdentifier _identifier;
|
||||
|
||||
public static List<Models.LanguageInfo> SupportedTargetLanguages =>
|
||||
[
|
||||
new Models.LanguageInfo("ar", "العربية"),
|
||||
new Models.LanguageInfo("az", "Azərbaycan dili"),
|
||||
new Models.LanguageInfo("zh-Hans", "简体中文"),
|
||||
new Models.LanguageInfo("zh-Hant", "繁體中文"),
|
||||
new Models.LanguageInfo("cs", "Čeština"),
|
||||
new Models.LanguageInfo("da", "Dansk"),
|
||||
new Models.LanguageInfo("nl", "Nederlands"),
|
||||
new Models.LanguageInfo("en", "English"),
|
||||
new Models.LanguageInfo("eo", "Esperanto"),
|
||||
new Models.LanguageInfo("fi", "Suomi"),
|
||||
new Models.LanguageInfo("fr", "Français"),
|
||||
new Models.LanguageInfo("de", "Deutsch"),
|
||||
new Models.LanguageInfo("el", "Ελληνικά"),
|
||||
new Models.LanguageInfo("he", "עברית"),
|
||||
new Models.LanguageInfo("hi", "हिन्दी"),
|
||||
new Models.LanguageInfo("hu", "Magyar"),
|
||||
new Models.LanguageInfo("id", "Bahasa Indonesia"),
|
||||
new Models.LanguageInfo("ga", "Gaeilge"),
|
||||
new Models.LanguageInfo("it", "Italiano"),
|
||||
new Models.LanguageInfo("ja", "日本語"),
|
||||
new Models.LanguageInfo("ko", "한국어"),
|
||||
new Models.LanguageInfo("fa", "فارسی"),
|
||||
new Models.LanguageInfo("pl", "Polski"),
|
||||
new Models.LanguageInfo("pt", "Português"),
|
||||
new Models.LanguageInfo("ru", "Русский"),
|
||||
new Models.LanguageInfo("sk", "Slovenčina"),
|
||||
new Models.LanguageInfo("es", "Español"),
|
||||
new Models.LanguageInfo("sv", "Svenska"),
|
||||
new Models.LanguageInfo("tr", "Türkçe"),
|
||||
new Models.LanguageInfo("uk", "Українська"),
|
||||
new Models.LanguageInfo("vi", "Tiếng Việt"),
|
||||
];
|
||||
|
||||
static LanguageHelper()
|
||||
{
|
||||
_identifier = _factory.Load(PathHelper.LanguageProfilePath);
|
||||
}
|
||||
|
||||
private static string? ThreeLetterToTwoLetter(string? threeLetterCode)
|
||||
{
|
||||
if (threeLetterCode == null) return null;
|
||||
|
||||
foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
|
||||
{
|
||||
if (string.Equals(ci.ThreeLetterISOLanguageName, threeLetterCode, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ci.TwoLetterISOLanguageName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
string? code = ThreeLetterToTwoLetter(_identifier.Identify(text).FirstOrDefault()?.Item1.Iso639_2T);
|
||||
if (code != null && code == "zh")
|
||||
{
|
||||
if (ChineseConverter.ConvertToTraditionalChinese(text) == text)
|
||||
{
|
||||
return "zh-Hant";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "zh-Hans";
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
public static bool IsCJK(string text)
|
||||
{
|
||||
return DetectLanguageCode(text) switch
|
||||
{
|
||||
"zh" or "ja" or "ko" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static string DetectCountryCode(string? text)
|
||||
{
|
||||
if (text == null) return "en";
|
||||
var code = DetectLanguageCode(text);
|
||||
if (code == null) return "en";
|
||||
// 处理中文简体和繁体
|
||||
if (code == "zh-Hans") return "cn";
|
||||
if (code == "zh-Hant") return "cn";
|
||||
// 其他语言直接返回两字母代码
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
// 原文(非 CJK 语言添加空格)
|
||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
if (!LanguageDetectionHelper.IsCJK(originalText))
|
||||
if (!LanguageHelper.IsCJK(originalText))
|
||||
{
|
||||
foreach (var span in originalTextSpans)
|
||||
{
|
||||
@@ -269,7 +269,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
private int ParseTtmlTime(string? t)
|
||||
private static int ParseTtmlTime(string? t)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(t))
|
||||
return 0;
|
||||
@@ -420,18 +420,21 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
}
|
||||
if (linesInSingleLang.Count > 0 && linesInSingleLang[0].StartMs > 0)
|
||||
if (linesInSingleLang.Count > 0)
|
||||
{
|
||||
linesInSingleLang.Insert(
|
||||
0,
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = linesInSingleLang[0].StartMs,
|
||||
OriginalText = "● ● ●",
|
||||
CharTimings = [],
|
||||
}
|
||||
);
|
||||
if (linesInSingleLang[0].StartMs > 0)
|
||||
{
|
||||
linesInSingleLang.Insert(
|
||||
0,
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = linesInSingleLang[0].StartMs,
|
||||
OriginalText = "● ● ●",
|
||||
CharTimings = [],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.FileProperties;
|
||||
|
||||
public static class MetadataHelper
|
||||
{
|
||||
public const string AppAuthor = "Zhe Fang";
|
||||
public const string AppDisplayName = "Better Lyrics";
|
||||
public const string AppName = "BetterLyrics";
|
||||
public static string AppVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = Package.Current.Id.Version;
|
||||
return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
||||
}
|
||||
}
|
||||
|
||||
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||
|
||||
public static async Task<DateTime> GetBuildDate()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var filePath = assembly.Location;
|
||||
if (!File.Exists(filePath))
|
||||
return DateTime.MinValue;
|
||||
|
||||
StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
// 获取文件基本属性
|
||||
BasicProperties props = await file.GetBasicPropertiesAsync();
|
||||
// 返回修改日期
|
||||
return props.DateModified.DateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs
Normal file
46
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class PathHelper
|
||||
{
|
||||
private static string LocalFolder => ApplicationData.Current.LocalFolder.Path;
|
||||
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
|
||||
|
||||
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Core14.profile.xml");
|
||||
|
||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||
|
||||
public static string LrcLibLyricsCacheDirectory => Path.Combine(CacheFolder, "lrclib-lyrics");
|
||||
public static string NeteaseLyricsCacheDirectory => Path.Combine(CacheFolder, "netease-lyrics");
|
||||
public static string QQLyricsCacheDirectory => Path.Combine(CacheFolder, "qq-lyrics");
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(CacheFolder, "kugou-lyrics");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(CacheFolder, "amll-ttml-db-lyrics");
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(CacheFolder, "amll-ttml-db-index.json");
|
||||
|
||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(CacheFolder, "itunes-album-art");
|
||||
|
||||
public static void EnsureDirectories()
|
||||
{
|
||||
Directory.CreateDirectory(LocalFolder);
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
|
||||
Directory.CreateDirectory(LrcLibLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(QQLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(KugouLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class StringHelper
|
||||
{
|
||||
// 去除空格、括号、下划线、横杠、点、大小写等
|
||||
public static string Normalize(this string s) =>
|
||||
new string(s
|
||||
.Where(c => char.IsLetterOrDigit(c))
|
||||
.ToArray())
|
||||
.ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,6 @@ using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class AnimationHelper
|
||||
{
|
||||
public const int DebounceDefaultDuration = 200;
|
||||
public const int StackedNotificationsShowingDuration = 3900;
|
||||
public const int StoryboardDefaultDuration = 200;
|
||||
}
|
||||
|
||||
public class ValueTransition<T>
|
||||
where T : struct
|
||||
{
|
||||
@@ -1,148 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class WindowColorHelper
|
||||
{
|
||||
public static Color GetDominantColor(IntPtr myHwnd, WindowColorSampleMode mode)
|
||||
{
|
||||
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return Color.Transparent;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case WindowColorSampleMode.BelowWindow:
|
||||
{
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int sampleHeight = 1;
|
||||
int sampleY = myRect.Bottom + 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
case WindowColorSampleMode.WindowArea:
|
||||
{
|
||||
int width = myRect.Right - myRect.Left;
|
||||
int height = myRect.Bottom - myRect.Top;
|
||||
if (width <= 0 || height <= 0)
|
||||
return Color.Transparent;
|
||||
// 采集窗口区域的平均色
|
||||
return GetAverageColorFromScreenRegion(myRect.Left, myRect.Top, width, height);
|
||||
}
|
||||
case WindowColorSampleMode.WindowEdge:
|
||||
{
|
||||
int width = myRect.Right - myRect.Left;
|
||||
int height = myRect.Bottom - myRect.Top;
|
||||
if (width <= 0 || height <= 0)
|
||||
return Color.Transparent;
|
||||
|
||||
var edgeThickness = new Thickness(36, 0, 36, 0);
|
||||
List<Color> edgeColors = [];
|
||||
|
||||
// Top edge
|
||||
if (edgeThickness.Top > 0 && edgeThickness.Top < height)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Top,
|
||||
width,
|
||||
(int)edgeThickness.Top
|
||||
)
|
||||
);
|
||||
// Bottom edge
|
||||
if (edgeThickness.Bottom > 0 && edgeThickness.Bottom < height)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Bottom - (int)edgeThickness.Bottom,
|
||||
width,
|
||||
(int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
// Left edge
|
||||
if (edgeThickness.Left > 0 && edgeThickness.Left < width)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Left,
|
||||
myRect.Top + (int)edgeThickness.Top,
|
||||
(int)edgeThickness.Left,
|
||||
height - (int)edgeThickness.Top - (int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
// Right edge
|
||||
if (edgeThickness.Right > 0 && edgeThickness.Right < width)
|
||||
edgeColors.Add(
|
||||
GetAverageColorFromScreenRegion(
|
||||
myRect.Right - (int)edgeThickness.Right,
|
||||
myRect.Top + (int)edgeThickness.Top,
|
||||
(int)edgeThickness.Right,
|
||||
height - (int)edgeThickness.Top - (int)edgeThickness.Bottom
|
||||
)
|
||||
);
|
||||
|
||||
// 合并四边平均色
|
||||
if (edgeColors.Count == 0)
|
||||
return Color.Transparent;
|
||||
long r = 0,
|
||||
g = 0,
|
||||
b = 0;
|
||||
foreach (var c in edgeColors)
|
||||
{
|
||||
r += c.R;
|
||||
g += c.G;
|
||||
b += c.B;
|
||||
}
|
||||
return Color.FromArgb(
|
||||
255,
|
||||
(int)(r / edgeColors.Count),
|
||||
(int)(g / edgeColors.Count),
|
||||
(int)(b / edgeColors.Count)
|
||||
);
|
||||
}
|
||||
default:
|
||||
return Color.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
IntPtr hdcDest = gDest.GetHdc();
|
||||
IntPtr hdcSrc = (nint)User32.GetDC(IntPtr.Zero); // Entire screen
|
||||
|
||||
Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, Gdi32.RasterOperationMode.SRCCOPY);
|
||||
|
||||
gDest.ReleaseHdc(hdcDest);
|
||||
User32.ReleaseDC(IntPtr.Zero, hdcSrc);
|
||||
|
||||
return ComputeAverageColor(bmp);
|
||||
}
|
||||
|
||||
private static Color ComputeAverageColor(Bitmap bmp)
|
||||
{
|
||||
long r = 0, g = 0, b = 0;
|
||||
int count = 0;
|
||||
|
||||
for (int y = 0; y < bmp.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bmp.Width; x++)
|
||||
{
|
||||
Color pixel = bmp.GetPixel(x, y);
|
||||
r += pixel.R;
|
||||
g += pixel.G;
|
||||
b += pixel.B;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) return Color.Transparent;
|
||||
return Color.FromArgb((int)(r / count), (int)(g / count), (int)(b / count));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
@@ -61,6 +62,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
{
|
||||
newWindow = new LyricsWindow();
|
||||
((LyricsWindow)newWindow).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
}
|
||||
else if (typeof(T) == typeof(SettingsWindow))
|
||||
{
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class DetectLanguageResult
|
||||
{
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[JsonPropertyName("language")]
|
||||
public string Language { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class Notification : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial bool IsForeverDismissable { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? Message { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? RelatedSettingsKeyName { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial InfoBarSeverity Severity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility Visibility { get; set; }
|
||||
|
||||
public Notification(string? message = null, InfoBarSeverity severity = InfoBarSeverity.Informational, bool isForeverDismissable = false, string? relatedSettingsKeyName = null)
|
||||
{
|
||||
Message = message;
|
||||
Severity = severity;
|
||||
IsForeverDismissable = isForeverDismissable;
|
||||
Visibility = IsForeverDismissable ? Visibility.Visible : Visibility.Collapsed;
|
||||
RelatedSettingsKeyName = relatedSettingsKeyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ namespace BetterLyrics.WinUI3.Serialization
|
||||
[JsonSerializable(typeof(List<MediaSourceProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<LocalLyricsFolder>))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(List<DetectLanguageResult>))]
|
||||
[JsonSerializable(typeof(TranslateResponse))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class AlbumArtSearchService : IAlbumArtSearchService
|
||||
{
|
||||
private readonly HttpClient _iTunesHttpClinet;
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public AlbumArtSearchService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<AlbumArtSearchService>>();
|
||||
_iTunesHttpClinet = new();
|
||||
}
|
||||
|
||||
public async Task<byte[]?> SearchAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null)
|
||||
{
|
||||
byte[]? result = null;
|
||||
|
||||
foreach (var provider in _settingsService.AlbumArtSearchProvidersInfo)
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (provider.Provider)
|
||||
{
|
||||
case AlbumArtSearchProvider.Local:
|
||||
result = SearchFile(artist, album);
|
||||
break;
|
||||
case AlbumArtSearchProvider.SMTC:
|
||||
result = bytesFromSMTC;
|
||||
break;
|
||||
case AlbumArtSearchProvider.iTunes:
|
||||
result = await SearchiTunesAsync(artist, album);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[]? SearchFile(string artist, string album)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), album, artist))
|
||||
{
|
||||
Track track = new(file);
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
if (bytes != null)
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<byte[]?> SearchiTunesAsync(string artist, string album)
|
||||
{
|
||||
// Source: https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce
|
||||
try
|
||||
{
|
||||
string format = ".jpg";
|
||||
var cachedAlbumArt = FileHelper.ReadAlbumArtCache(artist, album, format, PathHelper.iTunesAlbumArtCacheDirectory);
|
||||
|
||||
if (cachedAlbumArt != null)
|
||||
{
|
||||
return cachedAlbumArt;
|
||||
}
|
||||
|
||||
// Build the iTunes API URL
|
||||
string url = $"https://itunes.apple.com/search?term=" + artist + "+" + album + "&country=" + LanguageHelper.DetectCountryCode(album + artist) + "&entity=album";
|
||||
url.Replace(" ", "-");
|
||||
// Make a request to the API
|
||||
|
||||
HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Parse the JSON response
|
||||
var data = JsonSerializer.Deserialize(responseBody, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
|
||||
if (data.TryGetProperty("results", out var results) && results.ValueKind == JsonValueKind.Array && results.GetArrayLength() > 0)
|
||||
{
|
||||
// Get the first result
|
||||
var result = results[0];
|
||||
if (result.TryGetProperty("artworkUrl100", out var artworkUrlProp))
|
||||
{
|
||||
string artworkUrl = artworkUrlProp.GetString()?.Replace("100x100bb.jpg", "1200x1200bb.jpg") ?? string.Empty;
|
||||
var fetched = await _iTunesHttpClinet.GetByteArrayAsync(artworkUrl);
|
||||
|
||||
if (fetched != null && fetched.Length > 0)
|
||||
{
|
||||
// Write to cache
|
||||
FileHelper.WriteAlbumArtCache(artist, album, fetched, format, PathHelper.iTunesAlbumArtCacheDirectory);
|
||||
return fetched;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error searching iTunes album art for {Artist} - {Album}", artist, album);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface IAlbumArtSearchService
|
||||
{
|
||||
Task<byte[]?> SearchAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface ILyricsSearchService
|
||||
{
|
||||
Task<string?> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface IMusicSearchService
|
||||
{
|
||||
Task<byte[]?> SearchAlbumArtAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null);
|
||||
|
||||
Task<string?> SearchLyricsAsync(
|
||||
string title,
|
||||
string artist,
|
||||
string album,
|
||||
double durationMs,
|
||||
CancellationToken token
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
List<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
|
||||
List<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
EasingType LyricsScrollEasingType { get; set; }
|
||||
int LyricsScrollDuration { get; set; }
|
||||
|
||||
int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface ILibreTranslateService
|
||||
public interface ITranslateService
|
||||
{
|
||||
Task<string> TranslateAsync(string text, string targetLangCode, CancellationToken? token);
|
||||
}
|
||||
@@ -18,27 +18,25 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class MusicSearchService : IMusicSearchService
|
||||
public class LyricsSearchService : ILyricsSearchService
|
||||
{
|
||||
private readonly HttpClient _amllTtmlDbHttpClient;
|
||||
private readonly HttpClient _lrcLibHttpClient;
|
||||
private readonly HttpClient _iTunesHttpClinet;
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MusicSearchService(ISettingsService settingsService)
|
||||
public LyricsSearchService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<MusicSearchService>>();
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsSearchService>>();
|
||||
|
||||
_lrcLibHttpClient = new();
|
||||
_lrcLibHttpClient.DefaultRequestHeaders.Add(
|
||||
"User-Agent",
|
||||
$"{AppInfo.AppName} {AppInfo.AppVersion} ({AppInfo.GithubUrl})"
|
||||
$"{MetadataHelper.AppName} {MetadataHelper.AppVersion} ({MetadataHelper.GithubUrl})"
|
||||
);
|
||||
_amllTtmlDbHttpClient = new();
|
||||
_iTunesHttpClinet = new();
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadAmllTtmlDbIndexAsync()
|
||||
@@ -51,7 +49,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync();
|
||||
await using var fs = new FileStream(
|
||||
AppInfo.AmllTtmlDbIndexPath,
|
||||
PathHelper.AmllTtmlDbIndexPath,
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.None
|
||||
@@ -66,126 +64,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
private static string GuessCountryCode(string album, string artist)
|
||||
{
|
||||
string s = album + artist;
|
||||
if (s.Any(c => c >= 0x4e00 && c <= 0x9fff)) // 中文
|
||||
return "cn";
|
||||
if (s.Any(c => (c >= 0x3040 && c <= 0x30ff) || (c >= 0x31f0 && c <= 0x31ff))) // 日文
|
||||
return "jp";
|
||||
if (s.Any(c => c >= 0xac00 && c <= 0xd7af)) // 韩文
|
||||
return "kr";
|
||||
if (s.Any(c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) // 英文
|
||||
return "us";
|
||||
// 其他情况
|
||||
return "us";
|
||||
}
|
||||
|
||||
public async Task<byte[]?> SearchAlbumArtAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null)
|
||||
{
|
||||
byte[]? result = null;
|
||||
|
||||
foreach (var provider in _settingsService.AlbumArtSearchProvidersInfo)
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (provider.Provider)
|
||||
{
|
||||
case AlbumArtSearchProvider.Local:
|
||||
result = SearchLocalAlbumArt(artist, album);
|
||||
break;
|
||||
case AlbumArtSearchProvider.SMTC:
|
||||
result = bytesFromSMTC;
|
||||
break;
|
||||
case AlbumArtSearchProvider.iTunes:
|
||||
result = await SearchiTunesAlbumArtAsync(artist, album);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[]? SearchLocalAlbumArt(string artist, string album)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (MusicMatch(Path.GetFileNameWithoutExtension(file), album, artist))
|
||||
{
|
||||
Track track = new(file);
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
if (bytes != null)
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<byte[]?> SearchiTunesAlbumArtAsync(string artist, string album)
|
||||
{
|
||||
// Source: https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce
|
||||
try
|
||||
{
|
||||
string format = ".jpg";
|
||||
var cachedAlbumArt = ReadAlbumArtCache(artist, album, format, AppInfo.iTunesAlbumArtCacheDirectory);
|
||||
|
||||
if (cachedAlbumArt != null)
|
||||
{
|
||||
return cachedAlbumArt;
|
||||
}
|
||||
|
||||
// Build the iTunes API URL
|
||||
string url = $"https://itunes.apple.com/search?term=" + artist + "+" + album + "&country=" + GuessCountryCode(album, artist) + "&entity=album";
|
||||
url.Replace(" ", "-");
|
||||
// Make a request to the API
|
||||
|
||||
HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Parse the JSON response
|
||||
var data = JsonSerializer.Deserialize(responseBody, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
|
||||
if (data.TryGetProperty("results", out var results) && results.ValueKind == JsonValueKind.Array && results.GetArrayLength() > 0)
|
||||
{
|
||||
// Get the first result
|
||||
var result = results[0];
|
||||
if (result.TryGetProperty("artworkUrl100", out var artworkUrlProp))
|
||||
{
|
||||
string artworkUrl = artworkUrlProp.GetString()?.Replace("100x100bb.jpg", "1200x1200bb.jpg") ?? string.Empty;
|
||||
var fetched = await _iTunesHttpClinet.GetByteArrayAsync(artworkUrl);
|
||||
|
||||
if (fetched != null && fetched.Length > 0)
|
||||
{
|
||||
// Write to cache
|
||||
WriteAlbumArtCache(artist, album, fetched, format, AppInfo.iTunesAlbumArtCacheDirectory);
|
||||
return fetched;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error searching iTunes album art for {Artist} - {Album}", artist, album);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string?> SearchLyricsAsync(string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
public async Task<string?> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
|
||||
|
||||
@@ -202,7 +81,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
// Check cache first
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
cachedLyrics = ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
{
|
||||
return cachedLyrics;
|
||||
@@ -215,11 +94,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
|
||||
{
|
||||
searchedLyrics = LocalLyricsSearchInMusicFiles(title, artist);
|
||||
searchedLyrics = SearchEmbedded(title, artist);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchedLyrics = await LocalLyricsSearchInLyricsFiles(title, artist, lyricsFormat);
|
||||
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -230,13 +109,13 @@ namespace BetterLyrics.WinUI3.Services
|
||||
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
|
||||
break;
|
||||
case LyricsSearchProvider.QQ:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Netease);
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
|
||||
@@ -252,7 +131,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
}
|
||||
|
||||
return searchedLyrics;
|
||||
@@ -262,36 +141,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool MusicMatch(string fileName, string title, string artist)
|
||||
{
|
||||
var normFileName = Normalize(fileName);
|
||||
var normTitle = Normalize(title);
|
||||
var normArtist = Normalize(artist);
|
||||
|
||||
// 常见两种顺序
|
||||
return normFileName == normTitle + normArtist
|
||||
|| normFileName == normArtist + normTitle;
|
||||
}
|
||||
|
||||
// 预处理:去除空格、括号、下划线、横杠、点、大小写等
|
||||
static string Normalize(string s) =>
|
||||
new string(s
|
||||
.Where(c => char.IsLetterOrDigit(c))
|
||||
.ToArray())
|
||||
.ToLowerInvariant();
|
||||
|
||||
private static string SanitizeFileName(string fileName, char replacement = '_')
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(fileName.Length);
|
||||
foreach (var c in fileName)
|
||||
{
|
||||
sb.Append(Array.IndexOf(invalidChars, c) >= 0 ? replacement : c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private async Task<string?> LocalLyricsSearchInLyricsFiles(string title, string artist, LyricsFormat format)
|
||||
private async Task<string?> SearchFile(string title, string artist, LyricsFormat format)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
{
|
||||
@@ -299,7 +149,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(folder.Path, $"*{format.ToFileExtension()}", SearchOption.AllDirectories))
|
||||
{
|
||||
if (MusicMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
{
|
||||
string? raw = await File.ReadAllTextAsync(file, FileHelper.GetEncoding(file));
|
||||
if (raw != null)
|
||||
@@ -313,7 +163,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? LocalLyricsSearchInMusicFiles(string title, string artist)
|
||||
private string? SearchEmbedded(string title, string artist)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
{
|
||||
@@ -321,7 +171,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (MusicMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -340,42 +190,18 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ReadLyricsCache(string title, string artist, LyricsFormat format, string cacheFolderPath)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeTitle = SanitizeFileName(title);
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, $"{safeArtist} - {safeTitle}{format.ToFileExtension()}");
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllText(cacheFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[]? ReadAlbumArtCache(string album, string artist, string format, string cacheFolderPath)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeAlbum = SanitizeFileName(album);
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, $"{safeArtist} - {safeAlbum}{format}");
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllBytes(cacheFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string?> SearchAmllTtmlDbAsync(string title, string artist)
|
||||
{
|
||||
// 检索本地 JSONL 索引文件,查找 rawLyricFile
|
||||
if (!File.Exists(AppInfo.AmllTtmlDbIndexPath))
|
||||
if (!File.Exists(PathHelper.AmllTtmlDbIndexPath))
|
||||
{
|
||||
var downloadOk = await DownloadAmllTtmlDbIndexAsync();
|
||||
if (!downloadOk || !File.Exists(AppInfo.AmllTtmlDbIndexPath))
|
||||
if (!downloadOk || !File.Exists(PathHelper.AmllTtmlDbIndexPath))
|
||||
return null;
|
||||
}
|
||||
|
||||
string? rawLyricFile = null;
|
||||
await foreach (var line in File.ReadLinesAsync(AppInfo.AmllTtmlDbIndexPath))
|
||||
await foreach (var line in File.ReadLinesAsync(PathHelper.AmllTtmlDbIndexPath))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
@@ -401,7 +227,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
if (musicName == null || artists == null)
|
||||
continue;
|
||||
|
||||
if (MusicMatch($"{artists} - {musicName}", title, artist))
|
||||
if (FileHelper.IsSwitchableNormalizedMatch($"{artists} - {musicName}", title, artist))
|
||||
{
|
||||
if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp))
|
||||
{
|
||||
@@ -465,13 +291,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string?> SearchUsingLyricifyAsync(
|
||||
string title,
|
||||
string artist,
|
||||
string album,
|
||||
int durationMs,
|
||||
Searchers searchers
|
||||
)
|
||||
private static async Task<string?> SearchQQNeteaseKugouAsync(string title, string artist, string album, int durationMs, Searchers searchers)
|
||||
{
|
||||
var result = await SearchersHelper.GetSearcher(searchers).SearchForResult(
|
||||
new Lyricify.Lyrics.Models.TrackMultiArtistMetadata()
|
||||
@@ -508,39 +328,5 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void WriteLyricsCache(
|
||||
string title,
|
||||
string artist,
|
||||
string lyrics,
|
||||
LyricsFormat format,
|
||||
string cacheFolderPath
|
||||
)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeTitle = SanitizeFileName(title);
|
||||
var cacheFilePath = Path.Combine(
|
||||
cacheFolderPath,
|
||||
$"{safeArtist} - {safeTitle}{format.ToFileExtension()}"
|
||||
);
|
||||
File.WriteAllText(cacheFilePath, lyrics);
|
||||
}
|
||||
|
||||
private void WriteAlbumArtCache(
|
||||
string album,
|
||||
string artist,
|
||||
byte[] img,
|
||||
string format,
|
||||
string cacheFolderPath
|
||||
)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeAlbum = SanitizeFileName(album);
|
||||
var cacheFilePath = Path.Combine(
|
||||
cacheFolderPath,
|
||||
$"{safeArtist} - {safeAlbum}{format}"
|
||||
);
|
||||
File.WriteAllBytes(cacheFilePath, img);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>>>
|
||||
{
|
||||
private readonly IMusicSearchService _musicSearchService;
|
||||
private readonly IAlbumArtSearchService _albumArtSearchService;
|
||||
private readonly ILogger<PlaybackService> _logger;
|
||||
private readonly MediaManager _mediaManager = new();
|
||||
private readonly LatestOnlyTaskRunner _AlbumArtRefreshRunner = new();
|
||||
@@ -44,9 +44,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService) : base(settingsService)
|
||||
public PlaybackService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService)
|
||||
{
|
||||
_musicSearchService = musicSearchService;
|
||||
_albumArtSearchService = albumArtSearchService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<PlaybackService>>();
|
||||
|
||||
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo;
|
||||
@@ -222,7 +222,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return;
|
||||
}
|
||||
|
||||
byte[]? bytes = await _musicSearchService.SearchAlbumArtAsync(
|
||||
byte[]? bytes = await _albumArtSearchService.SearchAsync(
|
||||
_cachedSongInfo.Title,
|
||||
_cachedSongInfo.Artist,
|
||||
_cachedSongInfo?.Album ?? string.Empty,
|
||||
|
||||
@@ -74,11 +74,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string SelectedTargetLanguageIndexKey = "SelectedTargetLanguageIndex";
|
||||
|
||||
private const string LyricsBackgroundThemeKey = "LyricsBackgroundTheme";
|
||||
|
||||
private const string IgnoreFullscreenWindowKey = "IgnoreFullscreenWindow";
|
||||
|
||||
private const string PreferredDisplayTypeKey = "PreferredDisplayTypeKey";
|
||||
|
||||
private const string LyricsScrollEasingTypeKey = "LyricsScrollEasingType";
|
||||
private const string LyricsScrollDurationKey = "LyricsScrollDuration";
|
||||
|
||||
private readonly ApplicationDataContainer _localSettings;
|
||||
|
||||
public SettingsService()
|
||||
@@ -184,10 +185,23 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(SelectedTargetLanguageIndexKey, 6);
|
||||
|
||||
SetDefault(LyricsFontStrokeWidthKey, 3);
|
||||
|
||||
SetDefault(IgnoreFullscreenWindowKey, false);
|
||||
|
||||
SetDefault(PreferredDisplayTypeKey, (int)LyricsDisplayType.SplitView);
|
||||
|
||||
SetDefault(LyricsScrollEasingTypeKey, (int)EasingType.EaseInOutQuad);
|
||||
SetDefault(LyricsScrollDurationKey, 500); // 500ms
|
||||
}
|
||||
|
||||
public EasingType LyricsScrollEasingType
|
||||
{
|
||||
get => (EasingType)GetValue<int>(LyricsScrollEasingTypeKey);
|
||||
set => SetValue(LyricsScrollEasingTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public int LyricsScrollDuration
|
||||
{
|
||||
get => GetValue<int>(LyricsScrollDurationKey);
|
||||
set => SetValue(LyricsScrollDurationKey, value);
|
||||
}
|
||||
|
||||
public LyricsDisplayType PreferredDisplayType
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -12,13 +13,13 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class LibreTranslateService : ILibreTranslateService
|
||||
public class TranslateService : ITranslateService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public LibreTranslateService(ISettingsService settingsService)
|
||||
public TranslateService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_httpClient = new HttpClient();
|
||||
@@ -31,7 +32,19 @@ namespace BetterLyrics.WinUI3.Services
|
||||
throw new ArgumentException("Text and target language must be provided.");
|
||||
}
|
||||
|
||||
string? originalLangCode = LanguageDetectionHelper.DetectLanguageCode(text);
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(text);
|
||||
if (string.IsNullOrWhiteSpace(originalLangCode) || originalLangCode == targetLangCode)
|
||||
{
|
||||
return text; // No translation needed
|
||||
}
|
||||
else if (originalLangCode == "zh-Hant" && targetLangCode == "zh-Hans")
|
||||
{
|
||||
return ChineseConverter.ConvertToSimplifiedChinese(text);
|
||||
}
|
||||
else if (originalLangCode == "zh-Hans" && targetLangCode == "zh-Hant")
|
||||
{
|
||||
return ChineseConverter.ConvertToTraditionalChinese(text);
|
||||
}
|
||||
|
||||
var url = $"{_settingsService.LibreTranslateServer}/translate";
|
||||
var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(
|
||||
@@ -526,7 +526,7 @@
|
||||
<value>Exit</value>
|
||||
</data>
|
||||
<data name="SystemTrayUnlock.Text" xml:space="preserve">
|
||||
<value>Unlock the window (Restart needed)</value>
|
||||
<value>Unlock the window</value>
|
||||
</data>
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>Lock</value>
|
||||
@@ -675,4 +675,46 @@
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>Media library</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation type</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation duration</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
|
||||
<value>Linear</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
|
||||
<value>Smooth step</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
|
||||
<value>Ease-in-out sine</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
|
||||
<value>Ease-in-out quad</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
|
||||
<value>Ease-in-out elastic</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
|
||||
<value>Ease-in-out back</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
|
||||
<value>Ease-in-out bounce</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
|
||||
<value>Ease-in-out circ</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
|
||||
<value>Ease-in-out expo</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
|
||||
<value>Ease-in-out quint</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
|
||||
<value>Ease-in-out quart</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
|
||||
<value>Ease-in-out cubic</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -526,7 +526,7 @@
|
||||
<value>プログラムを終了します</value>
|
||||
</data>
|
||||
<data name="SystemTrayUnlock.Text" xml:space="preserve">
|
||||
<value>ウィンドウのロックを解除する(再起動が必要)</value>
|
||||
<value>ウィンドウのロックを解除します</value>
|
||||
</data>
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>ロック</value>
|
||||
@@ -675,4 +675,46 @@
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>メディアライブラリ</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーションタイプ</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーションの期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
|
||||
<value>リニア</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
|
||||
<value>スムーズなステップ</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
|
||||
<value>サインがゆっくりと出入りします</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
|
||||
<value>セカンダリスローインとアウト</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
|
||||
<value>弾力性は内外に遅くなります</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
|
||||
<value>リバウンドはスローアウトで遅くなります</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
|
||||
<value>ゆっくりと出入りします</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
|
||||
<value>丸い、ゆっくりと出入り</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
|
||||
<value>インデックスは内外に遅くなります</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
|
||||
<value>5つの遅いインとアウト</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
|
||||
<value>4つの遅いインとアウト</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
|
||||
<value>3つの遅いインとアウト</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -526,7 +526,7 @@
|
||||
<value>프로그램을 종료하십시오</value>
|
||||
</data>
|
||||
<data name="SystemTrayUnlock.Text" xml:space="preserve">
|
||||
<value>창 잠금 해제 (다시 시작)</value>
|
||||
<value>창을 잠금 해제하십시오</value>
|
||||
</data>
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>잠금</value>
|
||||
@@ -675,4 +675,46 @@
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>미디어 라이브러리</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>가사 스크롤링 애니메이션 유형</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>가사 스크롤링 애니메이션 지속 시간</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
|
||||
<value>선의</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
|
||||
<value>부드러운 단계</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
|
||||
<value>천천히 입력하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
|
||||
<value>이차 느린 속도가 느려집니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
|
||||
<value>탄력성이 속도가 느려집니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
|
||||
<value>리바운드는 느리게 느려집니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
|
||||
<value>천천히 안팎으로 튀어 나옵니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
|
||||
<value>둥글고 느리게 안팎으로</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
|
||||
<value>인덱스 속도가 느려집니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
|
||||
<value>5 번 느리게 안팎으로</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
|
||||
<value>4 개의 느린 안팎</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
|
||||
<value>세 번 느리게 안팎으로</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -526,7 +526,7 @@
|
||||
<value>退出程序</value>
|
||||
</data>
|
||||
<data name="SystemTrayUnlock.Text" xml:space="preserve">
|
||||
<value>解锁窗口(需要重新启动)</value>
|
||||
<value>解锁窗口</value>
|
||||
</data>
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>锁定</value>
|
||||
@@ -675,4 +675,46 @@
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒体库</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
|
||||
<value>线性</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
|
||||
<value>平滑步进</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
|
||||
<value>正弦缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
|
||||
<value>二次缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
|
||||
<value>弹性缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
|
||||
<value>回弹缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
|
||||
<value>弹跳缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
|
||||
<value>圆形缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
|
||||
<value>指数缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
|
||||
<value>五次缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
|
||||
<value>四次缓入缓出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
|
||||
<value>三次缓入缓出</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -526,7 +526,7 @@
|
||||
<value>退出程序</value>
|
||||
</data>
|
||||
<data name="SystemTrayUnlock.Text" xml:space="preserve">
|
||||
<value>解鎖窗口(需要重新啟動)</value>
|
||||
<value>解鎖窗口</value>
|
||||
</data>
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>鎖定</value>
|
||||
@@ -675,4 +675,46 @@
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒體庫</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫類型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
|
||||
<value>線性</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
|
||||
<value>平滑步進</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
|
||||
<value>正弦緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
|
||||
<value>二次緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
|
||||
<value>彈性緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
|
||||
<value>回彈緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
|
||||
<value>彈跳緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
|
||||
<value>圓形緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
|
||||
<value>指數緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
|
||||
<value>五次緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
|
||||
<value>四次緩入緩出</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
|
||||
<value>三次緩入緩出</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,62 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
public LyricsRendererViewModel(ISettingsService settingsService, IPlaybackService playbackService, ILyricsSearchService musicSearchService, ILibWatcherService libWatcherService, ITranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_lyrcsSearchService = musicSearchService;
|
||||
_playbackService = playbackService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_translateService = libreTranslateService;
|
||||
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
_albumArtCornerRadius = _settingsService.CoverImageRadius;
|
||||
_isDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
_albumArtBgOpacity = _settingsService.CoverOverlayOpacity;
|
||||
_albumArtBgBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
_lyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
_lyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
|
||||
_lyricsTextFormat.FontWeight = _settingsService.LyricsFontWeight.ToFontWeight();
|
||||
|
||||
_lyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
_lyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
_lyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
_lyricsFontSize = _settingsService.LyricsFontSize;
|
||||
_lyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
_isLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
_lyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
|
||||
_customBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
_customFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
|
||||
_lyricsBgTheme = _settingsService.LyricsBackgroundTheme;
|
||||
|
||||
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_canvasYScrollTransition.SetDuration(_settingsService.LyricsScrollDuration / 1000f);
|
||||
_canvasYScrollTransition.SetEasingType(_settingsService.LyricsScrollEasingType);
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged +=
|
||||
LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged;
|
||||
_playbackService.PositionChanged += PlaybackService_PositionChanged;
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,22 +44,24 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
DrawAlbumArtBackground(control, combinedDs);
|
||||
|
||||
if (_isDockMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, combinedDs);
|
||||
DrawImmersiveBackground(control, combinedDs, 0f);
|
||||
}
|
||||
combinedDs.DrawImage(blurredLyrics);
|
||||
|
||||
if (_isDesktopMode)
|
||||
else if (_isDesktopMode)
|
||||
{
|
||||
ds.DrawImage(blurredLyrics);
|
||||
DrawImmersiveBackground(control, combinedDs, 12f);
|
||||
}
|
||||
else
|
||||
{
|
||||
ds.DrawImage(combined);
|
||||
DrawAlbumArtBackground(control, combinedDs);
|
||||
}
|
||||
|
||||
combinedDs.DrawImage(blurredLyrics);
|
||||
|
||||
ds.DrawImage(combined);
|
||||
|
||||
DrawAlbumArt(control, ds);
|
||||
DrawTitleAndArtist(control, ds);
|
||||
|
||||
@@ -78,29 +80,29 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
out float charProgress
|
||||
);
|
||||
|
||||
//ds.DrawText(
|
||||
// $"[DEBUG]\n" +
|
||||
// $"Cur playing {_playingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" +
|
||||
// $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
|
||||
// $"Cur time {_totalTime + _positionOffset}\n" +
|
||||
// $"Lang size {_multiLangLyrics.Count}\n" +
|
||||
// $"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
|
||||
// new Vector2(10, 10),
|
||||
// ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White
|
||||
//);
|
||||
ds.DrawText(
|
||||
$"[DEBUG]\n" +
|
||||
$"Cur playing {_playingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" +
|
||||
$"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
|
||||
$"Cur time {_totalTime + _positionOffset}\n" +
|
||||
$"Lang size {_multiLangLyrics.Count}\n" +
|
||||
$"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
|
||||
new Vector2(10, 10),
|
||||
ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White
|
||||
);
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
LyricsLine? line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
|
||||
if (line != null)
|
||||
{
|
||||
ds.DrawText(
|
||||
$"[{i}] {line.OriginalText} {line.HighlightOpacityTransition.Value}",
|
||||
new Vector2(10, 30 + (i - _startVisibleLineIndex) * 20),
|
||||
ThemeTypeSent == ElementTheme.Light ? Colors.Black : Colors.White
|
||||
);
|
||||
}
|
||||
}
|
||||
//for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
//{
|
||||
// LyricsLine? line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
|
||||
// if (line != null)
|
||||
// {
|
||||
// ds.DrawText(
|
||||
// $"[{i}] {line.OriginalText} {line.HighlightOpacityTransition.Value}",
|
||||
// new Vector2(10, 30 + (i - _startVisibleLineIndex) * 20),
|
||||
// ThemeTypeSent == ElementTheme.Light ? Colors.Black : Colors.White
|
||||
// );
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +112,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float imageWidth = (float)canvasBitmap.Size.Width;
|
||||
float imageHeight = (float)canvasBitmap.Size.Height;
|
||||
|
||||
float scaleFactor = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)) / MathF.Min(imageWidth, imageHeight);
|
||||
float targetSize = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)) * 1.4f;
|
||||
float scaleFactor = targetSize / MathF.Min(imageWidth, imageHeight);
|
||||
|
||||
float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2;
|
||||
float y = _canvasHeight / 2 - imageHeight * scaleFactor / 2;
|
||||
@@ -193,11 +196,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
using var coverOverlayEffect = new OpacityEffect
|
||||
{
|
||||
Opacity = CoverOverlayOpacity / 100f,
|
||||
Opacity = _albumArtBgOpacity / 100f,
|
||||
Source = new GaussianBlurEffect
|
||||
{
|
||||
BlurAmount = CoverOverlayBlurAmount,
|
||||
BlurAmount = _albumArtBgBlurAmount,
|
||||
Source = overlappedCovers,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
},
|
||||
};
|
||||
ds.DrawImage(coverOverlayEffect);
|
||||
@@ -307,7 +312,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float centerX = position.X;
|
||||
float centerY = position.Y + layoutHeight / 2;
|
||||
|
||||
switch (LyricsAlignmentType)
|
||||
switch (_lyricsAlignmentType)
|
||||
{
|
||||
case TextAlignmentType.Left:
|
||||
textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Left;
|
||||
@@ -473,13 +478,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
Source = new BlendEffect
|
||||
{
|
||||
Background = IsLyricsGlowEffectEnabled
|
||||
Background = _isLyricsGlowEffectEnabled
|
||||
? new GaussianBlurEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = fgLyrics,
|
||||
AlphaMask = LyricsGlowEffectScope switch
|
||||
AlphaMask = _lyricsGlowEffectScope switch
|
||||
{
|
||||
LineRenderingType.UntilCurrentChar => mask,
|
||||
LineRenderingType.CurrentCharOnly => highlightMask,
|
||||
@@ -506,41 +511,21 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawImmersiveBackground(
|
||||
ICanvasAnimatedControl control,
|
||||
CanvasDrawingSession ds,
|
||||
bool withGradient = true
|
||||
)
|
||||
private void DrawImmersiveBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, float radius)
|
||||
{
|
||||
ds.FillRectangle(
|
||||
CanvasCommandList list = new(control.Device);
|
||||
using var listDs = list.CreateDrawingSession();
|
||||
listDs.FillRoundedRectangle(
|
||||
new Rect(0, 0, _canvasWidth, _canvasHeight),
|
||||
new CanvasLinearGradientBrush(
|
||||
control,
|
||||
[
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 0f,
|
||||
Color = withGradient
|
||||
? Color.FromArgb(
|
||||
211,
|
||||
_immersiveBgTransition.Value.R,
|
||||
_immersiveBgTransition.Value.G,
|
||||
_immersiveBgTransition.Value.B
|
||||
)
|
||||
: _immersiveBgTransition.Value,
|
||||
},
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 1,
|
||||
Color = _immersiveBgTransition.Value,
|
||||
},
|
||||
]
|
||||
)
|
||||
{
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(0, _canvasHeight),
|
||||
}
|
||||
radius,
|
||||
radius,
|
||||
_immersiveBgTransition.Value
|
||||
);
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = list,
|
||||
Opacity = _immersiveBgOpacityTransition.Value
|
||||
});
|
||||
}
|
||||
|
||||
private CanvasLinearGradientBrush GetHorizontalFillBrush(
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||
IRecipient<PropertyChangedMessage<LineRenderingType>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<EasingType>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>>>
|
||||
{
|
||||
@@ -64,7 +65,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled))
|
||||
{
|
||||
IsDynamicCoverOverlayEnabled = message.NewValue;
|
||||
_isDynamicCoverOverlayEnabled = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled))
|
||||
{
|
||||
@@ -72,7 +73,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsGlowEffectEnabled))
|
||||
{
|
||||
IsLyricsGlowEffectEnabled = message.NewValue;
|
||||
_isLyricsGlowEffectEnabled = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsFanLyricsEnabled))
|
||||
{
|
||||
@@ -92,6 +93,15 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isDesktopMode = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsLyricsWindowLocked))
|
||||
{
|
||||
_isLyricsWindowLocked = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsMouseWithinWindow))
|
||||
{
|
||||
_isMouseWithinWindow = message.NewValue;
|
||||
_immersiveBgOpacityTransition.StartTransition(_isDesktopMode ? (_isMouseWithinWindow ? 1f : 0f) : 1f);
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
@@ -141,7 +151,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsLineSpacingFactor))
|
||||
{
|
||||
LyricsLineSpacingFactor = message.NewValue;
|
||||
_lyricsLineSpacingFactor = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,25 +167,26 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayOpacity))
|
||||
{
|
||||
CoverOverlayOpacity = message.NewValue;
|
||||
_albumArtBgOpacity = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount))
|
||||
{
|
||||
CoverOverlayBlurAmount = message.NewValue;
|
||||
_albumArtBgBlurAmount = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsVerticalEdgeOpacity))
|
||||
{
|
||||
LyricsVerticalEdgeOpacity = message.NewValue;
|
||||
_lyricsVerticalEdgeOpacity = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBlurAmount))
|
||||
{
|
||||
LyricsBlurAmount = message.NewValue;
|
||||
_lyricsBlurAmount = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
LyricsFontSize = message.NewValue;
|
||||
_lyricsFontSize = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.SelectedTargetLanguageIndex))
|
||||
{
|
||||
@@ -186,6 +198,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_lyricsFontStrokeWidth = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsScrollDuration))
|
||||
{
|
||||
_canvasYScrollTransition.SetDuration(message.NewValue / 1000f);
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
@@ -202,7 +218,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsGlowEffectScope))
|
||||
{
|
||||
LyricsGlowEffectScope = message.NewValue;
|
||||
_lyricsGlowEffectScope = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +229,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsAlignmentType))
|
||||
{
|
||||
LyricsAlignmentType = message.NewValue;
|
||||
_lyricsAlignmentType = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.SongInfoAlignmentType))
|
||||
{
|
||||
@@ -256,7 +272,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontWeight))
|
||||
{
|
||||
LyricsFontWeight = message.NewValue;
|
||||
_lyricsTextFormat.FontWeight = message.NewValue.ToFontWeight();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,20 +290,15 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnLyricsFontSizeChanged(int value)
|
||||
public void Receive(PropertyChangedMessage<EasingType> message)
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_lyricsTextFormat.FontWeight = value.ToFontWeight();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsScrollEasingType))
|
||||
{
|
||||
_canvasYScrollTransition.SetEasingType(message.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
private readonly ValueTransition<float> _canvasYScrollTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.8f,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
durationSeconds: 0.5f,
|
||||
easingType: EasingType.EaseInOutCubic
|
||||
);
|
||||
|
||||
private readonly ValueTransition<Color> _immersiveBgTransition = new(
|
||||
@@ -24,6 +24,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _immersiveBgOpacityTransition = new(
|
||||
initialValue: 1f,
|
||||
durationSeconds: 0.2f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsXTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
|
||||
@@ -43,12 +43,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_displayType = _displayTypeReceived;
|
||||
_playingLineIndex = playingLineIndex;
|
||||
|
||||
_immersiveBgOpacityTransition.Update(_elapsedTime);
|
||||
_immersiveBgTransition.Update(_elapsedTime);
|
||||
_albumArtBgTransition.Update(_elapsedTime);
|
||||
_lyricsBgBrightnessTransition.Update(_elapsedTime);
|
||||
_songInfoOpacityTransition.Update(_elapsedTime);
|
||||
|
||||
if (IsDynamicCoverOverlayEnabled)
|
||||
if (_isDynamicCoverOverlayEnabled)
|
||||
{
|
||||
_rotateAngle += _coverRotateSpeed;
|
||||
_rotateAngle %= MathF.PI * 2;
|
||||
@@ -129,7 +130,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (control == null)
|
||||
return;
|
||||
|
||||
_lyricsTextFormat.FontSize = LyricsFontSize;
|
||||
_lyricsTextFormat.FontSize = _lyricsFontSize;
|
||||
|
||||
float y = 0;
|
||||
|
||||
@@ -163,7 +164,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
y +=
|
||||
(float)line.CanvasTextLayout.LayoutBounds.Height
|
||||
/ line.CanvasTextLayout.LineCount
|
||||
* (line.CanvasTextLayout.LineCount + LyricsLineSpacingFactor);
|
||||
* (line.CanvasTextLayout.LineCount + _lyricsLineSpacingFactor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,9 +385,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
: 0
|
||||
);
|
||||
|
||||
line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor);
|
||||
line.BlurAmountTransition.StartTransition(_lyricsBlurAmount * distanceFactor);
|
||||
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
|
||||
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f));
|
||||
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - _lyricsVerticalEdgeOpacity / 100f));
|
||||
line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,14 +64,29 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private readonly float _coverRotateSpeed = 0.003f;
|
||||
private float _rotateAngle = 0f;
|
||||
|
||||
private TextAlignmentType _lyricsAlignmentType;
|
||||
|
||||
private readonly float _lyricsGlowEffectAmount = 8f;
|
||||
private int _lyricsBlurAmount;
|
||||
private int _lyricsVerticalEdgeOpacity;
|
||||
|
||||
private ElementTheme _lyricsBgTheme;
|
||||
private LineRenderingType _lyricsGlowEffectScope;
|
||||
|
||||
private int _lyricsFontStrokeWidth;
|
||||
private int _lyricsFontSize;
|
||||
private float _lyricsLineSpacingFactor;
|
||||
|
||||
private LyricsFontColorType _lyricsBgFontColorType;
|
||||
private LyricsFontColorType _lyricsFgFontColorType;
|
||||
private LyricsFontColorType _lyricsStrokeFontColorType;
|
||||
|
||||
private float _maxLyricsWidth = 0f;
|
||||
|
||||
private readonly IMusicSearchService _musicSearchService;
|
||||
private readonly ILyricsSearchService _lyrcsSearchService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly IPlaybackService _playbackService;
|
||||
private readonly ILibreTranslateService _libreTranslateService;
|
||||
private readonly ITranslateService _translateService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly float _leftMargin = 36f;
|
||||
@@ -97,14 +112,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private Color? _customFgFontColor;
|
||||
private Color? _customStrokeFontColor;
|
||||
|
||||
private LyricsFontColorType _lyricsBgFontColorType;
|
||||
private LyricsFontColorType _lyricsFgFontColorType;
|
||||
private LyricsFontColorType _lyricsStrokeFontColorType;
|
||||
|
||||
private ElementTheme _lyricsBgTheme;
|
||||
|
||||
private int _lyricsFontStrokeWidth;
|
||||
|
||||
private int _playingLineIndex = -1;
|
||||
|
||||
private int _startVisibleLineIndex = -1;
|
||||
@@ -117,6 +124,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private bool _isPlaying = true;
|
||||
|
||||
private bool _isLyricsWindowLocked = false;
|
||||
private bool _isMouseWithinWindow = false;
|
||||
|
||||
private bool _isDynamicCoverOverlayEnabled;
|
||||
private bool _isLyricsGlowEffectEnabled;
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private int _langIndex = 0;
|
||||
@@ -153,106 +166,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private LatestOnlyTaskRunner _refreshLyricsRunner = new();
|
||||
private LatestOnlyTaskRunner _showTranslationsRunner = new();
|
||||
|
||||
public LyricsRendererViewModel(ISettingsService settingsService, IPlaybackService playbackService, IMusicSearchService musicSearchService, ILibWatcherService libWatcherService, ILibreTranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_musicSearchService = musicSearchService;
|
||||
_playbackService = playbackService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_libreTranslateService = libreTranslateService;
|
||||
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
_albumArtCornerRadius = _settingsService.CoverImageRadius;
|
||||
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
_lyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
_lyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
|
||||
_customBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
_customFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
|
||||
_lyricsBgTheme = _settingsService.LyricsBackgroundTheme;
|
||||
|
||||
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
|
||||
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
|
||||
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged +=
|
||||
LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.AlbumArtChangedChanged += _playbackService_AlbumArtChangedChanged;
|
||||
_playbackService.PositionChanged += PlaybackService_PositionChanged;
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
|
||||
private void _playbackService_AlbumArtChangedChanged(object? sender, AlbumArtChangedEventArgs e)
|
||||
{
|
||||
if (e.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_lastAlbumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtSwBitmap = e.AlbumArtSwBitmap;
|
||||
_albumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtAccentColor = e.AlbumArtAccentColor;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
public int CoverOverlayBlurAmount { get; set; }
|
||||
|
||||
public int CoverOverlayOpacity { get; set; }
|
||||
|
||||
private LyricsDisplayType _displayTypeReceived = LyricsDisplayType.PlaceholderOnly;
|
||||
private LyricsDisplayType _displayType = LyricsDisplayType.PlaceholderOnly;
|
||||
|
||||
public bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
|
||||
public bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
public TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
public int LyricsBlurAmount { get; set; }
|
||||
private int _albumArtBgBlurAmount;
|
||||
private int _albumArtBgOpacity;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
public LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
public int LyricsVerticalEdgeOpacity { get; set; }
|
||||
public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; }
|
||||
@@ -390,6 +311,26 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
_totalTime = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaybackService_AlbumArtChangedChanged(object? sender, AlbumArtChangedEventArgs e)
|
||||
{
|
||||
if (e.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_lastAlbumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtSwBitmap = e.AlbumArtSwBitmap;
|
||||
_albumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtAccentColor = e.AlbumArtAccentColor;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,9 +355,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private async Task ShowTranslationsAsync(CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Showing translation for lyrics...");
|
||||
string targetLangCode = AppInfo.TranslationLanguagesInfo[_settingsService.SelectedTargetLanguageIndex].Code;
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.SelectedTargetLanguageIndex].Code;
|
||||
var originalText = string.Join("\n", _multiLangLyrics.FirstOrDefault()?.Select(x => x.OriginalText) ?? []);
|
||||
string? originalLangCode = LanguageDetectionHelper.DetectLanguageCode(originalText);
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
|
||||
if (originalLangCode == targetLangCode)
|
||||
{
|
||||
@@ -432,7 +373,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
var translationList = langLyrics.Select(x => x.OriginalText).ToList();
|
||||
var translation = string.Join("\n", translationList);
|
||||
if (LanguageDetectionHelper.DetectLanguageCode(translation) == targetLangCode)
|
||||
if (LanguageHelper.DetectLanguageCode(translation) == targetLangCode)
|
||||
{
|
||||
_translationList = translationList;
|
||||
break;
|
||||
@@ -454,7 +395,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
var translated = await _libreTranslateService.TranslateAsync(originalText, targetLangCode, token);
|
||||
var translated = await _translateService.TranslateAsync(originalText, targetLangCode, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_translationList = translated.Split('\n').ToList();
|
||||
@@ -492,7 +433,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
lyricsRaw = await _musicSearchService.SearchLyricsAsync(
|
||||
lyricsRaw = await _lyrcsSearchService.SearchAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace BetterLyrics.WinUI3
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>
|
||||
{
|
||||
private ForegroundWindowWatcherHelper? _watcherHelper = null;
|
||||
private ForegroundWindowWatcher? _watcherHelper = null;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
@@ -49,9 +49,6 @@ namespace BetterLyrics.WinUI3
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsWindowLocked { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Notification Notification { get; set; } = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowInfoBar { get; set; } = false;
|
||||
|
||||
@@ -64,6 +61,10 @@ namespace BetterLyrics.WinUI3
|
||||
[ObservableProperty]
|
||||
public partial double TitleBarHeight { get; set; } = 36;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsMouseWithinWindow { get; set; } = false;
|
||||
|
||||
private bool _ignoreFullscreenWindow = false;
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
@@ -115,13 +116,13 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
}
|
||||
|
||||
public void StartWatchWindowColorChange(WindowColorSampleMode mode)
|
||||
public void StartWatchWindowColorChange(WindowPixelSampleMode mode)
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
_watcherHelper = new ForegroundWindowWatcherHelper(
|
||||
_watcherHelper = new ForegroundWindowWatcher(
|
||||
hwnd,
|
||||
onWindowChanged =>
|
||||
{
|
||||
@@ -136,9 +137,9 @@ namespace BetterLyrics.WinUI3
|
||||
UpdateAccentColor(hwnd, mode);
|
||||
}
|
||||
|
||||
public void UpdateAccentColor(nint hwnd, WindowColorSampleMode mode)
|
||||
public void UpdateAccentColor(nint hwnd, WindowPixelSampleMode mode)
|
||||
{
|
||||
ActivatedWindowAccentColor = WindowColorHelper.GetDominantColor(hwnd, mode).ToColor();
|
||||
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, mode).ToColor();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -147,9 +148,8 @@ namespace BetterLyrics.WinUI3
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DesktopModeHelper.Lock(window);
|
||||
DesktopModeHelper.SetClickThrough(window, true);
|
||||
IsLyricsWindowLocked = true;
|
||||
StartWatchWindowColorChange(WindowColorSampleMode.WindowEdge);
|
||||
}
|
||||
|
||||
private void StopWatchWindowColorChange()
|
||||
@@ -170,6 +170,7 @@ namespace BetterLyrics.WinUI3
|
||||
if (IsDesktopMode)
|
||||
{
|
||||
DesktopModeHelper.Enable(window);
|
||||
StartWatchWindowColorChange(WindowPixelSampleMode.WindowEdge);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -189,7 +190,7 @@ namespace BetterLyrics.WinUI3
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockModeHelper.Enable(window, _settingsService.LyricsFontSize * 4);
|
||||
StartWatchWindowColorChange(WindowColorSampleMode.BelowWindow);
|
||||
StartWatchWindowColorChange(WindowPixelSampleMode.BelowWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ using Windows.System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Popups;
|
||||
using WinRT.Interop;
|
||||
using AppInfo = BetterLyrics.WinUI3.Helper.AppInfo;
|
||||
using MetadataHelper = BetterLyrics.WinUI3.Helper.MetadataHelper;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -33,11 +33,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly IPlaybackService _playbackService;
|
||||
private readonly ILibreTranslateService _libreTranslateService;
|
||||
private readonly ITranslateService _libreTranslateService;
|
||||
|
||||
private readonly string _autoStartupTaskId = "AutoStartup";
|
||||
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ILibreTranslateService libreTranslateService) : base(settingsService)
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ITranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
@@ -80,18 +80,18 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
LyricsCustomStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
|
||||
|
||||
LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
|
||||
LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme;
|
||||
|
||||
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
|
||||
|
||||
IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
|
||||
LyricsScrollEasingType = _settingsService.LyricsScrollEasingType;
|
||||
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
|
||||
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
BuildDate = (await Helper.AppInfo.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
BuildDate = (await Helper.MetadataHelper.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial object NavViewSelectedItemTag { get; set; }
|
||||
|
||||
public string Version { get; set; } = Helper.AppInfo.AppVersion;
|
||||
public string Version { get; set; } = Helper.MetadataHelper.AppVersion;
|
||||
|
||||
public string BuildDate { get; set; } = string.Empty;
|
||||
|
||||
@@ -240,6 +240,24 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial EasingType LyricsScrollEasingType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsScrollDuration { get; set; }
|
||||
|
||||
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
|
||||
{
|
||||
_settingsService.LyricsScrollEasingType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsScrollDurationChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsScrollDuration = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
|
||||
{
|
||||
_settingsService.LyricsBackgroundTheme = value;
|
||||
@@ -349,13 +367,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private async Task LaunchProjectGitHubPageAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(Helper.AppInfo.GithubUrl));
|
||||
await Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenCacheFolder()
|
||||
{
|
||||
OpenFolderInFileExplorer(Helper.AppInfo.CacheFolder);
|
||||
OpenFolderInFileExplorer(PathHelper.CacheFolder);
|
||||
}
|
||||
|
||||
private void OpenFolderInFileExplorer(string path)
|
||||
@@ -410,7 +428,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
string targetLangCode = AppInfo.TranslationLanguagesInfo[SelectedTargetLanguageIndex].Code;
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[SelectedTargetLanguageIndex].Code;
|
||||
string result = await _libreTranslateService.TranslateAsync("Hello, world!", targetLangCode, null);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial bool IsLyricsWindowLocked { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string ToolTipText { get; set; } = AppInfo.AppName;
|
||||
public partial string ToolTipText { get; set; } = MetadataHelper.AppName;
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DesktopModeHelper.Unlock(window);
|
||||
DesktopModeHelper.SetClickThrough(window, false);
|
||||
IsLyricsWindowLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,20 @@
|
||||
xmlns:scontrols="using:ShadowViewer.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid x:Name="RootGrid" RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
PointerEntered="RootGrid_PointerEntered"
|
||||
PointerExited="RootGrid_PointerExited"
|
||||
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
|
||||
<local:LyricsPage />
|
||||
|
||||
<!-- Top command -->
|
||||
<Grid
|
||||
x:Name="TopCommandGrid"
|
||||
Margin="6"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
Opacity="0"
|
||||
PointerEntered="TopCommandGrid_PointerEntered"
|
||||
PointerExited="TopCommandGrid_PointerExited">
|
||||
@@ -113,7 +114,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Maximise -->
|
||||
<Button
|
||||
@@ -123,7 +124,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Restore -->
|
||||
<Button
|
||||
@@ -134,7 +135,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Close -->
|
||||
<Button
|
||||
@@ -144,7 +145,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -267,5 +267,15 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel = TipContainerCenter;
|
||||
}
|
||||
|
||||
private void RootGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.IsMouseWithinWindow = true;
|
||||
}
|
||||
|
||||
private void RootGrid_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.IsMouseWithinWindow = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,6 +673,47 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFanLyricsEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageScrollEasing"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeSmoothStep" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutSine" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuad" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutCubic" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuart" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuint" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutExpo" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutCirc" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutBack" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutElastic" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutBounce" />
|
||||
</ComboBox>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollDuration">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.LyricsScrollDuration, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" ms" />
|
||||
<Slider
|
||||
Maximum="1000"
|
||||
Minimum="100"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsScrollDuration, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user