mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e354e29ec1 | ||
|
|
dd5fb91764 | ||
|
|
041459e564 | ||
|
|
7de813b645 | ||
|
|
838d663834 |
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.91.0" />
|
||||
Version="1.0.93.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
@@ -117,6 +117,9 @@
|
||||
<Content Update="Assets\AIMP.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AlbumArtPlaceholder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AMLLPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -83,42 +84,42 @@
|
||||
<Grid Grid.Column="1">
|
||||
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ListViewItem IsEnabled="{Binding IsFound}">
|
||||
<Grid Opacity="{Binding IsFound, Converter={StaticResource BoolToOpacityConverter}}">
|
||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||
<Grid Opacity="{x:Bind IsFound, Converter={StaticResource BoolToOpacityConverter}}">
|
||||
<RelativePanel Padding="0,6">
|
||||
<TextBlock
|
||||
x:Name="SearchedTitle"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
Text="{Binding Title}"
|
||||
Text="{x:Bind Title}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedArtists"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="SearchedTitle"
|
||||
Text="{Binding Artist}"
|
||||
Text="{x:Bind Artist}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedAlbum"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
RelativePanel.Below="SearchedArtists"
|
||||
Text="{Binding Album}"
|
||||
Text="{x:Bind Album}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Name="SearchedProvider"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
Text="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
</RelativePanel>
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlNotFound"
|
||||
VerticalAlignment="Center"
|
||||
Text="Not found"
|
||||
Visibility="{Binding IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolNegationToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI;
|
||||
using static Vanara.PInvoke.Ole32;
|
||||
@@ -46,43 +47,14 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return RandomAccessStreamReference.CreateFromStream(stream);
|
||||
}
|
||||
|
||||
public static async Task<IRandomAccessStream> CreateTextPlaceholderBytesAsync(int width, int height)
|
||||
public static async Task<IRandomAccessStream> GetAlbumArtPlaceholderAsync()
|
||||
{
|
||||
using var device = CanvasDevice.GetSharedDevice();
|
||||
using var renderTarget = new CanvasRenderTarget(device, width, height, 96);
|
||||
|
||||
// 随机生成渐变色
|
||||
Windows.UI.Color RandomColor()
|
||||
{
|
||||
var rand = new Random(Guid.NewGuid().GetHashCode());
|
||||
double h = rand.NextDouble() * 360;
|
||||
double s = 0.35 + rand.NextDouble() * 0.3; // 0.35~0.65,适中饱和度
|
||||
double l = 0.5 + rand.NextDouble() * 0.3; // 0.5~0.8,明亮
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, l);
|
||||
}
|
||||
|
||||
Windows.UI.Color color1 = RandomColor();
|
||||
Windows.UI.Color color2 = RandomColor();
|
||||
|
||||
using (var ds = renderTarget.CreateDrawingSession())
|
||||
{
|
||||
// 绘制线性渐变背景
|
||||
using var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
|
||||
{
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(width, height)
|
||||
};
|
||||
ds.FillRectangle(0, 0, width, height, gradientBrush);
|
||||
}
|
||||
|
||||
// 保存为 PNG 并转为 byte[]
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
|
||||
stream.Seek(0);
|
||||
Uri uri = new Uri($"ms-appx:///Assets/AlbumArtPlaceholder.png");
|
||||
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
|
||||
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
public static Task<ThemeColorResult> GetAccentColorAsync(BitmapDecoder decoder, PaletteGeneratorType generatorType)
|
||||
{
|
||||
return generatorType switch
|
||||
|
||||
@@ -150,7 +150,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
// 补偿曲线
|
||||
float[] frequencies = { 20, 50, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 20000 };
|
||||
float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
|
||||
//float[] gains = { 0.5f, 0.3f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.1f, 0.9f, 0.8f };
|
||||
float[] gains = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
if (freq <= frequencies[0])
|
||||
{
|
||||
return gains[0];
|
||||
|
||||
@@ -6,6 +6,7 @@ using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
@@ -31,6 +32,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
private static readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
|
||||
private static DispatcherQueueTimer? _setLyricsWindowVisibilityByPlayingStatusTimer;
|
||||
|
||||
public static void HideWindow<T>()
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
@@ -346,27 +349,36 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
}
|
||||
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus()
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dispatcherQueue">请确保此参数指向同一个对象,建议传值 BaseViewModel._dispatcherQueue</param>
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus(DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
var window = GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer ??= dispatcherQueue.CreateTimer();
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
var window = GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
}
|
||||
HideWindow<LyricsWindow>();
|
||||
}
|
||||
HideWindow<LyricsWindow>();
|
||||
}
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(true);
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(true);
|
||||
}
|
||||
OpenOrShowWindow<LyricsWindow>();
|
||||
}
|
||||
OpenOrShowWindow<LyricsWindow>();
|
||||
}
|
||||
}, Constants.Time.DebounceTimeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ namespace BetterLyrics.WinUI3.Models
|
||||
this.OnPropertyChanged(nameof(AlbumArtLayoutSettings));
|
||||
}
|
||||
|
||||
partial void OnAutoShowOrHideWindowChanged(bool value)
|
||||
{
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
}
|
||||
|
||||
public void UpdateMonitorNameAndBounds()
|
||||
{
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -31,4 +32,14 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
public SongInfo() { }
|
||||
}
|
||||
|
||||
public static class SongInfoExtensions
|
||||
{
|
||||
public static SongInfo Placeholder => new()
|
||||
{
|
||||
Title = "N/A",
|
||||
Album = "N/A",
|
||||
Artist = "N/A",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
case nameof(LyricsWindowStatus.TitleBarArea):
|
||||
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -134,7 +137,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
WindowHelper.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
WindowHelper.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
WindowHelper.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
WindowHelper.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
|
||||
@@ -491,84 +491,87 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}, searcher);
|
||||
}
|
||||
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
if (result != null)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var original = response?.Lrc?.Lyric;
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
string? original = null;
|
||||
var candidate = response?.Candidates.FirstOrDefault();
|
||||
if (candidate != null)
|
||||
{
|
||||
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
|
||||
if (original != null)
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
|
||||
if (parsedList != null)
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var original = response?.Lrc?.Lyric;
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
string? original = null;
|
||||
var candidate = response?.Candidates.FirstOrDefault();
|
||||
if (candidate != null)
|
||||
{
|
||||
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
|
||||
if (original != null)
|
||||
{
|
||||
string translated = "";
|
||||
foreach (var item in parsedList)
|
||||
var parsedList = Lyricify.Lyrics.Parsers.KrcParser.ParseLyrics(original);
|
||||
if (parsedList != null)
|
||||
{
|
||||
if (item is Lyricify.Lyrics.Models.FullSyllableLineInfo fullSyllableLineInfo)
|
||||
string translated = "";
|
||||
foreach (var item in parsedList)
|
||||
{
|
||||
var startTimeSpan = TimeSpan.FromMilliseconds(fullSyllableLineInfo.StartTime ?? 0);
|
||||
string startTimeStr = startTimeSpan.ToString(@"mm\:ss\.ff");
|
||||
string chTranslation = fullSyllableLineInfo.Translations.GetValueOrDefault("zh") ?? "";
|
||||
translated += $"[{startTimeStr}]{chTranslation}\n";
|
||||
if (item is Lyricify.Lyrics.Models.FullSyllableLineInfo fullSyllableLineInfo)
|
||||
{
|
||||
var startTimeSpan = TimeSpan.FromMilliseconds(fullSyllableLineInfo.StartTime ?? 0);
|
||||
string startTimeStr = startTimeSpan.ToString(@"mm\:ss\.ff");
|
||||
string chTranslation = fullSyllableLineInfo.Translations.GetValueOrDefault("zh") ?? "";
|
||||
translated += $"[{startTimeStr}]{chTranslation}\n";
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.KugouTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.KugouTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lyricsSearchResult.Raw = original;
|
||||
lyricsSearchResult.Raw = original;
|
||||
}
|
||||
}
|
||||
|
||||
lyricsSearchResult.Title = result?.Title;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
using var placeHolderStream = await ImageHelper.CreateTextPlaceholderBytesAsync(500, 500);
|
||||
using var placeHolderStream = await ImageHelper.GetAlbumArtPlaceholderAsync();
|
||||
var tempBuffer = new Windows.Storage.Streams.Buffer((uint)placeHolderStream.Size);
|
||||
await placeHolderStream.ReadAsync(tempBuffer, (uint)placeHolderStream.Size, InputStreamOptions.None);
|
||||
buffer = tempBuffer;
|
||||
|
||||
@@ -213,14 +213,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
|
||||
}
|
||||
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
private async void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
|
||||
SendFocusedMessagesAsync();
|
||||
await SendFocusedMessagesAsync();
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties? timelineProperties)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
@@ -241,16 +241,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
if (IsMediaSourceTimelineSyncEnabled(mediaSession.Id))
|
||||
{
|
||||
_cachedPosition = timelineProperties.Position;
|
||||
_cachedPosition = timelineProperties?.Position ?? TimeSpan.Zero;
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties.EndTime));
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties?.EndTime ?? TimeSpan.Zero));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo? playbackInfo)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
@@ -268,7 +268,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
_cachedIsPlaying = playbackInfo?.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
@@ -279,26 +279,28 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProperties)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
if (mediaSession == null)
|
||||
{
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
}
|
||||
|
||||
string sessionId = mediaSession.Id;
|
||||
string? sessionId = mediaSession?.Id;
|
||||
|
||||
var desiredSession = GetCurrentSession();
|
||||
|
||||
//RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (mediaSession != desiredSession) return;
|
||||
|
||||
if (!IsMediaSourceEnabled(sessionId))
|
||||
if (sessionId != null && !IsMediaSourceEnabled(sessionId))
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (sessionId == Constants.PlayerID.LXMusic)
|
||||
{
|
||||
@@ -315,33 +317,33 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
currentMediaSourceProviderInfo?.PositionOffset = 0;
|
||||
}
|
||||
|
||||
string fixedArtist = mediaProperties.Artist;
|
||||
string fixedAlbum = mediaProperties.AlbumTitle;
|
||||
string fixedArtist = mediaProperties?.Artist ?? "N/A";
|
||||
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
|
||||
string? songId = null;
|
||||
|
||||
if (sessionId == Constants.PlayerID.AppleMusic || sessionId == Constants.PlayerID.AppleMusicAlternative)
|
||||
{
|
||||
fixedArtist = mediaProperties.Artist.Split(" — ").FirstOrDefault() ?? mediaProperties.Artist;
|
||||
fixedAlbum = mediaProperties.Artist.Split(" — ").LastOrDefault() ?? mediaProperties.AlbumTitle;
|
||||
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
|
||||
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
|
||||
}
|
||||
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId))
|
||||
else if (PlayerIdMatcher.IsNeteaseFamily(sessionId ?? ""))
|
||||
{
|
||||
songId = mediaProperties.Genres.FirstOrDefault()?.Replace("NCM-", "");
|
||||
songId = mediaProperties?.Genres.FirstOrDefault()?.Replace("NCM-", "");
|
||||
}
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Title = mediaProperties?.Title ?? "N/A",
|
||||
Artist = fixedArtist,
|
||||
Album = fixedAlbum,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
PlayerId = sessionId,
|
||||
SongId = songId
|
||||
};
|
||||
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
|
||||
_cachedSongInfo.Duration = (int)((_cachedSongInfo.DurationMs ?? 0) / 1000f);
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (sessionId == Constants.PlayerID.LXMusic)
|
||||
{
|
||||
@@ -356,7 +358,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_SMTCAlbumArtBuffer = _lxMusicAlbumArtBytes.AsBuffer();
|
||||
}
|
||||
else if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
else if (mediaProperties?.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
_SMTCAlbumArtBuffer = await ImageHelper.ToBufferAsync(streamReference);
|
||||
}
|
||||
@@ -438,7 +440,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedSongInfo = SongInfoExtensions.Placeholder;
|
||||
_cachedIsPlaying = false;
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
@@ -448,14 +450,21 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private async Task SendFocusedMessagesAsync()
|
||||
{
|
||||
var desiredSession = GetCurrentSession();
|
||||
if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null;
|
||||
|
||||
var mediaProps = await desiredSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession.ControlSession.GetTimelineProperties());
|
||||
var desiredSession = GetCurrentSession();
|
||||
//if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
mediaProps = await desiredSession?.ControlSession?.TryGetMediaPropertiesAsync();
|
||||
}
|
||||
catch (Exception) { }
|
||||
//if (desiredSession == null || desiredSession.ControlSession == null) return;
|
||||
|
||||
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession?.ControlSession?.GetTimelineProperties());
|
||||
MediaManager_OnAnyMediaPropertyChanged(desiredSession, mediaProps);
|
||||
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession.ControlSession.GetPlaybackInfo());
|
||||
MediaManager_OnAnyPlaybackStateChanged(desiredSession, desiredSession?.ControlSession?.GetPlaybackInfo());
|
||||
}
|
||||
|
||||
private void StartSSE()
|
||||
|
||||
@@ -19,10 +19,14 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
// 新建一个 AppSettings 类
|
||||
public partial class SettingsService : BaseViewModel, ISettingsService
|
||||
{
|
||||
private DispatcherQueueTimer _writeAppSettingsTimer;
|
||||
|
||||
public AppSettings AppSettings { get; set; }
|
||||
|
||||
public SettingsService()
|
||||
{
|
||||
_writeAppSettingsTimer = _dispatcherQueue.CreateTimer();
|
||||
|
||||
AppSettings = ReadAppSettings();
|
||||
|
||||
AppSettings.PropertyChanged += AppSettings_PropertyChanged;
|
||||
@@ -94,12 +98,12 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
|
||||
private void AppSettings_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
private void AppSettings_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
private void AppSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
@@ -112,7 +116,7 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WriteAppSettingsDebounce();
|
||||
WriteAppSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,9 +165,9 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
return data;
|
||||
}
|
||||
|
||||
private void WriteAppSettingsDebounce()
|
||||
private void WriteAppSettings()
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_writeAppSettingsTimer.Debounce(() =>
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
|
||||
@@ -11,14 +11,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial class BaseViewModel : ObservableRecipient
|
||||
{
|
||||
private protected readonly DispatcherQueue _dispatcherQueue;
|
||||
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
|
||||
|
||||
public BaseViewModel()
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Effects;
|
||||
using Windows.UI;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
{
|
||||
@@ -128,21 +129,20 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
for (int i = 0; i < _spectrumAnalyzer.BarCount; i++)
|
||||
{
|
||||
float x = i * pointSpacing;
|
||||
float amplitude = _spectrumAnalyzer.SmoothSpectrum.Average() * 10 + _spectrumAnalyzer.SmoothSpectrum[i] * 0.5f;
|
||||
float y = (float)_canvasHeight - amplitude;
|
||||
float y = _spectrumAnalyzer.SmoothSpectrum[i];
|
||||
points[i] = new Vector2(x, y);
|
||||
}
|
||||
|
||||
// 限制最高点高度
|
||||
var minY = points.OrderBy(p => p.Y).FirstOrDefault().Y;
|
||||
var limitY = _canvasHeight * (1 - 0.1f);
|
||||
if (minY < limitY)
|
||||
var maxY = points.OrderByDescending(p => p.Y).FirstOrDefault().Y;
|
||||
var limitY = _canvasHeight * 0.2f;
|
||||
if (maxY > limitY)
|
||||
{
|
||||
var num = (float)(limitY / minY);
|
||||
var num = (float)(limitY / maxY);
|
||||
points = points.Select(p => new Vector2(p.X, p.Y * num)).ToArray();
|
||||
}
|
||||
// 防止越过画布边界
|
||||
points = points.Select(p => new Vector2(p.X, (float)(Math.Min(_canvasHeight, p.Y)))).ToArray();
|
||||
|
||||
points = points.Select(p => new Vector2(p.X, (float)(_canvasHeight - p.Y))).ToArray();
|
||||
|
||||
// 用于填充的闭合路径
|
||||
using var pathBuilder = new CanvasPathBuilder(ds);
|
||||
@@ -176,7 +176,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
var gradientStops = new CanvasGradientStop[]
|
||||
{
|
||||
new() { Position = 0.0f, Color = Colors.Transparent },
|
||||
new() { Position = 0.8f, Color = Colors.Transparent },
|
||||
new() { Position = 0.7f, Color = Colors.Transparent },
|
||||
new() { Position = 1.0f, Color = _adaptiveColoredFontColor ?? _albumArtAccentColor1Transition.Value }
|
||||
};
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.Generic;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.Foundation;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
@@ -39,6 +39,7 @@ namespace BetterLyrics.WinUI3
|
||||
private readonly ILiveStatesService _liveStatesService;
|
||||
|
||||
private ForegroundWindowWatcher? _fgWindowWatcher = null;
|
||||
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
|
||||
{
|
||||
@@ -54,7 +55,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
|
||||
{
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus();
|
||||
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
}
|
||||
|
||||
[ObservableProperty] public partial AppSettings AppSettings { get; set; }
|
||||
@@ -142,11 +143,13 @@ namespace BetterLyrics.WinUI3
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
_fgWindowWatcherTimer = _dispatcherQueue.CreateTimer();
|
||||
_fgWindowWatcher = new ForegroundWindowWatcher(
|
||||
hwnd,
|
||||
fgHwnd =>
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_fgWindowWatcherTimer.Debounce(() =>
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTop &&
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling &&
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private readonly MediaTimelineController _timelineController = new();
|
||||
private readonly SystemMediaTransportControls _smtc;
|
||||
|
||||
private readonly DispatcherQueueTimer _refreshSongsTimer;
|
||||
|
||||
// All songs
|
||||
private List<Track> _tracks = [];
|
||||
// Songs in current playlist
|
||||
@@ -95,6 +97,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
|
||||
{
|
||||
_refreshSongsTimer = _dispatcherQueue.CreateTimer();
|
||||
|
||||
_settingsService = settingsService;
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
|
||||
@@ -263,7 +267,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public void RefreshSongs()
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
_refreshSongsTimer.Debounce(() =>
|
||||
{
|
||||
IsDataLoading = true;
|
||||
_tracks.Clear();
|
||||
|
||||
@@ -29,35 +29,6 @@
|
||||
|
||||
<dev:SnowFlakeEffect FlakeCount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.SnowFlakeOverlayAmount, Mode=OneWay}" Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
|
||||
<!-- No music playing placeholder -->
|
||||
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
HorizontalAlignment="Center"
|
||||
FontFamily="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsCJKFontFamily, Mode=OneWay}"
|
||||
FontSize="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.OriginalLyricsFontSize, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
|
||||
Reference in New Issue
Block a user