diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index 5e5a7db..a3954a6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.0.45.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml index 45c5045..0e44b3f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml @@ -51,6 +51,8 @@ + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs index 4a77ff9..92c1a19 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs @@ -3,7 +3,16 @@ using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.AlbumArtSearchService; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.LibWatcherService; +using BetterLyrics.WinUI3.Services.LyricsSearchService; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.SettingsService; +using BetterLyrics.WinUI3.Services.TranslateService; using BetterLyrics.WinUI3.ViewModels; +using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel; +using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -64,7 +73,7 @@ namespace BetterLyrics.WinUI3 private void EnsureSingleInstance() { bool createdNew; - _instanceMutex = new Mutex(true, MetadataHelper.AppName, out createdNew); + _instanceMutex = new Mutex(true, Constants.App.AppName, out createdNew); if (!createdNew) { @@ -102,11 +111,12 @@ namespace BetterLyrics.WinUI3 }) // Services .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() // ViewModels .AddSingleton() .AddSingleton() diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AIMP.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AIMP.png new file mode 100644 index 0000000..3b1f6e8 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AIMP.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AppleMusic.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AppleMusic.png new file mode 100644 index 0000000..f98f4a4 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AppleMusic.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Chrome.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Chrome.png new file mode 100644 index 0000000..ab51947 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Chrome.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Edge.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Edge.png new file mode 100644 index 0000000..6d1a52e Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Edge.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/KugouMusic.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/KugouMusic.png new file mode 100644 index 0000000..b6d8555 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/KugouMusic.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LXMusic.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LXMusic.png new file mode 100644 index 0000000..096988d Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LXMusic.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LastFM.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LastFM.png new file mode 100644 index 0000000..938c17d Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LastFM.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Leaf.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Leaf.png new file mode 100644 index 0000000..699cd55 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Leaf.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MediaPlayerWindows11.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MediaPlayerWindows11.png new file mode 100644 index 0000000..309af9a Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MediaPlayerWindows11.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MusicBee.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MusicBee.png new file mode 100644 index 0000000..6c1aa54 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MusicBee.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/NetEaseCloudMusic.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/NetEaseCloudMusic.png new file mode 100644 index 0000000..47c4ddd Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/NetEaseCloudMusic.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PotPlayer.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PotPlayer.png new file mode 100644 index 0000000..d7acd93 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PotPlayer.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/QQMusic.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/QQMusic.png new file mode 100644 index 0000000..355cdeb Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/QQMusic.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Question.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Question.png new file mode 100644 index 0000000..ca4e343 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Question.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Spotify.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Spotify.png new file mode 100644 index 0000000..9d58c7c Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Spotify.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/foobar2000.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/foobar2000.png new file mode 100644 index 0000000..5b72c8d Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/foobar2000.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/iTunes.png b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/iTunes.png new file mode 100644 index 0000000..71d8460 Binary files /dev/null and b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/iTunes.png differ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj index 0f1ebb1..b04ef3b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj @@ -53,8 +53,8 @@ - - + + @@ -65,8 +65,8 @@ - - + + @@ -78,6 +78,11 @@ + + + ..\..\..\Last.fm\src\Hqub.Lastfm\bin\Release\netstandard2.0\Hqub.Lastfm.dll + + MSBuild:Compile @@ -93,27 +98,78 @@ + + Always + + + Always + + + Always + Always + + Always + Always Always + + Always + + + Always + + + Always + + + Always + + + Always + Always Always + + Always + + + Always + + + Always + + + Always + + + Always + Always + + Always + + + Always + Always + + Always + Always diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/AmllTTmlDB.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/AmllTTmlDB.cs new file mode 100644 index 0000000..a20ff9b --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/AmllTTmlDB.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class AmllTTmlDB + { + private const string BaseUrl = "https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/"; + public const string QueryPrefix = $"{BaseUrl}raw-lyrics/"; + public const string Index = $"{BaseUrl}metadata/raw-lyrics-index.jsonl"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/App.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/App.cs new file mode 100644 index 0000000..0a9110a --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/App.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class App + { + public const string AppAuthor = "Zhe Fang"; + public const string AppName = "BetterLyrics"; + + public const string AutoStartupTaskId = "AutoStartup"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LXMusic.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LXMusic.cs new file mode 100644 index 0000000..d16ddee --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LXMusic.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class LXMusic + { + public const string QuerySuffix = "/subscribe-player-status?filter=progress,duration"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LastFMTemplate b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LastFMTemplate new file mode 100644 index 0000000..fffc31e --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LastFMTemplate @@ -0,0 +1,9 @@ +namespace BetterLyrics.WinUI3.Constants +{ + public static class LastFM + { + public const string ApiKey = "Your api key here"; + public const string SharedSecret = "Your shared secret here"; + public const string UnAuthUrl = "https://www.last.fm/settings/applications"; + } +} \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Link.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Link.cs new file mode 100644 index 0000000..f121b36 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Link.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class Link + { + public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics"; + public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info"; + public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv"; + public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerID.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerID.cs new file mode 100644 index 0000000..82e6347 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerID.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class PlayerID + { + public const string LXMusic = "cn.toside.music.desktop"; + public const string MediaPlayerWindows11 = "Microsoft.ZuneMusic_8wekyb3d8bbwe!Microsoft.ZuneMusic"; + public const string AIMP = "AIMP.exe"; + public const string Foobar2000 = "foobar2000.exe"; + public const string MusicBee = "MusicBee.exe"; + public const string PotPlayer = "PotPlayerMini64.exe"; + public const string Spotify = "Spotify.exe"; + public const string AppleMusic = "AppleInc.AppleMusicWin_nzyj5cx40ttqa!App"; + public const string NetEaseCloudMusic = "cloudmusic.exe"; + public const string KugouMusic = "kugou"; + public const string QQMusic = "QQMusic.exe"; + public const string iTunes = "49586DaveAntoine.MediaControllerforiTunes_9bzempp7dntjg!App"; + public const string Chrome = "Chrome"; + public const string Edge = "MSEdge"; + public const string BetterLyrics = "37412.BetterLyrics_rd1g0rsrrtxw8!App"; + public const string BetterLyricsDebug = "37412.BetterLyrics_c8mj3v9sysxb4!App"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerName.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerName.cs new file mode 100644 index 0000000..35a3960 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/PlayerName.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public class PlayerName + { + public const string LXMusic = "LX Music"; + public const string MediaPlayerWindows11 = "Media Player"; + public const string AIMP = "AIMP"; + public const string Foobar2000 = "foobar2000"; + public const string MusicBee = "MusicBee"; + public const string PotPlayer = "PotPlayer"; + public const string Spotify = "Spotify"; + public const string AppleMusic = "Apple Music"; + public const string NetEaseCloudMusic = "网易云音乐"; + public const string KugouMusic = "酷狗音乐"; + public const string QQMusic = "QQ 音乐"; + public const string iTunes = "iTunes"; + public const string Chrome = "Google Chrome"; + public const string Edge = "Microsoft Edge"; + public const string BetterLyrics = "BetterLyrics"; + public const string BetterLyricsDebug = "BetterLyrics (Debug)"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs new file mode 100644 index 0000000..1d5ad53 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class Time + { + public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(300); + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/iTunes.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/iTunes.cs new file mode 100644 index 0000000..dd61c97 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/iTunes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Constants +{ + public static class iTunes + { + public const string QueryPrefix = "https://itunes.apple.com/search?"; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/LyricsSearchProviderToDisplayNameConverter.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/LyricsSearchProviderToDisplayNameConverter.cs index cadcebf..54b671d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/LyricsSearchProviderToDisplayNameConverter.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/LyricsSearchProviderToDisplayNameConverter.cs @@ -15,9 +15,9 @@ namespace BetterLyrics.WinUI3.Converter return provider switch { LyricsSearchProvider.LrcLib => "LrcLib", - LyricsSearchProvider.QQ => "QQ", - LyricsSearchProvider.Netease => "Netease", - LyricsSearchProvider.Kugou => "Kugou", + LyricsSearchProvider.QQ => "QQ 音乐", + LyricsSearchProvider.Netease => "网易云音乐", + LyricsSearchProvider.Kugou => "酷狗音乐", LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db", LyricsSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"), LyricsSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"), diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToDisplayedNameConverter.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToDisplayedNameConverter.cs new file mode 100644 index 0000000..ebb3fbc --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToDisplayedNameConverter.cs @@ -0,0 +1,47 @@ +using BetterLyrics.WinUI3.Constants; +using BetterLyrics.WinUI3.Helper; +using Microsoft.UI.Xaml.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Converter +{ + public class MediaSourceProviderToDisplayedNameConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is string provider) + { + return provider switch + { + PlayerID.Spotify => PlayerName.Spotify, + PlayerID.AppleMusic => PlayerName.AppleMusic, + PlayerID.iTunes => PlayerName.iTunes, + PlayerID.KugouMusic => PlayerName.KugouMusic, + PlayerID.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic, + PlayerID.QQMusic => PlayerName.QQMusic, + PlayerID.LXMusic => PlayerName.LXMusic, + PlayerID.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11, + PlayerID.AIMP => PlayerName.AIMP, + PlayerID.Foobar2000 => PlayerName.Foobar2000, + PlayerID.MusicBee => PlayerName.MusicBee, + PlayerID.PotPlayer => PlayerName.PotPlayer, + PlayerID.Chrome => PlayerName.Chrome, + PlayerID.Edge => PlayerName.Edge, + PlayerID.BetterLyrics => PlayerName.BetterLyrics, + PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug, + _ => provider, + }; + } + return value?.ToString() ?? ""; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToLogoUriConverter.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToLogoUriConverter.cs new file mode 100644 index 0000000..4f424fc --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/MediaSourceProviderToLogoUriConverter.cs @@ -0,0 +1,47 @@ +using BetterLyrics.WinUI3.Constants; +using BetterLyrics.WinUI3.Helper; +using Microsoft.UI.Xaml.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Converter +{ + public class MediaSourceProviderToLogoUriConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is string provider) + { + return provider switch + { + PlayerID.Spotify => PathHelper.SpotifyLogoPath, + PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath, + PlayerID.iTunes => PathHelper.iTunesLogoPath, + PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath, + PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath, + PlayerID.QQMusic => PathHelper.QQMusicLogoPath, + PlayerID.LXMusic => PathHelper.LXMusicLogoPath, + PlayerID.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath, + PlayerID.AIMP => PathHelper.AIMPLogoPath, + PlayerID.Foobar2000 => PathHelper.Foobar2000LogoPath, + PlayerID.MusicBee => PathHelper.MusicBeeLogoPath, + PlayerID.PotPlayer => PathHelper.PotPlayerLogoPath, + PlayerID.Chrome => PathHelper.ChromeLogoPath, + PlayerID.Edge => PathHelper.EdgeLogoPath, + PlayerID.BetterLyrics => PathHelper.LogoPath, + PlayerID.BetterLyricsDebug => PathHelper.LogoPath, + _ => PathHelper.UnknownPlayerLogoPath, + }; + } + return PathHelper.UnknownPlayerLogoPath; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/AlbumArtChangedEventArgs.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/AlbumArtChangedEventArgs.cs index 5c2403e..f19b09f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/AlbumArtChangedEventArgs.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/AlbumArtChangedEventArgs.cs @@ -8,8 +8,9 @@ using Windows.UI; namespace BetterLyrics.WinUI3.Events { - public class AlbumArtChangedEventArgs(SoftwareBitmap? albumArtSwBitmap, Color? albumArtLightAccentColor, Color? albumArtDarkAccentColor) : EventArgs + public class AlbumArtChangedEventArgs(byte[]? bytes, SoftwareBitmap? albumArtSwBitmap, Color? albumArtLightAccentColor, Color? albumArtDarkAccentColor) : EventArgs { + public byte[]? Bytes { get; set; } = bytes; public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = albumArtSwBitmap; public Color? AlbumArtLightAccentColor { get; set; } = albumArtLightAccentColor; public Color? AlbumArtDarkAccentColor { get; set; } = albumArtDarkAccentColor; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMIsAuthenticatedChangedEventArgs.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMIsAuthenticatedChangedEventArgs.cs new file mode 100644 index 0000000..175a8b5 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMIsAuthenticatedChangedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Events +{ + public class LastFMIsAuthenticatedChangedEventArgs : EventArgs + { + public bool IsAuthenticated { get; set; } + public LastFMIsAuthenticatedChangedEventArgs(bool isAuthenticated) + { + IsAuthenticated = isAuthenticated; + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMUserChangedEventArgs.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMUserChangedEventArgs.cs new file mode 100644 index 0000000..d838ba9 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Events/LastFMUserChangedEventArgs.cs @@ -0,0 +1,18 @@ +using Hqub.Lastfm.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Events +{ + public class LastFMUserChangedEventArgs : EventArgs + { + public User? User { get; set; } + public LastFMUserChangedEventArgs(User? user) + { + User = user; + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DesktopModeHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DesktopModeHelper.cs index 916ca3c..41c61c3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DesktopModeHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DesktopModeHelper.cs @@ -1,5 +1,5 @@ using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DockModeHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DockModeHelper.cs index f1899b8..07b999b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DockModeHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/DockModeHelper.cs @@ -1,5 +1,5 @@ using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs index ae52cc2..abeea6f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs @@ -1,4 +1,4 @@ -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Graphics.Canvas.Text; using System; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs index d9941b0..58c5463 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs @@ -6,6 +6,7 @@ using Microsoft.Graphics.Canvas.Text; using Microsoft.UI; using Microsoft.UI.Xaml.Media.Imaging; using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -168,34 +169,55 @@ namespace BetterLyrics.WinUI3.Helper var themeColor = Rgba32.ParseHex(GetAccentColorsFromByte(imageBytes, 1).FirstOrDefault().ToHex()); - // 新建正方形画布 using var square = new Image(size, size, themeColor); - // 计算居中位置 int offsetX = (size - image.Width) / 2; int offsetY = (size - image.Height) / 2; - // 绘制原图到正方形画布 square.Mutate(ctx => ctx.DrawImage(image, new Point(offsetX, offsetY), 1f)); - // 保存为 PNG 字节流 using var ms = new MemoryStream(); - square.Save(ms, new PngEncoder()); + square.Save(ms, new JpegEncoder()); return ms.ToArray(); } public static byte[] Resize(byte[] imageBytes, int size) { - using Image image = Image.Load(imageBytes); - var factor = Math.Max((float)size / image.Width, (float)size / image.Height); + using (Image image = Image.Load(imageBytes)) + { + var factor = Math.Max((float)size / image.Width, (float)size / image.Height); - int width = (int)(image.Width * factor); - int height = (int)(image.Height * factor); - image.Mutate(x => x.Resize(width, height, KnownResamplers.Welch)); + int width = (int)(image.Width * factor); + int height = (int)(image.Height * factor); - using var ms = new MemoryStream(); - image.Save(ms, new PngEncoder()); - return ms.ToArray(); + if (factor > 1) + { + image.Mutate(x => x.Resize(width, height, KnownResamplers.Welch)); + } + else + { + image.Mutate(x => x.Resize(width, height, KnownResamplers.NearestNeighbor)); + } + + using var ms = new MemoryStream(); + image.Save(ms, new JpegEncoder()); + return ms.ToArray(); + } + } + + public static byte[] GenerateNoiseBGRA(int width, int height) + { + var random = new Random(); + var pixelData = new byte[width * height * 4]; + for (int i = 0; i < width * height; i++) + { + byte gray = (byte)random.Next(0, 256); + pixelData[i * 4 + 0] = gray; // B + pixelData[i * 4 + 1] = gray; // G + pixelData[i * 4 + 2] = gray; // R + pixelData[i * 4 + 3] = 255; // A + } + return pixelData; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs index fd16dc2..c1bd26f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LanguageHelper.cs @@ -1,4 +1,5 @@ using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using Lyricify.Lyrics.Helpers.General; using NTextCat; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataHelper.cs index d3ab08b..57f9db3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MetadataHelper.cs @@ -14,9 +14,6 @@ namespace BetterLyrics.WinUI3.Helper public static class MetadataHelper { - public const string AppAuthor = "Zhe Fang"; - public const string AppDisplayName = "Better Lyrics"; - public const string AppName = "BetterLyrics"; public static string AppVersion { get @@ -25,24 +22,5 @@ namespace BetterLyrics.WinUI3.Helper return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; } } - - public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics"; - public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info"; - public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv"; - public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1"; - - public static async Task GetBuildDate() - { - var assembly = Assembly.GetExecutingAssembly(); - var filePath = assembly.Location; - if (!File.Exists(filePath)) - return DateTime.MinValue; - - StorageFile file = await StorageFile.GetFileFromPathAsync(filePath); - // 获取文件基本属性 - BasicProperties props = await file.GetBasicPropertiesAsync(); - // 返回修改日期 - return props.DateModified.DateTime; - } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/NoiseGenerationHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/NoiseGenerationHelper.cs deleted file mode 100644 index 02b7a41..0000000 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/NoiseGenerationHelper.cs +++ /dev/null @@ -1,331 +0,0 @@ -using System; -using System.Buffers; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Windows.Storage; -using static BetterLyrics.WinUI3.Helper.NoiseOverlayHelper.BitmapFileCreator; - -namespace BetterLyrics.WinUI3.Helper -{ - internal static class NoiseOverlayHelper - { - - const string NoiseOverlayFileName = "noise_overlay.bmp"; - - static readonly string NoiseOverlayFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, "Assets", NoiseOverlayFileName); - - /// - /// 生成 BGRA 格式的灰阶噪声像素数据 - /// - public static byte[] GenerateNoiseBitmapBGRA(int width, int height) - { - var random = new Random(); - var pixelData = new byte[width * height * 4]; - for (int i = 0; i < width * height; i++) - { - byte gray = (byte)random.Next(0, 256); - pixelData[i * 4 + 0] = gray; // B - pixelData[i * 4 + 1] = gray; // G - pixelData[i * 4 + 2] = gray; // R - pixelData[i * 4 + 3] = 255; // A - } - return pixelData; - } - - /// - /// 生成单色灰阶随机噪声 - /// - /// 输出文件路径 - /// 图片宽度 - /// 图片高度 - public static BitmapFile GenerateNoiseBitmap(int width, int height) - { - const uint NumOfGrayscale = 16; - uint bitCount = NextPowerOfTwo((uint)Math.Round(Math.Sqrt(NumOfGrayscale))); - - var palette = BitmapFileCreator.CreateGrayscalePalette(16); - var pixelData = GenerateRandomNoise(width, height, bitCount); - - var fileHeader = BitmapFileCreator.CreateFileHeader(palette, pixelData); - var infoHeader = BitmapFileCreator.CreateInfoHeader(width, height, bitCount); - - return new BitmapFile(fileHeader, infoHeader, palette, pixelData); - } - - /// - /// 读取噪声图片的位头信息 - /// - public static BitmapFileCreator.WINBMPINFOHEADER ReadBitmapInfoHeaders(string? FilePath) - { - var _filePath = FilePath ?? NoiseOverlayFilePath; - using var fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read); - using var br = new BinaryReader(fs); - - // 跳过文件头 - fs.Seek(Marshal.SizeOf(), SeekOrigin.Begin); - - // 读取信息头 - byte[] infoHeaderBytes = br.ReadBytes(Marshal.SizeOf()); - return MemoryMarshal.Read(infoHeaderBytes); - } - - public static BitmapFileCreator.WINBMPINFOHEADER ReadBitmapInfoHeaders(byte[] FileBytes) - { - // 跳过文件头 - var offset = Marshal.SizeOf(); - - // 读取信息头 - var infoHeaderLength = Marshal.SizeOf(); - Span infoHeaderBytes = new(FileBytes, offset, infoHeaderLength); - return MemoryMarshal.Read(infoHeaderBytes); - } - - /// - /// safe 的写入 struct 到流 - /// - /// 值 strcut - /// 要写入的字节流 - /// 要被写入的结构 - public static void WriteStruct(Stream stream, in T structure) where T : struct - { - int size = Unsafe.SizeOf(); - byte[] buffer = ArrayPool.Shared.Rent(size); - try - { - MemoryMarshal.Write(buffer, in structure); - stream.Write(buffer, 0, size); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - /// - /// 随机填充位图内容 - /// - /// 填充宽度 - /// 填充高度 - /// 单个调色盘索引所占比特位数 - /// 字节数据 - private static byte[] GenerateRandomNoise(int width, int height, uint bitCount) - { - // 创建位图行字节数,4K 对齐 - int rowSize = ((width * (int)bitCount + 31) >> 5) << 2; - - // 创建随机位图数据 - Random rnd = new(); - return Enumerable.Range(0, rowSize * height) - .Select(i => (byte)rnd.Next(0x00, 0xFF)) - .ToArray(); - } - - private static uint NextPowerOfTwo(uint value) - { - value--; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - return value + 1; - } - public static class BitmapFileCreator - { - - /// - /// 创建BMP文件头 - /// - /// 调色盘数据 - /// 像素数据 - /// 文件头结构 - public static BITMAPFILEHEADER CreateFileHeader(byte[] palette, byte[] pixelData) - { - return new BITMAPFILEHEADER - { - bfType = 0x4D42, - bfSize = (uint)(Marshal.SizeOf() + - Marshal.SizeOf() + - palette.Length + - pixelData.Length), - bfReserved1 = 0, - bfReserved2 = 0, - bfOffBits = (uint)(Marshal.SizeOf() + - Marshal.SizeOf() + - palette.Length) - }; - } - - /// - /// 将指定值填充到为最接近的2的幂数 - /// - /// - /// 生成灰阶调色盘 - /// - /// 灰阶数量 - /// 调色盘byte数组 - public static byte[] CreateGrayscalePalette(int colors) - { - return Enumerable.Range(0, colors) - .SelectMany(i => Enumerable.Repeat((byte)(i * 0x10), 4)) - .ToArray(); - } - - /// - /// 创建BMP信息头 - /// - /// 宽度 - /// 高度 - /// 单个像素(调色盘索引)位数 - /// BMP信息头结构 - public static WINBMPINFOHEADER CreateInfoHeader(int width, int height, uint bitCount) - { - return new WINBMPINFOHEADER - { - biSize = (uint)Marshal.SizeOf(), - biWidth = (uint)width, - biHeight = (uint)height, - biPlanes = 1, - biBitCount = (ushort)bitCount, - biCompression = 0, - biSizeImage = 0, - biXPelsPerMeter = 0, - biYPelsPerMeter = 0, - biClrUsed = 0, - biClrImportant = 0 - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - /// - /// BMP 位图文件头 - /// - public struct BITMAPFILEHEADER - { - /// - /// 文件类型标识,用于指定文件格式 - /// - public ushort bfType; - /// - /// 文件大小,以字节为单位 - /// - public uint bfSize; - /// - /// 保留字段,未使用 - /// - public ushort bfReserved1; - /// - /// 保留字段,未使用 - /// - public ushort bfReserved2; - /// - /// 像素数据的起始位置,以字节为单位,从文件头开始计算 - /// - public uint bfOffBits; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - /// - /// BMP 位图信息头 - /// - public struct WINBMPINFOHEADER - { - /// - /// 指定此结构体的字节大小。 - /// - public uint biSize; - - /// - /// 以像素为单位的位图宽度。 - /// - public uint biWidth; - - /// - /// 以像素为单位的位图高度。 - /// - public uint biHeight; - - /// - /// 目标设备的平面数,通常为1。 - /// - public ushort biPlanes; - - /// - /// 每个像素的位数,表示颜色深度,如1、4、8、16、24、32等。 - /// - public ushort biBitCount; - - /// - /// 压缩类型,0表示不压缩。 - /// - public uint biCompression; - - /// - /// 位图图像数据的大小(以字节为单位),若图像未压缩,该值可设为0。 - /// - public uint biSizeImage; - - /// - /// 每米X轴方向的像素数(水平分辨率),通常设为0。 - /// - public uint biXPelsPerMeter; - - /// - /// 每米Y轴方向的像素数(垂直分辨率),通常设为0。 - /// - public uint biYPelsPerMeter; - - /// - /// 实际使用的颜色索引数,若为0,则使用位图中实际出现的颜色数。 - /// - public uint biClrUsed; - - /// - /// 重要颜色索引数,0表示所有颜色都重要。 - /// - public uint biClrImportant; - } - } - - public class BitmapFile(BitmapFileCreator.BITMAPFILEHEADER fileHeader, BitmapFileCreator.WINBMPINFOHEADER infoHeader, byte[] palette, byte[] pixelData) - { - public BITMAPFILEHEADER FileHeader = fileHeader; - public WINBMPINFOHEADER InfoHeader = infoHeader; - public byte[] Palette = palette; - public byte[] PixelData = pixelData; - - /// - /// 转换为byte[] - /// - /// - public static explicit operator byte[](BitmapFile bf) - { - var result = new byte[bf.FileHeader.bfSize]; - var ms = new MemoryStream(result); - bf.WriteToStream(ms); - return result; - } - - public byte[] ToByteArray() => (byte[])this; - - public Task ToByteArrayAsync() { return Task.FromResult(ToByteArray());} - - /// - /// 写入此结构到流中 - /// - public void WriteToStream(Stream stream) - { - NoiseOverlayHelper.WriteStruct(stream, FileHeader); - NoiseOverlayHelper.WriteStruct(stream, InfoHeader); - stream.Write(Palette); - stream.Write(PixelData); - stream.Flush(); - } - } - } -} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ObjectHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ObjectHelper.cs index d646c6c..c0f0b1c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ObjectHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ObjectHelper.cs @@ -32,6 +32,10 @@ namespace BetterLyrics.WinUI3.Helper if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); } + // Windows.Graphics.Imaging.SoftwareBitmap + isDisposedField = objType.GetField("_objRef_global__System_IDisposable", BindingFlags.NonPublic | BindingFlags.Instance); + if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); } + // System.IO.FileStream var strategyField = objType.GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance); if (strategyField != null) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs index 81d93af..e80ff14 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/PathHelper.cs @@ -18,6 +18,21 @@ namespace BetterLyrics.WinUI3.Helper //public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Core14.profile.xml"); public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml"); public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico"); + public static string AIMPLogoPath => Path.Combine(AssetsFolder, "AIMP.png"); + public static string Foobar2000LogoPath => Path.Combine(AssetsFolder, "foobar2000.png"); + public static string MusicBeeLogoPath => Path.Combine(AssetsFolder, "MusicBee.png"); + public static string SpotifyLogoPath => Path.Combine(AssetsFolder, "Spotify.png"); + public static string AppleMusicLogoPath => Path.Combine(AssetsFolder, "AppleMusic.png"); + public static string iTunesLogoPath => Path.Combine(AssetsFolder, "iTunes.png"); + public static string KugouMusicLogoPath => Path.Combine(AssetsFolder, "KugouMusic.png"); + public static string NetEaseCloudMusicLogoPath => Path.Combine(AssetsFolder, "NetEaseCloudMusic.png"); + public static string QQMusicLogoPath => Path.Combine(AssetsFolder, "QQMusic.png"); + public static string LXMusicLogoPath => Path.Combine(AssetsFolder, "LXMusic.png"); + public static string MediaPlayerWindows11LogoPath => Path.Combine(AssetsFolder, "MediaPlayerWindows11.png"); + public static string PotPlayerLogoPath => Path.Combine(AssetsFolder, "PotPlayer.png"); + public static string ChromeLogoPath => Path.Combine(AssetsFolder, "Chrome.png"); + public static string EdgeLogoPath => Path.Combine(AssetsFolder, "Edge.png"); + public static string UnknownPlayerLogoPath => Path.Combine(AssetsFolder, "Question.png"); public static string LogDirectory => Path.Combine(CacheFolder, "logs"); public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt"); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs index 9ec6a7f..1d8d2cb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs @@ -26,7 +26,7 @@ namespace BetterLyrics.WinUI3.Models LyricsLines = lyricsLines; } - public void SetDisplayedTextAlongWith(LyricsData translationData, int toleranceMs = 0) + public void SetDisplayedTextAlongWith(LyricsData translationData, string separator, int toleranceMs = 0) { foreach (var line in LyricsLines) { @@ -47,11 +47,11 @@ namespace BetterLyrics.WinUI3.Models { tmp = ChineseConverter.ConvertToSimplifiedChinese(transLine.OriginalText); } - line.DisplayedText = $"{line.OriginalText}\n{tmp}"; + line.DisplayedText = $"{line.OriginalText}{separator}{tmp}"; } else { - line.DisplayedText = $"{line.OriginalText}\n{transLine.OriginalText}"; + line.DisplayedText = $"{line.OriginalText}{separator}{transLine.OriginalText}"; } } else @@ -62,7 +62,7 @@ namespace BetterLyrics.WinUI3.Models } } - public void SetDisplayedTextAlongWith(string translation) + public void SetDisplayedTextAlongWith(string translation, string separator) { List translationArr = translation.Split(StringHelper.NewLine).ToList(); int i = 0; @@ -74,7 +74,7 @@ namespace BetterLyrics.WinUI3.Models } else { - line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}{translationArr[i]}"; + line.DisplayedText = $"{line.OriginalText}{separator}{translationArr[i]}"; } i++; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs index 410890d..3d6d8ef 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/MediaSourceProviderInfo.cs @@ -2,6 +2,10 @@ using BetterLyrics.WinUI3.Enums; using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; namespace BetterLyrics.WinUI3.Models { @@ -13,12 +17,39 @@ namespace BetterLyrics.WinUI3.Models [ObservableProperty] public partial string Provider { get; set; } + [ObservableProperty] + public partial bool IsLastFMTrackEnabled { get; set; } + + [ObservableProperty] + public partial int TimelineSyncThreshold { get; set; } + + [ObservableProperty] + public partial int PositionOffset { get; set; } + + [ObservableProperty] + public partial bool ResetPositionOffsetOnSongChanged { get; set; } + + [ObservableProperty] + public partial ObservableCollection LyricsSearchProvidersInfo { get; set; } + public MediaSourceProviderInfo() { } - public MediaSourceProviderInfo(string provider, bool isEnabled) + public MediaSourceProviderInfo(string provider) { Provider = provider; - IsEnabled = isEnabled; + IsEnabled = true; + IsLastFMTrackEnabled = false; + if (provider == Constants.PlayerID.AppleMusic) + { + TimelineSyncThreshold = PositionOffset = 1000; + } + else + { + TimelineSyncThreshold = 0; + PositionOffset = 0; + } + ResetPositionOffsetOnSongChanged = false; + LyricsSearchProvidersInfo = [.. Enum.GetValues().Select(p => new LyricsSearchProviderInfo(p, true))]; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.xaml.cs index 2797f7f..5b60e47 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/LyricsRenderer.xaml.cs @@ -1,9 +1,8 @@ // 2025/6/23 by Zhe Fang -using BetterLyrics.WinUI3.ViewModels; +using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media.Animation; namespace BetterLyrics.WinUI3.Renderer { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/AlbumArtSearchService.cs similarity index 94% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/AlbumArtSearchService.cs index 2516dc1..c45bc40 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/AlbumArtSearchService.cs @@ -1,6 +1,7 @@ using ATL; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.Logging; using System; @@ -14,7 +15,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using System.Threading.Tasks; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService { public class AlbumArtSearchService : IAlbumArtSearchService { @@ -102,7 +103,7 @@ namespace BetterLyrics.WinUI3.Services } // Build the iTunes API URL - string url = $"https://itunes.apple.com/search?term=" + WebUtility.UrlEncode($"{artist} {album}").Replace("%20", "+") + "&country=" + countryCode + "&entity=album&media=music&limit=1"; + string url = $"{Constants.iTunes.QueryPrefix}term=" + WebUtility.UrlEncode($"{artist} {album}").Replace("%20", "+") + "&country=" + countryCode + "&entity=album&media=music&limit=1"; // Make a request to the API using HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IAlbumArtSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/IAlbumArtSearchService.cs similarity index 82% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IAlbumArtSearchService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/IAlbumArtSearchService.cs index ef6267b..7295126 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IAlbumArtSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService/IAlbumArtSearchService.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService { public interface IAlbumArtSearchService { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/ILastFMService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/ILastFMService.cs new file mode 100644 index 0000000..3e29844 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/ILastFMService.cs @@ -0,0 +1,27 @@ +using BetterLyrics.WinUI3.Events; +using BetterLyrics.WinUI3.Models; +using Hqub.Lastfm.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Services.LastFMService +{ + public interface ILastFMService + { + User User { get; } + bool IsAuthenticated { get; } + + event EventHandler? UserChanged; + event EventHandler? IsAuthenticatedChanged; + + Task AuthAsync(); + Task ConfirmAuth(); + Task UnAuthAsync(); + Task ConfirmUnAuthAsync(); + Task TrackAsync(SongInfo songInfo); + Task RefreshAsync(); + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/LastFMService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/LastFMService.cs new file mode 100644 index 0000000..668e0f8 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LastFMService/LastFMService.cs @@ -0,0 +1,154 @@ +using BetterLyrics.WinUI3.Events; +using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Services.SettingsService; +using BetterLyrics.WinUI3.ViewModels; +using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel; +using BetterLyrics.WinUI3.Views; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Hqub.Lastfm; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.System; + +namespace BetterLyrics.WinUI3.Services.LastFMService +{ + public partial class LastFMService : ILastFMService + { + private readonly ISettingsService _settingsService; + private readonly LastfmClient _client; + + public event EventHandler? UserChanged; + public event EventHandler? IsAuthenticatedChanged; + + public Hqub.Lastfm.Entities.User? User { get; private set; } + + public bool IsAuthenticated { get; private set; } + + public LastFMService(ISettingsService settingsService) + { + _settingsService = settingsService; + + _client = new LastfmClient(Constants.LastFM.ApiKey, Constants.LastFM.SharedSecret); + _client.Session.SessionKey = _settingsService.LastFMSessionKey; + UpdateAuthStatusAsync(); + } + + public async Task ConfirmAuth() + { + try + { + await _client.AuthenticateViaWebAsync(); + _settingsService.LastFMSessionKey = _client.Session.SessionKey; + await UpdateAuthStatusAsync(); + } + catch (Exception) + { + App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LastFMAuthFailed") ?? "", InfoBarSeverity.Error); + } + } + + public async Task ConfirmUnAuthAsync() + { + _client.Session.SessionKey = ""; + _settingsService.LastFMSessionKey = ""; + await UpdateAuthStatusAsync(); + } + + public async Task AuthAsync() + { + var dialogXamlRoot = WindowHelper.GetWindowByWindowType()?.Content.XamlRoot; + if (dialogXamlRoot == null) + { + return; + } + + var dialog = new ContentDialog + { + Title = App.ResourceLoader?.GetString("LastFMRequestAuthTitle") ?? "", + Content = App.ResourceLoader?.GetString("LastFMRequestAuthDesc") ?? "", + PrimaryButtonText = App.ResourceLoader?.GetString("LastFMRequestAuthConfirm") ?? "", + CloseButtonText = App.ResourceLoader?.GetString("Cancel") ?? "", + DefaultButton = ContentDialogButton.Close, + XamlRoot = dialogXamlRoot, + }; + dialog.PrimaryButtonClick += async (s, args) => + { + await ConfirmAuth(); + }; + + string url = await _client.GetWebAuthenticationUrlAsync(); + await Launcher.LaunchUriAsync(new Uri(url)); + await dialog.ShowAsync(); + } + + public async Task UnAuthAsync() + { + var dialogXamlRoot = WindowHelper.GetWindowByWindowType()?.Content.XamlRoot; + if (dialogXamlRoot == null) + { + return; + } + + var dialog = new ContentDialog + { + Title = App.ResourceLoader?.GetString("LastFMRequestUnAuthTitle") ?? "", + Content = App.ResourceLoader?.GetString("LastFMRequestUnAuthDesc") ?? "", + PrimaryButtonText = App.ResourceLoader?.GetString("LastFMRequestUnAuthConfirm") ?? "", + CloseButtonText = App.ResourceLoader?.GetString("Cancel") ?? "", + DefaultButton = ContentDialogButton.Close, + XamlRoot = dialogXamlRoot, + }; + dialog.PrimaryButtonClick += async (s, args) => + { + await ConfirmUnAuthAsync(); + }; + + await Launcher.LaunchUriAsync(new Uri(Constants.LastFM.UnAuthUrl)); + await dialog.ShowAsync(); + } + + private async Task UpdateAuthStatusAsync() + { + IsAuthenticated = _client.Session.Authenticated; + IsAuthenticatedChanged?.Invoke(this, new LastFMIsAuthenticatedChangedEventArgs(IsAuthenticated)); + if (IsAuthenticated) + { + User = await _client.User.GetInfoAsync(); + } + else + { + User = null; + } + UserChanged?.Invoke(this, new LastFMUserChangedEventArgs(User)); + } + + public async Task TrackAsync(SongInfo songInfo) + { + if (IsAuthenticated) + { + await _client.Track.ScrobbleAsync(new Hqub.Lastfm.Entities.Scrobble + { + Track = songInfo.Title, + Artist = songInfo.Artist, + Date = DateTime.Now, + }); + } + } + + public async Task RefreshAsync() + { + await UpdateAuthStatusAsync(); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/ILibWatcherService.cs similarity index 87% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/ILibWatcherService.cs index c307f0a..4829741 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/ILibWatcherService.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Models; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.LibWatcherService { public interface ILibWatcherService { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/LibWatcherService.cs similarity index 95% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/LibWatcherService.cs index ffd1d84..bc23de0 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService/LibWatcherService.cs @@ -6,10 +6,11 @@ using System.IO; using System.Linq; using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.ViewModels; using Microsoft.UI.Dispatching; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.LibWatcherService { public class LibWatcherService : BaseViewModel, IDisposable, ILibWatcherService { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILyricsSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs similarity index 62% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILyricsSearchService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs index 197c512..107b5ac 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILyricsSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/ILyricsSearchService.cs @@ -5,10 +5,10 @@ using System.Threading; using System.Threading.Tasks; using BetterLyrics.WinUI3.Enums; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.LyricsSearchService { public interface ILyricsSearchService { - Task<(string?, LyricsSearchProvider?)> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token); + Task<(string?, LyricsSearchProvider?)> SearchAsync(string mediaSessionId, string title, string artist, string album, double durationMs, CancellationToken token); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs similarity index 95% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs index db9e480..e2ebdea 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService/LyricsSearchService.cs @@ -3,6 +3,7 @@ using ATL; using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.DependencyInjection; using Lyricify.Lyrics.Providers.Web.Kugou; using Lyricify.Lyrics.Searchers; @@ -18,7 +19,7 @@ using System.Threading; using System.Threading.Tasks; using Windows.Storage; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.LyricsSearchService { public class LyricsSearchService : ILyricsSearchService { @@ -36,7 +37,7 @@ namespace BetterLyrics.WinUI3.Services _lrcLibHttpClient = new(); _lrcLibHttpClient.DefaultRequestHeaders.Add( "User-Agent", - $"{MetadataHelper.AppName} {MetadataHelper.AppVersion} ({MetadataHelper.GithubUrl})" + $"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GithubUrl})" ); _amllTtmlDbHttpClient = new(); } @@ -60,10 +61,9 @@ namespace BetterLyrics.WinUI3.Services public async Task DownloadAmllTtmlDbIndexAsync() { - const string url = "https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/metadata/raw-lyrics-index.jsonl"; try { - using var response = await _amllTtmlDbHttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + using var response = await _amllTtmlDbHttpClient.GetAsync(Constants.AmllTTmlDB.Index, HttpCompletionOption.ResponseHeadersRead); if (!response.IsSuccessStatusCode) return false; await using var stream = await response.Content.ReadAsStreamAsync(); @@ -86,13 +86,13 @@ namespace BetterLyrics.WinUI3.Services } } - public async Task<(string?, LyricsSearchProvider?)> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token) + public async Task<(string?, LyricsSearchProvider?)> SearchAsync(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); try { - foreach (var provider in _settingsService.LyricsSearchProvidersInfo) + foreach (var provider in _settingsService.MediaSourceProvidersInfo.Where(x => x.Provider == mediaSessionId).FirstOrDefault()?.LyricsSearchProvidersInfo ?? []) { if (!provider.IsEnabled) { @@ -268,7 +268,7 @@ namespace BetterLyrics.WinUI3.Services return null; // 下载歌词内容 - var url = $"https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/raw-lyrics/{rawLyricFile}"; + var url = $"{Constants.AmllTTmlDB.QueryPrefix}{rawLyricFile}"; try { using var response = await _amllTtmlDbHttpClient.GetAsync(url); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/IMediaSessionsService.cs similarity index 84% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/IMediaSessionsService.cs index bd718dd..d103e5b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/IMediaSessionsService.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Models; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.MediaSessionsService { - public interface IPlaybackService + public interface IMediaSessionsService { event EventHandler? IsPlayingChanged; event EventHandler? TimelineChanged; @@ -23,5 +23,6 @@ namespace BetterLyrics.WinUI3.Services bool IsPlaying { get; } SongInfo? SongInfo { get; } + TimeSpan Position { get; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/MediaSessionsService.cs similarity index 73% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/MediaSessionsService.cs index 6c2822c..fb3ae85 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/MediaSessionsService/MediaSessionsService.cs @@ -1,17 +1,20 @@ // 2025/6/23 by Zhe Fang +using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Services.AlbumArtSearchService; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.ViewModels; +using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using EvtSource; using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml; -using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; @@ -25,24 +28,22 @@ using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.Media.Control; using Windows.Storage.Streams; -using Windows.UI.Shell; using WindowsMediaController; -using static WindowsMediaController.MediaManager; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.MediaSessionsService { - public partial class PlaybackService : BaseViewModel, IPlaybackService, + public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService, IRecipient>>, IRecipient>> { private readonly IAlbumArtSearchService _albumArtSearchService; - private readonly ILogger _logger; + private readonly ILogger _logger; - private readonly string _lxMusicId = "cn.toside.music.desktop"; private double _lxMusicPositionSeconds = 0; private double _lxMusicDurationSeconds = 0; private bool _cachedIsPlaying = false; + private TimeSpan _cachedPosition = TimeSpan.Zero; private EventSourceReader? _sse = null; @@ -62,10 +63,10 @@ namespace BetterLyrics.WinUI3.Services public event EventHandler? AlbumArtChangedChanged; public event EventHandler? MediaSourceProvidersInfoChanged; - public PlaybackService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService) + public MediaSessionsService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService) { _albumArtSearchService = albumArtSearchService; - _logger = Ioc.Default.GetRequiredService>(); + _logger = Ioc.Default.GetRequiredService>(); _mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo; InitMediaManager(); @@ -73,6 +74,7 @@ namespace BetterLyrics.WinUI3.Services public bool IsPlaying => _cachedIsPlaying; public SongInfo? SongInfo => _cachedSongInfo; + public TimeSpan Position => _cachedPosition; private bool IsMediaSourceEnabled(string id) { @@ -109,12 +111,24 @@ namespace BetterLyrics.WinUI3.Services var focusedSession = _mediaManager.GetFocusedSession(); - if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != focusedSession) return; + if (mediaSession != focusedSession) return; - _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + if (!IsMediaSourceEnabled(mediaSession.Id)) { - TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(timelineProperties.Position, timelineProperties.EndTime)); - }); + _cachedPosition = TimeSpan.Zero; + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, TimeSpan.Zero)); + }); + } + else + { + _cachedPosition = timelineProperties.Position; + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties.EndTime)); + }); + } } private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo) @@ -125,13 +139,20 @@ namespace BetterLyrics.WinUI3.Services var focusedSession = _mediaManager.GetFocusedSession(); RecordMediaSourceProviderInfo(mediaSession); - if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != focusedSession) return; + if (mediaSession != focusedSession) return; - _cachedIsPlaying = playbackInfo.PlaybackStatus switch + if (!IsMediaSourceEnabled(mediaSession.Id)) { - GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true, - _ => false, - }; + _cachedIsPlaying = false; + } + else + { + _cachedIsPlaying = playbackInfo.PlaybackStatus switch + { + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true, + _ => false, + }; + } _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => { @@ -149,55 +170,88 @@ namespace BetterLyrics.WinUI3.Services var focusedSession = _mediaManager.GetFocusedSession(); RecordMediaSourceProviderInfo(mediaSession); - if (!IsMediaSourceEnabled(id) || mediaSession != focusedSession) return; + if (mediaSession != focusedSession) return; - _cachedSongInfo = new SongInfo + if (!IsMediaSourceEnabled(id)) { - Title = mediaProperties.Title, - Artist = mediaProperties.Artist, - Album = mediaProperties.AlbumTitle, - DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds, - SourceAppUserModelId = id, - }; + _cachedSongInfo = null; - _cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f); + _onAnyMediaPropertyChangedRunner.RunAsync(async token => + { + _logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}", + mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle); - _onAnyMediaPropertyChangedRunner.RunAsync(async token => - { - _logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}", - mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle); - - if (id == _lxMusicId) - { - StartSSE(); - } - else - { - StopSSE(); - } - - if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference) - { - _SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference); - } - else - { - _SMTCAlbumArtBytes = null; - } - - await _albumArtRefreshRunner.RunAsync(async tokne => - { - await UpdateAlbumArtRelated(tokne); - }); - - if (!token.IsCancellationRequested) - { - _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + if (id == Constants.PlayerID.LXMusic) { - SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo)); + StopSSE(); + } + + _SMTCAlbumArtBytes = null; + + await _albumArtRefreshRunner.RunAsync(async tokne => + { + await UpdateAlbumArtRelated(tokne); }); - } - }).ConfigureAwait(false); + + if (!token.IsCancellationRequested) + { + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo)); + }); + } + }).ConfigureAwait(false); + } + else + { + _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); + + _onAnyMediaPropertyChangedRunner.RunAsync(async token => + { + _logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}", + mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle); + + if (id == Constants.PlayerID.LXMusic) + { + StartSSE(); + } + else + { + StopSSE(); + } + + if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference) + { + _SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference); + } + else + { + _SMTCAlbumArtBytes = null; + } + + await _albumArtRefreshRunner.RunAsync(async tokne => + { + await UpdateAlbumArtRelated(tokne); + }); + + if (!token.IsCancellationRequested) + { + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo)); + }); + } + }).ConfigureAwait(false); + } } private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession) @@ -231,7 +285,9 @@ namespace BetterLyrics.WinUI3.Services var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id); if (found == null) { - _mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id, true)); + _mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id)); + // 在这里就写进设置 + // 因为 SettingsPageViewModel 可能还没有初始化 _settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo; _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => { @@ -258,9 +314,9 @@ namespace BetterLyrics.WinUI3.Services if (focusedSession == null || focusedSession.ControlSession == null) return; var mediaProps = await focusedSession.ControlSession.TryGetMediaPropertiesAsync(); + MediaManager_OnAnyTimelinePropertyChanged(focusedSession, focusedSession.ControlSession.GetTimelineProperties()); MediaManager_OnAnyMediaPropertyChanged(focusedSession, mediaProps); MediaManager_OnAnyPlaybackStateChanged(focusedSession, focusedSession.ControlSession.GetPlaybackInfo()); - MediaManager_OnAnyTimelinePropertyChanged(focusedSession, focusedSession.ControlSession.GetTimelineProperties()); } private async Task UpdateAlbumArtRelated(CancellationToken token) @@ -285,8 +341,8 @@ namespace BetterLyrics.WinUI3.Services token.ThrowIfCancellationRequested(); } - bytes = ImageHelper.MakeSquareWithThemeColor(bytes); bytes = ImageHelper.Resize(bytes, _targetAlbumArtSize); + bytes = ImageHelper.MakeSquareWithThemeColor(bytes); using var stream = new InMemoryRandomAccessStream(); await stream.WriteAsync(bytes.AsBuffer()); @@ -303,7 +359,7 @@ namespace BetterLyrics.WinUI3.Services _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => { - AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(albumArtSwBitmap, albumArtLightAccentColor, albumArtDarkAccentColor)); + AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(null, albumArtSwBitmap, albumArtLightAccentColor, albumArtDarkAccentColor)); }); } @@ -311,7 +367,7 @@ namespace BetterLyrics.WinUI3.Services { try { - _sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}/subscribe-player-status?filter=progress,duration")).Start(); + _sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}{Constants.LXMusic.QuerySuffix}")).Start(); _sse.MessageReceived += Sse_MessageReceived; _sse.Disconnected += Sse_Disconnected; } @@ -348,7 +404,7 @@ namespace BetterLyrics.WinUI3.Services private void Sse_MessageReceived(object sender, EventSourceMessageEventArgs e) { - if (_cachedSongInfo?.SourceAppUserModelId == _lxMusicId) + if (_cachedSongInfo?.SourceAppUserModelId == Constants.PlayerID.LXMusic) { var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement); if (data.ValueKind == JsonValueKind.Number) @@ -421,7 +477,6 @@ namespace BetterLyrics.WinUI3.Services if (message.PropertyName == nameof(SettingsPageViewModel.MediaSourceProvidersInfo)) { _mediaSourceProvidersInfo = [.. message.NewValue]; - _settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo; MediaManager_OnFocusedSessionChanged(null); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/ISettingsService.cs similarity index 94% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/ISettingsService.cs index cdcb0d8..ffcb93c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/ISettingsService.cs @@ -6,7 +6,7 @@ using BetterLyrics.WinUI3.Models; using Microsoft.UI.Xaml; using Windows.UI; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.SettingsService { public interface ISettingsService { @@ -38,7 +38,6 @@ namespace BetterLyrics.WinUI3.Services string LibreTranslateServer { get; set; } int SelectedTargetLanguageIndex { get; set; } - bool ResetPositionOffsetOnSongChanged { get; set; } int PositionOffset { get; set; } // Lyrics lib @@ -78,7 +77,6 @@ namespace BetterLyrics.WinUI3.Services float LyricsLineSpacingFactor { get; set; } - List LyricsSearchProvidersInfo { get; set; } List AlbumArtSearchProvidersInfo { get; set; } List MediaSourceProvidersInfo { get; set; } @@ -94,7 +92,6 @@ namespace BetterLyrics.WinUI3.Services LyricsDisplayType DisplayType { get; set; } - int TimelineSyncThreshold { get; set; } int LockHotKeyIndex { get; set; } bool IsImmersiveMode { get; set; } string LXMusicServer { get; set; } @@ -107,5 +104,10 @@ namespace BetterLyrics.WinUI3.Services PlaybackOrder PlaybackOrder { get; set; } bool IsLibreTranslateEnabled { get; set; } string DockMonitorDeviceName { get; set; } + + // LastFM + string LastFMSessionKey { get; set; } + + string LyricsTranslationSeparator { get; set; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/SettingsService.cs similarity index 90% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/SettingsService.cs index 549235f..c37f580 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService/SettingsService.cs @@ -10,10 +10,11 @@ using Microsoft.UI.Xaml; using System; using System.Collections.Generic; using System.Linq; +using Windows.Media.Core; using Windows.Storage; using Windows.UI; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.SettingsService { public class SettingsService : ISettingsService { @@ -51,7 +52,7 @@ namespace BetterLyrics.WinUI3.Services private const string IsLyricsGlowEffectEnabledKey = "IsLyricsGlowEffectEnabled"; private const string LanguageKey = "Language"; - private const string LocalLyricsFoldersKey = "LocalLyricsFolders"; + private const string LocalMediaFoldersKey = "LocalLyricsFolders"; private const string LyricsAlignmentTypeKey = "TextAlignmentType"; private const string SongInfoAlignmentTypeKey = "SongInfoAlignmentType"; private const string LyricsBlurAmountKey = "LyricsBlurAmount"; @@ -71,7 +72,6 @@ namespace BetterLyrics.WinUI3.Services private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope"; private const string LyricsHighlightSopeKey = "LyricsHighlightSope"; private const string LyricsLineSpacingFactorKey = "LyricsLineSpacingFactor"; - private const string LyricsSearchProvidersInfoKey = "LyricsSearchProvidersInfo"; private const string AlbumArtSearchProvidersInfoKey = "AlbumArtSearchProvidersInfo"; private const string LyricsVerticalEdgeOpacityKey = "LyricsVerticalEdgeOpacity"; @@ -94,11 +94,8 @@ namespace BetterLyrics.WinUI3.Services private const string LyricsScrollEasingTypeKey = "LyricsScrollEasingType"; private const string LyricsScrollDurationKey = "LyricsScrollDuration"; - public const string TimelineSyncThresholdKey = "TimelineSyncThreshold"; - private const string IsLyricsFloatAnimationEnabledKey = "IsLyricsFloatAnimationEnabled"; - private const string ResetPositionOffsetOnSongChangedKey = "ResetPositionOffsetOnSongChanged"; private const string PlaybackOrderKey = "PlaybackOrder"; private const string PositionOffsetKey = "PositionOffset"; @@ -115,6 +112,11 @@ namespace BetterLyrics.WinUI3.Services private const string DockMonitorDeviceNameKey = "DockMonitorDeviceName"; + // LastFM + private const string LastFMSessionKeyKey = "LastFMSessionKey"; + + private const string LyricsTranslationSeparatorKey = "LyricsTranslationSeparator"; + private readonly ApplicationDataContainer _localSettings; public SettingsService() @@ -123,29 +125,7 @@ namespace BetterLyrics.WinUI3.Services SetDefault(IsFirstRunKey, true); // Lyrics lib - SetDefault(LocalLyricsFoldersKey, "[]"); - SetDefault( - LyricsSearchProvidersInfoKey, - System.Text.Json.JsonSerializer.Serialize( - Enum.GetValues() - .Select(p => new LyricsSearchProviderInfo(p, true)) - .ToList(), - SourceGenerationContext.Default.ListLyricsSearchProviderInfo - ) - ); - if (LyricsSearchProvidersInfo.Count != Enum.GetValues().Length) - { - LyricsSearchProvidersInfo = Enum.GetValues() - .Select(p => new LyricsSearchProviderInfo( - p, - LyricsSearchProvidersInfo - .Where(x => x.Provider == p) - .FirstOrDefault() - ?.IsEnabled ?? true - )) - .ToList(); - } - + SetDefault(LocalMediaFoldersKey, "[]"); SetDefault( AlbumArtSearchProvidersInfoKey, System.Text.Json.JsonSerializer.Serialize( @@ -169,6 +149,23 @@ namespace BetterLyrics.WinUI3.Services } SetDefault(MediaSourceProvidersInfoKey, "[]"); + var tmp = MediaSourceProvidersInfo; + for (int i = 0; i < tmp.Count; i++) + { + var mediaSource = tmp[i]; + if (mediaSource.LyricsSearchProvidersInfo == null || mediaSource.LyricsSearchProvidersInfo.Count != Enum.GetValues().Length) + { + mediaSource.LyricsSearchProvidersInfo = [..Enum.GetValues() + .Select(p => new LyricsSearchProviderInfo( + p, + mediaSource.LyricsSearchProvidersInfo? + .Where(x => x.Provider == p) + .FirstOrDefault() + ?.IsEnabled ?? true + ))]; + } + } + MediaSourceProvidersInfo = tmp; // App appearance SetDefault(LanguageKey, (int)Language.FollowSystem); @@ -235,11 +232,9 @@ namespace BetterLyrics.WinUI3.Services SetDefault(LyricsScrollEasingTypeKey, (int)EasingType.EaseInOutSine); SetDefault(LyricsScrollDurationKey, 500); // 500ms - SetDefault(TimelineSyncThresholdKey, 0); // 0ms SetDefault(IsLyricsFloatAnimationEnabledKey, true); - SetDefault(ResetPositionOffsetOnSongChangedKey, false); SetDefault(PositionOffsetKey, 0); SetDefault(LockHotKeyIndexKey, 'U' - 'A'); SetDefault(DockPlacementKey, (int)DockPlacement.Top); @@ -250,6 +245,10 @@ namespace BetterLyrics.WinUI3.Services SetDefault(LyricsFontFamilyKey, FontHelper.SystemFontFamilies.ElementAtOrDefault(0)); SetDefault(IsDragEverywhereEnabledKey, false); SetDefault(DockMonitorDeviceNameKey, MonitorHelper.GetPrimaryMonitorDeviceName()); + + SetDefault(LastFMSessionKeyKey, ""); + + SetDefault(LyricsTranslationSeparatorKey, StringHelper.NewLine); } public bool IsDragEverywhereEnabled @@ -448,12 +447,12 @@ namespace BetterLyrics.WinUI3.Services { get => System.Text.Json.JsonSerializer.Deserialize( - GetValue(LocalLyricsFoldersKey) ?? "[]", + GetValue(LocalMediaFoldersKey) ?? "[]", SourceGenerationContext.Default.ListLocalMediaFolder )!; set => SetValue( - LocalLyricsFoldersKey, + LocalMediaFoldersKey, System.Text.Json.JsonSerializer.Serialize( value, SourceGenerationContext.Default.ListLocalMediaFolder @@ -563,23 +562,6 @@ namespace BetterLyrics.WinUI3.Services set => SetValue(LyricsLineSpacingFactorKey, value); } - public List LyricsSearchProvidersInfo - { - get => - System.Text.Json.JsonSerializer.Deserialize( - GetValue(LyricsSearchProvidersInfoKey) ?? "[]", - SourceGenerationContext.Default.ListLyricsSearchProviderInfo - )!; - set => - SetValue( - LyricsSearchProvidersInfoKey, - System.Text.Json.JsonSerializer.Serialize( - value, - SourceGenerationContext.Default.ListLyricsSearchProviderInfo - ) - ); - } - public List AlbumArtSearchProvidersInfo { get => @@ -656,24 +638,12 @@ namespace BetterLyrics.WinUI3.Services set => SetValue(IgnoreFullscreenWindowKey, value); } - public int TimelineSyncThreshold - { - get => GetValue(TimelineSyncThresholdKey); - set => SetValue(TimelineSyncThresholdKey, value); - } - public bool IsLyricsFloatAnimationEnabled { get => GetValue(IsLyricsFloatAnimationEnabledKey); set => SetValue(IsLyricsFloatAnimationEnabledKey, value); } - public bool ResetPositionOffsetOnSongChanged - { - get => GetValue(ResetPositionOffsetOnSongChangedKey); - set => SetValue(ResetPositionOffsetOnSongChangedKey, value); - } - public PlaybackOrder PlaybackOrder { get => (PlaybackOrder)GetValue(PlaybackOrderKey); @@ -698,6 +668,22 @@ namespace BetterLyrics.WinUI3.Services set => SetValue(DockMonitorDeviceNameKey, value); } + // LastFM + + public string LastFMSessionKey + { + get => GetValue(LastFMSessionKeyKey)!; + set => SetValue(LastFMSessionKeyKey, value); + } + + public string LyricsTranslationSeparator + { + get => GetValue(LyricsTranslationSeparatorKey)!; + set => SetValue(LyricsTranslationSeparatorKey, value); + } + + // Common methods + private T? GetValue(string key) { if (_localSettings.Values.TryGetValue(key, out object? value)) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ITranslateService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/ITranslateService.cs similarity index 88% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ITranslateService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/ITranslateService.cs index 25a6282..47f1c0d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ITranslateService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/ITranslateService.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.TranslateService { public interface ITranslateService { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/TranslateService.cs similarity index 96% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/TranslateService.cs index b867ff0..fdda2f8 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/TranslateService/TranslateService.cs @@ -1,6 +1,7 @@ using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Serialization; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.ViewModels; using Lyricify.Lyrics.Helpers.General; using Microsoft.UI.Dispatching; @@ -13,7 +14,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace BetterLyrics.WinUI3.Services +namespace BetterLyrics.WinUI3.Services.TranslateService { public class TranslateService : BaseViewModel, ITranslateService { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw index 1454d75..96f05a9 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw @@ -285,9 +285,6 @@ About - - Lyrics library - App appearance @@ -566,13 +563,10 @@ If you encounter any problems, please go to the Settings page, About tab, and vi Easing animation type - Playback sources + Playback and lyrics sources - Playback sources - - - Enable or disable lyrics display for a specified media source + Monitor this playback source Log record @@ -643,7 +637,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi Translate server is not set, please configure it in settings first - + Reset to 0 when switching songs @@ -919,4 +913,67 @@ If you encounter any problems, please go to the Settings page, About tab, and vi This setting will not affect the dock mode and the dock mode will always remain centered. + + Last.fm + + + Last.fm + + + Authorize + + + Revoke authorization + + + Track listening history via Last.fm + + + Username + + + Total playing count + + + Registration date + + + Source and translation separator + + + Immersive mode + + + Play all + + + No playback source captured + + + Refresh + + + Authorization failed, please try again + + + Grant BetterLyrics permission to access your Last.fm account + + + Please complete the authorization in your browser + + + I have completed the authorization + + + Cancel + + + Revoke BetterLyrics' permission to access your Last.fm account + + + Please complete the cancellation operation in your browser + + + I have canceled my authorization + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw index a087abb..91c3066 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw @@ -285,9 +285,6 @@ について - - 歌詞 - アプリの外観 @@ -566,13 +563,10 @@ アニメーションタイプを緩和します - 再生ソース + プレイと歌詞 - 再生ソース - - - 指定されたメディアソースの歌詞ディスプレイを有効または無効にする + この再生ソースを監視します ログレコード @@ -643,7 +637,7 @@ 翻訳サーバーは設定されていません。最初に設定で構成してください - + 曲を切り替えるときに0にリセットします @@ -919,4 +913,67 @@ この設定はドックモードには影響しません。ドックモードは常に中心のままです。 + + Last.fm + + + Last.fm + + + 許可 + + + 承認を取り消します + + + last.fm 経由でリスニング履歴を追跡します + + + ユーザー名 + + + 合計プレイカウント + + + 登録日 + + + ソースおよび翻訳セパレーター + + + 没入モード + + + すべてを再生します + + + キャプチャされた再生ソースはありません + + + リフレッシュします + + + 承認が失敗しました、もう一度やり直してください + + + BetterLyricsにLast.fmアカウントへのアクセスを許可してください! + + + ブラウザの承認を完了してください + + + 私は承認を完了しました + + + キャンセル + + + Last.fmアカウントへのBetterLyricsアクセスを取り消します! + + + ブラウザでキャンセル操作を完了してください + + + 認可をキャンセルしました + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw index 5995e3b..527a6ea 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw @@ -285,9 +285,6 @@ 에 대한 - - 가사 - 앱 모양 @@ -566,13 +563,10 @@ 애니메이션 유형 완화 - 재생 소스 + 연극과 가사 - 재생 소스 - - - 지정된 미디어 소스의 가사 디스플레이 활성화 또는 비활성화 + 이 재생 소스를 모니터링하십시오 로그 레코드 @@ -643,7 +637,7 @@ 번역 서버가 설정되지 않았습니다. 먼저 설정으로 구성하십시오. - + 노래를 전환 할 때 0 으로 재설정하십시오 @@ -919,4 +913,67 @@ 이 설정은 도크 모드에 영향을 미치지 않으며 도크 모드는 항상 중앙에 유지됩니다. + + Last.fm + + + Last.fm + + + 승인 + + + 취소 승인 + + + Last.fm 을 통해 청취 기록을 추적합니다 + + + 사용자 이름 + + + 총 플레이 카운트 + + + 등록일 + + + 소스 및 번역 분리기 + + + 몰입 형 모드 + + + 모두 재생하십시오 + + + 재생 소스가 캡처되지 않았습니다 + + + 새로 고치다 + + + 승인이 실패했습니다. 다시 시도하십시오 + + + Last.fm 계정에 BetterLyrics 액세스 권한을 부여하세요! + + + 브라우저에서 승인을 완료하십시오 + + + 나는 승인을 완료했다 + + + 취소 + + + Last.fm 계정에 대한 BetterLyrics 액세스를 취소하십시오! + + + 브라우저에서 취소 작업을 완료하십시오 + + + 내 승인을 취소했습니다 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw index 64cf5b6..e70e07e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw @@ -285,9 +285,6 @@ 关于 - - 歌词源 - 应用外观 @@ -566,13 +563,10 @@ 缓动动画类型 - 播放源 + 播放与歌词源 - 播放源 - - - 为指定媒体源启用或禁用歌词显示 + 监听此播放源 日志记录 @@ -643,7 +637,7 @@ 未设置Translate服务器,请先在设置中进行配置 - + 切换歌曲时重置为 0 @@ -919,4 +913,67 @@ 此设置不会影响停靠模式,停靠模式将始终保持居中。 + + Last.fm + + + Last.fm + + + 授权 + + + 撤销授权 + + + 通过 Last.fm 跟踪听歌历史记录 + + + 用户名 + + + 听歌总数量 + + + 注册日期 + + + 原文译文分隔符 + + + 沉浸模式 + + + 播放全部 + + + 没有捕获的播放源 + + + 刷新 + + + 授权失败,请重试 + + + 授予 BetterLyrics 访问您 Last.fm 账户的权限 + + + 请在浏览器中完成授权 + + + 我已经完成了授权 + + + 取消 + + + 撤销 BetterLyrics 访问您 Last.fm 账户的权限 + + + 请在浏览器中完成取消操作 + + + 我已经取消了我的授权 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw index 4608ae5..be81055 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw @@ -285,9 +285,6 @@ 關於 - - 歌詞源 - 應用外觀 @@ -566,13 +563,10 @@ 緩動動畫類型 - 播放來源 + 播放與歌詞源 - 播放來源 - - - 為指定媒體源啟用或禁用歌詞顯示 + 監聽此播放來源 日誌記錄 @@ -643,7 +637,7 @@ 未設定翻譯伺服器,請先在設定中進行配置 - + 切換歌曲時重置為 0 @@ -919,4 +913,67 @@ 此設定不會影響停靠模式,停靠模式將始終保持居中。 + + Last.fm + + + Last.fm + + + 授權 + + + 撤銷授權 + + + 透過 Last.fm 追蹤聽歌歷史記錄 + + + 使用者名稱 + + + 聽歌總數量 + + + 註冊日期 + + + 原文譯文分隔符 + + + 沉浸模式 + + + 播放全部 + + + 沒有捕獲的播放源 + + + 重新整理 + + + 授權失敗,請重試 + + + 授予 BetterLyrics 訪問您 Last.fm 賬戶的權限 + + + 請在瀏覽器中完成授權 + + + 我已經完成了授權 + + + 取消 + + + 撤銷 BetterLyrics 訪問您 Last.fm 賬戶的權限 + + + 請在瀏覽器中完成取消操作 + + + 我已經取消了我的授權 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseViewModel.cs index 9510752..9ab0484 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseViewModel.cs @@ -1,6 +1,6 @@ // 2025/6/23 by Zhe Fang -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Dispatching; using System; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseWindowViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseWindowViewModel.cs index 6b86c24..82f2ce0 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseWindowViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/BaseWindowViewModel.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs index f774321..05353e2 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs @@ -3,7 +3,8 @@ using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -27,7 +28,7 @@ namespace BetterLyrics.WinUI3.ViewModels IRecipient>, IRecipient> { - private readonly IPlaybackService _playbackService; + private readonly IMediaSessionsService _mediaSessionsService; private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1)); private bool _isDockMode = false; @@ -37,12 +38,11 @@ namespace BetterLyrics.WinUI3.ViewModels private int _lyricsDockFontSize = 8; private int _lyricsDesktopFontSize = 8; - public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService) + public LyricsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService) : base(settingsService) { IsFirstRun = _settingsService.IsFirstRun; IsTranslationEnabled = _settingsService.IsTranslationEnabled; DisplayType = _settingsService.DisplayType; - ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged; PositionOffset = _settingsService.PositionOffset; IsImmersiveMode = _settingsService.IsImmersiveMode; ShowTranslationOnly = _settingsService.ShowTranslationOnly; @@ -56,12 +56,12 @@ namespace BetterLyrics.WinUI3.ViewModels //Volume = SystemVolumeHelper.GetMasterVolume(); //SystemVolumeHelper.VolumeChanged += SystemVolumeHelper_VolumeChanged; - _playbackService = playbackService; - _playbackService.SongInfoChanged += PlaybackService_SongInfoChanged; - _playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged; - _playbackService.TimelineChanged += PlaybackService_TimelineChanged; + _mediaSessionsService = mediaSessionsService; + _mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged; + _mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged; + _mediaSessionsService.TimelineChanged += PlaybackService_TimelineChanged; - IsSongPlaying = _playbackService.IsPlaying; + IsSongPlaying = _mediaSessionsService.IsPlaying; } private void PlaybackService_TimelineChanged(object? sender, Events.TimelineChangedEventArgs e) @@ -83,10 +83,6 @@ namespace BetterLyrics.WinUI3.ViewModels { SongInfo = e.SongInfo; SongDurationSeconds = SongInfo?.Duration ?? 0; - if (ResetPositionOffsetOnSongChanged) - { - PositionOffset = 0; - } } [ObservableProperty] @@ -138,10 +134,6 @@ namespace BetterLyrics.WinUI3.ViewModels [NotifyPropertyChangedRecipients] public partial bool ShowTranslationOnly { get; set; } - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial bool ResetPositionOffsetOnSongChanged { get; set; } - [ObservableProperty] public partial bool IsSongPlaying { get; set; } @@ -213,25 +205,25 @@ namespace BetterLyrics.WinUI3.ViewModels [RelayCommand] private async Task PlaySongAsync() { - await _playbackService.PlayAsync(); + await _mediaSessionsService.PlayAsync(); } [RelayCommand] private async Task PauseSongAsync() { - await _playbackService.PauseAsync(); + await _mediaSessionsService.PauseAsync(); } [RelayCommand] private async Task PreviousSongAsync() { - await _playbackService.PreviousAsync(); + await _mediaSessionsService.PreviousAsync(); } [RelayCommand] private async Task NextSongAsync() { - await _playbackService.NextAsync(); + await _mediaSessionsService.NextAsync(); } partial void OnIsFirstRunChanged(bool value) @@ -271,17 +263,17 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStandardFontSize)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStandardFontSize)) { UpdateHintMessageFontSize(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDockFontSize)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDockFontSize)) { UpdateHintMessageFontSize(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDesktopFontSize)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDesktopFontSize)) { UpdateHintMessageFontSize(); } @@ -290,9 +282,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontFamily)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontFamily)) { LyricsFontFamily = message.NewValue; } @@ -306,9 +298,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsRendererViewModel) + if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel) { - if (message.PropertyName == nameof(LyricsRendererViewModel.TotalTime)) + if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.TotalTime)) { if (_timelineThrottle.CanTrigger()) { @@ -323,9 +315,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsRendererViewModel) + if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel) { - if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsSearchProvider)) + if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.LyricsSearchProvider)) { LyricsSearchProvider = message.NewValue; } @@ -334,9 +326,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is LyricsRendererViewModel) + if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel) { - if (message.PropertyName == nameof(LyricsRendererViewModel.TranslationSearchProvider)) + if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.TranslationSearchProvider)) { TranslationSearchProvider = message.NewValue; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Ctor.cs similarity index 69% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Ctor.cs index acc2240..7e3e48d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Ctor.cs @@ -1,20 +1,37 @@ using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.LibWatcherService; +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 +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel { - public LyricsRendererViewModel(ISettingsService settingsService, IPlaybackService playbackService, ILyricsSearchService musicSearchService, ILibWatcherService libWatcherService, ITranslateService libreTranslateService) : base(settingsService) + public LyricsRendererViewModel( + ISettingsService settingsService, + IMediaSessionsService mediaSessionsService, + ILyricsSearchService musicSearchService, + ILibWatcherService libWatcherService, + ITranslateService libreTranslateService, + ILastFMService lastFMService + ) : base(settingsService) { _lyrcsSearchService = musicSearchService; - _playbackService = playbackService; + _mediaSessionsService = mediaSessionsService; _libWatcherService = libWatcherService; _translateService = libreTranslateService; + _lastFMService = lastFMService; + + _mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo; + _logger = Ioc.Default.GetRequiredService>(); _albumArtCornerRadius = _settingsService.CoverImageRadius; @@ -59,9 +76,13 @@ namespace BetterLyrics.WinUI3.ViewModels _showTranslationOnly = _settingsService.ShowTranslationOnly; _isLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled; _targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex; + _lyricsTranslationSeparator = _settingsService.LyricsTranslationSeparator; + + _dockPlacement = _settingsService.DockPlacement; + _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment(); - _timelineSyncThreshold = _settingsService.TimelineSyncThreshold; + _timelineSyncThreshold = 0; _canvasYScrollTransition.SetDuration(_settingsService.LyricsScrollDuration / 1000f); _canvasYScrollTransition.SetEasingType(_settingsService.LyricsScrollEasingType); @@ -73,12 +94,12 @@ namespace BetterLyrics.WinUI3.ViewModels _libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged; - _playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged; - _playbackService.SongInfoChanged += PlaybackService_SongInfoChanged; - _playbackService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged; - _playbackService.TimelineChanged += PlaybackService_TimelineChanged; + _mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged; + _mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged; + _mediaSessionsService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged; + _mediaSessionsService.TimelineChanged += PlaybackService_TimelineChanged; - _isPlaying = _playbackService.IsPlaying; + _isPlaying = _mediaSessionsService.IsPlaying; UpdateColorConfig(); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Draw.cs similarity index 86% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Draw.cs index 4688211..55c0020 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Draw.cs @@ -17,7 +17,7 @@ using Windows.Foundation; using Windows.Graphics.Effects; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel { @@ -35,15 +35,15 @@ namespace BetterLyrics.WinUI3.ViewModels if (_isDockMode) { - FillBackgroundColor(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value); + FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _albumArtBgOpacity / 100f); } else if (_isDesktopMode) { - FillBackgroundColor(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value); + FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _albumArtBgOpacity / 100f); } else { - FillBackgroundColor(control, combinedDs, _albumArtAccentColorTransition.Value, 0f, _albumArtBgOpacity / 100f); + FillBackground(control, combinedDs, _albumArtAccentColorTransition.Value, 0f, _albumArtBgOpacity / 100f); DrawAlbumArtBackground(control, combinedDs); } @@ -83,7 +83,7 @@ namespace BetterLyrics.WinUI3.ViewModels $"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" + + $"Song duration: {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}\n" + $"Y offset: {_canvasYScrollTransition.Value}", new Vector2(10, 40), ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White, @@ -259,7 +259,7 @@ namespace BetterLyrics.WinUI3.ViewModels } ); - if (line.HighlightOpacityTransition.Value !=0) + if (line.HighlightOpacityTransition.Value != 0) { // 再叠加高亮行歌词层(前景歌词层) using var mask = new CanvasCommandList(control.Device); @@ -324,13 +324,13 @@ namespace BetterLyrics.WinUI3.ViewModels ); // Brushes - using var fadeInBrush = GetHorizontalFillBrush( + using var fadeInBrush = CreateHorizontalFillBrush( control, [(0f, 0f), (1f, 1f)], (float)highlightRect.Right - fadingWidth, fadingWidth ); - using var fadeOutBrush = GetHorizontalFillBrush( + using var fadeOutBrush = CreateHorizontalFillBrush( control, [(0f, 1f), (1f, 0f)], (float)highlightRect.Right, @@ -345,22 +345,34 @@ namespace BetterLyrics.WinUI3.ViewModels } else { - float height = 0f; + //float height = 0f; var regions = textLayout.GetCharacterRegions(0, line.OriginalText.Length); if (regions.Length > 0) { - height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top; + //height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top; + + for (int j = 0; j < regions.Length; j++) + { + var region = regions[j]; + var rect = new Rect( + region.LayoutBounds.X, + region.LayoutBounds.Y + line.Position.Y, + region.LayoutBounds.Width, + region.LayoutBounds.Height + ); + maskDs.FillRectangle(rect, Colors.White); + } } - maskDs.FillRectangle( - new Rect( - textLayout.LayoutBounds.X, - line.Position.Y, - textLayout.LayoutBounds.Width, - height - ), - Colors.White - ); + //maskDs.FillRectangle( + // new Rect( + // textLayout.LayoutBounds.X, + // line.Position.Y, + // textLayout.LayoutBounds.Width, + // height + // ), + // Colors.White + //); } using var opacityEffect = new OpacityEffect @@ -438,7 +450,7 @@ namespace BetterLyrics.WinUI3.ViewModels } } - private void FillBackgroundColor(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, float radius, float opacity) + private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, float radius, float opacity) { ds.FillRoundedRectangle( new Rect(0, 0, _canvasWidth, _canvasHeight), @@ -448,7 +460,17 @@ namespace BetterLyrics.WinUI3.ViewModels ); } - private CanvasLinearGradientBrush GetHorizontalFillBrush( + private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasLinearGradientBrush brush, float radius, float opacity) + { + ds.FillRoundedRectangle( + new Rect(0, 0, _canvasWidth, _canvasHeight), + radius, + radius, + brush + ); + } + + private CanvasLinearGradientBrush CreateHorizontalFillBrush( ICanvasAnimatedControl control, List<(float position, float opacity)> stops, float startX, @@ -465,5 +487,23 @@ namespace BetterLyrics.WinUI3.ViewModels EndPoint = new Vector2(startX + width, 0), }; } + + private CanvasLinearGradientBrush CreateVerticalFillBrush( + ICanvasAnimatedControl control, + List<(float position, Color color)> stops, + float startY, + float height + ) + { + return new CanvasLinearGradientBrush(control, stops.Select(x => new CanvasGradientStop + { + Position = x.position, + Color = x.color, + }).ToArray()) + { + StartPoint = new Vector2(0, startY), + EndPoint = new Vector2(0, startY + height), + }; + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Effects.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Effects.cs similarity index 99% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Effects.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Effects.cs index b700107..04967fe 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Effects.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Effects.cs @@ -7,7 +7,7 @@ using System; using System.Numerics; using Windows.Foundation; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Messages.cs similarity index 75% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Messages.cs index b45c4af..f6a42f3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Messages.cs @@ -6,10 +6,11 @@ using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using System; using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel : IRecipient>, @@ -24,14 +25,15 @@ namespace BetterLyrics.WinUI3.ViewModels IRecipient>, IRecipient>, IRecipient>, - IRecipient>>, + IRecipient>, + IRecipient>>, IRecipient>> { public void Receive(PropertyChangedMessage> message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LocalMediaFolders)) { // Music lib changed, re-fetch lyrics _logger.LogInformation("Local lyrics folders changed, refreshing lyrics."); @@ -43,13 +45,19 @@ namespace BetterLyrics.WinUI3.ViewModels } } - public void Receive(PropertyChangedMessage> message) + public void Receive(PropertyChangedMessage> message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsSearchProvidersInfo)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.MediaSourceProvidersInfo)) { - // Lyrics search providers info changed, re-fetch lyrics + _mediaSourceProvidersInfo = message.NewValue.ToList(); + + UpdateTimelineSyncThreshold(); + UpdatePositionOffset(); + UpdateIsLastFMTrackEnabled(); + + // Media source providers info changed (maybe include lyrics search providers info changed), re-fetch lyrics _logger.LogInformation("Lyrics search providers info changed, refreshing lyrics."); _ = _refreshLyricsRunner.RunAsync(async token => { @@ -61,31 +69,31 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsDynamicCoverOverlayEnabled)) { _isDynamicCoverOverlayEnabled = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsDebugOverlayEnabled)) { _isDebugOverlayEnabled = message.NewValue; _isDebugOverlayEnabledChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsGlowEffectEnabled)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLyricsGlowEffectEnabled)) { _isLyricsGlowEffectEnabled = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.IsFanLyricsEnabled)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsFanLyricsEnabled)) { _isFanLyricsEnabled = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsFloatAnimationEnabled)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLyricsFloatAnimationEnabled)) { _isLyricsFloatAnimationEnabled = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.IsLibreTranslateEnabled)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLibreTranslateEnabled)) { _isLibreTranslateEnabled = message.NewValue; UpdateTranslations(); @@ -145,19 +153,19 @@ namespace BetterLyrics.WinUI3.ViewModels UpdateColorConfig(); } } - else if (message.Sender is SettingsPageViewModel) + else if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomBgFontColor)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomBgFontColor)) { _customBgFontColor = message.NewValue; UpdateColorConfig(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomFgFontColor)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomFgFontColor)) { _customFgFontColor = message.NewValue; UpdateColorConfig(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomStrokeFontColor)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomStrokeFontColor)) { _customStrokeFontColor = message.NewValue; UpdateColorConfig(); @@ -167,9 +175,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsLineSpacingFactor)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsLineSpacingFactor)) { _lyricsLineSpacingFactor = message.NewValue; _isLayoutChanged = true; @@ -179,94 +187,83 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.CoverImageRadius)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverImageRadius)) { _albumArtCornerRadius = message.NewValue; _isAlbumArtCornerRadiusChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayOpacity)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverOverlayOpacity)) { _albumArtBgOpacity = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverOverlayBlurAmount)) { _albumArtBgBlurAmount = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.CoverAcrylicEffectAmount)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverAcrylicEffectAmount)) { _coverAcrylicEffectAmount = message.NewValue; _isCoverAcrylicEffectAmountChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsVerticalEdgeOpacity)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsVerticalEdgeOpacity)) { _lyricsVerticalEdgeOpacity = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBlurAmount)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBlurAmount)) { _lyricsBlurAmount = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStandardFontSize)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStandardFontSize)) { _lyricsStandardFontSize = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDockFontSize)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDockFontSize)) { _lyricsDockFontSize = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDesktopFontSize)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDesktopFontSize)) { _lyricsDesktopFontSize = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.SelectedTargetLanguageIndex)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.SelectedTargetLanguageIndex)) { _targetLanguageIndex = message.NewValue; _logger.LogInformation("Target language index changed: {Index}", _targetLanguageIndex); UpdateTranslations(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontStrokeWidth)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontStrokeWidth)) { _lyricsFontStrokeWidth = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsScrollDuration)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsScrollDuration)) { _canvasYScrollTransition.SetDuration(message.NewValue / 1000f); } - else if (message.PropertyName == nameof(SettingsPageViewModel.TimelineSyncThreshold)) - { - _timelineSyncThreshold = message.NewValue; - } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBgFontOpacity)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBgFontOpacity)) { _defaultOpacity = message.NewValue / 100f; _isLayoutChanged = true; } } - else if (message.Sender is LyricsPageViewModel) - { - if (message.PropertyName == nameof(LyricsPageViewModel.PositionOffset)) - { - _positionOffset = TimeSpan.FromMilliseconds(message.NewValue); - } - } } public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsGlowEffectScope)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsGlowEffectScope)) { _lyricsGlowEffectScope = message.NewValue; } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsHighlightScope)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsHighlightScope)) { _lyricsHighlightScope = message.NewValue; } @@ -275,14 +272,14 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsAlignmentType)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsAlignmentType)) { _lyricsAlignmentType = message.NewValue; _isLayoutChanged = true; } - else if (message.PropertyName == nameof(SettingsPageViewModel.SongInfoAlignmentType)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.SongInfoAlignmentType)) { _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = message.NewValue.ToCanvasHorizontalAlignment(); @@ -297,19 +294,19 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBgFontColorType)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBgFontColorType)) { _lyricsBgFontColorType = message.NewValue; UpdateColorConfig(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFgFontColorType)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFgFontColorType)) { _lyricsFgFontColorType = message.NewValue; UpdateColorConfig(); } - else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStrokeFontColorType)) + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStrokeFontColorType)) { _lyricsStrokeFontColorType = message.NewValue; UpdateColorConfig(); @@ -319,9 +316,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontWeight)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontWeight)) { _lyricsTextFormat.FontWeight = message.NewValue.ToFontWeight(); _isLayoutChanged = true; @@ -331,9 +328,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBackgroundTheme)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBackgroundTheme)) { _lyricsBgTheme = message.NewValue; UpdateColorConfig(); @@ -343,9 +340,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsScrollEasingType)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsScrollEasingType)) { _canvasYScrollTransition.SetEasingType(message.NewValue); } @@ -354,13 +351,29 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontFamily)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontFamily)) { _lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = message.NewValue; _isLayoutChanged = true; } + else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsTranslationSeparator)) + { + _lyricsTranslationSeparator = message.NewValue; + UpdateTranslations(); + } + } + } + + public void Receive(PropertyChangedMessage message) + { + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) + { + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.DockPlacement)) + { + _dockPlacement = message.NewValue; + } } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Transition.cs similarity index 98% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Transition.cs index 88ea245..c5545fe 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Transition.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Transition.cs @@ -8,7 +8,7 @@ using System.Text; using System.Threading.Tasks; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Update.cs similarity index 94% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Update.cs index b8ab560..5262fac 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.Update.cs @@ -7,6 +7,7 @@ using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Imaging; using System; using System.Collections.Generic; using System.Diagnostics; @@ -16,7 +17,7 @@ using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel { @@ -43,6 +44,12 @@ namespace BetterLyrics.WinUI3.ViewModels if (_isPlaying) { TotalTime += _elapsedTime; + _totalPlayingTime += _elapsedTime; + if (_isLastFMTrackEnabled && !_isLastFMTracked && SongInfo?.Duration != null && SongInfo.Duration > 0 && _totalPlayingTime.TotalSeconds >= SongInfo.Duration * 0.5) + { + _isLastFMTracked = true; + _lastFMService.TrackAsync(SongInfo); + } } var playingLineIndex = GetCurrentPlayingLineIndex(); @@ -292,7 +299,7 @@ namespace BetterLyrics.WinUI3.ViewModels line.Position = new Vector2(0, y); line.UpdateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType); line.UpdateCenterPosition(_maxLyricsWidth, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType); - + //line.UpdateTextGeometry(); //line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor); @@ -579,7 +586,7 @@ namespace BetterLyrics.WinUI3.ViewModels { if (_coverAcrylicEffectAmount > 0) { - var ret = NoiseOverlayHelper.GenerateNoiseBitmapBGRA((int)_canvasWidth, (int)_canvasHeight); + var ret = ImageHelper.GenerateNoiseBGRA((int)_canvasWidth, (int)_canvasHeight); _coverAcrylicNoiseCanvasBitmap?.Dispose(); _coverAcrylicNoiseCanvasBitmap = null; _coverAcrylicNoiseCanvasBitmap = CanvasBitmap.CreateFromBytes( @@ -591,5 +598,27 @@ namespace BetterLyrics.WinUI3.ViewModels ); } } + + private MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo() + { + return _mediaSourceProvidersInfo.Where(x => x.Provider == SongInfo?.SourceAppUserModelId)?.FirstOrDefault(); + } + + private void UpdateTimelineSyncThreshold() + { + _timelineSyncThreshold = GetCurrentMediaSourceProviderInfo()?.TimelineSyncThreshold ?? 0; + } + + private void UpdatePositionOffset() + { + var current = GetCurrentMediaSourceProviderInfo(); + _positionOffset = TimeSpan.FromMilliseconds(current?.PositionOffset ?? 0); + } + + private void UpdateIsLastFMTrackEnabled() + { + var current = GetCurrentMediaSourceProviderInfo(); + _isLastFMTrackEnabled = current?.IsLastFMTrackEnabled ?? false; + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.cs similarity index 93% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.cs index ab2c08e..e681cc2 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel/LyricsRendererViewModel.cs @@ -5,7 +5,13 @@ using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.LibWatcherService; +using BetterLyrics.WinUI3.Services.LyricsSearchService; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.TranslateService; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.WinUI; using Microsoft.Extensions.Logging; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; @@ -21,10 +27,14 @@ using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel { public partial class LyricsRendererViewModel : BaseViewModel { + private bool _isLastFMTrackEnabled = false; + private bool _isLastFMTracked = false; + private TimeSpan _totalPlayingTime = TimeSpan.Zero; + private TimeSpan _elapsedTime = TimeSpan.Zero; [ObservableProperty] @@ -100,8 +110,9 @@ namespace BetterLyrics.WinUI3.ViewModels private readonly ILyricsSearchService _lyrcsSearchService; private readonly ILibWatcherService _libWatcherService; - private readonly IPlaybackService _playbackService; + private readonly IMediaSessionsService _mediaSessionsService; private readonly ITranslateService _translateService; + private readonly ILastFMService _lastFMService; private readonly ILogger _logger; private readonly float _leftMargin = 36f; @@ -110,6 +121,8 @@ namespace BetterLyrics.WinUI3.ViewModels private readonly float _topMargin = 36f; private readonly float _bottomMargin = 36f; + private DockPlacement _dockPlacement; + private Color _adaptiveGrayedFontColor = Colors.Transparent; private Color? _adaptiveColoredFontColor = null; @@ -157,8 +170,10 @@ namespace BetterLyrics.WinUI3.ViewModels private bool _showTranslationOnly; private int _targetLanguageIndex; private bool _isLibreTranslateEnabled; + private string _lyricsTranslationSeparator; - private int _timelineSyncThreshold; + private List _mediaSourceProvidersInfo; + private int _timelineSyncThreshold = 0; private CanvasTextFormat _lyricsTextFormat = new() { @@ -357,6 +372,11 @@ namespace BetterLyrics.WinUI3.ViewModels if (Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold) { TotalTime = e.Position; + if (TotalTime.TotalSeconds <= 1) + { + _totalPlayingTime = TimeSpan.Zero; + _isLastFMTracked = false; + } } } @@ -364,6 +384,10 @@ namespace BetterLyrics.WinUI3.ViewModels { SongInfo = e.SongInfo; + UpdateTimelineSyncThreshold(); + UpdatePositionOffset(); + UpdateIsLastFMTrackEnabled(); + if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist) { _lastSongTitle = _songTitle; @@ -383,6 +407,10 @@ namespace BetterLyrics.WinUI3.ViewModels await RefreshLyricsAsync(token); }); TotalTime = TimeSpan.Zero; + + // 处理 Last.fm 追踪 + _totalPlayingTime = TimeSpan.Zero; + _isLastFMTracked = false; } } @@ -459,7 +487,7 @@ namespace BetterLyrics.WinUI3.ViewModels } else { - _lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], 50); + _lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _lyricsTranslationSeparator, 50); _langIndex = 0; } TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider(); @@ -480,7 +508,7 @@ namespace BetterLyrics.WinUI3.ViewModels } else { - _lyricsDataArr[0].SetDisplayedTextAlongWith(translated); + _lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _lyricsTranslationSeparator); _langIndex = 0; } TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate; @@ -508,6 +536,7 @@ namespace BetterLyrics.WinUI3.ViewModels if (SongInfo != null) { (lyricsRaw, lyricsSearchProvider) = await _lyrcsSearchService.SearchAsync( + SongInfo.SourceAppUserModelId ?? "", SongInfo.Title, SongInfo.Artist, SongInfo.Album ?? "", diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs index 484970a..3ab5078 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsWindowViewModel.cs @@ -3,8 +3,11 @@ using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.ViewModels; +using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel; +using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.DependencyInjection; @@ -17,6 +20,7 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using System; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Vanara.PInvoke; using Windows.System; @@ -34,7 +38,7 @@ namespace BetterLyrics.WinUI3 IRecipient>, IRecipient> { - private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService(); + private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService(); private ForegroundWindowWatcher? _windowWatcher = null; private bool _ignoreFullscreenWindow; private bool _hideWindowWhenNotPlaying; @@ -53,7 +57,7 @@ namespace BetterLyrics.WinUI3 _dockWindowHeight = _settingsService.DockWindowHeight; OnIsImmersiveModeChanged(_settingsService.IsImmersiveMode); - _playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged; + _mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged; } private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e) @@ -106,7 +110,7 @@ namespace BetterLyrics.WinUI3 if (IsDockMode || IsDesktopMode) { - if (_hideWindowWhenNotPlaying && !_playbackService.IsPlaying) + if (_hideWindowWhenNotPlaying && !_mediaSessionsService.IsPlaying) { if (IsDockMode) { @@ -222,16 +226,16 @@ namespace BetterLyrics.WinUI3 var hwnd = WindowNative.GetWindowHandle(window); _windowWatcher = new ForegroundWindowWatcher( hwnd, - onWindowChanged => + fgHwnd => { _dispatcherQueueTimer.Debounce(() => { - if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter) + if ((IsDockMode || IsDesktopMode) && _ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter) { presenter.IsAlwaysOnTop = true; } UpdateAccentColor(hwnd); - }, TimeSpan.FromMilliseconds(300)); + }, Constants.Time.DebounceTimeout); } ); _windowWatcher.Start(); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs index 1676642..332352d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs @@ -3,6 +3,8 @@ using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.LibWatcherService; +using BetterLyrics.WinUI3.Services.SettingsService; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; @@ -408,9 +410,9 @@ namespace BetterLyrics.WinUI3.ViewModels public void Receive(PropertyChangedMessage> message) { - if (message.Sender is SettingsPageViewModel) + if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders)) + if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LocalMediaFolders)) { RefreshSongs(); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Ctor.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Ctor.cs similarity index 65% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Ctor.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Ctor.cs index 8216dfc..9ff29f8 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Ctor.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Ctor.cs @@ -1,27 +1,43 @@ using BetterLyrics.WinUI3.Helper; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.LibWatcherService; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.SettingsService; +using BetterLyrics.WinUI3.Services.TranslateService; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel { public partial class SettingsPageViewModel { - public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ITranslateService libreTranslateService) : base(settingsService) + public SettingsPageViewModel( + ISettingsService settingsService, + ILibWatcherService libWatcherService, + IMediaSessionsService mediaSessionsService, + ITranslateService libreTranslateService, + ILastFMService lastFMService) : base(settingsService) { _libWatcherService = libWatcherService; - _playbackService = playbackService; + _mediaSessionsService = mediaSessionsService; _libreTranslateService = libreTranslateService; + // LastFM + _lastFMService = lastFMService; + _lastFMService.UserChanged += LastFMService_UserChanged; + _lastFMService.IsAuthenticatedChanged += LastFMService_IsAuthenticatedChanged; + IsLastFMAuthenticated = _lastFMService.IsAuthenticated; + LastFMUser = _lastFMService.User; + IsLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled; LibreTranslateServer = _settingsService.LibreTranslateServer; SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex; LocalMediaFolders = [.. _settingsService.LocalMediaFolders]; - LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo]; AlbumArtSearchProvidersInfo = [.. _settingsService.AlbumArtSearchProvidersInfo]; Language = _settingsService.Language; @@ -64,14 +80,14 @@ namespace BetterLyrics.WinUI3.ViewModels LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth; LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme; MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo]; + SelectedMediaSourceProvider = MediaSourceProvidersInfo.FirstOrDefault(); + IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow; LyricsScrollEasingType = _settingsService.LyricsScrollEasingType; LyricsScrollDuration = _settingsService.LyricsScrollDuration; - TimelineSyncThreshold = _settingsService.TimelineSyncThreshold; IsLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled; - ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged; LockHotKeyIndex = _settingsService.LockHotKeyIndex; LXMusicServer = _settingsService.LXMusicServer; @@ -88,7 +104,29 @@ namespace BetterLyrics.WinUI3.ViewModels MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()]; SelectedDockMonitorDeviceName = _settingsService.DockMonitorDeviceName; - _playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged; + LyricsTranslationSeparator = _settingsService.LyricsTranslationSeparator; + + _mediaSessionsService.MediaSourceProvidersInfoChanged += MediaSessionsService_SessionIdsChanged; + _mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged; + } + + private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e) + { + var current = MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.SourceAppUserModelId)?.FirstOrDefault(); + if (_mediaSessionsService.Position.TotalSeconds <= 1 && current?.ResetPositionOffsetOnSongChanged == true) + { + current.PositionOffset = 0; + } + } + + private void LastFMService_IsAuthenticatedChanged(object? sender, Events.LastFMIsAuthenticatedChangedEventArgs e) + { + IsLastFMAuthenticated = e.IsAuthenticated; + } + + private void LastFMService_UserChanged(object? sender, Events.LastFMUserChangedEventArgs e) + { + LastFMUser = e.User; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.OnPropsChanged.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.OnPropsChanged.cs similarity index 96% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.OnPropsChanged.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.OnPropsChanged.cs index 557fc63..d8399aa 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.OnPropsChanged.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.OnPropsChanged.cs @@ -5,7 +5,7 @@ using Microsoft.UI.Xaml; using Windows.Globalization; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel { public partial class SettingsPageViewModel { @@ -176,18 +176,10 @@ namespace BetterLyrics.WinUI3.ViewModels { _settingsService.LyricsVerticalEdgeOpacity = value; } - partial void OnTimelineSyncThresholdChanged(int value) - { - _settingsService.TimelineSyncThreshold = value; - } partial void OnIsLyricsFloatAnimationEnabledChanged(bool value) { _settingsService.IsLyricsFloatAnimationEnabled = value; } - partial void OnResetPositionOffsetOnSongChangedChanged(bool value) - { - _settingsService.ResetPositionOffsetOnSongChanged = value; - } partial void OnLyricsBgFontOpacityChanged(int value) { _settingsService.LyricsBgFontOpacity = value; @@ -227,5 +219,9 @@ namespace BetterLyrics.WinUI3.ViewModels { _settingsService.DockMonitorDeviceName = value; } + partial void OnLyricsTranslationSeparatorChanged(string value) + { + _settingsService.LyricsTranslationSeparator = value; + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Props.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Props.cs similarity index 94% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Props.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Props.cs index 4ffc74f..e64feb4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.Props.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.Props.cs @@ -2,6 +2,7 @@ using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using CommunityToolkit.Mvvm.ComponentModel; +using Hqub.Lastfm.Entities; using Microsoft.UI.Xaml; using System; using System.Collections.Generic; @@ -11,7 +12,7 @@ using System.Text; using System.Threading.Tasks; using Windows.UI; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel { public partial class SettingsPageViewModel { @@ -87,10 +88,6 @@ namespace BetterLyrics.WinUI3.ViewModels [ObservableProperty] public partial ObservableCollection LocalMediaFolders { get; set; } - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial ObservableCollection LyricsSearchProvidersInfo { get; set; } - [ObservableProperty] [NotifyPropertyChangedRecipients] public partial ObservableCollection AlbumArtSearchProvidersInfo { get; set; } @@ -98,6 +95,9 @@ namespace BetterLyrics.WinUI3.ViewModels [ObservableProperty] public partial ObservableCollection MediaSourceProvidersInfo { get; set; } + [ObservableProperty] + public partial MediaSourceProviderInfo? SelectedMediaSourceProvider { get; set; } + [ObservableProperty] [NotifyPropertyChangedRecipients] public partial bool IsFanLyricsEnabled { get; set; } @@ -181,10 +181,6 @@ namespace BetterLyrics.WinUI3.ViewModels [ObservableProperty] public partial object NavViewSelectedItemTag { get; set; } = "App"; - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial bool ResetPositionOffsetOnSongChanged { get; set; } - [ObservableProperty] [NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } @@ -215,10 +211,6 @@ namespace BetterLyrics.WinUI3.ViewModels [NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; } - [ObservableProperty] - [NotifyPropertyChangedRecipients] - public partial int TimelineSyncThreshold { get; set; } - [ObservableProperty] public partial bool IsLXMusicServerTesting { get; set; } = false; @@ -234,11 +226,23 @@ namespace BetterLyrics.WinUI3.ViewModels [NotifyPropertyChangedRecipients] public partial int DockWindowHeight { get; set; } + // Dock Monitor [ObservableProperty] [NotifyPropertyChangedRecipients] public partial string SelectedDockMonitorDeviceName { get; set; } [ObservableProperty] public partial ObservableCollection MonitorDeviceNames { get; set; } + + // LastFM + [ObservableProperty] + public partial bool IsLastFMAuthenticated { get; set; } + + [ObservableProperty] + public partial User? LastFMUser { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial string LyricsTranslationSeparator { get; set; } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.cs similarity index 84% rename from BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs rename to BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.cs index 4ff714a..7b8dbb6 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel/SettingsPageViewModel.cs @@ -4,45 +4,45 @@ using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.LastFMService; +using BetterLyrics.WinUI3.Services.LibWatcherService; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.TranslateService; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; -using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel; -using Windows.Globalization; -using Windows.UI; using WinRT.Interop; -using MetadataHelper = BetterLyrics.WinUI3.Helper.MetadataHelper; -namespace BetterLyrics.WinUI3.ViewModels +namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel { public partial class SettingsPageViewModel : BaseViewModel { private readonly ILibWatcherService _libWatcherService; - private readonly IPlaybackService _playbackService; + private readonly IMediaSessionsService _mediaSessionsService; private readonly ITranslateService _libreTranslateService; + private readonly ILastFMService _lastFMService; - private readonly string _autoStartupTaskId = "AutoStartup"; - - private void PlaybackService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e) + private void MediaSessionsService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e) { MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo]; } public void OnLyricsSearchProvidersReordered() { - _settingsService.LyricsSearchProvidersInfo = [.. LyricsSearchProvidersInfo]; + _settingsService.MediaSourceProvidersInfo = [.. MediaSourceProvidersInfo]; Broadcast( - LyricsSearchProvidersInfo, - LyricsSearchProvidersInfo, - nameof(LyricsSearchProvidersInfo) + MediaSourceProvidersInfo, + MediaSourceProvidersInfo, + nameof(MediaSourceProvidersInfo) ); } @@ -70,16 +70,6 @@ namespace BetterLyrics.WinUI3.ViewModels Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders)); } - public void ToggleLyricsSearchProvider() - { - _settingsService.LyricsSearchProvidersInfo = [.. LyricsSearchProvidersInfo]; - Broadcast( - LyricsSearchProvidersInfo, - LyricsSearchProvidersInfo, - nameof(LyricsSearchProvidersInfo) - ); - } - public void ToggleAlbumArtSearchProvider(AlbumArtSearchProviderInfo providerInfo) { _settingsService.AlbumArtSearchProvidersInfo = [.. AlbumArtSearchProvidersInfo]; @@ -90,13 +80,17 @@ namespace BetterLyrics.WinUI3.ViewModels ); } - public void ToggleMediaSourceProvider(MediaSourceProviderInfo providerInfo) + public void BroadcastMediaSourceProvidersInfoChanged() { - Broadcast( - MediaSourceProvidersInfo, - MediaSourceProvidersInfo, - nameof(MediaSourceProvidersInfo) - ); + _dispatcherQueueTimer.Debounce(() => + { + _settingsService.MediaSourceProvidersInfo = [.. MediaSourceProvidersInfo]; + Broadcast( + MediaSourceProvidersInfo, + MediaSourceProvidersInfo, + nameof(MediaSourceProvidersInfo) + ); + }, TimeSpan.FromMilliseconds(100)); } private void AddFolderAsync(string path) @@ -130,7 +124,7 @@ namespace BetterLyrics.WinUI3.ViewModels [RelayCommand] private async Task LaunchProjectGitHubPageAsync() { - await Windows.System.Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl)); + await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GithubUrl)); } [RelayCommand] @@ -223,9 +217,27 @@ namespace BetterLyrics.WinUI3.ViewModels SelectedDockMonitorDeviceName = MonitorHelper.GetPrimaryMonitorDeviceName(); } + [RelayCommand] + private async Task LastFMAuthAsync() + { + await _lastFMService.AuthAsync(); + } + + [RelayCommand] + private async Task LastFMUnAuthAsync() + { + await _lastFMService.UnAuthAsync(); + } + + [RelayCommand] + private async Task LastFMRefreshAsync() + { + await _lastFMService.RefreshAsync(); + } + public async Task ToggleAutoStartupAsync(bool target) { - StartupTask startupTask = await StartupTask.GetAsync(_autoStartupTaskId); + StartupTask startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId); if (target) { await startupTask.RequestEnableAsync(); @@ -240,7 +252,7 @@ namespace BetterLyrics.WinUI3.ViewModels public async Task DetectIsAutoStartupEnabledAsync() { bool result = false; - var startupTask = await StartupTask.GetAsync(_autoStartupTaskId); + var startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId); switch (startupTask.State) { case StartupTaskState.Disabled: diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsWindowViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsWindowViewModel.cs index d79b4d9..d19b0bf 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsWindowViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsWindowViewModel.cs @@ -1,4 +1,4 @@ -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; namespace BetterLyrics.WinUI3.ViewModels { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs index ee129f6..89512e3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs @@ -1,5 +1,5 @@ using BetterLyrics.WinUI3.Helper; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -18,7 +18,7 @@ namespace BetterLyrics.WinUI3.ViewModels public partial bool IsLyricsWindowLocked { get; set; } = false; [ObservableProperty] - public partial string ToolTipText { get; set; } = MetadataHelper.AppName; + public partial string ToolTipText { get; set; } = Constants.App.AppName; public void Receive(PropertyChangedMessage message) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml index b9d2880..3181b32 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml @@ -79,52 +79,6 @@ - - - diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs index b7e1fa6..86ce8a3 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs @@ -1,7 +1,8 @@ // 2025/6/23 by Zhe Fang using BetterLyrics.WinUI3.Enums; -using BetterLyrics.WinUI3.Services; +using BetterLyrics.WinUI3.Services.MediaSessionsService; +using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.ViewModels; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.UI.Xaml; @@ -16,7 +17,7 @@ namespace BetterLyrics.WinUI3.Views public sealed partial class LyricsPage : Page { private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService(); - private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService(); + private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService(); public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext; @@ -50,11 +51,6 @@ namespace BetterLyrics.WinUI3.Views _settingsService.DisplayType = ViewModel.DisplayType; } - private void PositionOffsetResetButton_Click(object sender, RoutedEventArgs e) - { - ViewModel.PositionOffset = 0; - } - private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { if (ViewModel.IsImmersiveMode && BottomCommandGrid.Children.Count != 0) @@ -78,11 +74,6 @@ namespace BetterLyrics.WinUI3.Views DisplayTypeSwitchFlyout.ShowAt(BottomRightCommandStackPanel); } - private void TimelineOffsetButton_Click(object sender, RoutedEventArgs e) - { - TimelineOffsetFlyout.ShowAt(BottomLeftCommandStackPanel); - } - private void TranslationButton_Click(object sender, RoutedEventArgs e) { TranslationFlyout.ShowAt(BottomRightCommandStackPanel); @@ -142,7 +133,7 @@ namespace BetterLyrics.WinUI3.Views private void TimelineSliderOverlay_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { - _playbackService.ChangePosition(TimelineSlider.Value); + _mediaSessionsService.ChangePosition(TimelineSlider.Value); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml index 95b8de4..98d8fee 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml @@ -123,6 +123,9 @@ Command="{x:Bind ViewModel.ImmersiveToggleButtonEnabledChangedCommand}" IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}" Style="{StaticResource TitleBarToggleButtonStyle}"> + + + +