This commit is contained in:
Zhe Fang
2025-07-11 18:08:05 -04:00
parent 07b82191d0
commit 34d7f3f319
37 changed files with 604 additions and 402 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -4,7 +4,8 @@ namespace BetterLyrics.WinUI3.Enums
{
public enum LineRenderingType
{
UntilCurrentChar,
CurrentCharOnly,
CurrentChar,
LineStartToCurrentChar,
CurrentLine
}
}

View File

@@ -1,15 +0,0 @@
// 2025/6/23 by Zhe Fang
using System.Collections.Generic;
namespace BetterLyrics.WinUI3.Helper
{
public static class CollectionHelper
{
public static T? SafeGet<T>(this IList<T> list, int index)
{
if (list == null || index < 0 || index >= list.Count) return default;
return list[index];
}
}
}

View File

@@ -47,13 +47,6 @@ namespace BetterLyrics.WinUI3.Helper
_originalWindowBounds.Remove(hwnd);
}
// <20>ָ<EFBFBD><D6B8><EFBFBD>ʽ
if (_originalWindowStyles.TryGetValue(hwnd, out var style))
{
window.SetWindowStyle(style);
_originalWindowStyles.Remove(hwnd);
}
window.SetIsShownInSwitchers(true);
}
@@ -83,10 +76,6 @@ namespace BetterLyrics.WinUI3.Helper
new Windows.Graphics.RectInt32(targetX, targetY, targetWidth, targetHeight)
);
// <20><><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD>ʽ
if (!_originalWindowStyles.ContainsKey(hwnd))
_originalWindowStyles[hwnd] = window.GetWindowStyle();
// <20><><EFBFBD><EFBFBD>ԭTopMost״̬
if (!_originalTopmostStates.ContainsKey(hwnd))
_originalTopmostStates[hwnd] = window.GetIsAlwaysOnTop();
@@ -95,8 +84,6 @@ namespace BetterLyrics.WinUI3.Helper
window.SetIsAlwaysOnTop(true);
window.SetIsShownInSwitchers(false);
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
}
public static void SetClickThrough(Window window, bool enable)
@@ -105,11 +92,22 @@ namespace BetterLyrics.WinUI3.Helper
int exStyle = User32.GetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE);
if (enable)
{
// <20><><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD>ʽ
if (!_originalWindowStyles.ContainsKey(hwnd))
_originalWindowStyles[hwnd] = window.GetWindowStyle();
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle | (int)User32.WindowStylesEx.WS_EX_TRANSPARENT | (int)User32.WindowStylesEx.WS_EX_LAYERED);
}
else
{
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle & ~(int)User32.WindowStylesEx.WS_EX_TRANSPARENT);
// <20>ָ<EFBFBD><D6B8><EFBFBD>ʽ
if (_originalWindowStyles.TryGetValue(hwnd, out var style))
{
window.SetWindowStyle(style);
_originalWindowStyles.Remove(hwnd);
}
}
}
}

View File

@@ -69,9 +69,9 @@ namespace BetterLyrics.WinUI3.Helper
public static bool IsSwitchableNormalizedMatch(string fileName, string q1, string q2)
{
var normFileName = fileName.Normalize();
var normQ1 = q1.Normalize();
var normQ2 = q2.Normalize();
var normFileName = StringHelper.Normalize(fileName.Normalize());
var normQ1 = StringHelper.Normalize(q1);
var normQ2 = StringHelper.Normalize(q2);
// 常见两种顺序
return normFileName == normQ1 + normQ2

View File

@@ -102,7 +102,7 @@ namespace BetterLyrics.WinUI3.Helper
}
}
public static List<Windows.UI.Color> GetAccentColorsFromByte(byte[] bytes)
public static List<Color> GetAccentColorsFromByte(byte[] bytes)
{
// 使用 ImageSharp 读取图片
using var image = SixLabors.ImageSharp.Image.Load<SixLabors.ImageSharp.PixelFormats.Rgba32>(bytes);
@@ -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));
}
}
}

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers.General;
using NTextCat;
using System;
@@ -12,6 +13,7 @@ namespace BetterLyrics.WinUI3.Services
{
private static readonly RankedLanguageIdentifierFactory _factory = new();
private static readonly RankedLanguageIdentifier _identifier;
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
public static List<Models.LanguageInfo> SupportedTargetLanguages =>
[
@@ -106,5 +108,10 @@ namespace BetterLyrics.WinUI3.Services
// 其他语言直接返回两字母代码
return code;
}
public static string GetUserTargetLanguageCode()
{
return SupportedTargetLanguages[_settingsService.SelectedTargetLanguageIndex].Code;
}
}
}

View File

@@ -10,29 +10,21 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Windows.Globalization.Fonts;
using LyricsData = BetterLyrics.WinUI3.Models.LyricsData;
namespace BetterLyrics.WinUI3.Helper
{
public class LyricsParser
{
private List<List<LyricsLine>> _multiLangLyricsLines = [];
private List<LyricsData> _lyricsDataArr = [];
public List<List<LyricsLine>> Parse(string? raw, int durationMs)
public List<LyricsData> Parse(string? raw, int? durationMs)
{
_multiLangLyricsLines = [];
durationMs ??= (int)TimeSpan.FromMinutes(99).TotalMilliseconds;
_lyricsDataArr = [];
if (raw == null)
{
_multiLangLyricsLines.Add(
[
new LyricsLine
{
StartMs = 0,
EndMs = durationMs,
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
CharTimings = [],
},
]
);
_lyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder(durationMs.Value));
}
else
{
@@ -43,10 +35,10 @@ namespace BetterLyrics.WinUI3.Helper
ParseLrc(raw);
break;
case LyricsFormat.Qrc:
ParseUsingLyricify(Lyricify.Lyrics.Parsers.QrcParser.Parse(raw).Lines);
ParseQQNeteaseKugou(Lyricify.Lyrics.Parsers.QrcParser.Parse(raw).Lines);
break;
case LyricsFormat.Krc:
ParseUsingLyricify(Lyricify.Lyrics.Parsers.KrcParser.Parse(raw).Lines);
ParseQQNeteaseKugou(Lyricify.Lyrics.Parsers.KrcParser.Parse(raw).Lines);
break;
case LyricsFormat.Ttml:
ParseTtml(raw);
@@ -55,8 +47,8 @@ namespace BetterLyrics.WinUI3.Helper
break;
}
}
PostProcessLyricsLines(durationMs);
return _multiLangLyricsLines;
PostProcessLyricsLines(durationMs.Value);
return _lyricsDataArr;
}
private void ParseLrc(string raw)
@@ -120,9 +112,9 @@ namespace BetterLyrics.WinUI3.Helper
int languageCount = grouped.Max(g => g.Count());
// 初始化每种语言的歌词列表
_multiLangLyricsLines.Clear();
_lyricsDataArr.Clear();
for (int i = 0; i < languageCount; i++)
_multiLangLyricsLines.Add(new List<LyricsLine>());
_lyricsDataArr.Add(new LyricsData());
// 遍历每个时间分组
foreach (var group in grouped)
@@ -159,7 +151,7 @@ namespace BetterLyrics.WinUI3.Helper
currentIndex += charText?.Length ?? 0;
}
}
_multiLangLyricsLines[langIdx].Add(line);
_lyricsDataArr[langIdx].LyricsLines.Add(line);
}
}
}
@@ -259,9 +251,9 @@ namespace BetterLyrics.WinUI3.Helper
});
}
}
_multiLangLyricsLines.Add(originalLines);
_lyricsDataArr.Add(new LyricsData(originalLines));
if (translationLines.Count > 0)
_multiLangLyricsLines.Add(translationLines);
_lyricsDataArr.Add(new LyricsData(translationLines));
}
catch
{
@@ -330,7 +322,7 @@ namespace BetterLyrics.WinUI3.Helper
return 0;
}
private void ParseUsingLyricify(List<ILineInfo>? lines)
private void ParseQQNeteaseKugou(List<ILineInfo>? lines)
{
lines = lines?.Where(x => x.Text != string.Empty).ToList();
List<LyricsLine> lyricsLines = [];
@@ -384,27 +376,27 @@ namespace BetterLyrics.WinUI3.Helper
}
}
_multiLangLyricsLines.Add(lyricsLines);
_lyricsDataArr.Add(new LyricsData(lyricsLines));
}
private void PostProcessLyricsLines(int durationMs)
{
for (int langIdx = 0; langIdx < _multiLangLyricsLines.Count; langIdx++)
for (int langIdx = 0; langIdx < _lyricsDataArr.Count; langIdx++)
{
var linesInSingleLang = _multiLangLyricsLines[langIdx];
for (int i = 0; i < linesInSingleLang.Count; i++)
var lines = _lyricsDataArr[langIdx].LyricsLines;
for (int i = 0; i < lines.Count; i++)
{
if (i + 1 < linesInSingleLang.Count)
if (i + 1 < lines.Count)
{
linesInSingleLang[i].EndMs = linesInSingleLang[i + 1].StartMs;
lines[i].EndMs = lines[i + 1].StartMs;
}
else
{
linesInSingleLang[i].EndMs = durationMs;
lines[i].EndMs = durationMs;
}
// 修正 CharTimings 的 EndMs
var timings = linesInSingleLang[i].CharTimings;
var timings = lines[i].CharTimings;
if (timings.Count > 0)
{
for (int j = 0; j < timings.Count; j++)
@@ -415,21 +407,21 @@ namespace BetterLyrics.WinUI3.Helper
}
else
{
timings[j].EndMs = linesInSingleLang[i].EndMs;
timings[j].EndMs = lines[i].EndMs;
}
}
}
}
if (linesInSingleLang.Count > 0)
if (lines.Count > 0)
{
if (linesInSingleLang[0].StartMs > 0)
if (lines[0].StartMs > 0)
{
linesInSingleLang.Insert(
lines.Insert(
0,
new LyricsLine
{
StartMs = 0,
EndMs = linesInSingleLang[0].StartMs,
EndMs = lines[0].StartMs,
OriginalText = "● ● ●",
CharTimings = [],
}

View File

@@ -27,6 +27,8 @@ namespace BetterLyrics.WinUI3.Helper
}
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv";
public static async Task<DateTime> GetBuildDate()
{

View File

@@ -20,18 +20,26 @@ namespace BetterLyrics.WinUI3.Helper
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 LyricsCacheDirectory => Path.Combine(CacheFolder, "lyrics");
public static string iTunesAlbumArtCacheDirectory => Path.Combine(CacheFolder, "itunes-album-art");
public static string LrcLibLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "lrclib");
public static string NeteaseLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "netease");
public static string QQLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "qq");
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.json");
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
public static string TranslationCacheDirectory => Path.Combine(CacheFolder, "translations");
public static string QQTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "qq");
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
public static void EnsureDirectories()
{
Directory.CreateDirectory(LocalFolder);
Directory.CreateDirectory(LogDirectory);
Directory.CreateDirectory(LrcLibLyricsCacheDirectory);
@@ -40,6 +48,8 @@ namespace BetterLyrics.WinUI3.Helper
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
Directory.CreateDirectory(QQTranslationCacheDirectory);
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
}
}

View File

@@ -9,10 +9,11 @@ namespace BetterLyrics.WinUI3.Helper
public static class StringHelper
{
// 去除空格、括号、下划线、横杠、点、大小写等
public static string Normalize(this string s) =>
public static string Normalize(string s) =>
new string(s
.Where(c => char.IsLetterOrDigit(c))
.ToArray())
.ToLowerInvariant();
public static string NewLine = "\n";
}
}

View File

@@ -0,0 +1,95 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
{
public class LyricsData
{
public List<LyricsLine> LyricsLines { get; set; }
public string? LanguageCode => LanguageHelper.DetectLanguageCode(WrappedOriginalText);
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
public LyricsData()
{
LyricsLines = [];
}
public LyricsData(List<LyricsLine> lyricsLines)
{
LyricsLines = lyricsLines;
}
public void SetDisplayedTextAlongWith(LyricsData translationData)
{
int i = 0;
foreach (var line in LyricsLines)
{
if (i >= translationData.LyricsLines.Count)
{
line.DisplayedText = line.OriginalText; // No translation available, keep original text
}
else
{
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}({translationData.LyricsLines[i].OriginalText})";
}
i++;
}
}
public void SetDisplayedTextAlongWith(string translation)
{
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
int i = 0;
foreach (var line in LyricsLines)
{
if (i >= translationArr.Count)
{
line.DisplayedText = line.OriginalText; // No translation available, keep original text
}
else
{
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}({translationArr[i]})";
}
i++;
}
}
public void SetDisplayedTextInOriginalText()
{
foreach (var line in LyricsLines)
{
line.DisplayedText = line.OriginalText;
}
}
public static LyricsData GetNotfoundPlaceholder(int durationMs)
{
return new LyricsData([new LyricsLine
{
StartMs = 0,
EndMs = durationMs,
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
CharTimings = [],
}]);
}
public static LyricsData GetLoadingPlaceholder()
{
return new LyricsData([
new LyricsLine
{
StartMs = 0,
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
OriginalText = "● ● ●",
DisplayedText = "● ● ●",
CharTimings = [],
},
]);
}
}
}

View File

@@ -67,6 +67,7 @@ namespace BetterLyrics.WinUI3.Services
LyricsFontWeight LyricsFontWeight { get; set; }
LineRenderingType LyricsGlowEffectScope { get; set; }
LineRenderingType LyricsHighlightScope { get; set; }
float LyricsLineSpacingFactor { get; set; }
@@ -84,5 +85,7 @@ namespace BetterLyrics.WinUI3.Services
bool IsTranslationEnabled { get; set; }
LyricsDisplayType PreferredDisplayType { get; set; }
int TimelineSyncThreshold { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using System;
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -9,6 +10,8 @@ namespace BetterLyrics.WinUI3.Services
{
public interface ITranslateService
{
Task<string> TranslateAsync(string text, string targetLangCode, CancellationToken? token);
Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken? token);
int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr);
}
}

View File

@@ -15,6 +15,7 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;
namespace BetterLyrics.WinUI3.Services
{
@@ -39,6 +40,23 @@ namespace BetterLyrics.WinUI3.Services
_amllTtmlDbHttpClient = new();
}
private static bool IsAmllTtmlDbIndexInvalid()
{
bool existed = File.Exists(PathHelper.AmllTtmlDbIndexPath);
if (!existed)
{
return true;
}
else
{
long currentTs = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
string lastUpdatedStr = File.ReadAllText(PathHelper.AmllTtmlDbLastUpdatedPath);
long lastUpdated = Convert.ToInt64(lastUpdatedStr);
return currentTs - lastUpdated > 1 * 24 * 60 * 60;
}
}
public async Task<bool> DownloadAmllTtmlDbIndexAsync()
{
const string url = "https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/metadata/raw-lyrics-index.jsonl";
@@ -56,6 +74,9 @@ namespace BetterLyrics.WinUI3.Services
);
await stream.CopyToAsync(fs);
long currentTs = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
File.WriteAllText(PathHelper.AmllTtmlDbLastUpdatedPath, currentTs.ToString());
return true;
}
catch
@@ -192,11 +213,10 @@ namespace BetterLyrics.WinUI3.Services
private async Task<string?> SearchAmllTtmlDbAsync(string title, string artist)
{
// 检索本地 JSONL 索引文件,查找 rawLyricFile
if (!File.Exists(PathHelper.AmllTtmlDbIndexPath))
if (IsAmllTtmlDbIndexInvalid())
{
var downloadOk = await DownloadAmllTtmlDbIndexAsync();
if (!downloadOk || !File.Exists(PathHelper.AmllTtmlDbIndexPath))
if (!downloadOk)
return null;
}

View File

@@ -62,6 +62,7 @@ namespace BetterLyrics.WinUI3.Services
private const string LyricsFontSizeKey = "LyricsFontSize";
private const string LyricsFontWeightKey = "LyricsFontWeightKey";
private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope";
private const string LyricsHighlightSopeKey = "LyricsHighlightSope";
private const string LyricsLineSpacingFactorKey = "LyricsLineSpacingFactor";
private const string LyricsSearchProvidersInfoKey = "LyricsSearchProvidersInfo";
private const string AlbumArtSearchProvidersInfoKey = "AlbumArtSearchProvidersInfo";
@@ -80,6 +81,8 @@ namespace BetterLyrics.WinUI3.Services
private const string LyricsScrollEasingTypeKey = "LyricsScrollEasingType";
private const string LyricsScrollDurationKey = "LyricsScrollDuration";
public const string TimelineSyncThresholdKey = "TimelineSyncThreshold";
private readonly ApplicationDataContainer _localSettings;
public SettingsService()
@@ -155,7 +158,7 @@ namespace BetterLyrics.WinUI3.Services
SetDefault(IsCoverOverlayEnabledKey, true);
SetDefault(IsDynamicCoverOverlayEnabledKey, true);
SetDefault(CoverOverlayOpacityKey, 100); // 100 % = 1.0
SetDefault(CoverOverlayBlurAmountKey, 200);
SetDefault(CoverOverlayBlurAmountKey, 100);
SetDefault(CoverImageRadiusKey, 12); // 12 %
// Lyrics
SetDefault(LyricsAlignmentTypeKey, (int)TextAlignmentType.Center);
@@ -177,7 +180,8 @@ namespace BetterLyrics.WinUI3.Services
SetDefault(LyricsLineSpacingFactorKey, 0.5f);
SetDefault(LyricsVerticalEdgeOpacityKey, 0);
SetDefault(IsLyricsGlowEffectEnabledKey, true);
SetDefault(LyricsGlowEffectScopeKey, (int)LineRenderingType.CurrentCharOnly);
SetDefault(LyricsGlowEffectScopeKey, (int)LineRenderingType.CurrentChar);
SetDefault(LyricsHighlightSopeKey, (int)LineRenderingType.LineStartToCurrentChar);
SetDefault(IsFanLyricsEnabledKey, false);
SetDefault(LibreTranslateServerKey, "");
@@ -190,6 +194,7 @@ namespace BetterLyrics.WinUI3.Services
SetDefault(LyricsScrollEasingTypeKey, (int)EasingType.EaseInOutQuad);
SetDefault(LyricsScrollDurationKey, 500); // 500ms
SetDefault(TimelineSyncThresholdKey, 0); // 0ms
}
public EasingType LyricsScrollEasingType
@@ -419,6 +424,12 @@ namespace BetterLyrics.WinUI3.Services
set => SetValue(LyricsGlowEffectScopeKey, (int)value);
}
public LineRenderingType LyricsHighlightScope
{
get => (LineRenderingType)GetValue<int>(LyricsHighlightSopeKey);
set => SetValue(LyricsHighlightSopeKey, (int)value);
}
public float LyricsLineSpacingFactor
{
get => GetValue<float>(LyricsLineSpacingFactorKey);
@@ -506,6 +517,12 @@ namespace BetterLyrics.WinUI3.Services
set => SetValue(IgnoreFullscreenWindowKey, value);
}
public int TimelineSyncThreshold
{
get => GetValue<int>(TimelineSyncThresholdKey);
set => SetValue(TimelineSyncThresholdKey, value);
}
private T? GetValue<T>(string key)
{
if (_localSettings.Values.TryGetValue(key, out object? value))

View File

@@ -1,6 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Serialization;
using BetterLyrics.WinUI3.ViewModels;
using Lyricify.Lyrics.Helpers.General;
using System;
using System.Collections.Generic;
@@ -13,19 +14,16 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services
{
public class TranslateService : ITranslateService
public class TranslateService : BaseViewModel, ITranslateService
{
private readonly ISettingsService _settingsService;
private readonly HttpClient _httpClient;
public TranslateService(ISettingsService settingsService)
public TranslateService(ISettingsService settingsService) :base(settingsService)
{
_settingsService = settingsService;
_httpClient = new HttpClient();
}
public async Task<string> TranslateAsync(string text, string targetLangCode, CancellationToken? token)
public async Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken? token)
{
if (string.IsNullOrWhiteSpace(text))
{
@@ -46,6 +44,19 @@ namespace BetterLyrics.WinUI3.Services
return ChineseConverter.ConvertToTraditionalChinese(text);
}
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
{
_dispatcherQueue.TryEnqueue(() =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("TranslateServerNotSet"),
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
);
});
throw new InvalidOperationException("LibreTranslate server URL is not configured.");
}
var url = $"{_settingsService.LibreTranslateServer}/translate";
var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(
[
@@ -62,5 +73,21 @@ namespace BetterLyrics.WinUI3.Services
var result = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.TranslateResponse);
return result?.TranslatedText ?? string.Empty;
}
public int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr)
{
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode();
if (lyricsDataArr.Count > 1)
{
for (int i = 1; i < lyricsDataArr.Count; i++)
{
if (lyricsDataArr[i].LanguageCode == targetLangCode)
{
return i; // Translation lyrics data found
}
}
}
return -1; // No translation lyrics data found
}
}
}

View File

@@ -310,7 +310,7 @@
<value>No music playing now</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>Developer options</value>
<value>Advanced options</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>Play test music</value>
@@ -459,13 +459,10 @@
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>Extra Black</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeWholeLyrics.Content" xml:space="preserve">
<value>Whole lyrics</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentLine.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>Current line</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentChar.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>Current char</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
@@ -717,4 +714,25 @@
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>Ease-in-out cubic</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>Current line start to current char</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>Highlight scope</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>Lyrics timeline sync threshold</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>If the lyrics progress is jittery, try increasing this threshold; changing this value can cause lyrics synchronization to deviate</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQ feedback &amp; chat group</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>Join now</value>
</data>
</root>

View File

@@ -310,7 +310,7 @@
<value>今は音楽が再生されていません</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>開発者オプション</value>
<value>高度なオプション</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>テスト音楽を再生します</value>
@@ -459,13 +459,10 @@
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>余分な黒</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeWholeLyrics.Content" xml:space="preserve">
<value>歌詞全体</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentLine.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>現在の行</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentChar.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>現在の文字</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
@@ -717,4 +714,25 @@
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>3つの遅いインとアウト</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>現在のラインが現在の文字から始まります</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>ハイライトスコープ</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>歌詞タイムライン同期しきい値</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>歌詞の進行が不安定な場合は、このしきい値を増やしてみてください。この値を変更すると、歌詞の同期が逸脱する可能性があります</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQフィードバックチャットグループ</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>今すぐ参加してください</value>
</data>
</root>

View File

@@ -310,7 +310,7 @@
<value>지금 음악이 재생되지 않습니다</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>개발자 옵션</value>
<value>고급 옵션</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>테스트 음악을 재생하십시오</value>
@@ -459,13 +459,10 @@
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>여분의 검은 색</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeWholeLyrics.Content" xml:space="preserve">
<value>전체 가사</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentLine.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>현재 라인</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentChar.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>현재 숯</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
@@ -717,4 +714,25 @@
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>세 번 느리게 안팎으로</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>현재 라인은 현재 숯으로 시작합니다</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>하이라이트 범위</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>가사 타임 라인 동기화 임계 값</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>가사 진행 상황이 불안하다면이 임계 값을 높이십시오. 이 값을 변경하면 가사가 동기화 될 수 있습니다</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQ 피드백 및 채팅 그룹</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>지금 가입하십시오</value>
</data>
</root>

View File

@@ -310,7 +310,7 @@
<value>当前没有正在播放的音乐</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>开发者选项</value>
<value>高级选项</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>播放测试音乐</value>
@@ -459,13 +459,10 @@
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>超黑</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeWholeLyrics.Content" xml:space="preserve">
<value>全部歌词</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentLine.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>当前行</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentChar.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>当前字符</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
@@ -717,4 +714,25 @@
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>三次缓入缓出</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>当前歌词开始到当前字符</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>高亮显示范围</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>歌词时间轴同步阈值</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>当歌词进度抖动时,请尝试增加该阈值;更改此值会导致歌词同步有偏差</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQ 反馈交流群</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>立即加入</value>
</data>
</root>

View File

@@ -310,7 +310,7 @@
<value>目前沒有正在播放的音樂</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>開發者選項</value>
<value>高級選項</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>播放測試音樂</value>
@@ -459,13 +459,10 @@
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>超黑</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeWholeLyrics.Content" xml:space="preserve">
<value>全部歌詞</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentLine.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>目前行</value>
</data>
<data name="SettingsPageLyricsGlowEffectScopeCurrentChar.Content" xml:space="preserve">
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>目前字元</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
@@ -717,4 +714,25 @@
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>三次緩入緩出</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>當前歌詞開始到當前字符</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>高亮顯示範圍</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>歌詞時間軸同步閾值</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>當歌詞進度抖動時,請嘗試增加該閾值;更改此值會導致歌詞同步偏差</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQ 回饋交流群</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>立即加入</value>
</data>
</root>

View File

@@ -11,8 +11,7 @@ using Microsoft.UI.Xaml;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class BaseWindowViewModel : BaseViewModel
public partial class BaseWindowViewModel(ISettingsService settingsService) : BaseViewModel(settingsService)
{
public BaseWindowViewModel(ISettingsService settingsService) : base(settingsService) { }
}
}

View File

@@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class LyricsPageViewModel : BaseViewModel, IRecipient<PropertyChangedMessage<int>>, IRecipient<PropertyChangedMessage<bool>>
public partial class LyricsPageViewModel : BaseViewModel, IRecipient<PropertyChangedMessage<bool>>
{
private readonly IPlaybackService _playbackService;
@@ -23,15 +23,12 @@ namespace BetterLyrics.WinUI3.ViewModels
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
{
LyricsFontSize = _settingsService.LyricsFontSize;
IsFirstRun = _settingsService.IsFirstRun;
IsTranslationEnabled = _settingsService.IsTranslationEnabled;
PreferredDisplayType = _settingsService.PreferredDisplayType;
_playbackService = playbackService;
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
IsFirstRun = _settingsService.IsFirstRun;
}
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
@@ -51,12 +48,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsWelcomeTeachingTipOpen { get; set; }
[ObservableProperty]
public partial Visibility BottomCommandGridVisibility { get; set; } = Visibility.Visible;
[ObservableProperty]
public partial int LyricsFontSize { get; set; }
[ObservableProperty]
public partial LyricsDisplayType PreferredDisplayType { get; set; }
@@ -69,7 +60,7 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsTranslationEnabled { get; set; } = false;
public partial bool IsTranslationEnabled { get; set; }
partial void OnIsTranslationEnabledChanged(bool value)
{
@@ -98,19 +89,8 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
{
LyricsFontSize = message.NewValue;
}
}
}
[RelayCommand]
private void OpenSettingsWindow()
private static void OpenSettingsWindow()
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
}

View File

@@ -33,6 +33,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_lyricsBlurAmount = _settingsService.LyricsBlurAmount;
_isLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
_lyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
_lyricsHighlightScope = _settingsService.LyricsHighlightScope;
_customBgFontColor = _settingsService.LyricsCustomBgFontColor;
_customFgFontColor = _settingsService.LyricsCustomFgFontColor;
@@ -45,6 +46,8 @@ namespace BetterLyrics.WinUI3.ViewModels
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
_timelineSyncThreshold = _settingsService.TimelineSyncThreshold;
_canvasYScrollTransition.SetDuration(_settingsService.LyricsScrollDuration / 1000f);
_canvasYScrollTransition.SetEasingType(_settingsService.LyricsScrollEasingType);
@@ -56,7 +59,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_playbackService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged;
_playbackService.PositionChanged += PlaybackService_PositionChanged;
UpdateFontColor();
UpdateColorConfig();
}
}
}

View File

@@ -67,9 +67,9 @@ namespace BetterLyrics.WinUI3.ViewModels
if (_isDebugOverlayEnabled)
{
var currentPlayingLine = _multiLangLyrics
.SafeGet(_langIndex)
?.SafeGet(_playingLineIndex);
var currentPlayingLine = _lyricsDataArr
.ElementAtOrDefault(_langIndex)
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
if (currentPlayingLine != null)
{
@@ -85,7 +85,7 @@ namespace BetterLyrics.WinUI3.ViewModels
$"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" +
$"Lang size {_lyricsDataArr.Count}\n" +
$"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
new Vector2(10, 10),
ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White
@@ -118,13 +118,13 @@ namespace BetterLyrics.WinUI3.ViewModels
float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2;
float y = _canvasHeight / 2 - imageHeight * scaleFactor / 2;
// Source: https://zhuanlan.zhihu.com/p/37178216
float bright = _lyricsBgBrightnessTransition.Value / 1f * 2f; // 明度参数范围在0.0f到2.0f之间
// Original source: https://zhuanlan.zhihu.com/p/37178216
float gain = _lyricsBgBrightnessTransition.Value;
float whiteX = Math.Min(2 - bright, 1);
float whiteY = 1f;
float blackX = Math.Max(1 - bright, 0);
float blackY = 0f;
float whiteX = 1 - 0.5f * gain;
float whiteY = 0.5f + 0.5f * gain;
float blackX = 0.5f - 0.5f * gain;
float blackY = 0 + 0.5f * gain;
ds.DrawImage(new OpacityEffect
{
@@ -274,9 +274,9 @@ namespace BetterLyrics.WinUI3.ViewModels
private void DrawBlurredLyrics(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
var currentPlayingLine = _multiLangLyrics
.SafeGet(_langIndex)
?.SafeGet(_playingLineIndex);
var currentPlayingLine = _lyricsDataArr
.ElementAtOrDefault(_langIndex)
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
if (currentPlayingLine == null)
{
@@ -285,7 +285,7 @@ namespace BetterLyrics.WinUI3.ViewModels
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
{
var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
if (line == null)
{
@@ -486,8 +486,9 @@ namespace BetterLyrics.WinUI3.ViewModels
Source = fgLyrics,
AlphaMask = _lyricsGlowEffectScope switch
{
LineRenderingType.UntilCurrentChar => mask,
LineRenderingType.CurrentCharOnly => highlightMask,
LineRenderingType.CurrentChar => highlightMask,
LineRenderingType.LineStartToCurrentChar => mask,
LineRenderingType.CurrentLine => fgLyrics,
_ => mask,
},
},
@@ -498,7 +499,13 @@ namespace BetterLyrics.WinUI3.ViewModels
Foreground = new AlphaMaskEffect
{
Source = fgLyrics,
AlphaMask = mask,
AlphaMask = _lyricsHighlightScope switch
{
LineRenderingType.CurrentChar => highlightMask,
LineRenderingType.LineStartToCurrentChar => mask,
LineRenderingType.CurrentLine => fgLyrics,
_ => mask,
},
},
},
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value,

View File

@@ -57,8 +57,6 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
// Receive methods for handling messages from other view models
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is SettingsPageViewModel)
@@ -86,12 +84,12 @@ namespace BetterLyrics.WinUI3.ViewModels
if (message.PropertyName == nameof(LyricsWindowViewModel.IsDockMode))
{
_isDockMode = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
{
_isDesktopMode = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsLyricsWindowLocked))
{
@@ -122,7 +120,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_immersiveBgTransition.StartTransition(message.NewValue);
_environmentalColor = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
}
else if (message.Sender is SettingsPageViewModel)
@@ -130,17 +128,17 @@ namespace BetterLyrics.WinUI3.ViewModels
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomBgFontColor))
{
_customBgFontColor = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomFgFontColor))
{
_customFgFontColor = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomStrokeFontColor))
{
_customStrokeFontColor = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
}
}
@@ -202,6 +200,10 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_canvasYScrollTransition.SetDuration(message.NewValue / 1000f);
}
else if (message.PropertyName == nameof(SettingsPageViewModel.TimelineSyncThreshold))
{
_timelineSyncThreshold = message.NewValue;
}
}
else if (message.Sender is LyricsPageViewModel)
{
@@ -220,6 +222,10 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_lyricsGlowEffectScope = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsHighlightScope))
{
_lyricsHighlightScope = message.NewValue;
}
}
}
@@ -251,17 +257,17 @@ namespace BetterLyrics.WinUI3.ViewModels
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBgFontColorType))
{
_lyricsBgFontColorType = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFgFontColorType))
{
_lyricsFgFontColorType = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStrokeFontColorType))
{
_lyricsStrokeFontColorType = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
}
}
@@ -285,7 +291,7 @@ namespace BetterLyrics.WinUI3.ViewModels
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBackgroundTheme))
{
_lyricsBgTheme = message.NewValue;
UpdateFontColor();
UpdateColorConfig();
}
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using System;
using System.Linq;
using System.Numerics;
using Windows.UI;
@@ -135,9 +136,9 @@ namespace BetterLyrics.WinUI3.ViewModels
float y = 0;
// Init Positions
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
{
var line = _multiLangLyrics[_langIndex].SafeGet(i);
var line = _lyricsDataArr[_langIndex].LyricsLines.ElementAtOrDefault(i);
if (line == null)
{
@@ -178,7 +179,7 @@ namespace BetterLyrics.WinUI3.ViewModels
if ((!_isPlayingLineChanged && forceScroll) || _isPlayingLineChanged)
{
LyricsLine? currentPlayingLine = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(_playingLineIndex);
LyricsLine? currentPlayingLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
if (currentPlayingLine == null) return;
@@ -186,7 +187,7 @@ namespace BetterLyrics.WinUI3.ViewModels
if (playingTextLayout == null) return;
float? targetYScrollOffset = (float?)(-currentPlayingLine!.Position.Y + _multiLangLyrics.SafeGet(_langIndex)?[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2);
float? targetYScrollOffset = (float?)(-currentPlayingLine!.Position.Y + _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2);
if (!targetYScrollOffset.HasValue) return;
@@ -201,7 +202,7 @@ namespace BetterLyrics.WinUI3.ViewModels
// Update visible line indices
for (int i = startLineIndex; i <= endLineIndex; i++)
{
var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
if (line == null || line.CanvasTextLayout == null)
{
@@ -249,7 +250,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_endVisibleLineIndex = endVisibleLineIndex;
}
private void UpdateFontColor()
private void UpdateColorConfig()
{
if (_isDesktopMode || _isDockMode)
{
@@ -360,15 +361,15 @@ namespace BetterLyrics.WinUI3.ViewModels
private void UpdateLinesProps()
{
var currentPlayingLine = _multiLangLyrics
.SafeGet(_langIndex)
?.SafeGet(_playingLineIndex);
var currentPlayingLine = _lyricsDataArr
.ElementAtOrDefault(_langIndex)
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
if (currentPlayingLine == null) return;
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
{
var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
if (line == null) continue;

View File

@@ -72,6 +72,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private ElementTheme _lyricsBgTheme;
private LineRenderingType _lyricsGlowEffectScope;
private LineRenderingType _lyricsHighlightScope;
private int _lyricsFontStrokeWidth;
private int _lyricsFontSize;
@@ -134,11 +135,13 @@ namespace BetterLyrics.WinUI3.ViewModels
private int _langIndex = 0;
private List<List<LyricsLine>> _multiLangLyrics = [];
private List<LyricsData> _lyricsDataArr = [];
private List<string> _translationList = [];
private bool _isTranslationEnabled = false;
private int _targetLanguageIndex = 6;
private int _timelineSyncThreshold;
private CanvasTextFormat _lyricsTextFormat = new()
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
@@ -184,9 +187,9 @@ namespace BetterLyrics.WinUI3.ViewModels
private int GetCurrentPlayingLineIndex()
{
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
{
var line = _multiLangLyrics.SafeGet(_langIndex)?[i];
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[i];
if (line == null)
{
continue;
@@ -262,14 +265,14 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (
SongInfo == null
|| _multiLangLyrics.SafeGet(_langIndex) == null
|| _multiLangLyrics[_langIndex].Count == 0
|| _lyricsDataArr.ElementAtOrDefault(_langIndex) == null
|| _lyricsDataArr[_langIndex].LyricsLines.Count == 0
)
{
return new Tuple<int, int>(-1, -1);
}
return new Tuple<int, int>(0, _multiLangLyrics[_langIndex].Count - 1);
return new Tuple<int, int>(0, _lyricsDataArr[_langIndex].LyricsLines.Count - 1);
}
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
@@ -288,7 +291,10 @@ namespace BetterLyrics.WinUI3.ViewModels
private void PlaybackService_PositionChanged(object? sender, PositionChangedEventArgs e)
{
_totalTime = e.Position;
if (Math.Abs(_totalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold)
{
_totalTime = e.Position;
}
}
private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
@@ -330,7 +336,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_albumArtBgTransition.Reset(0f);
_albumArtBgTransition.StartTransition(1f);
UpdateFontColor();
UpdateColorConfig();
}
}
@@ -341,93 +347,55 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_ = _refreshLyricsRunner.RunAsync(async token =>
{
await ShowTranslationsAsync(token);
await SetDisplayedAlongWithTranslationsAsync(token);
IsTranslating = false;
_isLayoutChanged = true;
});
}
else
{
ShowOriginalsOnly();
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
IsTranslating = false;
_isLayoutChanged = true;
}
}
private async Task ShowTranslationsAsync(CancellationToken token)
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
{
_logger.LogInformation("Showing translation for lyrics...");
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.SelectedTargetLanguageIndex].Code;
var originalText = string.Join("\n", _multiLangLyrics.FirstOrDefault()?.Select(x => x.OriginalText) ?? []);
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode();
string originalText = _lyricsDataArr[0].WrappedOriginalText;
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
if (originalLangCode == targetLangCode)
{
_logger.LogInformation("Original lyrics already in target language: {TargetLangCode}", targetLangCode);
ShowOriginalsOnly();
return;
}
// Try get translation from itself first
if (_multiLangLyrics.Count > 1)
{
foreach (var langLyrics in _multiLangLyrics.Skip(1))
{
var translationList = langLyrics.Select(x => x.OriginalText).ToList();
var translation = string.Join("\n", translationList);
if (LanguageHelper.DetectLanguageCode(translation) == targetLangCode)
{
_translationList = translationList;
break;
}
}
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
}
else
{
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
// Try get translation from itself first
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
if (found >= 0)
{
_dispatcherQueue.TryEnqueue(() =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("TranslateServerNotSet"),
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
);
});
ShowOriginalsOnly();
return;
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found]);
}
var translated = await _translateService.TranslateAsync(originalText, targetLangCode, token);
token.ThrowIfCancellationRequested();
_translationList = translated.Split('\n').ToList();
}
int i = 0;
foreach (var line in _multiLangLyrics.FirstOrDefault() ?? [])
{
line.DisplayedText = i < _translationList.Count ? $"{line.OriginalText}\n{_translationList[i]}" : line.OriginalText;
i++;
}
_isLayoutChanged = true;
}
private void ShowOriginalsOnly()
{
_logger.LogInformation("Showing original lyrics only, translation disabled.");
foreach (var langLyrics in _multiLangLyrics)
{
foreach (var line in langLyrics)
else
{
line.DisplayedText = line.OriginalText;
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
token.ThrowIfCancellationRequested();
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
}
}
_isLayoutChanged = true;
}
private async Task RefreshLyricsAsync(CancellationToken token)
{
_logger.LogInformation("Refreshing lyrics...");
SetLyricsLoadingPlaceholder();
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
_isLayoutChanged = true;
string? lyricsRaw = null;
@@ -448,33 +416,14 @@ namespace BetterLyrics.WinUI3.ViewModels
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
}
_multiLangLyrics = new LyricsParser().Parse(
lyricsRaw,
(int?)SongInfo?.DurationMs ?? (int)TimeSpan.FromMinutes(99).TotalMilliseconds
);
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _multiLangLyrics.Count);
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _lyricsDataArr.Count);
// This ensures that original lyrics are always shown while waiting for translations
ShowOriginalsOnly();
UpdateTranslations();
}
private void SetLyricsLoadingPlaceholder()
{
_multiLangLyrics = [];
_multiLangLyrics.Add(
[
new LyricsLine
{
StartMs = 0,
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
OriginalText = "● ● ●",
DisplayedText = "● ● ●",
CharTimings = [],
},
]
);
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
_isLayoutChanged = true;
UpdateTranslations();
}
}
}

View File

@@ -1,7 +1,5 @@
// 2025/6/23 by Zhe Fang
using System.Diagnostics;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
@@ -15,8 +13,11 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.UI;
using WinRT.Interop;
using WinUIEx;
namespace BetterLyrics.WinUI3
{
@@ -26,7 +27,8 @@ namespace BetterLyrics.WinUI3
IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<bool>>
{
private ForegroundWindowWatcher? _watcherHelper = null;
private ForegroundWindowWatcher? _windowWatcher = null;
private bool _ignoreFullscreenWindow = false;
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService)
{
@@ -49,24 +51,16 @@ namespace BetterLyrics.WinUI3
[NotifyPropertyChangedRecipients]
public partial bool IsLyricsWindowLocked { get; set; } = false;
[ObservableProperty]
public partial bool ShowInfoBar { get; set; } = false;
[ObservableProperty]
public partial ElementTheme ThemeType { get; set; } = ElementTheme.Default;
[ObservableProperty]
public partial double TitleBarFontSize { get; set; } = 11;
[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)
{
if (message.Sender is SystemTrayViewModel)
@@ -122,7 +116,7 @@ namespace BetterLyrics.WinUI3
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
_watcherHelper = new ForegroundWindowWatcher(
_windowWatcher = new ForegroundWindowWatcher(
hwnd,
onWindowChanged =>
{
@@ -133,10 +127,16 @@ namespace BetterLyrics.WinUI3
UpdateAccentColor(hwnd, mode);
}
);
_watcherHelper.Start();
_windowWatcher.Start();
UpdateAccentColor(hwnd, mode);
}
private void StopWatchWindowColorChange()
{
_windowWatcher?.Stop();
_windowWatcher = null;
}
public void UpdateAccentColor(nint hwnd, WindowPixelSampleMode mode)
{
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, mode).ToColor();
@@ -152,12 +152,6 @@ namespace BetterLyrics.WinUI3
IsLyricsWindowLocked = true;
}
private void StopWatchWindowColorChange()
{
_watcherHelper?.Stop();
_watcherHelper = null;
}
[RelayCommand]
private void ToggleDesktopMode()
{

View File

@@ -69,6 +69,7 @@ namespace BetterLyrics.WinUI3.ViewModels
LyricsFontSize = _settingsService.LyricsFontSize;
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
LyricsHighlightScope = _settingsService.LyricsHighlightScope;
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
LyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
@@ -86,6 +87,7 @@ namespace BetterLyrics.WinUI3.ViewModels
LyricsScrollEasingType = _settingsService.LyricsScrollEasingType;
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
TimelineSyncThreshold = _settingsService.TimelineSyncThreshold;
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
@@ -207,6 +209,10 @@ namespace BetterLyrics.WinUI3.ViewModels
[NotifyPropertyChangedRecipients]
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LineRenderingType LyricsHighlightScope { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial float LyricsLineSpacingFactor { get; set; }
@@ -218,7 +224,7 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; }
public string Version { get; set; } = Helper.MetadataHelper.AppVersion;
public string Version { get; set; } = MetadataHelper.AppVersion;
public string BuildDate { get; set; } = string.Empty;
@@ -248,25 +254,9 @@ namespace BetterLyrics.WinUI3.ViewModels
[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;
}
partial void OnLyricsFontStrokeWidthChanged(int value)
{
_settingsService.LyricsFontStrokeWidth = value;
}
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int TimelineSyncThreshold { get; set; }
public void OnLyricsSearchProvidersReordered()
{
@@ -288,11 +278,6 @@ namespace BetterLyrics.WinUI3.ViewModels
);
}
public void OpenMusicFolder(LocalLyricsFolder folder)
{
OpenFolderInFileExplorer(folder.Path);
}
public void RemoveFolderAsync(LocalLyricsFolder folder)
{
LocalLyricsFolders.Remove(folder);
@@ -371,31 +356,13 @@ namespace BetterLyrics.WinUI3.ViewModels
}
[RelayCommand]
private void OpenCacheFolder()
private static async Task OpenCacheFolderAsync()
{
OpenFolderInFileExplorer(PathHelper.CacheFolder);
}
private void OpenFolderInFileExplorer(string path)
{
Process.Start(
new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = path,
UseShellExecute = true,
}
);
await Launcher.LaunchFolderPathAsync(PathHelper.CacheFolder);
}
[RelayCommand]
private void PlayTestingMusicTask()
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
}
[RelayCommand]
private void RestartApp()
private static void RestartApp()
{
WindowHelper.RestartApp();
}
@@ -429,7 +396,7 @@ namespace BetterLyrics.WinUI3.ViewModels
try
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateAsync("Hello, world!", targetLangCode, null);
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
_dispatcherQueue.TryEnqueue(() =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageLibreTranslateTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
@@ -479,51 +446,58 @@ namespace BetterLyrics.WinUI3.ViewModels
return result;
}
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
{
_settingsService.LyricsScrollEasingType = value;
}
partial void OnLyricsScrollDurationChanged(int value)
{
_settingsService.LyricsScrollDuration = value;
}
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
{
_settingsService.LyricsBackgroundTheme = value;
}
partial void OnLyricsFontStrokeWidthChanged(int value)
{
_settingsService.LyricsFontStrokeWidth = value;
}
partial void OnIgnoreFullscreenWindowChanged(bool value)
{
_settingsService.IgnoreFullscreenWindow = value;
}
partial void OnSelectedTargetLanguageIndexChanged(int value)
{
_settingsService.SelectedTargetLanguageIndex = value;
}
partial void OnLibreTranslateServerChanged(string value)
{
_settingsService.LibreTranslateServer = value;
}
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
{
_settingsService.AutoStartWindowType = value;
}
partial void OnAutoLockOnDesktopModeChanged(bool value)
{
_settingsService.AutoLockOnDesktopMode = value;
}
partial void OnCoverImageRadiusChanged(int value)
{
_settingsService.CoverImageRadius = value;
}
partial void OnCoverOverlayBlurAmountChanged(int value)
{
_settingsService.CoverOverlayBlurAmount = value;
}
partial void OnCoverOverlayOpacityChanged(int value)
{
_settingsService.CoverOverlayOpacity = value;
}
partial void OnIsDynamicCoverOverlayEnabledChanged(bool value)
{
_settingsService.IsDynamicCoverOverlayEnabled = value;
}
partial void OnLanguageChanged(Enums.Language value)
{
switch (value)
@@ -551,85 +525,78 @@ namespace BetterLyrics.WinUI3.ViewModels
}
_settingsService.Language = Language;
}
partial void OnIsFanLyricsEnabledChanged(bool value)
{
_settingsService.IsFanLyricsEnabled = value;
}
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
{
_settingsService.IsLyricsGlowEffectEnabled = value;
}
partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value)
{
_settingsService.LyricsAlignmentType = value;
}
partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value)
{
_settingsService.SongInfoAlignmentType = value;
}
partial void OnLyricsBlurAmountChanged(int value)
{
_settingsService.LyricsBlurAmount = value;
}
partial void OnLyricsCustomBgFontColorChanged(Color value)
{
_settingsService.LyricsCustomBgFontColor = value;
}
partial void OnLyricsCustomFgFontColorChanged(Color value)
{
_settingsService.LyricsCustomFgFontColor = value;
}
partial void OnLyricsCustomStrokeFontColorChanged(Color value)
{
_settingsService.LyricsCustomStrokeFontColor = value;
}
partial void OnLyricsBgFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsBgFontColorType = value;
}
partial void OnLyricsFgFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsFgFontColorType = value;
}
partial void OnLyricsStrokeFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsStrokeFontColorType = value;
}
partial void OnLyricsFontSizeChanged(int value)
{
_settingsService.LyricsFontSize = value;
}
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
{
_settingsService.LyricsFontWeight = value;
}
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
{
_settingsService.LyricsGlowEffectScope = value;
}
partial void OnLyricsHighlightScopeChanged(LineRenderingType value)
{
_settingsService.LyricsHighlightScope = value;
}
partial void OnLyricsLineSpacingFactorChanged(float value)
{
_settingsService.LyricsLineSpacingFactor = value;
}
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
{
_settingsService.LyricsVerticalEdgeOpacity = value;
}
partial void OnTimelineSyncThresholdChanged(int value)
{
_settingsService.TimelineSyncThreshold = value;
}
}
}

View File

@@ -2,8 +2,7 @@
namespace BetterLyrics.WinUI3.ViewModels
{
public class SettingsWindowViewModel : BaseWindowViewModel
public partial class SettingsWindowViewModel(ISettingsService settingsService) : BaseWindowViewModel(settingsService)
{
public SettingsWindowViewModel(ISettingsService settingsService) : base(settingsService) { }
}
}

View File

@@ -8,10 +8,8 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class SystemTrayViewModel : BaseViewModel, IRecipient<PropertyChangedMessage<bool>>
public partial class SystemTrayViewModel(ISettingsService settingsService) : BaseViewModel(settingsService), IRecipient<PropertyChangedMessage<bool>>
{
public SystemTrayViewModel(ISettingsService settingsService) : base(settingsService) { }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLyricsWindowLocked { get; set; } = false;
@@ -34,15 +32,14 @@ namespace BetterLyrics.WinUI3.ViewModels
}
[RelayCommand]
private void ExitApp()
private static void ExitApp()
{
WindowHelper.ExitAllWindows();
}
[RelayCommand]
private void OpenSettings()
private static void OpenSettings()
{
// 打开设置窗口
WindowHelper.OpenOrShowWindow<SettingsWindow>();
}

View File

@@ -188,8 +188,8 @@
VerticalAlignment="Center"
Text="{x:Bind ViewModel.CoverOverlayBlurAmount, Mode=OneWay}" />
<Slider
Maximum="200"
Minimum="50"
Maximum="100"
Minimum="0"
SnapsTo="Ticks"
StepFrequency="10"
TickFrequency="10"
@@ -653,6 +653,14 @@
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsHighlightScope" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E6;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsHighlightScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPageLyricsGlowEffect"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
@@ -662,8 +670,9 @@
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageLyricsGlowEffectScope" IsEnabled="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsGlowEffectScopeCurrentLine" />
<ComboBoxItem x:Uid="SettingsPageLyricsGlowEffectScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
@@ -794,6 +803,7 @@
<!-- About -->
<controls:Case Value="About">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard Header="BetterLyrics" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Logo.png}">
<controls:SettingsCard.Description>
<RichTextBlock>
@@ -815,24 +825,52 @@
Glyph=&#xE943;}"
IsClickEnabled="True" />
<controls:SettingsCard x:Uid="SettingsPageQQGroup" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/QQ.png}">
<Button x:Uid="SettingsPageJoinNowButton" Click="QQGroupButton_Click" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDiscord" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Discord.png}">
<Button x:Uid="SettingsPageJoinNowButton" Click="DiscodGroupButton_Click" />
</controls:SettingsCard>
</StackPanel>
</controls:Case>
<!-- Dev -->
<controls:Case Value="Dev">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard x:Uid="SettingsPageMockMusicPlaying">
<HyperlinkButton
x:Uid="SettingsPagePlayingMockMusicButton"
Command="{x:Bind ViewModel.PlayTestingMusicTaskCommand}"
NavigateUri="https://soundcloud.com/carlyraejepsen/cut-to-the-feeling" />
<HyperlinkButton x:Uid="SettingsPagePlayingMockMusicButton" NavigateUri="https://soundcloud.com/carlyraejepsen/cut-to-the-feeling" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageCache">
<Button x:Uid="SettingsPageOpenLogFolderButton" Command="{x:Bind ViewModel.OpenCacheFolderCommand}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDebugOverlay">
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDebugOverlayEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold">
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.TimelineSyncThreshold, Mode=OneWay}" />
<TextBlock
Margin="0,0,14,0"
VerticalAlignment="Center"
Text=" ms" />
<Slider
Maximum="1000"
Minimum="0"
SnapsTo="Ticks"
StepFrequency="100"
TickFrequency="100"
TickPlacement="Outside"
Value="{x:Bind ViewModel.TimelineSyncThreshold, Mode=TwoWay}" />
</StackPanel>
</controls:SettingsCard>
</StackPanel>
</controls:Case>

View File

@@ -1,5 +1,6 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -121,5 +122,15 @@ namespace BetterLyrics.WinUI3.Views
}
}
}
private void QQGroupButton_Click(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri(MetadataHelper.QQGroupUrl));
}
private void DiscodGroupButton_Click(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri(MetadataHelper.DiscordUrl));
}
}
}