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