Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b043f9acd0 | ||
|
|
7b2ff0cc8f | ||
|
|
7c9ab73a34 | ||
|
|
a08bf91784 | ||
|
|
61906670fd | ||
|
|
430b2f4d28 | ||
|
|
eb05c1ea13 | ||
|
|
9bebf36e6a | ||
|
|
d2b0b6afb1 | ||
|
|
9c2f4fbff9 |
1
.gitignore
vendored
@@ -406,3 +406,4 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/BetterLyrics.WinUI3 (Package)_TemporaryKey.pfx
|
||||
/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LastFM.cs
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.40.0" />
|
||||
Version="1.0.45.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
<converter:TranslationSearchProviderToDisplayNameConverter x:Key="TranslationSearchProviderToDisplayNameConverter" />
|
||||
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
|
||||
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
|
||||
<converter:MediaSourceProviderToLogoUriConverter x:Key="MediaSourceProviderToLogoUriConverter" />
|
||||
<converter:MediaSourceProviderToDisplayedNameConverter x:Key="MediaSourceProviderToDisplayedNameConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
|
||||
|
||||
@@ -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<ISettingsService, SettingsService>()
|
||||
.AddSingleton<IPlaybackService, PlaybackService>()
|
||||
.AddSingleton<IMediaSessionsService, MediaSessionsService>()
|
||||
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
|
||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ITranslateService, TranslateService>()
|
||||
.AddSingleton<ILastFMService, LastFMService>()
|
||||
// ViewModels
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
|
||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AIMP.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AppleMusic.png
Normal file
|
After Width: | Height: | Size: 836 B |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Chrome.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Edge.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/KugouMusic.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LXMusic.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/LastFM.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Leaf.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 260 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/MusicBee.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/PotPlayer.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/QQMusic.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Question.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Spotify.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/foobar2000.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/iTunes.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -53,8 +53,8 @@
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
@@ -65,8 +65,8 @@
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.7" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.8" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="TinyPinyin.Net" Version="1.0.2" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
@@ -78,6 +78,11 @@
|
||||
<PackageReference Include="WinUIEx" Version="2.6.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Hqub.Lastfm">
|
||||
<HintPath>..\..\..\Last.fm\src\Hqub.Lastfm\bin\Release\netstandard2.0\Hqub.Lastfm.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\InAppLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -93,27 +98,78 @@
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\AIMP.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\AppleMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Chrome.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Discord.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Edge.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EmptyBox.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EmptyState.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\foobar2000.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\iTunes.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\KugouMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\LastFM.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Leaf.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\LXMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\MediaPlayerWindows11.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\MusicBee.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\NetEaseCloudMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\PotPlayer.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\QQ.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\QQMusic.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Question.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Segoe Fluent Icons.ttf">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Spotify.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Telegram.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
16
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/App.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
13
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/LXMusic.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
16
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Link.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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)";
|
||||
}
|
||||
}
|
||||
13
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/Time.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
13
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Constants/iTunes.cs
Normal file
@@ -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?";
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using System;
|
||||
|
||||
@@ -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,37 +169,55 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
var themeColor = Rgba32.ParseHex(GetAccentColorsFromByte(imageBytes, 1).FirstOrDefault().ToHex());
|
||||
|
||||
// 新建正方形画布
|
||||
using var square = new Image<Rgba32>(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(size / image.Width, size / image.Height);
|
||||
if (factor <= 1)
|
||||
using (Image image = Image.Load(imageBytes))
|
||||
{
|
||||
return imageBytes;
|
||||
}
|
||||
int width = image.Width * factor;
|
||||
int height = image.Height * factor;
|
||||
image.Mutate(x => x.Resize(width, height, KnownResamplers.Welch));
|
||||
var factor = Math.Max((float)size / image.Width, (float)size / image.Height);
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
image.Save(ms, new PngEncoder());
|
||||
return ms.ToArray();
|
||||
int width = (int)(image.Width * factor);
|
||||
int height = (int)(image.Height * factor);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using NTextCat;
|
||||
|
||||
@@ -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<DateTime> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// 生成 BGRA 格式的灰阶噪声像素数据
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成单色灰阶随机噪声
|
||||
/// </summary>
|
||||
/// <param name="outputPath">输出文件路径</param>
|
||||
/// <param name="width">图片宽度</param>
|
||||
/// <param name="height">图片高度</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取噪声图片的位头信息
|
||||
/// </summary>
|
||||
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<BitmapFileCreator.BITMAPFILEHEADER>(), SeekOrigin.Begin);
|
||||
|
||||
// 读取信息头
|
||||
byte[] infoHeaderBytes = br.ReadBytes(Marshal.SizeOf<BitmapFileCreator.WINBMPINFOHEADER>());
|
||||
return MemoryMarshal.Read<BitmapFileCreator.WINBMPINFOHEADER>(infoHeaderBytes);
|
||||
}
|
||||
|
||||
public static BitmapFileCreator.WINBMPINFOHEADER ReadBitmapInfoHeaders(byte[] FileBytes)
|
||||
{
|
||||
// 跳过文件头
|
||||
var offset = Marshal.SizeOf<BitmapFileCreator.BITMAPFILEHEADER>();
|
||||
|
||||
// 读取信息头
|
||||
var infoHeaderLength = Marshal.SizeOf<BitmapFileCreator.WINBMPINFOHEADER>();
|
||||
Span<byte> infoHeaderBytes = new(FileBytes, offset, infoHeaderLength);
|
||||
return MemoryMarshal.Read<BitmapFileCreator.WINBMPINFOHEADER>(infoHeaderBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// safe 的写入 struct 到流
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值 strcut</typeparam>
|
||||
/// <param name="stream">要写入的字节流</param>
|
||||
/// <param name="structure">要被写入的结构</param>
|
||||
public static void WriteStruct<T>(Stream stream, in T structure) where T : struct
|
||||
{
|
||||
int size = Unsafe.SizeOf<T>();
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
MemoryMarshal.Write(buffer, in structure);
|
||||
stream.Write(buffer, 0, size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 随机填充位图内容
|
||||
/// </summary>
|
||||
/// <param name="width">填充宽度</param>
|
||||
/// <param name="height">填充高度</param>
|
||||
/// <param name="bitCount">单个调色盘索引所占比特位数</param>
|
||||
/// <returns>字节数据</returns>
|
||||
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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 创建BMP文件头
|
||||
/// </summary>
|
||||
/// <param name="palette">调色盘数据</param>
|
||||
/// <param name="pixelData">像素数据</param>
|
||||
/// <returns>文件头结构</returns>
|
||||
public static BITMAPFILEHEADER CreateFileHeader(byte[] palette, byte[] pixelData)
|
||||
{
|
||||
return new BITMAPFILEHEADER
|
||||
{
|
||||
bfType = 0x4D42,
|
||||
bfSize = (uint)(Marshal.SizeOf<BITMAPFILEHEADER>() +
|
||||
Marshal.SizeOf<WINBMPINFOHEADER>() +
|
||||
palette.Length +
|
||||
pixelData.Length),
|
||||
bfReserved1 = 0,
|
||||
bfReserved2 = 0,
|
||||
bfOffBits = (uint)(Marshal.SizeOf<BITMAPFILEHEADER>() +
|
||||
Marshal.SizeOf<WINBMPINFOHEADER>() +
|
||||
palette.Length)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值填充到为最接近的2的幂数
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 生成灰阶调色盘
|
||||
/// </summary>
|
||||
/// <param name="colors">灰阶数量</param>
|
||||
/// <returns>调色盘byte数组</returns>
|
||||
public static byte[] CreateGrayscalePalette(int colors)
|
||||
{
|
||||
return Enumerable.Range(0, colors)
|
||||
.SelectMany(i => Enumerable.Repeat((byte)(i * 0x10), 4))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建BMP信息头
|
||||
/// </summary>
|
||||
/// <param name="width">宽度</param>
|
||||
/// <param name="height">高度</param>
|
||||
/// <param name="bitCount">单个像素(调色盘索引)位数</param>
|
||||
/// <returns>BMP信息头结构</returns>
|
||||
public static WINBMPINFOHEADER CreateInfoHeader(int width, int height, uint bitCount)
|
||||
{
|
||||
return new WINBMPINFOHEADER
|
||||
{
|
||||
biSize = (uint)Marshal.SizeOf<WINBMPINFOHEADER>(),
|
||||
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)]
|
||||
/// <summary>
|
||||
/// BMP 位图文件头
|
||||
/// </summary>
|
||||
public struct BITMAPFILEHEADER
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件类型标识,用于指定文件格式
|
||||
/// </summary>
|
||||
public ushort bfType;
|
||||
/// <summary>
|
||||
/// 文件大小,以字节为单位
|
||||
/// </summary>
|
||||
public uint bfSize;
|
||||
/// <summary>
|
||||
/// 保留字段,未使用
|
||||
/// </summary>
|
||||
public ushort bfReserved1;
|
||||
/// <summary>
|
||||
/// 保留字段,未使用
|
||||
/// </summary>
|
||||
public ushort bfReserved2;
|
||||
/// <summary>
|
||||
/// 像素数据的起始位置,以字节为单位,从文件头开始计算
|
||||
/// </summary>
|
||||
public uint bfOffBits;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
/// <summary>
|
||||
/// BMP 位图信息头
|
||||
/// </summary>
|
||||
public struct WINBMPINFOHEADER
|
||||
{
|
||||
/// <summary>
|
||||
/// 指定此结构体的字节大小。
|
||||
/// </summary>
|
||||
public uint biSize;
|
||||
|
||||
/// <summary>
|
||||
/// 以像素为单位的位图宽度。
|
||||
/// </summary>
|
||||
public uint biWidth;
|
||||
|
||||
/// <summary>
|
||||
/// 以像素为单位的位图高度。
|
||||
/// </summary>
|
||||
public uint biHeight;
|
||||
|
||||
/// <summary>
|
||||
/// 目标设备的平面数,通常为1。
|
||||
/// </summary>
|
||||
public ushort biPlanes;
|
||||
|
||||
/// <summary>
|
||||
/// 每个像素的位数,表示颜色深度,如1、4、8、16、24、32等。
|
||||
/// </summary>
|
||||
public ushort biBitCount;
|
||||
|
||||
/// <summary>
|
||||
/// 压缩类型,0表示不压缩。
|
||||
/// </summary>
|
||||
public uint biCompression;
|
||||
|
||||
/// <summary>
|
||||
/// 位图图像数据的大小(以字节为单位),若图像未压缩,该值可设为0。
|
||||
/// </summary>
|
||||
public uint biSizeImage;
|
||||
|
||||
/// <summary>
|
||||
/// 每米X轴方向的像素数(水平分辨率),通常设为0。
|
||||
/// </summary>
|
||||
public uint biXPelsPerMeter;
|
||||
|
||||
/// <summary>
|
||||
/// 每米Y轴方向的像素数(垂直分辨率),通常设为0。
|
||||
/// </summary>
|
||||
public uint biYPelsPerMeter;
|
||||
|
||||
/// <summary>
|
||||
/// 实际使用的颜色索引数,若为0,则使用位图中实际出现的颜色数。
|
||||
/// </summary>
|
||||
public uint biClrUsed;
|
||||
|
||||
/// <summary>
|
||||
/// 重要颜色索引数,0表示所有颜色都重要。
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 转换为byte[]
|
||||
/// </summary>
|
||||
/// <param name="bf"></param>
|
||||
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<byte[]> ToByteArrayAsync() { return Task.FromResult(ToByteArray());}
|
||||
|
||||
/// <summary>
|
||||
/// 写入此结构到流中
|
||||
/// </summary>
|
||||
public void WriteToStream(Stream stream)
|
||||
{
|
||||
NoiseOverlayHelper.WriteStruct(stream, FileHeader);
|
||||
NoiseOverlayHelper.WriteStruct(stream, InfoHeader);
|
||||
stream.Write(Palette);
|
||||
stream.Write(PixelData);
|
||||
stream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
if (!_isTransitioning) return;
|
||||
|
||||
_progress += (float)(elapsedTime.TotalSeconds / _durationSeconds);
|
||||
_progress += (float)(elapsedTime / TimeSpan.FromSeconds(_durationSeconds));
|
||||
if (_progress >= 1f)
|
||||
{
|
||||
_progress = 1f;
|
||||
|
||||
@@ -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<string> 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++;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,31 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public class LyricsLine
|
||||
{
|
||||
private const float _animationDuration = 0.3f;
|
||||
public ValueTransition<float> AngleTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> BlurAmountTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> HighlightOpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> OpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> ScaleTransition { get; set; } = new(initialValue: 0.95f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> AngleTransition { get; set; } = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
public ValueTransition<float> BlurAmountTransition { get; set; } = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
public ValueTransition<float> HighlightOpacityTransition { get; set; } = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
public ValueTransition<float> OpacityTransition { get; set; } = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
public ValueTransition<float> ScaleTransition { get; set; } = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
|
||||
public CanvasTextLayout? CanvasTextLayout { get; private set; }
|
||||
|
||||
@@ -64,10 +84,15 @@ namespace BetterLyrics.WinUI3.Models
|
||||
CanvasTextLayout.HorizontalAlignment = type.ToCanvasHorizontalAlignment();
|
||||
}
|
||||
|
||||
public void UpdateTextGeometry()
|
||||
public void DisposeTextGeometry()
|
||||
{
|
||||
TextGeometry?.Dispose();
|
||||
TextGeometry = null;
|
||||
}
|
||||
|
||||
public void UpdateTextGeometry()
|
||||
{
|
||||
DisposeTextGeometry();
|
||||
if (CanvasTextLayout == null)
|
||||
{
|
||||
return;
|
||||
@@ -75,12 +100,17 @@ namespace BetterLyrics.WinUI3.Models
|
||||
TextGeometry = CanvasGeometry.CreateText(CanvasTextLayout);
|
||||
}
|
||||
|
||||
public void UpdateFontEffect(ICanvasAnimatedControl control, bool drawStroke, Color strokeColor, int strokeWidth, Color fontColor)
|
||||
public void DisposeFontEffects()
|
||||
{
|
||||
BackgroundFontEffect?.Dispose();
|
||||
BackgroundFontEffect = null;
|
||||
ForegroundFontEffect?.Dispose();
|
||||
ForegroundFontEffect = null;
|
||||
}
|
||||
|
||||
public void UpdateFontEffect(ICanvasAnimatedControl control, bool drawStroke, Color strokeColor, int strokeWidth, Color fontColor)
|
||||
{
|
||||
DisposeFontEffects();
|
||||
if (TextGeometry == null)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -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<LyricsSearchProviderInfo> 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<LyricsSearchProvider>().Select(p => new LyricsSearchProviderInfo(p, true))];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
{
|
||||
@@ -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<LastFMUserChangedEventArgs>? UserChanged;
|
||||
event EventHandler<LastFMIsAuthenticatedChangedEventArgs>? IsAuthenticatedChanged;
|
||||
|
||||
Task AuthAsync();
|
||||
Task ConfirmAuth();
|
||||
Task UnAuthAsync();
|
||||
Task ConfirmUnAuthAsync();
|
||||
Task TrackAsync(SongInfo songInfo);
|
||||
Task RefreshAsync();
|
||||
}
|
||||
}
|
||||
@@ -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<LastFMUserChangedEventArgs>? UserChanged;
|
||||
public event EventHandler<LastFMIsAuthenticatedChangedEventArgs>? 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<SettingsWindow>()?.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<SettingsWindow>()?.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -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
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<bool> 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);
|
||||
@@ -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<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
@@ -23,5 +23,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
bool IsPlaying { get; }
|
||||
SongInfo? SongInfo { get; }
|
||||
TimeSpan Position { get; }
|
||||
}
|
||||
}
|
||||
@@ -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<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>>>
|
||||
{
|
||||
private readonly IAlbumArtSearchService _albumArtSearchService;
|
||||
private readonly ILogger<PlaybackService> _logger;
|
||||
private readonly ILogger<MediaSessionsService> _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;
|
||||
|
||||
@@ -54,6 +55,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private SongInfo? _cachedSongInfo;
|
||||
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
|
||||
private byte[]? _SMTCAlbumArtBytes = null;
|
||||
private int _targetAlbumArtSize = 400;
|
||||
|
||||
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
public event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
@@ -61,10 +63,10 @@ namespace BetterLyrics.WinUI3.Services
|
||||
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
public PlaybackService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService)
|
||||
public MediaSessionsService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService)
|
||||
{
|
||||
_albumArtSearchService = albumArtSearchService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<PlaybackService>>();
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<MediaSessionsService>>();
|
||||
|
||||
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo;
|
||||
InitMediaManager();
|
||||
@@ -72,6 +74,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
public bool IsPlaying => _cachedIsPlaying;
|
||||
public SongInfo? SongInfo => _cachedSongInfo;
|
||||
public TimeSpan Position => _cachedPosition;
|
||||
|
||||
private bool IsMediaSourceEnabled(string id)
|
||||
{
|
||||
@@ -108,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)
|
||||
@@ -124,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, () =>
|
||||
{
|
||||
@@ -148,56 +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);
|
||||
_SMTCAlbumArtBytes = ImageHelper.Resize(_SMTCAlbumArtBytes, 800);
|
||||
}
|
||||
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)
|
||||
@@ -281,10 +337,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync(400, 400);
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync(_targetAlbumArtSize, _targetAlbumArtSize);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
bytes = ImageHelper.Resize(bytes, _targetAlbumArtSize);
|
||||
bytes = ImageHelper.MakeSquareWithThemeColor(bytes);
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
@@ -302,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));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -310,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;
|
||||
}
|
||||
@@ -347,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)
|
||||
@@ -420,7 +477,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.MediaSourceProvidersInfo))
|
||||
{
|
||||
_mediaSourceProvidersInfo = [.. message.NewValue];
|
||||
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
}
|
||||
}
|
||||
@@ -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<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
List<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
|
||||
List<MediaSourceProviderInfo> 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; }
|
||||
}
|
||||
}
|
||||
@@ -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<LyricsSearchProvider>()
|
||||
.Select(p => new LyricsSearchProviderInfo(p, true))
|
||||
.ToList(),
|
||||
SourceGenerationContext.Default.ListLyricsSearchProviderInfo
|
||||
)
|
||||
);
|
||||
if (LyricsSearchProvidersInfo.Count != Enum.GetValues<LyricsSearchProvider>().Length)
|
||||
{
|
||||
LyricsSearchProvidersInfo = Enum.GetValues<LyricsSearchProvider>()
|
||||
.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<LyricsSearchProvider>().Length)
|
||||
{
|
||||
mediaSource.LyricsSearchProvidersInfo = [..Enum.GetValues<LyricsSearchProvider>()
|
||||
.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<string>(LocalLyricsFoldersKey) ?? "[]",
|
||||
GetValue<string>(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<LyricsSearchProviderInfo> LyricsSearchProvidersInfo
|
||||
{
|
||||
get =>
|
||||
System.Text.Json.JsonSerializer.Deserialize(
|
||||
GetValue<string>(LyricsSearchProvidersInfoKey) ?? "[]",
|
||||
SourceGenerationContext.Default.ListLyricsSearchProviderInfo
|
||||
)!;
|
||||
set =>
|
||||
SetValue(
|
||||
LyricsSearchProvidersInfoKey,
|
||||
System.Text.Json.JsonSerializer.Serialize(
|
||||
value,
|
||||
SourceGenerationContext.Default.ListLyricsSearchProviderInfo
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public List<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo
|
||||
{
|
||||
get =>
|
||||
@@ -656,24 +638,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(IgnoreFullscreenWindowKey, value);
|
||||
}
|
||||
|
||||
public int TimelineSyncThreshold
|
||||
{
|
||||
get => GetValue<int>(TimelineSyncThresholdKey);
|
||||
set => SetValue(TimelineSyncThresholdKey, value);
|
||||
}
|
||||
|
||||
public bool IsLyricsFloatAnimationEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsLyricsFloatAnimationEnabledKey);
|
||||
set => SetValue(IsLyricsFloatAnimationEnabledKey, value);
|
||||
}
|
||||
|
||||
public bool ResetPositionOffsetOnSongChanged
|
||||
{
|
||||
get => GetValue<bool>(ResetPositionOffsetOnSongChangedKey);
|
||||
set => SetValue(ResetPositionOffsetOnSongChangedKey, value);
|
||||
}
|
||||
|
||||
public PlaybackOrder PlaybackOrder
|
||||
{
|
||||
get => (PlaybackOrder)GetValue<int>(PlaybackOrderKey);
|
||||
@@ -698,6 +668,22 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(DockMonitorDeviceNameKey, value);
|
||||
}
|
||||
|
||||
// LastFM
|
||||
|
||||
public string LastFMSessionKey
|
||||
{
|
||||
get => GetValue<string>(LastFMSessionKeyKey)!;
|
||||
set => SetValue(LastFMSessionKeyKey, value);
|
||||
}
|
||||
|
||||
public string LyricsTranslationSeparator
|
||||
{
|
||||
get => GetValue<string>(LyricsTranslationSeparatorKey)!;
|
||||
set => SetValue(LyricsTranslationSeparatorKey, value);
|
||||
}
|
||||
|
||||
// Common methods
|
||||
|
||||
private T? GetValue<T>(string key)
|
||||
{
|
||||
if (_localSettings.Values.TryGetValue(key, out object? value))
|
||||
@@ -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
|
||||
{
|
||||
@@ -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
|
||||
{
|
||||
@@ -285,9 +285,6 @@
|
||||
<data name="SettingsPageAbout.Content" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
|
||||
<value>Lyrics library</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
|
||||
<value>App appearance</value>
|
||||
</data>
|
||||
@@ -566,13 +563,10 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Easing animation type</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>Playback sources</value>
|
||||
<value>Playback and lyrics sources</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>Playback sources</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>Enable or disable lyrics display for a specified media source</value>
|
||||
<value>Monitor this playback source</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>Log record</value>
|
||||
@@ -643,7 +637,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>Translate server is not set, please configure it in settings first</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
|
||||
<value>Reset to 0 when switching songs</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
@@ -919,4 +913,67 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLyricsAlignment.Description" xml:space="preserve">
|
||||
<value>This setting will not affect the dock mode and the dock mode will always remain centered.</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFM.Content" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMManager.Header" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMAuth.Content" xml:space="preserve">
|
||||
<value>Authorize</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUnAuth.Content" xml:space="preserve">
|
||||
<value>Revoke authorization</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMTrack.Header" xml:space="preserve">
|
||||
<value>Track listening history via Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUsername.Header" xml:space="preserve">
|
||||
<value>Username</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMPlaycount.Header" xml:space="preserve">
|
||||
<value>Total playing count</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRegistered.Header" xml:space="preserve">
|
||||
<value>Registration date</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>Source and translation separator</value>
|
||||
</data>
|
||||
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Immersive mode</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
|
||||
<value>Play all</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
|
||||
<value>No playback source captured</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRefresh.Content" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
<data name="LastFMAuthFailed" xml:space="preserve">
|
||||
<value>Authorization failed, please try again</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthTitle" xml:space="preserve">
|
||||
<value>Grant BetterLyrics permission to access your Last.fm account</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthDesc" xml:space="preserve">
|
||||
<value>Please complete the authorization in your browser</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthConfirm" xml:space="preserve">
|
||||
<value>I have completed the authorization</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthTitle" xml:space="preserve">
|
||||
<value>Revoke BetterLyrics' permission to access your Last.fm account</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthDesc" xml:space="preserve">
|
||||
<value>Please complete the cancellation operation in your browser</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthConfirm" xml:space="preserve">
|
||||
<value>I have canceled my authorization</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -285,9 +285,6 @@
|
||||
<data name="SettingsPageAbout.Content" xml:space="preserve">
|
||||
<value>について</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
|
||||
<value>歌詞</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
|
||||
<value>アプリの外観</value>
|
||||
</data>
|
||||
@@ -566,13 +563,10 @@
|
||||
<value>アニメーションタイプを緩和します</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>再生ソース</value>
|
||||
<value>プレイと歌詞</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>再生ソース</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>指定されたメディアソースの歌詞ディスプレイを有効または無効にする</value>
|
||||
<value>この再生ソースを監視します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>ログレコード</value>
|
||||
@@ -643,7 +637,7 @@
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>翻訳サーバーは設定されていません。最初に設定で構成してください</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
|
||||
<value>曲を切り替えるときに0にリセットします</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
@@ -919,4 +913,67 @@
|
||||
<data name="SettingsPageLyricsAlignment.Description" xml:space="preserve">
|
||||
<value>この設定はドックモードには影響しません。ドックモードは常に中心のままです。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFM.Content" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMManager.Header" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMAuth.Content" xml:space="preserve">
|
||||
<value>許可</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUnAuth.Content" xml:space="preserve">
|
||||
<value>承認を取り消します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMTrack.Header" xml:space="preserve">
|
||||
<value>last.fm 経由でリスニング履歴を追跡します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUsername.Header" xml:space="preserve">
|
||||
<value>ユーザー名</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMPlaycount.Header" xml:space="preserve">
|
||||
<value>合計プレイカウント</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRegistered.Header" xml:space="preserve">
|
||||
<value>登録日</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>ソースおよび翻訳セパレーター</value>
|
||||
</data>
|
||||
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
|
||||
<value>没入モード</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
|
||||
<value>すべてを再生します</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
|
||||
<value>キャプチャされた再生ソースはありません</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRefresh.Content" xml:space="preserve">
|
||||
<value>リフレッシュします</value>
|
||||
</data>
|
||||
<data name="LastFMAuthFailed" xml:space="preserve">
|
||||
<value>承認が失敗しました、もう一度やり直してください</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthTitle" xml:space="preserve">
|
||||
<value>BetterLyricsにLast.fmアカウントへのアクセスを許可してください!</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthDesc" xml:space="preserve">
|
||||
<value>ブラウザの承認を完了してください</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthConfirm" xml:space="preserve">
|
||||
<value>私は承認を完了しました</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthTitle" xml:space="preserve">
|
||||
<value>Last.fmアカウントへのBetterLyricsアクセスを取り消します!</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthDesc" xml:space="preserve">
|
||||
<value>ブラウザでキャンセル操作を完了してください</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthConfirm" xml:space="preserve">
|
||||
<value>認可をキャンセルしました</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -285,9 +285,6 @@
|
||||
<data name="SettingsPageAbout.Content" xml:space="preserve">
|
||||
<value>에 대한</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
|
||||
<value>가사</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
|
||||
<value>앱 모양</value>
|
||||
</data>
|
||||
@@ -566,13 +563,10 @@
|
||||
<value>애니메이션 유형 완화</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>재생 소스</value>
|
||||
<value>연극과 가사</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>재생 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>지정된 미디어 소스의 가사 디스플레이 활성화 또는 비활성화</value>
|
||||
<value>이 재생 소스를 모니터링하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>로그 레코드</value>
|
||||
@@ -643,7 +637,7 @@
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>번역 서버가 설정되지 않았습니다. 먼저 설정으로 구성하십시오.</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
|
||||
<value>노래를 전환 할 때 0 으로 재설정하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
@@ -919,4 +913,67 @@
|
||||
<data name="SettingsPageLyricsAlignment.Description" xml:space="preserve">
|
||||
<value>이 설정은 도크 모드에 영향을 미치지 않으며 도크 모드는 항상 중앙에 유지됩니다.</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFM.Content" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMManager.Header" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMAuth.Content" xml:space="preserve">
|
||||
<value>승인</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUnAuth.Content" xml:space="preserve">
|
||||
<value>취소 승인</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMTrack.Header" xml:space="preserve">
|
||||
<value>Last.fm 을 통해 청취 기록을 추적합니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUsername.Header" xml:space="preserve">
|
||||
<value>사용자 이름</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMPlaycount.Header" xml:space="preserve">
|
||||
<value>총 플레이 카운트</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRegistered.Header" xml:space="preserve">
|
||||
<value>등록일</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>소스 및 번역 분리기</value>
|
||||
</data>
|
||||
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
|
||||
<value>몰입 형 모드</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
|
||||
<value>모두 재생하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
|
||||
<value>재생 소스가 캡처되지 않았습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRefresh.Content" xml:space="preserve">
|
||||
<value>새로 고치다</value>
|
||||
</data>
|
||||
<data name="LastFMAuthFailed" xml:space="preserve">
|
||||
<value>승인이 실패했습니다. 다시 시도하십시오</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthTitle" xml:space="preserve">
|
||||
<value>Last.fm 계정에 BetterLyrics 액세스 권한을 부여하세요!</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthDesc" xml:space="preserve">
|
||||
<value>브라우저에서 승인을 완료하십시오</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthConfirm" xml:space="preserve">
|
||||
<value>나는 승인을 완료했다</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>취소</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthTitle" xml:space="preserve">
|
||||
<value>Last.fm 계정에 대한 BetterLyrics 액세스를 취소하십시오!</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthDesc" xml:space="preserve">
|
||||
<value>브라우저에서 취소 작업을 완료하십시오</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthConfirm" xml:space="preserve">
|
||||
<value>내 승인을 취소했습니다</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -285,9 +285,6 @@
|
||||
<data name="SettingsPageAbout.Content" xml:space="preserve">
|
||||
<value>关于</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
|
||||
<value>歌词源</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
|
||||
<value>应用外观</value>
|
||||
</data>
|
||||
@@ -566,13 +563,10 @@
|
||||
<value>缓动动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
<value>播放与歌词源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>为指定媒体源启用或禁用歌词显示</value>
|
||||
<value>监听此播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>日志记录</value>
|
||||
@@ -643,7 +637,7 @@
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>未设置Translate服务器,请先在设置中进行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
|
||||
<value>切换歌曲时重置为 0</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
@@ -919,4 +913,67 @@
|
||||
<data name="SettingsPageLyricsAlignment.Description" xml:space="preserve">
|
||||
<value>此设置不会影响停靠模式,停靠模式将始终保持居中。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFM.Content" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMManager.Header" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMAuth.Content" xml:space="preserve">
|
||||
<value>授权</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUnAuth.Content" xml:space="preserve">
|
||||
<value>撤销授权</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMTrack.Header" xml:space="preserve">
|
||||
<value>通过 Last.fm 跟踪听歌历史记录</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUsername.Header" xml:space="preserve">
|
||||
<value>用户名</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMPlaycount.Header" xml:space="preserve">
|
||||
<value>听歌总数量</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRegistered.Header" xml:space="preserve">
|
||||
<value>注册日期</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>原文译文分隔符</value>
|
||||
</data>
|
||||
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
|
||||
<value>沉浸模式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
|
||||
<value>播放全部</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
|
||||
<value>没有捕获的播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRefresh.Content" xml:space="preserve">
|
||||
<value>刷新</value>
|
||||
</data>
|
||||
<data name="LastFMAuthFailed" xml:space="preserve">
|
||||
<value>授权失败,请重试</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthTitle" xml:space="preserve">
|
||||
<value>授予 BetterLyrics 访问您 Last.fm 账户的权限</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthDesc" xml:space="preserve">
|
||||
<value>请在浏览器中完成授权</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthConfirm" xml:space="preserve">
|
||||
<value>我已经完成了授权</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthTitle" xml:space="preserve">
|
||||
<value>撤销 BetterLyrics 访问您 Last.fm 账户的权限</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthDesc" xml:space="preserve">
|
||||
<value>请在浏览器中完成取消操作</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthConfirm" xml:space="preserve">
|
||||
<value>我已经取消了我的授权</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -285,9 +285,6 @@
|
||||
<data name="SettingsPageAbout.Content" xml:space="preserve">
|
||||
<value>關於</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
|
||||
<value>歌詞源</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
|
||||
<value>應用外觀</value>
|
||||
</data>
|
||||
@@ -566,13 +563,10 @@
|
||||
<value>緩動動畫類型</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>播放來源</value>
|
||||
<value>播放與歌詞源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>播放來源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>為指定媒體源啟用或禁用歌詞顯示</value>
|
||||
<value>監聽此播放來源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>日誌記錄</value>
|
||||
@@ -643,7 +637,7 @@
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>未設定翻譯伺服器,請先在設定中進行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
|
||||
<value>切換歌曲時重置為 0</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
@@ -919,4 +913,67 @@
|
||||
<data name="SettingsPageLyricsAlignment.Description" xml:space="preserve">
|
||||
<value>此設定不會影響停靠模式,停靠模式將始終保持居中。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFM.Content" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMManager.Header" xml:space="preserve">
|
||||
<value>Last.fm</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMAuth.Content" xml:space="preserve">
|
||||
<value>授權</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUnAuth.Content" xml:space="preserve">
|
||||
<value>撤銷授權</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMTrack.Header" xml:space="preserve">
|
||||
<value>透過 Last.fm 追蹤聽歌歷史記錄</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMUsername.Header" xml:space="preserve">
|
||||
<value>使用者名稱</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMPlaycount.Header" xml:space="preserve">
|
||||
<value>聽歌總數量</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRegistered.Header" xml:space="preserve">
|
||||
<value>註冊日期</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTranslationSeparator.Header" xml:space="preserve">
|
||||
<value>原文譯文分隔符</value>
|
||||
</data>
|
||||
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
|
||||
<value>沉浸模式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayAll.Content" xml:space="preserve">
|
||||
<value>播放全部</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
|
||||
<value>沒有捕獲的播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPageLastFMRefresh.Content" xml:space="preserve">
|
||||
<value>重新整理</value>
|
||||
</data>
|
||||
<data name="LastFMAuthFailed" xml:space="preserve">
|
||||
<value>授權失敗,請重試</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthTitle" xml:space="preserve">
|
||||
<value>授予 BetterLyrics 訪問您 Last.fm 賬戶的權限</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthDesc" xml:space="preserve">
|
||||
<value>請在瀏覽器中完成授權</value>
|
||||
</data>
|
||||
<data name="LastFMRequestAuthConfirm" xml:space="preserve">
|
||||
<value>我已經完成了授權</value>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthTitle" xml:space="preserve">
|
||||
<value>撤銷 BetterLyrics 訪問您 Last.fm 賬戶的權限</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthDesc" xml:space="preserve">
|
||||
<value>請在瀏覽器中完成取消操作</value>
|
||||
</data>
|
||||
<data name="LastFMRequestUnAuthConfirm" xml:space="preserve">
|
||||
<value>我已經取消了我的授權</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<PropertyChangedMessage<LyricsSearchProvider?>>,
|
||||
IRecipient<PropertyChangedMessage<TranslationSearchProvider?>>
|
||||
{
|
||||
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<int> 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<string> 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<TimeSpan> 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<LyricsSearchProvider?> 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<TranslationSearchProvider?> 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;
|
||||
}
|
||||
|
||||
@@ -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<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
_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();
|
||||
}
|
||||
@@ -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,8 @@ 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)}",
|
||||
$"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,
|
||||
_debugTextFormat
|
||||
@@ -323,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,
|
||||
@@ -344,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
|
||||
@@ -431,10 +444,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
// Reset scale
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
|
||||
line.DisposeFontEffects();
|
||||
line.DisposeTextGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
@@ -444,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,
|
||||
@@ -461,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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -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<PropertyChangedMessage<int>>,
|
||||
@@ -24,14 +25,15 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IRecipient<PropertyChangedMessage<LineRenderingType>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<EasingType>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<DockPlacement>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalMediaFolder>>>
|
||||
{
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> 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<ObservableCollection<LyricsSearchProviderInfo>> message)
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> 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<bool> 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<float> 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<int> 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<LineRenderingType> 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<TextAlignmentType> 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<LyricsFontColorType> 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<LyricsFontWeight> 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<ElementTheme> 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<EasingType> 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<string> 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<DockPlacement> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.DockPlacement))
|
||||
{
|
||||
_dockPlacement = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -6,6 +6,8 @@ using Microsoft.Graphics.Canvas.Text;
|
||||
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;
|
||||
@@ -15,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
|
||||
{
|
||||
@@ -42,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();
|
||||
@@ -238,7 +246,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
UpdateCanvasYScrollOffset(control, false, true);
|
||||
}
|
||||
|
||||
UpdateLinesProps();
|
||||
UpdateLinesProps(control);
|
||||
|
||||
_isLayoutChanged = false;
|
||||
|
||||
@@ -288,11 +296,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
continue;
|
||||
}
|
||||
|
||||
line.UpdateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType);
|
||||
line.UpdateTextGeometry();
|
||||
line.Position = new Vector2(0, y);
|
||||
line.UpdateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType);
|
||||
line.UpdateCenterPosition(_maxLyricsWidth, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType);
|
||||
line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor);
|
||||
|
||||
//line.UpdateTextGeometry();
|
||||
//line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor);
|
||||
|
||||
if (line.CanvasTextLayout == null)
|
||||
{
|
||||
@@ -501,7 +510,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
private void UpdateLinesProps()
|
||||
private void UpdateLinesProps(ICanvasAnimatedControl control)
|
||||
{
|
||||
var currentPlayingLine = _lyricsDataArr
|
||||
.ElementAtOrDefault(_langIndex)
|
||||
@@ -515,6 +524,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (line == null) continue;
|
||||
|
||||
line.UpdateTextGeometry();
|
||||
line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor);
|
||||
|
||||
if (_isLayoutChanged || _isVisibleLinesBoundaryChanged || _isPlayingLineChanged)
|
||||
{
|
||||
float distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y);
|
||||
@@ -574,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(
|
||||
@@ -586,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MediaSourceProviderInfo> _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 ?? "",
|
||||
@@ -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<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<DockPlacement>>
|
||||
{
|
||||
private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService<IPlaybackService>();
|
||||
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
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();
|
||||
|
||||
@@ -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<ObservableCollection<LocalMediaFolder>> 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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<LocalMediaFolder> LocalMediaFolders { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
|
||||
@@ -98,6 +95,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<MediaSourceProviderInfo> 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<string> 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; }
|
||||
}
|
||||
}
|
||||
@@ -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<bool> 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<bool> DetectIsAutoStartupEnabledAsync()
|
||||
{
|
||||
bool result = false;
|
||||
var startupTask = await StartupTask.GetAsync(_autoStartupTaskId);
|
||||
var startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);
|
||||
switch (startupTask.State)
|
||||
{
|
||||
case StartupTaskState.Disabled:
|
||||
@@ -1,4 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
|
||||
@@ -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<bool> message)
|
||||
{
|
||||
|
||||
@@ -79,52 +79,6 @@
|
||||
<TextBlock Text="{Binding ElementName=TimelineSlider, Path=Maximum, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Position offset -->
|
||||
<Button Click="TimelineOffsetButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="TimelineOffsetFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.ResetPositionOffsetOnSongChanged, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="LyricsPagePositionOffsetHint" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -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<ISettingsService>();
|
||||
private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService<IPlaybackService>();
|
||||
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
Command="{x:Bind ViewModel.ImmersiveToggleButtonEnabledChangedCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}"
|
||||
Style="{StaticResource TitleBarToggleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="LyricsWindowImmersiveButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize}"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
x:Name="SelectAllToggleButton"
|
||||
x:Uid="MusicGalleryPageSelectAll"
|
||||
Click="SelectAllToggleButton_Click" />
|
||||
<Button x:Uid="MusicGalleryPagePlayAll" Click="PlayAllButton_Click" />
|
||||
<Button x:Uid="MusicGalleryPageAddToPlayingQueue">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
|
||||
@@ -167,5 +167,15 @@ namespace BetterLyrics.WinUI3.Views
|
||||
ViewModel.SelectedSongsTabInfoIndex = 0;
|
||||
ViewModel.ApplyPlaylist();
|
||||
}
|
||||
|
||||
private void PlayAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TrackPlayingQueue.Clear();
|
||||
ViewModel.PlayingSongIndex = -1;
|
||||
|
||||
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.PlayingSongIndex + 1, SongListView.Items.Cast<Track>().Select(x => new PlayQueueItem(x)));
|
||||
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
|
||||
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,62 +23,73 @@
|
||||
IsSettingsVisible="False"
|
||||
SelectionChanged="NavView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageApp"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsSelected="True"
|
||||
Tag="App" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageAlbumStyle"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="AlbumArtStyle" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageMediaLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="MediaLib" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageAlbumLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="AlbumArtLib" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPagePlaybackLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="PlaybackLib" />
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageLyricsLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="LyricsLib" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageBackgroundOverlay"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Background" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageLyrics"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Lyrics" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageTranslation"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Translation" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageLastFM"
|
||||
Icon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
|
||||
Tag="LastFM" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageAbout"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="About" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageDev"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Dev" />
|
||||
|
||||
</NavigationView.MenuItems>
|
||||
|
||||
<ScrollViewer Padding="36,0">
|
||||
@@ -200,13 +211,13 @@
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDockMonitor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedDockMonitorDeviceName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedDockMonitorDeviceName, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -383,31 +394,168 @@
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Playback source -->
|
||||
<!-- Playback and lyrics source -->
|
||||
<controls:Case Value="PlaybackLib">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<ListView
|
||||
x:Name="MediaSourceProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
ItemsSource="{x:Bind ViewModel.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<Grid>
|
||||
<NavigationView
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsBackEnabled="False"
|
||||
IsSettingsVisible="False"
|
||||
MenuItemsSource="{x:Bind ViewModel.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||
PaneDisplayMode="Top"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSourceProvidersInfo.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSourceProvidersInfo.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<NavigationView.MenuItemTemplate>
|
||||
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="MediaSourceProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
<NavigationViewItem>
|
||||
<NavigationViewItem.Icon>
|
||||
<ImageIcon Source="{Binding Provider, Converter={StaticResource MediaSourceProviderToLogoUriConverter}, Mode=OneWay}" />
|
||||
</NavigationViewItem.Icon>
|
||||
<NavigationViewItem.Content>
|
||||
<TextBlock
|
||||
MaxWidth="200"
|
||||
Text="{Binding Provider, Converter={StaticResource MediaSourceProviderToDisplayedNameConverter}, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</NavigationViewItem.Content>
|
||||
</NavigationViewItem>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</NavigationView.MenuItemTemplate>
|
||||
<NavigationView.Content>
|
||||
<Grid Background="{ThemeResource CardStrokeColorDefaultBrush}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsEnabled, Mode=OneWay}" Toggled="MediaSourceProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLastFMTrack" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsLastFMTrackEnabled, Mode=OneWay}" Toggled="MediaSourceProviderLastFMTrackToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.SelectedMediaSourceProvider.TimelineSyncThreshold, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" ms" />
|
||||
<Slider
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
ValueChanged="MediaSourceProviderTimelineSyncThresholdSlider_ValueChanged"
|
||||
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.TimelineSyncThreshold, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsExpander x:Uid="MainPagePositionOffsetSlider" IsExpanded="True">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.SelectedMediaSourceProvider.PositionOffset, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" ms" />
|
||||
<Slider
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
ValueChanged="MediaSourceProviderPositionOffsetSlider_ValueChanged"
|
||||
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.PositionOffset, Mode=TwoWay}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
Click="MediaSourceProviderPositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="LyricsPagePositionOffsetHint">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.ResetPositionOffsetOnSongChanged, Mode=TwoWay}" Toggled="ResetPositionOffsetOnSongChangedToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsSearchProvidersConfig" />
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="LyricsSearchProvidersListView_DragItemsCompleted"
|
||||
ItemsSource="{x:Bind ViewModel.SelectedMediaSourceProvider.LyricsSearchProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</ListView.OpacityTransition>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
||||
<controls:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="LyricsSearchProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</NavigationView.Content>
|
||||
</NavigationView>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="12">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSourceProvidersInfo.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSourceProvidersInfo.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<Image MaxWidth="200" Source="/Assets/Leaf.png" />
|
||||
<TextBlock
|
||||
x:Uid="SettingsPagePlaybackNotFound"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Media lib -->
|
||||
@@ -481,46 +629,6 @@
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics source -->
|
||||
<controls:Case Value="LyricsLib">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<controls:SettingsCard
|
||||
x:Name="LyricsSearchProvidersSettingsExpander"
|
||||
x:Uid="SettingsPageLyricsSearchProvidersConfig"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="LyricsSearchProvidersListView_DragItemsCompleted"
|
||||
ItemsSource="{x:Bind ViewModel.LyricsSearchProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</ListView.OpacityTransition>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="LyricsSearchProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics style and effect -->
|
||||
<controls:Case Value="Lyrics">
|
||||
@@ -780,6 +888,13 @@
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsTranslationSeparator" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBox AcceptsReturn="True" Text="{x:Bind ViewModel.LyricsTranslationSeparator, Mode=TwoWay}" />
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- Effect -->
|
||||
|
||||
<TextBlock
|
||||
@@ -978,6 +1093,41 @@
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Last.FM -->
|
||||
<controls:Case Value="LastFM">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageLastFMManager"
|
||||
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
|
||||
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<Button
|
||||
x:Uid="SettingsPageLastFMAuth"
|
||||
Command="{x:Bind ViewModel.LastFMAuthCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
|
||||
<Button
|
||||
x:Uid="SettingsPageLastFMUnAuth"
|
||||
Command="{x:Bind ViewModel.LastFMUnAuthCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLastFMUsername" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<HyperlinkButton Content="{x:Bind ViewModel.LastFMUser.Name, Mode=OneWay}" NavigateUri="{x:Bind ViewModel.LastFMUser.Url, Mode=OneWay}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLastFMPlaycount" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Playcount, Mode=OneWay}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLastFMRegistered" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Registered.ToLongDateString(), Mode=OneWay}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<Button x:Uid="SettingsPageLastFMRefresh" Command="{x:Bind ViewModel.LastFMRefreshCommand}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- About -->
|
||||
<controls:Case Value="About">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
@@ -1037,25 +1187,6 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDebugOverlayEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.TimelineSyncThreshold, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" ms" />
|
||||
<Slider
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.TimelineSyncThreshold, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLXMusicServer">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBox
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
@@ -42,13 +44,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void LyricsSearchProviderToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch)
|
||||
{
|
||||
if (toggleSwitch.DataContext is LyricsSearchProviderInfo providerInfo)
|
||||
{
|
||||
ViewModel.ToggleLyricsSearchProvider();
|
||||
}
|
||||
}
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
|
||||
private void NavView_SelectionChanged(
|
||||
@@ -71,9 +67,10 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch)
|
||||
{
|
||||
if (toggleSwitch.DataContext is MediaSourceProviderInfo providerInfo)
|
||||
if (ViewModel.SelectedMediaSourceProvider != null)
|
||||
{
|
||||
ViewModel.ToggleMediaSourceProvider(providerInfo);
|
||||
ViewModel.SelectedMediaSourceProvider.IsEnabled = toggleSwitch.IsOn;
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,22 +115,54 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void QQGroupButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Launcher.LaunchUriAsync(new Uri(MetadataHelper.QQGroupUrl));
|
||||
Launcher.LaunchUriAsync(new Uri(Constants.Link.QQGroupUrl));
|
||||
}
|
||||
|
||||
private void DiscodGroupButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Launcher.LaunchUriAsync(new Uri(MetadataHelper.DiscordUrl));
|
||||
Launcher.LaunchUriAsync(new Uri(Constants.Link.DiscordUrl));
|
||||
}
|
||||
|
||||
private void TelegramGroupButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Launcher.LaunchUriAsync(new Uri(MetadataHelper.TelegramUrl));
|
||||
Launcher.LaunchUriAsync(new Uri(Constants.Link.TelegramUrl));
|
||||
}
|
||||
|
||||
private void AutoStartupToggleSwitch_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AutoStartupToggleSwitch.Toggled -= AutoStartupToggleSwitch_Toggled;
|
||||
}
|
||||
|
||||
private void MediaSourceProviderLastFMTrackToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch)
|
||||
{
|
||||
if (ViewModel.SelectedMediaSourceProvider != null)
|
||||
{
|
||||
ViewModel.SelectedMediaSourceProvider.IsLastFMTrackEnabled = toggleSwitch.IsOn;
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaSourceProviderTimelineSyncThresholdSlider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
|
||||
{
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
|
||||
private void MediaSourceProviderPositionOffsetResetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.SelectedMediaSourceProvider?.PositionOffset = 0;
|
||||
}
|
||||
|
||||
private void MediaSourceProviderPositionOffsetSlider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
|
||||
{
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
|
||||
private void ResetPositionOffsetOnSongChangedToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.BroadcastMediaSourceProvidersInfoChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Promotion/banner.png
Normal file
|
After Width: | Height: | Size: 453 KiB |
20
README.ja.md
@@ -1,29 +1,33 @@
|
||||
<div style="text-align: center;">
|
||||
[_よくある質問(FAQ)はこちらをクリック_](#faq)
|
||||
|
||||
[❓ よくある質問(FAQ)はこちらをクリック](#faq)
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<div align=center>
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64">
|
||||
</div>
|
||||
|
||||
<h2 align="center">
|
||||
<h2 align=center>
|
||||
BetterLyrics
|
||||
</h2>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-CN.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-TW.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ja.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ko.md)
|
||||
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/stargazers)
|
||||
|
||||
</div>
|
||||
|
||||
<h4 align="center">
|
||||
WinUI 3とWin2Dで構築された動的歌詞表示ツール — ローカル再生や他のプレーヤーにも対応
|
||||
</h3>
|
||||
|
||||
20
README.ko.md
@@ -1,29 +1,33 @@
|
||||
<div style="text-align: center;">
|
||||
[_자주 묻는 질문(FAQ) 보러가기_](#faq)
|
||||
|
||||
[❓ 자주 묻는 질문(FAQ) 보러가기](#faq)
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<div align=center>
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64">
|
||||
</div>
|
||||
|
||||
<h2 align="center">
|
||||
<h2 align=center>
|
||||
BetterLyrics
|
||||
</h2>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-CN.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-TW.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ja.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ko.md)
|
||||
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/stargazers)
|
||||
|
||||
</div>
|
||||
|
||||
<h4 align="center">
|
||||
WinUI 3와 Win2D로 제작된 동적 가사 디스플레이 도구 — 로컬 재생 및 다양한 플레이어 지원
|
||||
</h3>
|
||||
|
||||
20
README.md
@@ -1,29 +1,33 @@
|
||||
<div style="text-align: center;">
|
||||
[_Click here to view frequently asked questions (FAQ)_](#faq)
|
||||
|
||||
[❓ Click here to view frequently asked questions (FAQ)](#faq)
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<div align=center>
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64">
|
||||
</div>
|
||||
|
||||
<h2 align="center">
|
||||
<h2 align=center>
|
||||
BetterLyrics
|
||||
</h2>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-CN.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.zh-TW.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ja.md) [](https://github.com/jayfunc/BetterLyrics/blob/dev/README.ko.md)
|
||||
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/jayfunc/BetterLyrics/stargazers)
|
||||
|
||||
</div>
|
||||
|
||||
<h4 align="center">
|
||||
Your dynamic lyrics display tool built with WinUI 3 and Win2D — works with local playback and other players
|
||||
</h3>
|
||||
|
||||