diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj
index b6cc731..37e72f5 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj
@@ -68,6 +68,10 @@
+
+
+
+
False
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsParser.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsParser.cs
index 5429a3f..e43f094 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsParser.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsParser.cs
@@ -11,9 +11,9 @@ namespace BetterLyrics.WinUI3.Helper
{
public class LyricsParser
{
- private List _lyricsLines = [];
+ private List> _multiLangLyricsLines = [];
- public List Parse(
+ public List> Parse(
string raw,
LyricsFormat? lyricsFormat = null,
string? title = null,
@@ -21,7 +21,7 @@ namespace BetterLyrics.WinUI3.Helper
int durationMs = 0
)
{
- _lyricsLines = [];
+ _multiLangLyricsLines = [];
switch (lyricsFormat)
{
case LyricsFormat.Lrc:
@@ -34,21 +34,24 @@ namespace BetterLyrics.WinUI3.Helper
default:
break;
}
+ return _multiLangLyricsLines;
+ }
- if (_lyricsLines.Count > 0 && _lyricsLines[0].StartMs > 0)
+ private void PostProcessLyricsLines(List lines)
+ {
+ if (lines.Count > 0 && lines[0].StartMs > 0)
{
- _lyricsLines.Insert(
+ lines.Insert(
0,
new LyricsLine
{
StartMs = 0,
- EndMs = _lyricsLines[0].StartMs,
- Texts = [""],
+ EndMs = lines[0].StartMs,
+ Text = "",
CharTimings = [],
}
);
}
- return _lyricsLines;
}
private void ParseLrc(string raw, int durationMs)
@@ -66,13 +69,15 @@ namespace BetterLyrics.WinUI3.Helper
{
var matches = syllableRegex.Matches(line);
var syllables = new List<(int, string)>();
- foreach (Match m in matches)
+ for (int i = 0; i < matches.Count; i++)
{
+ var m = matches[i];
int min = int.Parse(m.Groups[2].Value);
int sec = int.Parse(m.Groups[3].Value);
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
int totalMs = min * 60_000 + sec * 1000 + ms;
string text = m.Groups[6].Value;
+
syllables.Add((totalMs, text));
}
if (syllables.Count > 0)
@@ -105,37 +110,71 @@ namespace BetterLyrics.WinUI3.Helper
}
}
- // 按时间排序
- lrcLines = lrcLines.OrderBy(l => l.time).ToList();
+ // 按时间分组
+ var grouped = lrcLines.GroupBy(l => l.time).OrderBy(g => g.Key).ToList();
+ int languageCount = grouped.Max(g => g.Count());
- // 构建 LyricsLine
- for (int i = 0; i < lrcLines.Count; i++)
+ // 初始化每种语言的歌词列表
+ _multiLangLyricsLines.Clear();
+ for (int i = 0; i < languageCount; i++)
+ _multiLangLyricsLines.Add(new List());
+
+ // 遍历每个时间分组
+ foreach (var group in grouped)
{
- var (start, text, syllables) = lrcLines[i];
- var line = new LyricsLine
+ var linesInGroup = group.ToList();
+ for (int langIdx = 0; langIdx < languageCount; langIdx++)
{
- StartMs = start,
- EndMs = (i + 1 < lrcLines.Count) ? lrcLines[i + 1].time : durationMs,
- Texts = [text],
- CharTimings = [],
- };
-
- if (syllables != null && syllables.Count > 0)
- {
- for (int j = 0; j < syllables.Count; j++)
+ // 如果该语言有翻译,取对应行,否则用原文(第一行)
+ var (start, text, syllables) =
+ langIdx < linesInGroup.Count ? linesInGroup[langIdx] : linesInGroup[0];
+ var line = new LyricsLine
{
- var (charStart, charText) = syllables[j];
- int charEnd =
- (j + 1 < syllables.Count) ? syllables[j + 1].Item1 : line.EndMs;
- if (!string.IsNullOrEmpty(charText))
+ StartMs = start,
+ EndMs = 0, // 稍后统一修正
+ Text = text,
+ CharTimings = [],
+ };
+ if (syllables != null && syllables.Count > 0)
+ {
+ for (int j = 0; j < syllables.Count; j++)
{
+ var (charStart, charText) = syllables[j];
+ int charEnd = (j + 1 < syllables.Count) ? syllables[j + 1].Item1 : 0;
line.CharTimings.Add(
new CharTiming { StartMs = charStart, EndMs = charEnd }
);
}
}
+ _multiLangLyricsLines[langIdx].Add(line);
}
- _lyricsLines.Add(line);
+ }
+
+ // 修正 EndMs
+ for (int langIdx = 0; langIdx < languageCount; langIdx++)
+ {
+ var linesInSingleLang = _multiLangLyricsLines[langIdx];
+ for (int i = 0; i < linesInSingleLang.Count; i++)
+ {
+ if (i + 1 < linesInSingleLang.Count)
+ linesInSingleLang[i].EndMs = linesInSingleLang[i + 1].StartMs;
+ else
+ linesInSingleLang[i].EndMs = durationMs;
+
+ // 修正 CharTimings 的最后一个 EndMs
+ var timings = linesInSingleLang[i].CharTimings;
+ if (timings.Count > 0)
+ {
+ for (int j = 0; j < timings.Count; j++)
+ {
+ if (j + 1 < timings.Count)
+ timings[j].EndMs = timings[j + 1].StartMs;
+ else
+ timings[j].EndMs = linesInSingleLang[i].EndMs;
+ }
+ }
+ }
+ PostProcessLyricsLines(linesInSingleLang);
}
}
@@ -143,6 +182,7 @@ namespace BetterLyrics.WinUI3.Helper
{
try
{
+ List singleLangLyricsLine = [];
var xdoc = XDocument.Parse(raw);
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
if (body == null)
@@ -191,16 +231,18 @@ namespace BetterLyrics.WinUI3.Helper
if (spans.Count == 0)
text = p.Value.Trim();
- _lyricsLines.Add(
+ singleLangLyricsLine.Add(
new LyricsLine
{
StartMs = pStartMs,
EndMs = pEndMs,
- Texts = [text],
+ Text = text,
CharTimings = charTimings,
}
);
}
+ PostProcessLyricsLines(singleLangLyricsLine);
+ _multiLangLyricsLines.Add(singleLangLyricsLine);
}
catch
{
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs
new file mode 100644
index 0000000..c176122
--- /dev/null
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BetterLyrics.WinUI3.Models
+{
+ public class LyricsData
+ {
+ public int LanguageIndex { get; set; } = 0;
+
+ public List LyricsLines => MultiLangLyricsLines[LanguageIndex];
+ public List> MultiLangLyricsLines { get; set; } = [];
+ }
+}
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs
index 2147d28..82794f8 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs
@@ -7,11 +7,7 @@ namespace BetterLyrics.WinUI3.Models
{
public class LyricsLine
{
- public List Texts { get; set; } = [];
-
- public int LanguageIndex { get; set; } = 0;
-
- public string Text => Texts[LanguageIndex];
+ public string Text { get; set; } = "";
public List CharTimings { get; set; } = [];
@@ -40,8 +36,7 @@ namespace BetterLyrics.WinUI3.Models
{
return new LyricsLine
{
- Texts = new List(this.Texts),
- LanguageIndex = this.LanguageIndex,
+ Text = this.Text,
CharTimings = this.CharTimings,
StartMs = this.StartMs,
EndMs = this.EndMs,
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs
index 1eb8390..c2707b3 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs
@@ -42,7 +42,7 @@ namespace BetterLyrics.WinUI3.Services
)
)
{
- if (file.Contains(title) && file.Contains(artist))
+ if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
{
Track track = new(file);
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
@@ -152,6 +152,52 @@ namespace BetterLyrics.WinUI3.Services
return (null, null);
}
+ private static int LevenshteinDistance(string a, string b)
+ {
+ if (string.IsNullOrEmpty(a))
+ return b.Length;
+ if (string.IsNullOrEmpty(b))
+ return a.Length;
+ int[,] d = new int[a.Length + 1, b.Length + 1];
+ for (int i = 0; i <= a.Length; i++)
+ d[i, 0] = i;
+ for (int j = 0; j <= b.Length; j++)
+ d[0, j] = j;
+ for (int i = 1; i <= a.Length; i++)
+ for (int j = 1; j <= b.Length; j++)
+ d[i, j] = Math.Min(
+ Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
+ d[i - 1, j - 1] + (a[i - 1] == b[j - 1] ? 0 : 1)
+ );
+ return d[a.Length, b.Length];
+ }
+
+ // 判断相似度
+ private static bool FuzzyMatch(string fileName, string title, string artist)
+ {
+ var normFile = Normalize(fileName);
+ var normTarget1 = Normalize(title + artist);
+ var normTarget2 = Normalize(artist + title);
+
+ int dist1 = LevenshteinDistance(normFile, normTarget1);
+ int dist2 = LevenshteinDistance(normFile, normTarget2);
+
+ return dist1 <= 3 || dist2 <= 3; // 阈值可调整
+ }
+
+ private static string Normalize(string s)
+ {
+ if (string.IsNullOrWhiteSpace(s))
+ return "";
+ var sb = new StringBuilder();
+ foreach (var c in s.ToLowerInvariant())
+ {
+ if (char.IsLetterOrDigit(c))
+ sb.Append(c);
+ }
+ return sb.ToString();
+ }
+
private string? LocalLyricsSearchInMusicFiles(string title, string artist)
{
foreach (var folder in _settingsService.LocalLyricsFolders)
@@ -166,13 +212,15 @@ namespace BetterLyrics.WinUI3.Services
)
)
{
- if (file.Contains(title) && file.Contains(artist))
+ if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
{
+ //Track track = new(file);
+ //var plain = track.Lyrics.UnsynchronizedLyrics;
+
try
{
- // TODO: replace TagLib with ATL or another library that supports AOT
- string plain = TagLib.File.Create(file).Tag.Lyrics;
- if (plain != string.Empty)
+ var plain = TagLib.File.Create(file).Tag.Lyrics;
+ if (plain != null && plain != string.Empty)
{
return plain;
}
@@ -204,7 +252,7 @@ namespace BetterLyrics.WinUI3.Services
)
)
{
- if (file.Contains(title) && file.Contains(artist))
+ if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
{
string? raw = await File.ReadAllTextAsync(
file,
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs
index be60096..502ca5c 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs
@@ -10,6 +10,7 @@ using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
+using Windows.ApplicationModel;
using Windows.Media.Control;
using Windows.Storage.Streams;
@@ -164,6 +165,15 @@ namespace BetterLyrics.WinUI3.Services
SourceAppUserModelId = _currentSession?.SourceAppUserModelId,
};
+ if (
+ SongInfo.SourceAppUserModelId?.Contains(Package.Current.Id.FamilyName)
+ ?? false
+ )
+ {
+ SongInfo.Title = "甜度爆表";
+ SongInfo.Artist = "AI";
+ }
+
if (mediaProps?.Thumbnail is IRandomAccessStreamReference streamReference)
{
SongInfo.AlbumArt = await ImageHelper.ToByteArrayAsync(streamReference);
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw
index 12d8a8e..f3f2fa6 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw
@@ -483,4 +483,7 @@
Local .TTML files
+
+ This folder contains added folders, please delete these folders to add the folder
+
\ No newline at end of file
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw
index f1545aa..97251c5 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw
@@ -483,4 +483,7 @@
ローカル.TTMLファイル
+
+ このフォルダーには追加されたフォルダーが含まれています。これらのフォルダを削除してフォルダーを追加してください
+
\ No newline at end of file
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw
index ff0533d..bb6a7b6 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw
@@ -483,4 +483,7 @@
로컬 .TTML 파일
+
+ 이 폴더에는 추가 된 폴더가 포함되어 있습니다. 폴더를 추가하려면이 폴더를 삭제하십시오.
+
\ No newline at end of file
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw
index e747f7a..fc94a83 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw
@@ -483,4 +483,7 @@
本地 .TTML 文件
+
+ 该文件夹包含已添加文件夹,请删除这些文件夹以添加该文件夹
+
\ No newline at end of file
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw
index ce30747..fd74f82 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw
@@ -483,4 +483,7 @@
本地 .TTML 文件
+
+ 該文件夾包含已添加文件夾,請刪除這些文件夾以添加該文件夾
+
\ No newline at end of file
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs
index a45d531..44db46d 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs
@@ -63,7 +63,8 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial SongInfo? SongInfo { get; set; }
- private List _lyrics = [];
+ private List> _multiLangLyrics = [];
+ private int _langIndex = 0;
private List? _lyricsForGlowEffect = [];
@@ -215,7 +216,7 @@ namespace BetterLyrics.WinUI3.ViewModels
///
private async Task RefreshLyricsAsync()
{
- _lyrics = [];
+ _multiLangLyrics = [];
_isRelayoutNeeded = true;
LyricsStatus = LyricsStatus.Loading;
string? lyricsRaw = null;
@@ -237,7 +238,7 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else if (SongInfo != null)
{
- _lyrics = new LyricsParser().Parse(
+ _multiLangLyrics = new LyricsParser().Parse(
lyricsRaw,
lyricsFormat,
SongInfo.Title,
@@ -373,9 +374,9 @@ namespace BetterLyrics.WinUI3.ViewModels
private int GetCurrentPlayingLineIndex()
{
- for (int i = 0; i < _lyrics?.Count; i++)
+ for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
{
- var line = _lyrics?[i];
+ var line = _multiLangLyrics.SafeGet(_langIndex)?[i];
if (line?.EndMs < TotalTime.TotalMilliseconds)
{
continue;
@@ -394,12 +395,16 @@ namespace BetterLyrics.WinUI3.ViewModels
private Tuple GetMaxLyricsLineIndexBoundaries()
{
- if (SongInfo == null || _lyrics == null || _lyrics.Count == 0)
+ if (
+ SongInfo == null
+ || _multiLangLyrics.SafeGet(_langIndex) == null
+ || _multiLangLyrics[_langIndex].Count == 0
+ )
{
return new Tuple(-1, -1);
}
- return new Tuple(0, _lyrics.Count - 1);
+ return new Tuple(0, _multiLangLyrics[_langIndex].Count - 1);
}
private void DrawLyrics(
@@ -694,7 +699,7 @@ namespace BetterLyrics.WinUI3.ViewModels
DrawLyrics(
control,
lyricsDs,
- _lyrics,
+ _multiLangLyrics.SafeGet(_langIndex),
_defaultOpacity,
LyricsHighlightType.LineByLine
);
@@ -870,9 +875,9 @@ namespace BetterLyrics.WinUI3.ViewModels
float y = _topMargin;
// Init Positions
- for (int i = 0; i < _lyrics?.Count; i++)
+ for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
{
- var line = _lyrics.SafeGet(i);
+ var line = _multiLangLyrics[_langIndex].SafeGet(i);
if (line == null)
{
@@ -933,13 +938,16 @@ namespace BetterLyrics.WinUI3.ViewModels
_isRelayoutNeeded = false;
}
- UpdateLinesProps(_lyrics, _defaultOpacity);
+ UpdateLinesProps(_multiLangLyrics.SafeGet(_langIndex), _defaultOpacity);
UpdateCanvasYScrollOffset(control);
if (IsLyricsGlowEffectEnabled)
{
// Deep copy lyrics lines for glow effect
- _lyricsForGlowEffect = _lyrics?.Select(line => line.Clone()).ToList();
+ _lyricsForGlowEffect = _multiLangLyrics
+ .SafeGet(_langIndex)
+ ?.Select(line => line.Clone())
+ .ToList();
switch (LyricsGlowEffectScope)
{
case LyricsGlowEffectScope.WholeLyrics:
@@ -1124,7 +1132,9 @@ namespace BetterLyrics.WinUI3.ViewModels
}
// Set _scrollOffsetY
- LyricsLine? currentPlayingLine = _lyrics?[currentPlayingLineIndex];
+ LyricsLine? currentPlayingLine = _multiLangLyrics
+ .SafeGet(_langIndex)
+ ?[currentPlayingLineIndex];
if (currentPlayingLine == null)
{
@@ -1142,7 +1152,7 @@ namespace BetterLyrics.WinUI3.ViewModels
float targetYScrollOffset =
(float?)(
-currentPlayingLine.Position.Y
- + _lyrics?[0].Position.Y
+ + _multiLangLyrics.SafeGet(_langIndex)?[0].Position.Y
- playingTextLayout.LayoutBounds.Height / 2
) ?? 0f;
@@ -1159,9 +1169,14 @@ namespace BetterLyrics.WinUI3.ViewModels
_startVisibleLineIndex = _endVisibleLineIndex = -1;
// Update visible line indices
- for (int i = startLineIndex; i >= 0 && i <= endLineIndex && i < _lyrics?.Count; i++)
+ for (int i = startLineIndex; i <= endLineIndex; i++)
{
- var line = _lyrics?[i];
+ var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
+
+ if (line == null)
+ {
+ continue;
+ }
using var textLayout = new CanvasTextLayout(
control,
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsViewModel.cs
index 7fc48f8..9ea8953 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsViewModel.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsViewModel.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Enums;
@@ -13,8 +14,10 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
using Windows.ApplicationModel.Core;
using Windows.Globalization;
+using Windows.Media;
using Windows.Media.Playback;
using Windows.System;
using WinRT.Interop;
@@ -68,11 +71,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial Enums.Language Language { get; set; }
- private readonly MediaPlayer _mediaPlayer = new();
-
- private readonly ISettingsService _settingsService;
- private readonly ILibWatcherService _libWatcherService;
-
public string Version { get; set; } = AppInfo.AppVersion;
[ObservableProperty]
@@ -81,6 +79,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial Thickness RootGridMargin { get; set; } = new(0, 0, 0, 0);
+ private readonly MediaPlayer _mediaPlayer = new();
+ private readonly ISettingsService _settingsService;
+ private readonly ILibWatcherService _libWatcherService;
+ private readonly IPlaybackService _playbackService;
+
public SettingsViewModel(
ISettingsService settingsService,
ILibWatcherService libWatcherService,
@@ -89,6 +92,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_settingsService = settingsService;
_libWatcherService = libWatcherService;
+ _playbackService = playbackService;
RootGridMargin = new Thickness(0, _settingsService.TitleBarType.GetHeight(), 0, 0);
@@ -221,7 +225,20 @@ namespace BetterLyrics.WinUI3.ViewModels
private void AddFolderAsync(string path)
{
- if (LocalLyricsFolders.Any(x => x.Path == path))
+ var normalizedPath =
+ Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar)
+ + Path.DirectorySeparatorChar;
+
+ if (
+ LocalLyricsFolders.Any(x =>
+ Path.GetFullPath(x.Path)
+ .TrimEnd(Path.DirectorySeparatorChar)
+ .Equals(
+ normalizedPath.TrimEnd(Path.DirectorySeparatorChar),
+ StringComparison.OrdinalIgnoreCase
+ )
+ )
+ )
{
WeakReferenceMessenger.Default.Send(
new ShowNotificatonMessage(
@@ -231,8 +248,17 @@ namespace BetterLyrics.WinUI3.ViewModels
)
);
}
- else if (LocalLyricsFolders.Any((item) => path.StartsWith(item.Path)))
+ else if (
+ LocalLyricsFolders.Any(item =>
+ normalizedPath.StartsWith(
+ Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar)
+ + Path.DirectorySeparatorChar,
+ StringComparison.OrdinalIgnoreCase
+ )
+ )
+ )
{
+ // 添加的文件夹是现有文件夹的子文件夹
WeakReferenceMessenger.Default.Send(
new ShowNotificatonMessage(
new Notification(
@@ -241,12 +267,19 @@ namespace BetterLyrics.WinUI3.ViewModels
)
);
}
- else if (LocalLyricsFolders.Any((item) => item.Path.StartsWith(path)))
+ else if (
+ LocalLyricsFolders.Any(item =>
+ Path.GetFullPath(item.Path)
+ .TrimEnd(Path.DirectorySeparatorChar)
+ .StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)
+ )
+ )
{
+ // 添加的文件夹是现有文件夹的父文件夹
WeakReferenceMessenger.Default.Send(
new ShowNotificatonMessage(
new Notification(
- App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo")
+ App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo")
)
)
);
diff --git a/README.md b/README.md
index 3367727..10993b2 100644
--- a/README.md
+++ b/README.md
@@ -39,15 +39,43 @@ Your smooth dynamic local lyrics display built with WinUI 3
We provide more than one setting item to better align with your preference
-- Theme (light, dark, follow system)
+- Theme
+ - Light
+ - Dark
+ - Follow system
-- Backdrop (none, mica, acrylic, transparent)
+- Backdrop
+ - None
+ - Mica
+ - Acrylic
+ - Transparent
-- Album art as background (dynamic, blur amount, opacity)
+- Album art as background
+ - Dynamic
+ - Blur amount
+ - Opacity
-- Lyrics (alignment, font size, font color **(picked from album art accent color)** line spacing, opacity, blur amount, dynamic **glow** effect)
+- Album art as cover
+ - Corner radius
-- Language (English, Simplified Chinese, Traditional Chinese, Japanese, Korean)
+- Lyrics
+ - Alignment
+ - Font size
+ - Font color **(from album art accent color)**
+ - Line spacing
+ - Opacity
+ - Blur amount
+ - Dynamic **glow** effect
+ - Whole lyrics
+ - Line by line
+ - Word by word
+
+- Language
+ - English
+ - Simplified Chinese
+ - Traditional Chinese
+ - Japanese
+ - Korean
## Screenshots
@@ -72,7 +100,7 @@ We provide more than one setting item to better align with your preference
## Demonstration
-Watch our introduction video「BetterLyrics 阶段性开发成果展示」(uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
+Watch our introduction video (uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
## Try it now
@@ -109,11 +137,11 @@ To be added later.
- [LRCLIB](https://lrclib.net/)
- Online lyrics API provider
- [Audio Tools Library (ATL) for .NET](https://github.com/Zeugma440/atldotnet)
- - Local music file metadata extractor
+ - Used for extracting pictures in music files
- [WinUIEx](https://github.com/dotMorten/WinUIEx)
- Provide easy ways to access Win32 API regarding windowing
- [TagLib#](https://github.com/mono/taglib-sharp)
- - Previously used it as metadata extractor
+ - Used for reading original lyrics content
- [Stackoverflow - How to animate Margin property in WPF](https://stackoverflow.com/a/21542882/11048731)
- [DevWinUI](https://github.com/ghost1372/DevWinUI)
- [Bilibili -【WinUI3】SystemBackdropController:定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
@@ -149,7 +177,7 @@ To be added later.
-
+ S
```
## Star History