From 352ceca81d4dc8216a89b3ba5e3d824e543938bb Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Fri, 4 Jul 2025 07:25:38 -0400 Subject: [PATCH] fix --- .../Package.appxmanifest | 2 +- .../BetterLyrics.WinUI3/App.xaml.cs | 6 +- .../BetterLyrics.WinUI3.csproj | 2 + .../BetterLyrics.WinUI3/Enums/EasingType.cs | 15 +- .../Enums/LyricsAlignmentType.cs | 11 - .../Enums/TextAlignmentType.cs | 28 + .../MediaSourceProvidersInfoEventArgs.cs | 14 + .../Helper/AnimationHelper.cs | 68 ++- .../BetterLyrics.WinUI3/Helper/ColorHelper.cs | 5 + .../Helper/EasingHelper.cs | 90 ++- .../BetterLyrics.WinUI3/Models/LyricsLine.cs | 11 +- .../Models/LyricsSearchProviderInfo.cs | 8 +- .../Models/MediaSourceProviderInfo.cs | 25 + .../BetterLyrics.WinUI3/Models/SongInfo.cs | 6 +- .../Serialization/SourceGenerationContext.cs | 1 + .../Services/IMusicSearchService.cs | 5 +- .../Services/IPlaybackService.cs | 6 +- .../Services/ISettingsService.cs | 6 +- .../Services/MusicSearchService.cs | 103 ++-- .../Services/PlaybackService.cs | 316 +++++----- .../Services/SettingsService.cs | 38 +- .../Strings/en-US/Resources.resw | 32 +- .../Strings/ja-JP/Resources.resw | 32 +- .../Strings/ko-KR/Resources.resw | 32 +- .../Strings/zh-CN/Resources.resw | 32 +- .../Strings/zh-TW/Resources.resw | 32 +- .../ViewModels/LyricsPageViewModel.cs | 96 +-- .../LyricsRendererViewModel.Draw.cs | 548 +++++++++--------- .../LyricsRendererViewModel.Messages.cs | 159 ++--- .../LyricsRendererViewModel.Transition.cs | 56 ++ .../LyricsRendererViewModel.Update.cs | 204 ++++--- .../ViewModels/LyricsRendererViewModel.cs | 225 ++++--- .../LyricsSettingsControlViewModel.cs | 130 ----- .../ViewModels/LyricsWindowViewModel.cs | 5 +- .../ViewModels/SettingsPageViewModel.cs | 153 ++++- .../BetterLyrics.WinUI3/Views/LyricsPage.xaml | 302 +--------- .../Views/LyricsPage.xaml.cs | 119 +--- .../Views/LyricsWindow.xaml | 2 + .../Views/SettingsPage.xaml | 288 +++++---- .../Views/SettingsPage.xaml.cs | 28 +- 40 files changed, 1634 insertions(+), 1607 deletions(-) delete mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/LyricsAlignmentType.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/TextAlignmentType.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/MediaSourceProvidersInfoEventArgs.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs delete mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsSettingsControlViewModel.cs diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index a3182e1..7bdcfe8 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="1.0.8.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs index 6d49f44..a7a0da9 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Text; using System.Threading.Tasks; -using BetterInAppLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Services; @@ -94,7 +93,6 @@ namespace BetterLyrics.WinUI3 .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .BuildServiceProvider() ); } @@ -107,7 +105,7 @@ namespace BetterLyrics.WinUI3 private void CurrentDomain_FirstChanceException(object? sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e) { - //_logger.LogError(e.Exception, "CurrentDomain_FirstChanceException"); + _logger.LogError(e.Exception, "CurrentDomain_FirstChanceException"); } private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) @@ -117,7 +115,7 @@ namespace BetterLyrics.WinUI3 private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - //_logger.LogError(e.Exception, "TaskScheduler_UnobservedTaskException"); + _logger.LogError(e.Exception, "TaskScheduler_UnobservedTaskException"); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj index 7e70b6f..754f9dd 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj @@ -40,7 +40,9 @@ + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/EasingType.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/EasingType.cs index f2feab1..72818d7 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/EasingType.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/EasingType.cs @@ -4,12 +4,17 @@ namespace BetterLyrics.WinUI3.Enums { public enum EasingType { - EaseInOutQuad, - EaseInQuad, - EaseOutQuad, - EaseInOutExpo, Linear, SmoothStep, - SmootherStep, + EaseInOutSine, + EaseInOutQuad, + EaseInOutCubic, + EaseInOutQuart, + EaseInOutQuint, + EaseInOutExpo, + EaseInOutCirc, + EaseInOutBack, + EaseInOutElastic, + EaseInOutBounce, } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/LyricsAlignmentType.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/LyricsAlignmentType.cs deleted file mode 100644 index a71b689..0000000 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/LyricsAlignmentType.cs +++ /dev/null @@ -1,11 +0,0 @@ -// 2025/6/23 by Zhe Fang - -namespace BetterLyrics.WinUI3.Enums -{ - public enum LyricsAlignmentType - { - Left, - Center, - Right, - } -} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/TextAlignmentType.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/TextAlignmentType.cs new file mode 100644 index 0000000..96c33f3 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/TextAlignmentType.cs @@ -0,0 +1,28 @@ +// 2025/6/23 by Zhe Fang + +using Microsoft.Graphics.Canvas.Text; +using System; + +namespace BetterLyrics.WinUI3.Enums +{ + public enum TextAlignmentType + { + Left, + Center, + Right, + } + + public static class LyricsAlignmentTypeExtensions + { + public static CanvasHorizontalAlignment ToCanvasHorizontalAlignment(this TextAlignmentType alignmentType) + { + return alignmentType switch + { + TextAlignmentType.Left => CanvasHorizontalAlignment.Left, + TextAlignmentType.Center => CanvasHorizontalAlignment.Center, + TextAlignmentType.Right => CanvasHorizontalAlignment.Right, + _ => throw new ArgumentOutOfRangeException(nameof(alignmentType), alignmentType, null), + }; + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/MediaSourceProvidersInfoEventArgs.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/MediaSourceProvidersInfoEventArgs.cs new file mode 100644 index 0000000..892430d --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/MediaSourceProvidersInfoEventArgs.cs @@ -0,0 +1,14 @@ +using BetterLyrics.WinUI3.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Events +{ + public class MediaSourceProvidersInfoEventArgs(List sessionIds):EventArgs + { + public List MediaSourceProviersInfo { get; set; } = sessionIds; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/AnimationHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/AnimationHelper.cs index 1197c2e..ccaca3e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/AnimationHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/AnimationHelper.cs @@ -1,6 +1,7 @@ // 2025/6/23 by Zhe Fang using System; +using System.Diagnostics; using BetterLyrics.WinUI3.Enums; namespace BetterLyrics.WinUI3.Helper @@ -17,13 +18,15 @@ namespace BetterLyrics.WinUI3.Helper { private T _currentValue; private float _durationSeconds; - private readonly EasingType? _easingType; + private EasingType? _easingType; private Func _interpolator; private bool _isTransitioning; private float _progress; private T _startValue; private T _targetValue; + public float DurationSeconds => _durationSeconds; + public bool IsTransitioning => _isTransitioning; public T Value => _currentValue; @@ -44,16 +47,16 @@ namespace BetterLyrics.WinUI3.Helper else if (easingType.HasValue) { _easingType = easingType; - _interpolator = GetInterpolatorByEasingType(easingType.Value); + _interpolator = GetInterpolatorByEasingType(_easingType.Value); } else { - _interpolator = GetInterpolatorByEasingType(EasingType.Linear); - _easingType = EasingType.Linear; + _easingType = EasingType.SmoothStep; + _interpolator = GetInterpolatorByEasingType(_easingType.Value); } } - public void JumpTo(T value) + private void JumpTo(T value) { _currentValue = value; _startValue = value; @@ -71,8 +74,14 @@ namespace BetterLyrics.WinUI3.Helper _isTransitioning = false; } - public void StartTransition(T targetValue) + public void StartTransition(T targetValue, bool jumpTo = false) { + if (jumpTo) + { + JumpTo(targetValue); + return; + } + if (!targetValue.Equals(_currentValue)) { _startValue = _currentValue; @@ -82,6 +91,12 @@ namespace BetterLyrics.WinUI3.Helper } } + public static bool Equals(double x, double y, double tolerance) + { + var diff = Math.Abs(x - y); + return diff <= tolerance || diff <= Math.Max(Math.Abs(x), Math.Abs(y)) * tolerance; + } + public void Update(TimeSpan elapsedTime) { if (!_isTransitioning) return; @@ -110,26 +125,41 @@ namespace BetterLyrics.WinUI3.Helper float t = progress; switch (type) { - case EasingType.EaseInOutExpo: - t = EasingHelper.EaseInOutExpo(t); + case EasingType.EaseInOutSine: + t = EasingHelper.EaseInOutSine(t); break; case EasingType.EaseInOutQuad: t = EasingHelper.EaseInOutQuad(t); break; - case EasingType.EaseInQuad: - t = EasingHelper.EaseInQuad(t); + case EasingType.EaseInOutCubic: + t = EasingHelper.EaseInOutCubic(t); break; - case EasingType.EaseOutQuad: - t = EasingHelper.EaseOutQuad(t); + case EasingType.EaseInOutQuart: + t = EasingHelper.EaseInOutQuart(t); break; - case EasingType.Linear: - t = EasingHelper.Linear(t); + case EasingType.EaseInOutQuint: + t = EasingHelper.EaseInOutQuint(t); + break; + case EasingType.EaseInOutExpo: + t = EasingHelper.EaseInOutExpo(t); + break; + case EasingType.EaseInOutCirc: + t = EasingHelper.EaseInOutCirc(t); + break; + case EasingType.EaseInOutBack: + t = EasingHelper.EaseInOutBack(t); + break; + case EasingType.EaseInOutElastic: + t = EasingHelper.EaseInOutElastic(t); + break; + case EasingType.EaseInOutBounce: + t = EasingHelper.EaseInOutBounce(t); break; case EasingType.SmoothStep: t = EasingHelper.SmoothStep(t); break; - case EasingType.SmootherStep: - t = EasingHelper.SmootherStep(t); + case EasingType.Linear: + t = EasingHelper.Linear(t); break; default: break; @@ -139,5 +169,11 @@ namespace BetterLyrics.WinUI3.Helper } throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}."); } + + public void SetEasingType(EasingType easingType) + { + _easingType = easingType; + _interpolator = GetInterpolatorByEasingType(easingType); + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ColorHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ColorHelper.cs index 9eeddef..918b04a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ColorHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ColorHelper.cs @@ -81,5 +81,10 @@ namespace BetterLyrics.WinUI3.Helper { return Color.FromArgb(color.A, color.R, color.G, color.B); } + + public static Color WithAlpha(this Color color, byte alpha) + { + return Color.FromArgb(alpha, color.R, color.G, color.B); + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/EasingHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/EasingHelper.cs index b8997ec..10e3147 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/EasingHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/EasingHelper.cs @@ -10,6 +10,29 @@ namespace BetterLyrics.WinUI3.Helper { public class EasingHelper { + public static float EaseInOutSine(float t) + { + return -(MathF.Cos(MathF.PI * t) - 1f) / 2f; + } + public static float EaseInOutQuad(float t) + { + return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t; + } + + public static float EaseInOutCubic(float t) + { + return t < 0.5f ? 4 * t * t * t : 1 - MathF.Pow(-2 * t + 2, 3) / 2; + } + public static float EaseInOutQuart(float t) + { + return t < 0.5f ? 8 * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 4) / 2; + } + + public static float EaseInOutQuint(float t) + { + return t < 0.5f ? 16 * t * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 5) / 2; + } + public static float EaseInOutExpo(float t) { return t == 0 @@ -20,25 +43,70 @@ namespace BetterLyrics.WinUI3.Helper : (2 - MathF.Pow(2, -20 * t + 10)) / 2; } - public static float EaseInOutQuad(float t) + public static float EaseInOutCirc(float t) { - return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t; + return t < 0.5f + ? (1 - MathF.Sqrt(1 - MathF.Pow(2 * t, 2))) / 2 + : (MathF.Sqrt(1 - MathF.Pow(-2 * t + 2, 2)) + 1) / 2; } - public static float EaseInQuad(float t) => t * t; - - public static float EaseOutQuad(float t) => t * (2 - t); - - public static float Linear(float t) => t; - - public static float SmootherStep(float t) + public static float EaseInOutBack(float t) { - return t * t * t * (t * (6 * t - 15) + 10); + float c1 = 1.70158f; + float c2 = c1 * 1.525f; + + return t < 0.5 + ? (MathF.Pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2 + : (MathF.Pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2; + } + + public static float EaseInOutElastic(float t) + { + if (t == 0 || t == 1) return t; + float p = 0.3f; + float s = p / 4; + return t < 0.5f + ? -(MathF.Pow(2, 20 * t - 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2 + : (MathF.Pow(2, -20 * t + 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2 + 1; + } + + private static float EaseOutBounce(float t) + { + if (t < 4 / 11f) + { + return (121 * t * t) / 16f; + } + else if (t < 8 / 11f) + { + return (363 / 40f * t * t) - (99 / 10f * t) + 17 / 5f; + } + else if (t < 9 / 10f) + { + return (4356 / 361f * t * t) - (35442 / 1805f * t) + 16061 / 1805f; + } + else + { + return (54 / 5f * t * t) - (513 / 25f * t) + 268 / 25f; + } + } + + public static float EaseInOutBounce(float t) + { + if (t < 0.5f) + { + return (1 - EaseOutBounce(1 - 2 * t)) / 2; + } + else + { + return (1 + EaseOutBounce(2 * t - 1)) / 2; + } } public static float SmoothStep(float t) { - return t * t * (3 - 2 * t); + return t * t * (3f - 2f * t); } + + public static float Linear(float t) => t; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs index 4be2170..fe8e64b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLine.cs @@ -9,11 +9,12 @@ namespace BetterLyrics.WinUI3.Models { public class LyricsLine { - public ValueTransition AngleTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f); - public ValueTransition BlurAmountTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f); - public ValueTransition HighlightOpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f); - public ValueTransition OpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f); - public ValueTransition ScaleTransition { get; set; } = new(initialValue: 0.95f, durationSeconds: 0.3f); + private const float _animationDuration = 0.5f; + public ValueTransition AngleTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration); + public ValueTransition BlurAmountTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration); + public ValueTransition HighlightOpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration); + public ValueTransition OpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration); + public ValueTransition ScaleTransition { get; set; } = new(initialValue: 0.95f, durationSeconds: _animationDuration); public CanvasTextLayout? CanvasTextLayout { get; set; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsSearchProviderInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsSearchProviderInfo.cs index 9b2b3a8..410890d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsSearchProviderInfo.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsSearchProviderInfo.cs @@ -5,17 +5,17 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace BetterLyrics.WinUI3.Models { - public partial class LyricsSearchProviderInfo : ObservableObject + public partial class MediaSourceProviderInfo : ObservableObject { [ObservableProperty] public partial bool IsEnabled { get; set; } [ObservableProperty] - public partial LyricsSearchProvider Provider { get; set; } + public partial string Provider { get; set; } - public LyricsSearchProviderInfo() { } + public MediaSourceProviderInfo() { } - public LyricsSearchProviderInfo(LyricsSearchProvider provider, bool isEnabled) + public MediaSourceProviderInfo(string provider, bool isEnabled) { Provider = provider; IsEnabled = isEnabled; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs new file mode 100644 index 0000000..9b2b3a8 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs @@ -0,0 +1,25 @@ +// 2025/6/23 by Zhe Fang + +using BetterLyrics.WinUI3.Enums; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace BetterLyrics.WinUI3.Models +{ + public partial class LyricsSearchProviderInfo : ObservableObject + { + [ObservableProperty] + public partial bool IsEnabled { get; set; } + + [ObservableProperty] + public partial LyricsSearchProvider Provider { get; set; } + + public LyricsSearchProviderInfo() { } + + public LyricsSearchProviderInfo(LyricsSearchProvider provider, bool isEnabled) + { + Provider = provider; + IsEnabled = isEnabled; + } + + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs index b393fdf..ec7ad61 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs @@ -1,6 +1,8 @@ // 2025/6/23 by Zhe Fang using CommunityToolkit.Mvvm.ComponentModel; +using Windows.Graphics.Imaging; +using Windows.UI; namespace BetterLyrics.WinUI3.Models { @@ -9,7 +11,9 @@ namespace BetterLyrics.WinUI3.Models [ObservableProperty] public partial string? Album { get; set; } - public byte[]? AlbumArt { get; set; } = null; + public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = null; + + public Color? AlbumArtAccentColor { get; set; } = null; [ObservableProperty] public partial string Artist { get; set; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs index 0924851..567cd4a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs @@ -9,6 +9,7 @@ namespace BetterLyrics.WinUI3.Serialization { [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(JsonElement))] diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IMusicSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IMusicSearchService.cs index 21c41c6..77014b8 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IMusicSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IMusicSearchService.cs @@ -8,14 +8,13 @@ namespace BetterLyrics.WinUI3.Services { public interface IMusicSearchService { - byte[]? SearchAlbumArtAsync(string title, string artist); + Task SearchAlbumArtAsync(string title, string artist, string album); Task<(string?, LyricsFormat?)> SearchLyricsAsync( string title, string artist, string album = "", - double durationMs = 0.0, - MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleAndArtist + double durationMs = 0.0 ); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs index f95a85b..d4116c2 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs @@ -14,10 +14,6 @@ namespace BetterLyrics.WinUI3.Services event EventHandler? SongInfoChanged; - bool IsPlaying { get; } - - TimeSpan Position { get; } - - SongInfo? SongInfo { get; } + event EventHandler? MediaSourceProvidersInfoChanged; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs index 4f92ca6..42454a5 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs @@ -37,7 +37,8 @@ namespace BetterLyrics.WinUI3.Services // Lyrics style and effetc - LyricsAlignmentType LyricsAlignmentType { get; set; } + TextAlignmentType LyricsAlignmentType { get; set; } + TextAlignmentType SongInfoAlignmentType { get; set; } int LyricsBlurAmount { get; set; } @@ -55,7 +56,8 @@ namespace BetterLyrics.WinUI3.Services List LyricsSearchProvidersInfo { get; set; } - int LyricsVerticalEdgeOpacity { get; set; } + List MediaSourceProvidersInfo { get; set; } + int LyricsVerticalEdgeOpacity { get; set; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs index 6e29225..a9d7d34 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MusicSearchService.cs @@ -1,5 +1,11 @@ // 2025/6/23 by Zhe Fang +using ATL; +using BetterLyrics.WinUI3.Enums; +using BetterLyrics.WinUI3.Helper; +using iTunesSearch.Library; +using Lyricify.Lyrics.Providers.Web.Kugou; +using Lyricify.Lyrics.Searchers; using System; using System.IO; using System.Linq; @@ -7,11 +13,6 @@ using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; -using ATL; -using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Helper; -using Lyricify.Lyrics.Providers.Web.Kugou; -using Lyricify.Lyrics.Searchers; namespace BetterLyrics.WinUI3.Services { @@ -21,17 +22,23 @@ namespace BetterLyrics.WinUI3.Services private readonly HttpClient _lrcLibHttpClient; + private readonly HttpClient _iTunesHttpClinet; + + private readonly iTunesSearchManager _iTunesSearchManager; + private readonly ISettingsService _settingsService; public MusicSearchService(ISettingsService settingsService) { _settingsService = settingsService; - _lrcLibHttpClient = new HttpClient(); + _lrcLibHttpClient = new(); _lrcLibHttpClient.DefaultRequestHeaders.Add( "User-Agent", $"{AppInfo.AppName} {AppInfo.AppVersion} ({AppInfo.GithubUrl})" ); - _amllTtmlDbHttpClient = new HttpClient(); + _amllTtmlDbHttpClient = new(); + _iTunesHttpClinet = new(); + _iTunesSearchManager = new(); } public async Task DownloadAmllTtmlDbIndexAsync() @@ -59,7 +66,22 @@ namespace BetterLyrics.WinUI3.Services } } - public byte[]? SearchAlbumArtAsync(string title, string artist) + private static string GuessCountryCode(string album, string artist) + { + string s = album + artist; + if (s.Any(c => c >= 0x4e00 && c <= 0x9fff)) // 中文 + return "cn"; + if (s.Any(c => (c >= 0x3040 && c <= 0x30ff) || (c >= 0x31f0 && c <= 0x31ff))) // 日文 + return "jp"; + if (s.Any(c => c >= 0xac00 && c <= 0xd7af)) // 韩文 + return "kr"; + if (s.Any(c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) // 英文 + return "us"; + // 其他情况 + return "us"; + } + + public async Task SearchAlbumArtAsync(string title, string artist, string album) { foreach (var folder in _settingsService.LocalLyricsFolders) { @@ -80,12 +102,19 @@ namespace BetterLyrics.WinUI3.Services } } - return null; + //var resultItems = await _iTunesSearchManager.GetAlbumsAsync(album, 1, countryCode: GuessCountryCode(album, artist)); + //var url = resultItems.Albums.Where(al => Normalize(al.ArtistName).Contains(Normalize(artist))) + // .FirstOrDefault()?.ArtworkUrl100.Replace("100x100bb.jpg", "100000x100000-999.jpg"); + //if (url != null) + //{ + // return await _iTunesHttpClinet.GetByteArrayAsync(url); + //} + + return await ImageHelper.CreateTextPlaceholderBytesAsync($"{artist} - {title}", 400, 400); } public async Task<(string?, LyricsFormat?)> SearchLyricsAsync( - string title, string artist, string album = "", double durationMs = 0.0, - MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleArtistAlbumAndDuration + string title, string artist, string album = "", double durationMs = 0.0 ) { foreach (var provider in _settingsService.LyricsSearchProvidersInfo) @@ -126,16 +155,16 @@ namespace BetterLyrics.WinUI3.Services switch (provider.Provider) { case LyricsSearchProvider.LrcLib: - searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000), matchMode); + searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000)); break; case LyricsSearchProvider.QQ: - searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.QQMusic); + searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.QQMusic); break; case LyricsSearchProvider.Kugou: - searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.Kugou); + searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Kugou); break; case LyricsSearchProvider.Netease: - searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.Netease); + searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Netease); break; case LyricsSearchProvider.AmllTtmlDb: searchedLyrics = await SearchAmllTtmlDbAsync(title, artist); @@ -161,9 +190,22 @@ namespace BetterLyrics.WinUI3.Services private static bool MusicMatch(string fileName, string title, string artist) { - return fileName.Contains(title) && fileName.Contains(artist); + var normFileName = Normalize(fileName); + var normTitle = Normalize(title); + var normArtist = Normalize(artist); + + // 常见两种顺序 + return normFileName == normTitle + normArtist + || normFileName == normArtist + normTitle; } + // 预处理:去除空格、括号、下划线、横杠、点、大小写等 + static string Normalize(string s) => + new string(s + .Where(c => char.IsLetterOrDigit(c)) + .ToArray()) + .ToLowerInvariant(); + private static string SanitizeFileName(string fileName, char replacement = '_') { var invalidChars = Path.GetInvalidFileNameChars(); @@ -303,20 +345,15 @@ namespace BetterLyrics.WinUI3.Services } } - private async Task SearchLrcLibAsync(string title, string artist, string album, int duration, MusicSearchMatchMode matchMode) + private async Task SearchLrcLibAsync(string title, string artist, string album, int duration) { // Build API query URL var url = - $"https://lrclib.net/api/search?" - + $"track_name={Uri.EscapeDataString(title)}&" - + $"artist_name={Uri.EscapeDataString(artist)}"; - - if (matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration) - { - url += - $"&album_name={Uri.EscapeDataString(album)}" - + $"&durationMs={Uri.EscapeDataString(duration.ToString())}"; - } + $"https://lrclib.net/api/search?" + + $"track_name={Uri.EscapeDataString(title)}&" + + $"artist_name={Uri.EscapeDataString(artist)}&" + + $"&album_name={Uri.EscapeDataString(album)}" + + $"&durationMs={Uri.EscapeDataString(duration.ToString())}"; var response = await _lrcLibHttpClient.GetAsync(url); if (!response.IsSuccessStatusCode) @@ -347,30 +384,28 @@ namespace BetterLyrics.WinUI3.Services string artist, string album, int durationMs, - MusicSearchMatchMode matchMode, Searchers searchers ) { var result = await SearchersHelper.GetSearcher(searchers).SearchForResult( new Lyricify.Lyrics.Models.TrackMultiArtistMetadata() { - DurationMs = matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration ? durationMs : null, - Album = matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration ? album : null, - AlbumArtists = [artist], + DurationMs = durationMs, + Album = album, Artists = [artist], Title = title, - } + } //, Lyricify.Lyrics.Searchers.Helpers.CompareHelper.MatchType.Perfect ); if (result is QQMusicSearchResult qqResult) { - var response = await Lyricify.Lyrics.Decrypter.Qrc.Helper.GetLyricsAsync(qqResult.Id); + var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id); var original = response?.Lyrics; return original; } else if (result is NeteaseSearchResult neteaseResult) { - var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id); + var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyricNew(neteaseResult.Id); return response?.Lrc.Lyric; } else if (result is KugouSearchResult kugouResult) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs index 7a1001d..7c53438 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs @@ -1,208 +1,218 @@ // 2025/6/23 by Zhe Fang -using System; -using System.Threading.Tasks; using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; -using CommunityToolkit.WinUI; +using BetterLyrics.WinUI3.ViewModels; +using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.UI.Dispatching; -using Windows.ApplicationModel; +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Windows.Graphics.Imaging; using Windows.Media.Control; using Windows.Storage.Streams; +using WindowsMediaController; +using static Lyricify.Lyrics.Providers.Web.Musixmatch.GetTokenResponse; namespace BetterLyrics.WinUI3.Services { - public partial class PlaybackService : IPlaybackService + public partial class PlaybackService : BaseViewModel, IPlaybackService, IRecipient>> { - private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); - private readonly IMusicSearchService _musicSearchService; - private GlobalSystemMediaTransportControlsSession? _currentSession = null; + private readonly MediaManager _mediaManager = new(); - private GlobalSystemMediaTransportControlsSessionManager? _sessionManager = null; + private CancellationTokenSource? _mediaPropsCts; - public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService) - { - _musicSearchService = musicSearchService; - InitMediaManager().ConfigureAwait(true); - } + private List _mediaSourceProvidersInfo; public event EventHandler? IsPlayingChanged; - public event EventHandler? PositionChanged; - public event EventHandler? SongInfoChanged; + public event EventHandler? MediaSourceProvidersInfoChanged; - public bool IsPlaying { get; private set; } - - public TimeSpan Position { get; private set; } - - public SongInfo? SongInfo { get; private set; } - - private void CurrentSession_MediaPropertiesChanged(GlobalSystemMediaTransportControlsSession? sender, MediaPropertiesChangedEventArgs? args) + public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService) : base(settingsService) { - App.DispatcherQueueTimer!.Debounce( - async () => - { - GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null; - if (sender == null) - { - SongInfo = null; - } - else - { - try - { - mediaProps = await sender.TryGetMediaPropertiesAsync(); - } - catch (Exception) { } - - if (mediaProps == null) - { - SongInfo = null; - } - else - { - SongInfo = new SongInfo - { - Title = mediaProps.Title, - Artist = mediaProps.Artist, - Album = mediaProps?.AlbumTitle ?? string.Empty, - DurationMs = _currentSession - ?.GetTimelineProperties() - .EndTime.TotalMilliseconds, - SourceAppUserModelId = _currentSession?.SourceAppUserModelId, - }; - - if (mediaProps?.Thumbnail is IRandomAccessStreamReference streamReference) - { - SongInfo.AlbumArt = await ImageHelper.ToByteArrayAsync( - streamReference - ); - } - else - { - SongInfo.AlbumArt = _musicSearchService.SearchAlbumArtAsync( - SongInfo.Title, - SongInfo.Artist - ); - - if (SongInfo.AlbumArt == null) - { - SongInfo.AlbumArt = - await ImageHelper.CreateTextPlaceholderBytesAsync( - $"{SongInfo.Artist} - {SongInfo.Title}", - 400, - 400 - ); - } - } - } - } - _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, - () => - { - SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(SongInfo)); - } - ); - }, - TimeSpan.FromMilliseconds(1000) - ); + _musicSearchService = musicSearchService; + _mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo; + InitMediaManager(); } - private void CurrentSession_PlaybackInfoChanged(GlobalSystemMediaTransportControlsSession? sender, PlaybackInfoChangedEventArgs? args) + private bool IsMediaSourceEnabled(string id) { - if (sender == null) + return _mediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true; + } + + private void InitMediaManager() + { + _mediaManager.Start(); + + _mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened; + _mediaManager.OnAnySessionClosed += MediaManager_OnAnySessionClosed; + _mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged; + _mediaManager.OnAnyMediaPropertyChanged += MediaManager_OnAnyMediaPropertyChanged; + _mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged; + _mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged; + + MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession()); + } + + private async void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession mediaSession) + { + if (mediaSession == null || !IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId)) { - IsPlaying = false; + SendNullMessages(); } else { - var playbackState = sender.GetPlaybackInfo().PlaybackStatus; - // _logger.LogDebug(playbackState.ToString()); - - switch (playbackState) - { - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Closed: - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Opened: - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Changing: - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Stopped: - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Paused: - IsPlaying = false; - break; - case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing: - IsPlaying = true; - break; - default: - break; - } + MediaManager_OnAnyMediaPropertyChanged(mediaSession, await mediaSession.ControlSession.TryGetMediaPropertiesAsync()); + MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo()); } - _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, - () => - { - IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(IsPlaying)); - } - ); } - private void CurrentSession_TimelinePropertiesChanged(GlobalSystemMediaTransportControlsSession? sender, TimelinePropertiesChangedEventArgs? args) + private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties) { - if (sender == null) - { - Position = TimeSpan.Zero; - } - else - { - Position = sender.GetTimelineProperties().Position; - } + if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return; + _dispatcherQueue.TryEnqueue( DispatcherQueuePriority.High, () => { - PositionChanged?.Invoke(this, new PositionChangedEventArgs(Position)); + PositionChanged?.Invoke(this, new PositionChangedEventArgs(timelineProperties.Position)); } ); } - private async Task InitMediaManager() + private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo) { - _sessionManager = await GlobalSystemMediaTransportControlsSessionManager.RequestAsync(); - _sessionManager.CurrentSessionChanged += SessionManager_CurrentSessionChanged; + if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return; - SessionManager_CurrentSessionChanged(_sessionManager, null); + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, + () => + { + IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(playbackInfo.PlaybackStatus switch + { + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true, + _ => false, + })); + } + ); } - private void SessionManager_CurrentSessionChanged( - GlobalSystemMediaTransportControlsSessionManager sender, - CurrentSessionChangedEventArgs? args - ) + private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties) { - // _logger.LogDebug("SessionManager_CurrentSessionChanged"); - // Unregister events associated with the previous session - if (_currentSession != null) + string id = mediaSession.ControlSession.SourceAppUserModelId; + if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return; + + _mediaPropsCts?.Cancel(); + var cts = new CancellationTokenSource(); + _mediaPropsCts = cts; + var token = cts.Token; + + try { - _currentSession.MediaPropertiesChanged -= CurrentSession_MediaPropertiesChanged; - _currentSession.PlaybackInfoChanged -= CurrentSession_PlaybackInfoChanged; - _currentSession.TimelinePropertiesChanged -= - CurrentSession_TimelinePropertiesChanged; + SongInfo? songInfo; + + token.ThrowIfCancellationRequested(); + + songInfo = new SongInfo + { + Title = mediaProperties.Title, + Artist = mediaProperties.Artist, + Album = mediaProperties.AlbumTitle, + DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds, + SourceAppUserModelId = id, + }; + + byte[] bytes; + + if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference) + { + bytes = await ImageHelper.ToByteArrayAsync( + streamReference + ); + token.ThrowIfCancellationRequested(); + } + else + { + bytes = await _musicSearchService.SearchAlbumArtAsync( + songInfo.Title, + songInfo.Artist, + songInfo.Album + ); + token.ThrowIfCancellationRequested(); + } + + var decoder = await ImageHelper.GetDecoderFromByte(bytes); + token.ThrowIfCancellationRequested(); + songInfo.AlbumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied); + token.ThrowIfCancellationRequested(); + songInfo.AlbumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault(); + if (!token.IsCancellationRequested) + { + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, + () => + { + SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(songInfo)); + }); + } } + catch (OperationCanceledException) { } + catch (Exception) { } + } - // Record and register events for current session - _currentSession = sender.GetCurrentSession(); - - if (_currentSession != null) + private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession) + { + if (_mediaManager.CurrentMediaSessions.Count == 0) { - _currentSession.MediaPropertiesChanged += CurrentSession_MediaPropertiesChanged; - _currentSession.PlaybackInfoChanged += CurrentSession_PlaybackInfoChanged; - _currentSession.TimelinePropertiesChanged += - CurrentSession_TimelinePropertiesChanged; + SendNullMessages(); } + } - CurrentSession_MediaPropertiesChanged(_currentSession, null); - CurrentSession_PlaybackInfoChanged(_currentSession, null); - CurrentSession_TimelinePropertiesChanged(_currentSession, null); + private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession) + { + string id = mediaSession.ControlSession.SourceAppUserModelId; + var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id); + if (found == null) + { + _mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id, true)); + _settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo; + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, + () => + { + MediaSourceProvidersInfoChanged?.Invoke(this, new MediaSourceProvidersInfoEventArgs(_mediaSourceProvidersInfo)); + }); + } + } + + private void SendNullMessages() + { + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, + () => + { + SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(null)); + IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(false)); + PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.Zero)); + }); + } + + public void Receive(PropertyChangedMessage> message) + { + if (message.Sender is SettingsPageViewModel) + { + if (message.PropertyName == nameof(SettingsPageViewModel.MediaSourceProvidersInfo)) + { + _mediaSourceProvidersInfo = [.. message.NewValue]; + _settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo; + MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession()); + } + } } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs index 29e09ef..8f4de0a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs @@ -23,7 +23,7 @@ namespace BetterLyrics.WinUI3.Services private const string AutoStartWindowTypeKey = "AutoStartWindowType"; - private const string CoverImageRadiusKey = "CoverImageRadius"; + private const string CoverImageRadiusKey = "AlbumArtCornerRadius"; private const string CoverOverlayBlurAmountKey = "CoverOverlayBlurAmount"; private const string CoverOverlayOpacityKey = "CoverOverlayOpacity"; private const string IsCoverOverlayEnabledKey = "IsCoverOverlayEnabled"; @@ -41,7 +41,8 @@ namespace BetterLyrics.WinUI3.Services private const string LanguageKey = "Language"; private const string LocalLyricsFoldersKey = "LocalLyricsFolders"; - private const string LyricsAlignmentTypeKey = "LyricsAlignmentType"; + private const string LyricsAlignmentTypeKey = "TextAlignmentType"; + private const string SongInfoAlignmentTypeKey = "SongInfoAlignmentType"; private const string LyricsBlurAmountKey = "LyricsBlurAmount"; private const string LyricsFontColorTypeKey = "LyricsFontColorType"; private const string LyricsFontSizeKey = "LyricsFontSize"; @@ -51,6 +52,8 @@ namespace BetterLyrics.WinUI3.Services private const string LyricsSearchProvidersInfoKey = "LyricsSearchProvidersInfo"; private const string LyricsVerticalEdgeOpacityKey = "LyricsVerticalEdgeOpacity"; + private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo"; + private readonly ApplicationDataContainer _localSettings; public SettingsService() @@ -69,6 +72,7 @@ namespace BetterLyrics.WinUI3.Services SourceGenerationContext.Default.ListLyricsSearchProviderInfo ) ); + SetDefault(MediaSourceProvidersInfoKey, "[]"); if (LyricsSearchProvidersInfo.Count != Enum.GetValues().Length) { LyricsSearchProvidersInfo = Enum.GetValues() @@ -97,7 +101,8 @@ namespace BetterLyrics.WinUI3.Services SetDefault(CoverOverlayBlurAmountKey, 200); SetDefault(CoverImageRadiusKey, 24); // 24 % // Lyrics - SetDefault(LyricsAlignmentTypeKey, (int)LyricsAlignmentType.Center); + SetDefault(LyricsAlignmentTypeKey, (int)TextAlignmentType.Center); + SetDefault(SongInfoAlignmentTypeKey, (int)TextAlignmentType.Left); SetDefault(LyricsFontWeightKey, (int)LyricsFontWeight.Bold); SetDefault(LyricsBlurAmountKey, 5); SetDefault(LyricsFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed); @@ -210,12 +215,18 @@ namespace BetterLyrics.WinUI3.Services ); } - public LyricsAlignmentType LyricsAlignmentType + public TextAlignmentType LyricsAlignmentType { - get => (LyricsAlignmentType)GetValue(LyricsAlignmentTypeKey); + get => (TextAlignmentType)GetValue(LyricsAlignmentTypeKey); set => SetValue(LyricsAlignmentTypeKey, (int)value); } + public TextAlignmentType SongInfoAlignmentType + { + get => (TextAlignmentType)GetValue(SongInfoAlignmentTypeKey); + set => SetValue(SongInfoAlignmentTypeKey, (int)value); + } + public int LyricsBlurAmount { get => GetValue(LyricsBlurAmountKey); @@ -275,6 +286,23 @@ namespace BetterLyrics.WinUI3.Services ); } + public List MediaSourceProvidersInfo + { + get => + System.Text.Json.JsonSerializer.Deserialize( + GetValue(MediaSourceProvidersInfoKey) ?? "[]", + SourceGenerationContext.Default.ListMediaSourceProviderInfo + )!; + set => + SetValue( + MediaSourceProvidersInfoKey, + System.Text.Json.JsonSerializer.Serialize( + value, + SourceGenerationContext.Default.ListMediaSourceProviderInfo + ) + ); + } + public int LyricsVerticalEdgeOpacity { get => GetValue(LyricsVerticalEdgeOpacityKey); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw index 218136b..c2f6462 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw @@ -228,15 +228,27 @@ Alignment + + Alignment + Center + + Center + Left + + Left + Right + + Right + Lyrics background blur amount @@ -328,7 +340,7 @@ Adaptive to lyrics background (Grayed) - Album art style + Album art area style Corner radius @@ -540,4 +552,22 @@ Auto-lock when activating desktop mode + + Album art + + + Song title & artist + + + Easing animation type + + + Media sources + + + Media source + + + Enable or disable lyrics display for a specified media source + \ 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 d7353bc..b5e938a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw @@ -228,15 +228,27 @@ アライメント + + アライメント + 中心 + + 中心 + + + + + + + 歌詞の背景ぼやけ @@ -328,7 +340,7 @@ 歌詞の背景に適応する(灰色) - アルバムアートスタイル + アルバムエリアスタイル コーナー半径 @@ -540,4 +552,22 @@ デスクトップモードをアクティブにするときの自動ロック + + アルバムアート + + + 曲のタイトル&アーティスト + + + アニメーションタイプを緩和します + + + メディアソース + + + メディアソース + + + 指定されたメディアソースの歌詞ディスプレイを有効または無効にする + \ 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 00a4355..8c78ad4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw @@ -228,15 +228,27 @@ 조정 + + 조정 + 센터 + + 센터 + 왼쪽 + + 왼쪽 + 오른쪽 + + 오른쪽 + 가사 배경 블러 @@ -328,7 +340,7 @@ 가사 배경 (회색)에 적응 - 앨범 아트 스타일 + 앨범 영역 스타일 코너 반경 @@ -540,4 +552,22 @@ 데스크탑 모드를 활성화 할 때 자동 잠금 + + 앨범 아트 + + + 노래 제목 및 아티스트 + + + 애니메이션 유형 완화 + + + 미디어 소스 + + + 미디어 소스 + + + 지정된 미디어 소스의 가사 디스플레이 활성화 또는 비활성화 + \ 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 c3eddb6..3237b9c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw @@ -228,15 +228,27 @@ 对齐方式 + + 对齐方式 + 居中 + + 居中 + 靠左 + + 靠左 + 靠右 + + 靠右 + 歌词背景模糊度 @@ -328,7 +340,7 @@ 适应歌词背景(灰色) - 专辑封面样式 + 专辑区域样式 圆角半径 @@ -540,4 +552,22 @@ 启动桌面模式时随即锁定窗口 + + 专辑 + + + 歌曲标题和艺术家 + + + 缓动动画类型 + + + 媒体来源 + + + 媒体来源 + + + 为指定媒体源启用或禁用歌词显示 + \ 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 cdc91a4..220926e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw @@ -228,15 +228,27 @@ 對齊方式 + + 對齊方式 + 居中 + + 居中 + 靠左 + + 靠左 + 靠右 + + 靠右 + 歌詞背景模糊度 @@ -328,7 +340,7 @@ 適應歌詞背景(灰色) - 專輯封面樣式 + 专辑区域样式 圓角半徑 @@ -540,4 +552,22 @@ 啟動桌面模式時隨即鎖定窗口 + + 專輯 + + + 歌曲標題和藝術家 + + + 缓动动画类型 + + + 媒體來源 + + + 媒體來源 + + + 為指定媒體源啟用或禁用歌詞顯示 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs index 0c97541..7135d8f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs @@ -1,9 +1,5 @@ // 2025/6/23 by Zhe Fang -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using BetterInAppLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; @@ -13,9 +9,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media.Imaging; -using WinUIEx.Messaging; +using System.Diagnostics; namespace BetterLyrics.WinUI3.ViewModels { @@ -28,35 +22,23 @@ namespace BetterLyrics.WinUI3.ViewModels public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService) { LyricsFontSize = _settingsService.LyricsFontSize; - CoverImageRadius = _settingsService.CoverImageRadius; _playbackService = playbackService; - _playbackService.SongInfoChanged += async (_, args) => - await UpdateSongInfoUI(args.SongInfo).ConfigureAwait(true); + _playbackService.SongInfoChanged += PlaybackService_SongInfoChanged; + IsFirstRun = _settingsService.IsFirstRun; + } - UpdateSongInfoUI(_playbackService.SongInfo).ConfigureAwait(true); + private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e) + { + SongInfo = e.SongInfo; + TrySwitchToPreferredDisplayType(e.SongInfo); } - [ObservableProperty] - public partial bool AboutToUpdateUI { get; set; } - - [ObservableProperty] - public partial BitmapImage? CoverImage { get; set; } - - [ObservableProperty] - public partial double CoverImageGridActualHeight { get; set; } - - [ObservableProperty] - public partial CornerRadius CoverImageGridCornerRadius { get; set; } - - [ObservableProperty] - public partial int CoverImageRadius { get; set; } - [ObservableProperty] [NotifyPropertyChangedRecipients] - public partial LyricsDisplayType DisplayType { get; set; } + public partial LyricsDisplayType DisplayType { get; set; } = LyricsDisplayType.PlaceholderOnly; [ObservableProperty] public partial bool IsFirstRun { get; set; } @@ -70,28 +52,12 @@ namespace BetterLyrics.WinUI3.ViewModels [ObservableProperty] public partial int LyricsFontSize { get; set; } - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial double MaxLyricsWidth { get; set; } = 0.0; - [ObservableProperty] public partial LyricsDisplayType? PreferredDisplayType { get; set; } = LyricsDisplayType.SplitView; [ObservableProperty] public partial SongInfo? SongInfo { get; set; } = null; - public void OpenMatchedFileFolderInFileExplorer(string path) - { - Process.Start( - new ProcessStartInfo - { - FileName = "explorer.exe", - Arguments = $"/select,\"{path}\"", - UseShellExecute = true, - } - ); - } - public void Receive(PropertyChangedMessage message) { if (message.Sender is LyricsWindowViewModel) @@ -114,37 +80,13 @@ namespace BetterLyrics.WinUI3.ViewModels { if (message.Sender is SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.CoverImageRadius)) - { - CoverImageRadius = message.NewValue; - } - } - if (message.Sender is LyricsSettingsControlViewModel) - { - if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize)) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize)) { LyricsFontSize = message.NewValue; } } } - public async Task UpdateSongInfoUI(SongInfo? songInfo) - { - AboutToUpdateUI = true; - await Task.Delay(AnimationHelper.StoryboardDefaultDuration); - - SongInfo = songInfo; - - CoverImage = - (songInfo?.AlbumArt == null) - ? null - : await ImageHelper.GetBitmapImageFromBytesAsync(songInfo.AlbumArt); - - TrySwitchToPreferredDisplayType(songInfo); - - AboutToUpdateUI = false; - } - [RelayCommand] private void OpenSettingsWindow() { @@ -185,24 +127,6 @@ namespace BetterLyrics.WinUI3.ViewModels } - partial void OnCoverImageGridActualHeightChanged(double value) - { - if (double.IsNaN(value)) - return; - - CoverImageGridCornerRadius = new CornerRadius(CoverImageRadius / 100f * value / 2); - } - - partial void OnCoverImageRadiusChanged(int value) - { - if (double.IsNaN(CoverImageGridActualHeight)) - return; - - CoverImageGridCornerRadius = new CornerRadius( - value / 100f * CoverImageGridActualHeight / 2 - ); - } - partial void OnIsFirstRunChanged(bool value) { IsWelcomeTeachingTipOpen = value; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs index 3fda0d8..209b68e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using BetterLyrics.WinUI3.Enums; +using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Models; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; using Microsoft.Graphics.Canvas.Effects; @@ -11,9 +8,16 @@ using Microsoft.Graphics.Canvas.Geometry; using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.UI; +using Microsoft.UI.Text; +using Microsoft.UI.Xaml.Media; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; using Windows.Foundation; using Windows.Graphics.Imaging; using Windows.UI; +using Windows.UI.Text; namespace BetterLyrics.WinUI3.ViewModels { @@ -25,18 +29,7 @@ namespace BetterLyrics.WinUI3.ViewModels using var blurredLyrics = new CanvasCommandList(control); using (var blurredLyricsDs = blurredLyrics.CreateDrawingSession()) { - switch (DisplayType) - { - case LyricsDisplayType.AlbumArtOnly: - case LyricsDisplayType.PlaceholderOnly: - break; - case LyricsDisplayType.LyricsOnly: - case LyricsDisplayType.SplitView: - DrawBlurredLyrics(control, blurredLyricsDs); - break; - default: - break; - } + DrawBlurredLyrics(control, blurredLyricsDs); } using var combined = new CanvasCommandList(control); @@ -60,6 +53,10 @@ namespace BetterLyrics.WinUI3.ViewModels ds.DrawImage(combined); } + DrawAlbumArt(control, ds); + + DrawTitleAndArtist(control, ds); + if (_isDebugOverlayEnabled) { var currentPlayingLineIndex = GetCurrentPlayingLineIndex(); @@ -77,48 +74,88 @@ namespace BetterLyrics.WinUI3.ViewModels ); ds.DrawText( - $"DEBUG: " - + $"Cur playing {currentPlayingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" - + $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" - + $"Cur time {TotalTime}\n" + - $"Lang size: {_multiLangLyrics.Count}\n" + - $"{_lyricsOpacityTransition.Value}", + $"[DEBUG]\n" + + $"Cur playing {currentPlayingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" + + $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" + + $"Cur time {TotalTime}\n" + + $"Lang size {_multiLangLyrics.Count}\n" + + $"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}", new Vector2(10, 10), ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White ); + + //for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++) + //{ + // LyricsLine? line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i); + // if (line != null) + // { + // ds.DrawText( + // $"[{i}] {line.Text} {line.ScaleTransition.Value}", + // new Vector2(10, 30 + (i - _startVisibleLineIndex) * 20), + // ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White + // ); + // } + //} } } } - private static void DrawImgae( - ICanvasAnimatedControl control, - CanvasDrawingSession ds, - SoftwareBitmap softwareBitmap, - float opacity - ) + private void DrawBackgroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, SoftwareBitmap swBitmap, float opacity) { - using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap); + using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, swBitmap); float imageWidth = (float)canvasBitmap.Size.Width; float imageHeight = (float)canvasBitmap.Size.Height; - var scaleFactor = - (float)Math.Sqrt(Math.Pow(control.Size.Width, 2) + Math.Pow(control.Size.Height, 2)) - / Math.Min(imageWidth, imageHeight); + float scaleFactor = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)) / MathF.Min(imageWidth, imageHeight); - ds.DrawImage( - new OpacityEffect + float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2; + float y = _canvasHeight / 2 - imageHeight * scaleFactor / 2; + + ds.DrawImage(new OpacityEffect + { + Source = new ScaleEffect + { + Scale = new Vector2(scaleFactor), + Source = canvasBitmap, + }, + Opacity = opacity, + }, new Vector2(x, y) + ); + } + + private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, SoftwareBitmap swBitmap, float opacity) + { + using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, swBitmap); + float imageWidth = (float)canvasBitmap.Size.Width; + float imageHeight = (float)canvasBitmap.Size.Height; + + float scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight); + if (scaleFactor < 0.1f) return; + + _albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16; + + float cornerRadius = _albumArtCornerRadius / 100f * _albumArtSize / 2; + + using var cornerRadiusMask = new CanvasCommandList(control.Device); + using var cornerRadiusMaskDs = cornerRadiusMask.CreateDrawingSession(); + cornerRadiusMaskDs.FillRoundedRectangle( + new Rect(0, 0, imageWidth * scaleFactor, imageHeight * scaleFactor), + cornerRadius, cornerRadius, Colors.White + ); + + ds.DrawImage(new OpacityEffect + { + Source = new AlphaMaskEffect { Source = new ScaleEffect { - InterpolationMode = CanvasImageInterpolation.HighQualityCubic, - BorderMode = EffectBorderMode.Hard, Scale = new Vector2(scaleFactor), Source = canvasBitmap, }, - Opacity = opacity, + AlphaMask = cornerRadiusMask, }, - (float)control.Size.Width / 2 - imageWidth * scaleFactor / 2, - (float)control.Size.Height / 2 - imageHeight * scaleFactor / 2 + Opacity = opacity, + }, new Vector2(_albumArtXTransition.Value, _albumArtY) ); } @@ -129,23 +166,13 @@ namespace BetterLyrics.WinUI3.ViewModels var overlappedCovers = new CanvasCommandList(control.Device); using var overlappedCoversDs = overlappedCovers.CreateDrawingSession(); - if (_lastAlbumArtBitmap != null) + if (_lastAlbumArtSwBitmap != null) { - DrawImgae( - control, - overlappedCoversDs, - _lastAlbumArtBitmap, - 1 - _albumArtBgTransition.Value - ); + DrawBackgroundImgae(control, overlappedCoversDs, _lastAlbumArtSwBitmap, 1 - _albumArtBgTransition.Value); } - if (_albumArtBitmap != null) + if (_albumArtSwBitmap != null) { - DrawImgae( - control, - overlappedCoversDs, - _albumArtBitmap, - _albumArtBgTransition.Value - ); + DrawBackgroundImgae(control, overlappedCoversDs, _albumArtSwBitmap, _albumArtBgTransition.Value); } using var coverOverlayEffect = new OpacityEffect @@ -162,6 +189,70 @@ namespace BetterLyrics.WinUI3.ViewModels ds.Transform = Matrix3x2.Identity; } + private void DrawAlbumArt(ICanvasAnimatedControl control, CanvasDrawingSession ds) + { + using var albumArt = new CanvasCommandList(control.Device); + using var albumArtDs = albumArt.CreateDrawingSession(); + if (_albumArtSwBitmap != null) + { + DrawForegroundImgae(control, albumArtDs, _albumArtSwBitmap, _albumArtBgTransition.Value); + } + if (_lastAlbumArtSwBitmap != null) + { + DrawForegroundImgae(control, albumArtDs, _lastAlbumArtSwBitmap, 1 - _albumArtBgTransition.Value); + } + + using var opacity = new CanvasCommandList(control.Device); + using var opacityDs = opacity.CreateDrawingSession(); + opacityDs.DrawImage(new GaussianBlurEffect + { + Source = albumArt, + BlurAmount = 12f, + Optimization = EffectOptimization.Quality, + }); + opacityDs.DrawImage(albumArt); + + ds.DrawImage(new OpacityEffect + { + Source = opacity, + Opacity = _albumArtOpacityTransition.Value + }); + } + + private void DrawTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds) + { + if (_lastSongTitle != null || _lastSongArtist != null) + { + DrawSingleTitleAndArtist(control, ds, _lastSongTitle, _lastSongArtist, 1 - _songInfoOpacityTransition.Value); + } + if (_songTitle != null || _songArtist != null) + { + DrawSingleTitleAndArtist(control, ds, _songTitle, _songArtist, _songInfoOpacityTransition.Value); + } + } + + private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, float opacity) + { + float titleY = _albumArtY + _albumArtSize + 12; + + CanvasTextLayout titleLayout = new( + control, title ?? string.Empty, + _titleTextFormat, _albumArtSize, _canvasHeight + ); + CanvasTextLayout artistLayout = new( + control, artist ?? string.Empty, + _artistTextFormat, _albumArtSize, _canvasHeight + ); + ds.DrawTextLayout( + titleLayout, + new Vector2(_albumArtXTransition.Value, titleY), + _fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity))); + ds.DrawTextLayout( + artistLayout, + new Vector2(_albumArtXTransition.Value, titleY + (float)titleLayout.LayoutBounds.Height), + _fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity))); + } + private void DrawBlurredLyrics(ICanvasAnimatedControl control, CanvasDrawingSession ds) { var currentPlayingLineIndex = GetCurrentPlayingLineIndex(); @@ -206,35 +297,26 @@ namespace BetterLyrics.WinUI3.ViewModels switch (LyricsAlignmentType) { - case LyricsAlignmentType.Left: + case TextAlignmentType.Left: textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Left; break; - case LyricsAlignmentType.Center: + case TextAlignmentType.Center: textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Center; - centerX += (float)_maxLyricsWidthTransition.Value / 2; + centerX += _maxLyricsWidth / 2; break; - case LyricsAlignmentType.Right: + case TextAlignmentType.Right: textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Right; - centerX += (float)_maxLyricsWidthTransition.Value; + centerX += _maxLyricsWidth; break; default: break; } - float offsetToLeft = - (float)control.Size.Width - _rightMargin - _maxLyricsWidthTransition.Value; - // 组合变换:缩放 -> 旋转 -> 平移 ds.Transform = Matrix3x2.CreateScale(line.ScaleTransition.Value, new Vector2(centerX, centerY)) - * Matrix3x2.CreateRotation( - line.AngleTransition.Value, - currentPlayingLine.Position - ) - * Matrix3x2.CreateTranslation( - offsetToLeft, - _canvasYScrollTransition.Value + (float)(control.Size.Height / 2) - ); + * Matrix3x2.CreateRotation(line.AngleTransition.Value, currentPlayingLine.Position) + * Matrix3x2.CreateTranslation(_lyricsXTransition.Value, _canvasYScrollTransition.Value + _canvasHeight / 2); // Create the original lyrics line using var lyrics = new CanvasCommandList(control.Device); @@ -255,138 +337,132 @@ namespace BetterLyrics.WinUI3.ViewModels ); // 再叠加当前行歌词层 - // Only draw the current line and the two lines around it - // This layer is to highlight the current line - // and for fade-in and fade-out effects, two lines around it is also drawn - if (Math.Abs(i - currentPlayingLineIndex) <= 1) + using var mask = new CanvasCommandList(control.Device); + using var maskDs = mask.CreateDrawingSession(); + + using var highlightMask = new CanvasCommandList(control.Device); + using var highlightMaskDs = highlightMask.CreateDrawingSession(); + + if (i == currentPlayingLineIndex) { - using var mask = new CanvasCommandList(control.Device); - using var maskDs = mask.CreateDrawingSession(); - - using var highlightMask = new CanvasCommandList(control.Device); - using var highlightMaskDs = highlightMask.CreateDrawingSession(); - - if (i == currentPlayingLineIndex) + GetLinePlayingProgress( + line, + out int charStartIndex, + out int charLength, + out float charProgress + ); + var regions = textLayout.GetCharacterRegions(0, charStartIndex); + var highlightRegion = textLayout + .GetCharacterRegions(charStartIndex, charLength) + .FirstOrDefault(); + if (regions.Length > 0) { - GetLinePlayingProgress( - line, - out int charStartIndex, - out int charLength, - out float charProgress - ); - var regions = textLayout.GetCharacterRegions(0, charStartIndex); - var highlightRegion = textLayout - .GetCharacterRegions(charStartIndex, charLength) - .FirstOrDefault(); - if (regions.Length > 0) + // Draw the mask for the current line + for (int j = 0; j < regions.Length; j++) { - // Draw the mask for the current line - for (int j = 0; j < regions.Length; j++) - { - var region = regions[j]; - var rect = new Rect( - region.LayoutBounds.X, - region.LayoutBounds.Y + position.Y, - region.LayoutBounds.Width, - region.LayoutBounds.Height - ); - maskDs.FillRectangle(rect, Colors.Black); - } + var region = regions[j]; + var rect = new Rect( + region.LayoutBounds.X, + region.LayoutBounds.Y + position.Y, + region.LayoutBounds.Width, + region.LayoutBounds.Height + ); + maskDs.FillRectangle(rect, Colors.Black); } - - float highlightTotalWidth = (float)highlightRegion.LayoutBounds.Width; - // Draw the highlight for the current character - float highlightWidth = highlightTotalWidth * charProgress; - - float fadingWidth = (float)highlightRegion.LayoutBounds.Height / 2; - - // Rects - var highlightRect = new Rect( - highlightRegion.LayoutBounds.X, - highlightRegion.LayoutBounds.Y + position.Y, - highlightWidth, - highlightRegion.LayoutBounds.Height - ); - - var fadeInRect = new Rect( - highlightRect.Right - fadingWidth, - highlightRegion.LayoutBounds.Y + position.Y, - fadingWidth, - highlightRegion.LayoutBounds.Height - ); - var fadeOutRect = new Rect( - highlightRect.Right, - highlightRegion.LayoutBounds.Y + position.Y, - fadingWidth, - highlightRegion.LayoutBounds.Height - ); - - // Brushes - using var fadeInBrush = GetHorizontalFillBrush( - control, - [(0f, 0f), (1f, 1f)], - (float)highlightRect.Right - fadingWidth, - fadingWidth - ); - using var fadeOutBrush = GetHorizontalFillBrush( - control, - [(0f, 1f), (1f, 0f)], - (float)highlightRect.Right, - fadingWidth - ); - - maskDs.FillRectangle(highlightRect, Colors.White); - maskDs.FillRectangle(fadeOutRect, fadeOutBrush); - - highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush); - highlightMaskDs.FillRectangle(fadeOutRect, fadeOutBrush); - } - else - { - maskDs.FillRectangle( - new Rect( - textLayout.LayoutBounds.X, - position.Y, - textLayout.LayoutBounds.Width, - textLayout.LayoutBounds.Height - ), - Colors.White - ); } - ds.DrawImage( - new OpacityEffect - { - Source = new BlendEffect - { - Background = IsLyricsGlowEffectEnabled - ? new GaussianBlurEffect - { - Source = new AlphaMaskEffect - { - Source = lyrics, - AlphaMask = LyricsGlowEffectScope switch - { - LineRenderingType.UntilCurrentChar => mask, - LineRenderingType.CurrentCharOnly => highlightMask, - _ => mask, - }, - }, - BlurAmount = _lyricsGlowEffectAmount, - Optimization = EffectOptimization.Quality, - } - : new CanvasCommandList(control.Device), - Foreground = new AlphaMaskEffect - { - Source = lyrics, - AlphaMask = mask, - }, - }, - Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value, - } + float highlightTotalWidth = (float)highlightRegion.LayoutBounds.Width; + // Draw the highlight for the current character + float highlightWidth = highlightTotalWidth * charProgress; + + float fadingWidth = (float)highlightRegion.LayoutBounds.Height / 2; + + // Rects + var highlightRect = new Rect( + highlightRegion.LayoutBounds.X, + highlightRegion.LayoutBounds.Y + position.Y, + highlightWidth, + highlightRegion.LayoutBounds.Height + ); + + var fadeInRect = new Rect( + highlightRect.Right - fadingWidth, + highlightRegion.LayoutBounds.Y + position.Y, + fadingWidth, + highlightRegion.LayoutBounds.Height + ); + var fadeOutRect = new Rect( + highlightRect.Right, + highlightRegion.LayoutBounds.Y + position.Y, + fadingWidth, + highlightRegion.LayoutBounds.Height + ); + + // Brushes + using var fadeInBrush = GetHorizontalFillBrush( + control, + [(0f, 0f), (1f, 1f)], + (float)highlightRect.Right - fadingWidth, + fadingWidth + ); + using var fadeOutBrush = GetHorizontalFillBrush( + control, + [(0f, 1f), (1f, 0f)], + (float)highlightRect.Right, + fadingWidth + ); + + maskDs.FillRectangle(highlightRect, Colors.White); + maskDs.FillRectangle(fadeOutRect, fadeOutBrush); + + highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush); + highlightMaskDs.FillRectangle(fadeOutRect, fadeOutBrush); + } + else + { + maskDs.FillRectangle( + new Rect( + textLayout.LayoutBounds.X, + position.Y, + textLayout.LayoutBounds.Width, + textLayout.LayoutBounds.Height + ), + Colors.White ); } + ds.DrawImage( + new OpacityEffect + { + Source = new BlendEffect + { + Background = IsLyricsGlowEffectEnabled + ? new GaussianBlurEffect + { + Source = new AlphaMaskEffect + { + Source = lyrics, + AlphaMask = LyricsGlowEffectScope switch + { + LineRenderingType.UntilCurrentChar => mask, + LineRenderingType.CurrentCharOnly => highlightMask, + _ => mask, + }, + }, + BlurAmount = _lyricsGlowEffectAmount, + Optimization = EffectOptimization.Quality, + } + : new CanvasCommandList(control.Device), + Foreground = new AlphaMaskEffect + { + Source = lyrics, + AlphaMask = mask, + }, + }, + Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value, + } + ); + // Reset scale ds.Transform = Matrix3x2.Identity; } @@ -399,7 +475,7 @@ namespace BetterLyrics.WinUI3.ViewModels ) { ds.FillRectangle( - new Rect(0, 0, control.Size.Width, control.Size.Height), + new Rect(0, 0, _canvasWidth, _canvasHeight), new CanvasLinearGradientBrush( control, [ @@ -424,7 +500,7 @@ namespace BetterLyrics.WinUI3.ViewModels ) { StartPoint = new Vector2(0, 0), - EndPoint = new Vector2(0, (float)control.Size.Height), + EndPoint = new Vector2(0, _canvasHeight), } ); } @@ -451,87 +527,5 @@ namespace BetterLyrics.WinUI3.ViewModels EndPoint = new Vector2(startX + width, 0), }; } - - void DrawShenGuang(ICanvasAnimatedControl control, CanvasDrawingSession ds) - { - float w = (float)control.Size.Width; - float h = (float)control.Size.Height; - - float beamLength = h; // 光束长度等于画布高度 - float beamAngle = (float)(Math.PI / 6); // 30° - float centerX = w / 2; - float centerY = h; - float angle = _shenGuangAngleTransition.Value; - - var p0 = new Vector2(centerX, centerY); - var p1 = new Vector2( - centerX + beamLength * (float)Math.Cos(angle - beamAngle / 2), - centerY + beamLength * (float)Math.Sin(angle - beamAngle / 2) - ); - var p2 = new Vector2( - centerX + beamLength * (float)Math.Cos(angle + beamAngle / 2), - centerY + beamLength * (float)Math.Sin(angle + beamAngle / 2) - ); - - using var path = new CanvasPathBuilder(control); - path.BeginFigure(p0); - path.AddLine(p1); - path.AddArc( - p2, - beamLength, - beamLength, - 0, - CanvasSweepDirection.Clockwise, - CanvasArcSize.Small - ); - path.EndFigure(CanvasFigureLoop.Closed); - - using var geometry = CanvasGeometry.CreatePath(path); - - // 渐变为白色,透明度递减 - using var brush = new CanvasRadialGradientBrush( - control, - new[] - { - new CanvasGradientStop - { - Position = 0f, - Color = Color.FromArgb(180, 255, 255, 255), - }, - new CanvasGradientStop - { - Position = 0.5f, - Color = Color.FromArgb(60, 255, 255, 255), - }, - new CanvasGradientStop - { - Position = 1f, - Color = Color.FromArgb(0, 255, 255, 255), - }, - } - ) - { - Center = p0, - OriginOffset = new Vector2(0, 0), - RadiusX = beamLength * 0.8f, - RadiusY = beamLength * 0.8f, - }; - - using var beamCmd = new CanvasCommandList(control); - using (var beamDs = beamCmd.CreateDrawingSession()) - { - beamDs.FillGeometry(geometry, brush); - } - - var blur = new GaussianBlurEffect - { - Source = beamCmd, - BlurAmount = 36f, - Optimization = EffectOptimization.Quality, - BorderMode = EffectBorderMode.Soft, - }; - - ds.DrawImage(blur); - } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs index 234ba23..743fd7a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs @@ -1,15 +1,8 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using BetterInAppLyrics.WinUI3.ViewModels; -using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Models; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; -using Microsoft.UI; -using Microsoft.UI.Xaml; -using Windows.Graphics.Imaging; +using System.Collections.ObjectModel; using Windows.UI; namespace BetterLyrics.WinUI3.ViewModels @@ -17,20 +10,17 @@ namespace BetterLyrics.WinUI3.ViewModels public partial class LyricsRendererViewModel : IRecipient>, IRecipient>, - IRecipient>, IRecipient>, IRecipient>, IRecipient>, IRecipient>, - IRecipient>, + IRecipient>, IRecipient>, IRecipient>, IRecipient>>, IRecipient>> { - public async void Receive( - PropertyChangedMessage> message - ) + public async void Receive(PropertyChangedMessage> message) { if (message.Sender is SettingsPageViewModel) { @@ -42,9 +32,7 @@ namespace BetterLyrics.WinUI3.ViewModels } } - public async void Receive( - PropertyChangedMessage> message - ) + public async void Receive(PropertyChangedMessage> message) { if (message.Sender is SettingsPageViewModel) { @@ -62,33 +50,19 @@ namespace BetterLyrics.WinUI3.ViewModels { if (message.Sender is SettingsPageViewModel) { - if ( - message.PropertyName - == nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled) - ) + if (message.PropertyName == nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled)) { IsDynamicCoverOverlayEnabled = message.NewValue; } - else if ( - message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled)) { _isDebugOverlayEnabled = message.NewValue; } - } - else if (message.Sender is LyricsSettingsControlViewModel) - { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsGlowEffectEnabled)) { IsLyricsGlowEffectEnabled = message.NewValue; } - else if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.IsFanLyricsEnabled) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.IsFanLyricsEnabled)) { _isFanLyricsEnabled = message.NewValue; } @@ -118,9 +92,9 @@ namespace BetterLyrics.WinUI3.ViewModels UpdateFontColor(); } } - else if (message.Sender is LyricsSettingsControlViewModel) + else if (message.Sender is SettingsPageViewModel) { - if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsCustomFontColor)) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomFontColor)) { _customFontColor = message.NewValue; UpdateFontColor(); @@ -128,25 +102,11 @@ namespace BetterLyrics.WinUI3.ViewModels } } - public void Receive(PropertyChangedMessage message) - { - if (message.Sender is LyricsPageViewModel) - { - if (message.PropertyName == nameof(LyricsPageViewModel.MaxLyricsWidth)) - { - _maxLyricsWidthTransition.StartTransition((float)message.NewValue); - } - } - } - public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.LyricsLineSpacingFactor) - ) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsLineSpacingFactor)) { LyricsLineSpacingFactor = message.NewValue; } @@ -159,37 +119,25 @@ namespace BetterLyrics.WinUI3.ViewModels { if (message.PropertyName == nameof(SettingsPageViewModel.CoverImageRadius)) { - CoverImageRadius = message.NewValue; + _albumArtCornerRadius = message.NewValue; } else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayOpacity)) { CoverOverlayOpacity = message.NewValue; } - else if ( - message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount)) { CoverOverlayBlurAmount = message.NewValue; } - } - else if (message.Sender is LyricsSettingsControlViewModel) - { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsVerticalEdgeOpacity)) { LyricsVerticalEdgeOpacity = message.NewValue; } - else if ( - message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsBlurAmount) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBlurAmount)) { LyricsBlurAmount = message.NewValue; } - else if ( - message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize) - ) + else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize)) { LyricsFontSize = message.NewValue; } @@ -198,45 +146,41 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.LyricsGlowEffectScope) - ) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsGlowEffectScope)) { LyricsGlowEffectScope = message.NewValue; } } } - public void Receive(PropertyChangedMessage message) + public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.LyricsAlignmentType) - ) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsAlignmentType)) { LyricsAlignmentType = message.NewValue; } + else if (message.PropertyName == nameof(SettingsPageViewModel.SongInfoAlignmentType)) + { + _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = + message.NewValue.ToCanvasHorizontalAlignment(); + } } } public void Receive(PropertyChangedMessage message) { - DisplayType = message.NewValue; + _displayTypeReceived = message.NewValue; } public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if ( - message.PropertyName - == nameof(LyricsSettingsControlViewModel.LyricsFontColorType) - ) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontColorType)) { LyricsFontColorType = message.NewValue; } @@ -245,9 +189,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontWeight)) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontWeight)) { LyricsFontWeight = message.NewValue; } @@ -266,7 +210,7 @@ namespace BetterLyrics.WinUI3.ViewModels partial void OnLyricsFontWeightChanged(LyricsFontWeight value) { - _textFormat.FontWeight = value.ToFontWeight(); + _lyricsTextFormat.FontWeight = value.ToFontWeight(); _isRelayoutNeeded = true; } @@ -274,40 +218,5 @@ namespace BetterLyrics.WinUI3.ViewModels { _isRelayoutNeeded = true; } - - async partial void OnSongInfoChanged(SongInfo? oldValue, SongInfo? newValue) - { - TotalTime = TimeSpan.Zero; - - SoftwareBitmap? newalbumArtBitmap; - Color? newAlbumArtAccentColor; - - if (newValue?.AlbumArt is byte[] bytes) - { - var decoder = await ImageHelper.GetDecoderFromByte(bytes); - newalbumArtBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied); - newAlbumArtAccentColor = (ImageHelper.GetAccentColorsFromByte(bytes)).SafeGet(0); - } - else - { - newalbumArtBitmap = null; - newAlbumArtAccentColor = null; - } - - _lastAlbumArtBitmap = _albumArtBitmap; - _albumArtBitmap = newalbumArtBitmap; - - _albumArtBgTransition.Reset(0f); - _albumArtBgTransition.StartTransition(1f); - - _albumArtAccentColor = newAlbumArtAccentColor; - _lyricsWindowBgColor = _albumArtAccentColor ?? Colors.Gray; - - if (!_isDesktopMode && !_isDockMode) _adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor); - - UpdateFontColor(); - - await RefreshLyricsAsync(); - } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs new file mode 100644 index 0000000..4997c06 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs @@ -0,0 +1,56 @@ +using BetterLyrics.WinUI3.Helper; +using Microsoft.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI; + +namespace BetterLyrics.WinUI3.ViewModels +{ + public partial class LyricsRendererViewModel + { + private readonly ValueTransition _canvasYScrollTransition = new( + initialValue: 0f, + durationSeconds: 0.3f + ); + + private readonly ValueTransition _immersiveBgTransition = new( + initialValue: Colors.Transparent, + durationSeconds: 1f, + interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to) + ); + + private readonly ValueTransition _lyricsXTransition = new( + initialValue: 0f, + durationSeconds: 0.3f + ); + + private readonly ValueTransition _lyricsOpacityTransition = new( + initialValue: 0f, + durationSeconds: 1f + ); + + private readonly ValueTransition _albumArtBgTransition = new( + initialValue: 0f, + durationSeconds: 1f + ); + + private readonly ValueTransition _albumArtOpacityTransition = new( + initialValue: 0f, + durationSeconds: 1f + ); + + private readonly ValueTransition _albumArtXTransition = new( + initialValue: 0f, + durationSeconds: 0.3f + ); + + private readonly ValueTransition _songInfoOpacityTransition = new( + initialValue: 0f, + durationSeconds: 1f + ); + + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs index 9b51421..f1f8753 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Numerics; +using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; +using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.UI; using Microsoft.UI.Xaml; +using System; +using System.Numerics; using Windows.UI; namespace BetterLyrics.WinUI3.ViewModels @@ -15,6 +16,13 @@ namespace BetterLyrics.WinUI3.ViewModels { public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args) { + bool isCanvasWidthChanged = _canvasWidth != control.Size.Width; + bool isDisplayTypeChanged = _displayType != _displayTypeReceived; + + _canvasWidth = (float)control.Size.Width; + _canvasHeight = (float)control.Size.Height; + _displayType = _displayTypeReceived; + if (_isPlaying) { TotalTime += args.Timing.ElapsedTime; @@ -22,15 +30,9 @@ namespace BetterLyrics.WinUI3.ViewModels ElapsedTime = args.Timing.ElapsedTime; - if (_immersiveBgTransition.IsTransitioning) - { - _immersiveBgTransition.Update(ElapsedTime); - } - - if (_albumArtBgTransition.IsTransitioning) - { - _albumArtBgTransition.Update(ElapsedTime); - } + _immersiveBgTransition.Update(ElapsedTime); + _albumArtBgTransition.Update(ElapsedTime); + _songInfoOpacityTransition.Update(ElapsedTime); if (IsDynamicCoverOverlayEnabled) { @@ -38,41 +40,53 @@ namespace BetterLyrics.WinUI3.ViewModels _rotateAngle %= MathF.PI * 2; } - if (_maxLyricsWidthTransition.IsTransitioning) + _albumArtSize = MathF.Min( + (_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16, + (_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2); + _albumArtSize = MathF.Max(0, _albumArtSize); + + if (isDisplayTypeChanged || isCanvasWidthChanged) + { + bool jumpTo = !isDisplayTypeChanged && isCanvasWidthChanged; + switch (_displayType) + { + case LyricsDisplayType.AlbumArtOnly: + _lyricsOpacityTransition.StartTransition(0f, jumpTo); + _albumArtOpacityTransition.StartTransition(1f, jumpTo); + _albumArtXTransition.StartTransition(_canvasWidth / 2 - _albumArtSize / 2, jumpTo); + break; + case LyricsDisplayType.LyricsOnly: + _lyricsOpacityTransition.StartTransition(1f, jumpTo); + _albumArtOpacityTransition.StartTransition(0f, jumpTo); + _lyricsXTransition.StartTransition(_leftMargin, jumpTo); + _isRelayoutNeeded = true; + break; + case LyricsDisplayType.SplitView: + _lyricsOpacityTransition.StartTransition(1f, jumpTo); + _albumArtOpacityTransition.StartTransition(1f, jumpTo); + _lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo); + _albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo); + _isRelayoutNeeded = true; + break; + case LyricsDisplayType.PlaceholderOnly: + break; + default: + break; + } + } + + _lyricsXTransition.Update(ElapsedTime); + _albumArtXTransition.Update(ElapsedTime); + _lyricsOpacityTransition.Update(ElapsedTime); + _albumArtOpacityTransition.Update(ElapsedTime); + + if (_lyricsXTransition.IsTransitioning) { - _maxLyricsWidthTransition.Update(ElapsedTime); _isRelayoutNeeded = true; } - switch (DisplayType) - { - case Enums.LyricsDisplayType.AlbumArtOnly: - _lyricsOpacityTransition.StartTransition(0f); - break; - case Enums.LyricsDisplayType.LyricsOnly: - case Enums.LyricsDisplayType.SplitView: - _lyricsOpacityTransition.StartTransition(1f); - break; - case Enums.LyricsDisplayType.PlaceholderOnly: - break; - default: - break; - } - - if (_lyricsOpacityTransition.IsTransitioning) - { - _lyricsOpacityTransition.Update(ElapsedTime); - } - - // 神光角度目标值(左右±15度摆动,周期约4秒) - double t = DateTimeOffset.Now.ToUnixTimeMilliseconds() / 1000.0; - float targetAngle = (float)(-Math.PI / 2 + Math.Sin(t * Math.PI / 2) * (Math.PI / 12)); // -90°为正上,±15°摆动 - _shenGuangAngleTransition.StartTransition(targetAngle); - - if (_shenGuangAngleTransition.IsTransitioning) - { - _shenGuangAngleTransition.Update(ElapsedTime); - } + _maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin; + _maxLyricsWidth = Math.Max(_maxLyricsWidth, 0); if (_isRelayoutNeeded) { @@ -93,9 +107,9 @@ namespace BetterLyrics.WinUI3.ViewModels if (control == null) return; - _textFormat.FontSize = LyricsFontSize; + _lyricsTextFormat.FontSize = LyricsFontSize; - float y = _topMargin; + float y = 0; // Init Positions for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++) @@ -117,9 +131,9 @@ namespace BetterLyrics.WinUI3.ViewModels line.CanvasTextLayout = new CanvasTextLayout( control, line.Text, - _textFormat, - (float)_maxLyricsWidthTransition.Value, - (float)control.Size.Height + _lyricsTextFormat, + _maxLyricsWidth, + _canvasHeight ); line.Position = new Vector2(0, y); @@ -167,13 +181,10 @@ namespace BetterLyrics.WinUI3.ViewModels } else if (!withAnimation) { - _canvasYScrollTransition.JumpTo(targetYScrollOffset); + _canvasYScrollTransition.StartTransition(targetYScrollOffset, true); } - if (_canvasYScrollTransition.IsTransitioning) - { - _canvasYScrollTransition.Update(ElapsedTime); - } + _canvasYScrollTransition.Update(ElapsedTime); _startVisibleLineIndex = _endVisibleLineIndex = -1; @@ -191,7 +202,7 @@ namespace BetterLyrics.WinUI3.ViewModels if ( _canvasYScrollTransition.Value - + (float)(control.Size.Height / 2) + + _canvasHeight / 2 + line.Position.Y + textLayout.LayoutBounds.Height >= 0 @@ -204,7 +215,7 @@ namespace BetterLyrics.WinUI3.ViewModels } if ( _canvasYScrollTransition.Value - + (float)(control.Size.Height / 2) + + _canvasHeight / 2 + line.Position.Y + textLayout.LayoutBounds.Height >= control.Size.Height @@ -225,8 +236,7 @@ namespace BetterLyrics.WinUI3.ViewModels private protected void UpdateFontColor() { - ThemeTypeSent = - Helper.ColorHelper.GetElementThemeFromBackgroundColor(_lyricsWindowBgColor); + ThemeTypeSent = Helper.ColorHelper.GetElementThemeFromBackgroundColor(_lyricsWindowBgColor); Color fallbackFg = Colors.Transparent; switch (ThemeTypeSent) @@ -261,13 +271,11 @@ namespace BetterLyrics.WinUI3.ViewModels { var currentPlayingLineIndex = GetCurrentPlayingLineIndex(); - int halfVisibleLineCount = - Math.Max(1, Math.Max( - currentPlayingLineIndex - _startVisibleLineIndex, - _endVisibleLineIndex - currentPlayingLineIndex - )); + var currentPlayingLine = _multiLangLyrics + .SafeGet(_langIndex) + ?.SafeGet(currentPlayingLineIndex); - if (halfVisibleLineCount < 1) + if (currentPlayingLine == null) { return; } @@ -281,61 +289,45 @@ namespace BetterLyrics.WinUI3.ViewModels continue; } - int distanceFromPlayingLine = Math.Abs(i - currentPlayingLineIndex); - if (distanceFromPlayingLine > halfVisibleLineCount) - { - continue; - } + float distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y); - float distanceFactor = distanceFromPlayingLine / (float)halfVisibleLineCount; + float distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1); - line.AngleTransition.StartTransition( - _isFanLyricsEnabled - ? (float)Math.PI - * (30f / 180f) - * distanceFactor - * (i - currentPlayingLineIndex > 0 ? 1 : -1) - : 0 - ); - line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor); - line.ScaleTransition.StartTransition( + if (!line.AngleTransition.IsTransitioning) + line.AngleTransition.StartTransition( + _isFanLyricsEnabled + ? (float)Math.PI + * (30f / 180f) + * distanceFactor + * (i - currentPlayingLineIndex > 0 ? 1 : -1) + : 0 + ); + + if (!line.BlurAmountTransition.IsTransitioning) + line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor); + + if (!line.ScaleTransition.IsTransitioning) + line.ScaleTransition.StartTransition( _highlightedScale - distanceFactor * (_highlightedScale - _defaultScale) ); - line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f)); + + if (!line.OpacityTransition.IsTransitioning) + line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f)); // Only calculate highlight opacity for the current line and the two lines around it // to avoid unnecessary calculations - if (distanceFromPlayingLine <= 1) + if (!line.HighlightOpacityTransition.IsTransitioning) { line.HighlightOpacityTransition.StartTransition( - distanceFromPlayingLine == 0 ? 1 : 0 + distanceFromPlayingLine == 0 ? 1f : 0f ); } - if (line.AngleTransition.IsTransitioning) - { - line.AngleTransition.Update(ElapsedTime); - } - if (line.ScaleTransition.IsTransitioning) - { - line.ScaleTransition.Update(ElapsedTime); - } - if (line.BlurAmountTransition.IsTransitioning) - { - line.BlurAmountTransition.Update(ElapsedTime); - } - if (line.OpacityTransition.IsTransitioning) - { - line.OpacityTransition.Update(ElapsedTime); - } - // Only update highlight opacity for the current line and the two lines around it - if (distanceFromPlayingLine <= 1) - { - if (line.HighlightOpacityTransition.IsTransitioning) - { - line.HighlightOpacityTransition.Update(ElapsedTime); - } - } + line.AngleTransition.Update(ElapsedTime); + line.ScaleTransition.Update(ElapsedTime); + line.BlurAmountTransition.Update(ElapsedTime); + line.OpacityTransition.Update(ElapsedTime); + line.HighlightOpacityTransition.Update(ElapsedTime); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs index f4b3384..43b0efc 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Events; @@ -9,8 +11,11 @@ using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Services; using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; +using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.UI; +using Microsoft.UI.Text; using Microsoft.UI.Xaml; using Windows.Graphics.Imaging; using Windows.UI; @@ -19,103 +24,92 @@ namespace BetterLyrics.WinUI3.ViewModels { public partial class LyricsRendererViewModel : BaseViewModel { - private readonly ValueTransition _albumArtBgTransition = new( - initialValue: 0f, - durationSeconds: 1.0f - ); + private SoftwareBitmap? _lastAlbumArtSwBitmap = null; + private SoftwareBitmap? _albumArtSwBitmap = null; - private readonly ValueTransition _canvasYScrollTransition = new( - initialValue: 0f, - durationSeconds: 0.8f, - easingType: EasingType.SmootherStep - ); + private float _albumArtSize = 0f; + private int _albumArtCornerRadius = 0; - private readonly float _coverRotateSpeed = 0.003f; + private float _albumArtY = 0f; + + private string? _lastSongTitle; + private string? _songTitle; + + private string? _lastSongArtist; + private string? _songArtist; + + private float _canvasWidth = 0f; + private float _canvasHeight = 0f; private readonly float _defaultOpacity = 0.3f; - - private readonly float _defaultScale = 0.75f; - private readonly float _highlightedOpacity = 1.0f; + private readonly float _defaultScale = 0.75f; private readonly float _highlightedScale = 1.0f; - private readonly ValueTransition _immersiveBgTransition = new( - initialValue: Colors.Transparent, - durationSeconds: 0.3f, - interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to) - ); - - private readonly ILibWatcherService _libWatcherService; + private readonly float _coverRotateSpeed = 0.003f; + private float _rotateAngle = 0f; private readonly float _lyricsGlowEffectAmount = 8f; - private readonly ValueTransition _maxLyricsWidthTransition = new( - initialValue: 0f, - durationSeconds: 0.3f, - easingType: EasingType.SmoothStep - ); + private float _maxLyricsWidth = 0f; - private readonly ValueTransition _lyricsOpacityTransition = new( - initialValue: 0f, - durationSeconds: 0.3f - ); - - private protected readonly IMusicSearchService _musicSearchService; - - private protected readonly IPlaybackService _playbackService; + private readonly IMusicSearchService _musicSearchService; + private readonly ILibWatcherService _libWatcherService; + private readonly IPlaybackService _playbackService; + private readonly float _leftMargin = 36f; + private readonly float _middleMargin = 36f; private readonly float _rightMargin = 36f; + private readonly float _topMargin = 36f; + private readonly float _bottomMargin = 36f; - private readonly ValueTransition _shenGuangAngleTransition = new(0f, 0.2f); - - private readonly float _topMargin = 0f; - + private Color _fontColor; private Color? _adaptiveFontColor = null; - - private Color? _albumArtAccentColor = null; - - private SoftwareBitmap? _albumArtBitmap = null; - private Color? _customFontColor; - + private Color _lightFontColor = Colors.White; private Color _darkFontColor = Colors.Black; + private Color? _albumArtAccentColor = null; + private Color _lyricsWindowBgColor = Colors.Transparent; + private int _startVisibleLineIndex = -1; private int _endVisibleLineIndex = -1; - private protected Color _fontColor; - private bool _isDebugOverlayEnabled = false; - private bool _isDesktopMode = false; - private bool _isDockMode = false; - private bool _isFanLyricsEnabled = false; private bool _isPlaying = true; - private protected bool _isRelayoutNeeded = true; + private bool _isRelayoutNeeded = true; private int _langIndex = 0; - private SoftwareBitmap? _lastAlbumArtBitmap = null; - - private Color _lightFontColor = Colors.White; - - private Color _lyricsWindowBgColor = Colors.Transparent; - private List> _multiLangLyrics = []; - private float _rotateAngle = 0f; - - private int _startVisibleLineIndex = -1; - - private protected CanvasTextFormat _textFormat = new() + private CanvasTextFormat _lyricsTextFormat = new() { HorizontalAlignment = CanvasHorizontalAlignment.Left, VerticalAlignment = CanvasVerticalAlignment.Top, }; + private CanvasTextFormat _titleTextFormat = new() + { + FontSize = 18, + FontWeight = FontWeights.Bold, + HorizontalAlignment = CanvasHorizontalAlignment.Left, + WordWrapping = CanvasWordWrapping.Wrap, + }; + private CanvasTextFormat _artistTextFormat = new() + { + FontSize = 16, + FontWeight = FontWeights.Bold, + HorizontalAlignment = CanvasHorizontalAlignment.Left, + WordWrapping = CanvasWordWrapping.Wrap, + }; + + private Task? _refreshLyricsTask; + private CancellationTokenSource? _refreshLyricsCts; public LyricsRendererViewModel( ISettingsService settingsService, IPlaybackService playbackService, @@ -125,7 +119,7 @@ namespace BetterLyrics.WinUI3.ViewModels _playbackService = playbackService; _libWatcherService = libWatcherService; - CoverImageRadius = _settingsService.CoverImageRadius; + _albumArtCornerRadius = _settingsService.CoverImageRadius; IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled; CoverOverlayOpacity = _settingsService.CoverOverlayOpacity; CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount; @@ -141,6 +135,8 @@ namespace BetterLyrics.WinUI3.ViewModels LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope; _customFontColor = _settingsService.LyricsCustomFontColor; + _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.LyricsAlignmentType.ToCanvasHorizontalAlignment(); + _libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged; @@ -148,20 +144,15 @@ namespace BetterLyrics.WinUI3.ViewModels _playbackService.SongInfoChanged += PlaybackService_SongInfoChanged; _playbackService.PositionChanged += PlaybackService_PositionChanged; - _isPlaying = _playbackService.IsPlaying; - SongInfo = _playbackService.SongInfo; - TotalTime = _playbackService.Position; - UpdateFontColor(); } - public int CoverImageRadius { get; set; } - public int CoverOverlayBlurAmount { get; set; } public int CoverOverlayOpacity { get; set; } - public LyricsDisplayType DisplayType { get; set; } + private LyricsDisplayType _displayTypeReceived = LyricsDisplayType.PlaceholderOnly; + private LyricsDisplayType _displayType = LyricsDisplayType.PlaceholderOnly; public TimeSpan ElapsedTime { get; set; } = TimeSpan.Zero; @@ -169,7 +160,7 @@ namespace BetterLyrics.WinUI3.ViewModels public bool IsLyricsGlowEffectEnabled { get; set; } - public LyricsAlignmentType LyricsAlignmentType { get; set; } + public TextAlignmentType LyricsAlignmentType { get; set; } public int LyricsBlurAmount { get; set; } @@ -303,36 +294,88 @@ namespace BetterLyrics.WinUI3.ViewModels TotalTime = e.Position; } - private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e) + private async void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e) { SongInfo = e.SongInfo; + if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist) + { + _lastSongTitle = _songTitle; + _songTitle = SongInfo?.Title; + + _lastSongArtist = _songArtist; + _songArtist = SongInfo?.Artist; + + _songInfoOpacityTransition.Reset(0f); + _songInfoOpacityTransition.StartTransition(1f); + + await RefreshLyricsAsync(); + + TotalTime = TimeSpan.Zero; + } + + if (SongInfo?.AlbumArtSwBitmap != _albumArtSwBitmap) + { + _lastAlbumArtSwBitmap = _albumArtSwBitmap; + _albumArtSwBitmap = SongInfo?.AlbumArtSwBitmap; + + _albumArtAccentColor = SongInfo?.AlbumArtAccentColor; + _lyricsWindowBgColor = _albumArtAccentColor ?? Colors.Gray; + + _albumArtBgTransition.Reset(0f); + _albumArtBgTransition.StartTransition(1f); + + if (!_isDesktopMode && !_isDockMode) _adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor); + UpdateFontColor(); + } } private async Task RefreshLyricsAsync() { - SetLyricsLoadingPlaceholder(); - - string? lyricsRaw = null; - LyricsFormat? lyricsFormat = null; - - if (SongInfo != null) + // 取消上一次 + _refreshLyricsCts?.Cancel(); + if (_refreshLyricsTask != null) { - (lyricsRaw, lyricsFormat) = await _musicSearchService.SearchLyricsAsync( - SongInfo.Title, - SongInfo.Artist, - SongInfo.Album ?? "", - SongInfo.DurationMs ?? 0 - ); + await _refreshLyricsTask; } - _multiLangLyrics = new LyricsParser().Parse( - lyricsRaw, - lyricsFormat, - SongInfo?.Title, - SongInfo?.Artist, - (int)(SongInfo?.DurationMs ?? 0) - ); - _isRelayoutNeeded = true; + var cts = new CancellationTokenSource(); + _refreshLyricsCts = cts; + var token = cts.Token; + + _refreshLyricsTask = RefreshLyricsCoreAsync(token); + await _refreshLyricsTask; + } + + private async Task RefreshLyricsCoreAsync(CancellationToken token) + { + try + { + SetLyricsLoadingPlaceholder(); + + string? lyricsRaw = null; + LyricsFormat? lyricsFormat = null; + + if (SongInfo != null) + { + (lyricsRaw, lyricsFormat) = await _musicSearchService.SearchLyricsAsync( + SongInfo.Title, + SongInfo.Artist, + SongInfo.Album ?? "", + SongInfo.DurationMs ?? 0 + ); + token.ThrowIfCancellationRequested(); + } + + _multiLangLyrics = new LyricsParser().Parse( + lyricsRaw, + lyricsFormat, + SongInfo?.Title, + SongInfo?.Artist, + (int)(SongInfo?.DurationMs ?? 0) + ); + _isRelayoutNeeded = true; + } + catch (Exception) { } } private void SetLyricsLoadingPlaceholder() @@ -344,7 +387,7 @@ namespace BetterLyrics.WinUI3.ViewModels { StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds, - Text = App.ResourceLoader!.GetString("LyricsLoading"), + Text = "● ● ●", CharTimings = [], }, ] diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsSettingsControlViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsSettingsControlViewModel.cs deleted file mode 100644 index 0db0439..0000000 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsSettingsControlViewModel.cs +++ /dev/null @@ -1,130 +0,0 @@ -// 2025/6/23 by Zhe Fang - -using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Services; -using BetterLyrics.WinUI3.ViewModels; -using CommunityToolkit.Mvvm.ComponentModel; -using Windows.UI; - -namespace BetterInAppLyrics.WinUI3.ViewModels -{ - public partial class LyricsSettingsControlViewModel : BaseViewModel - { - public LyricsSettingsControlViewModel(ISettingsService settingsService) - : base(settingsService) - { - IsActive = true; - - LyricsAlignmentType = _settingsService.LyricsAlignmentType; - LyricsFontWeight = _settingsService.LyricsFontWeight; - LyricsBlurAmount = _settingsService.LyricsBlurAmount; - LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity; - LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor; - LyricsFontSize = _settingsService.LyricsFontSize; - IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled; - LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope; - IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled; - LyricsFontColorType = _settingsService.LyricsFontColorType; - LyricsCustomFontColor = _settingsService.LyricsCustomFontColor; - } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial bool IsFanLyricsEnabled { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial bool IsLyricsGlowEffectEnabled { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial LyricsAlignmentType LyricsAlignmentType { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial int LyricsBlurAmount { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial Color LyricsCustomFontColor { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial LyricsFontColorType LyricsFontColorType { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial int LyricsFontSize { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial LyricsFontWeight LyricsFontWeight { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial LineRenderingType LyricsGlowEffectScope { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial float LyricsLineSpacingFactor { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial int LyricsVerticalEdgeOpacity { get; set; } - - partial void OnIsFanLyricsEnabledChanged(bool value) - { - _settingsService.IsFanLyricsEnabled = value; - } - - partial void OnIsLyricsGlowEffectEnabledChanged(bool value) - { - _settingsService.IsLyricsGlowEffectEnabled = value; - } - - partial void OnLyricsAlignmentTypeChanged(LyricsAlignmentType value) - { - _settingsService.LyricsAlignmentType = value; - } - - partial void OnLyricsBlurAmountChanged(int value) - { - _settingsService.LyricsBlurAmount = value; - } - - partial void OnLyricsCustomFontColorChanged(Color value) - { - _settingsService.LyricsCustomFontColor = value; - } - - partial void OnLyricsFontColorTypeChanged(LyricsFontColorType value) - { - _settingsService.LyricsFontColorType = value; - } - - partial void OnLyricsFontSizeChanged(int value) - { - _settingsService.LyricsFontSize = value; - } - - partial void OnLyricsFontWeightChanged(LyricsFontWeight value) - { - _settingsService.LyricsFontWeight = value; - } - - partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value) - { - _settingsService.LyricsGlowEffectScope = value; - } - - partial void OnLyricsLineSpacingFactorChanged(float value) - { - _settingsService.LyricsLineSpacingFactor = value; - } - - partial void OnLyricsVerticalEdgeOpacityChanged(int value) - { - _settingsService.LyricsVerticalEdgeOpacity = value; - } - } -} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs index a84108d..f2f0a1f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs @@ -1,7 +1,6 @@ // 2025/6/23 by Zhe Fang using System.Threading.Tasks; -using BetterInAppLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Messages; @@ -104,9 +103,9 @@ namespace BetterLyrics.WinUI3 public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsSettingsControlViewModel) + if (message.Sender is SettingsPageViewModel) { - if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize)) + if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize)) { if (IsDockMode) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs index ba33b43..96a289e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs @@ -1,6 +1,7 @@ // 2025/6/23 by Zhe Fang using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; @@ -19,19 +20,20 @@ using Microsoft.UI.Xaml; using Windows.Globalization; using Windows.Media.Playback; using Windows.System; +using Windows.UI; using WinRT.Interop; namespace BetterLyrics.WinUI3.ViewModels { - public partial class SettingsPageViewModel : ObservableRecipient + public partial class SettingsPageViewModel : BaseViewModel { private readonly ILibWatcherService _libWatcherService; - private readonly ISettingsService _settingsService; + private readonly IPlaybackService _playbackService; - public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) + public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService) : base(settingsService) { - _settingsService = settingsService; _libWatcherService = libWatcherService; + _playbackService = playbackService; LocalLyricsFolders = [.. _settingsService.LocalLyricsFolders]; LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo]; @@ -46,12 +48,33 @@ namespace BetterLyrics.WinUI3.ViewModels CoverOverlayOpacity = _settingsService.CoverOverlayOpacity; CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount; + LyricsAlignmentType = _settingsService.LyricsAlignmentType; + SongInfoAlignmentType = _settingsService.SongInfoAlignmentType; + LyricsFontWeight = _settingsService.LyricsFontWeight; + LyricsBlurAmount = _settingsService.LyricsBlurAmount; + LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity; + LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor; + LyricsFontSize = _settingsService.LyricsFontSize; + IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled; + LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope; + IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled; + LyricsFontColorType = _settingsService.LyricsFontColorType; + LyricsCustomFontColor = _settingsService.LyricsCustomFontColor; + + MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo]; + _playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged; + Task.Run(async () => { BuildDate = (await AppInfo.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)"); }); } + private void PlaybackService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e) + { + MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo]; + } + [ObservableProperty] public partial AutoStartWindowType AutoStartWindowType { get; set; } @@ -88,12 +111,63 @@ namespace BetterLyrics.WinUI3.ViewModels [NotifyPropertyChangedRecipients] public partial ObservableCollection LyricsSearchProvidersInfo { get; set; } + [ObservableProperty] + public partial ObservableCollection MediaSourceProvidersInfo { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial bool IsFanLyricsEnabled { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial bool IsLyricsGlowEffectEnabled { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial TextAlignmentType LyricsAlignmentType { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial TextAlignmentType SongInfoAlignmentType { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial int LyricsBlurAmount { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial Color LyricsCustomFontColor { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial LyricsFontColorType LyricsFontColorType { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial int LyricsFontSize { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial LyricsFontWeight LyricsFontWeight { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial LineRenderingType LyricsGlowEffectScope { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial float LyricsLineSpacingFactor { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial int LyricsVerticalEdgeOpacity { get; set; } + [ObservableProperty] public partial object NavViewSelectedItemTag { get; set; } public string Version { get; set; } = Helper.AppInfo.AppVersion; - public string BuildDate { get; set; } + public string BuildDate { get; set; } = string.Empty; public void OnLyricsSearchProvidersReordered() { @@ -134,6 +208,15 @@ namespace BetterLyrics.WinUI3.ViewModels ); } + public void ToggleMediaSourceProvider(MediaSourceProviderInfo providerInfo) + { + Broadcast( + MediaSourceProvidersInfo, + MediaSourceProvidersInfo, + nameof(MediaSourceProvidersInfo) + ); + } + private void AddFolderAsync(string path) { var normalizedPath = @@ -315,5 +398,65 @@ namespace BetterLyrics.WinUI3.ViewModels } _settingsService.Language = Language; } + + partial void OnIsFanLyricsEnabledChanged(bool value) + { + _settingsService.IsFanLyricsEnabled = value; + } + + partial void OnIsLyricsGlowEffectEnabledChanged(bool value) + { + _settingsService.IsLyricsGlowEffectEnabled = value; + } + + partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value) + { + _settingsService.LyricsAlignmentType = value; + } + + partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value) + { + _settingsService.SongInfoAlignmentType = value; + } + + partial void OnLyricsBlurAmountChanged(int value) + { + _settingsService.LyricsBlurAmount = value; + } + + partial void OnLyricsCustomFontColorChanged(Color value) + { + _settingsService.LyricsCustomFontColor = value; + } + + partial void OnLyricsFontColorTypeChanged(LyricsFontColorType value) + { + _settingsService.LyricsFontColorType = value; + } + + partial void OnLyricsFontSizeChanged(int value) + { + _settingsService.LyricsFontSize = value; + } + + partial void OnLyricsFontWeightChanged(LyricsFontWeight value) + { + _settingsService.LyricsFontWeight = value; + } + + partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value) + { + _settingsService.LyricsGlowEffectScope = value; + } + + partial void OnLyricsLineSpacingFactorChanged(float value) + { + _settingsService.LyricsLineSpacingFactor = value; + } + + partial void OnLyricsVerticalEdgeOpacityChanged(int value) + { + _settingsService.LyricsVerticalEdgeOpacity = value; + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml index b29f9b4..27d6e3d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml @@ -20,224 +20,25 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + - - + + + + + + + + - - - - - + - - +