From 6f60952d09f58bcc675a55d9007ef2d1e571d3d9 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Thu, 8 Jan 2026 16:21:44 -0500 Subject: [PATCH] fix: lyrics cache was not updated when searching without enabling cache, lyrics animation issue --- .../Package.appxmanifest | 2 +- .../BetterLyrics.WinUI3/Constants/Time.cs | 1 + .../Controls/LyricsCanvas.xaml.cs | 29 +-- .../Controls/LyricsEffectSettingsControl.xaml | 8 + .../Controls/LyricsSearchControl.xaml | 2 +- .../Controls/NowPlayingBar.xaml | 2 +- .../Controls/PlaybackSettingsControl.xaml | 3 + .../Extensions/LyricsDataExtensions.cs | 18 +- .../Helper/MetadataComparer.cs | 35 ++-- .../Helper/ValueTransition.cs | 10 + .../Logic/LyricsAnimator.cs | 181 ++++++++++-------- .../Logic/LyricsLayoutManager.cs | 48 ++--- .../Logic/LyricsSynchronizer.cs | 32 ++-- .../Models/Lyrics/BaseLyrics.cs | 20 ++ .../Models/Lyrics/BaseRenderLyrics.cs | 22 +++ .../Models/Lyrics/LyricsChar.cs | 12 -- .../Models/Lyrics/LyricsData.cs | 4 +- .../Models/Lyrics/LyricsLine.cs | 51 +++-- .../Models/Lyrics/LyricsSyllable.cs | 19 -- .../Models/Lyrics/RenderLyricsChar.cs | 20 +- .../Models/Lyrics/RenderLyricsLine.cs | 135 +++++++------ .../Models/Lyrics/RenderLyricsSyllable.cs | 13 ++ .../Models/Settings/LyricsEffectSettings.cs | 2 + .../Settings/LyricsSearchProviderInfo.cs | 1 + .../Parsers/LyricsParser/LyricsParser.Lrc.cs | 10 +- .../LyricsParser/LyricsParser.QrcKrc.cs | 11 +- .../Parsers/LyricsParser/LyricsParser.Ttml.cs | 12 +- .../Parsers/LyricsParser/LyricsParser.cs | 12 +- .../Renderer/LyricsRenderer.cs | 26 +-- .../Renderer/PlayingLineRenderer.cs | 34 ++-- .../Renderer/UnplayingLineRenderer.cs | 18 +- .../GSMTCService.LyricsUpdater.cs | 2 +- .../LyricsCacheService/LyricsCacheService.cs | 16 +- .../ILyricsSearchService.cs | 2 +- .../LyricsSearchService.cs | 21 +- .../Strings/ar/Resources.resw | 34 ++-- .../Strings/de/Resources.resw | 34 ++-- .../Strings/en/Resources.resw | 3 + .../Strings/es/Resources.resw | 34 ++-- .../Strings/fr/Resources.resw | 34 ++-- .../Strings/hi/Resources.resw | 34 ++-- .../Strings/id/Resources.resw | 34 ++-- .../Strings/ja/Resources.resw | 34 ++-- .../Strings/ko/Resources.resw | 34 ++-- .../Strings/ms/Resources.resw | 34 ++-- .../Strings/pt/Resources.resw | 34 ++-- .../Strings/ru/Resources.resw | 34 ++-- .../Strings/th/Resources.resw | 34 ++-- .../Strings/vi/Resources.resw | 34 ++-- .../Strings/zh-Hans/Resources.resw | 37 ++-- .../Strings/zh-Hant/Resources.resw | 34 ++-- 51 files changed, 730 insertions(+), 620 deletions(-) create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseRenderLyrics.cs delete mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsChar.cs delete mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsSyllable.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsSyllable.cs diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index 3a487b9..1473f36 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.2.247.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs index 27bb86b..04ea75f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs @@ -6,6 +6,7 @@ namespace BetterLyrics.WinUI3.Constants { public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(250); public static readonly TimeSpan AnimationDuration = TimeSpan.FromMilliseconds(350); + public static readonly TimeSpan LongAnimationDuration = TimeSpan.FromMilliseconds(650); public static readonly TimeSpan WaitingDuration = TimeSpan.FromMilliseconds(300); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsCanvas.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsCanvas.xaml.cs index fa0f849..617750f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsCanvas.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsCanvas.xaml.cs @@ -119,7 +119,7 @@ namespace BetterLyrics.WinUI3.Controls private bool _isLayoutChanged = true; private bool _isMouseScrollingChanged = false; - private int _playingLineIndex; + private int _primaryPlayingLineIndex; private (int Start, int End) _visibleRange; private double _canvasTargetScrollOffset; @@ -382,7 +382,6 @@ namespace BetterLyrics.WinUI3.Controls control: sender, ds: args.DrawingSession, lines: _renderLyricsLines, - playingLineIndex: _playingLineIndex, mouseHoverLineIndex: _mouseHoverLineIndex, isMousePressing: _isMousePressing, startVisibleIndex: _visibleRange.Start, @@ -411,8 +410,6 @@ namespace BetterLyrics.WinUI3.Controls return _synchronizer.GetLinePlayingProgress( _songPositionWithOffset.TotalMilliseconds, line, - nextLine, - songDuration, isForceWordByWord ); } @@ -476,17 +473,17 @@ namespace BetterLyrics.WinUI3.Controls #region UpdatePlayingLineIndex - int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, _renderLyricsLines); - bool isPlayingLineChanged = newPlayingIndex != _playingLineIndex; - _playingLineIndex = newPlayingIndex; + int primaryPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, _renderLyricsLines); + bool isPrimaryPlayingLineChanged = primaryPlayingIndex != _primaryPlayingLineIndex; + _primaryPlayingLineIndex = primaryPlayingIndex; #endregion #region UpdateTargetScrollOffset - if (isPlayingLineChanged || _isLayoutChanged) + if (isPrimaryPlayingLineChanged || _isLayoutChanged) { - var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _playingLineIndex); + var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _primaryPlayingLineIndex); if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value; _canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType); @@ -524,7 +521,7 @@ namespace BetterLyrics.WinUI3.Controls _renderLyricsLines, _isMouseScrolling ? maxRange.Start : _visibleRange.Start, _isMouseScrolling ? maxRange.End : _visibleRange.End, - _playingLineIndex, + _primaryPlayingLineIndex, sender.Size.Height, _canvasTargetScrollOffset, lyricsStyle.PlayingLineTopOffset / 100.0, @@ -536,7 +533,7 @@ namespace BetterLyrics.WinUI3.Controls elapsedTime, _isMouseScrolling, _isLayoutChanged, - isPlayingLineChanged, + isPrimaryPlayingLineChanged, _isMouseScrollingChanged, _songPositionWithOffset.TotalMilliseconds ); @@ -669,15 +666,7 @@ namespace BetterLyrics.WinUI3.Controls private void UpdateRenderLyricsLines() { _renderLyricsLines = null; - var lines = _gsmtcService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine() - { - LyricsSyllables = x.LyricsSyllables, - StartMs = x.StartMs, - EndMs = x.EndMs, - PhoneticText = x.PhoneticText, - OriginalText = x.OriginalText, - TranslatedText = x.TranslatedText - }).ToList(); + var lines = _gsmtcService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine(x)).ToList(); if (lines != null) { LyricsLayoutManager.CalculateLanes(lines); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsEffectSettingsControl.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsEffectSettingsControl.xaml index 934af03..6883d36 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsEffectSettingsControl.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsEffectSettingsControl.xaml @@ -108,6 +108,14 @@ Minimum="0" Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationAmount, Mode=TwoWay}" /> + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsSearchControl.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsSearchControl.xaml index f4f44b1..5dc2acd 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsSearchControl.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/LyricsSearchControl.xaml @@ -291,7 +291,7 @@ - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/NowPlayingBar.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/NowPlayingBar.xaml index 0fe8a6b..86c3b40 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/NowPlayingBar.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/NowPlayingBar.xaml @@ -447,7 +447,7 @@ Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" /> - + + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs index 011748b..c5793c9 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs @@ -19,7 +19,7 @@ namespace BetterLyrics.WinUI3.Extensions { StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds, - OriginalText = "● ● ●", + PrimaryText = "● ● ●", }, ], LanguageCode = "N/A", @@ -37,12 +37,12 @@ namespace BetterLyrics.WinUI3.Extensions if (transLine != null) { // 此处 transLine.OriginalText 指翻译中的“原文”属性 - line.TranslatedText = transLine.OriginalText; + line.SecondaryText = transLine.PrimaryText; } else { // 没有匹配的翻译 - line.TranslatedText = ""; + line.SecondaryText = ""; } } } @@ -58,12 +58,12 @@ namespace BetterLyrics.WinUI3.Extensions if (transLine != null) { // 此处 transLine.OriginalText 指音译中的“原文”属性 - line.PhoneticText = transLine.OriginalText; + line.TertiaryText = transLine.PrimaryText; } else { // 没有匹配的音译 - line.PhoneticText = ""; + line.TertiaryText = ""; } } } @@ -76,11 +76,11 @@ namespace BetterLyrics.WinUI3.Extensions { if (i >= translationArr.Count) { - line.TranslatedText = ""; // No translation available, keep empty + line.SecondaryText = ""; // No translation available, keep empty } else { - line.TranslatedText = translationArr[i]; + line.SecondaryText = translationArr[i]; } i++; } @@ -94,11 +94,11 @@ namespace BetterLyrics.WinUI3.Extensions { if (i >= transliterationArr.Count) { - line.PhoneticText = ""; // No transliteration available, keep empty + line.TertiaryText = ""; // No transliteration available, keep empty } else { - line.PhoneticText = transliterationArr[i]; + line.TertiaryText = transliterationArr[i]; } i++; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataComparer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataComparer.cs index 1374c0f..3d5b343 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataComparer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataComparer.cs @@ -1,4 +1,5 @@ using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Models.Entities; using F23.StringSimilarity; using System; using System.IO; @@ -17,21 +18,31 @@ namespace BetterLyrics.WinUI3.Helper // JaroWinkler 适合短字符串匹配 private static readonly JaroWinkler _algo = new(); - public static int CalculateScore(SongInfo local, LyricsCacheItem remote) + public static int CalculateScore(SongInfo songInfo, LyricsCacheItem remote) { - if (local == null || remote == null) return 0; + return CalculateScore(songInfo, remote.Title, remote.Artist, remote.Album, remote.Duration * 1000); + } + public static int CalculateScore(SongInfo songInfo, FilesIndexItem local) + { + return CalculateScore(songInfo, local.Title, local.Artist, local.Album, local.Duration * 1000, local.FileName); + } + + public static int CalculateScore( + SongInfo songInfo, + string? compareTitle, string? compareArtist, string? compareAlbum, double? compareDurationMs, string? compareFileName = null) + { double totalScore = 0; - bool localHasMetadata = !string.IsNullOrWhiteSpace(local.Title); - bool remoteHasMetadata = !string.IsNullOrWhiteSpace(remote.Title); + bool localHasMetadata = !string.IsNullOrWhiteSpace(songInfo.Title); + bool remoteHasMetadata = !string.IsNullOrWhiteSpace(compareTitle); if (localHasMetadata && remoteHasMetadata) { - double titleScore = GetStringSimilarity(local.Title, remote.Title); - double artistScore = GetStringSimilarity(local.Artist, remote.Artist); - double albumScore = GetStringSimilarity(local.Album, remote.Album); - double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration); + double titleScore = GetStringSimilarity(songInfo.Title, compareTitle); + double artistScore = GetStringSimilarity(songInfo.Artist, compareArtist); + double albumScore = GetStringSimilarity(songInfo.Album, compareAlbum); + double durationScore = GetDurationSimilarity(songInfo.DurationMs, compareDurationMs); totalScore = (titleScore * WeightTitle) + (artistScore * WeightArtist) + @@ -41,12 +52,12 @@ namespace BetterLyrics.WinUI3.Helper else { string? localQuery = localHasMetadata - ? $"{local.Title} {local.Artist}" - : Path.GetFileNameWithoutExtension(local.LinkedFileName); + ? $"{songInfo.Title} {songInfo.Artist}" + : Path.GetFileNameWithoutExtension(songInfo.LinkedFileName); string? remoteQuery = remoteHasMetadata - ? $"{remote.Title} {remote.Artist}" - : null; + ? $"{compareTitle} {compareArtist}" + : Path.GetFileNameWithoutExtension(compareFileName); string fp1 = CreateSortedFingerprint(localQuery); string fp2 = CreateSortedFingerprint(remoteQuery); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs index a3e6f2f..452395b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs @@ -64,6 +64,16 @@ namespace BetterLyrics.WinUI3.Helper _durationSeconds = seconds; } + public void SetDurationMs(double millionSeconds) + { + SetDuration(millionSeconds / 1000.0); + } + + public void SetDuration(TimeSpan timeSpan) + { + SetDuration(timeSpan.TotalSeconds); + } + public void SetDelay(double seconds) { _delaySeconds = seconds; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs index ec8937e..7da6224 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs @@ -1,4 +1,5 @@ -using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Constants; +using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models.Lyrics; using BetterLyrics.WinUI3.Models.Settings; using DevWinUI; @@ -49,15 +50,15 @@ namespace BetterLyrics.WinUI3.Logic var line = lines.ElementAtOrDefault(i); if (line == null) continue; - bool isSecondaryLinePlaying = line.StartMs <= currentPositionMs && currentPositionMs <= line.EndMs; - if (i == primaryPlayingLineIndex) isSecondaryLinePlaying = true; + bool isSecondaryLinePlaying = line.GetIsPlaying(currentPositionMs); bool isSecondaryLinePlayingChanged = line.IsPlayingLastFrame != isSecondaryLinePlaying; line.IsPlayingLastFrame = isSecondaryLinePlaying; + // 行动画 if (isLayoutChanged || isPrimaryPlayingLineChanged || isMouseScrollingChanged || isSecondaryLinePlayingChanged) { int lineCountDelta = i - primaryPlayingLineIndex; - double distanceFromPlayingLine = Math.Abs(line.OriginalPosition.Y - currentPlayingLine.OriginalPosition.Y); + double distanceFromPlayingLine = Math.Abs(line.PrimaryPosition.Y - currentPlayingLine.PrimaryPosition.Y); double distanceFactor; if (lineCountDelta < 0) @@ -151,93 +152,115 @@ namespace BetterLyrics.WinUI3.Logic line.YOffsetTransition.StartTransition(targetYScrollOffset); } - if (line.RenderLyricsOriginalChars != null) + var maxAnimationDurationMs = Math.Max(line.EndMs - currentPositionMs, 0); + + // 字符动画 + foreach (var renderChar in line.PrimaryRenderChars) { - foreach (var renderChar in line.RenderLyricsOriginalChars) + renderChar.ProgressPlayed = renderChar.GetPlayProgress(currentPositionMs); + + bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs); + bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying; + + if (isSecondaryLinePlayingChanged || isCharPlayingChanged) { - var syllable = line.LyricsSyllables.FirstOrDefault(x => x.StartIndex <= renderChar.Index && renderChar.Index <= x.EndIndex); - if (syllable == null) continue; - - var avgCharDuration = syllable.DurationMs / syllable.Length; - if (avgCharDuration == null || avgCharDuration == 0) continue; - - var charStartMs = syllable.StartMs + (renderChar.Index - syllable.StartIndex) * avgCharDuration.Value; - var charEndMs = charStartMs + avgCharDuration; - var progressPlayed = (currentPositionMs - charStartMs) / avgCharDuration.Value; - progressPlayed = Math.Clamp(progressPlayed, 0, 1); - renderChar.ProgressPlayed = progressPlayed; - - bool isCharPlaying = charStartMs <= currentPositionMs && currentPositionMs <= charEndMs; - bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying; - - if (isSecondaryLinePlayingChanged || isCharPlayingChanged) + if (lyricsEffect.IsLyricsGlowEffectEnabled) { - if (lyricsEffect.IsLyricsScaleEffectEnabled) + double targetGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.2 : lyricsEffect.LyricsGlowEffectAmount; + switch (lyricsEffect.LyricsGlowEffectScope) { - double targetScale = - lyricsEffect.IsLyricsScaleEffectAmountAutoAdjust ? 1.15 : lyricsEffect.LyricsScaleEffectAmount / 100.0; - - if (isCharPlayingChanged) - { - if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration) + case Enums.LyricsEffectScope.LineStartToCurrentChar: + if (isSecondaryLinePlayingChanged) { - renderChar.ScaleTransition.SetDuration((syllable.DurationMs ?? 0) / 1000.0 / 2); - renderChar.ScaleTransition.StartTransition(isCharPlaying ? targetScale : 1); + renderChar.GlowTransition.SetDurationMs(Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs)); + renderChar.GlowTransition.StartTransition(isSecondaryLinePlaying ? targetGlow : 0); } - } + break; + default: + break; } - - if (lyricsEffect.IsLyricsGlowEffectEnabled) - { - double targetGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.2 : lyricsEffect.LyricsGlowEffectAmount; - switch (lyricsEffect.LyricsGlowEffectScope) - { - case Enums.LyricsEffectScope.LongDurationSyllable: - if (isCharPlayingChanged) - { - if (syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration) - { - renderChar.GlowTransition.SetDuration((syllable.DurationMs ?? 0) / 1000.0 / 2); - renderChar.GlowTransition.StartTransition(isCharPlaying ? targetGlow : 0); - } - } - break; - case Enums.LyricsEffectScope.LineStartToCurrentChar: - if (isSecondaryLinePlayingChanged) - { - renderChar.GlowTransition.SetDuration(renderChar.AnimationDuration); - renderChar.GlowTransition.StartTransition(isSecondaryLinePlaying ? targetGlow : 0); - } - break; - default: - break; - } - } - - if (lyricsEffect.IsLyricsFloatAnimationEnabled) - { - double targetFloat = - lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.1 : lyricsEffect.LyricsFloatAnimationAmount; - - if (isSecondaryLinePlayingChanged) - { - renderChar.FloatTransition.StartTransition(isSecondaryLinePlaying ? targetFloat : 0); - } - if (isCharPlayingChanged) - { - renderChar.FloatTransition.StartTransition(0); - } - } - - renderChar.IsPlayingLastFrame = isCharPlaying; } - renderChar.ScaleTransition.Update(elapsedTime); - renderChar.GlowTransition.Update(elapsedTime); - renderChar.FloatTransition.Update(elapsedTime); + if (lyricsEffect.IsLyricsFloatAnimationEnabled) + { + double targetFloat = + lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.1 : lyricsEffect.LyricsFloatAnimationAmount; + + if (isSecondaryLinePlayingChanged) + { + renderChar.FloatTransition.StartTransition(isSecondaryLinePlaying ? targetFloat : 0); + } + if (isCharPlayingChanged) + { + renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs)); + renderChar.FloatTransition.StartTransition(0); + } + } + + if (isCharPlayingChanged) + { + renderChar.IsPlayingLastFrame = isCharPlaying; + } } } + // 音节动画 + foreach (var syllable in line.PrimaryRenderSyllables) + { + bool isSyllablePlaying = syllable.GetIsPlaying(currentPositionMs); + bool isSyllablePlayingChanged = syllable.IsPlayingLastFrame != isSyllablePlaying; + + if (isSyllablePlayingChanged) + { + var syllableHeight = syllable.ChildrenRenderLyricsChars.FirstOrDefault()?.LayoutRect.Height ?? 0; + + if (lyricsEffect.IsLyricsScaleEffectEnabled) + { + double targetScale = + lyricsEffect.IsLyricsScaleEffectAmountAutoAdjust ? 1.15 : lyricsEffect.LyricsScaleEffectAmount / 100.0; + + foreach (var renderChar in syllable.ChildrenRenderLyricsChars) + { + if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration) + { + renderChar.ScaleTransition.SetDurationMs(Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0); + renderChar.ScaleTransition.StartTransition(isSyllablePlaying ? targetScale : 1); + } + } + } + + if (lyricsEffect.IsLyricsGlowEffectEnabled) + { + double targetGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust ? syllableHeight * 0.2 : lyricsEffect.LyricsGlowEffectAmount; + switch (lyricsEffect.LyricsGlowEffectScope) + { + case Enums.LyricsEffectScope.LongDurationSyllable: + if (syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration) + { + foreach (var renderChar in syllable.ChildrenRenderLyricsChars) + { + renderChar.GlowTransition.SetDurationMs(Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0); + renderChar.GlowTransition.StartTransition(isSyllablePlaying ? targetGlow : 0); + } + } + break; + default: + break; + } + } + + syllable.IsPlayingLastFrame = isSyllablePlaying; + } + } + + // 更新动画 + foreach (var renderChar in line.PrimaryRenderChars) + { + renderChar.ScaleTransition.Update(elapsedTime); + renderChar.GlowTransition.Update(elapsedTime); + renderChar.FloatTransition.Update(elapsedTime); + } + line.AngleTransition.Update(elapsedTime); line.ScaleTransition.Update(elapsedTime); line.BlurAmountTransition.Update(elapsedTime); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs index 3c8e342..786778d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs @@ -79,52 +79,52 @@ namespace BetterLyrics.WinUI3.Logic // 左上角坐标 line.TopLeftPosition = new Vector2(0, (float)currentY); // 注音层 - line.PhoneticPosition = line.TopLeftPosition; - if (line.PhoneticCanvasTextLayout != null) + line.TertiaryPosition = line.TopLeftPosition; + if (line.TertiaryTextLayout != null) { - currentY += line.PhoneticCanvasTextLayout.LayoutBounds.Height; + currentY += line.TertiaryTextLayout.LayoutBounds.Height; // 间距 - currentY += (line.PhoneticCanvasTextLayout.LayoutBounds.Height / line.PhoneticCanvasTextLayout.LineCount) * 0.1; + currentY += (line.TertiaryTextLayout.LayoutBounds.Height / line.TertiaryTextLayout.LineCount) * 0.1; - actualWidth = Math.Max(actualWidth, line.PhoneticCanvasTextLayout.LayoutBounds.Width); + actualWidth = Math.Max(actualWidth, line.TertiaryTextLayout.LayoutBounds.Width); } // 原文层 - line.OriginalPosition = new Vector2(0, (float)currentY); - if (line.OriginalCanvasTextLayout != null) + line.PrimaryPosition = new Vector2(0, (float)currentY); + if (line.PrimaryTextLayout != null) { - currentY += line.OriginalCanvasTextLayout.LayoutBounds.Height; + currentY += line.PrimaryTextLayout.LayoutBounds.Height; - actualWidth = Math.Max(actualWidth, line.OriginalCanvasTextLayout.LayoutBounds.Width); + actualWidth = Math.Max(actualWidth, line.PrimaryTextLayout.LayoutBounds.Width); } // 翻译层 - if (line.TranslatedCanvasTextLayout != null) + if (line.SecondaryTextLayout != null) { // 间距 - currentY += (line.TranslatedCanvasTextLayout.LayoutBounds.Height / line.TranslatedCanvasTextLayout.LineCount) * 0.1; + currentY += (line.SecondaryTextLayout.LayoutBounds.Height / line.SecondaryTextLayout.LineCount) * 0.1; } - line.TranslatedPosition = new Vector2(0, (float)currentY); - if (line.TranslatedCanvasTextLayout != null) + line.SecondaryPosition = new Vector2(0, (float)currentY); + if (line.SecondaryTextLayout != null) { - currentY += line.TranslatedCanvasTextLayout.LayoutBounds.Height; + currentY += line.SecondaryTextLayout.LayoutBounds.Height; - actualWidth = Math.Max(actualWidth, line.TranslatedCanvasTextLayout.LayoutBounds.Width); + actualWidth = Math.Max(actualWidth, line.SecondaryTextLayout.LayoutBounds.Width); } // 右下角坐标 line.BottomRightPosition = new Vector2(0 + (float)actualWidth, (float)currentY); // 行间距 - if (line.OriginalCanvasTextLayout != null) + if (line.PrimaryTextLayout != null) { - currentY += (line.OriginalCanvasTextLayout.LayoutBounds.Height / line.OriginalCanvasTextLayout.LineCount) * style.LyricsLineSpacingFactor; + currentY += (line.PrimaryTextLayout.LayoutBounds.Height / line.PrimaryTextLayout.LineCount) * style.LyricsLineSpacingFactor; } // 更新中心点 line.UpdateCenterPosition(lyricsWidth, style.LyricsAlignmentType); - line.RecalculateCharacterGeometries(); + line.RecreateRenderChars(); } } @@ -140,9 +140,9 @@ namespace BetterLyrics.WinUI3.Logic var currentLine = lines.ElementAtOrDefault(playingLineIndex); var firstLine = lines.FirstOrDefault(); - if (currentLine?.OriginalCanvasTextLayout == null || firstLine == null) return null; + if (currentLine?.PrimaryTextLayout == null || firstLine == null) return null; - return -currentLine.OriginalPosition.Y + firstLine.OriginalPosition.Y + return -currentLine.PrimaryPosition.Y + firstLine.PrimaryPosition.Y - (currentLine.BottomRightPosition.Y - currentLine.TopLeftPosition.Y) / 2.0; } @@ -215,7 +215,7 @@ namespace BetterLyrics.WinUI3.Logic lanesEndMs.Add(0); } - lanesEndMs[assignedLane] = end ?? 0; + lanesEndMs[assignedLane] = end; line.LaneIndex = assignedLane; } } @@ -241,7 +241,7 @@ namespace BetterLyrics.WinUI3.Logic { int mid = (left + right) / 2; var line = lines[mid]; - if (line.OriginalCanvasTextLayout == null) break; + if (line.PrimaryTextLayout == null) break; double value = offset + line.BottomRightPosition.Y; if (value >= mousePosition.Y) { result = mid; right = mid - 1; } else { left = mid + 1; } @@ -267,7 +267,7 @@ namespace BetterLyrics.WinUI3.Logic { int mid = (left + right) / 2; var line = lines[mid]; - if (line.OriginalCanvasTextLayout == null) break; + if (line.PrimaryTextLayout == null) break; double value = offset + line.BottomRightPosition.Y; // 理论上说应该使用下面这一行来精确计算视野内的首个可见行,但是考虑到动画视觉效果,还是注释掉了 //if (value >= lyricsY) { result = mid; right = mid - 1; } @@ -284,7 +284,7 @@ namespace BetterLyrics.WinUI3.Logic { int mid = (left + right) / 2; var line = lines[mid]; - if (line.OriginalCanvasTextLayout == null) break; + if (line.PrimaryTextLayout == null) break; double value = offset + line.BottomRightPosition.Y; // 同理 //if (value >= lyricsY + lyricsHeight) { result = mid; right = mid - 1; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs index 3fb0cec..2b05cce 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs @@ -66,19 +66,14 @@ namespace BetterLyrics.WinUI3.Logic public LinePlaybackState GetLinePlayingProgress( double currentTimeMs, - LyricsLine line, - LyricsLine? nextLine, - double songDurationMs, + RenderLyricsLine line, bool isForceWordByWord) { var state = new LinePlaybackState { SyllableStartIndex = 0, SyllableLength = 0, SyllableProgress = 0 }; if (line == null) return state; - double lineEndMs; - if (line.EndMs != null) lineEndMs = line.EndMs.Value; - else if (nextLine != null) lineEndMs = nextLine.StartMs; - else lineEndMs = songDurationMs; + double lineEndMs = line.EndMs; // 还没到 if (currentTimeMs < line.StartMs) return state; @@ -87,42 +82,43 @@ namespace BetterLyrics.WinUI3.Logic if (currentTimeMs > lineEndMs) { state.SyllableProgress = 1f; - state.SyllableStartIndex = Math.Max(0, line.OriginalText.Length - 1); + state.SyllableStartIndex = Math.Max(0, line.PrimaryText.Length - 1); state.SyllableLength = 1; return state; } // 逐字 - if (line.LyricsSyllables != null && line.LyricsSyllables.Count > 1) + if (line.PrimaryRenderSyllables != null && line.PrimaryRenderSyllables.Count > 1) { return CalculateSyllableProgress(currentTimeMs, line, lineEndMs); } // 强制逐字 - if (isForceWordByWord && line.OriginalText.Length > 0) + if (isForceWordByWord && line.PrimaryText.Length > 0) { return CalculateSimulatedProgress(currentTimeMs, line, lineEndMs); } else { // 普通行 - state.SyllableStartIndex = line.OriginalText.Length; + state.SyllableStartIndex = line.PrimaryText.Length; state.SyllableProgress = 1f; return state; } } - private LinePlaybackState CalculateSyllableProgress(double time, LyricsLine line, double lineEndMs) + private LinePlaybackState CalculateSyllableProgress(double time, RenderLyricsLine line, double lineEndMs) { var state = new LinePlaybackState(); - int count = line.LyricsSyllables.Count; + int count = line.PrimaryRenderSyllables.Count; for (int i = 0; i < count; i++) { - var timing = line.LyricsSyllables[i]; - var nextTiming = (i + 1 < count) ? line.LyricsSyllables[i + 1] : null; + var timing = line.PrimaryRenderSyllables[i]; + var nextTiming = (i + 1 < count) ? line.PrimaryRenderSyllables[i + 1] : null; - double timingEndMs = timing.EndMs ?? nextTiming?.StartMs ?? lineEndMs; + //double timingEndMs = timing.EndMs ?? nextTiming?.StartMs ?? lineEndMs; + double timingEndMs = timing.EndMs; // 在当前字范围内 if (time >= timing.StartMs && time <= timingEndMs) @@ -146,10 +142,10 @@ namespace BetterLyrics.WinUI3.Logic return state; } - private LinePlaybackState CalculateSimulatedProgress(double time, LyricsLine line, double lineEndMs) + private LinePlaybackState CalculateSimulatedProgress(double time, RenderLyricsLine line, double lineEndMs) { var state = new LinePlaybackState(); - int textLength = line.OriginalText.Length; + int textLength = line.PrimaryText.Length; double progress = (time - line.StartMs) / (lineEndMs - line.StartMs); progress = Math.Clamp(progress, 0, 1); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs new file mode 100644 index 0000000..fb4be92 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BetterLyrics.WinUI3.Models.Lyrics +{ + public class BaseLyrics + { + public int StartMs { get; set; } + public int EndMs { get; set; } + public int DurationMs => EndMs - StartMs; + + public string Text { get; set; } = ""; + public int Length => Text.Length; + + public int StartIndex { get; set; } + public int EndIndex => StartIndex + Length - 1; + + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseRenderLyrics.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseRenderLyrics.cs new file mode 100644 index 0000000..385d370 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseRenderLyrics.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BetterLyrics.WinUI3.Models.Lyrics +{ + public class BaseRenderLyrics : BaseLyrics + { + public bool IsPlayingLastFrame { get; set; } = false; + + public BaseRenderLyrics(BaseLyrics baseLyrics) + { + this.Text = baseLyrics.Text; + this.StartMs = baseLyrics.StartMs; + this.EndMs = baseLyrics.EndMs; + this.StartIndex = baseLyrics.StartIndex; + } + + public bool GetIsPlaying(double currentMs) => this.StartMs <= currentMs && currentMs < this.EndMs; + public double GetPlayProgress(double currentMs) => Math.Clamp((currentMs - this.StartMs) / this.DurationMs, 0, 1); + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsChar.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsChar.cs deleted file mode 100644 index d622c88..0000000 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsChar.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BetterLyrics.WinUI3.Models.Lyrics -{ - public class LyricsChar - { - public int StartMs { get; set; } - public int EndMs { get; set; } - public int DurationMs => EndMs - StartMs; - - public string Text { get; set; } = ""; - public int Index { get; set; } - } -} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsData.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsData.cs index c9d3fd6..99bfa48 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsData.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsData.cs @@ -18,7 +18,7 @@ namespace BetterLyrics.WinUI3.Models.Lyrics set => field = value; } public bool AutoGenerated { get; set; } = false; - public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText)); + public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.PrimaryText)); public LyricsData() { @@ -35,7 +35,7 @@ namespace BetterLyrics.WinUI3.Models.Lyrics { StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds, - OriginalText = _localizationService.GetLocalizedString("LyricsNotFound"), + PrimaryText = _localizationService.GetLocalizedString("LyricsNotFound"), }]); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs index ec097d9..bec7696 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs @@ -1,29 +1,46 @@ // 2025/6/23 by Zhe Fang using System.Collections.Generic; +using System.Linq; namespace BetterLyrics.WinUI3.Models.Lyrics { - public class LyricsLine + public class LyricsLine : BaseLyrics { - public List LyricsSyllables { get; set; } = []; + public List PrimarySyllables { get; set; } = []; + public List SecondarySyllables { get; set; } = []; + public List TertiarySyllables { get; set; } = []; - public int? DurationMs => EndMs - StartMs; - public int? EndMs { get; set; } - public int StartMs { get; set; } + public List PrimaryChars { get; private set; } = []; + public List SecondaryChars { get; private set; } = []; + public List TertiaryChars { get; private set; } = []; - /// - /// 原文 - /// - public string OriginalText { get; set; } = ""; - /// - /// 译文 - /// - public string TranslatedText { get; set; } = ""; - /// - /// 注音 - /// - public string PhoneticText { get; set; } = ""; + public string PrimaryText { get; set; } = ""; + public string SecondaryText { get; set; } = ""; + public string TertiaryText { get; set; } = ""; + + public LyricsLine() + { + for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++) + { + var syllable = PrimarySyllables.FirstOrDefault(x => x.StartIndex <= charStartIndex && charStartIndex <= x.EndIndex); + if (syllable == null) continue; + + var avgCharDuration = syllable.DurationMs / syllable.Length; + if (avgCharDuration == 0) continue; + + var charStartMs = syllable.StartMs + (charStartIndex - syllable.StartIndex) * avgCharDuration; + var charEndMs = charStartMs + avgCharDuration; + + PrimaryChars.Add(new BaseLyrics + { + StartIndex = charStartIndex, + StartMs = charStartMs, + EndMs = charEndMs, + Text = PrimaryText[charStartIndex].ToString() + }); + } + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsSyllable.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsSyllable.cs deleted file mode 100644 index 7bac5e6..0000000 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsSyllable.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 2025/6/23 by Zhe Fang - -namespace BetterLyrics.WinUI3.Models.Lyrics -{ - public class LyricsSyllable - { - public string Text { get; set; } = string.Empty; - public int Length => Text.Length; - - public int StartIndex { get; set; } - public int EndIndex => StartIndex + Length - 1; - - public int StartMs { get; set; } - public int? EndMs { get; set; } - - public int? DurationMs => EndMs - StartMs; - public bool IsLongDuration => DurationMs >= 700; - } -} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsChar.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsChar.cs index c961138..c0fa3c4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsChar.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsChar.cs @@ -1,14 +1,13 @@ -using BetterLyrics.WinUI3.Enums; +using BetterLyrics.WinUI3.Constants; +using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using Windows.Foundation; namespace BetterLyrics.WinUI3.Models.Lyrics { - public class RenderLyricsChar : LyricsChar + public class RenderLyricsChar : BaseRenderLyrics { - public Rect LayoutRect { get; set; } - - public double AnimationDuration { get; set; } = 0.3; + public Rect LayoutRect { get; private set; } public ValueTransition ScaleTransition { get; set; } public ValueTransition GlowTransition { get; set; } @@ -16,25 +15,24 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public double ProgressPlayed { get; set; } = 0; // 0~1 - public bool IsPlayingLastFrame { get; set; } = false; - - public RenderLyricsChar() + public RenderLyricsChar(BaseLyrics lyricsChars, Rect layoutRect) : base(lyricsChars) { ScaleTransition = new( initialValue: 1.0, - durationSeconds: AnimationDuration, + durationSeconds: Time.AnimationDuration.TotalSeconds, easingType: EasingType.EaseInOutSine ); GlowTransition = new( initialValue: 0, - durationSeconds: AnimationDuration, + durationSeconds: Time.AnimationDuration.TotalSeconds, easingType: EasingType.EaseInOutSine ); FloatTransition = new( initialValue: 0, - durationSeconds: AnimationDuration, + durationSeconds: Time.LongAnimationDuration.TotalSeconds, easingType: EasingType.EaseInOutSine ); + LayoutRect = layoutRect; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs index ba86cc7..210c0d3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs @@ -12,9 +12,10 @@ using Windows.UI; namespace BetterLyrics.WinUI3.Models.Lyrics { - public class RenderLyricsLine : LyricsLine + public class RenderLyricsLine : BaseRenderLyrics { - public List RenderLyricsOriginalChars { get; set; } = []; + public List PrimaryRenderChars { get; private set; } = []; + public List PrimaryRenderSyllables { get; private set; } public double AnimationDuration { get; set; } = 0.3; @@ -28,22 +29,22 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public ValueTransition YOffsetTransition { get; set; } public ValueTransition ColorTransition { get; set; } - public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; } - public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; } - public CanvasTextLayout? PhoneticCanvasTextLayout { get; private set; } + public CanvasTextLayout? PrimaryTextLayout { get; private set; } + public CanvasTextLayout? SecondaryTextLayout { get; private set; } + public CanvasTextLayout? TertiaryTextLayout { get; private set; } /// /// 原文坐标(相对于坐标原点) /// - public Vector2 OriginalPosition { get; set; } + public Vector2 PrimaryPosition { get; set; } /// /// 译文坐标(相对于坐标原点) /// - public Vector2 TranslatedPosition { get; set; } + public Vector2 SecondaryPosition { get; set; } /// /// 注音坐标(相对于坐标原点) /// - public Vector2 PhoneticPosition { get; set; } + public Vector2 TertiaryPosition { get; set; } /// /// 顶部坐标(相对于坐标原点) @@ -58,21 +59,21 @@ namespace BetterLyrics.WinUI3.Models.Lyrics /// public Vector2 BottomRightPosition { get; set; } - public CanvasGeometry? OriginalCanvasGeometry { get; private set; } - public CanvasGeometry? TranslatedCanvasGeometry { get; private set; } - public CanvasGeometry? PhoneticCanvasGeometry { get; private set; } + public CanvasGeometry? PrimaryCanvasGeometry { get; private set; } + public CanvasGeometry? SecondaryCanvasGeometry { get; private set; } + public CanvasGeometry? TertiaryCanvasGeometry { get; private set; } + + public string PrimaryText { get; set; } = ""; + public string SecondaryText { get; set; } = ""; + public string TertiaryText { get; set; } = ""; /// /// 轨道索引 (0 = 主轨道, 1 = 第一副轨道, etc.) /// 用于布局计算时的堆叠逻辑 /// public int LaneIndex { get; set; } = 0; - /// - /// 是否为背景人声/和声 - /// - public bool IsPlayingLastFrame { get; set; } = false; - public RenderLyricsLine() + public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine) { AngleTransition = new( initialValue: 0, @@ -119,11 +120,18 @@ namespace BetterLyrics.WinUI3.Models.Lyrics durationSeconds: 0.3f, interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to) ); + + StartMs = lyricsLine.StartMs; + EndMs = lyricsLine.EndMs; + TertiaryText = lyricsLine.TertiaryText; + PrimaryText = lyricsLine.PrimaryText; + SecondaryText = lyricsLine.SecondaryText; + PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList(); } public void UpdateCenterPosition(double maxWidth, TextAlignmentType type) { - if (OriginalCanvasTextLayout == null) + if (PrimaryTextLayout == null) { return; } @@ -141,14 +149,14 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public void DisposeTextLayout() { - PhoneticCanvasTextLayout?.Dispose(); - PhoneticCanvasTextLayout = null; + TertiaryTextLayout?.Dispose(); + TertiaryTextLayout = null; - OriginalCanvasTextLayout?.Dispose(); - OriginalCanvasTextLayout = null; + PrimaryTextLayout?.Dispose(); + PrimaryTextLayout = null; - TranslatedCanvasTextLayout?.Dispose(); - TranslatedCanvasTextLayout = null; + SecondaryTextLayout?.Dispose(); + SecondaryTextLayout = null; } public void RecreateTextLayout( @@ -161,9 +169,9 @@ namespace BetterLyrics.WinUI3.Models.Lyrics { DisposeTextLayout(); - if (createPhonetic && PhoneticText != "") + if (createPhonetic && TertiaryText != "") { - PhoneticCanvasTextLayout = new CanvasTextLayout(control, PhoneticText, new CanvasTextFormat + TertiaryTextLayout = new CanvasTextLayout(control, TertiaryText, new CanvasTextFormat { HorizontalAlignment = CanvasHorizontalAlignment.Left, VerticalAlignment = CanvasVerticalAlignment.Top, @@ -173,10 +181,10 @@ namespace BetterLyrics.WinUI3.Models.Lyrics { HorizontalAlignment = type.ToCanvasHorizontalAlignment(), }; - PhoneticCanvasTextLayout.SetFontFamily(PhoneticText, fontFamilyCJK, fontFamilyWestern); + TertiaryTextLayout.SetFontFamily(TertiaryText, fontFamilyCJK, fontFamilyWestern); } - OriginalCanvasTextLayout = new CanvasTextLayout(control, OriginalText, new CanvasTextFormat + PrimaryTextLayout = new CanvasTextLayout(control, PrimaryText, new CanvasTextFormat { HorizontalAlignment = CanvasHorizontalAlignment.Left, VerticalAlignment = CanvasVerticalAlignment.Top, @@ -186,11 +194,11 @@ namespace BetterLyrics.WinUI3.Models.Lyrics { HorizontalAlignment = type.ToCanvasHorizontalAlignment() }; - OriginalCanvasTextLayout.SetFontFamily(OriginalText, fontFamilyCJK, fontFamilyWestern); + PrimaryTextLayout.SetFontFamily(PrimaryText, fontFamilyCJK, fontFamilyWestern); - if (createTranslated && TranslatedText != "") + if (createTranslated && SecondaryText != "") { - TranslatedCanvasTextLayout = new CanvasTextLayout(control, TranslatedText, new CanvasTextFormat + SecondaryTextLayout = new CanvasTextLayout(control, SecondaryText, new CanvasTextFormat { HorizontalAlignment = CanvasHorizontalAlignment.Left, VerticalAlignment = CanvasVerticalAlignment.Top, @@ -200,60 +208,77 @@ namespace BetterLyrics.WinUI3.Models.Lyrics { HorizontalAlignment = type.ToCanvasHorizontalAlignment() }; - TranslatedCanvasTextLayout.SetFontFamily(TranslatedText, fontFamilyCJK, fontFamilyWestern); + SecondaryTextLayout.SetFontFamily(SecondaryText, fontFamilyCJK, fontFamilyWestern); } } public void DisposeTextGeometry() { - PhoneticCanvasGeometry?.Dispose(); - PhoneticCanvasGeometry = null; + TertiaryCanvasGeometry?.Dispose(); + TertiaryCanvasGeometry = null; - OriginalCanvasGeometry?.Dispose(); - OriginalCanvasGeometry = null; + PrimaryCanvasGeometry?.Dispose(); + PrimaryCanvasGeometry = null; - TranslatedCanvasGeometry?.Dispose(); - TranslatedCanvasGeometry = null; + SecondaryCanvasGeometry?.Dispose(); + SecondaryCanvasGeometry = null; } public void RecreateTextGeometry() { DisposeTextGeometry(); - if (PhoneticCanvasTextLayout != null) + if (TertiaryTextLayout != null) { - PhoneticCanvasGeometry = CanvasGeometry.CreateText(PhoneticCanvasTextLayout); + TertiaryCanvasGeometry = CanvasGeometry.CreateText(TertiaryTextLayout); } - if (OriginalCanvasTextLayout != null) + if (PrimaryTextLayout != null) { - OriginalCanvasGeometry = CanvasGeometry.CreateText(OriginalCanvasTextLayout); + PrimaryCanvasGeometry = CanvasGeometry.CreateText(PrimaryTextLayout); } - if (TranslatedCanvasTextLayout != null) + if (SecondaryTextLayout != null) { - TranslatedCanvasGeometry = CanvasGeometry.CreateText(TranslatedCanvasTextLayout); + SecondaryCanvasGeometry = CanvasGeometry.CreateText(SecondaryTextLayout); } } - public void RecalculateCharacterGeometries() + public void RecreateRenderChars() { - RenderLyricsOriginalChars.Clear(); - if (OriginalCanvasTextLayout == null) return; + PrimaryRenderChars.Clear(); + if (PrimaryTextLayout == null) return; - var textLength = OriginalText.Length; - - for (int i = 0; i < textLength; i++) + foreach (var syllable in PrimaryRenderSyllables) { - var region = OriginalCanvasTextLayout.GetCharacterRegions(i, 1).FirstOrDefault(); + syllable.ChildrenRenderLyricsChars.Clear(); + } + + var textLength = PrimaryText.Length; + + for (int startCharIndex = 0; startCharIndex < textLength; startCharIndex++) + { + var region = PrimaryTextLayout.GetCharacterRegions(startCharIndex, 1).FirstOrDefault(); var bounds = region.LayoutBounds; - RenderLyricsOriginalChars.Add(new RenderLyricsChar() + var syllable = PrimaryRenderSyllables.FirstOrDefault(x => x.StartIndex <= startCharIndex && startCharIndex <= x.EndIndex); + if (syllable == null) continue; + + var avgCharDuration = syllable.DurationMs / syllable.Length; + var charStartMs = syllable.StartMs + (startCharIndex - syllable.StartIndex) * avgCharDuration; + var charEndMs = charStartMs + avgCharDuration; + + var renderLyricsChar = new RenderLyricsChar(new BaseLyrics { - Index = i, - LayoutRect = bounds, - Text = OriginalText[i].ToString() - }); + StartIndex = startCharIndex, + Text = PrimaryText[startCharIndex].ToString(), + StartMs = charStartMs, + EndMs = charEndMs, + }, bounds); + + syllable.ChildrenRenderLyricsChars.Add(renderLyricsChar); + + PrimaryRenderChars.Add(renderLyricsChar); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsSyllable.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsSyllable.cs new file mode 100644 index 0000000..479df8f --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsSyllable.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BetterLyrics.WinUI3.Models.Lyrics +{ + public class RenderLyricsSyllable : BaseRenderLyrics + { + public List ChildrenRenderLyricsChars { get; set; } = []; + + public RenderLyricsSyllable(BaseLyrics lyricsSyllable) : base(lyricsSyllable) { } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsEffectSettings.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsEffectSettings.cs index fbaceee..5be05ce 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsEffectSettings.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsEffectSettings.cs @@ -24,6 +24,7 @@ namespace BetterLyrics.WinUI3.Models.Settings [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true; [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationAmountAutoAdjust { get; set; } = true; [ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFloatAnimationAmount { get; set; } = 8; + [ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFloatAnimationDuration { get; set; } = 450; // 450ms [ObservableProperty][NotifyPropertyChangedRecipients] public partial EasingType LyricsScrollEasingType { get; set; } [ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; } @@ -71,6 +72,7 @@ namespace BetterLyrics.WinUI3.Models.Settings IsLyricsFloatAnimationEnabled = this.IsLyricsFloatAnimationEnabled, IsLyricsFloatAnimationAmountAutoAdjust = this.IsLyricsFloatAnimationAmountAutoAdjust, LyricsFloatAnimationAmount = this.LyricsFloatAnimationAmount, + LyricsFloatAnimationDuration = this.LyricsFloatAnimationDuration, LyricsScrollEasingType = this.LyricsScrollEasingType, LyricsScrollDuration = this.LyricsScrollDuration, diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsSearchProviderInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsSearchProviderInfo.cs index f642239..95ef27f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsSearchProviderInfo.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Settings/LyricsSearchProviderInfo.cs @@ -11,6 +11,7 @@ namespace BetterLyrics.WinUI3.Models.Settings [ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; } [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMatchingThresholdOverwritten { get; set; } = false; [ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 40; + [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IgnoreCacheWhenSearching { get; set; } = false; public LyricsSearchProviderInfo() { } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs index 7fe881c..3439efb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs @@ -24,7 +24,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser foreach (var line in lines) { var matches = syllableRegex.Matches(line); - var syllables = new List(); + var syllables = new List(); int startIndex = 0; for (int i = 0; i < matches.Count; i++) @@ -36,7 +36,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser int totalMs = min * 60_000 + sec * 1000 + ms; string text = match.Groups[6].Value; - syllables.Add(new LyricsSyllable { StartMs = totalMs, Text = text, StartIndex = startIndex }); + syllables.Add(new BaseLyrics { StartMs = totalMs, Text = text, StartIndex = startIndex }); startIndex += text.Length; } @@ -58,8 +58,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = syllables[0].StartMs, EndMs = lineEndMs, - OriginalText = string.Concat(syllables.Select(s => s.Text)), - LyricsSyllables = syllables + PrimaryText = string.Concat(syllables.Select(s => s.Text)), + PrimarySyllables = syllables }); } else @@ -81,7 +81,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser content = bracketRegex!.Replace(line, "").Trim(); if (content == "//") content = ""; - lrcLines.Add(new LyricsLine { StartMs = lineStartMs, OriginalText = content }); + lrcLines.Add(new LyricsLine { StartMs = lineStartMs, PrimaryText = content }); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs index 41a4a25..7087ac6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs @@ -17,12 +17,13 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++) { var lineRead = lines[lineIndex]; + var nextLineRead = lines.ElementAtOrDefault(lineIndex + 1); var lineWrite = new LyricsLine { StartMs = lineRead.StartTime ?? 0, - EndMs = lineRead.EndTime ?? 0, - OriginalText = lineRead.Text, - LyricsSyllables = [], + EndMs = lineRead.EndTime ?? (nextLineRead?.StartTime ?? 0), + PrimaryText = lineRead.Text, + PrimarySyllables = [], }; var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables; @@ -36,14 +37,14 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser ) { var syllable = syllables[syllableIndex]; - var charTiming = new LyricsSyllable + var charTiming = new BaseLyrics { StartMs = syllable.StartTime, EndMs = syllable.EndTime, Text = syllable.Text, StartIndex = startIndex, }; - lineWrite.LyricsSyllables.Add(charTiming); + lineWrite.PrimarySyllables.Add(charTiming); startIndex += syllable.Text.Length; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs index e9e42ff..b9ff6f5 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs @@ -93,7 +93,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser } } - var syllables = new List(); + var syllables = new List(); int startIndex = 0; var sbText = new System.Text.StringBuilder(); @@ -103,7 +103,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser int sEndMs = ParseTtmlTime(span.Attribute("end")?.Value); string text = span.Value; - syllables.Add(new LyricsSyllable + syllables.Add(new BaseLyrics { StartMs = sStartMs, EndMs = sEndMs, @@ -126,8 +126,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = containerStartMs, EndMs = containerEndMs, - OriginalText = fullOriginalText, - LyricsSyllables = syllables + PrimaryText = fullOriginalText, + PrimarySyllables = syllables }); var transSpan = container.Elements() @@ -151,7 +151,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = startMs, EndMs = endMs, - OriginalText = text + PrimaryText = text }); } else @@ -160,7 +160,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = startMs, EndMs = endMs, - OriginalText = "" + PrimaryText = "" }); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs index 5a2095f..4748be6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs @@ -167,14 +167,14 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { foreach (var item in main.LyricsLines) { - item.OriginalText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.OriginalText) : ChineseHelper.ToSC(item.OriginalText); + item.PrimaryText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.PrimaryText) : ChineseHelper.ToSC(item.PrimaryText); } } if (settings.SelectedTargetLanguageCode == LanguageHelper.ChineseCode) { foreach (var item in main.LyricsLines) { - item.TranslatedText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.TranslatedText) : ChineseHelper.ToSC(item.TranslatedText); + item.SecondaryText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.SecondaryText) : ChineseHelper.ToSC(item.SecondaryText); } } @@ -235,8 +235,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = line.StartMs, EndMs = line.EndMs, - OriginalText = PhoneticHelper.ToPinyin(line.OriginalText), - LyricsSyllables = line.LyricsSyllables.Select(c => new LyricsSyllable + PrimaryText = PhoneticHelper.ToPinyin(line.PrimaryText), + PrimarySyllables = line.PrimarySyllables.Select(c => new BaseLyrics { StartMs = c.StartMs, EndMs = c.EndMs, @@ -256,8 +256,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = line.StartMs, EndMs = line.EndMs, - OriginalText = PhoneticHelper.ToJyutping(line.OriginalText), - LyricsSyllables = line.LyricsSyllables.Select(c => new LyricsSyllable + PrimaryText = PhoneticHelper.ToJyutping(line.PrimaryText), + PrimarySyllables = line.PrimarySyllables.Select(c => new BaseLyrics { StartMs = c.StartMs, EndMs = c.EndMs, diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.cs index dd37de4..8246374 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.cs @@ -26,7 +26,6 @@ namespace BetterLyrics.WinUI3.Renderer ICanvasAnimatedControl control, CanvasDrawingSession ds, IList? lines, - int playingLineIndex, int mouseHoverLineIndex, bool isMousePressing, int startVisibleIndex, @@ -57,7 +56,6 @@ namespace BetterLyrics.WinUI3.Renderer control, layerDs, lines, - playingLineIndex, mouseHoverLineIndex, isMousePressing, startVisibleIndex, @@ -89,7 +87,6 @@ namespace BetterLyrics.WinUI3.Renderer control, ds, lines, - playingLineIndex, mouseHoverLineIndex, isMousePressing, startVisibleIndex, @@ -114,7 +111,6 @@ namespace BetterLyrics.WinUI3.Renderer ICanvasAnimatedControl control, CanvasDrawingSession ds, IList? lines, - int playingLineIndex, int mouseHoverLineIndex, bool isMousePressing, int startVisibleIndex, @@ -134,9 +130,6 @@ namespace BetterLyrics.WinUI3.Renderer { if (lines == null) return; - var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex); - if (currentPlayingLine == null) return; - var effectSettings = windowStatus.LyricsEffectSettings; var styleSettings = windowStatus.LyricsStyleSettings; @@ -148,8 +141,8 @@ namespace BetterLyrics.WinUI3.Renderer var line = lines.ElementAtOrDefault(i); if (line == null) continue; - if (line.OriginalCanvasTextLayout == null) continue; - if (line.OriginalCanvasTextLayout.LayoutBounds.Width <= 0) continue; + if (line.PrimaryTextLayout == null) continue; + if (line.PrimaryTextLayout.LayoutBounds.Width <= 0) continue; double xOffset = lyricsX; double yOffset = line.YOffsetTransition.Value + userScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor; @@ -167,8 +160,7 @@ namespace BetterLyrics.WinUI3.Renderer using (var textOnlyLayer = RenderBaseTextLayer(control, line, styleSettings.LyricsFontStrokeWidth, strokeColor, line.ColorTransition.Value)) { - bool isPlaying = currentProgressMs >= line.StartMs && currentProgressMs <= line.EndMs; - if (i == playingLineIndex) isPlaying = true; + bool isPlaying = line.GetIsPlaying(currentProgressMs); if (isPlaying) { @@ -206,14 +198,14 @@ namespace BetterLyrics.WinUI3.Renderer { if (strokeWidth > 0) { - DrawGeometrySafely(clds, line.PhoneticCanvasGeometry, line.PhoneticPosition, strokeColor, strokeWidth); - DrawGeometrySafely(clds, line.OriginalCanvasGeometry, line.OriginalPosition, strokeColor, strokeWidth); - DrawGeometrySafely(clds, line.TranslatedCanvasGeometry, line.TranslatedPosition, strokeColor, strokeWidth); + DrawGeometrySafely(clds, line.TertiaryCanvasGeometry, line.TertiaryPosition, strokeColor, strokeWidth); + DrawGeometrySafely(clds, line.PrimaryCanvasGeometry, line.PrimaryPosition, strokeColor, strokeWidth); + DrawGeometrySafely(clds, line.SecondaryCanvasGeometry, line.SecondaryPosition, strokeColor, strokeWidth); } - DrawTextLayoutSafely(clds, line.PhoneticCanvasTextLayout, line.PhoneticPosition, fillColor); - DrawTextLayoutSafely(clds, line.OriginalCanvasTextLayout, line.OriginalPosition, fillColor); - DrawTextLayoutSafely(clds, line.TranslatedCanvasTextLayout, line.TranslatedPosition, fillColor); + DrawTextLayoutSafely(clds, line.TertiaryTextLayout, line.TertiaryPosition, fillColor); + DrawTextLayoutSafely(clds, line.PrimaryTextLayout, line.PrimaryPosition, fillColor); + DrawTextLayoutSafely(clds, line.SecondaryTextLayout, line.SecondaryPosition, fillColor); } return commandList; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs index 05a5aba..41125e6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs @@ -33,17 +33,17 @@ namespace BetterLyrics.WinUI3.Renderer private void DrawPhonetic(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line) { - if (line.PhoneticCanvasTextLayout == null) return; + if (line.TertiaryTextLayout == null) return; var opacity = line.PhoneticOpacityTransition.Value; var blur = line.BlurAmountTransition.Value; - var bounds = line.PhoneticCanvasTextLayout.LayoutBounds; + var bounds = line.TertiaryTextLayout.LayoutBounds; if (double.IsNaN(opacity)) return; var destRect = new Rect( - bounds.X + line.PhoneticPosition.X, - bounds.Y + line.PhoneticPosition.Y, + bounds.X + line.TertiaryPosition.X, + bounds.Y + line.TertiaryPosition.Y, bounds.Width, bounds.Height ); @@ -67,17 +67,17 @@ namespace BetterLyrics.WinUI3.Renderer private void DrawTranslated(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line) { - if (line.TranslatedCanvasTextLayout == null) return; + if (line.SecondaryTextLayout == null) return; var opacity = line.TranslatedOpacityTransition.Value; var blur = line.BlurAmountTransition.Value; - var bounds = line.TranslatedCanvasTextLayout.LayoutBounds; + var bounds = line.SecondaryTextLayout.LayoutBounds; if (double.IsNaN(opacity)) return; var destRect = new Rect( - bounds.X + line.TranslatedPosition.X, - bounds.Y + line.TranslatedPosition.Y, + bounds.X + line.SecondaryPosition.X, + bounds.Y + line.SecondaryPosition.Y, bounds.Width, bounds.Height ); @@ -109,12 +109,12 @@ namespace BetterLyrics.WinUI3.Renderer Color fgColor, LyricsEffectSettings settings) { - if (line.OriginalCanvasTextLayout == null) return; + if (line.PrimaryTextLayout == null) return; var curCharIndex = state.SyllableStartIndex + state.SyllableLength * state.SyllableProgress; - float fadeWidth = (1f / Math.Max(1, line.OriginalText.Length)) * 0.5f; + float fadeWidth = (1f / Math.Max(1, line.PrimaryText.Length)) * 0.5f; - var lineRegions = line.OriginalCanvasTextLayout.GetCharacterRegions(0, line.OriginalText.Length); + var lineRegions = line.PrimaryTextLayout.GetCharacterRegions(0, line.PrimaryText.Length); foreach (var subLineRegion in lineRegions) { @@ -139,8 +139,8 @@ namespace BetterLyrics.WinUI3.Renderer var subLineLayoutBounds = subLineRegion.LayoutBounds; Rect subLineRect = new( - subLineLayoutBounds.X + line.OriginalPosition.X, - subLineLayoutBounds.Y + line.OriginalPosition.Y, + subLineLayoutBounds.X + line.PrimaryPosition.X, + subLineLayoutBounds.Y + line.PrimaryPosition.Y, subLineLayoutBounds.Width, subLineLayoutBounds.Height ); @@ -208,14 +208,14 @@ namespace BetterLyrics.WinUI3.Renderer int charIndex, ICanvasImage source) { - if (charIndex >= line.RenderLyricsOriginalChars.Count) return; + if (charIndex >= line.PrimaryRenderChars.Count) return; - RenderLyricsChar renderChar = line.RenderLyricsOriginalChars[charIndex]; + RenderLyricsChar renderChar = line.PrimaryRenderChars[charIndex]; var rect = renderChar.LayoutRect; var sourceCharRect = new Rect( - rect.X + line.OriginalPosition.X, - rect.Y + line.OriginalPosition.Y, + rect.X + line.PrimaryPosition.X, + rect.Y + line.PrimaryPosition.Y, rect.Width, rect.Height ); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/UnplayingLineRenderer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/UnplayingLineRenderer.cs index b9cf074..db67ac5 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/UnplayingLineRenderer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/UnplayingLineRenderer.cs @@ -17,32 +17,32 @@ namespace BetterLyrics.WinUI3.Renderer { var blurAmount = (float)line.BlurAmountTransition.Value; - if (line.PhoneticCanvasTextLayout != null) + if (line.TertiaryTextLayout != null) { var opacity = line.PhoneticOpacityTransition.Value; DrawPart(ds, textOnlyLayer, - line.PhoneticCanvasTextLayout, - line.PhoneticPosition, + line.TertiaryTextLayout, + line.TertiaryPosition, blurAmount, (float)opacity); } - if (line.OriginalCanvasTextLayout != null) + if (line.PrimaryTextLayout != null) { double opacity = Math.Max(line.PlayedOriginalOpacityTransition.Value, line.UnplayedOriginalOpacityTransition.Value); DrawPart(ds, textOnlyLayer, - line.OriginalCanvasTextLayout, - line.OriginalPosition, + line.PrimaryTextLayout, + line.PrimaryPosition, blurAmount, (float)opacity); } - if (line.TranslatedCanvasTextLayout != null) + if (line.SecondaryTextLayout != null) { var opacity = line.TranslatedOpacityTransition.Value; DrawPart(ds, textOnlyLayer, - line.TranslatedCanvasTextLayout, - line.TranslatedPosition, + line.SecondaryTextLayout, + line.SecondaryPosition, blurAmount, (float)opacity); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/GSMTCService/GSMTCService.LyricsUpdater.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/GSMTCService/GSMTCService.LyricsUpdater.cs index 6e07066..049ff95 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/GSMTCService/GSMTCService.LyricsUpdater.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/GSMTCService/GSMTCService.LyricsUpdater.cs @@ -28,7 +28,7 @@ namespace BetterLyrics.WinUI3.Services.GSMTCService if (CurrentSongInfo != SongInfoExtensions.Placeholder) { CurrentLyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync( - CurrentSongInfo, true, CurrentMediaSourceProviderInfo?.LyricsSearchType, token), token); + CurrentSongInfo, CurrentMediaSourceProviderInfo?.LyricsSearchType, token), token); if (CurrentLyricsSearchResult != null) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsCacheService/LyricsCacheService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsCacheService/LyricsCacheService.cs index da0e998..9612d91 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsCacheService/LyricsCacheService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsCacheService/LyricsCacheService.cs @@ -32,7 +32,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsCacheService } /// - /// Write cache to DB + /// Write or update cache to DB /// public async Task SaveLyricsAsync(SongInfo songInfo, LyricsCacheItem result) { @@ -52,8 +52,18 @@ namespace BetterLyrics.WinUI3.Services.LyricsCacheService } else { - // No need to handle this case - return; + existingItem.Title = result.Title; + existingItem.Artist = result.Artist; + existingItem.Album = result.Album; + + existingItem.TransliterationProvider = result.TransliterationProvider; + existingItem.TranslationProvider = result.TranslationProvider; + + existingItem.Raw = result.Raw; + existingItem.Translation = result.Translation; + + existingItem.MatchPercentage = result.MatchPercentage; + existingItem.Reference = result.Reference; } await context.SaveChangesAsync(); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs index e7f1c42..7af4196 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs @@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService { public interface ILyricsSearchService { - Task SearchSmartlyAsync(SongInfo songInfo, bool checkCache, LyricsSearchType? lyricsSearchType, CancellationToken token); + Task SearchSmartlyAsync(SongInfo songInfo, LyricsSearchType? lyricsSearchType, CancellationToken token); Task> SearchAllAsync(SongInfo songInfo, bool checkCache, CancellationToken token); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs index d3f019d..d02f462 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs @@ -104,7 +104,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService } } - public async Task SearchSmartlyAsync(SongInfo songInfo, bool checkCache, LyricsSearchType? lyricsSearchType, CancellationToken token) + public async Task SearchSmartlyAsync(SongInfo songInfo, LyricsSearchType? lyricsSearchType, CancellationToken token) { if (lyricsSearchType == null) { @@ -112,8 +112,6 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService } var lyricsSearchResult = new LyricsCacheItem(); - //lyricsSearchResult.Raw = File.ReadAllText("C:\\Users\\Zhe\\Desktop\\星河回响 (Tech Demo).lrc"); - //return lyricsSearchResult; string overridenTitle = songInfo.Title; string overridenArtist = songInfo.Artist; @@ -150,7 +148,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService .WithTitle(overridenTitle) .WithArtist(overridenArtist) .WithAlbum(overridenAlbum), - targetProvider.Value, checkCache, token); + targetProvider.Value, true, token); } } @@ -172,7 +170,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService .WithTitle(overridenTitle) .WithArtist(overridenArtist) .WithAlbum(overridenAlbum), - provider.Provider, checkCache, token); + provider.Provider, !provider.IgnoreCacheWhenSearching, token); int matchingThreshold = mediaSourceProviderInfo.MatchingThreshold; if (provider.IsMatchingThresholdOverwritten) @@ -288,7 +286,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService private async Task SearchFile(SongInfo songInfo, LyricsFormat format) { - int maxScore = 0; + int maxScore = -1; FilesIndexItem? bestFileEntity = null; MediaFolder? bestFolderConfig = null; @@ -316,7 +314,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService { if (item.FileName.EndsWith(targetExt, StringComparison.OrdinalIgnoreCase)) { - int score = MetadataComparer.CalculateScore(songInfo, new LyricsCacheItem { Reference = item.FileName }); + int score = MetadataComparer.CalculateScore(songInfo, item); if (score > maxScore) { @@ -363,13 +361,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService { if (string.IsNullOrEmpty(item.EmbeddedLyrics)) continue; - int score = MetadataComparer.CalculateScore(songInfo, new LyricsCacheItem - { - Title = item.Title, - Artist = item.Artist, - Album = item.Album, - Duration = item.Duration - }); + int score = MetadataComparer.CalculateScore(songInfo, item); if (score > maxScore) { @@ -444,6 +436,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService Title = title, Artist = artist, Album = album, + Duration = 0, }); if (score > lyricsSearchResult.MatchPercentage) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ar/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ar/Resources.resw index d6fb2c8..a61435e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ar/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ar/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/de/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/de/Resources.resw index 17aaa44..d1c7efb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/de/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/de/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en/Resources.resw index a96ea27..029e22e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en/Resources.resw @@ -243,6 +243,9 @@ e.g. http://localhost:5000 + + Duration + Loading lyrics... diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/es/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/es/Resources.resw index b6aaf6a..352822e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/es/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/es/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/fr/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/fr/Resources.resw index 33a6a46..35186ff 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/fr/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/fr/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/hi/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/hi/Resources.resw index 07df67b..d9505d8 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/hi/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/hi/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/id/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/id/Resources.resw index a5538e2..2bc2f27 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/id/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/id/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja/Resources.resw index 8ec90d1..60a9737 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko/Resources.resw index d02aee4..9ccc429 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ms/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ms/Resources.resw index bb6b927..6289ceb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ms/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ms/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/pt/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/pt/Resources.resw index 53997c6..ec955c6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/pt/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/pt/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ru/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ru/Resources.resw index 8f951ed..26cf55c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ru/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ru/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/th/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/th/Resources.resw index 9570db6..46e4165 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/th/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/th/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/vi/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/vi/Resources.resw index ddee18f..fed2c08 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/vi/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/vi/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hans/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hans/Resources.resw index 92c39ca..a2620cb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hans/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hans/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - + @@ -243,6 +243,9 @@ 例如 http://localhost:5000 + + 持续时间 + 加载歌词中... diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hant/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hant/Resources.resw index fb85a5d..06cfd6c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hant/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-Hant/Resources.resw @@ -59,46 +59,46 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - - + + - + - - - - + + + + - - + + - - + + - - - - + + + + - + - +