mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94e76a6ac9 | ||
|
|
516d388009 | ||
|
|
ad33eed57c | ||
|
|
64bf2dc3d9 | ||
|
|
b86e4a3d12 | ||
|
|
411506b9cd |
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.53.0" />
|
||||
Version="1.0.56.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<None Remove="Controls\AllLyricsSettingsControl.xaml" />
|
||||
<None Remove="Controls\AppSettingsControl.xaml" />
|
||||
<None Remove="Controls\ExtendedSlider.xaml" />
|
||||
<None Remove="Controls\LyricsBavkgroundSettingsControl.xaml" />
|
||||
<None Remove="Controls\LyricsSettingsControl.xaml" />
|
||||
<None Remove="Controls\MediaSettingsControl.xaml" />
|
||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||
@@ -61,6 +60,7 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.8" />
|
||||
@@ -87,11 +87,6 @@
|
||||
<PackageReference Include="WinUIEx" Version="2.6.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Hqub.Lastfm">
|
||||
<HintPath>..\..\..\Last.fm\src\Hqub.Lastfm\bin\Release\netstandard2.0\Hqub.Lastfm.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\InAppLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -222,7 +217,7 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\LyricsBavkgroundSettingsControl.xaml">
|
||||
<Page Update="Controls\LyricsBackgroundSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartPIPLyrics" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.LyricsBavkgroundSettingsControl"
|
||||
x:Class="BetterLyrics.WinUI3.Controls.LyricsBackgroundSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
@@ -20,11 +20,11 @@ using Windows.Foundation.Collections;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class LyricsBavkgroundSettingsControl : UserControl
|
||||
public sealed partial class LyricsBackgroundSettingsControl : UserControl
|
||||
{
|
||||
public LyricsBackgroundSettingsControlViewModel ViewModel => (LyricsBackgroundSettingsControlViewModel)DataContext;
|
||||
|
||||
public LyricsBavkgroundSettingsControl()
|
||||
public LyricsBackgroundSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<LyricsBackgroundSettingsControlViewModel>();
|
||||
@@ -234,14 +234,46 @@
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsLineFadeEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- 高亮 -->
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsHighlightScope" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<!-- 译文高亮 -->
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageLyricsTranslationHighlight"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageAmount">
|
||||
<local:ExtendedSlider
|
||||
Default="60"
|
||||
Frequency="5"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsTranslationHighlightAmount, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<!-- 原文高亮 -->
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageLyricsHighlightScope"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsHighlightScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageAmount">
|
||||
<local:ExtendedSlider
|
||||
Default="100"
|
||||
Frequency="5"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsHighlightAmount, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<!-- 阴影 -->
|
||||
<controls:SettingsExpander
|
||||
@@ -319,6 +351,7 @@
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- 滚动动画 -->
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageScrollEasing"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
@@ -341,7 +374,6 @@
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollTopDuration">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsScrollTopDurationExtendedSlider"
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
@@ -351,7 +383,6 @@
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollDuration">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsScrollDurationExtendedSlider"
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
@@ -361,7 +392,6 @@
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollBottomDuration">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsScrollBottomDurationExtendedSlider"
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
@@ -369,6 +399,24 @@
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollTopDelay">
|
||||
<local:ExtendedSlider
|
||||
Default="0"
|
||||
Frequency="50"
|
||||
Maximum="2000"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDelay, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageScrollBottomDelay">
|
||||
<local:ExtendedSlider
|
||||
Default="0"
|
||||
Frequency="50"
|
||||
Maximum="2000"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDelay, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Button
|
||||
Margin="3,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="CheckButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
@@ -91,5 +92,22 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
Shortcut = [];
|
||||
UpdateTextBox();
|
||||
}
|
||||
|
||||
private void CheckButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool registered = GlobalHotKeyHelper.IsHotKeyRegistered(Shortcut);
|
||||
if (registered)
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString("SettingsPageShortcutRegSuccessInfo"),
|
||||
InfoBarSeverity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString("SettingsPageShortcutRegFailInfo"),
|
||||
InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 LyricsChangedEventArgs(LyricsData? lyricsData) : EventArgs
|
||||
{
|
||||
public LyricsData? LyricsData { get; } = lyricsData;
|
||||
}
|
||||
}
|
||||
@@ -262,17 +262,50 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static CanvasCommandList CreateTranslationHighlightMask(ICanvasAnimatedControl control, LyricsLine lyricsLine)
|
||||
{
|
||||
var mask = new CanvasCommandList(control);
|
||||
using var ds = mask.CreateDrawingSession();
|
||||
|
||||
if (lyricsLine.CanvasTextLayout == null)
|
||||
{
|
||||
return mask;
|
||||
}
|
||||
|
||||
var regions = lyricsLine.CanvasTextLayout.GetCharacterRegions(lyricsLine.OriginalText.Length, lyricsLine.DisplayedText.Length - lyricsLine.OriginalText.Length);
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
for (int j = 0; j < regions.Length; j++)
|
||||
{
|
||||
var region = regions[j];
|
||||
var rect = new Rect(
|
||||
region.LayoutBounds.X,
|
||||
region.LayoutBounds.Y + lyricsLine.Position.Y,
|
||||
region.LayoutBounds.Width,
|
||||
region.LayoutBounds.Height
|
||||
);
|
||||
ds.FillRectangle(rect, Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建高亮效果层
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="lineRenderingType"></param>
|
||||
public static AlphaMaskEffect CreateForegroundHighlightEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask)
|
||||
public static OpacityEffect CreateForegroundHighlightEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double opacity)
|
||||
{
|
||||
return new AlphaMaskEffect
|
||||
return new OpacityEffect
|
||||
{
|
||||
Source = foregroundFontEffect,
|
||||
AlphaMask = mask,
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = foregroundFontEffect,
|
||||
AlphaMask = mask,
|
||||
},
|
||||
Opacity = (float)opacity,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,6 +324,19 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
};
|
||||
}
|
||||
|
||||
public static OpacityEffect CreateForegroundTranslationEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double opacity)
|
||||
{
|
||||
return new OpacityEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = foregroundFontEffect,
|
||||
AlphaMask = mask,
|
||||
},
|
||||
Opacity = (float)opacity,
|
||||
};
|
||||
}
|
||||
|
||||
public static IGraphicsEffectSource GetAlphaMask(ICanvasAnimatedControl control, IGraphicsEffectSource charMask, IGraphicsEffectSource lineStartToCharMask, IGraphicsEffectSource lineMask, LineRenderingType lineRenderingType)
|
||||
{
|
||||
var result = lineRenderingType switch
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class GlobalHotKeyHelper
|
||||
{
|
||||
private static Dictionary<int, Action> _hotKeyActions = [];
|
||||
private static Dictionary<int, Action> _actions = [];
|
||||
private static Dictionary<int, List<string>> _keys = [];
|
||||
|
||||
/// <summary>
|
||||
/// Register a global hotkey for a specific window type
|
||||
@@ -22,7 +23,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
/// <param name="id"></param>
|
||||
/// <param name="keys"></param>
|
||||
/// <param name="action"></param>
|
||||
public static void RegisterHotKey<T>(ShortcutID id, List<string> keys, Action action)
|
||||
private static void RegisterHotKey<T>(ShortcutID id, List<string> keys, Action action)
|
||||
{
|
||||
if (keys.Count == 0) return;
|
||||
|
||||
@@ -55,18 +56,23 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
key = (VirtualKey)Enum.Parse(typeof(VirtualKey), item, true);
|
||||
}
|
||||
}
|
||||
User32.RegisterHotKey(hwnd, (int)id, modifiers, (uint)key);
|
||||
_hotKeyActions[(int)id] = action;
|
||||
bool success = User32.RegisterHotKey(hwnd, (int)id, modifiers, (uint)key);
|
||||
if (success)
|
||||
{
|
||||
_actions[(int)id] = action;
|
||||
_keys[(int)id] = keys;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterHotKey<T>(ShortcutID id)
|
||||
private static void UnregisterHotKey<T>(ShortcutID id)
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<T>();
|
||||
if (window == null) return;
|
||||
|
||||
HWND hwnd = WindowNative.GetWindowHandle(window);
|
||||
User32.UnregisterHotKey(hwnd, (int)id);
|
||||
_hotKeyActions.Remove((int)id);
|
||||
_actions.Remove((int)id);
|
||||
_keys.Remove((int)id);
|
||||
}
|
||||
|
||||
public static void UpdateHotKey<T>(ShortcutID id, List<string> keys, Action action)
|
||||
@@ -75,6 +81,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
RegisterHotKey<T>(id, keys, action);
|
||||
}
|
||||
|
||||
public static bool IsHotKeyRegistered(ShortcutID id)
|
||||
{
|
||||
return _actions.ContainsKey((int)id);
|
||||
}
|
||||
|
||||
public static bool IsHotKeyRegistered(List<string> keys)
|
||||
{
|
||||
return _keys.ContainsValue(keys);
|
||||
}
|
||||
|
||||
public static bool TryInvokeAction(ShortcutID id)
|
||||
{
|
||||
return TryInvokeAction((int)id);
|
||||
@@ -82,7 +98,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static bool TryInvokeAction(int id)
|
||||
{
|
||||
if (_hotKeyActions.TryGetValue(id, out var action))
|
||||
if (_actions.TryGetValue(id, out var action))
|
||||
{
|
||||
action?.Invoke();
|
||||
return true;
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
square.Mutate(ctx => ctx.DrawImage(image, new Point(offsetX, offsetY), 1f));
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
square.Save(ms, new JpegEncoder());
|
||||
square.Save(ms, new PngEncoder());
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
_identifier = _factory.Load(PathHelper.LanguageProfilePath);
|
||||
}
|
||||
|
||||
private static string SimplifiedChineseOrTraditionalChinese(string text)
|
||||
{
|
||||
return text == ChineseConverter.ConvertToSimplifiedChinese(text) ? "zh-Hans" : "zh-Hant";
|
||||
}
|
||||
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
@@ -67,9 +72,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
code = code switch
|
||||
{
|
||||
"simple" => "en",
|
||||
"zh_classical" => "zh-Hant",
|
||||
"zh_yue" => "zh-Hant",
|
||||
"zh" => text == ChineseConverter.ConvertToSimplifiedChinese(text) ? "zh-Hans" : "zh-Hant",
|
||||
"zh_classical" => SimplifiedChineseOrTraditionalChinese(text),
|
||||
"zh_yue" => SimplifiedChineseOrTraditionalChinese(text),
|
||||
"zh" => SimplifiedChineseOrTraditionalChinese(text),
|
||||
_ => code
|
||||
};
|
||||
return code;
|
||||
|
||||
@@ -7,11 +7,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class BackgroundTaskRunner
|
||||
public class LatestOnlyTaskRunner
|
||||
{
|
||||
private CancellationTokenSource? _cts;
|
||||
|
||||
public void Run(Func<CancellationToken, Task> taskFactory)
|
||||
public async Task RunAsync(Func<CancellationToken, Task> taskFactory)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
_cts?.Dispose();
|
||||
@@ -19,19 +19,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await taskFactory(token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}, token);
|
||||
await taskFactory(token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class PointHelper
|
||||
{
|
||||
public static PointInt32 ToPointInt32(this Point point)
|
||||
{
|
||||
return new PointInt32((int)point.X, (int)point.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
@@ -11,6 +10,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
private T _currentValue;
|
||||
private double _durationSeconds;
|
||||
private double _delaySeconds;
|
||||
private double _delayRemaining;
|
||||
private EasingType? _easingType;
|
||||
private Func<T, T, double, T> _interpolator;
|
||||
private bool _isTransitioning;
|
||||
@@ -19,18 +20,21 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private T _targetValue;
|
||||
|
||||
public double DurationSeconds => _durationSeconds;
|
||||
public double DelaySeconds => _delaySeconds;
|
||||
|
||||
public bool IsTransitioning => _isTransitioning;
|
||||
public T Value => _currentValue;
|
||||
public T TargetValue => _targetValue;
|
||||
public EasingType? EasingType => _easingType;
|
||||
|
||||
public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null)
|
||||
public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null, double delaySeconds = 0)
|
||||
{
|
||||
_currentValue = initialValue;
|
||||
_startValue = initialValue;
|
||||
_targetValue = initialValue;
|
||||
_durationSeconds = durationSeconds;
|
||||
_delaySeconds = delaySeconds;
|
||||
_delayRemaining = 0;
|
||||
_progress = 1f;
|
||||
_isTransitioning = false;
|
||||
|
||||
@@ -58,12 +62,18 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_durationSeconds = seconds;
|
||||
}
|
||||
|
||||
public void SetDelay(double seconds)
|
||||
{
|
||||
_delaySeconds = seconds;
|
||||
}
|
||||
|
||||
private void JumpTo(T value)
|
||||
{
|
||||
_currentValue = value;
|
||||
_startValue = value;
|
||||
_targetValue = value;
|
||||
_progress = 1f;
|
||||
_delayRemaining = 0;
|
||||
_isTransitioning = false;
|
||||
}
|
||||
|
||||
@@ -73,6 +83,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_startValue = value;
|
||||
_targetValue = value;
|
||||
_progress = 0f;
|
||||
_delayRemaining = 0;
|
||||
_isTransitioning = false;
|
||||
}
|
||||
|
||||
@@ -89,6 +100,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_startValue = _currentValue;
|
||||
_targetValue = targetValue;
|
||||
_progress = 0f;
|
||||
_delayRemaining = _delaySeconds;
|
||||
_isTransitioning = true;
|
||||
}
|
||||
}
|
||||
@@ -103,7 +115,24 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
if (!_isTransitioning) return;
|
||||
|
||||
_progress += (double)(elapsedTime / TimeSpan.FromSeconds(_durationSeconds));
|
||||
if (_delayRemaining > 0)
|
||||
{
|
||||
double consume = Math.Min(_delayRemaining, elapsedTime.TotalSeconds);
|
||||
_delayRemaining -= consume;
|
||||
if (_delayRemaining > 0)
|
||||
return;
|
||||
elapsedTime = TimeSpan.FromSeconds(elapsedTime.TotalSeconds - consume);
|
||||
}
|
||||
|
||||
if (_durationSeconds <= 0)
|
||||
{
|
||||
_progress = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_progress += elapsedTime.TotalSeconds / _durationSeconds;
|
||||
}
|
||||
|
||||
if (_progress >= 1f)
|
||||
{
|
||||
_progress = 1f;
|
||||
@@ -178,4 +207,4 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_interpolator = GetInterpolatorByEasingType(easingType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ namespace BetterLyrics.WinUI3.Models
|
||||
PositionOffset = 1000;
|
||||
break;
|
||||
default:
|
||||
// 设置 100 以防不必要的重复同步
|
||||
TimelineSyncThreshold = 100;
|
||||
// 设置 300 以防不必要的重复同步
|
||||
TimelineSyncThreshold = 300;
|
||||
PositionOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsShadowAmount { get; set; } = 8;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType LyricsHighlightScope { get; set; } = LineRenderingType.LineStartToCurrentChar;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsHighlightAmount { get; set; } = 100; // 100% 是上界
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsTranslationHighlightAmount { get; set; } = 60; // 100% 是上界
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFloatAmount { get; set; } = 1;
|
||||
|
||||
@@ -31,6 +34,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; }
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollTopDuration { get; set; }
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollBottomDuration { get; set; }
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollTopDelay { get; set; } = 0;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollBottomDelay { get; set; } = 0;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsVerticalEdgeOpacity { get; set; } = 0; // 0% opacity
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFanLyricsEnabled { get; set; } = false;
|
||||
|
||||
@@ -4,11 +4,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
public partial class PictureInPictureModeSettings : BaseModeSettings
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Point WindowPosition { get; set; } = new Point(100, 100);
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ToggleShortcut { get; set; } = new List<string>() { "Ctrl", "Shift", "P" };
|
||||
|
||||
public PictureInPictureModeSettings()
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
public partial class StandardModeSettings : BaseModeSettings
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect WindowBounds { get; set; } = new Rect(100, 100, 1000, 600);
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMaximized { get; set; } = false;
|
||||
|
||||
public StandardModeSettings()
|
||||
{
|
||||
LyricsDisplayType = LyricsDisplayType.SplitView;
|
||||
|
||||
@@ -16,15 +16,5 @@
|
||||
x:Name="LyricsCanvas"
|
||||
Draw="LyricsCanvas_Draw"
|
||||
Update="LyricsCanvas_Update" />
|
||||
<Grid
|
||||
Margin="36"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Visibility="{x:Bind ViewModel.IsTranslating, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<FontIcon
|
||||
x:Name="RotatingIcon"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
|
||||
}
|
||||
|
||||
public async Task<byte[]?> SearchAsync(string mediaSessionId, string title, string artist, string album, byte[]? bytesFromSMTC = null)
|
||||
{
|
||||
return await Task.Run(async () => await SearchAsyncCore(mediaSessionId, title, artist, album, bytesFromSMTC));
|
||||
}
|
||||
|
||||
public async Task<byte[]?> SearchAsyncCore(string mediaSessionId, string title, string artist, string album, byte[]? bytesFromSMTC = null)
|
||||
{
|
||||
byte[]? result = null;
|
||||
|
||||
|
||||
@@ -88,6 +88,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}
|
||||
|
||||
public async Task<(string?, LyricsSearchProvider?)> SearchAsync(string mediaSessionId, string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
{
|
||||
return await Task.Run(async () => await SearchAsyncCore(mediaSessionId, title, artist, album, durationMs, token), token);
|
||||
}
|
||||
|
||||
private async Task<(string?, LyricsSearchProvider?)> SearchAsyncCore(string mediaSessionId, string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TagLib.Riff;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
@@ -12,7 +15,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChanged;
|
||||
event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
|
||||
event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
Task PlayAsync();
|
||||
@@ -26,5 +30,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
bool IsPlaying { get; }
|
||||
SongInfo? SongInfo { get; }
|
||||
TimeSpan Position { get; }
|
||||
|
||||
LyricsSearchProvider? LyricsSearchProvider { get; }
|
||||
TranslationSearchProvider? TranslationSearchProvider { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
public partial class MediaSessionsService : IMediaSessionsService
|
||||
{
|
||||
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
|
||||
|
||||
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChanged;
|
||||
|
||||
private void UpdateAlbumArt()
|
||||
{
|
||||
_albumArtRefreshRunner.RunAsync(RefreshArtAlbum);
|
||||
}
|
||||
|
||||
private async Task RefreshArtAlbum(CancellationToken token)
|
||||
{
|
||||
if (_cachedSongInfo == null)
|
||||
{
|
||||
_logger.LogWarning("Cached song info is null, cannot update album art.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[]? bytes = await _albumArtSearchService.SearchAsync(
|
||||
SongInfo?.SourceAppUserModelId ?? "",
|
||||
_cachedSongInfo.Title,
|
||||
_cachedSongInfo.Artist,
|
||||
_cachedSongInfo?.Album ?? string.Empty,
|
||||
_SMTCAlbumArtBytes
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync(500, 500);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
bytes = ImageHelper.MakeSquareWithThemeColor(bytes);
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Premultiplied);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var albumArtLightAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, false).FirstOrDefault();
|
||||
var albumArtDarkAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, true).FirstOrDefault();
|
||||
|
||||
AlbumArtChanged?.Invoke(this, new AlbumArtChangedEventArgs(null, albumArtSwBitmap, albumArtLightAccentColor, albumArtDarkAccentColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
public partial class MediaSessionsService : IMediaSessionsService
|
||||
{
|
||||
private LatestOnlyTaskRunner _refreshLyricsRunner = new();
|
||||
private LatestOnlyTaskRunner _showTranslationsRunner = new();
|
||||
|
||||
private int _langIndex = 0;
|
||||
private List<LyricsData> _lyricsDataArr = [];
|
||||
|
||||
private LyricsData? CurrentLyricsData => _lyricsDataArr.ElementAtOrDefault(_langIndex);
|
||||
|
||||
public event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider? LyricsSearchProvider { get; set; }
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSearchProvider? TranslationSearchProvider { get; set; }
|
||||
|
||||
[ObservableProperty] public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
private void UpdateTranslations()
|
||||
{
|
||||
TranslationSearchProvider = null;
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
|
||||
IsTranslating = true;
|
||||
|
||||
if (_settingsService.AppSettings.TranslationSettings.IsTranslationEnabled)
|
||||
{
|
||||
_showTranslationsRunner.RunAsync(async token =>
|
||||
{
|
||||
await SetDisplayedAlongWithTranslationsAsync(token);
|
||||
IsTranslating = false;
|
||||
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_langIndex = 0;
|
||||
IsTranslating = false;
|
||||
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Showing translation for lyrics...");
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
|
||||
_logger.LogInformation("Target language code: {TargetLangCode}", targetLangCode);
|
||||
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
|
||||
if (originalText == null) return;
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
|
||||
|
||||
if (originalLangCode == targetLangCode)
|
||||
{
|
||||
_logger.LogInformation("Original lyrics already in target language: {TargetLangCode}", targetLangCode);
|
||||
|
||||
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try get translation from itself first
|
||||
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
|
||||
if (found >= 0)
|
||||
{
|
||||
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
|
||||
if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[found].SetDisplayedTextInOriginalText();
|
||||
_langIndex = found;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator, 50);
|
||||
_langIndex = 0;
|
||||
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
|
||||
}
|
||||
}
|
||||
else if (_settingsService.AppSettings.TranslationSettings.IsLibreTranslateEnabled)
|
||||
{
|
||||
_logger.LogInformation("LibreTranslate is enabled, trying to translate lyrics...");
|
||||
string translated = string.Empty;
|
||||
try
|
||||
{
|
||||
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
if (token.IsCancellationRequested) return;
|
||||
if (translated == string.Empty) return;
|
||||
|
||||
if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
|
||||
_lyricsDataArr[^1].SetDisplayedTextInOriginalText();
|
||||
_langIndex = _lyricsDataArr.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator);
|
||||
_langIndex = 0;
|
||||
}
|
||||
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LibreTranslateFailed")!, Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshLyricsAsync(CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Refreshing lyrics...");
|
||||
|
||||
LyricsSearchProvider = null;
|
||||
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
|
||||
|
||||
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsSearchProvider? lyricsSearchProvider = null;
|
||||
LyricsSearchProvider = lyricsSearchProvider;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
_logger.LogInformation("Searching lyrics for: Title={Title}, Artist={Artist}, Album={Album}, DurationMs={DurationMs}",
|
||||
SongInfo.Title, SongInfo.Artist, SongInfo.Album, SongInfo.DurationMs);
|
||||
(lyricsRaw, lyricsSearchProvider) = await _lyrcsSearchService.SearchAsync(
|
||||
SongInfo.SourceAppUserModelId ?? "",
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0,
|
||||
token
|
||||
);
|
||||
LyricsSearchProvider = lyricsSearchProvider;
|
||||
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsRaw != null, LyricsSearchProvider?.ToString() ?? "null");
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
|
||||
FillTranslationFromCache(LyricsSearchProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _lyricsDataArr.Count);
|
||||
|
||||
// This ensures that original lyrics are always shown while waiting for translations
|
||||
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
|
||||
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
|
||||
|
||||
UpdateTranslations();
|
||||
}
|
||||
|
||||
private void FillTranslationFromCache(LyricsSearchProvider? provider)
|
||||
{
|
||||
string? translationRaw = null;
|
||||
switch (provider)
|
||||
{
|
||||
case Enums.LyricsSearchProvider.QQ:
|
||||
translationRaw = Helper.FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, Helper.PathHelper.QQTranslationCacheDirectory);
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.Kugou:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.Netease:
|
||||
translationRaw = Helper.FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, Helper.PathHelper.NeteaseTranslationCacheDirectory);
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LrcLib:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.AmllTtmlDb:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalMusicFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalLrcFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalEslrcFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalTtmlFile:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (translationRaw != null)
|
||||
{
|
||||
var translationData = new LyricsParser().Parse(translationRaw, (int?)SongInfo?.DurationMs);
|
||||
if (provider == Enums.LyricsSearchProvider.QQ)
|
||||
{
|
||||
foreach (var data in translationData)
|
||||
{
|
||||
foreach (var item in data.LyricsLines)
|
||||
{
|
||||
if (item.OriginalText == "//")
|
||||
{
|
||||
item.OriginalText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lyricsDataArr = _lyricsDataArr.Concat(translationData).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLyrics()
|
||||
{
|
||||
_refreshLyricsRunner.RunAsync(RefreshLyricsAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,11 @@ using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
|
||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||
using BetterLyrics.WinUI3.Services.LibWatcherService;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
@@ -36,13 +40,18 @@ using WindowsMediaController;
|
||||
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<List<string>>>,
|
||||
IRecipient<PropertyChangedMessage<FullyObservableCollection<AlbumArtSearchProviderInfo>>>
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<List<string>>>
|
||||
{
|
||||
private readonly IAlbumArtSearchService _albumArtSearchService;
|
||||
private readonly ILogger<MediaSessionsService> _logger;
|
||||
private readonly ILyricsSearchService _lyrcsSearchService;
|
||||
private readonly ITranslateService _translateService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly ILiveStatesService _liveStatesService;
|
||||
private readonly ILogger<MediaSessionsService> _logger;
|
||||
|
||||
private double _lxMusicPositionSeconds = 0;
|
||||
private double _lxMusicDurationSeconds = 0;
|
||||
@@ -54,28 +63,40 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private readonly MediaManager _mediaManager = new();
|
||||
|
||||
private readonly BackgroundTaskRunner _albumArtRefreshRunner = new();
|
||||
private readonly BackgroundTaskRunner _onAnyMediaPropertyChangedRunner = new();
|
||||
|
||||
private SongInfo? _cachedSongInfo;
|
||||
private byte[]? _SMTCAlbumArtBytes = null;
|
||||
|
||||
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
public event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
public event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
public MediaSessionsService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService)
|
||||
public bool IsPlaying => _cachedIsPlaying;
|
||||
public SongInfo? SongInfo => _cachedSongInfo;
|
||||
public TimeSpan Position => _cachedPosition;
|
||||
|
||||
public MediaSessionsService(
|
||||
ISettingsService settingsService,
|
||||
IAlbumArtSearchService albumArtSearchService,
|
||||
ILyricsSearchService musicSearchService,
|
||||
ILibWatcherService libWatcherService,
|
||||
ILiveStatesService liveStatesService,
|
||||
ITranslateService libreTranslateService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_albumArtSearchService = albumArtSearchService;
|
||||
_lyrcsSearchService = musicSearchService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_translateService = libreTranslateService;
|
||||
_liveStatesService = liveStatesService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<MediaSessionsService>>();
|
||||
|
||||
_settingsService.AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += MediaSourceProvidersInfo_ItemPropertyChanged;
|
||||
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
|
||||
_settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
InitMediaManager();
|
||||
InitPlaybackShortcuts();
|
||||
}
|
||||
@@ -120,12 +141,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void LocalMediaFolders_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
UpdateAlbumArtRelated();
|
||||
UpdateAlbumArt();
|
||||
UpdateLyrics();
|
||||
}
|
||||
|
||||
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateAlbumArtRelated();
|
||||
UpdateAlbumArt();
|
||||
UpdateLyrics();
|
||||
}
|
||||
|
||||
private void MediaSourceProvidersInfo_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
@@ -133,16 +156,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(MediaSourceProviderInfo.AlbumArtSearchProvidersInfo):
|
||||
UpdateAlbumArtRelated();
|
||||
UpdateAlbumArt();
|
||||
break;
|
||||
case nameof(MediaSourceProviderInfo.LyricsSearchProvidersInfo):
|
||||
UpdateLyrics();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPlaying => _cachedIsPlaying;
|
||||
public SongInfo? SongInfo => _cachedSongInfo;
|
||||
public TimeSpan Position => _cachedPosition;
|
||||
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
|
||||
{
|
||||
UpdateAlbumArt();
|
||||
UpdateLyrics();
|
||||
}
|
||||
|
||||
private bool IsMediaSourceEnabled(string id)
|
||||
{
|
||||
@@ -159,18 +187,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
_mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged;
|
||||
|
||||
_mediaManager.Start();
|
||||
Task.Run(() =>
|
||||
{
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
|
||||
});
|
||||
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
|
||||
}
|
||||
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
|
||||
SendFocusedMessagesAsync().ConfigureAwait(false);
|
||||
SendFocusedMessagesAsync();
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
|
||||
@@ -202,51 +228,51 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != focusedSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(mediaSession.Id))
|
||||
{
|
||||
_cachedIsPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != focusedSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(mediaSession.Id))
|
||||
{
|
||||
_cachedIsPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
string id = mediaSession.Id;
|
||||
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != focusedSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(id))
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
_onAnyMediaPropertyChangedRunner.Run(async token =>
|
||||
string id = mediaSession.Id;
|
||||
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != focusedSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(id))
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
|
||||
@@ -256,42 +282,26 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
|
||||
_SMTCAlbumArtBytes = null;
|
||||
|
||||
UpdateAlbumArtRelated();
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentMediaSourceProviderInfo = GetCurrentMediaSourceProviderInfo();
|
||||
if (currentMediaSourceProviderInfo?.ResetPositionOffsetOnSongChanged == true)
|
||||
{
|
||||
currentMediaSourceProviderInfo?.PositionOffset = 0;
|
||||
}
|
||||
});
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
|
||||
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
|
||||
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
|
||||
|
||||
_onAnyMediaPropertyChangedRunner.Run(async token =>
|
||||
{
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
|
||||
@@ -312,18 +322,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_SMTCAlbumArtBytes = null;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAlbumArtRelated();
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
UpdateAlbumArt();
|
||||
UpdateLyrics();
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
|
||||
@@ -387,54 +391,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
MediaManager_OnAnyPlaybackStateChanged(focusedSession, focusedSession.ControlSession.GetPlaybackInfo());
|
||||
}
|
||||
|
||||
private void UpdateAlbumArtRelated()
|
||||
{
|
||||
_albumArtRefreshRunner.Run(async (token) =>
|
||||
{
|
||||
if (_cachedSongInfo == null)
|
||||
{
|
||||
_logger.LogWarning("Cached song info is null, cannot update album art.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[]? bytes = await _albumArtSearchService.SearchAsync(
|
||||
SongInfo?.SourceAppUserModelId ?? "",
|
||||
_cachedSongInfo.Title,
|
||||
_cachedSongInfo.Artist,
|
||||
_cachedSongInfo?.Album ?? string.Empty,
|
||||
_SMTCAlbumArtBytes
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync(500, 500);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
bytes = ImageHelper.MakeSquareWithThemeColor(bytes);
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var albumArtLightAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, false).FirstOrDefault();
|
||||
var albumArtDarkAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, true).FirstOrDefault();
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(null, albumArtSwBitmap, albumArtLightAccentColor, albumArtDarkAccentColor));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void StartSSE()
|
||||
{
|
||||
try
|
||||
@@ -467,7 +423,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void Sse_Disconnected(object sender, DisconnectEventArgs e)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
|
||||
{
|
||||
await Task.Delay(e.ReconnectDelay);
|
||||
if (_sse != null && !_sse.IsDisposed) _sse.Start();
|
||||
@@ -476,25 +432,26 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void Sse_MessageReceived(object sender, EventSourceMessageEventArgs e)
|
||||
{
|
||||
if (_cachedSongInfo?.SourceAppUserModelId == Constants.PlayerID.LXMusic)
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
if (data.ValueKind == JsonValueKind.Number)
|
||||
if (_cachedSongInfo?.SourceAppUserModelId == Constants.PlayerID.LXMusic)
|
||||
{
|
||||
if (e.Event == "progress")
|
||||
{
|
||||
_lxMusicPositionSeconds = data.GetDouble();
|
||||
}
|
||||
else if (e.Event == "duration")
|
||||
{
|
||||
_lxMusicDurationSeconds = data.GetDouble();
|
||||
}
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
if (data.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
if (e.Event == "progress")
|
||||
{
|
||||
_lxMusicPositionSeconds = data.GetDouble();
|
||||
}
|
||||
else if (e.Event == "duration")
|
||||
{
|
||||
_lxMusicDurationSeconds = data.GetDouble();
|
||||
}
|
||||
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.FromSeconds(_lxMusicPositionSeconds), TimeSpan.FromSeconds(_lxMusicDurationSeconds)));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task PlayAsync()
|
||||
@@ -556,19 +513,23 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
}
|
||||
}
|
||||
else if (message.Sender is AlbumArtSearchProviderInfo)
|
||||
else if (message.Sender is TranslationSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(AlbumArtSearchProviderInfo.IsEnabled))
|
||||
if (message.PropertyName == nameof(TranslationSettings.IsLibreTranslateEnabled))
|
||||
{
|
||||
UpdateAlbumArtRelated();
|
||||
UpdateTranslations();
|
||||
}
|
||||
else if (message.PropertyName == nameof(TranslationSettings.IsTranslationEnabled))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
else if (message.PropertyName == nameof(TranslationSettings.ShowTranslationOnly))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<FullyObservableCollection<AlbumArtSearchProviderInfo>> message)
|
||||
{
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<List<string>> message)
|
||||
{
|
||||
if (message.Sender is GeneralSettings)
|
||||
@@ -587,5 +548,28 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is TranslationSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageIndex))
|
||||
{
|
||||
_logger.LogInformation("Target language index changed: {Index}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex);
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsTranslationSeparator))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
|
||||
{
|
||||
public interface ITranslateService
|
||||
{
|
||||
Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken? token);
|
||||
Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken token);
|
||||
|
||||
int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken? token)
|
||||
public async Task<string> TranslateTextAsync(string text, string targetLangCode, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
@@ -49,12 +49,10 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
|
||||
new("q", text),
|
||||
new("source", originalLangCode),
|
||||
new("target", targetLangCode),
|
||||
]));
|
||||
token?.ThrowIfCancellationRequested();
|
||||
]), token);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
token?.ThrowIfCancellationRequested();
|
||||
var json = await response.Content.ReadAsStringAsync(token);
|
||||
|
||||
var result = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.TranslateResponse);
|
||||
return result?.TranslatedText ?? string.Empty;
|
||||
|
||||
@@ -150,9 +150,6 @@
|
||||
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>Picture-in-picture mode</value>
|
||||
</data>
|
||||
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>Exit picture-in-picture mode</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
@@ -478,6 +475,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
|
||||
<value>Activate standard mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartPIPLyrics.Content" xml:space="preserve">
|
||||
<value>Start Picture-in-Picture Mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
|
||||
<value>When starting the app</value>
|
||||
</data>
|
||||
@@ -779,7 +779,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Glow effect</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
|
||||
<value>Highlight scope</value>
|
||||
<value>Original highlight range</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
@@ -844,6 +844,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>Lyrics timeline sync threshold</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
|
||||
<value>Translation Highlight</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>Source and translation separator</value>
|
||||
</data>
|
||||
@@ -934,17 +937,23 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>Scope</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDelay.Header" xml:space="preserve">
|
||||
<value>Tail line delay</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDuration.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation duration (Last line)</value>
|
||||
<value>Final line duration</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation duration (Current line)</value>
|
||||
<value>Current line duration</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation type</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>First line delay</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation duration (First line)</value>
|
||||
<value>The duration of the first line</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>Test server</value>
|
||||
@@ -958,6 +967,12 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageSettingsManager.Header" xml:space="preserve">
|
||||
<value>Settings manager</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
|
||||
<value>This hotkey was not successfully registered</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
|
||||
<value>This hotkey has been successfully registered</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>Current value: </value>
|
||||
</data>
|
||||
|
||||
@@ -150,9 +150,6 @@
|
||||
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>ピクチャーインピクチャーモード</value>
|
||||
</data>
|
||||
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>ピクチャーインピクチャーモードを終了します</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
@@ -478,6 +475,9 @@
|
||||
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
|
||||
<value>標準モードをアクティブにします</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartPIPLyrics.Content" xml:space="preserve">
|
||||
<value>ピクチャーインピクチャーモードを開始します</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
|
||||
<value>アプリを起動するとき</value>
|
||||
</data>
|
||||
@@ -779,7 +779,7 @@
|
||||
<value>グロー効果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
|
||||
<value>ハイライトスコープ</value>
|
||||
<value>オリジナルのハイライト範囲</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>左</value>
|
||||
@@ -844,6 +844,9 @@
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>歌詞タイムライン同期しきい値</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
|
||||
<value>翻訳ハイライト</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>ソースおよび翻訳セパレーター</value>
|
||||
</data>
|
||||
@@ -934,17 +937,23 @@
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>範囲</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDelay.Header" xml:space="preserve">
|
||||
<value>テールラインの遅延</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDuration.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーション期間(最終行)</value>
|
||||
<value>最終ライン期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーション期間(現在の行)</value>
|
||||
<value>現在のライン期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーションタイプ</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>最初の行の遅延</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーション期間(最初の行)</value>
|
||||
<value>最初の行の期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>テストサーバー</value>
|
||||
@@ -958,6 +967,12 @@
|
||||
<data name="SettingsPageSettingsManager.Header" xml:space="preserve">
|
||||
<value>設定マネージャー</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
|
||||
<value>このホットキーは正常に登録されていません</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
|
||||
<value>このホットキーは正常に登録されています</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>現在の値: </value>
|
||||
</data>
|
||||
|
||||
@@ -150,9 +150,6 @@
|
||||
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>사진 인당 모드</value>
|
||||
</data>
|
||||
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>Picture-in-Picture 모드 종료</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>취소</value>
|
||||
</data>
|
||||
@@ -478,6 +475,9 @@
|
||||
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
|
||||
<value>표준 모드를 활성화합니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartPIPLyrics.Content" xml:space="preserve">
|
||||
<value>Picture-in-Picture 모드를 시작하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
|
||||
<value>앱을 시작할 때</value>
|
||||
</data>
|
||||
@@ -779,7 +779,7 @@
|
||||
<value>글로우 효과</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
|
||||
<value>하이라이트 범위</value>
|
||||
<value>원래 하이라이트 범위</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>왼쪽</value>
|
||||
@@ -844,6 +844,9 @@
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>가사 타임 라인 동기화 임계 값</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
|
||||
<value>번역 하이라이트</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>소스 및 번역 분리기</value>
|
||||
</data>
|
||||
@@ -934,17 +937,23 @@
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>범위</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDelay.Header" xml:space="preserve">
|
||||
<value>테일 라인 지연</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDuration.Header" xml:space="preserve">
|
||||
<value>가사 스크롤 애니메이션 지속 시간 (마지막 줄)</value>
|
||||
<value>최종 라인 기간</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>가사 스크롤 애니메이션 지속 시간 (현재 줄)</value>
|
||||
<value>현재 라인 기간</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>가사 스크롤링 애니메이션 유형</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>첫 번째 줄 지연</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>가사 스크롤 애니메이션 지속 시간 (첫 번째 줄)</value>
|
||||
<value>첫 번째 줄의 기간</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>테스트 서버</value>
|
||||
@@ -958,6 +967,12 @@
|
||||
<data name="SettingsPageSettingsManager.Header" xml:space="preserve">
|
||||
<value>설정 관리자</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
|
||||
<value>이 핫키는 성공적으로 등록되지 않았습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
|
||||
<value>이 핫키는 성공적으로 등록되었습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>현재 가치 : </value>
|
||||
</data>
|
||||
|
||||
@@ -150,9 +150,6 @@
|
||||
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>画中画模式</value>
|
||||
</data>
|
||||
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>退出画中画模式</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
@@ -478,6 +475,9 @@
|
||||
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
|
||||
<value>启动标准模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartPIPLyrics.Content" xml:space="preserve">
|
||||
<value>启动画中画模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
|
||||
<value>启动应用时</value>
|
||||
</data>
|
||||
@@ -779,7 +779,7 @@
|
||||
<value>辉光效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
|
||||
<value>高亮显示范围</value>
|
||||
<value>原文高亮显示范围</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
@@ -844,6 +844,9 @@
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>歌词时间轴同步阈值</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
|
||||
<value>翻译高亮</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>原文译文分隔符</value>
|
||||
</data>
|
||||
@@ -934,17 +937,23 @@
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>范围</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDelay.Header" xml:space="preserve">
|
||||
<value>尾行延时</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDuration.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画持续时间(最后一行)</value>
|
||||
<value>尾行持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画持续时间(当前行)</value>
|
||||
<value>当前行持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>首行延时</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画持续时间(第一行)</value>
|
||||
<value>首行持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>测试服务器</value>
|
||||
@@ -958,6 +967,12 @@
|
||||
<data name="SettingsPageSettingsManager.Header" xml:space="preserve">
|
||||
<value>设置管理器</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
|
||||
<value>该热键未成功注册</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
|
||||
<value>该热键已成功注册</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>当前值: </value>
|
||||
</data>
|
||||
|
||||
@@ -150,9 +150,6 @@
|
||||
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>畫中畫模式</value>
|
||||
</data>
|
||||
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
|
||||
<value>退出畫中畫模式</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
@@ -478,6 +475,9 @@
|
||||
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
|
||||
<value>啟動標準模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartPIPLyrics.Content" xml:space="preserve">
|
||||
<value>啟動畫中畫模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
|
||||
<value>啟動應用程式時</value>
|
||||
</data>
|
||||
@@ -779,7 +779,7 @@
|
||||
<value>輝光效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
|
||||
<value>高亮顯示範圍</value>
|
||||
<value>原文高亮顯示範圍</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
@@ -844,6 +844,9 @@
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>歌詞時間軌同步閾值</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationHighlight.Header" xml:space="preserve">
|
||||
<value>翻譯高亮</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>原文譯文分隔符</value>
|
||||
</data>
|
||||
@@ -934,17 +937,23 @@
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>範圍</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDelay.Header" xml:space="preserve">
|
||||
<value>尾行延時</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollBottomDuration.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫持續時間(最後一行)</value>
|
||||
<value>尾行持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫持續時間(當前行)</value>
|
||||
<value>當前行持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫類型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>首行延時</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫持續時間(第一行)</value>
|
||||
<value>首行持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>測試服務器</value>
|
||||
@@ -958,6 +967,12 @@
|
||||
<data name="SettingsPageSettingsManager.Header" xml:space="preserve">
|
||||
<value>設置管理器</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
|
||||
<value>該熱鍵未成功註冊</value>
|
||||
</data>
|
||||
<data name="SettingsPageShortcutRegSuccessInfo" xml:space="preserve">
|
||||
<value>該熱鍵已成功註冊</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>目前值: </value>
|
||||
</data>
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||
using BetterLyrics.WinUI3.Services.LibWatcherService;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
public LyricsRendererViewModel(
|
||||
ISettingsService settingsService,
|
||||
IMediaSessionsService mediaSessionsService,
|
||||
ILyricsSearchService musicSearchService,
|
||||
ILibWatcherService libWatcherService,
|
||||
ITranslateService libreTranslateService,
|
||||
ILastFMService lastFMService,
|
||||
ILiveStatesService liveStatesService
|
||||
)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_lyrcsSearchService = musicSearchService;
|
||||
_mediaSessionsService = mediaSessionsService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_translateService = libreTranslateService;
|
||||
_liveStatesService = liveStatesService;
|
||||
|
||||
_lastFMService = lastFMService;
|
||||
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
|
||||
_settingsService.AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += MediaSourceProvidersInfo_ItemPropertyChanged;
|
||||
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
|
||||
_settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
|
||||
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_timelineSyncThreshold = 0;
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
_mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
_mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_mediaSessionsService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged;
|
||||
_mediaSessionsService.TimelineChanged += PlaybackService_TimelineChanged;
|
||||
|
||||
IsPlaying = _mediaSessionsService.IsPlaying;
|
||||
|
||||
UpdateColorConfig();
|
||||
}
|
||||
|
||||
private void LocalMediaFolders_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
// Music lib changed, re-fetch lyrics
|
||||
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
|
||||
_refreshLyricsRunner.Run(async tokne =>
|
||||
{
|
||||
await RefreshLyricsAsync(tokne);
|
||||
});
|
||||
}
|
||||
|
||||
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// Music lib changed, re-fetch lyrics
|
||||
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
|
||||
_refreshLyricsRunner.Run(async tokne =>
|
||||
{
|
||||
await RefreshLyricsAsync(tokne);
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaSourceProvidersInfo_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(MediaSourceProviderInfo.LyricsSearchProvidersInfo):
|
||||
_logger.LogInformation("MediaSourceProviderInfo.LyricsSearchProvidersInfo changed, refreshing lyrics.");
|
||||
_refreshLyricsRunner.Run(async token =>
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
_drawFrameCount++;
|
||||
|
||||
var currentPlayingLine = _lyricsDataArr
|
||||
.ElementAtOrDefault(_langIndex)
|
||||
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
var currentPlayingLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine != null)
|
||||
{
|
||||
@@ -87,7 +85,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
$"Visible lines: [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
|
||||
$"Total line count: {GetMaxLyricsLineIndexBoundaries().Item2 + 1}\n" +
|
||||
$"Cur time: {TotalTime + _positionOffset}\n" +
|
||||
$"Lang size: {_lyricsDataArr.Count}\n" +
|
||||
$"Song duration: {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}\n" +
|
||||
$"Y offset: {_canvasYScrollTransition.Value}",
|
||||
new Vector2(10, 40),
|
||||
@@ -206,9 +203,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
private void DrawBlurredLyrics(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
var currentPlayingLine = _lyricsDataArr
|
||||
.ElementAtOrDefault(_langIndex)
|
||||
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
var currentPlayingLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine == null)
|
||||
{
|
||||
@@ -217,7 +212,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
|
||||
var textLayout = line.CanvasTextLayout;
|
||||
@@ -239,10 +234,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
using var combined = new CanvasCommandList(control);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
// Mock gradient blurred lyrics layer
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
|
||||
// Current line will not be blurred
|
||||
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
|
||||
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
|
||||
combinedDs.DrawImage(backgroundEffect);
|
||||
@@ -257,16 +250,23 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsLineFadeEnabled);
|
||||
using var lineMask = CanvasHelper.CreateLineMask(control, line);
|
||||
|
||||
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
|
||||
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsFontStrokeWidth, _fgFontColor);
|
||||
|
||||
using var effectLayer = new CanvasCommandList(control);
|
||||
using var effectLayerDs = effectLayer.CreateDrawingSession();
|
||||
if (line.OriginalText != line.DisplayedText && _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsTranslationHighlightAmount != 0)
|
||||
{
|
||||
using var translationHighlightMask = CanvasHelper.CreateTranslationHighlightMask(control, line);
|
||||
using var foregroundTranslationHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, translationHighlightMask,
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsTranslationHighlightAmount / 100.0);
|
||||
effectLayerDs.DrawImage(foregroundTranslationHighlightEffect);
|
||||
}
|
||||
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.IsLyricsShadowEnabled)
|
||||
{
|
||||
var shadowEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
|
||||
var shadowEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsShadowScope);
|
||||
using var foregroundShadowEffect = CanvasHelper.CreateForegroundShadowEffect(foregroundFontEffect, shadowEffectMask,
|
||||
using var foregroundShadowEffect = CanvasHelper.CreateForegroundShadowEffect(foregroundFontEffect, shadowEffectMask,
|
||||
_albumArtAccentColorTransition.Value, _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsShadowAmount);
|
||||
effectLayerDs.DrawImage(foregroundShadowEffect);
|
||||
}
|
||||
@@ -278,10 +278,14 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsGlowEffectAmount);
|
||||
effectLayerDs.DrawImage(foregroundBlurEffect);
|
||||
}
|
||||
var highlightEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightScope);
|
||||
using var foregroundHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, highlightEffectMask);
|
||||
effectLayerDs.DrawImage(foregroundHighlightEffect);
|
||||
if (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightAmount != 0)
|
||||
{
|
||||
var highlightEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask,
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightScope);
|
||||
using var foregroundHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, highlightEffectMask,
|
||||
_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsHighlightAmount / 100.0);
|
||||
effectLayerDs.DrawImage(foregroundHighlightEffect);
|
||||
}
|
||||
|
||||
combinedDs.DrawImage(new OpacityEffect
|
||||
{
|
||||
|
||||
@@ -53,22 +53,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
}
|
||||
}
|
||||
else if (message.Sender is TranslationSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(TranslationSettings.IsLibreTranslateEnabled))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
else if (message.PropertyName == nameof(TranslationSettings.IsTranslationEnabled))
|
||||
{
|
||||
_logger.LogInformation("Translation enabled state changed: {IsEnabled}", _settingsService.AppSettings.TranslationSettings.IsTranslationEnabled);
|
||||
UpdateTranslations();
|
||||
}
|
||||
else if (message.PropertyName == nameof(TranslationSettings.ShowTranslationOnly))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsWindowViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.IsLyricsWindowLocked))
|
||||
@@ -89,18 +73,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
UpdateIsLastFMTrackEnabled();
|
||||
}
|
||||
}
|
||||
if (message.Sender is LyricsSearchProviderInfo)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSearchProviderInfo.IsEnabled))
|
||||
{
|
||||
_logger.LogInformation("LyricsSearchProviderInfo.IsEnabled changed, refreshing lyrics.");
|
||||
_refreshLyricsRunner.Run(async token =>
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<Color> message)
|
||||
@@ -195,6 +167,14 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollTopDelay))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollBottomDelay))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsStyleSettings)
|
||||
{
|
||||
@@ -211,14 +191,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is TranslationSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageIndex))
|
||||
{
|
||||
_logger.LogInformation("Target language index changed: {Index}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex);
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is MediaSourceProviderInfo)
|
||||
{
|
||||
if (message.PropertyName == nameof(MediaSourceProviderInfo.TimelineSyncThreshold))
|
||||
@@ -337,10 +309,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsTranslationSeparator))
|
||||
{
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -336,9 +336,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
double y = 0;
|
||||
|
||||
// Init Positions
|
||||
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
|
||||
for (int i = 0; i < _currentLyricsData?.LyricsLines.Count; i++)
|
||||
{
|
||||
var line = _lyricsDataArr[_langIndex].LyricsLines.ElementAtOrDefault(i);
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i);
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
@@ -366,7 +366,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
// Set _scrollOffsetY
|
||||
|
||||
LyricsLine? currentPlayingLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
LyricsLine? currentPlayingLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
if (playingTextLayout == null) return;
|
||||
|
||||
double? targetYScrollOffset = -currentPlayingLine!.Position.Y + _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2.0;
|
||||
double? targetYScrollOffset = -currentPlayingLine!.Position.Y + _currentLyricsData?.LyricsLines[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2.0;
|
||||
|
||||
if (!targetYScrollOffset.HasValue) return;
|
||||
|
||||
@@ -385,7 +385,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
|
||||
|
||||
var lines = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines;
|
||||
var lines = _currentLyricsData?.LyricsLines;
|
||||
if (lines == null || lines.Count == 0) return;
|
||||
|
||||
double offset = _canvasYScrollTransition.Value + _canvasHeight / 2;
|
||||
@@ -555,15 +555,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
private void UpdateVisibleLinesProps(ICanvasAnimatedControl control)
|
||||
{
|
||||
var currentPlayingLine = _lyricsDataArr
|
||||
.ElementAtOrDefault(_langIndex)
|
||||
?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
var currentPlayingLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex + 1; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i);
|
||||
|
||||
if (line == null) continue;
|
||||
|
||||
@@ -591,25 +589,31 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f);
|
||||
|
||||
double yScrollDuration;
|
||||
double yScrollDelay;
|
||||
|
||||
if (lineCountDelta < 0)
|
||||
{
|
||||
yScrollDuration =
|
||||
_canvasYScrollTransition.DurationSeconds +
|
||||
distanceFactor * (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollTopDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
|
||||
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollTopDelay / 1000.0;
|
||||
}
|
||||
else if (lineCountDelta == 0)
|
||||
{
|
||||
yScrollDuration = _canvasYScrollTransition.DurationSeconds;
|
||||
yScrollDelay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yScrollDuration =
|
||||
_canvasYScrollTransition.DurationSeconds +
|
||||
distanceFactor * (_liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollBottomDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
|
||||
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.CurrentLyricsEffectSettings.LyricsScrollBottomDelay / 1000.0;
|
||||
}
|
||||
|
||||
line.YOffsetTransition.SetEasingType(_canvasYScrollTransition.EasingType ?? EasingType.Linear);
|
||||
line.YOffsetTransition.SetDuration(yScrollDuration);
|
||||
line.YOffsetTransition.SetDelay(yScrollDelay);
|
||||
line.YOffsetTransition.StartTransition(_canvasTargetYScrollOffset, _isLayoutChanged);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
@@ -55,8 +56,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
private int _drawFrameCount = 0;
|
||||
private int _displayedDrawFrameCount = 0;
|
||||
|
||||
private Queue<SoftwareBitmap?> _cachedAlbumArtSwBitmaps = [];
|
||||
|
||||
private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
|
||||
private SoftwareBitmap? _albumArtSwBitmap = null;
|
||||
|
||||
@@ -87,21 +86,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
private double _canvasTargetYScrollOffset = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
|
||||
|
||||
private double _maxLyricsWidth = 0f;
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILyricsSearchService _lyrcsSearchService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly IMediaSessionsService _mediaSessionsService;
|
||||
private readonly ITranslateService _translateService;
|
||||
private readonly ILastFMService _lastFMService;
|
||||
private readonly ILiveStatesService _liveStatesService;
|
||||
private readonly ILogger _logger;
|
||||
@@ -132,6 +120,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
private int _startVisibleLineIndex = -1;
|
||||
private int _endVisibleLineIndex = -1;
|
||||
|
||||
private LyricsData? _currentLyricsData;
|
||||
|
||||
private bool _isDebugOverlayEnabled = false;
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -142,10 +132,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private int _langIndex = 0;
|
||||
|
||||
private List<LyricsData> _lyricsDataArr = [];
|
||||
|
||||
private int _timelineSyncThreshold = 0;
|
||||
|
||||
private CanvasTextFormat _lyricsTextFormat = new()
|
||||
@@ -178,14 +164,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
FontWeight = FontWeights.ExtraBlack,
|
||||
};
|
||||
|
||||
private BackgroundTaskRunner _refreshLyricsRunner = new();
|
||||
private BackgroundTaskRunner _showTranslationsRunner = new();
|
||||
|
||||
private LyricsLayoutOrientation _lyricsLayoutOrientation;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; }
|
||||
|
||||
@@ -193,16 +173,53 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme ThemeTypeSent { get; set; }
|
||||
|
||||
public LyricsRendererViewModel(
|
||||
ISettingsService settingsService,
|
||||
IMediaSessionsService mediaSessionsService,
|
||||
ILastFMService lastFMService,
|
||||
ILiveStatesService liveStatesService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_mediaSessionsService = mediaSessionsService;
|
||||
_liveStatesService = liveStatesService;
|
||||
|
||||
_lastFMService = lastFMService;
|
||||
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_timelineSyncThreshold = 0;
|
||||
|
||||
_mediaSessionsService.IsPlayingChanged += MediaSessionsService_IsPlayingChanged;
|
||||
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
|
||||
_mediaSessionsService.AlbumArtChanged += MediaSessionsService_AlbumArtChangedChanged;
|
||||
_mediaSessionsService.LyricsChanged += MediaSessionsService_LyricsChanged;
|
||||
_mediaSessionsService.TimelineChanged += MediaSessionsService_TimelineChanged;
|
||||
|
||||
IsPlaying = _mediaSessionsService.IsPlaying;
|
||||
|
||||
UpdateColorConfig();
|
||||
}
|
||||
|
||||
private void MediaSessionsService_LyricsChanged(object? sender, LyricsChangedEventArgs e)
|
||||
{
|
||||
_currentLyricsData = e.LyricsData;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
private int GetCurrentPlayingLineIndex()
|
||||
{
|
||||
var totalMs = TotalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds;
|
||||
if (totalMs < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.FirstOrDefault()?.StartMs) return 0;
|
||||
if (totalMs < _currentLyricsData?.LyricsLines.FirstOrDefault()?.StartMs) return 0;
|
||||
|
||||
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
|
||||
for (int i = 0; i < _currentLyricsData?.LyricsLines.Count; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
var nextLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i + 1);
|
||||
var nextLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i + 1);
|
||||
if (nextLine != null && line.StartMs <= totalMs && totalMs < nextLine.StartMs)
|
||||
{
|
||||
return i;
|
||||
@@ -222,9 +239,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
charLength = 0;
|
||||
charProgress = 0f;
|
||||
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
if (line == null) return;
|
||||
var nextLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
var nextLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
|
||||
int lineEndMs;
|
||||
if (line.EndMs != null) lineEndMs = line.EndMs.Value;
|
||||
@@ -309,31 +326,22 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
if (
|
||||
SongInfo == null
|
||||
|| _lyricsDataArr.ElementAtOrDefault(_langIndex) == null
|
||||
|| _lyricsDataArr[_langIndex].LyricsLines.Count == 0
|
||||
|| _currentLyricsData == null
|
||||
|| _currentLyricsData.LyricsLines.Count == 0
|
||||
)
|
||||
{
|
||||
return new Tuple<int, int>(-1, -1);
|
||||
}
|
||||
|
||||
return new Tuple<int, int>(0, _lyricsDataArr[_langIndex].LyricsLines.Count - 1);
|
||||
return new Tuple<int, int>(0, _currentLyricsData.LyricsLines.Count - 1);
|
||||
}
|
||||
|
||||
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
|
||||
{
|
||||
_logger.LogInformation("Music library files changed: {ChangeType} {FilePath}, refreshing lyrics...", e.ChangeType, e.FilePath);
|
||||
_refreshLyricsRunner.Run(async token =>
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
}
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
|
||||
private void MediaSessionsService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
|
||||
{
|
||||
IsPlaying = e.IsPlaying;
|
||||
}
|
||||
|
||||
private void PlaybackService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
|
||||
private void MediaSessionsService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
|
||||
{
|
||||
var diff = Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds);
|
||||
if (diff >= _timelineSyncThreshold)
|
||||
@@ -352,7 +360,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
|
||||
private void MediaSessionsService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
|
||||
{
|
||||
SongInfo = e.SongInfo;
|
||||
|
||||
@@ -373,11 +381,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
_songInfoOpacityTransition.Reset(0f);
|
||||
_songInfoOpacityTransition.StartTransition(1f);
|
||||
|
||||
_logger.LogInformation("Song info changed: Title={Title}, Artist={Artist}, refreshing lyrics...", _songTitle, _songArtist);
|
||||
_refreshLyricsRunner.Run(async token =>
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
TotalTime = TimeSpan.Zero;
|
||||
|
||||
// 处理 Last.fm 追踪
|
||||
@@ -386,222 +389,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaybackService_AlbumArtChangedChanged(object? sender, AlbumArtChangedEventArgs e)
|
||||
private void MediaSessionsService_AlbumArtChangedChanged(object? sender, AlbumArtChangedEventArgs e)
|
||||
{
|
||||
if (e.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
_cachedAlbumArtSwBitmaps.Append(_albumArtSwBitmap);
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_albumArtSwBitmap = e.AlbumArtSwBitmap;
|
||||
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_albumArtChanged = true;
|
||||
|
||||
if (_cachedAlbumArtSwBitmaps.Count > 2)
|
||||
{
|
||||
_cachedAlbumArtSwBitmaps.Dequeue()?.Dispose();
|
||||
}
|
||||
_albumArtLightAccentColor = e.AlbumArtLightAccentColor ?? Colors.Transparent;
|
||||
_albumArtDarkAccentColor = e.AlbumArtDarkAccentColor ?? Colors.Transparent;
|
||||
|
||||
_cachedAlbumArtSwBitmaps.Append(e.AlbumArtSwBitmap);
|
||||
|
||||
_albumArtSwBitmap = e.AlbumArtSwBitmap;
|
||||
|
||||
if (_cachedAlbumArtSwBitmaps.Count > 2)
|
||||
{
|
||||
_cachedAlbumArtSwBitmaps.Dequeue()?.Dispose();
|
||||
}
|
||||
|
||||
_albumArtChanged = true;
|
||||
|
||||
_albumArtLightAccentColor = e.AlbumArtLightAccentColor ?? Colors.Transparent;
|
||||
_albumArtDarkAccentColor = e.AlbumArtDarkAccentColor ?? Colors.Transparent;
|
||||
|
||||
UpdateColorConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
e.AlbumArtSwBitmap?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTranslations()
|
||||
{
|
||||
TranslationSearchProvider = null;
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_isLayoutChanged = true;
|
||||
|
||||
IsTranslating = true;
|
||||
if (_settingsService.AppSettings.TranslationSettings.IsTranslationEnabled)
|
||||
{
|
||||
_refreshLyricsRunner.Run(async token =>
|
||||
{
|
||||
await SetDisplayedAlongWithTranslationsAsync(token);
|
||||
IsTranslating = false;
|
||||
_isLayoutChanged = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_langIndex = 0;
|
||||
IsTranslating = false;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Showing translation for lyrics...");
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
|
||||
_logger.LogInformation("Target language code: {TargetLangCode}", targetLangCode);
|
||||
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
|
||||
if (originalText == null) return;
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode ?? "null");
|
||||
|
||||
if (originalLangCode == targetLangCode)
|
||||
{
|
||||
_logger.LogInformation("Original lyrics already in target language: {TargetLangCode}", targetLangCode);
|
||||
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try get translation from itself first
|
||||
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
|
||||
if (found >= 0)
|
||||
{
|
||||
_logger.LogInformation("Found translation in lyrics data at index {FoundIndex}", found);
|
||||
if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[found].SetDisplayedTextInOriginalText();
|
||||
_langIndex = found;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator, 50);
|
||||
_langIndex = 0;
|
||||
}
|
||||
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
|
||||
}
|
||||
else if (_settingsService.AppSettings.TranslationSettings.IsLibreTranslateEnabled)
|
||||
{
|
||||
_logger.LogInformation("LibreTranslate is enabled, trying to translate lyrics...");
|
||||
string translated = string.Empty;
|
||||
try
|
||||
{
|
||||
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
if (translated == string.Empty) return;
|
||||
|
||||
if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
|
||||
_lyricsDataArr[^1].SetDisplayedTextInOriginalText();
|
||||
_langIndex = _lyricsDataArr.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _liveStatesService.LiveStates.CurrentLyricsStyleSettings.LyricsTranslationSeparator);
|
||||
_langIndex = 0;
|
||||
}
|
||||
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LibreTranslateFailed")!, Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshLyricsAsync(CancellationToken token)
|
||||
{
|
||||
LyricsSearchProvider = null;
|
||||
_logger.LogInformation("Refreshing lyrics...");
|
||||
|
||||
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
|
||||
_isLayoutChanged = true;
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsSearchProvider? lyricsSearchProvider = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
_logger.LogInformation("Searching lyrics for: Title={Title}, Artist={Artist}, Album={Album}, DurationMs={DurationMs}",
|
||||
SongInfo.Title, SongInfo.Artist, SongInfo.Album, SongInfo.DurationMs);
|
||||
(lyricsRaw, lyricsSearchProvider) = await _lyrcsSearchService.SearchAsync(
|
||||
SongInfo.SourceAppUserModelId ?? "",
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0,
|
||||
token
|
||||
);
|
||||
_logger.LogInformation("Lyrics was found? {Found}, Provider: {LyricsSearchProvider}", lyricsRaw != null, lyricsSearchProvider?.ToString() ?? "null");
|
||||
LyricsSearchProvider = lyricsSearchProvider;
|
||||
token.ThrowIfCancellationRequested();
|
||||
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
|
||||
FillTranslationFromCache(LyricsSearchProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _lyricsDataArr.Count);
|
||||
|
||||
// This ensures that original lyrics are always shown while waiting for translations
|
||||
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
|
||||
_isLayoutChanged = true;
|
||||
|
||||
UpdateTranslations();
|
||||
}
|
||||
|
||||
private void FillTranslationFromCache(LyricsSearchProvider? provider)
|
||||
{
|
||||
string? translationRaw = null;
|
||||
switch (provider)
|
||||
{
|
||||
case Enums.LyricsSearchProvider.QQ:
|
||||
translationRaw = Helper.FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, Helper.PathHelper.QQTranslationCacheDirectory);
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.Kugou:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.Netease:
|
||||
translationRaw = Helper.FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, Helper.PathHelper.NeteaseTranslationCacheDirectory);
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LrcLib:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.AmllTtmlDb:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalMusicFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalLrcFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalEslrcFile:
|
||||
break;
|
||||
case Enums.LyricsSearchProvider.LocalTtmlFile:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (translationRaw != null)
|
||||
{
|
||||
var translationData = new LyricsParser().Parse(translationRaw, (int?)SongInfo?.DurationMs);
|
||||
if (provider == Enums.LyricsSearchProvider.QQ)
|
||||
{
|
||||
foreach (var data in translationData)
|
||||
{
|
||||
foreach (var item in data.LyricsLines)
|
||||
{
|
||||
if (item.OriginalText == "//")
|
||||
{
|
||||
item.OriginalText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lyricsDataArr = _lyricsDataArr.Concat(translationData).ToList();
|
||||
}
|
||||
UpdateColorConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateDockOrDesktopWindow()
|
||||
{
|
||||
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
@@ -137,7 +138,7 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
|
||||
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode)
|
||||
{
|
||||
DockModeHelper.UpdateAppBarHeight(hwnd, _dockMonitorDeviceName, _dockWindowHeight, _dockPlacement);
|
||||
}
|
||||
@@ -217,8 +218,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateDesktopLockShortcut()
|
||||
{
|
||||
GlobalHotKeyHelper.UnregisterHotKey<LyricsWindow>(ShortcutID.DesktopLock);
|
||||
GlobalHotKeyHelper.RegisterHotKey<LyricsWindow>(ShortcutID.DesktopLock,
|
||||
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.DesktopLock,
|
||||
_settingsService.AppSettings.DesktopModeSettings.LockShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -232,8 +232,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateDesktopToggleShortcut()
|
||||
{
|
||||
GlobalHotKeyHelper.UnregisterHotKey<LyricsWindow>(ShortcutID.DesktopToggle);
|
||||
GlobalHotKeyHelper.RegisterHotKey<LyricsWindow>(ShortcutID.DesktopToggle,
|
||||
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.DesktopToggle,
|
||||
_settingsService.AppSettings.DesktopModeSettings.ToggleShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -248,8 +247,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateDockToggleShortcut()
|
||||
{
|
||||
GlobalHotKeyHelper.UnregisterHotKey<LyricsWindow>(ShortcutID.DockToggle);
|
||||
GlobalHotKeyHelper.RegisterHotKey<LyricsWindow>(ShortcutID.DockToggle,
|
||||
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.DockToggle,
|
||||
_settingsService.AppSettings.DockModeSettings.ToggleShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -264,8 +262,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdatePictureInPictureToggleShortcut()
|
||||
{
|
||||
GlobalHotKeyHelper.UnregisterHotKey<LyricsWindow>(ShortcutID.PictureInPictureToggle);
|
||||
GlobalHotKeyHelper.RegisterHotKey<LyricsWindow>(ShortcutID.PictureInPictureToggle,
|
||||
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.PictureInPictureToggle,
|
||||
_settingsService.AppSettings.PictureInPictureModeSettings.ToggleShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -462,6 +459,7 @@ namespace BetterLyrics.WinUI3
|
||||
if (LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.PictureInPictureMode)
|
||||
{
|
||||
window.AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
|
||||
window.AppWindow.Move(AppSettings.PictureInPictureModeSettings.WindowPosition.ToPointInt32());
|
||||
SetPIPModeTitleBarControlsStatus();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -80,6 +80,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
|
||||
LastFMUser = _lastFMService.User;
|
||||
|
||||
LyricsSearchProvider = _mediaSessionsService.LyricsSearchProvider;
|
||||
TranslationSearchProvider = _mediaSessionsService.TranslationSearchProvider;
|
||||
|
||||
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
try
|
||||
{
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
|
||||
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
|
||||
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, new System.Threading.CancellationToken());
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
|
||||
@@ -183,9 +186,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
|
||||
{
|
||||
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
|
||||
if (message.Sender is MediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.LyricsSearchProvider))
|
||||
if (message.PropertyName == nameof(MediaSessionsService.LyricsSearchProvider))
|
||||
{
|
||||
LyricsSearchProvider = message.NewValue;
|
||||
}
|
||||
@@ -194,9 +197,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public void Receive(PropertyChangedMessage<TranslationSearchProvider?> message)
|
||||
{
|
||||
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
|
||||
if (message.Sender is MediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.TranslationSearchProvider))
|
||||
if (message.PropertyName == nameof(MediaSessionsService.TranslationSearchProvider))
|
||||
{
|
||||
TranslationSearchProvider = message.NewValue;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly WindowMessageMonitor _wmm;
|
||||
private bool _autoSelectLyricsModeOnRunning = true;
|
||||
|
||||
public LyricsWindow()
|
||||
{
|
||||
@@ -75,8 +76,17 @@ namespace BetterLyrics.WinUI3.Views
|
||||
switch (type!)
|
||||
{
|
||||
case LyricsWindowMode.StandardMode:
|
||||
AppWindow.MoveAndResize(_settingsService.AppSettings.StandardModeSettings.WindowBounds.ToRectInt32());
|
||||
ViewModel.SetStandardModeTitleBarControlsStatus();
|
||||
if (_settingsService.AppSettings.StandardModeSettings.IsMaximized)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD>Ա<EFBFBD><D4B1>˳<EFBFBD><CBB3><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܶ<EFBFBD><DCB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><C4BB>ԵӰ<D4B5><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
this.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
AppWindow.MoveAndResize(_settingsService.AppSettings.StandardModeSettings.WindowBounds.ToRectInt32());
|
||||
}
|
||||
break;
|
||||
case LyricsWindowMode.DockMode:
|
||||
ViewModel.ToggleDockMode();
|
||||
@@ -84,9 +94,13 @@ namespace BetterLyrics.WinUI3.Views
|
||||
case LyricsWindowMode.DesktopMode:
|
||||
ViewModel.ToggleDesktopMode();
|
||||
break;
|
||||
case LyricsWindowMode.PictureInPictureMode:
|
||||
ViewModel.TogglePictureInPictureMode();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_autoSelectLyricsModeOnRunning = false;
|
||||
}
|
||||
|
||||
private void AOTFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
@@ -96,6 +110,8 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
|
||||
{
|
||||
if (_autoSelectLyricsModeOnRunning) return;
|
||||
|
||||
if (args.DidPositionChange || args.DidSizeChange)
|
||||
{
|
||||
var size = AppWindow.Size;
|
||||
@@ -107,14 +123,28 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ViewModel.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DesktopMode)
|
||||
switch (ViewModel.LiveStates.CurrentLyricsWindowMode)
|
||||
{
|
||||
_settingsService.AppSettings.DesktopModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
|
||||
}
|
||||
else if (ViewModel.LiveStates.CurrentLyricsWindowMode == LyricsWindowMode.DockMode) { }
|
||||
else
|
||||
{
|
||||
_settingsService.AppSettings.StandardModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
|
||||
case LyricsWindowMode.StandardMode:
|
||||
if (AppWindow.Presenter is OverlappedPresenter overlappedPresenter)
|
||||
{
|
||||
_settingsService.AppSettings.StandardModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
|
||||
_settingsService.AppSettings.StandardModeSettings.IsMaximized = overlappedPresenter.State == OverlappedPresenterState.Maximized;
|
||||
}
|
||||
break;
|
||||
case LyricsWindowMode.DockMode:
|
||||
break;
|
||||
case LyricsWindowMode.DesktopMode:
|
||||
_settingsService.AppSettings.DesktopModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
|
||||
break;
|
||||
case LyricsWindowMode.PictureInPictureMode:
|
||||
if (AppWindow.Presenter is CompactOverlayPresenter compactOverlayPresenter)
|
||||
{
|
||||
_settingsService.AppSettings.PictureInPictureModeSettings.WindowPosition = new Windows.Foundation.Point(rect.X, rect.Y);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
<!-- Lyrics background -->
|
||||
<controls:Case Value="Background">
|
||||
<uc:LyricsBavkgroundSettingsControl />
|
||||
<uc:LyricsBackgroundSettingsControl />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art area style -->
|
||||
|
||||
Reference in New Issue
Block a user