mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
Compare commits
335 Commits
v1.1.186.0
...
85f67c2ec6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f67c2ec6 | ||
|
|
bdbeb391ea | ||
|
|
3357e7aaf4 | ||
|
|
e43461d624 | ||
|
|
3e1907ad8c | ||
|
|
74eeffc8a6 | ||
|
|
c32eb3b877 | ||
|
|
047e53b830 | ||
|
|
fdb7bd16f6 | ||
|
|
4e33444d8e | ||
|
|
8fc67711b6 | ||
|
|
28757d9880 | ||
|
|
e5fb04f577 | ||
|
|
9d03c8f688 | ||
|
|
094fe7b7a1 | ||
|
|
bc32a3f34c | ||
|
|
b23d3c280f | ||
|
|
2738d45b69 | ||
|
|
77a9bb0a1b | ||
|
|
c07389acfb | ||
|
|
042229ae74 | ||
|
|
caaf93cf27 | ||
|
|
92e4b9468c | ||
|
|
6f60952d09 | ||
|
|
efc175668e | ||
|
|
3bf0fbef5f | ||
|
|
96b7835e8f | ||
|
|
a0b6511a53 | ||
|
|
3947050d6f | ||
|
|
707d85bc75 | ||
|
|
78bafb8508 | ||
|
|
b4d24c5570 | ||
|
|
83c9f9806d | ||
|
|
adde74afb0 | ||
|
|
67b4d4e409 | ||
|
|
8d7fbe63c5 | ||
|
|
5037b92913 | ||
|
|
c1ee7a6779 | ||
|
|
7ddfd1118b | ||
|
|
97f20decf2 | ||
|
|
81eb4e1c96 | ||
|
|
00ee4a051c | ||
|
|
a13bb6e8e4 | ||
|
|
0b436c1ea9 | ||
|
|
5d332fdfc6 | ||
|
|
572d2cd8ba | ||
|
|
1e5a95c55e | ||
|
|
18ce6d3a57 | ||
|
|
427aed6857 | ||
|
|
ebfa484a2e | ||
|
|
3ef9d81bea | ||
|
|
e999d07834 | ||
|
|
838b8de94f | ||
|
|
b3059dbeb1 | ||
|
|
6fea88a6a1 | ||
|
|
abca9ae5fb | ||
|
|
a062897e1a | ||
|
|
8b4748df1b | ||
|
|
1df5ea6bab | ||
|
|
c576635af2 | ||
|
|
c8590202ec | ||
|
|
2dc8b1283f | ||
|
|
c482edea0f | ||
|
|
315722252c | ||
|
|
32ba453264 | ||
|
|
d4902329bb | ||
|
|
83aee8948b | ||
|
|
1f9fab3228 | ||
|
|
7a3a659dfc | ||
|
|
a14afd3eb5 | ||
|
|
c2af7f3186 | ||
|
|
cd026dd2bf | ||
|
|
4bc1a9975d | ||
|
|
07eecf0930 | ||
|
|
35fba5abb0 | ||
|
|
03ef231a3f | ||
|
|
f41879f4e5 | ||
|
|
bda7510ed6 | ||
|
|
5ec8c7c61f | ||
|
|
7e6bd9dade | ||
|
|
56244cb793 | ||
|
|
cb5f70ab55 | ||
|
|
3cc018bb1f | ||
|
|
c517d2b008 | ||
|
|
e79f2a0223 | ||
|
|
39122b9147 | ||
|
|
accbdc1806 | ||
|
|
de014d1ad7 | ||
|
|
cc2ce5f8cf | ||
|
|
2a2d80436e | ||
|
|
ce3f79f35c | ||
|
|
12e6000cb3 | ||
|
|
c1dc684411 | ||
|
|
69ea2cb495 | ||
|
|
e2ee03c4be | ||
|
|
c6fe33d6ae | ||
|
|
7744e145fa | ||
|
|
0284b1de81 | ||
|
|
108c2cd34b | ||
|
|
390e30f7f5 | ||
|
|
900774668d | ||
|
|
6ca2d1f897 | ||
|
|
164bd077b8 | ||
|
|
8ec71fcfb7 | ||
|
|
f39ad54df8 | ||
|
|
9b809983df | ||
|
|
8006b3a443 | ||
|
|
26a7454de2 | ||
|
|
0793a074cf | ||
|
|
125bf1682e | ||
|
|
48bdffb2fe | ||
|
|
d324a7552f | ||
|
|
78c308c393 | ||
|
|
a1bba00db6 | ||
|
|
0787f5b111 | ||
|
|
884026594b | ||
|
|
b0a777db8d | ||
|
|
83f3a3bd6d | ||
|
|
bfb2ed29e5 | ||
|
|
131a0f0eb1 | ||
|
|
ac2a7b3f7b | ||
|
|
36eea7f8f2 | ||
|
|
6b338deb55 | ||
|
|
af323ecd00 | ||
|
|
c79d01c75b | ||
|
|
b51ec1e60f | ||
|
|
7fe925bcba | ||
|
|
0626472d66 | ||
|
|
33099bc186 | ||
|
|
e653efc227 | ||
|
|
074fef3faf | ||
|
|
029cbbd343 | ||
|
|
802b2a4c1c | ||
|
|
eccc4d519c | ||
|
|
5f274ea28a | ||
|
|
aa1a1f5d58 | ||
|
|
3a56d53487 | ||
|
|
bbc5eb772c | ||
|
|
05b491052b | ||
|
|
8accbf0431 | ||
|
|
1174209c2a | ||
|
|
23ed719046 | ||
|
|
a34f00662e | ||
|
|
f783314258 | ||
|
|
215a39c5d5 | ||
|
|
16bcef5f64 | ||
|
|
fbba9a3c36 | ||
|
|
f205ab0364 | ||
|
|
10314f3c2f | ||
|
|
b4710e87d3 | ||
|
|
282a934cd2 | ||
|
|
b4c4e394ef | ||
|
|
17cfdf37bd | ||
|
|
900a8e1e7c | ||
|
|
ea9a9c2f5f | ||
|
|
0c4d02b337 | ||
|
|
d137d82ecf | ||
|
|
02551e2053 | ||
|
|
026926e9b8 | ||
|
|
4c811db16a | ||
|
|
6f83fa11db | ||
|
|
bc8e15c144 | ||
|
|
85de1eb2cd | ||
|
|
d2bf19ed3d | ||
|
|
43c205c839 | ||
|
|
9664b1ab78 | ||
|
|
08c5f6b515 | ||
|
|
260de40f81 | ||
|
|
c00d0eb005 | ||
|
|
32e761724c | ||
|
|
9fd08af582 | ||
|
|
266dcfc930 | ||
|
|
8764585f2c | ||
|
|
91ab3a48c0 | ||
|
|
80fa34d9e8 | ||
|
|
b4ca4fd990 | ||
|
|
86527f6b82 | ||
|
|
d8066bc683 | ||
|
|
b261a86791 | ||
|
|
34f2a51b74 | ||
|
|
b1e9c25e01 | ||
|
|
346de93c3f | ||
|
|
6f48cbcd16 | ||
|
|
85b3121479 | ||
|
|
94f00d1a31 | ||
|
|
be9e4bba0f | ||
|
|
2454927582 | ||
|
|
aca5f8e00d | ||
|
|
09709e8e62 | ||
|
|
98fd8b43c4 | ||
|
|
3051180eb9 | ||
|
|
d48c81cfa1 | ||
|
|
695147be9b | ||
|
|
e782944a44 | ||
|
|
01462d42ce | ||
|
|
65b7dfcc44 | ||
|
|
ec3146d4a7 | ||
|
|
9ec0bf0b1a | ||
|
|
47e4b93613 | ||
|
|
192ad4a503 | ||
|
|
091e33ae08 | ||
|
|
3b010ed674 | ||
|
|
a9f685d51b | ||
|
|
c6c31f8839 | ||
|
|
78c53760cc | ||
|
|
0bb6b5a204 | ||
|
|
dff36a5e4d | ||
|
|
0188e443db | ||
|
|
5a9cdedc0c | ||
|
|
31460fcc6d | ||
|
|
c12fc6f381 | ||
|
|
e5e0342994 | ||
|
|
061958f20c | ||
|
|
95c73d0a34 | ||
|
|
026a12ac87 | ||
|
|
da53f2166f | ||
|
|
717277e17c | ||
|
|
1dc3ea57e9 | ||
|
|
4ec2ba8b59 | ||
|
|
91d9f253f0 | ||
|
|
90cf373e50 | ||
|
|
cf2778da7a | ||
|
|
45ff7d7aa8 | ||
|
|
eb37cb1b55 | ||
|
|
45aa1d787d | ||
|
|
0b28419ab5 | ||
|
|
258bf9220e | ||
|
|
9ece9f3edc | ||
|
|
40c1f0a5ce | ||
|
|
5f75e6c63c | ||
|
|
43387ce4c8 | ||
|
|
34eda9a262 | ||
|
|
804673696f | ||
|
|
b69e3bb24b | ||
|
|
c028aa8e46 | ||
|
|
fe3e257215 | ||
|
|
eae2428d85 | ||
|
|
b078365136 | ||
|
|
1ede8dbef4 | ||
|
|
a66051b937 | ||
|
|
1eca21c285 | ||
|
|
2254a28e40 | ||
|
|
812eca369d | ||
|
|
132d3d8ac8 | ||
|
|
641a23621f | ||
|
|
6802d10142 | ||
|
|
36f43e6d54 | ||
|
|
e8298ec7bd | ||
|
|
99a21cb935 | ||
|
|
b6da7bea5d | ||
|
|
cf5bf75346 | ||
|
|
7497d7014d | ||
|
|
dd8c62ffa5 | ||
|
|
15b147ba06 | ||
|
|
85146ffc95 | ||
|
|
e9dce765e4 | ||
|
|
3b2c4477b5 | ||
|
|
9d71c4aecf | ||
|
|
7184c148c4 | ||
|
|
85f928ce3b | ||
|
|
7c5032b0c2 | ||
|
|
2c3bd056b7 | ||
|
|
9f2843b7a0 | ||
|
|
7fb6d5346e | ||
|
|
27125d9051 | ||
|
|
5b2fb8b345 | ||
|
|
d558811cb4 | ||
|
|
6e30aa7ebd | ||
|
|
15fc337944 | ||
|
|
b7ef159b9e | ||
|
|
393b33ed83 | ||
|
|
23dfda4413 | ||
|
|
fde7340f4d | ||
|
|
22330d7fe9 | ||
|
|
c64e5776e8 | ||
|
|
ffa2cd75a0 | ||
|
|
873e75a7e9 | ||
|
|
ffa4101d5f | ||
|
|
1c12b582c2 | ||
|
|
c50d31ced7 | ||
|
|
f8108151b6 | ||
|
|
2932366767 | ||
|
|
cbf643ca70 | ||
|
|
a72d0f5c28 | ||
|
|
3b4d98f9a3 | ||
|
|
d5828101d8 | ||
|
|
56051537ea | ||
|
|
6b465a09b1 | ||
|
|
450b86ebaf | ||
|
|
c0078baa13 | ||
|
|
6b28212ec3 | ||
|
|
9a3c2f5f70 | ||
|
|
31be2bd8f7 | ||
|
|
47056e07a1 | ||
|
|
f30673b9d3 | ||
|
|
d8624c49d0 | ||
|
|
72810e7440 | ||
|
|
e881d36743 | ||
|
|
aa3e79d3ff | ||
|
|
9979474ce1 | ||
|
|
2e7cd93cfe | ||
|
|
bdc31c3e0d | ||
|
|
631d079aa2 | ||
|
|
f76ef87167 | ||
|
|
76aa5ee8d0 | ||
|
|
d7f4978a66 | ||
|
|
0905c46e45 | ||
|
|
d0991c5ddb | ||
|
|
619a3ba196 | ||
|
|
13526bb85c | ||
|
|
61f4f608db | ||
|
|
f690da8501 | ||
|
|
145c13a0e6 | ||
|
|
cea4fbb54d | ||
|
|
1d489c68e9 | ||
|
|
90e7fa42d0 | ||
|
|
29a6879e45 | ||
|
|
58499a2d09 | ||
|
|
580255699b | ||
|
|
9cac7818f1 | ||
|
|
118668a457 | ||
|
|
37621dbf2a | ||
|
|
aa7d56f1cb | ||
|
|
8dbe76e790 | ||
|
|
de6410492e | ||
|
|
26df7c7f67 | ||
|
|
3c411374bd | ||
|
|
99f0b9443b | ||
|
|
a3bc148816 | ||
|
|
cea757702b | ||
|
|
8938a5c798 | ||
|
|
46f4589b64 | ||
|
|
adb02658f4 | ||
|
|
3d7e6061e9 | ||
|
|
a51220c7b9 |
9
BetterLyrics.Core/BetterLyrics.Core.csproj
Normal file
9
BetterLyrics.Core/BetterLyrics.Core.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
16
BetterLyrics.Core/Interfaces/ILyricsProvider.cs
Normal file
16
BetterLyrics.Core/Interfaces/ILyricsProvider.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using BetterLyrics.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface ILyricsProvider
|
||||||
|
{
|
||||||
|
string Id { get; }
|
||||||
|
string Name { get; }
|
||||||
|
string Author { get; }
|
||||||
|
|
||||||
|
Task<LyricsSearchResult> GetLyricsAsync(string title, string artist, string album, double duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
BetterLyrics.Core/Models/LyricsSearchResult.cs
Normal file
16
BetterLyrics.Core/Models/LyricsSearchResult.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.Core.Models
|
||||||
|
{
|
||||||
|
public record LyricsSearchResult(
|
||||||
|
string? Title,
|
||||||
|
string? Artist,
|
||||||
|
string? Album,
|
||||||
|
double? Duration,
|
||||||
|
string Raw,
|
||||||
|
string? Translation = null,
|
||||||
|
string? Transliteration = null,
|
||||||
|
string? Reference = null);
|
||||||
|
}
|
||||||
13
BetterLyrics.Plugins.Demo/BetterLyrics.Plugins.Demo.csproj
Normal file
13
BetterLyrics.Plugins.Demo/BetterLyrics.Plugins.Demo.csproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\BetterLyrics.Core\BetterLyrics.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
59
BetterLyrics.Plugins.Demo/DemoLyricsProvider.cs
Normal file
59
BetterLyrics.Plugins.Demo/DemoLyricsProvider.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using BetterLyrics.Core.Interfaces;
|
||||||
|
using BetterLyrics.Core.Models;
|
||||||
|
|
||||||
|
namespace BetterLyrics.Plugins.Demo
|
||||||
|
{
|
||||||
|
public class DemoLyricsProvider : ILyricsProvider
|
||||||
|
{
|
||||||
|
public string Id => "f7acc86b-6e3d-42c3-a9a9-8c05c5339412";
|
||||||
|
public string Name => "Demo Plugin";
|
||||||
|
public string Author => "jayfunc";
|
||||||
|
|
||||||
|
public async Task<LyricsSearchResult> GetLyricsAsync(string title, string artist, string album, double duration)
|
||||||
|
{
|
||||||
|
await Task.Delay(300);
|
||||||
|
|
||||||
|
string searchedTitle = "Demo Song";
|
||||||
|
string searchedArtist = "Demo Artist";
|
||||||
|
string searchedAlbum = "Demo Album";
|
||||||
|
double searchedDuration = 25.0;
|
||||||
|
|
||||||
|
string searchedRaw =
|
||||||
|
$"[00:00.00]Welcome to use Demo Plugin\n" +
|
||||||
|
$"[00:05.00]Playing: {title} now\n" +
|
||||||
|
$"[00:10.00]Artist: {artist}\n" +
|
||||||
|
$"[00:15.00]Album: {album}\n" +
|
||||||
|
$"[00:20.00]Duration: {duration}\n" +
|
||||||
|
$"[00:25.00]This is a test lyrics source...";
|
||||||
|
|
||||||
|
string searchedTranslation =
|
||||||
|
$"[00:00.00]欢迎使用演示插件\n" +
|
||||||
|
$"[00:05.00]当前正在播放:{title}\n" +
|
||||||
|
$"[00:10.00]歌手:{artist}\n" +
|
||||||
|
$"[00:15.00]专辑:{album}\n" +
|
||||||
|
$"[00:20.00]时长:{duration}\n" +
|
||||||
|
$"[00:25.00]这是一个测试歌词源...";
|
||||||
|
|
||||||
|
string searchedTransliteration =
|
||||||
|
$"[00:00.00]ˈwɛlkəm tuː juːz ˈdɛmoʊ ˈplʌgɪn\n" +
|
||||||
|
$"[00:05.00]ˈpleɪɪŋ: {title} naʊ\n" +
|
||||||
|
$"[00:10.00]ˈɑːrtɪst: {artist}\n" +
|
||||||
|
$"[00:15.00]ˈælbəm: {album}\n" +
|
||||||
|
$"[00:20.00]dʊˈreɪʃən: {duration}\n" +
|
||||||
|
$"[00:25.00]ðɪs ɪz ə tɛst ˈlɪrɪks sɔːrs...";
|
||||||
|
|
||||||
|
string searchedReference = "https://path.to.lyrics/if.the.lyrics.was.originally.fetched.from.web";
|
||||||
|
|
||||||
|
return new LyricsSearchResult(
|
||||||
|
searchedTitle,
|
||||||
|
searchedArtist,
|
||||||
|
searchedAlbum,
|
||||||
|
searchedDuration,
|
||||||
|
searchedRaw,
|
||||||
|
searchedTranslation,
|
||||||
|
searchedTransliteration,
|
||||||
|
searchedReference);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,27 +53,27 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
<DefaultLanguage>en-US</DefaultLanguage>
|
<DefaultLanguage>en</DefaultLanguage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AppxManifest Include="Package.appxmanifest">
|
<AppxManifest Include="Package.appxmanifest">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="37412.BetterLyrics"
|
Name="37412.BetterLyrics"
|
||||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||||
Version="1.1.186.0" />
|
Version="1.2.256.0" />
|
||||||
|
|
||||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||||
|
|
||||||
@@ -28,11 +28,22 @@
|
|||||||
</Dependencies>
|
</Dependencies>
|
||||||
|
|
||||||
<Resources>
|
<Resources>
|
||||||
<Resource Language="en-US"/>
|
<Resource Language="ar"/>
|
||||||
<Resource Language="zh-CN"/>
|
<Resource Language="de"/>
|
||||||
<Resource Language="zh-TW"/>
|
<Resource Language="en"/>
|
||||||
<Resource Language="ja-JP"/>
|
<Resource Language="es"/>
|
||||||
<Resource Language="ko-KR"/>
|
<Resource Language="fr"/>
|
||||||
|
<Resource Language="hi"/>
|
||||||
|
<Resource Language="id"/>
|
||||||
|
<Resource Language="ja"/>
|
||||||
|
<Resource Language="ko"/>
|
||||||
|
<Resource Language="ms"/>
|
||||||
|
<Resource Language="pt"/>
|
||||||
|
<Resource Language="ru"/>
|
||||||
|
<Resource Language="th"/>
|
||||||
|
<Resource Language="vi"/>
|
||||||
|
<Resource Language="zh-Hans"/>
|
||||||
|
<Resource Language="zh-Hant"/>
|
||||||
</Resources>
|
</Resources>
|
||||||
|
|
||||||
<Applications>
|
<Applications>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
x:Class="BetterLyrics.WinUI3.App"
|
x:Class="BetterLyrics.WinUI3.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:converter="using:BetterLyrics.WinUI3.Converter"
|
|
||||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
|
||||||
xmlns:globalization="using:Windows.Globalization"
|
xmlns:globalization="using:Windows.Globalization"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3"
|
xmlns:local="using:BetterLyrics.WinUI3"
|
||||||
xmlns:media="using:CommunityToolkit.WinUI.Media">
|
xmlns:media="using:CommunityToolkit.WinUI.Media">
|
||||||
@@ -13,8 +11,14 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<!-- Merged dictionaries here -->
|
<!-- Merged dictionaries here -->
|
||||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||||
|
|
||||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
|
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
|
||||||
<ResourceDictionary Source="ms-appx:///DevWinUI.Controls/Themes/Generic.xaml" />
|
<ResourceDictionary Source="ms-appx:///DevWinUI.Controls/Themes/Generic.xaml" />
|
||||||
|
|
||||||
|
<ResourceDictionary Source="/Styles/Converters.xaml" />
|
||||||
|
<ResourceDictionary Source="/Styles/InteractiveListViewHeaderStyle.xaml" />
|
||||||
|
<ResourceDictionary Source="/Styles/GhostSliderStyle.xaml" />
|
||||||
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<!-- Theme -->
|
<!-- Theme -->
|
||||||
@@ -42,45 +46,6 @@
|
|||||||
<ExponentialEase x:Key="EaseOut" EasingMode="EaseOut" />
|
<ExponentialEase x:Key="EaseOut" EasingMode="EaseOut" />
|
||||||
<ExponentialEase x:Key="EaseIn" EasingMode="EaseIn" />
|
<ExponentialEase x:Key="EaseIn" EasingMode="EaseIn" />
|
||||||
|
|
||||||
<!-- Converter -->
|
|
||||||
<converter:EnumToIntConverter x:Key="EnumToIntConverter" />
|
|
||||||
<converter:ColorToBrushConverter x:Key="ColorToBrushConverter" />
|
|
||||||
<converter:MatchedLocalFilesPathToVisibilityConverter x:Key="MatchedLocalFilesPathToVisibilityConverter" />
|
|
||||||
<converter:IntToCornerRadius x:Key="IntToCornerRadius" />
|
|
||||||
<converter:CornerRadiusToDoubleConverter x:Key="CornerRadiusToDoubleConverter" />
|
|
||||||
<converter:LyricsSearchProviderToDisplayNameConverter x:Key="LyricsSearchProviderToDisplayNameConverter" />
|
|
||||||
<converter:TranslationSearchProviderToDisplayNameConverter x:Key="TranslationSearchProviderToDisplayNameConverter" />
|
|
||||||
<converter:TransliterationSearchProviderToDisplayNameConverter x:Key="TransliterationSearchProviderToDisplayNameConverter" />
|
|
||||||
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
|
|
||||||
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
|
|
||||||
<converter:MillisecondsToFormattedTimeConverter x:Key="MillisecondsToFormattedTimeConverter" />
|
|
||||||
<converter:FPSToTimeSpanConverter x:Key="FPSToTimeSpanConverter" />
|
|
||||||
<converter:ShortcutToStringConverter x:Key="ShortcutToStringConverter" />
|
|
||||||
<converter:BoolNegationToVisibilityConverter x:Key="BoolNegationToVisibilityConverter" />
|
|
||||||
<converter:BoolToOpacityConverter x:Key="BoolToOpacityConverter" />
|
|
||||||
<converter:BoolToPartialOpacityConverter x:Key="BoolToPartialOpacityConverter" />
|
|
||||||
<converter:BoolNegationToOpacityConverter x:Key="BoolNegationToOpacityConverter" />
|
|
||||||
<converter:RectToMarginConverter x:Key="RectToMarginConverter" />
|
|
||||||
<converter:LanguageCodeToDisplayedNameConverter x:Key="LanguageCodeToDisplayedNameConverter" />
|
|
||||||
<converter:ByteArrayToImageConverter x:Key="ByteArrayToImageConverter" />
|
|
||||||
<converter:DisplayLanguageCodeToIndexConverter x:Key="DisplayLanguageCodeToIndexConverter" />
|
|
||||||
<converter:PathToParentFolderConverter x:Key="PathToParentFolderConverter" />
|
|
||||||
<converter:TrackToLyricsConverter x:Key="TrackToLyricsConverter" />
|
|
||||||
<converter:IntToBoolConverter x:Key="IntToBoolConverter" />
|
|
||||||
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
|
|
||||||
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
|
|
||||||
<converter:MillisecondsToSecondsConverter x:Key="MillisecondsToSecondsConverter" />
|
|
||||||
<converter:PictureInfosToImageSourceConverter x:Key="PictureInfosToImageSourceConverter" />
|
|
||||||
<converter:LyricsFontWeightToFontWeightConverter x:Key="LyricsFontWeightToFontWeightConverter" />
|
|
||||||
<converter:TextAlignmentTypeToHorizontalAlignmentConverter x:Key="TextAlignmentTypeToHorizontalAlignmentConverter" />
|
|
||||||
<converter:LyricsLayoutOrientationToOrientationConverter x:Key="LyricsLayoutOrientationToOrientationConverter" />
|
|
||||||
<converter:LyricsLayoutOrientationNegationToOrientationConverter x:Key="LyricsLayoutOrientationNegationToOrientationConverter" />
|
|
||||||
|
|
||||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
|
||||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
|
||||||
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
|
|
||||||
<converters:CollectionVisibilityConverter x:Key="CollectionVisibilityConverter" />
|
|
||||||
|
|
||||||
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
|
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
|
||||||
|
|
||||||
<!-- Style -->
|
<!-- Style -->
|
||||||
@@ -96,7 +61,7 @@
|
|||||||
<Setter Property="VerticalAlignment" Value="Top" />
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
<Setter Property="CornerRadius" Value="4" />
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="Padding" Value="14,6,14,9" />
|
<Setter Property="Padding" Value="16,9,16,9" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
||||||
@@ -108,7 +73,7 @@
|
|||||||
</Style>
|
</Style>
|
||||||
<Style
|
<Style
|
||||||
x:Key="TitleBarToggleButtonStyle"
|
x:Key="TitleBarToggleButtonStyle"
|
||||||
BasedOn="{StaticResource ToggleButtonRevealStyle}"
|
BasedOn="{StaticResource DefaultToggleButtonStyle}"
|
||||||
TargetType="ToggleButton">
|
TargetType="ToggleButton">
|
||||||
<Setter Property="VerticalAlignment" Value="Top" />
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
<Setter Property="CornerRadius" Value="4" />
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
@@ -116,7 +81,10 @@
|
|||||||
<Setter Property="Padding" Value="14,6,14,9" />
|
<Setter Property="Padding" Value="14,6,14,9" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">
|
<Style
|
||||||
|
x:Key="GhostToggleButtonStyle"
|
||||||
|
BasedOn="{StaticResource DefaultToggleButtonStyle}"
|
||||||
|
TargetType="ToggleButton">
|
||||||
<Setter Property="CornerRadius" Value="4" />
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
@@ -130,190 +98,6 @@
|
|||||||
<Setter Property="CornerRadius" Value="6" />
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="GhostSliderStyle" TargetType="Slider">
|
|
||||||
<Setter Property="Background" Value="{ThemeResource ControlStrokeColorOnAccentDefaultBrush}" />
|
|
||||||
<Setter Property="BorderThickness" Value="{ThemeResource SliderBorderThemeThickness}" />
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorPrimaryBrush}" />
|
|
||||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
|
||||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
|
||||||
<Setter Property="ManipulationMode" Value="None" />
|
|
||||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
|
||||||
<Setter Property="FocusVisualMargin" Value="-7,0,-7,0" />
|
|
||||||
<Setter Property="IsFocusEngagementEnabled" Value="True" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Slider">
|
|
||||||
<Grid Margin="{TemplateBinding Padding}">
|
|
||||||
<Grid.Resources>
|
|
||||||
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
|
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
|
||||||
<Setter Property="Background" Value="{ThemeResource TextFillColorPrimaryBrush}" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Thumb">
|
|
||||||
<Border
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="0,1,1,0" />
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Grid.Resources>
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="HeaderContentPresenter"
|
|
||||||
Grid.Row="0"
|
|
||||||
Margin="{ThemeResource SliderTopHeaderMargin}"
|
|
||||||
x:DeferLoadStrategy="Lazy"
|
|
||||||
Content="{TemplateBinding Header}"
|
|
||||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
|
||||||
FontWeight="{ThemeResource SliderHeaderThemeFontWeight}"
|
|
||||||
Foreground="{ThemeResource SliderHeaderForeground}"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<Grid
|
|
||||||
x:Name="SliderContainer"
|
|
||||||
Grid.Row="1"
|
|
||||||
Background="{ThemeResource SliderContainerBackground}"
|
|
||||||
Control.IsTemplateFocusTarget="True">
|
|
||||||
<Grid x:Name="HorizontalTemplate" MinHeight="{ThemeResource SliderHorizontalHeight}">
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="{ThemeResource SliderPreContentMargin}" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="{ThemeResource SliderPostContentMargin}" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Rectangle
|
|
||||||
x:Name="HorizontalTrackRect"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Height="2"
|
|
||||||
Fill="{TemplateBinding Background}" />
|
|
||||||
<Rectangle
|
|
||||||
x:Name="HorizontalDecreaseRect"
|
|
||||||
Grid.Row="1"
|
|
||||||
Fill="{TemplateBinding Foreground}" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="TopTickBar"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
|
||||||
Margin="0,0,0,4"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Fill="{ThemeResource SliderTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="HorizontalInlineTickBar"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Height="2"
|
|
||||||
Fill="{ThemeResource SliderInlineTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="BottomTickBar"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
|
||||||
Margin="0,4,0,0"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Fill="{ThemeResource SliderTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<Thumb
|
|
||||||
x:Name="HorizontalThumb"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="2"
|
|
||||||
Height="2"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
DataContext="{TemplateBinding Value}"
|
|
||||||
FocusVisualMargin="-14,-6,-14,-6"
|
|
||||||
Style="{StaticResource SliderThumbStyle}" />
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
x:Name="VerticalTemplate"
|
|
||||||
MinWidth="{ThemeResource SliderVerticalWidth}"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="{ThemeResource SliderPreContentMargin}" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="{ThemeResource SliderPostContentMargin}" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Rectangle
|
|
||||||
x:Name="VerticalTrackRect"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="{ThemeResource SliderTrackThemeHeight}"
|
|
||||||
Fill="{TemplateBinding Background}" />
|
|
||||||
<Rectangle
|
|
||||||
x:Name="VerticalDecreaseRect"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.Column="1"
|
|
||||||
Fill="{TemplateBinding Foreground}" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="LeftTickBar"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
|
||||||
Margin="0,0,4,0"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Fill="{ThemeResource SliderTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="VerticalInlineTickBar"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="{ThemeResource SliderTrackThemeHeight}"
|
|
||||||
Fill="{ThemeResource SliderInlineTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<TickBar
|
|
||||||
x:Name="RightTickBar"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
|
||||||
Margin="4,0,0,0"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
Fill="{ThemeResource SliderTickBarFill}"
|
|
||||||
Visibility="Collapsed" />
|
|
||||||
<Thumb
|
|
||||||
x:Name="VerticalThumb"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Width="24"
|
|
||||||
Height="8"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
DataContext="{TemplateBinding Value}"
|
|
||||||
FocusVisualMargin="-6,-14,-6,-14"
|
|
||||||
Style="{StaticResource SliderThumbStyle}" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="ListViewStretchedItemContainerStyle"
|
x:Key="ListViewStretchedItemContainerStyle"
|
||||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||||
@@ -357,10 +141,6 @@
|
|||||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
|
|
||||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
|
|
||||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />
|
|
||||||
|
|
||||||
<!-- Dimensions -->
|
<!-- Dimensions -->
|
||||||
|
|
||||||
<!-- Fonts -->
|
<!-- Fonts -->
|
||||||
|
|||||||
@@ -1,47 +1,57 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Helper;
|
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
|
using BetterLyrics.WinUI3.Models.DbContext;
|
||||||
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
|
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
|
||||||
using BetterLyrics.WinUI3.Services.DiscordService;
|
using BetterLyrics.WinUI3.Services.DiscordService;
|
||||||
|
using BetterLyrics.WinUI3.Services.FileSystemService;
|
||||||
|
using BetterLyrics.WinUI3.Services.GSMTCService;
|
||||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||||
using BetterLyrics.WinUI3.Services.LibWatcherService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
|
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
||||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
using BetterLyrics.WinUI3.Services.PlayHistoryService;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.PluginService;
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
using BetterLyrics.WinUI3.Services.SMTCService;
|
||||||
|
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||||
|
using BetterLyrics.WinUI3.Services.TranslationService;
|
||||||
|
using BetterLyrics.WinUI3.Services.TransliterationService;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.ApplicationModel.Resources;
|
using Microsoft.Windows.AppLifecycle; // 关键:App生命周期管理
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Vanara.PInvoke;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3
|
namespace BetterLyrics.WinUI3
|
||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
|
private Window? m_window;
|
||||||
private readonly ILogger<App> _logger;
|
private readonly ILogger<App> _logger;
|
||||||
|
|
||||||
public static new App Current => (App)Application.Current;
|
public static new App Current => (App)Application.Current;
|
||||||
|
|
||||||
private static Mutex? _instanceMutex;
|
private readonly string _appKey = Windows.ApplicationModel.Package.Current.Id.FamilyName;
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
this.InitializeComponent();
|
// Must be done before InitializeComponent
|
||||||
|
if (!TryHandleSingleInstance())
|
||||||
|
{
|
||||||
|
// 如果移交成功直接退出当前进程
|
||||||
|
Environment.Exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EnsureSingleInstance();
|
this.InitializeComponent();
|
||||||
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
PathHelper.EnsureDirectories();
|
PathHelper.EnsureDirectories();
|
||||||
@@ -49,29 +59,106 @@ namespace BetterLyrics.WinUI3
|
|||||||
|
|
||||||
_logger = Ioc.Default.GetRequiredService<ILogger<App>>();
|
_logger = Ioc.Default.GetRequiredService<ILogger<App>>();
|
||||||
|
|
||||||
|
// 注册全局异常捕获
|
||||||
UnhandledException += App_UnhandledException;
|
UnhandledException += App_UnhandledException;
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||||
AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
|
AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
|
||||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureSingleInstance()
|
/// <summary>
|
||||||
|
/// 处理单实例逻辑。
|
||||||
|
/// 返回 true 表示我是主实例,继续运行。
|
||||||
|
/// 返回 false 表示我是第二个实例,已通知主实例,我应该退出。
|
||||||
|
/// </summary>
|
||||||
|
private bool TryHandleSingleInstance()
|
||||||
{
|
{
|
||||||
_instanceMutex = new Mutex(true, Constants.App.AppName, out bool createdNew);
|
// 尝试查找或注册当前实例
|
||||||
|
var mainInstance = AppInstance.FindOrRegisterForKey(_appKey);
|
||||||
|
|
||||||
if (!createdNew)
|
// 如果当前实例就是注册的那个主实例
|
||||||
|
if (mainInstance.IsCurrent)
|
||||||
{
|
{
|
||||||
User32.MessageBox(HWND.NULL, new ResourceLoader().GetString("TryRunMultipleInstance"), null, User32.MB_FLAGS.MB_APPLMODAL);
|
// 监听 "Activated" 事件。
|
||||||
Environment.Exit(0);
|
// 当第二个实例启动并重定向过来时,这个事件会被触发。
|
||||||
|
mainInstance.Activated += OnMainInstanceActivated;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 我不是主实例,我是后来者。
|
||||||
|
// 获取当前实例的激活参数(比如是通过文件双击打开的,这里能拿到文件路径)
|
||||||
|
var args = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||||
|
|
||||||
|
// 将激活请求重定向给主实例
|
||||||
|
// 注意:这里是同步等待,确保发送成功后再退出
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mainInstance.RedirectActivationToAsync(args).AsTask().Wait();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// 即使重定向失败,作为第二个实例也应该退出
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
/// <summary>
|
||||||
|
/// 当第二个实例试图启动时,主实例会收到此回调
|
||||||
|
/// </summary>
|
||||||
|
private void OnMainInstanceActivated(object? sender, AppActivationArguments e)
|
||||||
{
|
{
|
||||||
|
// 这个事件是在后台线程触发的,必须切回 UI 线程操作窗口
|
||||||
|
m_window?.DispatcherQueue.TryEnqueue(() =>
|
||||||
|
{
|
||||||
|
HandleActivation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 唤醒逻辑
|
||||||
|
/// </summary>
|
||||||
|
private void HandleActivation()
|
||||||
|
{
|
||||||
|
WindowHook.OpenOrShowWindow<LyricsWindowSwitchWindow>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||||
|
{
|
||||||
|
await InitDatabasesAsync();
|
||||||
|
|
||||||
var settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
var settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
|
|
||||||
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
// Migrate MappedSongSearchQueries
|
||||||
|
var songSearchMapService = Ioc.Default.GetRequiredService<ISongSearchMapService>();
|
||||||
|
var obsoleteSongSearchMap = settingsService.AppSettings.MappedSongSearchQueries;
|
||||||
|
if (obsoleteSongSearchMap.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var item in obsoleteSongSearchMap)
|
||||||
|
{
|
||||||
|
await songSearchMapService.SaveMappingAsync(item);
|
||||||
|
}
|
||||||
|
obsoleteSongSearchMap.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start scan tasks in background
|
||||||
|
var fileSystemService = Ioc.Default.GetRequiredService<IFileSystemService>();
|
||||||
|
|
||||||
|
foreach (var item in settingsService.AppSettings.LocalMediaFolders)
|
||||||
|
{
|
||||||
|
if (item.LastSyncTime == null)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () => await fileSystemService.ScanMediaFolderAsync(item, CancellationToken.None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileSystemService.StartAllFolderTimers();
|
||||||
|
|
||||||
|
// Init system tray
|
||||||
|
m_window = WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
||||||
|
|
||||||
|
// Open lyrics window if set
|
||||||
if (settingsService.AppSettings.GeneralSettings.AutoStartLyricsWindow)
|
if (settingsService.AppSettings.GeneralSettings.AutoStartLyricsWindow)
|
||||||
{
|
{
|
||||||
var defaultStatus = settingsService.AppSettings.WindowBoundsRecords.Where(x => x.IsDefault);
|
var defaultStatus = settingsService.AppSettings.WindowBoundsRecords.Where(x => x.IsDefault);
|
||||||
@@ -87,37 +174,83 @@ namespace BetterLyrics.WinUI3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open music gallery if set
|
||||||
if (settingsService.AppSettings.MusicGallerySettings.AutoOpen)
|
if (settingsService.AppSettings.MusicGallerySettings.AutoOpen)
|
||||||
{
|
{
|
||||||
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task InitDatabasesAsync()
|
||||||
|
{
|
||||||
|
// Init databases
|
||||||
|
var playHistoryFactory = Ioc.Default.GetRequiredService<IDbContextFactory<PlayHistoryDbContext>>();
|
||||||
|
var songSearchMapFactory = Ioc.Default.GetRequiredService<IDbContextFactory<SongSearchMapDbContext>>();
|
||||||
|
var filesIndexFactory = Ioc.Default.GetRequiredService<IDbContextFactory<FilesIndexDbContext>>();
|
||||||
|
var lyricsCacheFactory = Ioc.Default.GetRequiredService<IDbContextFactory<LyricsCacheDbContext>>();
|
||||||
|
|
||||||
|
using (var playHistoryDb = await playHistoryFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
await playHistoryDb.Database.EnsureCreatedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var songSearchMapDb = await songSearchMapFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
await songSearchMapDb.Database.EnsureCreatedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var filesIndexDb = await filesIndexFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
await filesIndexDb.Database.EnsureCreatedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var lyricsCacheDb = await lyricsCacheFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
await lyricsCacheDb.Database.EnsureCreatedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void ConfigureServices()
|
private static void ConfigureServices()
|
||||||
{
|
{
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Verbose)
|
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Verbose)
|
||||||
|
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", Serilog.Events.LogEventLevel.Error)
|
||||||
.WriteTo.File(PathHelper.LogFilePattern, rollingInterval: RollingInterval.Day)
|
.WriteTo.File(PathHelper.LogFilePattern, rollingInterval: RollingInterval.Day)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
// Register services
|
|
||||||
Ioc.Default.ConfigureServices(
|
Ioc.Default.ConfigureServices(
|
||||||
new ServiceCollection()
|
new ServiceCollection()
|
||||||
|
// 数据库工厂
|
||||||
|
.AddDbContextFactory<PlayHistoryDbContext>(options => options.UseSqlite($"Data Source={PathHelper.PlayHistoryPath}"))
|
||||||
|
.AddDbContextFactory<FilesIndexDbContext>(options => options.UseSqlite($"Data Source={PathHelper.FilesIndexPath}"))
|
||||||
|
.AddDbContextFactory<LyricsCacheDbContext>(options => options.UseSqlite($"Data Source={PathHelper.LyricsCachePath}"))
|
||||||
|
.AddDbContextFactory<SongSearchMapDbContext>(options => options.UseSqlite($"Data Source={PathHelper.SongSearchMapPath}"))
|
||||||
|
|
||||||
|
// 日志
|
||||||
.AddLogging(loggingBuilder =>
|
.AddLogging(loggingBuilder =>
|
||||||
{
|
{
|
||||||
loggingBuilder.ClearProviders();
|
loggingBuilder.ClearProviders();
|
||||||
loggingBuilder.AddSerilog();
|
loggingBuilder.AddSerilog();
|
||||||
})
|
})
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
.AddSingleton<ISettingsService, SettingsService>()
|
.AddSingleton<ISettingsService, SettingsService>()
|
||||||
.AddSingleton<IMediaSessionsService, MediaSessionsService>()
|
.AddSingleton<ISMTCService, SMTCService>()
|
||||||
|
.AddSingleton<IGSMTCService, GSMTCService>()
|
||||||
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
|
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
|
||||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
.AddSingleton<ITranslationService, TranslationService>()
|
||||||
.AddSingleton<ITranslateService, TranslateService>()
|
.AddSingleton<ITransliterationService, TransliterationService>()
|
||||||
.AddSingleton<ILastFMService, LastFMService>()
|
.AddSingleton<ILastFMService, LastFMService>()
|
||||||
.AddSingleton<IResourceService, ResourceService>()
|
|
||||||
.AddSingleton<IDiscordService, DiscordService>()
|
.AddSingleton<IDiscordService, DiscordService>()
|
||||||
|
.AddSingleton<ILocalizationService, LocalizationService>()
|
||||||
|
.AddSingleton<IFileSystemService, FileSystemService>()
|
||||||
|
.AddSingleton<IPlayHistoryService, PlayHistoryService>()
|
||||||
|
.AddSingleton<ILyricsCacheService, LyricsCacheService>()
|
||||||
|
.AddSingleton<ISongSearchMapService, SongSearchMapService>()
|
||||||
|
.AddSingleton<IPluginService, PluginService>()
|
||||||
|
|
||||||
// ViewModels
|
// ViewModels
|
||||||
.AddSingleton<AppSettingsControlViewModel>()
|
.AddSingleton<AppSettingsControlViewModel>()
|
||||||
.AddSingleton<PlaybackSettingsControlViewModel>()
|
.AddSingleton<PlaybackSettingsControlViewModel>()
|
||||||
@@ -132,6 +265,8 @@ namespace BetterLyrics.WinUI3
|
|||||||
.AddSingleton<MusicGalleryPageViewModel>()
|
.AddSingleton<MusicGalleryPageViewModel>()
|
||||||
.AddSingleton<AboutControlViewModel>()
|
.AddSingleton<AboutControlViewModel>()
|
||||||
.AddSingleton<MusicGalleryWindowViewModel>()
|
.AddSingleton<MusicGalleryWindowViewModel>()
|
||||||
|
.AddSingleton<StatsDashboardControlViewModel>()
|
||||||
|
.AddSingleton<PlayQueueViewModel>()
|
||||||
|
|
||||||
.AddTransient<NowPlayingWindowViewModel>()
|
.AddTransient<NowPlayingWindowViewModel>()
|
||||||
.AddTransient<NowPlayingPageViewModel>()
|
.AddTransient<NowPlayingPageViewModel>()
|
||||||
@@ -149,7 +284,8 @@ namespace BetterLyrics.WinUI3
|
|||||||
|
|
||||||
private void CurrentDomain_FirstChanceException(object? sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
|
private void CurrentDomain_FirstChanceException(object? sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e.Exception, "CurrentDomain_FirstChanceException");
|
// FirstChance 异常非常多(比如内部 try-catch 也会触发),通常建议只在 Debug 模式记录,或者过滤特定类型
|
||||||
|
// _logger.LogError(e.Exception, "CurrentDomain_FirstChanceException");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
|
private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
|
||||||
|
|||||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 593 KiB |
@@ -10,13 +10,20 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
|
<AppxDefaultResourceQualifiers>Language=ar;de;en;es;fr;hi;id;ja;ko;ms;pt;ru;th;vi;zh-Hans;zh-Hant;</AppxDefaultResourceQualifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Remove="TemplateSelector\**" />
|
||||||
<Compile Remove="ViewModels\Lyrics\**" />
|
<Compile Remove="ViewModels\Lyrics\**" />
|
||||||
|
<Content Remove="TemplateSelector\**" />
|
||||||
<Content Remove="ViewModels\Lyrics\**" />
|
<Content Remove="ViewModels\Lyrics\**" />
|
||||||
|
<EmbeddedResource Remove="TemplateSelector\**" />
|
||||||
<EmbeddedResource Remove="ViewModels\Lyrics\**" />
|
<EmbeddedResource Remove="ViewModels\Lyrics\**" />
|
||||||
|
<None Remove="TemplateSelector\**" />
|
||||||
<None Remove="ViewModels\Lyrics\**" />
|
<None Remove="ViewModels\Lyrics\**" />
|
||||||
|
<Page Remove="TemplateSelector\**" />
|
||||||
<Page Remove="ViewModels\Lyrics\**" />
|
<Page Remove="ViewModels\Lyrics\**" />
|
||||||
|
<PRIResource Remove="TemplateSelector\**" />
|
||||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -35,11 +42,17 @@
|
|||||||
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
||||||
<None Remove="Controls\MediaSettingsControl.xaml" />
|
<None Remove="Controls\MediaSettingsControl.xaml" />
|
||||||
<None Remove="Controls\NowPlayingBar.xaml" />
|
<None Remove="Controls\NowPlayingBar.xaml" />
|
||||||
|
<None Remove="Controls\PatronControl.xaml" />
|
||||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||||
|
<None Remove="Controls\PlayQueue.xaml" />
|
||||||
<None Remove="Controls\PropertyRow.xaml" />
|
<None Remove="Controls\PropertyRow.xaml" />
|
||||||
|
<None Remove="Controls\RemoteServerConfigControl.xaml" />
|
||||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||||
|
<None Remove="Controls\StatsDashboardControl.xaml" />
|
||||||
<None Remove="Controls\SystemTray.xaml" />
|
<None Remove="Controls\SystemTray.xaml" />
|
||||||
<None Remove="Controls\WindowSettingsControl.xaml" />
|
<None Remove="Controls\WindowSettingsControl.xaml" />
|
||||||
|
<None Remove="Styles\GhostSliderStyle.xaml" />
|
||||||
|
<None Remove="Styles\InteractiveListViewHeaderStyle.xaml" />
|
||||||
<None Remove="Views\LyricsSearchWindow.xaml" />
|
<None Remove="Views\LyricsSearchWindow.xaml" />
|
||||||
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
||||||
<None Remove="Views\MusicGalleryPage.xaml" />
|
<None Remove="Views\MusicGalleryPage.xaml" />
|
||||||
@@ -58,36 +71,45 @@
|
|||||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251021-build.2365" />
|
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251021-build.2365" />
|
||||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250703-build.2173" />
|
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250703-build.2173" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.251219" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.251219" />
|
||||||
|
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.2.251219" />
|
||||||
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
|
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
|
||||||
<PackageReference Include="csharp-kana" Version="1.0.2" />
|
|
||||||
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
||||||
<PackageReference Include="DevWinUI.Controls" Version="9.7.1" />
|
<PackageReference Include="DevWinUI.Controls" Version="9.8.1" />
|
||||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.6" />
|
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.6" />
|
||||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
|
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
|
||||||
<PackageReference Include="FlaUI.UIA3" Version="5.0.0" />
|
<PackageReference Include="FlaUI.UIA3" Version="5.0.0" />
|
||||||
|
<PackageReference Include="FluentFTP" Version="53.0.2" />
|
||||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
||||||
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
||||||
<PackageReference Include="Interop.UIAutomationClient" Version="10.19041.0" />
|
<PackageReference Include="Interop.UIAutomationClient" Version="10.19041.0" />
|
||||||
|
<PackageReference Include="LiveChartsCore.SkiaSharpView.WinUI" Version="2.0.0-rc6.1" />
|
||||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.1">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
|
||||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||||
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
||||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
|
||||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
|
||||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||||
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
|
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
|
<PackageReference Include="SMBLibrary" Version="1.5.5.1" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="10.0.1" />
|
<PackageReference Include="System.Drawing.Common" Version="10.0.1" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.1" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.1" />
|
||||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||||
@@ -98,10 +120,12 @@
|
|||||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
||||||
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
||||||
<PackageReference Include="VCollab.DiscordRichPresence" Version="1.7.0" />
|
<PackageReference Include="VCollab.DiscordRichPresence" Version="1.7.0" />
|
||||||
|
<PackageReference Include="WebDav.Client" Version="2.9.0" />
|
||||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||||
<PackageReference Include="z440.atl.core" Version="7.9.0" />
|
<PackageReference Include="z440.atl.core" Version="7.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\BetterLyrics.Core\BetterLyrics.Core.csproj" />
|
||||||
<ProjectReference Include="..\..\ColorThief.WinUI3\ColorThief.WinUI3.csproj" />
|
<ProjectReference Include="..\..\ColorThief.WinUI3\ColorThief.WinUI3.csproj" />
|
||||||
<ProjectReference Include="..\..\Impressionist\Impressionist\Impressionist.csproj" />
|
<ProjectReference Include="..\..\Impressionist\Impressionist\Impressionist.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -119,6 +143,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TrimmerRootAssembly Include="FlaUI.UIA3" />
|
<TrimmerRootAssembly Include="FlaUI.UIA3" />
|
||||||
<TrimmerRootAssembly Include="Interop.UIAutomationClient" />
|
<TrimmerRootAssembly Include="Interop.UIAutomationClient" />
|
||||||
|
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore.Abstractions" />
|
||||||
|
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore.Relational" />
|
||||||
|
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||||
<TrimmerRootAssembly Include="NAudio.Wasapi" />
|
<TrimmerRootAssembly Include="NAudio.Wasapi" />
|
||||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||||
<TrimmerRootAssembly Include="Vanara.PInvoke.DwmApi" />
|
<TrimmerRootAssembly Include="Vanara.PInvoke.DwmApi" />
|
||||||
@@ -161,6 +189,9 @@
|
|||||||
<Content Update="Assets\EmptyState.png">
|
<Content Update="Assets\EmptyState.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Update="Assets\Folder.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Update="Assets\foobar2000.png">
|
<Content Update="Assets\foobar2000.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -203,6 +234,9 @@
|
|||||||
<Content Update="Assets\NetEaseCloudMusic.png">
|
<Content Update="Assets\NetEaseCloudMusic.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Update="Assets\OriginalSoundHQPlayer.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Update="Assets\Page.png">
|
<Content Update="Assets\Page.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -218,6 +252,9 @@
|
|||||||
<Content Update="Assets\Question.png">
|
<Content Update="Assets\Question.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Update="Assets\RevolvingHearts.gif">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Update="Assets\SaltPlayerForWindows.png">
|
<Content Update="Assets\SaltPlayerForWindows.png">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -234,6 +271,31 @@
|
|||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\PatronControl.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Styles\GhostSliderStyle.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Styles\Converters.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\PlayQueue.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\StatsDashboardControl.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Controls\FontFamilyAutoSuggestBox.xaml">
|
<Page Update="Controls\FontFamilyAutoSuggestBox.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@@ -335,12 +397,9 @@
|
|||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PRIResource Update="Strings\en-US\Resources.resw">
|
<Page Update="Controls\RemoteServerConfigControl.xaml">
|
||||||
<Generator></Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</PRIResource>
|
</Page>
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="TemplateSelector\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Controls\NowPlayingBar.xaml">
|
<Page Update="Controls\NowPlayingBar.xaml">
|
||||||
@@ -382,6 +441,11 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Styles\InteractiveListViewHeaderStyle.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<!-- Publish Properties -->
|
<!-- Publish Properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
public const string AuthorGitHub = "https://github.com/jayfunc";
|
public const string AuthorGitHub = "https://github.com/jayfunc";
|
||||||
|
|
||||||
|
public const string Crowdin = "https://crowdin.com/project/betterlyrics/invite?h=413bb0df7afa420247a98fefdae5e12c2647410";
|
||||||
|
|
||||||
public const string BetterLyricsGitHub = $"{AuthorGitHub}/BetterLyrics";
|
public const string BetterLyricsGitHub = $"{AuthorGitHub}/BetterLyrics";
|
||||||
|
|
||||||
public const string ShareHub = $"{BetterLyricsGitHub}/blob/dev/ShareHub/index.md";
|
public const string ShareHub = $"{BetterLyricsGitHub}/blob/dev/ShareHub/index.md";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace BetterLyrics.WinUI3.Constants
|
namespace BetterLyrics.WinUI3.Constants
|
||||||
{
|
{
|
||||||
public static class PlayerID
|
public static class PlayerId
|
||||||
{
|
{
|
||||||
public const string LXMusic = "cn.toside.music.desktop";
|
public const string LXMusic = "cn.toside.music.desktop";
|
||||||
public const string LXMusicPortable = "lx-music-desktop.exe";
|
public const string LXMusicPortable = "lx-music-desktop.exe";
|
||||||
@@ -25,5 +25,6 @@
|
|||||||
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
||||||
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
||||||
public const string Listen1 = "com.listen1.listen1";
|
public const string Listen1 = "com.listen1.listen1";
|
||||||
|
public const string OriginalSoundHQPlayer = "SennpaiStudio.528762A6196EF_z79ft30j24epr!App";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,5 +24,6 @@
|
|||||||
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows (Steam)";
|
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows (Steam)";
|
||||||
public const string MoeKoeMusic = "MoeKoe Music";
|
public const string MoeKoeMusic = "MoeKoe Music";
|
||||||
public const string Listen1 = "Listen 1";
|
public const string Listen1 = "Listen 1";
|
||||||
|
public const string OriginalSoundHQPlayer = "Original Sound HQ Player";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,7 @@ namespace BetterLyrics.WinUI3.Constants
|
|||||||
{
|
{
|
||||||
public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(250);
|
public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(250);
|
||||||
public static readonly TimeSpan AnimationDuration = TimeSpan.FromMilliseconds(350);
|
public static readonly TimeSpan AnimationDuration = TimeSpan.FromMilliseconds(350);
|
||||||
|
public static readonly TimeSpan LongAnimationDuration = TimeSpan.FromMilliseconds(650);
|
||||||
|
public static readonly TimeSpan WaitingDuration = TimeSpan.FromMilliseconds(300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,12 +57,12 @@
|
|||||||
|
|
||||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.BetterLyricsGitHub}" />
|
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.BetterLyricsGitHub}" />
|
||||||
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
|
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
|
||||||
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
||||||
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
|
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
|
||||||
</StackPanel>
|
</dev:WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
@@ -70,18 +70,18 @@
|
|||||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<TextBlock x:Uid="SetingsPageFeedback" />
|
<TextBlock x:Uid="SetingsPageFeedback" />
|
||||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
<HyperlinkButton Content="QQ 反馈交流群" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
||||||
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.Discord}" />
|
<HyperlinkButton Content="Discord" NavigateUri="{x:Bind const:Link.Discord}" />
|
||||||
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
<HyperlinkButton Content="Telegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
||||||
</StackPanel>
|
</dev:WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<TextBlock x:Uid="SetingsPageDonation" />
|
<TextBlock x:Uid="SetingsPageDonation" />
|
||||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="{x:Bind const:Link.BuyMeACoffee}" />
|
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="{x:Bind const:Link.BuyMeACoffee}" />
|
||||||
<HyperlinkButton Content="PayPal" NavigateUri="{x:Bind const:Link.PayPal}" />
|
<HyperlinkButton Content="PayPal" NavigateUri="{x:Bind const:Link.PayPal}" />
|
||||||
<HyperlinkButton
|
<HyperlinkButton
|
||||||
@@ -117,24 +117,23 @@
|
|||||||
</HyperlinkButton.ContextFlyout>
|
</HyperlinkButton.ContextFlyout>
|
||||||
</HyperlinkButton>
|
</HyperlinkButton>
|
||||||
<HyperlinkButton Content="爱发电" NavigateUri="{x:Bind const:Link.Afdian}" />
|
<HyperlinkButton Content="爱发电" NavigateUri="{x:Bind const:Link.Afdian}" />
|
||||||
</StackPanel>
|
</dev:WrapPanel>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
<Grid ColumnSpacing="6">
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="*" />
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Text="*" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Uid="SetingsPageThanks"
|
x:Uid="SetingsPageThanks"
|
||||||
|
Grid.Column="1"
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageThanksList">
|
|
||||||
<Button
|
|
||||||
Click="Patron_Click"
|
|
||||||
Content="{ui:FontIcon FontSize=16,
|
|
||||||
FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
@@ -149,6 +148,108 @@
|
|||||||
</dev:SettingsExpander.ItemsFooter>
|
</dev:SettingsExpander.ItemsFooter>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsExpander x:Uid="SettingsPageThanksList">
|
||||||
|
<dev:SettingsExpander.HeaderIcon>
|
||||||
|
<ImageIcon Source="ms-appx:///Assets/RevolvingHearts.gif" />
|
||||||
|
</dev:SettingsExpander.HeaderIcon>
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
|
||||||
|
<!-- 贡献者 -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<RichTextBlock>
|
||||||
|
<Paragraph>
|
||||||
|
<Run x:Uid="SetingsPageContributors" />
|
||||||
|
<Run Text="-" />
|
||||||
|
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Code" />
|
||||||
|
</Paragraph>
|
||||||
|
</RichTextBlock>
|
||||||
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
|
<HyperlinkButton Content="jayfunc" NavigateUri="https://github.com/jayfunc" />
|
||||||
|
<HyperlinkButton Content="Raspberry-Monster" NavigateUri="https://github.com/Raspberry-Monster" />
|
||||||
|
<HyperlinkButton Content="ZHider" NavigateUri="https://github.com/ZHider" />
|
||||||
|
<HyperlinkButton Content="kusutori" NavigateUri="https://github.com/kusutori" />
|
||||||
|
</dev:WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 贡献者 (Translator) -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<RichTextBlock>
|
||||||
|
<Paragraph>
|
||||||
|
<Run x:Uid="SetingsPageContributors" />
|
||||||
|
<Run Text="-" />
|
||||||
|
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Translator" />
|
||||||
|
</Paragraph>
|
||||||
|
</RichTextBlock>
|
||||||
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
|
<HyperlinkButton Content="borcolasky" NavigateUri="https://crowdin.com/profile/borcolasky" />
|
||||||
|
<HyperlinkButton Content="SuHeAndZl" NavigateUri="https://crowdin.com/profile/SuHeAndZl" />
|
||||||
|
</dev:WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 赞助 -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<TextBlock x:Uid="SettingsPagePatrons" />
|
||||||
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
|
<uc:PatronControl Date="Jan 8, 2026" PatronName="Eureka-K_K" />
|
||||||
|
<uc:PatronControl Date="Jan 3, 2026" PatronName="**轩" />
|
||||||
|
<uc:PatronControl Date="Dec 13, 2025" PatronName="<Anonymous>" />
|
||||||
|
<uc:PatronControl Date="Dec 3, 2025" PatronName="YE" />
|
||||||
|
<uc:PatronControl Date="Dec 2, 2025" PatronName="<Anonymous>" />
|
||||||
|
<uc:PatronControl Date="Nov 23, 2025" PatronName="**玄" />
|
||||||
|
<uc:PatronControl Date="Nov 21, 2025" PatronName="**智" />
|
||||||
|
<uc:PatronControl Date="Nov 17, 2025" PatronName="SuHeAndZl" />
|
||||||
|
<uc:PatronControl Date="Nov 2, 2025" PatronName="借过" />
|
||||||
|
<uc:PatronControl Date="Aug 28, 2025" PatronName="**华" />
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageUserWhoPurchased"
|
||||||
|
Margin="12,8"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||||
|
</dev:WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 特别鸣谢 -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<TextBlock x:Uid="SetingsPageSpecialThanks" />
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageYouNowUsing"
|
||||||
|
Margin="0,8"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 代码参考 -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<TextBlock x:Uid="SetingsPageDeps" />
|
||||||
|
<HyperlinkButton Margin="-12,0,0,0" NavigateUri="https://github.com/jayfunc/BetterLyrics/network/dependencies">
|
||||||
|
<TextBlock x:Uid="SetingsPageDeps" />
|
||||||
|
</HyperlinkButton>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- UI/UX 参考 -->
|
||||||
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<TextBlock x:Uid="SetingsPageUIUXRef" />
|
||||||
|
<dev:WrapPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
|
<HyperlinkButton Content="refined-now-playing-netease" NavigateUri="https://github.com/solstice23/refined-now-playing-netease" />
|
||||||
|
<HyperlinkButton Content="Lyricify" NavigateUri="https://github.com/WXRIW/Lyricify-App" />
|
||||||
|
<HyperlinkButton Content="椒盐音乐 Salt Player" NavigateUri="https://moriafly.com/program/salt-player" />
|
||||||
|
<HyperlinkButton Content="MyToolBar" NavigateUri="https://github.com/TwilightLemon/MyToolBar" />
|
||||||
|
</dev:WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageMockMusicPlaying">
|
<dev:SettingsCard x:Uid="SettingsPageMockMusicPlaying">
|
||||||
<HyperlinkButton x:Uid="SettingsPagePlayingMockMusicButton" NavigateUri="https://soundcloud.com/carlyraejepsen/cut-to-the-feeling" />
|
<HyperlinkButton x:Uid="SettingsPagePlayingMockMusicButton" NavigateUri="https://soundcloud.com/carlyraejepsen/cut-to-the-feeling" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
@@ -195,194 +296,28 @@
|
|||||||
Value="{x:Bind ViewModel.AppSettings.AdvancedSettings.FPS, Mode=TwoWay}" />
|
Value="{x:Bind ViewModel.AppSettings.AdvancedSettings.FPS, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<Grid
|
|
||||||
x:Name="CreditsReel"
|
|
||||||
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
|
|
||||||
Opacity="0"
|
|
||||||
SizeChanged="CreditsReel_SizeChanged"
|
|
||||||
Tapped="CreditsReel_Tapped"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<Grid.OpacityTransition>
|
|
||||||
<ScalarTransition />
|
|
||||||
</Grid.OpacityTransition>
|
|
||||||
<ScrollViewer
|
|
||||||
x:Name="CreditsReelScrollViewer"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
|
||||||
ScrollViewer.VerticalScrollMode="Disabled">
|
|
||||||
<RichTextBlock
|
<RichTextBlock
|
||||||
|
Margin="0,16,0,0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
|
||||||
HorizontalTextAlignment="Center"
|
HorizontalTextAlignment="Center"
|
||||||
LineHeight="28"
|
LineHeight="28">
|
||||||
PointerEntered="RichTextBlock_PointerEntered"
|
<Paragraph FontWeight="Bold">
|
||||||
PointerExited="RichTextBlock_PointerExited">
|
|
||||||
|
|
||||||
<Paragraph x:Name="CreditsReelHeader" />
|
|
||||||
|
|
||||||
<!-- 贡献者 -->
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run x:Uid="SetingsPageContributors" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/jayfunc">
|
|
||||||
<Run Text="jayfunc" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/Raspberry-Monster">
|
|
||||||
<Run Text="Raspberry-Monster" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/ZHider">
|
|
||||||
<Run Text="ZHider" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/kusutori">
|
|
||||||
<Run Text="kusutori" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
|
|
||||||
<!-- 赞助 -->
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run x:Uid="SettingsPagePatrons" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="YE" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Dec 3, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="**玄" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 23, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="**智" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 21, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="*鹤" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 17, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="借过" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 2, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="**华" />
|
|
||||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Aug 28, 2025" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run x:Uid="SettingsPageUserWhoPurchased" />
|
|
||||||
</Paragraph>
|
|
||||||
|
|
||||||
<!-- 特别鸣谢 -->
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run x:Uid="SetingsPageSpecialThanks" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run x:Uid="SettingsPageYouNowUsing" />
|
|
||||||
</Paragraph>
|
|
||||||
|
|
||||||
<!-- 代码参考 -->
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run x:Uid="SetingsPageDeps" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce">
|
|
||||||
<Run Text="Get album artwork from ITunes (with Python3 or C#)" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://stackoverflow.com/a/32013610/11048731">
|
|
||||||
<Run Text="FullyObservableCollection" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/Impressionist">
|
|
||||||
<Run Text="Impressionist" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/ColorThief.WinUI3">
|
|
||||||
<Run Text="ColorThief.WinUI3" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/Johnwikix/SpectrumVisualization">
|
|
||||||
<Run Text="SpectrumVisualization" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://www.shadertoy.com/view/Mdt3Df">
|
|
||||||
<Run Text="Snow (as shown in sweden)" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://www.shadertoy.com/view/lllSR2">
|
|
||||||
<Run Text="w10" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/mo-jinran/Taskbar-Lyrics">
|
|
||||||
<Run Text="Taskbar-Lyrics" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/jayfunc/BetterLyrics/network/dependencies">
|
|
||||||
<Run Text="..." />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
|
|
||||||
<!-- UI/UX 设计参考 -->
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run x:Uid="SetingsPageUIUXRef" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/solstice23/refined-now-playing-netease">
|
|
||||||
<Run Text="refined-now-playing-netease" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/WXRIW/Lyricify-App">
|
|
||||||
<Run Text="Lyricify" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://moriafly.com/program/salt-player">
|
|
||||||
<Run Text="椒盐音乐 Salt Player" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="https://github.com/TwilightLemon/MyToolBar">
|
|
||||||
<Run Text="MyToolBar" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Hyperlink NavigateUri="">
|
|
||||||
<Run Text="" />
|
|
||||||
</Hyperlink>
|
|
||||||
</Paragraph>
|
|
||||||
|
|
||||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
|
||||||
<Run Text="{x:Bind const:App.AppName}" />
|
<Run Text="{x:Bind const:App.AppName}" />
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
<Paragraph>
|
||||||
|
<Run Text="An elegant and deeply customizable lyrics visualizer & versatile music player" />
|
||||||
|
</Paragraph>
|
||||||
<Paragraph>
|
<Paragraph>
|
||||||
<Run Text="Proudly built by" />
|
<Run Text="Proudly built by" />
|
||||||
<Hyperlink NavigateUri="{x:Bind const:Link.AuthorGitHub}">
|
<Hyperlink NavigateUri="{x:Bind const:Link.AuthorGitHub}">
|
||||||
<Run Text="{x:Bind const:App.AppAuthor}" />
|
<Run Text="{x:Bind const:App.AppAuthor}" />
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
|
||||||
<Paragraph x:Name="CreditsReelFooter" />
|
|
||||||
|
|
||||||
</RichTextBlock>
|
</RichTextBlock>
|
||||||
</ScrollViewer>
|
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
@@ -11,7 +9,6 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
public sealed partial class AboutControl : UserControl
|
public sealed partial class AboutControl : UserControl
|
||||||
{
|
{
|
||||||
private bool _isCreditsScrolling = false;
|
|
||||||
public AboutControlViewModel ViewModel => (AboutControlViewModel)DataContext;
|
public AboutControlViewModel ViewModel => (AboutControlViewModel)DataContext;
|
||||||
|
|
||||||
public AboutControl()
|
public AboutControl()
|
||||||
@@ -20,47 +17,6 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
DataContext = Ioc.Default.GetRequiredService<AboutControlViewModel>();
|
DataContext = Ioc.Default.GetRequiredService<AboutControlViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Patron_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
CompositionTarget.Rendering += CompositionTarget_Rendering;
|
|
||||||
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
|
||||||
CreditsReel.Opacity = 1;
|
|
||||||
_isCreditsScrolling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CompositionTarget_Rendering(object? sender, object e)
|
|
||||||
{
|
|
||||||
if (_isCreditsScrolling)
|
|
||||||
{
|
|
||||||
CreditsReelScrollViewer.ChangeView(null, CreditsReelScrollViewer.VerticalOffset + 0.5, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CreditsReel_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
CreditsReel.Opacity = 0;
|
|
||||||
await Task.Delay(Constants.Time.AnimationDuration);
|
|
||||||
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
CompositionTarget.Rendering -= CompositionTarget_Rendering;
|
|
||||||
CreditsReelScrollViewer.ChangeView(null, 0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreditsReel_SizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
CreditsReelHeader.LineHeight = e.NewSize.Height;
|
|
||||||
CreditsReelFooter.LineHeight = e.NewSize.Height / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RichTextBlock_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
_isCreditsScrolling = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RichTextBlock_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
_isCreditsScrolling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WeChat_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
private void WeChat_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
WeChatFlyout.ShowAt(WeChatButton);
|
WeChatFlyout.ShowAt(WeChatButton);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
x:Class="BetterLyrics.WinUI3.Controls.AppSettingsControl"
|
x:Class="BetterLyrics.WinUI3.Controls.AppSettingsControl"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:consts="using:BetterLyrics.WinUI3.Constants"
|
||||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:dev="using:DevWinUI"
|
xmlns:dev="using:DevWinUI"
|
||||||
@@ -42,6 +43,14 @@
|
|||||||
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsExpander.ItemsFooter>
|
||||||
|
<InfoBar IsClosable="False" IsOpen="True">
|
||||||
|
<HyperlinkButton
|
||||||
|
x:Uid="SettingsPageHelpUsTranslate"
|
||||||
|
Padding="0"
|
||||||
|
NavigateUri="{x:Bind consts:Link.Crowdin}" />
|
||||||
|
</InfoBar>
|
||||||
|
</dev:SettingsExpander.ItemsFooter>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<!-- Startup -->
|
<!-- Startup -->
|
||||||
@@ -71,6 +80,10 @@
|
|||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoPlay, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoPlay, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageStopTrackOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.StopOnWindowClosed, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageExitOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageExitOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.ExitOnWindowClosed, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.ExitOnWindowClosed, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
@@ -91,6 +92,47 @@
|
|||||||
Text="{x:Bind LyricsWindowStatus.Name, Mode=OneWay}"
|
Text="{x:Bind LyricsWindowStatus.Name, Mode=OneWay}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}" Opacity="0">
|
||||||
|
<Grid.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Grid.OpacityTransition>
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Click="OpenButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="SystemTrayLyrics" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Click="CloseButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsEnabled="{x:Bind LyricsWindowStatus.IsOpened, Mode=OneWay}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="SettingsPageCloseStatus" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
|
using BetterLyrics.WinUI3.Views;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
@@ -9,6 +14,8 @@ namespace BetterLyrics.WinUI3.Controls;
|
|||||||
|
|
||||||
public sealed partial class DemoWindowGrid : UserControl
|
public sealed partial class DemoWindowGrid : UserControl
|
||||||
{
|
{
|
||||||
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
|
|
||||||
public DemoWindowGrid()
|
public DemoWindowGrid()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -22,4 +29,32 @@ public sealed partial class DemoWindowGrid : UserControl
|
|||||||
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
||||||
set => SetValue(LyricsWindowStatusProperty, value);
|
set => SetValue(LyricsWindowStatusProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var data = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
||||||
|
var window = WindowHook.GetWindows<NowPlayingWindow>().FirstOrDefault(x => x.LyricsWindowStatus == data);
|
||||||
|
window?.CloseWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
||||||
|
// <20>ģʽ
|
||||||
|
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
|
||||||
|
{
|
||||||
|
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
||||||
|
}
|
||||||
|
// <20><><EFBFBD><EFBFBD>ģʽ
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
|
||||||
|
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
|
||||||
|
{
|
||||||
|
item.CloseWindow();
|
||||||
|
}
|
||||||
|
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,43 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsExpander
|
||||||
|
x:Uid="SettingsPageAlbumArtLayer"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||||
|
<uc:ExtendedSlider
|
||||||
|
Default="100"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="%"
|
||||||
|
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayOpacity, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||||
|
<uc:ExtendedSlider
|
||||||
|
Default="50"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="%"
|
||||||
|
Value="{x:Bind LyricsBackgroundSettings.CoverOverlaySpeed, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageBlurAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||||
|
<uc:ExtendedSlider
|
||||||
|
Default="100"
|
||||||
|
Maximum="200"
|
||||||
|
Minimum="0"
|
||||||
|
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayBlurAmount, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageFluidLayer"
|
x:Uid="SettingsPageFluidLayer"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ using BetterLyrics.WinUI3.Extensions;
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Logic;
|
using BetterLyrics.WinUI3.Logic;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Lyrics;
|
||||||
using BetterLyrics.WinUI3.Models.Settings;
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
using BetterLyrics.WinUI3.Renderer;
|
using BetterLyrics.WinUI3.Renderer;
|
||||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
using BetterLyrics.WinUI3.Services.GSMTCService;
|
||||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Microsoft.Graphics.Canvas;
|
||||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@@ -20,7 +21,9 @@ using Microsoft.UI.Xaml.Controls;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Controls
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
@@ -28,20 +31,21 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
public sealed partial class LyricsCanvas : UserControl,
|
public sealed partial class LyricsCanvas : UserControl,
|
||||||
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
||||||
IRecipient<PropertyChangedMessage<LyricsData?>>,
|
IRecipient<PropertyChangedMessage<LyricsData?>>,
|
||||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
IRecipient<PropertyChangedMessage<SongInfo>>,
|
||||||
IRecipient<PropertyChangedMessage<int>>,
|
IRecipient<PropertyChangedMessage<int>>,
|
||||||
IRecipient<PropertyChangedMessage<double>>,
|
IRecipient<PropertyChangedMessage<double>>,
|
||||||
IRecipient<PropertyChangedMessage<bool>>,
|
IRecipient<PropertyChangedMessage<bool>>,
|
||||||
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
||||||
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||||
IRecipient<PropertyChangedMessage<string>>
|
IRecipient<PropertyChangedMessage<string>>,
|
||||||
|
IRecipient<PropertyChangedMessage<IRandomAccessStream?>>
|
||||||
{
|
{
|
||||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
private readonly IGSMTCService _gsmtcService = Ioc.Default.GetRequiredService<IGSMTCService>();
|
||||||
private readonly ILastFMService _lastFMService = Ioc.Default.GetRequiredService<ILastFMService>();
|
|
||||||
|
|
||||||
private readonly LyricsRenderer _lyricsRenderer = new();
|
private readonly LyricsRenderer _lyricsRenderer = new();
|
||||||
private readonly FluidBackgroundRenderer _fluidRenderer = new();
|
private readonly FluidBackgroundRenderer _fluidRenderer = new();
|
||||||
|
private readonly CoverBackgroundRenderer _coverRenderer = new();
|
||||||
private readonly PureColorBackgroundRenderer _pureColorRenderer = new();
|
private readonly PureColorBackgroundRenderer _pureColorRenderer = new();
|
||||||
private readonly SnowRenderer _snowRenderer = new();
|
private readonly SnowRenderer _snowRenderer = new();
|
||||||
private readonly FogRenderer _fogRenderer = new();
|
private readonly FogRenderer _fogRenderer = new();
|
||||||
@@ -54,48 +58,46 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private readonly ValueTransition<Color> _immersiveBgColorTransition = new(
|
private readonly ValueTransition<Color> _immersiveBgColorTransition = new(
|
||||||
initialValue: Colors.Transparent,
|
initialValue: Colors.Transparent,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<double> _immersiveBgOpacityTransition = new(
|
private readonly ValueTransition<double> _immersiveBgOpacityTransition = new(
|
||||||
initialValue: 1f,
|
initialValue: 1f,
|
||||||
durationSeconds: 0.3f
|
defaultTotalDuration: 0.3f
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<Color> _accentColor1Transition = new(
|
private readonly ValueTransition<Color> _accentColor1Transition = new(
|
||||||
initialValue: Colors.Transparent,
|
initialValue: Colors.Transparent,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<Color> _accentColor2Transition = new(
|
private readonly ValueTransition<Color> _accentColor2Transition = new(
|
||||||
initialValue: Colors.Transparent,
|
initialValue: Colors.Transparent,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<Color> _accentColor3Transition = new(
|
private readonly ValueTransition<Color> _accentColor3Transition = new(
|
||||||
initialValue: Colors.Transparent,
|
initialValue: Colors.Transparent,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<Color> _accentColor4Transition = new(
|
private readonly ValueTransition<Color> _accentColor4Transition = new(
|
||||||
initialValue: Colors.Transparent,
|
initialValue: Colors.Transparent,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<double> _canvasYScrollTransition = new(
|
private readonly ValueTransition<double> _canvasYScrollTransition = new(
|
||||||
initialValue: 0f,
|
initialValue: 0f,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
easingType: EasingType.EaseInOutSine
|
defaultEasingType: EasingType.EaseInOutSine
|
||||||
);
|
);
|
||||||
private readonly ValueTransition<double> _mouseYScrollTransition = new(
|
private readonly ValueTransition<double> _mouseYScrollTransition = new(
|
||||||
initialValue: 0f,
|
initialValue: 0f,
|
||||||
durationSeconds: 0.3f,
|
defaultTotalDuration: 0.3f,
|
||||||
easingType: EasingType.EaseInOutSine
|
defaultEasingType: EasingType.EaseInOutSine
|
||||||
);
|
);
|
||||||
|
|
||||||
private TimeSpan _songPositionWithOffset;
|
private TimeSpan _songPositionWithOffset;
|
||||||
private TimeSpan _songPosition; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
|
private TimeSpan _songPosition; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
|
||||||
private TimeSpan _totalPlayedTime; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ػ<EFBFBD><D8BB>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD>ŵ<EFBFBD>ʱ<EFBFBD>䣩
|
|
||||||
private bool _isLastFMTracked = false;
|
|
||||||
|
|
||||||
private double _renderLyricsStartX = 0;
|
private double _renderLyricsStartX = 0;
|
||||||
private double _renderLyricsStartY = 0;
|
private double _renderLyricsStartY = 0;
|
||||||
@@ -117,7 +119,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
private bool _isLayoutChanged = true;
|
private bool _isLayoutChanged = true;
|
||||||
private bool _isMouseScrollingChanged = false;
|
private bool _isMouseScrollingChanged = false;
|
||||||
|
|
||||||
private int _playingLineIndex;
|
private int _primaryPlayingLineIndex;
|
||||||
private (int Start, int End) _visibleRange;
|
private (int Start, int End) _visibleRange;
|
||||||
private double _canvasTargetScrollOffset;
|
private double _canvasTargetScrollOffset;
|
||||||
|
|
||||||
@@ -290,7 +292,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
}
|
}
|
||||||
else if (e.Property == MouseScrollOffsetProperty)
|
else if (e.Property == MouseScrollOffsetProperty)
|
||||||
{
|
{
|
||||||
canvas._mouseYScrollTransition.StartTransition(Convert.ToDouble(e.NewValue));
|
canvas._mouseYScrollTransition.Start(Convert.ToDouble(e.NewValue));
|
||||||
}
|
}
|
||||||
else if (e.Property == MousePositionProperty)
|
else if (e.Property == MousePositionProperty)
|
||||||
{
|
{
|
||||||
@@ -316,11 +318,11 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
else if (e.Property == AlbumArtThemeColorsProperty)
|
else if (e.Property == AlbumArtThemeColorsProperty)
|
||||||
{
|
{
|
||||||
var albumArtThemeColors = (AlbumArtThemeColors)e.NewValue;
|
var albumArtThemeColors = (AlbumArtThemeColors)e.NewValue;
|
||||||
canvas._immersiveBgColorTransition.StartTransition(albumArtThemeColors.EnvColor);
|
canvas._immersiveBgColorTransition.Start(albumArtThemeColors.EnvColor);
|
||||||
canvas._accentColor1Transition.StartTransition(albumArtThemeColors.AccentColor1);
|
canvas._accentColor1Transition.Start(albumArtThemeColors.AccentColor1);
|
||||||
canvas._accentColor2Transition.StartTransition(albumArtThemeColors.AccentColor2);
|
canvas._accentColor2Transition.Start(albumArtThemeColors.AccentColor2);
|
||||||
canvas._accentColor3Transition.StartTransition(albumArtThemeColors.AccentColor3);
|
canvas._accentColor3Transition.Start(albumArtThemeColors.AccentColor3);
|
||||||
canvas._accentColor4Transition.StartTransition(albumArtThemeColors.AccentColor4);
|
canvas._accentColor4Transition.Start(albumArtThemeColors.AccentColor4);
|
||||||
|
|
||||||
canvas._albumArtThemeColors = albumArtThemeColors;
|
canvas._albumArtThemeColors = albumArtThemeColors;
|
||||||
canvas._isLayoutChanged = true;
|
canvas._isLayoutChanged = true;
|
||||||
@@ -341,8 +343,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
||||||
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
||||||
|
|
||||||
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
|
double songDuration = _gsmtcService.CurrentSongInfo.DurationMs;
|
||||||
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
|
|
||||||
|
|
||||||
Color overlayColor;
|
Color overlayColor;
|
||||||
double finalOpacity;
|
double finalOpacity;
|
||||||
@@ -368,8 +369,8 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
lyricsBg.IsPureColorOverlayEnabled
|
lyricsBg.IsPureColorOverlayEnabled
|
||||||
);
|
);
|
||||||
|
|
||||||
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity / 100.0;
|
_coverRenderer.Draw(sender, args.DrawingSession);
|
||||||
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
|
|
||||||
_fluidRenderer.Draw(sender, args.DrawingSession);
|
_fluidRenderer.Draw(sender, args.DrawingSession);
|
||||||
|
|
||||||
_snowRenderer.Draw(sender, args.DrawingSession);
|
_snowRenderer.Draw(sender, args.DrawingSession);
|
||||||
@@ -380,7 +381,6 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
control: sender,
|
control: sender,
|
||||||
ds: args.DrawingSession,
|
ds: args.DrawingSession,
|
||||||
lines: _renderLyricsLines,
|
lines: _renderLyricsLines,
|
||||||
playingLineIndex: _playingLineIndex,
|
|
||||||
mouseHoverLineIndex: _mouseHoverLineIndex,
|
mouseHoverLineIndex: _mouseHoverLineIndex,
|
||||||
isMousePressing: _isMousePressing,
|
isMousePressing: _isMousePressing,
|
||||||
startVisibleIndex: _visibleRange.Start,
|
startVisibleIndex: _visibleRange.Start,
|
||||||
@@ -396,6 +396,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
strokeColor: _albumArtThemeColors.StrokeFontColor,
|
strokeColor: _albumArtThemeColors.StrokeFontColor,
|
||||||
bgColor: _albumArtThemeColors.BgFontColor,
|
bgColor: _albumArtThemeColors.BgFontColor,
|
||||||
fgColor: _albumArtThemeColors.FgFontColor,
|
fgColor: _albumArtThemeColors.FgFontColor,
|
||||||
|
currentProgressMs: _songPositionWithOffset.TotalMilliseconds,
|
||||||
getPlaybackState: (lineIndex) =>
|
getPlaybackState: (lineIndex) =>
|
||||||
{
|
{
|
||||||
if (_renderLyricsLines == null) return new LinePlaybackState();
|
if (_renderLyricsLines == null) return new LinePlaybackState();
|
||||||
@@ -408,9 +409,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
return _synchronizer.GetLinePlayingProgress(
|
return _synchronizer.GetLinePlayingProgress(
|
||||||
_songPositionWithOffset.TotalMilliseconds,
|
_songPositionWithOffset.TotalMilliseconds,
|
||||||
line,
|
line,
|
||||||
nextLine,
|
lyricsEffect.WordByWordEffectMode
|
||||||
songDuration,
|
|
||||||
isForceWordByWord
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -431,19 +430,19 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG && false
|
||||||
//args.DrawingSession.DrawText(
|
args.DrawingSession.DrawText(
|
||||||
// $"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||||
// $"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||||
// $"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
|
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
|
||||||
// $"Playing line (idx): {_playingLineIndex}\n" +
|
$"Playing line (idx): {_playingLineIndex}\n" +
|
||||||
// $"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
||||||
// $"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
||||||
// $"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
|
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
|
||||||
// $"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
|
$"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_gsmtcService.CurrentSongInfo.DurationMs)}\n" +
|
||||||
// $"Y offset: {_canvasYScrollTransition.Value}\n" +
|
$"Y offset: {_canvasYScrollTransition.Value}\n" +
|
||||||
// $"User scroll offset: {_mouseYScrollTransition.Value}",
|
$"User scroll offset: {_mouseYScrollTransition.Value}",
|
||||||
// new Vector2(0, 0), Colors.Red);
|
new Vector2(0, 0), Colors.Red);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -455,7 +454,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
|
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
|
||||||
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
||||||
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
||||||
var lyricsData = _mediaSessionsService.CurrentLyricsData;
|
var lyricsData = _gsmtcService.CurrentLyricsData;
|
||||||
|
|
||||||
TimeSpan elapsedTime = args.Timing.ElapsedTime;
|
TimeSpan elapsedTime = args.Timing.ElapsedTime;
|
||||||
|
|
||||||
@@ -473,22 +472,29 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
#region UpdatePlayingLineIndex
|
#region UpdatePlayingLineIndex
|
||||||
|
|
||||||
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, lyricsData);
|
int primaryPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, _renderLyricsLines);
|
||||||
bool isPlayingLineChanged = newPlayingIndex != _playingLineIndex;
|
bool isPrimaryPlayingLineChanged = primaryPlayingIndex != _primaryPlayingLineIndex;
|
||||||
_playingLineIndex = newPlayingIndex;
|
_primaryPlayingLineIndex = primaryPlayingIndex;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region UpdateTargetScrollOffset
|
#region UpdateTargetScrollOffset
|
||||||
|
|
||||||
if (isPlayingLineChanged || _isLayoutChanged)
|
if (isPrimaryPlayingLineChanged || _isLayoutChanged)
|
||||||
{
|
{
|
||||||
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _playingLineIndex);
|
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _primaryPlayingLineIndex);
|
||||||
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
|
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
|
||||||
|
|
||||||
|
if (_isLayoutChanged)
|
||||||
|
{
|
||||||
|
_canvasYScrollTransition.JumpTo(_canvasTargetScrollOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_canvasYScrollTransition.SetDurationMs(lyricsEffect.LyricsScrollDuration);
|
||||||
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
|
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
|
||||||
_canvasYScrollTransition.SetDuration(lyricsEffect.LyricsScrollDuration / 1000.0);
|
_canvasYScrollTransition.Start(_canvasTargetScrollOffset);
|
||||||
_canvasYScrollTransition.StartTransition(_canvasTargetScrollOffset, _isLayoutChanged);
|
}
|
||||||
}
|
}
|
||||||
_canvasYScrollTransition.Update(elapsedTime);
|
_canvasYScrollTransition.Update(elapsedTime);
|
||||||
|
|
||||||
@@ -521,7 +527,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
_renderLyricsLines,
|
_renderLyricsLines,
|
||||||
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
|
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
|
||||||
_isMouseScrolling ? maxRange.End : _visibleRange.End,
|
_isMouseScrolling ? maxRange.End : _visibleRange.End,
|
||||||
_playingLineIndex,
|
_primaryPlayingLineIndex,
|
||||||
sender.Size.Height,
|
sender.Size.Height,
|
||||||
_canvasTargetScrollOffset,
|
_canvasTargetScrollOffset,
|
||||||
lyricsStyle.PlayingLineTopOffset / 100.0,
|
lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||||
@@ -533,24 +539,26 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
elapsedTime,
|
elapsedTime,
|
||||||
_isMouseScrolling,
|
_isMouseScrolling,
|
||||||
_isLayoutChanged,
|
_isLayoutChanged,
|
||||||
isPlayingLineChanged,
|
isPrimaryPlayingLineChanged,
|
||||||
_isMouseScrollingChanged
|
_isMouseScrollingChanged,
|
||||||
|
_songPositionWithOffset.TotalMilliseconds
|
||||||
);
|
);
|
||||||
|
|
||||||
_isMouseScrollingChanged = false;
|
_isMouseScrollingChanged = false;
|
||||||
|
|
||||||
_lyricsRenderer.CalculateLyrics3DMatrix(
|
_lyricsRenderer.CalculateLyrics3DMatrix(
|
||||||
|
lyricsStyle: lyricsStyle,
|
||||||
lyricsEffect: lyricsEffect,
|
lyricsEffect: lyricsEffect,
|
||||||
lyricsX: _renderLyricsStartX,
|
lyricsX: _renderLyricsStartX,
|
||||||
lyricsY: _renderLyricsStartY,
|
lyricsY: _renderLyricsStartY,
|
||||||
lyricsWidth: _renderLyricsWidth,
|
lyricsWidth: _renderLyricsWidth,
|
||||||
canvasHeight: sender.Size.Height
|
lyricsHeight: _renderLyricsHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
_isLayoutChanged = false;
|
_isLayoutChanged = false;
|
||||||
|
|
||||||
if (_fluidRenderer.IsEnabled)
|
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
|
||||||
{
|
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity / 100.0;
|
||||||
_fluidRenderer.UpdateColors(
|
_fluidRenderer.UpdateColors(
|
||||||
_accentColor1Transition.Value,
|
_accentColor1Transition.Value,
|
||||||
_accentColor2Transition.Value,
|
_accentColor2Transition.Value,
|
||||||
@@ -558,7 +566,12 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
_accentColor4Transition.Value
|
_accentColor4Transition.Value
|
||||||
);
|
);
|
||||||
_fluidRenderer.Update(elapsedTime);
|
_fluidRenderer.Update(elapsedTime);
|
||||||
}
|
|
||||||
|
_coverRenderer.IsEnabled = lyricsBg.IsCoverOverlayEnabled;
|
||||||
|
_coverRenderer.Opacity = lyricsBg.CoverOverlayOpacity;
|
||||||
|
_coverRenderer.BlurAmount = lyricsBg.CoverOverlayBlurAmount;
|
||||||
|
_coverRenderer.Speed = lyricsBg.CoverOverlaySpeed;
|
||||||
|
_coverRenderer.Update(elapsedTime);
|
||||||
|
|
||||||
_snowRenderer.IsEnabled = lyricsBg.IsSnowFlakeOverlayEnabled;
|
_snowRenderer.IsEnabled = lyricsBg.IsSnowFlakeOverlayEnabled;
|
||||||
_snowRenderer.Amount = lyricsBg.SnowFlakeOverlayAmount / 100f;
|
_snowRenderer.Amount = lyricsBg.SnowFlakeOverlayAmount / 100f;
|
||||||
@@ -586,6 +599,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
private void Canvas_Unloaded(object sender, RoutedEventArgs e)
|
private void Canvas_Unloaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_fluidRenderer.Dispose();
|
_fluidRenderer.Dispose();
|
||||||
|
_coverRenderer.Dispose();
|
||||||
_snowRenderer.Dispose();
|
_snowRenderer.Dispose();
|
||||||
_fogRenderer.Dispose();
|
_fogRenderer.Dispose();
|
||||||
_spectrumRenderer.Dispose();
|
_spectrumRenderer.Dispose();
|
||||||
@@ -600,7 +614,13 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private async void Canvas_CreateResources(CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
private async void Canvas_CreateResources(CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
||||||
{
|
{
|
||||||
args.TrackAsyncAction(_fluidRenderer.LoadResourcesAsync().AsAsyncAction());
|
var tasks = new Task[]
|
||||||
|
{
|
||||||
|
_fluidRenderer.LoadResourcesAsync(),
|
||||||
|
ReloadCoverBackgroundResourcesAsync()
|
||||||
|
};
|
||||||
|
args.TrackAsyncAction(Task.WhenAll(tasks).AsAsyncAction());
|
||||||
|
|
||||||
_snowRenderer.LoadResources();
|
_snowRenderer.LoadResources();
|
||||||
_fogRenderer.LoadResources();
|
_fogRenderer.LoadResources();
|
||||||
|
|
||||||
@@ -637,73 +657,54 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private void UpdatePlaybackState(TimeSpan elapsedTime)
|
private void UpdatePlaybackState(TimeSpan elapsedTime)
|
||||||
{
|
{
|
||||||
if (_mediaSessionsService.CurrentIsPlaying)
|
if (_gsmtcService.CurrentIsPlaying)
|
||||||
{
|
{
|
||||||
_songPosition += elapsedTime;
|
_songPosition += elapsedTime;
|
||||||
_totalPlayedTime += elapsedTime;
|
_songPositionWithOffset = _songPosition + TimeSpan.FromMilliseconds(_gsmtcService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
|
||||||
_songPositionWithOffset = _songPosition + TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
|
|
||||||
CheckAndScrobbleLastFM();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckAndScrobbleLastFM()
|
|
||||||
{
|
|
||||||
bool isEnabled = _mediaSessionsService.CurrentMediaSourceProviderInfo?.IsLastFMTrackEnabled ?? false;
|
|
||||||
if (!isEnabled || _isLastFMTracked) return;
|
|
||||||
|
|
||||||
var songInfo = _mediaSessionsService.CurrentSongInfo;
|
|
||||||
if (songInfo == null || songInfo.Duration <= 0) return;
|
|
||||||
|
|
||||||
if (_totalPlayedTime.TotalSeconds >= songInfo.Duration * 0.5)
|
|
||||||
{
|
|
||||||
_isLastFMTracked = true;
|
|
||||||
_lastFMService.TrackAsync(songInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetPlaybackState()
|
private void ResetPlaybackState()
|
||||||
{
|
{
|
||||||
_songPosition = TimeSpan.Zero;
|
_songPosition = TimeSpan.Zero;
|
||||||
_totalPlayedTime = TimeSpan.Zero;
|
|
||||||
_isLastFMTracked = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateRenderLyricsLines()
|
private void UpdateRenderLyricsLines()
|
||||||
{
|
{
|
||||||
_renderLyricsLines = null;
|
_renderLyricsLines = null;
|
||||||
_renderLyricsLines = _mediaSessionsService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine()
|
var lines = _gsmtcService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine(x)).ToList();
|
||||||
|
if (lines != null)
|
||||||
{
|
{
|
||||||
LyricsSyllables = x.LyricsSyllables,
|
LyricsLayoutManager.CalculateLanes(lines);
|
||||||
StartMs = x.StartMs,
|
}
|
||||||
EndMs = x.EndMs,
|
_renderLyricsLines = lines;
|
||||||
PhoneticText = x.PhoneticText,
|
}
|
||||||
OriginalText = x.OriginalText,
|
|
||||||
TranslatedText = x.TranslatedText
|
private async Task ReloadCoverBackgroundResourcesAsync()
|
||||||
}).ToList();
|
{
|
||||||
|
if (_gsmtcService.AlbumArtBitmapStream is IRandomAccessStream stream)
|
||||||
|
{
|
||||||
|
stream.Seek(0);
|
||||||
|
CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(Canvas, stream);
|
||||||
|
_coverRenderer.SetCoverBitmap(bitmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
||||||
{
|
{
|
||||||
if (message.Sender is IMediaSessionsService)
|
if (message.Sender is IGSMTCService)
|
||||||
{
|
{
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
|
if (message.PropertyName == nameof(IGSMTCService.CurrentPosition))
|
||||||
{
|
{
|
||||||
var realPosition = message.NewValue;
|
var realPosition = message.NewValue;
|
||||||
|
|
||||||
var diff = Math.Abs(_songPosition.TotalMilliseconds - realPosition.TotalMilliseconds);
|
var diff = Math.Abs(_songPosition.TotalMilliseconds - realPosition.TotalMilliseconds);
|
||||||
var timelineSyncThreshold = _mediaSessionsService.CurrentMediaSourceProviderInfo?.TimelineSyncThreshold ?? 0;
|
var timelineSyncThreshold = _gsmtcService.CurrentMediaSourceProviderInfo?.TimelineSyncThreshold ?? 0;
|
||||||
|
|
||||||
// ƫ<><C6AB> or seek
|
// ƫ<><C6AB> or seek
|
||||||
if (diff >= timelineSyncThreshold)
|
if (diff >= timelineSyncThreshold)
|
||||||
{
|
{
|
||||||
_songPosition = realPosition;
|
_songPosition = realPosition;
|
||||||
|
|
||||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˿<EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD> LastFM ͳ<><CDB3>״̬
|
|
||||||
if (_songPosition.TotalSeconds <= 1)
|
|
||||||
{
|
|
||||||
_totalPlayedTime = TimeSpan.Zero;
|
|
||||||
_isLastFMTracked = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// <20>϶<EFBFBD><CFB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD><EFBFBD>
|
// <20>϶<EFBFBD><CFB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD><EFBFBD>
|
||||||
@@ -717,9 +718,9 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
public void Receive(PropertyChangedMessage<LyricsData?> message)
|
public void Receive(PropertyChangedMessage<LyricsData?> message)
|
||||||
{
|
{
|
||||||
if (message.Sender is IMediaSessionsService)
|
if (message.Sender is IGSMTCService)
|
||||||
{
|
{
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentLyricsData))
|
if (message.PropertyName == nameof(IGSMTCService.CurrentLyricsData))
|
||||||
{
|
{
|
||||||
UpdateRenderLyricsLines();
|
UpdateRenderLyricsLines();
|
||||||
_isLayoutChanged = true;
|
_isLayoutChanged = true;
|
||||||
@@ -727,11 +728,11 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
public void Receive(PropertyChangedMessage<SongInfo> message)
|
||||||
{
|
{
|
||||||
if (message.Sender is IMediaSessionsService)
|
if (message.Sender is IGSMTCService)
|
||||||
{
|
{
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
if (message.PropertyName == nameof(IGSMTCService.CurrentSongInfo))
|
||||||
{
|
{
|
||||||
ResetPlaybackState();
|
ResetPlaybackState();
|
||||||
}
|
}
|
||||||
@@ -827,6 +828,14 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
_isLayoutChanged = true;
|
_isLayoutChanged = true;
|
||||||
}
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsFadeOutEffectEnabled))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsOutOfSightEffectEnabled))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
else if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||||
{
|
{
|
||||||
@@ -874,5 +883,16 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<IRandomAccessStream?> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is IGSMTCService)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(IGSMTCService.AlbumArtBitmapStream))
|
||||||
|
{
|
||||||
|
_ = ReloadCoverBackgroundResourcesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,29 @@
|
|||||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||||
Text="Effect" />
|
Text="Effect" />
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageLyricsWordByWordEffectMode" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.WordByWordEffectMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageLyricsWordByWordEffectModeAuto" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageLyricsWordByWordEffectModeNever" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageLyricsWordByWordEffectModeAlways" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<!-- 模糊效果 -->
|
<!-- 模糊效果 -->
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsBlurEffectEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsBlurEffectEnabled, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 淡出效果 -->
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageLyricsFadeOutEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFadeOutEffectEnabled, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 远离视野 -->
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageLyricsOutOfSightEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsOutOfSightEffectEnabled, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<!-- 辉光效果 -->
|
<!-- 辉光效果 -->
|
||||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsExpander x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||||
@@ -98,6 +116,14 @@
|
|||||||
Minimum="0"
|
Minimum="0"
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationAmount, Mode=TwoWay}" />
|
Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationAmount, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="LyricsEffectSettingsControlAnimationDuration">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
Default="450"
|
||||||
|
Maximum="2000"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="ms"
|
||||||
|
Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationDuration, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
xmlns:dev="using:DevWinUI"
|
xmlns:dev="using:DevWinUI"
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:lyricsmodels="using:BetterLyrics.WinUI3.Models.Lyrics"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
<ScrollViewer>
|
<ScrollViewer Padding="8,0">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
@@ -97,19 +98,6 @@
|
|||||||
Style="{StaticResource GhostButtonStyle}" />
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<RichTextBlock
|
|
||||||
FontSize="12"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
TextWrapping="Wrap">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="*" />
|
|
||||||
<Run x:Uid="ArtistsSplitHint" />
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="; , / ; 、 ," />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -153,12 +141,11 @@
|
|||||||
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
|
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
|
|
||||||
<Button
|
<Button
|
||||||
x:Uid="LyricsSearchControlSearch"
|
x:Uid="LyricsSearchControlSearch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
Command="{x:Bind ViewModel.SearchCommand}"
|
Command="{x:Bind ViewModel.SearchCommand}"
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache">
|
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache">
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
||||||
@@ -170,7 +157,7 @@
|
|||||||
<Grid Grid.Column="1">
|
<Grid Grid.Column="1">
|
||||||
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
|
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
<DataTemplate x:DataType="models:LyricsCacheItem">
|
||||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||||
<StackPanel Padding="0,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
<StackPanel Padding="0,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
||||||
<local:PropertyRow
|
<local:PropertyRow
|
||||||
@@ -181,20 +168,13 @@
|
|||||||
<!-- Lyrics search result -->
|
<!-- Lyrics search result -->
|
||||||
<StackPanel Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
<StackPanel Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind Artist, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow
|
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
x:Uid="LyricsSearchControlDurauion"
|
|
||||||
Unit="s"
|
|
||||||
Value="{x:Bind Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
<local:PropertyRow
|
<local:PropertyRow
|
||||||
x:Uid="LyricsPageMatchPercentage"
|
x:Uid="LyricsPageMatchPercentage"
|
||||||
Unit="%"
|
Unit="%"
|
||||||
Value="{x:Bind MatchPercentage, Mode=OneWay}" />
|
Value="{x:Bind MatchPercentage, Mode=OneWay}" />
|
||||||
<local:PropertyRow
|
|
||||||
x:Uid="LyricsPageCachePath"
|
|
||||||
Link="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
|
||||||
ToolTipService.ToolTip="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- NOT FOUND -->
|
<!-- NOT FOUND -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -244,8 +224,6 @@
|
|||||||
<ProgressBar
|
<ProgressBar
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
IsIndeterminate="True"
|
IsIndeterminate="True"
|
||||||
ShowError="False"
|
|
||||||
ShowPaused="False"
|
|
||||||
Visibility="{x:Bind ViewModel.IsSearching, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
Visibility="{x:Bind ViewModel.IsSearching, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Column="2">
|
<Grid Grid.Column="2">
|
||||||
@@ -265,7 +243,7 @@
|
|||||||
</interactivity:DataTriggerBehavior>
|
</interactivity:DataTriggerBehavior>
|
||||||
</interactivity:Interaction.Behaviors>
|
</interactivity:Interaction.Behaviors>
|
||||||
<Pivot.HeaderTemplate>
|
<Pivot.HeaderTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsData">
|
<DataTemplate x:DataType="lyricsmodels:LyricsData">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Bind LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Bind LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||||
<InfoBadge
|
<InfoBadge
|
||||||
@@ -277,19 +255,44 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Pivot.HeaderTemplate>
|
</Pivot.HeaderTemplate>
|
||||||
<Pivot.ItemTemplate>
|
<Pivot.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsData">
|
<DataTemplate x:DataType="lyricsmodels:LyricsData">
|
||||||
<ListView ItemsSource="{x:Bind LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
<ListView
|
||||||
|
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||||
|
ItemsSource="{x:Bind LyricsLines, Mode=OneWay}"
|
||||||
|
SelectionMode="None">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsLine">
|
<DataTemplate x:DataType="lyricsmodels:LyricsLine">
|
||||||
<StackPanel Orientation="Horizontal">
|
<Grid Margin="0,6" ColumnSpacing="6">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid Grid.Column="0">
|
||||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||||
<TextBlock
|
<Button
|
||||||
Margin="1,0"
|
HorizontalAlignment="Center"
|
||||||
Foreground="{ThemeResource SystemFillColorNeutralBrush}"
|
VerticalAlignment="Center"
|
||||||
Text="-" />
|
Click="PlayLyricsLineButton_Click"
|
||||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind EndMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
<TextBlock Margin="6,0" Text="{x:Bind OriginalText, Mode=OneWay}" />
|
FontSize=16,
|
||||||
</StackPanel>
|
Glyph=}"
|
||||||
|
Opacity="0"
|
||||||
|
Style="{StaticResource AccentButtonStyle}">
|
||||||
|
<Button.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Button.OpacityTransition>
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<local:PropertyRow Grid.Column="1" Value="{x:Bind PrimaryText, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</ListView>
|
</ListView>
|
||||||
@@ -321,8 +324,8 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Row="1" ColumnSpacing="6">
|
<Grid Grid.Row="1" ColumnSpacing="6">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models.Lyrics;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
@@ -18,10 +18,10 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
DataContext = Ioc.Default.GetRequiredService<LyricsSearchControlViewModel>();
|
DataContext = Ioc.Default.GetRequiredService<LyricsSearchControlViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void PlayLyricsLineButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
|
var lyricsLine = (LyricsLine)((Button)sender).DataContext;
|
||||||
}
|
ViewModel.PlayLyricsLine(lyricsLine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<local:ExtendedSlider
|
<local:ExtendedSlider
|
||||||
Default="50"
|
Default="50"
|
||||||
Maximum="100"
|
Maximum="99"
|
||||||
Minimum="0"
|
Minimum="1"
|
||||||
Unit="%"
|
Unit="%"
|
||||||
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
|
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
Loaded="UserControl_Loaded"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -99,10 +100,7 @@
|
|||||||
BorderThickness="4"
|
BorderThickness="4"
|
||||||
CornerRadius="4"
|
CornerRadius="4"
|
||||||
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
<uc:DemoWindowGrid
|
<uc:DemoWindowGrid Margin="4" LyricsWindowStatus="{Binding}" />
|
||||||
Margin="4"
|
|
||||||
LyricsWindowStatus="{Binding}"
|
|
||||||
Tapped="DemoWindowGrid_Tapped" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
@@ -116,7 +114,6 @@
|
|||||||
<Grid ColumnSpacing="4">
|
<Grid ColumnSpacing="4">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
@@ -124,13 +121,6 @@
|
|||||||
Click="ConfigButton_Click">
|
Click="ConfigButton_Click">
|
||||||
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
|
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Click="CloseStatusButton_Click"
|
|
||||||
IsEnabled="{Binding IsOpened, Mode=OneWay}">
|
|
||||||
<TextBlock x:Uid="SettingsPageCloseStatus" />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@@ -197,29 +187,51 @@
|
|||||||
|
|
||||||
<controls:Segmented
|
<controls:Segmented
|
||||||
x:Name="ConfigSegmented"
|
x:Name="ConfigSegmented"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
SelectionChanged="ConfigSegmented_SelectionChanged"
|
SelectionChanged="ConfigSegmented_SelectionChanged"
|
||||||
Style="{StaticResource PivotSegmentedStyle}">
|
Style="{StaticResource PivotSegmentedStyle}">
|
||||||
|
|
||||||
<controls:SegmentedItem x:Name="WindowSegmentedItem" Tag="Window">
|
<controls:SegmentedItem x:Name="WindowSegmentedItem" Tag="Window">
|
||||||
<TextBlock x:Uid="AppSettingsControlGeneral" />
|
<TextBlock
|
||||||
|
x:Uid="AppSettingsControlGeneral"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem x:Name="LayoutSegmentedItem" Tag="Layout">
|
<controls:SegmentedItem x:Name="LayoutSegmentedItem" Tag="Layout">
|
||||||
<TextBlock x:Uid="SettingsPageLayout" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageLayout"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem x:Name="AlbumArtStyleSegmentedItem" Tag="AlbumArtStyle">
|
<controls:SegmentedItem x:Name="AlbumArtStyleSegmentedItem" Tag="AlbumArtStyle">
|
||||||
<TextBlock x:Uid="SettingsPageAlbumStyle" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageAlbumStyle"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Tag="AlbumArtEffect">
|
<controls:SegmentedItem Tag="AlbumArtEffect">
|
||||||
<TextBlock x:Uid="SettingsPageAlbumEffect" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageAlbumEffect"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Tag="LyricsStyle">
|
<controls:SegmentedItem Tag="LyricsStyle">
|
||||||
<TextBlock x:Uid="SettingsPageLyricsStyle" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageLyricsStyle"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Tag="LyricsEffect">
|
<controls:SegmentedItem Tag="LyricsEffect">
|
||||||
<TextBlock x:Uid="SettingsPageLyricsEffect" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageLyricsEffect"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Tag="LyricsBackground">
|
<controls:SegmentedItem Tag="LyricsBackground">
|
||||||
<TextBlock x:Uid="SettingsPageBackgroundOverlay" />
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageBackgroundOverlay"
|
||||||
|
MaxWidth="120"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
|
|
||||||
</controls:Segmented>
|
</controls:Segmented>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
using BetterLyrics.WinUI3.Serialization;
|
using BetterLyrics.WinUI3.Serialization;
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
@@ -26,14 +26,14 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
|
|
||||||
public LyricsWindowStatus LyricsWindowStatus
|
public LyricsWindowStatus? LyricsWindowStatus
|
||||||
{
|
{
|
||||||
get { return (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty); }
|
get { return (LyricsWindowStatus?)GetValue(LyricsWindowStatusProperty); }
|
||||||
set { SetValue(LyricsWindowStatusProperty, value); }
|
set { SetValue(LyricsWindowStatusProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||||
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsWindowSettingsControl), new PropertyMetadata(default));
|
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsWindowSettingsControl), new PropertyMetadata(null));
|
||||||
|
|
||||||
public LyricsWindowSettingsControl()
|
public LyricsWindowSettingsControl()
|
||||||
{
|
{
|
||||||
@@ -166,52 +166,14 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
ViewModel.OpenConfigPanel();
|
ViewModel.OpenConfigPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DemoWindowGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
|
||||||
// <20>ģʽ
|
|
||||||
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
|
|
||||||
{
|
|
||||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
|
||||||
}
|
|
||||||
// <20><><EFBFBD><EFBFBD>ģʽ
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
|
|
||||||
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
|
|
||||||
{
|
|
||||||
item.CloseWindow();
|
|
||||||
}
|
|
||||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfigSelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
|
|
||||||
{
|
|
||||||
if (sender is SelectorBar bar)
|
|
||||||
{
|
|
||||||
if (bar.SelectedItem is SelectorBarItem item)
|
|
||||||
{
|
|
||||||
ViewModel?.SelectorBarSelectedItemTag = item.Tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseStatusButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is FrameworkElement element)
|
|
||||||
{
|
|
||||||
if (element.DataContext is LyricsWindowStatus data)
|
|
||||||
{
|
|
||||||
var window = WindowHook.GetWindows<NowPlayingWindow>().FirstOrDefault(x => x.LyricsWindowStatus == data);
|
|
||||||
window?.CloseWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfigSegmented_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void ConfigSegmented_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.SelectorBarSelectedItemTag = (string)((SegmentedItem)((Segmented)sender).SelectedItem).Tag;
|
ViewModel.SelectorBarSelectedItemTag = (string)((SegmentedItem)((Segmented)sender).SelectedItem).Tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.CloseConfigPanelCommand.Execute(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
@@ -7,7 +6,6 @@ using CommunityToolkit.Mvvm.DependencyInjection;
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
@@ -29,22 +27,6 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private async void Grid_Tapped(object sender, TappedRoutedEventArgs e)
|
private async void Grid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
|
||||||
// <20>ģʽ
|
|
||||||
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
|
|
||||||
{
|
|
||||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
|
||||||
}
|
|
||||||
// <20><><EFBFBD><EFBFBD>ģʽ
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
|
|
||||||
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
|
|
||||||
{
|
|
||||||
item.CloseWindow();
|
|
||||||
}
|
|
||||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
|
||||||
}
|
|
||||||
await HideAsync();
|
await HideAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<UserControl
|
<UserControl
|
||||||
x:Class="BetterLyrics.WinUI3.Controls.MediaSettingsControl"
|
x:Class="BetterLyrics.WinUI3.Controls.MediaSettingsControl"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
@@ -6,9 +5,12 @@
|
|||||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:dev="using:DevWinUI"
|
xmlns:dev="using:DevWinUI"
|
||||||
|
xmlns:enums="using:BetterLyrics.WinUI3.Enums"
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||||
|
xmlns:settingsmodels="using:BetterLyrics.WinUI3.Models.Settings"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
@@ -49,39 +51,133 @@
|
|||||||
ItemsSource="{x:Bind ViewModel.AppSettings.LocalMediaFolders, Mode=OneWay}"
|
ItemsSource="{x:Bind ViewModel.AppSettings.LocalMediaFolders, Mode=OneWay}"
|
||||||
SelectionMode="None">
|
SelectionMode="None">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="settingsmodels:MediaFolder">
|
||||||
<dev:SettingsExpander>
|
<dev:SettingsExpander IsExpanded="True">
|
||||||
|
|
||||||
|
<dev:SettingsExpander.HeaderIcon>
|
||||||
|
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="{x:Bind SourceType, Converter={StaticResource FileSourceTypeToIconConverter}, Mode=OneWay}" />
|
||||||
|
</dev:SettingsExpander.HeaderIcon>
|
||||||
<dev:SettingsExpander.Header>
|
<dev:SettingsExpander.Header>
|
||||||
<HyperlinkButton
|
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind Name, Mode=OneWay}" />
|
||||||
Click="LocalFolderHyperlinkButton_Click"
|
|
||||||
Content="{Binding Path, Mode=OneWay}"
|
|
||||||
Tag="{Binding Path, Mode=OneWay}" />
|
|
||||||
</dev:SettingsExpander.Header>
|
</dev:SettingsExpander.Header>
|
||||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
<dev:SettingsExpander.Description>
|
||||||
|
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind ConnectionSummary, Mode=OneWay}" />
|
||||||
|
</dev:SettingsExpander.Description>
|
||||||
|
|
||||||
|
<ToggleSwitch IsOn="{x:Bind IsEnabled, Mode=TwoWay}" />
|
||||||
|
|
||||||
<dev:SettingsExpander.Items>
|
<dev:SettingsExpander.Items>
|
||||||
<dev:SettingsCard>
|
<dev:SettingsCard x:Uid="MediaSettingsControlNameSetting">
|
||||||
<dev:SettingsCard.Header>
|
<TextBox VerticalAlignment="Center" Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
<HyperlinkButton
|
|
||||||
x:Uid="SettingsPageRemovePath"
|
|
||||||
Click="SettingsPageRemovePathButton_Click"
|
|
||||||
Tag="{Binding}" />
|
|
||||||
</dev:SettingsCard.Header>
|
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageMusicLibRealTimeWatch">
|
<dev:SettingsCard x:Uid="MediaSettingsControlLastSyncTime" Description="{x:Bind LastSyncTime.ToString(), Mode=OneWay, TargetNullValue=N/A}">
|
||||||
<ToggleSwitch IsOn="{Binding IsRealTimeWatchEnabled, Mode=TwoWay}" />
|
<Button
|
||||||
|
x:Uid="MediaSettingsControlSyncNow"
|
||||||
|
Click="SyncNowButton_Click"
|
||||||
|
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="MusicSettingsControlAutoSyncInterval">
|
||||||
|
<ComboBox IsEnabled="{x:Bind IsEnabled, Mode=OneWay}" SelectedIndex="{x:Bind ScanInterval, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalDisabled" />
|
||||||
|
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryFifteenMin" />
|
||||||
|
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryHour" />
|
||||||
|
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEverySixHrs" />
|
||||||
|
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryDay" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard>
|
||||||
|
<Button x:Uid="SettingsPageRemovePath" Click="SettingsPageRemovePathButton_Click" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
|
|
||||||
|
<dev:SettingsExpander.ItemsHeader>
|
||||||
|
<StackPanel>
|
||||||
|
<!-- Index info -->
|
||||||
|
<InfoBar
|
||||||
|
IsClosable="False"
|
||||||
|
IsOpen="True"
|
||||||
|
Message="{x:Bind StatusText, Mode=OneWay}"
|
||||||
|
Severity="{x:Bind StatusSeverity, Mode=OneWay}" />
|
||||||
|
<ProgressBar
|
||||||
|
Background="Transparent"
|
||||||
|
Visibility="{x:Bind IsProcessing, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||||
|
Value="{x:Bind IndexingProgress, Mode=OneWay}">
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind IndexingProgress, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="0">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="IsIndeterminate" Value="True" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind IndexingProgress, Mode=OneWay}"
|
||||||
|
ComparisonCondition="NotEqual"
|
||||||
|
Value="0">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="IsIndeterminate" Value="False" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
</ProgressBar>
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsExpander.ItemsHeader>
|
||||||
|
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</ListView>
|
</ListView>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
<StackPanel
|
||||||
<Button
|
Margin="0,6,0,0"
|
||||||
x:Uid="SettingsPageAddFolderButton"
|
HorizontalAlignment="Right"
|
||||||
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
|
Orientation="Horizontal"
|
||||||
CommandParameter="{Binding ElementName=RootGrid}" />
|
Spacing="6">
|
||||||
</dev:SettingsCard>
|
<Button Command="{x:Bind ViewModel.OpenMusicGalleryWindowCommand}">
|
||||||
|
<TextBlock x:Uid="SystemTrayMusicGallery" />
|
||||||
|
</Button>
|
||||||
|
<DropDownButton x:Uid="SettingsPageAddFolderButton">
|
||||||
|
<DropDownButton.Flyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuFlyoutItem
|
||||||
|
x:Uid="SettingsPageLocalFolder"
|
||||||
|
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||||
|
CommandParameter="Local">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutSeparator />
|
||||||
|
|
||||||
|
<MenuFlyoutItem
|
||||||
|
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||||
|
CommandParameter="SMB"
|
||||||
|
Text="SMB">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutItem
|
||||||
|
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||||
|
CommandParameter="FTP"
|
||||||
|
Text="FTP">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutItem
|
||||||
|
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||||
|
CommandParameter="WebDAV"
|
||||||
|
Text="WebDAV">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
</MenuFlyout>
|
||||||
|
</DropDownButton.Flyout>
|
||||||
|
</DropDownButton>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System;
|
|
||||||
using Windows.System;
|
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
@@ -22,18 +20,14 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private void SettingsPageRemovePathButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
private void SettingsPageRemovePathButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.RemoveFolderAsync((LocalMediaFolder)(sender as HyperlinkButton)!.Tag);
|
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||||
|
ViewModel.RemoveFolder(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LocalFolderHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
private void SyncNowButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is HyperlinkButton button && button.Tag is string uriStr)
|
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||||
{
|
ViewModel.SyncFolder(folder);
|
||||||
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri))
|
|
||||||
{
|
|
||||||
await Launcher.LaunchUriAsync(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dev="using:DevWinUI"
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
@@ -11,10 +12,9 @@
|
|||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid x:Name="RootGrid">
|
<Grid x:Name="RootGrid">
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="BottomCommandGrid"
|
x:Name="BottomCommandGrid"
|
||||||
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
|
Background="{ThemeResource LayerOnMicaBaseAltFillColorDefaultBrush}"
|
||||||
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
|
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
|
||||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||||
PointerExited="BottomCommandGrid_PointerExited">
|
PointerExited="BottomCommandGrid_PointerExited">
|
||||||
@@ -60,16 +60,16 @@
|
|||||||
</interactivity:Interaction.Behaviors>
|
</interactivity:Interaction.Behaviors>
|
||||||
<Grid VerticalAlignment="Center" CornerRadius="4">
|
<Grid VerticalAlignment="Center" CornerRadius="4">
|
||||||
<local:ImageSwitcher
|
<local:ImageSwitcher
|
||||||
x:Name="AlbumArtImageSwitcher"
|
|
||||||
Width="36"
|
Width="36"
|
||||||
Height="36" />
|
Height="36"
|
||||||
|
Source="{x:Bind ViewModel.GSMTCService.AlbumArtBitmapImage, Mode=OneWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackPanel VerticalAlignment="Center">
|
<StackPanel VerticalAlignment="Center">
|
||||||
<TextBlock x:Name="TitleTextBlock" />
|
<TextBlock Text="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Title, Mode=OneWay}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="ArtistsTextBlock"
|
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Text="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Artist, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -113,7 +113,58 @@
|
|||||||
x:Name="BottomCenterCommandStackPanel"
|
x:Name="BottomCenterCommandStackPanel"
|
||||||
Padding="16"
|
Padding="16"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="3">
|
Spacing="12">
|
||||||
|
<!-- Playback order -->
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
Click="PlaybackOrderButton_Click"
|
||||||
|
Style="{StaticResource GhostButtonStyle}"
|
||||||
|
Visibility="{x:Bind ShowPlaybackOrderButton, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
|
||||||
|
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
|
||||||
|
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
|
||||||
|
</Grid>
|
||||||
|
</ToolTip>
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
<Button.Content>
|
||||||
|
<Grid>
|
||||||
|
<!-- Repeat all -->
|
||||||
|
<FontIcon
|
||||||
|
x:Name="PlaybackRepeatAll"
|
||||||
|
FontFamily="{StaticResource IconFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph="">
|
||||||
|
<FontIcon.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</FontIcon.OpacityTransition>
|
||||||
|
</FontIcon>
|
||||||
|
<!-- Repeat one -->
|
||||||
|
<FontIcon
|
||||||
|
x:Name="PlaybackRepeatOne"
|
||||||
|
FontFamily="{StaticResource IconFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph="">
|
||||||
|
<FontIcon.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</FontIcon.OpacityTransition>
|
||||||
|
</FontIcon>
|
||||||
|
<!-- Shuffle -->
|
||||||
|
<FontIcon
|
||||||
|
x:Name="PlaybackShuffle"
|
||||||
|
FontFamily="{StaticResource IconFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph="">
|
||||||
|
<FontIcon.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</FontIcon.OpacityTransition>
|
||||||
|
</FontIcon>
|
||||||
|
</Grid>
|
||||||
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
<!-- 上一曲目 -->
|
||||||
<Button
|
<Button
|
||||||
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
@@ -126,13 +177,13 @@
|
|||||||
Style="{StaticResource GhostButtonStyle}">
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
<interactivity:Interaction.Behaviors>
|
<interactivity:Interaction.Behaviors>
|
||||||
<interactivity:DataTriggerBehavior
|
<interactivity:DataTriggerBehavior
|
||||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
Binding="{x:Bind ViewModel.GSMTCService.CurrentIsPlaying, Mode=OneWay}"
|
||||||
ComparisonCondition="Equal"
|
ComparisonCondition="Equal"
|
||||||
Value="True">
|
Value="True">
|
||||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||||
</interactivity:DataTriggerBehavior>
|
</interactivity:DataTriggerBehavior>
|
||||||
<interactivity:DataTriggerBehavior
|
<interactivity:DataTriggerBehavior
|
||||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
Binding="{x:Bind ViewModel.GSMTCService.CurrentIsPlaying, Mode=OneWay}"
|
||||||
ComparisonCondition="Equal"
|
ComparisonCondition="Equal"
|
||||||
Value="False">
|
Value="False">
|
||||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||||
@@ -146,13 +197,13 @@
|
|||||||
Style="{StaticResource GhostButtonStyle}">
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
<interactivity:Interaction.Behaviors>
|
<interactivity:Interaction.Behaviors>
|
||||||
<interactivity:DataTriggerBehavior
|
<interactivity:DataTriggerBehavior
|
||||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
Binding="{x:Bind ViewModel.GSMTCService.CurrentIsPlaying, Mode=OneWay}"
|
||||||
ComparisonCondition="Equal"
|
ComparisonCondition="Equal"
|
||||||
Value="True">
|
Value="True">
|
||||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||||
</interactivity:DataTriggerBehavior>
|
</interactivity:DataTriggerBehavior>
|
||||||
<interactivity:DataTriggerBehavior
|
<interactivity:DataTriggerBehavior
|
||||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
Binding="{x:Bind ViewModel.GSMTCService.CurrentIsPlaying, Mode=OneWay}"
|
||||||
ComparisonCondition="Equal"
|
ComparisonCondition="Equal"
|
||||||
Value="False">
|
Value="False">
|
||||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||||
@@ -164,6 +215,17 @@
|
|||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
<!-- 播放队列按钮 -->
|
||||||
|
<Button
|
||||||
|
Click="PlayingQueueButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}"
|
||||||
|
Visibility="{x:Bind ShowPlayingQueueButton, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="MusicGalleryPagePlayingQueue" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -174,6 +236,19 @@
|
|||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="3">
|
Spacing="3">
|
||||||
|
|
||||||
|
<!-- Stop media session -->
|
||||||
|
<Button
|
||||||
|
Command="{x:Bind ViewModel.StopTrackCommand}"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=16,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}"
|
||||||
|
Visibility="{x:Bind ShowStopButton, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="MusicGalleryPageStopTrack" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<!-- Volume -->
|
<!-- Volume -->
|
||||||
<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
|
<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -342,13 +417,14 @@
|
|||||||
|
|
||||||
<Slider
|
<Slider
|
||||||
x:Name="TimelineSlider"
|
x:Name="TimelineSlider"
|
||||||
Margin="0,-12,0,0"
|
Margin="0,-14,0,0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Maximum="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DurationMs, Mode=OneWay, Converter={StaticResource MillisecondsToSecondsConverter}}"
|
Maximum="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.DurationMs, Mode=OneWay, Converter={StaticResource MillisecondsToSecondsConverter}}"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
Style="{StaticResource GhostSliderStyle}"
|
Style="{StaticResource GhostSliderStyle}"
|
||||||
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}" />
|
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"
|
||||||
|
Value="{x:Bind ViewModel.GSMTCService.CurrentPosition.TotalSeconds, Mode=OneWay}" />
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="TimelineSliderLyricsLineInfo"
|
x:Name="TimelineSliderLyricsLineInfo"
|
||||||
@@ -356,7 +432,7 @@
|
|||||||
Padding="8,4"
|
Padding="8,4"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
|
Background="{ThemeResource LayerOnMicaBaseAltFillColorDefaultBrush}"
|
||||||
CornerRadius="6"
|
CornerRadius="6"
|
||||||
Opacity="{x:Bind ViewModel.TimelineSliderThumbOpacity, Mode=OneWay}">
|
Opacity="{x:Bind ViewModel.TimelineSliderThumbOpacity, Mode=OneWay}">
|
||||||
<Grid.OpacityTransition>
|
<Grid.OpacityTransition>
|
||||||
@@ -371,7 +447,7 @@
|
|||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||||
<!-- TODO 原文翻译共同显示 -->
|
<!-- TODO 原文翻译共同显示 -->
|
||||||
<TextBlock Margin="0,0,0,2" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.OriginalText, Mode=OneWay}" />
|
<TextBlock Margin="0,0,0,2" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.PrimaryText, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
@@ -427,7 +503,58 @@
|
|||||||
</Grid.ContextFlyout>
|
</Grid.ContextFlyout>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="PlaybackOrderState">
|
||||||
|
<VisualState x:Name="RepeatAll">
|
||||||
|
<VisualState.StateTriggers>
|
||||||
|
<ui:CompareStateTrigger
|
||||||
|
Comparison="Equal"
|
||||||
|
Value="{x:Bind PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||||
|
To="0" />
|
||||||
|
</VisualState.StateTriggers>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="PlaybackRepeatAll.Opacity" Value="1" />
|
||||||
|
<Setter Target="PlaybackRepeatOne.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackShuffle.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Visible" />
|
||||||
|
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="PlaybackShuffleHint.Visibility" Value="Collapsed" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="RepeatOne">
|
||||||
|
<VisualState.StateTriggers>
|
||||||
|
<ui:CompareStateTrigger
|
||||||
|
Comparison="Equal"
|
||||||
|
Value="{x:Bind PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||||
|
To="1" />
|
||||||
|
</VisualState.StateTriggers>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="PlaybackRepeatAll.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackRepeatOne.Opacity" Value="1" />
|
||||||
|
<Setter Target="PlaybackShuffle.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Visible" />
|
||||||
|
<Setter Target="PlaybackShuffleHint.Visibility" Value="Collapsed" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Shuffle">
|
||||||
|
<VisualState.StateTriggers>
|
||||||
|
<ui:CompareStateTrigger
|
||||||
|
Comparison="Equal"
|
||||||
|
Value="{x:Bind PlaybackOrder, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||||
|
To="2" />
|
||||||
|
</VisualState.StateTriggers>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="PlaybackRepeatAll.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackRepeatOne.Opacity" Value="0" />
|
||||||
|
<Setter Target="PlaybackShuffle.Opacity" Value="1" />
|
||||||
|
<Setter Target="PlaybackRepeatAllHint.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="PlaybackRepeatOneHint.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="PlaybackShuffleHint.Visibility" Value="Visible" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@@ -18,15 +15,13 @@ using System.Numerics;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Controls;
|
namespace BetterLyrics.WinUI3.Controls;
|
||||||
|
|
||||||
public sealed partial class NowPlayingBar : UserControl,
|
public sealed partial class NowPlayingBar : UserControl
|
||||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
|
||||||
IRecipient<PropertyChangedMessage<BitmapImage?>>,
|
|
||||||
IRecipient<PropertyChangedMessage<TimeSpan>>
|
|
||||||
{
|
{
|
||||||
public NowPlayingBarViewModel ViewModel => (NowPlayingBarViewModel)DataContext;
|
public NowPlayingBarViewModel ViewModel => (NowPlayingBarViewModel)DataContext;
|
||||||
|
|
||||||
public event EventHandler? SongInfoTapped;
|
public event EventHandler? SongInfoTapped;
|
||||||
public event EventHandler? TimeTapped;
|
public event EventHandler? TimeTapped;
|
||||||
|
public event EventHandler? PlayQueueButtonClick;
|
||||||
|
|
||||||
public bool ShowTime
|
public bool ShowTime
|
||||||
{
|
{
|
||||||
@@ -46,6 +41,42 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
public static readonly DependencyProperty ShowSongInfoProperty =
|
public static readonly DependencyProperty ShowSongInfoProperty =
|
||||||
DependencyProperty.Register(nameof(ShowSongInfo), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
DependencyProperty.Register(nameof(ShowSongInfo), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
public bool ShowPlayingQueueButton
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(ShowPlayingQueueButtonProperty); }
|
||||||
|
set { SetValue(ShowPlayingQueueButtonProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ShowPlayingQueueButtonProperty =
|
||||||
|
DependencyProperty.Register(nameof(ShowPlayingQueueButton), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
public bool ShowPlaybackOrderButton
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(ShowPlaybackOrderButtonProperty); }
|
||||||
|
set { SetValue(ShowPlaybackOrderButtonProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ShowStopButtonProperty =
|
||||||
|
DependencyProperty.Register(nameof(ShowStopButton), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
public bool ShowStopButton
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(ShowStopButtonProperty); }
|
||||||
|
set { SetValue(ShowStopButtonProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ShowPlaybackOrderButtonProperty =
|
||||||
|
DependencyProperty.Register(nameof(ShowPlaybackOrderButton), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
public PlaybackOrder PlaybackOrder
|
||||||
|
{
|
||||||
|
get { return (PlaybackOrder)GetValue(PlaybackOrderProperty); }
|
||||||
|
set { SetValue(PlaybackOrderProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty PlaybackOrderProperty =
|
||||||
|
DependencyProperty.Register(nameof(PlaybackOrder), typeof(PlaybackOrder), typeof(NowPlayingBar), new PropertyMetadata(PlaybackOrder.RepeatAll));
|
||||||
|
|
||||||
public bool IsCompactMode
|
public bool IsCompactMode
|
||||||
{
|
{
|
||||||
get { return (bool)GetValue(IsCompactModeProperty); }
|
get { return (bool)GetValue(IsCompactModeProperty); }
|
||||||
@@ -70,8 +101,6 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = Ioc.Default.GetRequiredService<NowPlayingBarViewModel>();
|
DataContext = Ioc.Default.GetRequiredService<NowPlayingBarViewModel>();
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
@@ -167,7 +196,7 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
var grid = (Grid)sender;
|
var grid = (Grid)sender;
|
||||||
var pos = e.GetCurrentPoint(grid).Position;
|
var pos = e.GetCurrentPoint(grid).Position;
|
||||||
var ratio = pos.X / grid.ActualWidth;
|
var ratio = pos.X / grid.ActualWidth;
|
||||||
ViewModel.MediaSessionsService.ChangePosition(TimelineSlider.Maximum * ratio);
|
ViewModel.GSMTCService.ChangePosition(TimelineSlider.Maximum * ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TimelineSliderOverlay_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
private void TimelineSliderOverlay_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||||
@@ -190,7 +219,7 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
|
|
||||||
private void TimelineSliderOverlay_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
private void TimelineSliderOverlay_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.TimelineSliderThumbOpacity = 0.7f;
|
ViewModel.TimelineSliderThumbOpacity = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TimelineSliderOverlay_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
private void TimelineSliderOverlay_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||||
@@ -210,12 +239,12 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
|
|
||||||
private void SongInfoStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
private void SongInfoStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SongInfoTapped?.Invoke(this, EventArgs.Empty);
|
SongInfoTapped?.Invoke(sender, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TimeStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
private void TimeStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
TimeTapped?.Invoke(this, EventArgs.Empty);
|
TimeTapped?.Invoke(sender, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||||
@@ -262,40 +291,13 @@ public sealed partial class NowPlayingBar : UserControl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
private void PlayingQueueButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (message.Sender is IMediaSessionsService)
|
PlayQueueButtonClick?.Invoke(sender, EventArgs.Empty);
|
||||||
{
|
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
|
||||||
{
|
|
||||||
TitleTextBlock.Text = message.NewValue?.Title;
|
|
||||||
ArtistsTextBlock.Text = message.NewValue?.DisplayArtists;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void Receive(PropertyChangedMessage<BitmapImage?> message)
|
|
||||||
{
|
|
||||||
if (message.Sender is IMediaSessionsService)
|
|
||||||
{
|
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtBitmapImage))
|
|
||||||
{
|
|
||||||
AlbumArtImageSwitcher.Source = message.NewValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
private void PlaybackOrderButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (message.Sender is IMediaSessionsService)
|
PlaybackOrder = PlaybackOrder.GetNext();
|
||||||
{
|
|
||||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
|
|
||||||
{
|
|
||||||
DispatcherQueue.TryEnqueue(() =>
|
|
||||||
{
|
|
||||||
TimelineSlider.Value = message.NewValue.TotalSeconds;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.PatronControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid Margin="12,8">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind PatronName, Mode=OneWay}" />
|
||||||
|
<TextBlock Foreground="{ThemeResource TextFillColorTertiaryBrush}" Text="{x:Bind Date, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class PatronControl : UserControl
|
||||||
|
{
|
||||||
|
public string PatronName
|
||||||
|
{
|
||||||
|
get { return (string)GetValue(PatronNameProperty); }
|
||||||
|
set { SetValue(PatronNameProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty PatronNameProperty =
|
||||||
|
DependencyProperty.Register(nameof(PatronName), typeof(string), typeof(PatronControl), new PropertyMetadata(""));
|
||||||
|
|
||||||
|
public string Date
|
||||||
|
{
|
||||||
|
get { return (string)GetValue(DateProperty); }
|
||||||
|
set { SetValue(DateProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty DateProperty =
|
||||||
|
DependencyProperty.Register(nameof(Date), typeof(string), typeof(PatronControl), new PropertyMetadata(""));
|
||||||
|
|
||||||
|
public PatronControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
142
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/PlayQueue.xaml
Normal file
142
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/PlayQueue.xaml
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.PlayQueue"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.TranslationTransition>
|
||||||
|
<Vector3Transition />
|
||||||
|
</Grid.TranslationTransition>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0" Margin="12,12,12,0">
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="MusicGalleryPagePlayingQueue"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Margin="12,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=OneWay, Converter={StaticResource IndexToDisplayConverter}}" />
|
||||||
|
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||||
|
<TextBlock Text="{x:Bind ViewModel.SMTCService.TrackPlayingQueue.Count, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Scroll to playing item -->
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Click="ScrollToPlayingItemButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=16,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="MusicGalleryPageScrollToPlayingItem" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
<!-- Empty play queue -->
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Click="EmptyPlayingQueueButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=16,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="MusicGalleryPageEmptyPlayingQueue" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<NavigationViewItemSeparator Grid.Row="2" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
x:Name="PlayingQueueListView"
|
||||||
|
Grid.Row="3"
|
||||||
|
ItemsSource="{x:Bind ViewModel.SMTCService.TrackPlayingQueue, Mode=OneWay}"
|
||||||
|
SelectedIndex="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=TwoWay}">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid Padding="0,6">
|
||||||
|
<Grid Tapped="PlayingQueueListVireItemGrid_Tapped">
|
||||||
|
<StackPanel Margin="0,0,36,0">
|
||||||
|
<TextBlock Text="{Binding Track.Title}" TextWrapping="Wrap" />
|
||||||
|
<TextBlock
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Text="{Binding Track.Artist}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Grid HorizontalAlignment="Right">
|
||||||
|
<Button
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Click="RemoveFromPlayingQueueButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=16,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<TextBlock x:Uid="MusicGalleryPageRemoveFromPlayingQueue" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
|
||||||
|
<Grid Grid.Row="3">
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind ViewModel.SMTCService.TrackPlayingQueue.Count, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="0">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind ViewModel.SMTCService.TrackPlayingQueue.Count, Mode=OneWay}"
|
||||||
|
ComparisonCondition="NotEqual"
|
||||||
|
Value="0">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Spacing="12">
|
||||||
|
<Image MaxWidth="100" Source="/Assets/EmptyBox.png" />
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="MusicGalleryPagePlayingQueueEmpty"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class PlayQueue : UserControl, IRecipient<PropertyChangedMessage<int>>
|
||||||
|
{
|
||||||
|
public PlayQueueViewModel ViewModel => (PlayQueueViewModel)DataContext;
|
||||||
|
public PlayQueue()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = Ioc.Default.GetRequiredService<PlayQueueViewModel>();
|
||||||
|
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScrollToPlayingItem()
|
||||||
|
{
|
||||||
|
if (PlayingQueueListView == null) return;
|
||||||
|
|
||||||
|
var targetItem = ViewModel.SMTCService.TrackPlayingQueue
|
||||||
|
.ElementAtOrDefault(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
|
||||||
|
if (targetItem == null) return;
|
||||||
|
|
||||||
|
PlayingQueueListView.ScrollIntoView(targetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScrollToPlayingItemButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ScrollToPlayingItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PlayingQueueListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
|
||||||
|
await ViewModel.SMTCService.PlayTrackAsync(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RemoveFromPlayingQueueButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool playNext = false;
|
||||||
|
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
|
||||||
|
int index = ViewModel.SMTCService.TrackPlayingQueue.IndexOf(item);
|
||||||
|
if (item == PlayingQueueListView.SelectedItem)
|
||||||
|
{
|
||||||
|
playNext = true;
|
||||||
|
}
|
||||||
|
ViewModel.SMTCService.TrackPlayingQueue.Remove(item);
|
||||||
|
if (playNext)
|
||||||
|
{
|
||||||
|
if (ViewModel.SMTCService.TrackPlayingQueue.Count == 0)
|
||||||
|
{
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
else if (index >= ViewModel.SMTCService.TrackPlayingQueue.Count)
|
||||||
|
{
|
||||||
|
index = ViewModel.SMTCService.TrackPlayingQueue.Count - 1;
|
||||||
|
}
|
||||||
|
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = index;
|
||||||
|
await ViewModel.SMTCService.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EmptyPlayingQueueButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.SMTCService.TrackPlayingQueue.Clear();
|
||||||
|
ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex = -1;
|
||||||
|
await ViewModel.SMTCService.PlayTrackAtAsync(ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<int> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is MusicGallerySettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(MusicGallerySettings.PlayQueueIndex))
|
||||||
|
{
|
||||||
|
ScrollToPlayingItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||||
|
xmlns:settingsmodels="using:BetterLyrics.WinUI3.Models.Settings"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
@@ -44,10 +45,10 @@
|
|||||||
</interactivity:Interaction.Behaviors>
|
</interactivity:Interaction.Behaviors>
|
||||||
|
|
||||||
<InfoBar
|
<InfoBar
|
||||||
|
x:Uid="SettingsPageMusicGalleryOpened"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
IsClosable="False"
|
IsClosable="False"
|
||||||
IsOpen="{x:Bind ViewModel.AppSettings.MusicGallerySettings.LyricsWindowStatus.IsOpened, Mode=OneWay}"
|
IsOpen="{x:Bind ViewModel.AppSettings.MusicGallerySettings.LyricsWindowStatus.IsOpened, Mode=OneWay}"
|
||||||
Message="音乐库窗口已打开,将忽略对其他播放源的监听"
|
|
||||||
Severity="Informational" />
|
Severity="Informational" />
|
||||||
|
|
||||||
<!-- 播放源列表 -->
|
<!-- 播放源列表 -->
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
|
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||||
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
|
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
|
<DataTemplate x:DataType="settingsmodels:MediaSourceProviderInfo">
|
||||||
<Grid Padding="2,4" ColumnSpacing="12">
|
<Grid Padding="2,4" ColumnSpacing="12">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
<ScalarTransition />
|
<ScalarTransition />
|
||||||
</ListView.OpacityTransition>
|
</ListView.OpacityTransition>
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:AlbumArtSearchProviderInfo">
|
<DataTemplate x:DataType="settingsmodels:AlbumArtSearchProviderInfo">
|
||||||
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||||
<dev:SettingsCard.HeaderIcon>
|
<dev:SettingsCard.HeaderIcon>
|
||||||
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</ListView.ItemContainerStyle>
|
</ListView.ItemContainerStyle>
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
<DataTemplate x:DataType="settingsmodels:LyricsSearchProviderInfo">
|
||||||
<Grid>
|
<Grid>
|
||||||
<dev:SettingsExpander Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}" IsExpanded="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
<dev:SettingsExpander Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}" IsExpanded="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
||||||
<dev:SettingsExpander.HeaderIcon>
|
<dev:SettingsExpander.HeaderIcon>
|
||||||
@@ -227,6 +228,9 @@
|
|||||||
</dev:SettingsExpander.HeaderIcon>
|
</dev:SettingsExpander.HeaderIcon>
|
||||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
||||||
<dev:SettingsExpander.Items>
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache">
|
||||||
|
<CheckBox IsChecked="{Binding IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageOverwriteMatchingThreshold">
|
<dev:SettingsCard x:Uid="SettingsPageOverwriteMatchingThreshold">
|
||||||
<ToggleSwitch IsOn="{Binding IsMatchingThresholdOverwritten, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{Binding IsMatchingThresholdOverwritten, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
@@ -314,8 +318,8 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Left">
|
HorizontalContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<local:PropertyRow x:Uid="SettingsPagePlaybackSource" Value="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPagePlaybackSource" Value="{x:Bind ViewModel.GSMTCService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPagePlaybackSourceID" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPagePlaybackSourceID" Value="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
||||||
@@ -325,13 +329,10 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Left">
|
HorizontalContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Artist, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow
|
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.DurationMs, TargetNullValue=N/A, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||||
x:Uid="LyricsSearchControlDurauion"
|
|
||||||
Unit="s"
|
|
||||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
||||||
@@ -341,28 +342,22 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Left">
|
HorizontalContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Artist, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<local:PropertyRow
|
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
x:Uid="LyricsSearchControlDurauion"
|
<local:PropertyRow x:Uid="LyricsPageLanguageCode" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsData.LanguageCode, TargetNullValue=N/A, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||||
Unit="s"
|
|
||||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
<local:PropertyRow
|
<local:PropertyRow
|
||||||
x:Uid="LyricsPageLyricsProviderPrefix"
|
x:Uid="LyricsPageLyricsProviderPrefix"
|
||||||
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}"
|
Link="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Reference, Mode=OneWay, Converter={StaticResource UriStringToDecodedAbsoluteUri}}"
|
||||||
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay}"
|
ToolTipService.ToolTip="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay, Converter={StaticResource UriStringToDecodedAbsoluteUri}}"
|
||||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||||
<local:PropertyRow x:Uid="LyricsPageTransliterationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TransliterationProvider, Mode=OneWay, Converter={StaticResource TransliterationSearchProviderToDisplayNameConverter}}" />
|
<local:PropertyRow x:Uid="LyricsPageTransliterationProviderPrefix" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.TransliterationProvider, Mode=OneWay, Converter={StaticResource TransliterationSearchProviderToDisplayNameConverter}}" />
|
||||||
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TranslationProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.TranslationProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||||
<local:PropertyRow
|
<local:PropertyRow
|
||||||
x:Uid="LyricsPageMatchPercentage"
|
x:Uid="LyricsPageMatchPercentage"
|
||||||
Unit="%"
|
Unit="%"
|
||||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
Value="{x:Bind ViewModel.GSMTCService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
||||||
<local:PropertyRow
|
|
||||||
x:Uid="LyricsPageCachePath"
|
|
||||||
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
|
||||||
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
||||||
@@ -370,10 +365,6 @@
|
|||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<!-- Lyrics translation -->
|
<!-- Lyrics translation -->
|
||||||
<TextBlock x:Uid="SettingsPageTranslation" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageTranslation" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
<dev:SettingsExpander x:Uid="LyricsPageTranslationEnabled" IsExpanded="True">
|
<dev:SettingsExpander x:Uid="LyricsPageTranslationEnabled" IsExpanded="True">
|
||||||
@@ -397,16 +388,23 @@
|
|||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
|
<dev:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
<Grid ColumnSpacing="6">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Uid="LibreTranslateServerTextBox"
|
x:Uid="LibreTranslateServerTextBox"
|
||||||
|
Grid.Column="0"
|
||||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||||
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.LibreTranslateServer, Mode=TwoWay}" />
|
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.LibreTranslateServer, Mode=TwoWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
<Button
|
<Button
|
||||||
x:Uid="SettingsPageServerTestButton"
|
x:Uid="SettingsPageServerTestButton"
|
||||||
|
Grid.Column="1"
|
||||||
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
|
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
|
||||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</Grid>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
@@ -424,9 +422,33 @@
|
|||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageJapanese">
|
<dev:SettingsExpander x:Uid="SettingsPageJapanese" IsExpanded="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=OneWay}">
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageCutletDockerServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=OneWay}">
|
||||||
|
<dev:SettingsCard.Description>
|
||||||
|
<HyperlinkButton Content="https://github.com/jayfunc/cutlet-docker" NavigateUri="https://github.com/jayfunc/cutlet-docker" />
|
||||||
|
</dev:SettingsCard.Description>
|
||||||
|
<Grid ColumnSpacing="6">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox
|
||||||
|
x:Uid="CutletServerTextBox"
|
||||||
|
Grid.Column="0"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCutletDockerServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||||
|
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.CutletDockerServer, Mode=TwoWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
x:Uid="SettingsPageServerTestButton"
|
||||||
|
Grid.Column="1"
|
||||||
|
Command="{x:Bind ViewModel.CutletDockerServerTestCommand}"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCutletDockerServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<!-- 中文简体繁体偏好 -->
|
<!-- 中文简体繁体偏好 -->
|
||||||
<TextBlock x:Uid="SettingsPageChineseLyrics" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageChineseLyrics" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
@@ -435,9 +457,9 @@
|
|||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<!-- Last.fm -->
|
<!-- Last.fm -->
|
||||||
<TextBlock x:Uid="SettingsPageLastFM" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="Last.fm" />
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageLastFMManager"
|
Header="Last.fm"
|
||||||
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
|
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
|
||||||
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.RemoteServerConfigControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel Width="400" Spacing="16">
|
||||||
|
<ProgressBar
|
||||||
|
x:Name="ProgressBar"
|
||||||
|
IsIndeterminate="True"
|
||||||
|
Visibility="Collapsed" />
|
||||||
|
<InfoBar
|
||||||
|
x:Name="ErrorInfoBar"
|
||||||
|
IsClosable="True"
|
||||||
|
IsOpen="False"
|
||||||
|
Severity="Error" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
x:Name="NameBox"
|
||||||
|
x:Uid="RemoteServerConfigControlName"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<StackPanel x:Name="RemoteFieldsPanel" Spacing="16">
|
||||||
|
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="12">
|
||||||
|
<TextBox
|
||||||
|
x:Name="HostBox"
|
||||||
|
x:Uid="RemoteServerConfigControlServerAddress"
|
||||||
|
Grid.Column="0"
|
||||||
|
InputScope="Url"
|
||||||
|
PlaceholderText="192.168.1.x"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<NumberBox
|
||||||
|
x:Name="PortBox"
|
||||||
|
x:Uid="RemoteServerConfigControlPort"
|
||||||
|
Grid.Column="1"
|
||||||
|
MinWidth="100"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
ToolTipService.ToolTip="80"
|
||||||
|
Value="80" />
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="8">
|
||||||
|
<TextBox
|
||||||
|
x:Name="PathBox"
|
||||||
|
x:Uid="RemoteServerConfigControlPath"
|
||||||
|
Grid.Column="0"
|
||||||
|
TextChanged="PathBox_TextChanged"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="BrowseButton"
|
||||||
|
x:Uid="RemoteServerConfigControlBrowse"
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Click="BrowseButton_Click"
|
||||||
|
Visibility="Collapsed" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<InfoBar
|
||||||
|
x:Name="PathWarningBar"
|
||||||
|
IsClosable="False"
|
||||||
|
IsOpen="False"
|
||||||
|
Severity="Warning" />
|
||||||
|
|
||||||
|
<StackPanel x:Name="AuthFieldsPanel" Spacing="16">
|
||||||
|
<Grid ColumnDefinitions="*, *" ColumnSpacing="12">
|
||||||
|
<TextBox
|
||||||
|
x:Name="UserBox"
|
||||||
|
x:Uid="RemoteServerConfigControlUsername"
|
||||||
|
Grid.Column="0"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<PasswordBox
|
||||||
|
x:Name="PwdBox"
|
||||||
|
x:Uid="RemoteServerConfigControlPassword"
|
||||||
|
Grid.Column="1"
|
||||||
|
PasswordRevealMode="Peek" />
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
|
using BetterLyrics.WinUI3.Views;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class RemoteServerConfigControl : UserControl
|
||||||
|
{
|
||||||
|
private readonly FileSourceType _fileSourceType;
|
||||||
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
|
public RemoteServerConfigControl(FileSourceType fileSourceType)
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
_fileSourceType = fileSourceType;
|
||||||
|
|
||||||
|
SetupDefaults();
|
||||||
|
CheckPathForWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupDefaults()
|
||||||
|
{
|
||||||
|
if (_fileSourceType == FileSourceType.Local)
|
||||||
|
{
|
||||||
|
RemoteFieldsPanel.Visibility = Visibility.Collapsed;
|
||||||
|
AuthFieldsPanel.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
BrowseButton.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
PathBox.PlaceholderText = @"D:\Music";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BrowseButton.Visibility = Visibility.Collapsed;
|
||||||
|
RemoteFieldsPanel.Visibility = Visibility.Visible;
|
||||||
|
AuthFieldsPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
switch (_fileSourceType)
|
||||||
|
{
|
||||||
|
case FileSourceType.SMB:
|
||||||
|
PortBox.Value = 445;
|
||||||
|
PathBox.PlaceholderText = "SharedMusic";
|
||||||
|
break;
|
||||||
|
case FileSourceType.FTP:
|
||||||
|
PortBox.Value = 21;
|
||||||
|
PathBox.PlaceholderText = "/pub/music";
|
||||||
|
break;
|
||||||
|
case FileSourceType.WebDAV:
|
||||||
|
PortBox.Value = 80;
|
||||||
|
PathBox.PlaceholderText = "/dav/music";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetScheme()
|
||||||
|
{
|
||||||
|
string scheme = string.Empty;
|
||||||
|
switch (_fileSourceType)
|
||||||
|
{
|
||||||
|
case FileSourceType.SMB:
|
||||||
|
scheme = "smb";
|
||||||
|
break;
|
||||||
|
case FileSourceType.FTP:
|
||||||
|
scheme = "ftp";
|
||||||
|
break;
|
||||||
|
case FileSourceType.WebDAV:
|
||||||
|
scheme = "https";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaFolder GetConfig()
|
||||||
|
{
|
||||||
|
string finalName = HostBox.Text.Trim();
|
||||||
|
|
||||||
|
if (_fileSourceType == FileSourceType.Local)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(PathBox.Text))
|
||||||
|
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlPathRequired"));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||||
|
finalName = NameBox.Text.Trim();
|
||||||
|
else
|
||||||
|
finalName = PathBox.Text.TrimEnd(System.IO.Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
|
return new MediaFolder
|
||||||
|
{
|
||||||
|
Name = finalName,
|
||||||
|
SourceType = FileSourceType.Local,
|
||||||
|
UriScheme = "file",
|
||||||
|
UriPath = PathBox.Text.Trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(HostBox.Text))
|
||||||
|
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlServerAddressRequired"));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||||
|
{
|
||||||
|
finalName = NameBox.Text.Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finalName = $"{_fileSourceType} - {HostBox.Text}";
|
||||||
|
}
|
||||||
|
|
||||||
|
string scheme = GetScheme();
|
||||||
|
|
||||||
|
var folder = new MediaFolder
|
||||||
|
{
|
||||||
|
Name = finalName,
|
||||||
|
SourceType = _fileSourceType,
|
||||||
|
|
||||||
|
UriScheme = scheme,
|
||||||
|
UriHost = HostBox.Text.Trim(), // ȥ<><C8A5><EFBFBD><EFBFBD>β<EFBFBD>ո<EFBFBD>
|
||||||
|
UriPort = (int)PortBox.Value,
|
||||||
|
|
||||||
|
UriPath = PathBox.Text.Trim(),
|
||||||
|
|
||||||
|
UserName = UserBox.Text.Trim(),
|
||||||
|
Password = PwdBox.Password,
|
||||||
|
};
|
||||||
|
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowError(string? message)
|
||||||
|
{
|
||||||
|
ErrorInfoBar.Message = message;
|
||||||
|
ErrorInfoBar.IsOpen = !string.IsNullOrWhiteSpace(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProgressBarVisibility(Visibility visibility)
|
||||||
|
{
|
||||||
|
ProgressBar.Visibility = visibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckPathForWarning()
|
||||||
|
{
|
||||||
|
string? path = PathBox.Text?.Trim();
|
||||||
|
|
||||||
|
bool isSymbolRoot = string.IsNullOrEmpty(path) ||
|
||||||
|
path == "/" ||
|
||||||
|
path == "\\";
|
||||||
|
|
||||||
|
bool isDriveRoot = false;
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
var normalized = path.TrimEnd('\\', '/');
|
||||||
|
isDriveRoot = normalized.EndsWith(":") && normalized.Length == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRoot = isSymbolRoot || isDriveRoot;
|
||||||
|
|
||||||
|
if (isRoot)
|
||||||
|
{
|
||||||
|
PathWarningBar.Message = _localizationService.GetLocalizedString("FileSystemServiceRootDirectoryWarning");
|
||||||
|
PathWarningBar.IsOpen = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PathWarningBar.IsOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PathBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CheckPathForWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var folder = await PickerHelper.PickSingleFolderAsync<SettingsWindow>();
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
PathBox.Text = folder.Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Input;
|
using Microsoft.UI.Input;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@@ -16,7 +16,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
public sealed partial class ShortcutTextBox : UserControl
|
public sealed partial class ShortcutTextBox : UserControl
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public ShortcutTextBox()
|
public ShortcutTextBox()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,411 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.StatsDashboardControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
|
xmlns:converters="using:BetterLyrics.WinUI3.Converter"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dev="using:DevWinUI"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.WinUI"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||||
|
xmlns:statsmodels="using:BetterLyrics.WinUI3.Models.Stats"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style x:Key="StatsCardStyle" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="CornerRadius" Value="8" />
|
||||||
|
<Setter Property="Padding" Value="16" />
|
||||||
|
<Setter Property="Margin" Value="0,0,12,12" />
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
Grid.Row="0"
|
||||||
|
Background="Transparent"
|
||||||
|
IsIndeterminate="{x:Bind ViewModel.IsLoading, Mode=OneWay}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.GSMTCService.IsScrobbled, Mode=OneWay, Converter={StaticResource BoolNegationToVisibilityConverter}}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<InfoBar
|
||||||
|
x:Uid="StatsDashboardControlRecording"
|
||||||
|
Grid.Row="0"
|
||||||
|
IsClosable="False"
|
||||||
|
IsOpen="True"
|
||||||
|
Message="{x:Bind ViewModel.GSMTCService.CurrentSongInfo.Title, Mode=OneWay}" />
|
||||||
|
<ProgressBar
|
||||||
|
Grid.Row="1"
|
||||||
|
Background="Transparent"
|
||||||
|
Maximum="{x:Bind ViewModel.GSMTCService.TargetScrobbledDuration.TotalSeconds, Mode=OneWay}"
|
||||||
|
ShowPaused="{x:Bind ViewModel.GSMTCService.CurrentIsPlaying, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
|
||||||
|
Value="{x:Bind ViewModel.GSMTCService.ScrobbledDuration.TotalSeconds, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<controls:WrapPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="36,36,36,12"
|
||||||
|
HorizontalSpacing="12"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
VerticalSpacing="12">
|
||||||
|
<ComboBox
|
||||||
|
x:Uid="StatsDashboardControlTimeRange"
|
||||||
|
Header="Time Range"
|
||||||
|
SelectedIndex="{x:Bind ViewModel.SelectedTimeRange, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlToday" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlThisWeek" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlThisMonth" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlThisQuarter" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlThisYear" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlAllTime" />
|
||||||
|
<ComboBoxItem x:Uid="StatsDashboardControlCustom" />
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<CalendarDatePicker
|
||||||
|
x:Uid="StatsDashboardControlStart"
|
||||||
|
Date="{x:Bind ViewModel.CustomStartDate, Mode=TwoWay}"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCustomRangeSelected, Mode=OneWay}" />
|
||||||
|
<TimePicker
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCustomRangeSelected, Mode=OneWay}"
|
||||||
|
Time="{x:Bind ViewModel.CustomStartTime, Mode=TwoWay}" />
|
||||||
|
<CalendarDatePicker
|
||||||
|
x:Uid="StatsDashboardControlEnd"
|
||||||
|
Date="{x:Bind ViewModel.CustomEndDate, Mode=TwoWay}"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCustomRangeSelected, Mode=OneWay}" />
|
||||||
|
<TimePicker
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
IsEnabled="{x:Bind ViewModel.IsCustomRangeSelected, Mode=OneWay}"
|
||||||
|
Time="{x:Bind ViewModel.CustomEndTime, Mode=TwoWay}" />
|
||||||
|
<Button
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Command="{x:Bind ViewModel.RefreshDataCommand}"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=16,
|
||||||
|
Glyph=}" />
|
||||||
|
</controls:WrapPanel>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="3" Padding="36,0">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Column="0" Style="{StaticResource StatsCardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
Opacity="0.8"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<FontIcon FontSize="14" Glyph="" />
|
||||||
|
<TextBlock x:Uid="StatsDashboardControlTotalDuration" Style="{ThemeResource CaptionTextBlockStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="4">
|
||||||
|
<TextBlock
|
||||||
|
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}"
|
||||||
|
Text="{x:Bind ViewModel.TotalDuration.TotalHours, Mode=OneWay, Converter={StaticResource DoubleToDecimalConverter}}" />
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,0,0,2"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||||
|
Opacity="0.8"
|
||||||
|
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||||
|
Text="Hrs" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Grid.Column="1" Style="{StaticResource StatsCardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
Opacity="0.8"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<FontIcon FontSize="14" Glyph="" />
|
||||||
|
<TextBlock x:Uid="StatsDashboardControlTracksPlayed" Style="{ThemeResource CaptionTextBlockStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}"
|
||||||
|
Text="{x:Bind ViewModel.TotalTracksPlayed, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{StaticResource StatsCardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
Opacity="0.8"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<FontIcon FontSize="14" Glyph="" />
|
||||||
|
<TextBlock x:Uid="StatsDashboardControlTopSource" Style="{ThemeResource CaptionTextBlockStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}"
|
||||||
|
Text="{x:Bind ViewModel.TopPlayerName, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Activity by hour -->
|
||||||
|
<Border
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{StaticResource StatsCardStyle}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<TextBlock x:Uid="StatsDashboardControlActivityByHour" Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Margin="0,0,0,16">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="StatsDashboardControlMostActive"
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||||
|
<TextBlock FontWeight="SemiBold" Text="{x:Bind ViewModel.PeakHourText, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="1">
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="StatsDashboardControlLeastActive"
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||||
|
<TextBlock FontWeight="SemiBold" Text="{x:Bind ViewModel.QuietHourText, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<lvc:CartesianChart
|
||||||
|
Grid.Row="2"
|
||||||
|
Height="180"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Background="Transparent"
|
||||||
|
TooltipPosition="Top">
|
||||||
|
|
||||||
|
<lvc:CartesianChart.XAxes>
|
||||||
|
<lvc:AxesCollection>
|
||||||
|
<lvc:XamlAxis Labels="{x:Bind ViewModel.HourlyXAxisLabels, Mode=OneWay}" TextSize="{StaticResource BodyTextBlockFontSize}" />
|
||||||
|
</lvc:AxesCollection>
|
||||||
|
</lvc:CartesianChart.XAxes>
|
||||||
|
|
||||||
|
<lvc:CartesianChart.YAxes>
|
||||||
|
<lvc:AxesCollection>
|
||||||
|
<lvc:XamlAxis
|
||||||
|
x:Uid="StatsDashboardControlTrackCountAxis"
|
||||||
|
NameTextSize="{StaticResource BodyTextBlockFontSize}"
|
||||||
|
ShowSeparatorLines="False"
|
||||||
|
TextSize="{StaticResource BodyTextBlockFontSize}" />
|
||||||
|
</lvc:AxesCollection>
|
||||||
|
</lvc:CartesianChart.YAxes>
|
||||||
|
|
||||||
|
<lvc:CartesianChart.Series>
|
||||||
|
<lvc:SeriesCollection>
|
||||||
|
<lvc:XamlColumnSeries
|
||||||
|
x:Name="HourlySeries"
|
||||||
|
Rx="4"
|
||||||
|
Ry="4"
|
||||||
|
Values="{x:Bind ViewModel.HourlySeriesValues, Mode=OneWay}" />
|
||||||
|
</lvc:SeriesCollection>
|
||||||
|
</lvc:CartesianChart.Series>
|
||||||
|
|
||||||
|
</lvc:CartesianChart>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Top artists and sources -->
|
||||||
|
<Grid Grid.Row="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Top artists -->
|
||||||
|
<Border Grid.Column="0" Style="{StaticResource StatsCardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="StatsDashboardControlTopArtists"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
|
||||||
|
<ItemsControl ItemsSource="{x:Bind ViewModel.TopArtists, Mode=OneWay}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="statsmodels:ArtistPlayCount">
|
||||||
|
<Grid Margin="0,4">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||||
|
<PersonPicture
|
||||||
|
Width="32"
|
||||||
|
Height="32"
|
||||||
|
DisplayName="{x:Bind Artist}" />
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="{x:Bind Artist}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontWeight="SemiBold">
|
||||||
|
<Run Text="{x:Bind PlayCount}" />
|
||||||
|
<Run
|
||||||
|
x:Uid="StatsDashboardControlTrackCountText"
|
||||||
|
FontSize="10"
|
||||||
|
FontWeight="Normal"
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Top tracks -->
|
||||||
|
<Border
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{StaticResource StatsCardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="StatsDashboardControlTopSongs"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
<ItemsControl ItemsSource="{x:Bind ViewModel.TopSongs, Mode=OneWay}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="statsmodels:SongPlayCount">
|
||||||
|
<Grid Margin="0,4">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid
|
||||||
|
Width="40"
|
||||||
|
Height="40"
|
||||||
|
Margin="0,0,12,0"
|
||||||
|
Background="{ThemeResource LayerFillColorAltBrush}"
|
||||||
|
CornerRadius="4">
|
||||||
|
<FontIcon
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||||
|
Glyph="" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{ThemeResource BodyStrongTextBlockStyle}" Text="{x:Bind Title}" />
|
||||||
|
<TextBlock
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||||
|
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind Artist}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontWeight="SemiBold">
|
||||||
|
<Run Text="{x:Bind PlayCount}" />
|
||||||
|
<Run
|
||||||
|
x:Uid="StatsDashboardControlTrackCountText"
|
||||||
|
FontSize="10"
|
||||||
|
FontWeight="Normal"
|
||||||
|
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- 播放源分布 -->
|
||||||
|
<Border
|
||||||
|
Grid.Row="3"
|
||||||
|
Margin="0,0,0,20"
|
||||||
|
Style="{StaticResource StatsCardStyle}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="StatsDashboardControlSources"
|
||||||
|
Margin="0,0,0,12"
|
||||||
|
Style="{ThemeResource SubtitleTextBlockStyle}" />
|
||||||
|
|
||||||
|
<lvc:PieChart
|
||||||
|
Grid.Row="1"
|
||||||
|
MinHeight="250"
|
||||||
|
Background="Transparent"
|
||||||
|
LegendPosition="Bottom"
|
||||||
|
LegendTextSize="{StaticResource BodyTextBlockFontSize}"
|
||||||
|
Series="{x:Bind ViewModel.SourceSeries, Mode=OneWay}"
|
||||||
|
TooltipPosition="Center" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!--<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Command="{x:Bind ViewModel.GenerateTestDataCommand}"
|
||||||
|
Content="Generate test data" />-->
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Controls;
|
||||||
|
|
||||||
|
public sealed partial class StatsDashboardControl : UserControl
|
||||||
|
{
|
||||||
|
public StatsDashboardControlViewModel ViewModel => (StatsDashboardControlViewModel)DataContext;
|
||||||
|
|
||||||
|
public StatsDashboardControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = Ioc.Default.GetRequiredService<StatsDashboardControlViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
x:Name="TrayIcon"
|
x:Name="TrayIcon"
|
||||||
x:FieldModifier="public"
|
x:FieldModifier="public"
|
||||||
ContextMenuMode="SecondWindow"
|
ContextMenuMode="SecondWindow"
|
||||||
DoubleClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
|
|
||||||
IconSource="ms-appx:///Assets/Logo.ico"
|
IconSource="ms-appx:///Assets/Logo.ico"
|
||||||
LeftClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
|
LeftClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
|
||||||
NoLeftClickDelay="True"
|
NoLeftClickDelay="True"
|
||||||
|
|||||||
@@ -18,13 +18,7 @@
|
|||||||
<TextBlock x:Uid="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<StackPanel
|
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap" />
|
||||||
Margin="0,6,0,0"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Spacing="6">
|
|
||||||
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
|
||||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
|
||||||
</StackPanel>
|
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
@@ -102,6 +96,10 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageAlwaysHideUnlockButton" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysHideUnlockButton, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageAOT"
|
x:Uid="SettingsPageAOT"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using BetterLyrics.WinUI3.Models.Settings;
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
using System;
|
using System;
|
||||||
@@ -8,7 +8,7 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
|
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
@@ -16,8 +16,8 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
return provider switch
|
return provider switch
|
||||||
{
|
{
|
||||||
AlbumArtSearchProvider.Local => _resourceService.GetLocalizedString("AlbumArtSearchLocalProvider"),
|
AlbumArtSearchProvider.Local => _localizationService.GetLocalizedString("AlbumArtSearchLocalProvider"),
|
||||||
AlbumArtSearchProvider.SMTC => _resourceService.GetLocalizedString("AlbumArtSearchSMTCProvider"),
|
AlbumArtSearchProvider.SMTC => _localizationService.GetLocalizedString("AlbumArtSearchSMTCProvider"),
|
||||||
AlbumArtSearchProvider.iTunes => "iTunes",
|
AlbumArtSearchProvider.iTunes => "iTunes",
|
||||||
_ => throw new Exception($"Unknown AlbumArtSearchProvider: {provider}"),
|
_ => throw new Exception($"Unknown AlbumArtSearchProvider: {provider}"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,20 +17,16 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
using (var ms = new MemoryStream(byteArray))
|
using (var ms = new MemoryStream(byteArray))
|
||||||
{
|
{
|
||||||
var stream = ms.AsRandomAccessStream();
|
var stream = ms.AsRandomAccessStream();
|
||||||
|
|
||||||
var bitmapImage = new BitmapImage();
|
var bitmapImage = new BitmapImage();
|
||||||
|
|
||||||
bitmapImage.SetSource(stream);
|
bitmapImage.SetSource(stream);
|
||||||
|
|
||||||
return bitmapImage;
|
return bitmapImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch { }
|
||||||
{
|
|
||||||
return PathHelper.AlbumArtPlaceholderPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PathHelper.AlbumArtPlaceholderPath;
|
return new BitmapImage(new Uri(PathHelper.AlbumArtPlaceholderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
if (value is string langCode)
|
if (value is string langCode)
|
||||||
{
|
{
|
||||||
return LanguageHelper.SupportedDisplayLanguages.FindIndex(x => x.LanguageCode == langCode);
|
var found = LanguageHelper.SupportedDisplayLanguages.FindIndex(x => x.LanguageCode == langCode);
|
||||||
|
return found == -1 ? 0 : found;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class DoubleToDecimalConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value == null) return string.Empty;
|
||||||
|
|
||||||
|
if (double.TryParse(value.ToString(), out double number))
|
||||||
|
{
|
||||||
|
int decimalPlaces = 2;
|
||||||
|
if (parameter != null && int.TryParse(parameter.ToString(), out int parsedParams))
|
||||||
|
{
|
||||||
|
decimalPlaces = parsedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number.ToString($"F{decimalPlaces}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.ToString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class FileSourceTypeToIconConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is FileSourceType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
FileSourceType.Local => "\uE8B7", // Folder
|
||||||
|
FileSourceType.SMB => "\uE839", // Network
|
||||||
|
FileSourceType.FTP => "\uE838", // Globe
|
||||||
|
FileSourceType.WebDAV => "\uE753", // Cloud
|
||||||
|
_ => "\uE8B7"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return "\uE8B7";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,11 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
if (value is string langCode)
|
if (value is string langCode)
|
||||||
{
|
{
|
||||||
if (PhoneticHelper.IsPhoneticCode(langCode))
|
if (langCode == "N/A")
|
||||||
|
{
|
||||||
|
return langCode;
|
||||||
|
}
|
||||||
|
else if (PhoneticHelper.IsPhoneticCode(langCode))
|
||||||
{
|
{
|
||||||
return PhoneticHelper.GetDisplayName(langCode);
|
return PhoneticHelper.GetDisplayName(langCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
using System;
|
using System;
|
||||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
public partial class LyricsSearchProviderToDisplayNameConverter : IValueConverter
|
public partial class LyricsSearchProviderToDisplayNameConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
@@ -24,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
LyricsSearchProvider.Kugou => "酷狗音乐",
|
LyricsSearchProvider.Kugou => "酷狗音乐",
|
||||||
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||||
LyricsSearchProvider.AppleMusic => "Apple Music",
|
LyricsSearchProvider.AppleMusic => "Apple Music",
|
||||||
LyricsSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
LyricsSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||||
LyricsSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
LyricsSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||||
LyricsSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
LyricsSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||||
LyricsSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
LyricsSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||||
_ => "N/A",
|
_ => "N/A",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
|
||||||
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
@@ -10,13 +11,13 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
public partial class MatchedLocalFilesPathToVisibilityConverter : IValueConverter
|
public partial class MatchedLocalFilesPathToVisibilityConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
if (value is string path)
|
if (value is string path)
|
||||||
{
|
{
|
||||||
if (path == _resourceService.GetLocalizedString("MainPageNoLocalFilesMatched"))
|
if (path == _localizationService.GetLocalizedString("MainPageNoLocalFilesMatched"))
|
||||||
{
|
{
|
||||||
return Visibility.Collapsed;
|
return Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,37 @@ using System;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Converter
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
{
|
{
|
||||||
public class MillisecondsToFormattedTimeConverter : IValueConverter
|
public partial class MillisecondsToFormattedTimeConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
if (value is int milliseconds)
|
double? milliseconds = null;
|
||||||
|
|
||||||
|
if (value is int iVal) milliseconds = iVal;
|
||||||
|
else if (value is double dVal) milliseconds = dVal;
|
||||||
|
else if (value is long lVal) milliseconds = lVal;
|
||||||
|
|
||||||
|
if (milliseconds.HasValue)
|
||||||
{
|
{
|
||||||
return TimeSpan.FromMilliseconds(milliseconds).ToString(@"mm\:ss\.fff");
|
var ts = TimeSpan.FromMilliseconds(milliseconds.Value);
|
||||||
}
|
|
||||||
else if (value is double doubleMilliseconds)
|
string? format = parameter?.ToString();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(format))
|
||||||
{
|
{
|
||||||
return TimeSpan.FromMilliseconds(doubleMilliseconds).ToString(@"mm\:ss\.fff");
|
format = @"mm\:ss\.fff";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ts.ToString(format);
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
return ts.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return value?.ToString() ?? "";
|
return value?.ToString() ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Converter
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
{
|
{
|
||||||
public class MillisecondsToSecondsConverter : IValueConverter
|
public partial class MillisecondsToSecondsConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class PathToImageConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
string targetPath = PathHelper.AlbumArtPlaceholderPath;
|
||||||
|
if (value is string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
targetPath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new BitmapImage(new Uri(targetPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
using System;
|
using System;
|
||||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
public partial class TranslationSearchProviderToDisplayNameConverter : IValueConverter
|
public partial class TranslationSearchProviderToDisplayNameConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
@@ -24,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
TranslationSearchProvider.Kugou => "酷狗音乐",
|
TranslationSearchProvider.Kugou => "酷狗音乐",
|
||||||
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||||
TranslationSearchProvider.AppleMusic => "Apple Music",
|
TranslationSearchProvider.AppleMusic => "Apple Music",
|
||||||
TranslationSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
TranslationSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||||
TranslationSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
TranslationSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||||
TranslationSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
TranslationSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||||
TranslationSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
TranslationSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||||
TranslationSearchProvider.LibreTranslate => "LibreTranslate",
|
TranslationSearchProvider.LibreTranslate => "LibreTranslate",
|
||||||
_ => "N/A",
|
_ => "N/A",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
using System;
|
using System;
|
||||||
@@ -8,7 +8,7 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
{
|
{
|
||||||
public partial class TransliterationSearchProviderToDisplayNameConverter : IValueConverter
|
public partial class TransliterationSearchProviderToDisplayNameConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
@@ -22,11 +22,12 @@ namespace BetterLyrics.WinUI3.Converter
|
|||||||
TransliterationSearchProvider.Kugou => "酷狗音乐",
|
TransliterationSearchProvider.Kugou => "酷狗音乐",
|
||||||
TransliterationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
TransliterationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||||
TransliterationSearchProvider.AppleMusic => "Apple Music",
|
TransliterationSearchProvider.AppleMusic => "Apple Music",
|
||||||
TransliterationSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
TransliterationSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||||
TransliterationSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
TransliterationSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||||
TransliterationSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
TransliterationSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||||
TransliterationSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
TransliterationSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||||
TransliterationSearchProvider.BetterLyrics => "BetterLyrics",
|
TransliterationSearchProvider.BetterLyrics => "BetterLyrics",
|
||||||
|
TransliterationSearchProvider.CutletDocker => "cutlet-docker",
|
||||||
_ => "N/A",
|
_ => "N/A",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
using ATL;
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
using BetterLyrics.WinUI3.Extensions;
|
|
||||||
using Microsoft.UI.Xaml.Data;
|
using Microsoft.UI.Xaml.Data;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Converter
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
{
|
{
|
||||||
public partial class TrackToLyricsConverter : IValueConverter
|
public partial class UriStringToDecodedAbsoluteUri : IValueConverter
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
{
|
{
|
||||||
if (value is Track track)
|
if (value is string uriString)
|
||||||
{
|
{
|
||||||
return track.GetRawLyrics();
|
return uriString.ToDecodedAbsoluteUri();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum AutoScanInterval
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Every15Minutes,
|
||||||
|
EveryHour,
|
||||||
|
Every6Hours,
|
||||||
|
Daily
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum FileSourceType
|
||||||
|
{
|
||||||
|
Local,
|
||||||
|
SMB,
|
||||||
|
FTP,
|
||||||
|
WebDAV
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Enums
|
|
||||||
{
|
|
||||||
public enum Language
|
|
||||||
{
|
|
||||||
FollowSystem,
|
|
||||||
English,
|
|
||||||
SimplifiedChinese,
|
|
||||||
TraditionalChinese,
|
|
||||||
Japanese,
|
|
||||||
Korean,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Enums
|
|
||||||
{
|
|
||||||
public enum LocalSearchTargetProps
|
|
||||||
{
|
|
||||||
LyricsOnly,
|
|
||||||
LyricsAndAlbumArt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,5 +14,6 @@ namespace BetterLyrics.WinUI3.Enums
|
|||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
AppleMusic,
|
AppleMusic,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace BetterLyrics.WinUI3.Enums
|
|
||||||
{
|
|
||||||
public enum SettingsStoreType
|
|
||||||
{
|
|
||||||
Container,
|
|
||||||
JSON
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace BetterLyrics.WinUI3.Enums
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
{
|
{
|
||||||
public enum ShortcutID
|
public enum ShortcutId
|
||||||
{
|
{
|
||||||
LyricsWindowShowOrHide,
|
LyricsWindowShowOrHide,
|
||||||
LyricsWindowSwitch,
|
LyricsWindowSwitch,
|
||||||
|
|||||||
13
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/StatsRange.cs
Normal file
13
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Enums/StatsRange.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum StatsRange
|
||||||
|
{
|
||||||
|
Today,
|
||||||
|
ThisWeek,
|
||||||
|
ThisMonth,
|
||||||
|
ThisQuarter,
|
||||||
|
ThisYear,
|
||||||
|
AllTime,
|
||||||
|
Custom
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,5 +13,6 @@
|
|||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
LibreTranslate,
|
LibreTranslate,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
LocalLrcFile,
|
LocalLrcFile,
|
||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
BetterLyrics
|
BetterLyrics,
|
||||||
|
CutletDocker,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum WordByWordEffectMode
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
Never,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Windows.Graphics.Imaging;
|
|
||||||
using Windows.UI;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Events
|
|
||||||
{
|
|
||||||
public class AlbumArtChangedEventArgs(SoftwareBitmap? albumArtSwBitmap, List<Color> albumArtLightAccentColors, List<Color> albumArtDarkAccentColors) : EventArgs
|
|
||||||
{
|
|
||||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = albumArtSwBitmap;
|
|
||||||
public List<Color> AlbumArtLightAccentColors { get; set; } = albumArtLightAccentColors;
|
|
||||||
public List<Color> AlbumArtDarkAccentColors { get; set; } = albumArtDarkAccentColors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Events
|
|
||||||
{
|
|
||||||
public class LibChangedEventArgs(string folder, string filePath, WatcherChangeTypes changeType) : EventArgs
|
|
||||||
{
|
|
||||||
public WatcherChangeTypes ChangeType { get; } = changeType;
|
|
||||||
public string FilePath { get; } = filePath;
|
|
||||||
public string Folder { get; } = folder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Events
|
|
||||||
{
|
|
||||||
public class LyricsChangedEventArgs(LyricsData? lyricsData) : EventArgs
|
|
||||||
{
|
|
||||||
public LyricsData? LyricsData { get; } = lyricsData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Events
|
|
||||||
{
|
|
||||||
public class MediaSourceProvidersInfoEventArgs(List<MediaSourceProviderInfo> sessionIds) : EventArgs
|
|
||||||
{
|
|
||||||
public List<MediaSourceProviderInfo> MediaSourceProviersInfo { get; set; } = sessionIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models.Lyrics;
|
||||||
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
|
{
|
||||||
|
public static class LyricsDataExtensions
|
||||||
|
{
|
||||||
|
extension(LyricsData lyricsData)
|
||||||
|
{
|
||||||
|
public static LyricsData GetLoadingPlaceholder()
|
||||||
|
{
|
||||||
|
return new LyricsData()
|
||||||
|
{
|
||||||
|
LyricsLines = [
|
||||||
|
new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = 0,
|
||||||
|
EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds,
|
||||||
|
PrimaryText = "● ● ●",
|
||||||
|
PrimarySyllables = [new BaseLyrics { Text = "● ● ●", StartMs = 0, EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds }],
|
||||||
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
LanguageCode = "N/A",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LyricsData GetNotfoundPlaceholder()
|
||||||
|
{
|
||||||
|
return new LyricsData([new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = 0,
|
||||||
|
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||||
|
PrimaryText = "N/A",
|
||||||
|
PrimarySyllables = [new BaseLyrics { Text = "N/A", StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds }],
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTranslatedText(LyricsData translationData, int toleranceMs = 50)
|
||||||
|
{
|
||||||
|
foreach (var line in lyricsData.LyricsLines)
|
||||||
|
{
|
||||||
|
// 在翻译歌词中查找与当前行开始时间最接近且在容忍范围内的行
|
||||||
|
var transLine = translationData.LyricsLines
|
||||||
|
.FirstOrDefault(t => Math.Abs(t.StartMs - line.StartMs) <= toleranceMs);
|
||||||
|
|
||||||
|
if (transLine != null)
|
||||||
|
{
|
||||||
|
// 此处 transLine.OriginalText 指翻译中的“原文”属性
|
||||||
|
line.SecondaryText = transLine.PrimaryText;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 没有匹配的翻译
|
||||||
|
line.SecondaryText = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPhoneticText(LyricsData phoneticData, int toleranceMs = 50)
|
||||||
|
{
|
||||||
|
foreach (var line in lyricsData.LyricsLines)
|
||||||
|
{
|
||||||
|
// 在音译歌词中查找与当前行开始时间最接近且在容忍范围内的行
|
||||||
|
var transLine = phoneticData.LyricsLines
|
||||||
|
.FirstOrDefault(t => Math.Abs(t.StartMs - line.StartMs) <= toleranceMs);
|
||||||
|
|
||||||
|
if (transLine != null)
|
||||||
|
{
|
||||||
|
// 此处 transLine.OriginalText 指音译中的“原文”属性
|
||||||
|
line.TertiaryText = transLine.PrimaryText;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 没有匹配的音译
|
||||||
|
line.TertiaryText = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTranslation(string translation)
|
||||||
|
{
|
||||||
|
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
||||||
|
int i = 0;
|
||||||
|
foreach (var line in lyricsData.LyricsLines)
|
||||||
|
{
|
||||||
|
if (i >= translationArr.Count)
|
||||||
|
{
|
||||||
|
line.SecondaryText = ""; // No translation available, keep empty
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line.SecondaryText = translationArr[i];
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTransliteration(string transliteration)
|
||||||
|
{
|
||||||
|
List<string> transliterationArr = transliteration.Split(StringHelper.NewLine).ToList();
|
||||||
|
int i = 0;
|
||||||
|
foreach (var line in lyricsData.LyricsLines)
|
||||||
|
{
|
||||||
|
if (i >= transliterationArr.Count)
|
||||||
|
{
|
||||||
|
line.TertiaryText = ""; // No transliteration available, keep empty
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line.TertiaryText = transliterationArr[i];
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LyricsLine? GetLyricsLine(double sec)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < lyricsData.LyricsLines.Count; i++)
|
||||||
|
{
|
||||||
|
var line = lyricsData.LyricsLines[i];
|
||||||
|
if (line.StartMs > sec * 1000)
|
||||||
|
{
|
||||||
|
return lyricsData.LyricsLines.ElementAtOrDefault(i - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Helper;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
{
|
{
|
||||||
@@ -8,21 +6,6 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
{
|
{
|
||||||
extension(LyricsSearchProvider provider)
|
extension(LyricsSearchProvider provider)
|
||||||
{
|
{
|
||||||
public string GetCacheDirectory() => provider switch
|
|
||||||
{
|
|
||||||
LyricsSearchProvider.LrcLib => PathHelper.LrcLibLyricsCacheDirectory,
|
|
||||||
LyricsSearchProvider.QQ => PathHelper.QQLyricsCacheDirectory,
|
|
||||||
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
|
|
||||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
|
||||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
|
||||||
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
|
||||||
LyricsSearchProvider.LocalMusicFile => PathHelper.LocalMusicCacheDirectory,
|
|
||||||
LyricsSearchProvider.LocalLrcFile => PathHelper.LocalLrcCacheDirectory,
|
|
||||||
LyricsSearchProvider.LocalEslrcFile => PathHelper.LocalEslrcCacheDirectory,
|
|
||||||
LyricsSearchProvider.LocalTtmlFile => PathHelper.LocalTtmlCacheDirectory,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(provider)),
|
|
||||||
};
|
|
||||||
|
|
||||||
public LyricsFormat GetLyricsFormat() => provider switch
|
public LyricsFormat GetLyricsFormat() => provider switch
|
||||||
{
|
{
|
||||||
LyricsSearchProvider.LrcLib => LyricsFormat.Lrc,
|
LyricsSearchProvider.LrcLib => LyricsFormat.Lrc,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using BetterLyrics.WinUI3.Models.Settings;
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@@ -12,14 +11,14 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
{
|
{
|
||||||
public static class LyricsWindowStatusExtensions
|
public static class LyricsWindowStatusExtensions
|
||||||
{
|
{
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public static LyricsWindowStatus DesktopMode(Window? window = null)
|
public static LyricsWindowStatus DesktopMode(Window? window = null)
|
||||||
{
|
{
|
||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
return new LyricsWindowStatus(window)
|
return new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("DesktopMode"),
|
Name = _localizationService.GetLocalizedString("DesktopMode"),
|
||||||
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
||||||
WindowBounds = new Rect(100, 100, 600, 250),
|
WindowBounds = new Rect(100, 100, 600, 250),
|
||||||
IsLocked = true,
|
IsLocked = true,
|
||||||
@@ -44,7 +43,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
var status = new LyricsWindowStatus(window)
|
var status = new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("DockedMode"),
|
Name = _localizationService.GetLocalizedString("DockedMode"),
|
||||||
IsWorkArea = true,
|
IsWorkArea = true,
|
||||||
IsAlwaysOnTop = true,
|
IsAlwaysOnTop = true,
|
||||||
IsAlwaysOnTopPolling = true,
|
IsAlwaysOnTopPolling = true,
|
||||||
@@ -71,7 +70,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
var status = new LyricsWindowStatus(window)
|
var status = new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("FullscreenMode"),
|
Name = _localizationService.GetLocalizedString("FullscreenMode"),
|
||||||
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
||||||
LyricsStyleSettings = new LyricsStyleSettings
|
LyricsStyleSettings = new LyricsStyleSettings
|
||||||
{
|
{
|
||||||
@@ -93,7 +92,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
return new LyricsWindowStatus(window)
|
return new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("StandardMode"),
|
Name = _localizationService.GetLocalizedString("StandardMode"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
return new LyricsWindowStatus(window)
|
return new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("NarrowMode"),
|
Name = _localizationService.GetLocalizedString("NarrowMode"),
|
||||||
WindowBounds = new Rect(100, 100, 400, 800),
|
WindowBounds = new Rect(100, 100, 400, 800),
|
||||||
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
||||||
};
|
};
|
||||||
@@ -113,7 +112,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||||
return new LyricsWindowStatus(window)
|
return new LyricsWindowStatus(window)
|
||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("TaskbarMode"),
|
Name = _localizationService.GetLocalizedString("TaskbarMode"),
|
||||||
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
||||||
IsPinToTaskbar = true,
|
IsPinToTaskbar = true,
|
||||||
IsLocked = true,
|
IsLocked = true,
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
{
|
{
|
||||||
public static class SongInfoExtensions
|
public static class SongInfoExtensions
|
||||||
{
|
{
|
||||||
public static SongInfo Placeholder => new SongInfo
|
public static SongInfo Placeholder => new()
|
||||||
{
|
{
|
||||||
Title = "N/A",
|
Title = "N/A",
|
||||||
Album = "N/A",
|
Album = "N/A",
|
||||||
Artists = ["N/A"],
|
Artist = "N/A",
|
||||||
};
|
};
|
||||||
|
|
||||||
extension(SongInfo songInfo)
|
extension(SongInfo songInfo)
|
||||||
@@ -19,9 +24,9 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
return songInfo;
|
return songInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SongInfo WithArtist(string[] value)
|
public SongInfo WithArtist(string value)
|
||||||
{
|
{
|
||||||
songInfo.Artists = value;
|
songInfo.Artist = value;
|
||||||
return songInfo;
|
return songInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +35,45 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
songInfo.Album = value;
|
songInfo.Album = value;
|
||||||
return songInfo;
|
return songInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SongInfo WithSongId(string value)
|
||||||
|
{
|
||||||
|
songInfo.SongId = value;
|
||||||
|
return songInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayHistoryItem? ToPlayHistoryItem(double actualPlayedMs)
|
||||||
|
{
|
||||||
|
if (songInfo == null) return null;
|
||||||
|
|
||||||
|
return new PlayHistoryItem
|
||||||
|
{
|
||||||
|
Title = songInfo.Title,
|
||||||
|
Artist = songInfo.Artist,
|
||||||
|
Album = songInfo.Album,
|
||||||
|
PlayerId = songInfo.PlayerId ?? "N/A",
|
||||||
|
TotalDurationMs = songInfo.DurationMs,
|
||||||
|
DurationPlayedMs = actualPlayedMs,
|
||||||
|
StartedAt = DateTime.FromBinary(songInfo.StartedAt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCacheKey()
|
||||||
|
{
|
||||||
|
string title = songInfo.Title?.Trim() ?? "";
|
||||||
|
string album = songInfo.Album?.Trim() ?? "";
|
||||||
|
|
||||||
|
string artists = songInfo.Artist?.Trim() ?? "";
|
||||||
|
|
||||||
|
long seconds = (long)Math.Round(songInfo.Duration);
|
||||||
|
string durationPart = seconds.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
string rawKey = $"{title}|{artists}|{album}|{durationPart}";
|
||||||
|
|
||||||
|
using var sha256 = SHA256.Create();
|
||||||
|
byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(rawKey));
|
||||||
|
return Convert.ToHexString(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
@@ -75,6 +76,18 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ToDecodedAbsoluteUri()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str)) return "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var u = new Uri(str);
|
||||||
|
return u.IsFile ? u.LocalPath : System.Net.WebUtility.UrlDecode(u.AbsoluteUri);
|
||||||
|
}
|
||||||
|
catch { return str; }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
using ATL;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
|
||||||
{
|
|
||||||
public static class TrackExtensions
|
|
||||||
{
|
|
||||||
extension(Track track)
|
|
||||||
{
|
|
||||||
public string GetParentFolderName() => Directory.GetParent(track.Path)?.Name ?? "";
|
|
||||||
|
|
||||||
public string GetParentFolderPath() => Directory.GetParent(track.Path)?.FullName ?? "";
|
|
||||||
|
|
||||||
public string GetRawLyrics()
|
|
||||||
{
|
|
||||||
if (track.Path is string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return TagLib.File.Create(path).Tag.Lyrics;
|
|
||||||
}
|
|
||||||
catch (System.Exception)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFileName() => Path.GetFileName(track.Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@@ -9,16 +9,24 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
{
|
{
|
||||||
public static class WindowExtensions
|
public static class WindowExtensions
|
||||||
{
|
{
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
extension(Window window)
|
extension(Window window)
|
||||||
{
|
{
|
||||||
public void Init(
|
public void Init(
|
||||||
string titleKey,
|
string titleKey = "",
|
||||||
|
string title = "",
|
||||||
TitleBarHeightOption titleBarHeightOption = TitleBarHeightOption.Standard,
|
TitleBarHeightOption titleBarHeightOption = TitleBarHeightOption.Standard,
|
||||||
BackdropType backdropType = BackdropType.DesktopAcrylic)
|
BackdropType backdropType = BackdropType.DesktopAcrylic)
|
||||||
{
|
{
|
||||||
window.Title = _resourceService.GetLocalizedString(titleKey);
|
if (titleKey != "")
|
||||||
|
{
|
||||||
|
window.Title = _localizationService.GetLocalizedString(titleKey);
|
||||||
|
}
|
||||||
|
if (title != "")
|
||||||
|
{
|
||||||
|
window.Title = title;
|
||||||
|
}
|
||||||
window.AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
|
window.AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
|
||||||
window.AppWindow.SetIcons();
|
window.AppWindow.SetIcons();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public static class ColorHelper
|
public static class ColorHelper
|
||||||
{
|
{
|
||||||
|
public static Color GetSystemAccentColor()
|
||||||
|
{
|
||||||
|
if (Application.Current.Resources.TryGetValue("SystemAccentColor", out var resource) &&
|
||||||
|
resource is Color uiColor)
|
||||||
|
{
|
||||||
|
return uiColor;
|
||||||
|
}
|
||||||
|
return Color.FromArgb(255, 0, 120, 215);
|
||||||
|
}
|
||||||
|
|
||||||
public static ElementTheme GetElementThemeFromBackgroundColor(Color backgroundColor)
|
public static ElementTheme GetElementThemeFromBackgroundColor(Color backgroundColor)
|
||||||
{
|
{
|
||||||
// 计算亮度(YIQ公式)
|
// 计算亮度(YIQ公式)
|
||||||
|
|||||||
@@ -56,6 +56,57 @@
|
|||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://learn.microsoft.com/zh-cn/dotnet/standard/io/how-to-copy-directories
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceDir"></param>
|
||||||
|
/// <param name="destinationDir"></param>
|
||||||
|
/// <param name="recursive"></param>
|
||||||
|
/// <exception cref="DirectoryNotFoundException"></exception>
|
||||||
|
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
|
||||||
|
{
|
||||||
|
// Get information about the source directory
|
||||||
|
var dir = new DirectoryInfo(sourceDir);
|
||||||
|
|
||||||
|
// Check if the source directory exists
|
||||||
|
if (!dir.Exists)
|
||||||
|
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
||||||
|
|
||||||
|
// Cache directories before we start copying
|
||||||
|
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||||
|
|
||||||
|
// Create the destination directory
|
||||||
|
Directory.CreateDirectory(destinationDir);
|
||||||
|
|
||||||
|
// Get the files in the source directory and copy to the destination directory
|
||||||
|
foreach (FileInfo file in dir.GetFiles())
|
||||||
|
{
|
||||||
|
string targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||||
|
|
||||||
|
CopyLockedFile(file.FullName, targetFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If recursive and copying subdirectories, recursively call this method
|
||||||
|
if (recursive)
|
||||||
|
{
|
||||||
|
foreach (DirectoryInfo subDir in dirs)
|
||||||
|
{
|
||||||
|
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
|
||||||
|
CopyDirectory(subDir.FullName, newDestinationDir, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CopyLockedFile(string sourcePath, string targetPath)
|
||||||
|
{
|
||||||
|
using (var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
using (var destStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
sourceStream.CopyTo(destStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Extensions;
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Serialization;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Ude;
|
using Ude;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
@@ -27,6 +29,18 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return Encoding.GetEncoding(encoding);
|
return Encoding.GetEncoding(encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task CopyFileAsync(string sourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
var dir = Path.GetDirectoryName(destinationPath);
|
||||||
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
|
using (var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
|
using (var destinationStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
await sourceStream.CopyToAsync(destinationStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string SanitizeFileName(string fileName, char replacement = '_')
|
public static string SanitizeFileName(string fileName, char replacement = '_')
|
||||||
{
|
{
|
||||||
var invalidChars = Path.GetInvalidFileNameChars();
|
var invalidChars = Path.GetInvalidFileNameChars();
|
||||||
@@ -38,22 +52,6 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LyricsSearchResult? ReadLyricsCache(SongInfo songInfo, LyricsSearchProvider lyricsSearchProvider)
|
|
||||||
{
|
|
||||||
var cacheFilePath = Path.Combine(
|
|
||||||
lyricsSearchProvider.GetCacheDirectory(),
|
|
||||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
|
||||||
|
|
||||||
if (File.Exists(cacheFilePath))
|
|
||||||
{
|
|
||||||
var json = File.ReadAllText(cacheFilePath);
|
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LyricsSearchResult);
|
|
||||||
data?.SelfPath = cacheFilePath;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[]? ReadAlbumArtCache(string album, string artist, string format, string cacheFolderPath)
|
public static byte[]? ReadAlbumArtCache(string album, string artist, string format, string cacheFolderPath)
|
||||||
{
|
{
|
||||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {album}{format}"));
|
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{artist} - {album}{format}"));
|
||||||
@@ -64,19 +62,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteLyricsCache(SongInfo songInfo, LyricsSearchResult lyricsSearchResult)
|
|
||||||
{
|
|
||||||
var cacheFilePath = Path.Combine(
|
|
||||||
lyricsSearchResult.Provider.GetCacheDirectory(),
|
|
||||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
|
||||||
lyricsSearchResult.SelfPath = cacheFilePath;
|
|
||||||
var json = System.Text.Json.JsonSerializer.Serialize(lyricsSearchResult, SourceGenerationContext.Default.LyricsSearchResult);
|
|
||||||
File.WriteAllText(cacheFilePath, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteAlbumArtCache(SongInfo songInfo, byte[] img, string format, string cacheFolderPath)
|
public static void WriteAlbumArtCache(SongInfo songInfo, byte[] img, string format, string cacheFolderPath)
|
||||||
{
|
{
|
||||||
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{songInfo.DisplayArtists} - {songInfo.Album}{format}"));
|
var cacheFilePath = Path.Combine(cacheFolderPath, SanitizeFileName($"{songInfo.Artist} - {songInfo.Album}{format}"));
|
||||||
File.WriteAllBytes(cacheFilePath, img);
|
File.WriteAllBytes(cacheFilePath, img);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,5 +74,15 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
".wav", ".aiff", ".aif", ".pcm", ".cda", ".dsf", ".dff", ".au", ".snd",
|
".wav", ".aiff", ".aif", ".pcm", ".cda", ".dsf", ".dff", ".au", ".snd",
|
||||||
".mid", ".midi", ".mod", ".xm", ".it", ".s3m"
|
".mid", ".midi", ".mod", ".xm", ".it", ".s3m"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly string[] LyricExtensions =
|
||||||
|
Enum.GetValues(typeof(LyricsSearchProvider)).Cast<LyricsSearchProvider>()
|
||||||
|
.Where(x => x.IsLocal())
|
||||||
|
.Select(x => x.GetLyricsFormat())
|
||||||
|
.Where(x => x != LyricsFormat.NotSpecified)
|
||||||
|
.Select(x => x.ToFileExtension())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
public static readonly HashSet<string> AllSupportedExtensions = new(MusicExtensions.Union(LyricExtensions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
public static class FolderTreeBuilder
|
||||||
|
{
|
||||||
|
public static ObservableCollection<FolderNode> Build(List<ExtendedTrack> tracks, List<MediaFolder> folderConfigs)
|
||||||
|
{
|
||||||
|
var rootNodes = new ObservableCollection<FolderNode>();
|
||||||
|
|
||||||
|
// 按 MediaFolderId 分组
|
||||||
|
var folderGroups = tracks.GroupBy(t => t.MediaFolderId);
|
||||||
|
|
||||||
|
foreach (var group in folderGroups)
|
||||||
|
{
|
||||||
|
var config = folderConfigs.FirstOrDefault(f => f.Id == group.Key);
|
||||||
|
if (config == null) continue;
|
||||||
|
|
||||||
|
string baseUri = config.GetStandardUri().AbsoluteUri.TrimEnd('/');
|
||||||
|
|
||||||
|
var rootNode = new FolderNode
|
||||||
|
{
|
||||||
|
SourceType = config.SourceType,
|
||||||
|
FolderName = config.Name ?? config.ConnectionSummary, // 显示用户自定义的名字
|
||||||
|
MediaFolderId = group.Key,
|
||||||
|
FolderPath = baseUri,
|
||||||
|
IsExpanded = true
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var track in group)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!track.Uri.StartsWith(baseUri)) continue; // 防御性编程
|
||||||
|
|
||||||
|
string relativePart = track.Uri.Substring(baseUri.Length);
|
||||||
|
|
||||||
|
var segments = relativePart
|
||||||
|
.Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(s => System.Net.WebUtility.UrlDecode(s))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (segments.Length > 1) // 长度大于1说明在子文件夹里
|
||||||
|
{
|
||||||
|
var folderSegments = segments.Take(segments.Length - 1).ToArray();
|
||||||
|
CreateFolderStructure(rootNode, folderSegments, baseUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
rootNodes.Add(rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateFolderStructure(FolderNode parent, string[] segments, string rootBaseUri)
|
||||||
|
{
|
||||||
|
var current = parent;
|
||||||
|
string currentFullPath = parent.FolderPath;
|
||||||
|
|
||||||
|
foreach (var segmentName in segments)
|
||||||
|
{
|
||||||
|
var existingChild = current.SubFolders.FirstOrDefault(f => f.FolderName == segmentName);
|
||||||
|
|
||||||
|
currentFullPath += "/" + System.Net.WebUtility.UrlEncode(segmentName);
|
||||||
|
|
||||||
|
if (existingChild == null)
|
||||||
|
{
|
||||||
|
var newFolder = new FolderNode
|
||||||
|
{
|
||||||
|
FolderName = segmentName,
|
||||||
|
FolderPath = currentFullPath, // 存完整的 URI
|
||||||
|
MediaFolderId = parent.MediaFolderId
|
||||||
|
};
|
||||||
|
current.SubFolders.Add(newFolder);
|
||||||
|
current = newFolder;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = existingChild;
|
||||||
|
currentFullPath = existingChild.FolderPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using Microsoft.Graphics.Canvas.Text;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using NTextCat;
|
using NTextCat;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Windows.Globalization;
|
using Windows.Globalization;
|
||||||
|
|
||||||
@@ -10,9 +11,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public class LanguageHelper
|
public class LanguageHelper
|
||||||
{
|
{
|
||||||
|
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
private static readonly RankedLanguageIdentifierFactory _factory = new();
|
private static readonly RankedLanguageIdentifierFactory _factory = new();
|
||||||
private static readonly RankedLanguageIdentifier _identifier;
|
private static readonly RankedLanguageIdentifier _identifier;
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
|
||||||
|
|
||||||
public const string ChineseCode = "zh";
|
public const string ChineseCode = "zh";
|
||||||
public const string JapaneseCode = "ja";
|
public const string JapaneseCode = "ja";
|
||||||
@@ -92,12 +93,23 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
public static List<ExtendedLanguage> SupportedDisplayLanguages { get; set; } =
|
public static List<ExtendedLanguage> SupportedDisplayLanguages { get; set; } =
|
||||||
[
|
[
|
||||||
new ExtendedLanguage("", _resourceService.GetLocalizedString("SettingsPageSystemLanguage")),
|
new ExtendedLanguage(CultureInfo.CurrentUICulture.Name, _localizationService.GetLocalizedString("SettingsPageSystemLanguage")),
|
||||||
new ExtendedLanguage("en-US", "English"),
|
new ExtendedLanguage("ar"),
|
||||||
new ExtendedLanguage("ja-JP"),
|
new ExtendedLanguage("de"),
|
||||||
new ExtendedLanguage("ko-KR"),
|
new ExtendedLanguage("en"),
|
||||||
new ExtendedLanguage("zh-CN", "简体中文"),
|
new ExtendedLanguage("es"),
|
||||||
new ExtendedLanguage("zh-TW", "繁體中文"),
|
new ExtendedLanguage("fr"),
|
||||||
|
new ExtendedLanguage("hi"),
|
||||||
|
new ExtendedLanguage("id"),
|
||||||
|
new ExtendedLanguage("ja"),
|
||||||
|
new ExtendedLanguage("ko"),
|
||||||
|
new ExtendedLanguage("ms"),
|
||||||
|
new ExtendedLanguage("pt"),
|
||||||
|
new ExtendedLanguage("ru"),
|
||||||
|
new ExtendedLanguage("th"),
|
||||||
|
new ExtendedLanguage("vi"),
|
||||||
|
new ExtendedLanguage("zh-Hans"),
|
||||||
|
new ExtendedLanguage("zh-Hant"),
|
||||||
];
|
];
|
||||||
|
|
||||||
static LanguageHelper()
|
static LanguageHelper()
|
||||||
@@ -107,7 +119,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
public static string? DetectLanguageCode(string? text)
|
public static string? DetectLanguageCode(string? text)
|
||||||
{
|
{
|
||||||
if (text == null) return null;
|
if (string.IsNullOrWhiteSpace(text)) return null;
|
||||||
var guessList = _identifier.Identify(text);
|
var guessList = _identifier.Identify(text);
|
||||||
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
||||||
code = code switch
|
code = code switch
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Entities;
|
||||||
using F23.StringSimilarity;
|
using F23.StringSimilarity;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -9,29 +10,39 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public static partial class MetadataComparer
|
public static partial class MetadataComparer
|
||||||
{
|
{
|
||||||
private const double WeightTitle = 0.40;
|
private const double WeightTitle = 0.30;
|
||||||
private const double WeightArtist = 0.40;
|
private const double WeightArtist = 0.30;
|
||||||
private const double WeightAlbum = 0.10;
|
private const double WeightAlbum = 0.10;
|
||||||
private const double WeightDuration = 0.10;
|
private const double WeightDuration = 0.30;
|
||||||
|
|
||||||
// JaroWinkler 适合短字符串匹配
|
// JaroWinkler 适合短字符串匹配
|
||||||
private static readonly JaroWinkler _algo = new();
|
private static readonly JaroWinkler _algo = new();
|
||||||
|
|
||||||
public static int CalculateScore(SongInfo local, LyricsSearchResult remote)
|
public static int CalculateScore(SongInfo songInfo, LyricsCacheItem remote)
|
||||||
{
|
{
|
||||||
if (local == null || remote == null) return 0;
|
return CalculateScore(songInfo, remote.Title, remote.Artist, remote.Album, remote.Duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CalculateScore(SongInfo songInfo, FilesIndexItem local)
|
||||||
|
{
|
||||||
|
return CalculateScore(songInfo, local.Title, local.Artist, local.Album, local.Duration, local.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CalculateScore(
|
||||||
|
SongInfo songInfo,
|
||||||
|
string? compareTitle, string? compareArtist, string? compareAlbum, double? compareDuration, string? compareFileName = null)
|
||||||
|
{
|
||||||
double totalScore = 0;
|
double totalScore = 0;
|
||||||
|
|
||||||
bool localHasMetadata = !string.IsNullOrWhiteSpace(local.Title);
|
bool localHasMetadata = !string.IsNullOrWhiteSpace(songInfo.Title);
|
||||||
bool remoteHasMetadata = !string.IsNullOrWhiteSpace(remote.Title);
|
bool remoteHasMetadata = !string.IsNullOrWhiteSpace(compareTitle);
|
||||||
|
|
||||||
if (localHasMetadata && remoteHasMetadata)
|
if (localHasMetadata && remoteHasMetadata)
|
||||||
{
|
{
|
||||||
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
double titleScore = GetStringSimilarity(songInfo.Title, compareTitle);
|
||||||
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
double artistScore = GetStringSimilarity(songInfo.Artist, compareArtist);
|
||||||
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
double albumScore = GetStringSimilarity(songInfo.Album, compareAlbum);
|
||||||
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
double durationScore = GetDurationSimilarity(songInfo.Duration, compareDuration);
|
||||||
|
|
||||||
totalScore = (titleScore * WeightTitle) +
|
totalScore = (titleScore * WeightTitle) +
|
||||||
(artistScore * WeightArtist) +
|
(artistScore * WeightArtist) +
|
||||||
@@ -41,12 +52,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
string? localQuery = localHasMetadata
|
string? localQuery = localHasMetadata
|
||||||
? $"{local.Title} {string.Join(" ", local.Artists ?? [])}"
|
? $"{songInfo.Title} {songInfo.Artist}"
|
||||||
: Path.GetFileNameWithoutExtension(local.LinkedFileName);
|
: Path.GetFileNameWithoutExtension(songInfo.LinkedFileName);
|
||||||
|
|
||||||
string remoteQuery = remoteHasMetadata
|
string? remoteQuery = remoteHasMetadata
|
||||||
? $"{remote.Title} {string.Join(" ", remote.Artists ?? [])}"
|
? $"{compareTitle} {compareArtist}"
|
||||||
: Path.GetFileNameWithoutExtension(remote.Reference);
|
: Path.GetFileNameWithoutExtension(compareFileName);
|
||||||
|
|
||||||
string fp1 = CreateSortedFingerprint(localQuery);
|
string fp1 = CreateSortedFingerprint(localQuery);
|
||||||
string fp2 = CreateSortedFingerprint(remoteQuery);
|
string fp2 = CreateSortedFingerprint(remoteQuery);
|
||||||
@@ -83,19 +94,18 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return _algo.Similarity(s1, s2);
|
return _algo.Similarity(s1, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double GetDurationSimilarity(double localMs, double? remoteSeconds)
|
private static double GetDurationSimilarity(double localSeconds, double? remoteSeconds)
|
||||||
{
|
{
|
||||||
if (remoteSeconds == null || remoteSeconds == 0) return 0.0; // 远程没有时长数据,不匹配
|
if (remoteSeconds == null || remoteSeconds == 0) return 0.0; // 远程没有时长数据,不匹配
|
||||||
|
|
||||||
double localSeconds = localMs / 1000.0;
|
|
||||||
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
||||||
|
|
||||||
// 差距 <= 3秒:100% 相似
|
// 差距 <= 1 秒:100 % 相似
|
||||||
// 差距 >= 20秒:0% 相似
|
// 差距 >= 10 秒:0 % 相似
|
||||||
// 中间线性插值
|
// 中间线性插值
|
||||||
|
|
||||||
const double PerfectTolerance = 3.0;
|
const double PerfectTolerance = 1.0;
|
||||||
const double MaxTolerance = 20.0;
|
const double MaxTolerance = 10.0;
|
||||||
|
|
||||||
if (diff <= PerfectTolerance) return 1.0;
|
if (diff <= PerfectTolerance) return 1.0;
|
||||||
if (diff >= MaxTolerance) return 0.0;
|
if (diff >= MaxTolerance) return 0.0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ColorThiefDotNet;
|
using ColorThiefDotNet;
|
||||||
|
using CommunityToolkit.WinUI.Helpers;
|
||||||
using Impressionist.Abstractions;
|
using Impressionist.Abstractions;
|
||||||
using Impressionist.Implementations;
|
using Impressionist.Implementations;
|
||||||
using System;
|
using System;
|
||||||
@@ -50,7 +51,29 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return paletteResult;
|
return paletteResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Dictionary<Vector3, int>> GetPixelColor(BitmapDecoder bitmapDecoder)
|
public static List<Windows.UI.Color> GenerateChartColors(Windows.UI.Color baseColor, int count)
|
||||||
|
{
|
||||||
|
List<Windows.UI.Color> results = [];
|
||||||
|
|
||||||
|
var baseHsl = baseColor.ToHsl();
|
||||||
|
double baseHue = baseHsl.H;
|
||||||
|
double baseSaturation = baseHsl.S;
|
||||||
|
double baseBrightness = baseHsl.L;
|
||||||
|
|
||||||
|
double step = 360.0 / count;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
double newHue = (baseHue + (step * i)) % 360;
|
||||||
|
|
||||||
|
Windows.UI.Color newColor = CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(newHue, baseSaturation, baseBrightness);
|
||||||
|
results.Add(newColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Dictionary<Vector3, int>> GetPixelColor(BitmapDecoder bitmapDecoder)
|
||||||
{
|
{
|
||||||
var pixelDataProvider = await bitmapDecoder.GetPixelDataAsync();
|
var pixelDataProvider = await bitmapDecoder.GetPixelDataAsync();
|
||||||
var pixels = pixelDataProvider.DetachPixelData();
|
var pixels = pixelDataProvider.DetachPixelData();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Windows.Security.Credentials;
|
using System;
|
||||||
|
using Windows.Security.Credentials;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
@@ -12,23 +13,13 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <param name="value">要保存的值</param>
|
/// <param name="value">要保存的值</param>
|
||||||
public static void Save(string resource, string key, string value)
|
public static void Save(string resource, string key, string value)
|
||||||
{
|
{
|
||||||
// 删除旧值(避免重复存储)
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var vault = new PasswordVault();
|
var vault = new PasswordVault();
|
||||||
|
|
||||||
var oldCredential = vault.Retrieve(resource, key);
|
|
||||||
if (oldCredential != null)
|
|
||||||
{
|
|
||||||
vault.Remove(oldCredential);
|
|
||||||
}
|
|
||||||
|
|
||||||
vault.Add(new PasswordCredential(resource, key, value));
|
vault.Add(new PasswordCredential(resource, key, value));
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception) { }
|
||||||
{
|
|
||||||
// 没有旧值就忽略
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,7 +38,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
credential.RetrievePassword();
|
credential.RetrievePassword();
|
||||||
return credential.Password;
|
return credential.Password;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -65,10 +56,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
var credential = vault.Retrieve(resource, key);
|
var credential = vault.Retrieve(resource, key);
|
||||||
vault.Remove(credential);
|
vault.Remove(credential);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception) { }
|
||||||
{
|
|
||||||
// 不存在就忽略
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,48 +33,34 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
public static string SaltPlayerForWindowsLogoPath => Path.Combine(AssetsFolder, "SaltPlayerForWindows.png");
|
public static string SaltPlayerForWindowsLogoPath => Path.Combine(AssetsFolder, "SaltPlayerForWindows.png");
|
||||||
public static string MoeKoeMusicLogoPath => Path.Combine(AssetsFolder, "MoeKoeMusic.png");
|
public static string MoeKoeMusicLogoPath => Path.Combine(AssetsFolder, "MoeKoeMusic.png");
|
||||||
public static string Listen1LogoPath => Path.Combine(AssetsFolder, "Listen1.png");
|
public static string Listen1LogoPath => Path.Combine(AssetsFolder, "Listen1.png");
|
||||||
|
public static string OriginalSoundHQPlayerLogoPath => Path.Combine(AssetsFolder, "OriginalSoundHQPlayer.png");
|
||||||
public static string UnknownPlayerLogoPath => Path.Combine(AssetsFolder, "Question.png");
|
public static string UnknownPlayerLogoPath => Path.Combine(AssetsFolder, "Question.png");
|
||||||
|
|
||||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||||
|
|
||||||
public static string LyricsCacheDirectory => Path.Combine(CacheFolder, "lyrics");
|
public static string LyricsCacheDirectory => Path.Combine(CacheFolder, "lyrics");
|
||||||
public static string LrcLibLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "lrclib");
|
|
||||||
public static string NeteaseLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "netease");
|
|
||||||
public static string QQLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "qq");
|
|
||||||
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
|
|
||||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
|
|
||||||
public static string AppleMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "apple-music");
|
|
||||||
public static string LocalMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-music");
|
|
||||||
public static string LocalLrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-lrc");
|
|
||||||
public static string LocalEslrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-eslrc");
|
|
||||||
public static string LocalTtmlCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-ttml");
|
|
||||||
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
||||||
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
||||||
|
|
||||||
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
|
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
|
||||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
|
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
|
||||||
|
public static string LocalAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "local");
|
||||||
|
|
||||||
public static string PlayQueuePath => Path.Combine(CacheFolder, "play-queue.m3u");
|
public static string PlayQueuePath => Path.Combine(LocalFolder, "play-queue.m3u");
|
||||||
|
public static string PlayHistoryPath => Path.Combine(LocalFolder, "play-history.db");
|
||||||
|
public static string FilesIndexPath => Path.Combine(LocalFolder, "files-index.db");
|
||||||
|
public static string SongSearchMapPath => Path.Combine(LocalFolder, "song-search-map.db");
|
||||||
|
public static string LyricsCachePath => Path.Combine(LyricsCacheDirectory, "lyrics-cache.db");
|
||||||
|
|
||||||
public static void EnsureDirectories()
|
public static void EnsureDirectories()
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(SettingsDirectory);
|
Directory.CreateDirectory(SettingsDirectory);
|
||||||
|
|
||||||
Directory.CreateDirectory(LogDirectory);
|
Directory.CreateDirectory(LogDirectory);
|
||||||
|
Directory.CreateDirectory(LyricsCacheDirectory);
|
||||||
Directory.CreateDirectory(LrcLibLyricsCacheDirectory);
|
|
||||||
Directory.CreateDirectory(QQLyricsCacheDirectory);
|
|
||||||
Directory.CreateDirectory(KugouLyricsCacheDirectory);
|
|
||||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
|
||||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
|
||||||
Directory.CreateDirectory(AppleMusicCacheDirectory);
|
|
||||||
Directory.CreateDirectory(LocalMusicCacheDirectory);
|
|
||||||
Directory.CreateDirectory(LocalLrcCacheDirectory);
|
|
||||||
Directory.CreateDirectory(LocalEslrcCacheDirectory);
|
|
||||||
Directory.CreateDirectory(LocalTtmlCacheDirectory);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||||
}
|
Directory.CreateDirectory(LocalAlbumArtCacheDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public static class PhoneticHelper
|
public static class PhoneticHelper
|
||||||
{
|
{
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public const string PinyinCode = "zh-cmn-pinyin";
|
public const string PinyinCode = "zh-cmn-pinyin";
|
||||||
public const string JyutpingCode = "zh-yue-jyutping";
|
public const string JyutpingCode = "zh-yue-jyutping";
|
||||||
@@ -22,21 +22,16 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case PinyinCode:
|
case PinyinCode:
|
||||||
return _resourceService.GetLocalizedString("Pinyin");
|
return _localizationService.GetLocalizedString("Pinyin");
|
||||||
case JyutpingCode:
|
case JyutpingCode:
|
||||||
return _resourceService.GetLocalizedString("Jyutping");
|
return _localizationService.GetLocalizedString("Jyutping");
|
||||||
case RomanCode:
|
case RomanCode:
|
||||||
return _resourceService.GetLocalizedString("Romaji");
|
return _localizationService.GetLocalizedString("Romaji");
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(code));
|
throw new ArgumentOutOfRangeException(nameof(code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToRomaji(string text)
|
|
||||||
{
|
|
||||||
return Kana.Kana.KanaToRomaji(text, Kana.Error.Ignore).ToStr();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToPinyin(string text, Pinyin.ManTone.Style style = Pinyin.ManTone.Style.TONE)
|
public static string ToPinyin(string text, Pinyin.ManTone.Style style = Pinyin.ManTone.Style.TONE)
|
||||||
{
|
{
|
||||||
return Pinyin.Pinyin.Instance.HanziToPinyin(text, style).ToStr();
|
return Pinyin.Pinyin.Instance.HanziToPinyin(text, style).ToStr();
|
||||||
|
|||||||
@@ -42,13 +42,23 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<StorageFile?> PickSaveFileAsync<T>(IDictionary<string, IList<string>> fileTypeChoices)
|
public static async Task<StorageFile?> PickSaveFileAsync<T>(IDictionary<string, IList<string>> fileTypeChoices, string? suggestedFileName = null)
|
||||||
{
|
{
|
||||||
var window = WindowHook.GetWindow<T>();
|
var window = WindowHook.GetWindow<T>();
|
||||||
|
|
||||||
|
return await PickSaveFileAsync(window, fileTypeChoices, suggestedFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<StorageFile?> PickSaveFileAsync<T>(T? window, IDictionary<string, IList<string>> fileTypeChoices, string? suggestedFileName = null)
|
||||||
|
{
|
||||||
if (window == null) return null;
|
if (window == null) return null;
|
||||||
|
|
||||||
var picker = new Windows.Storage.Pickers.FileSavePicker();
|
var picker = new Windows.Storage.Pickers.FileSavePicker();
|
||||||
picker.FileTypeChoices.AddRange(fileTypeChoices);
|
picker.FileTypeChoices.AddRange(fileTypeChoices);
|
||||||
|
if (suggestedFileName != null)
|
||||||
|
{
|
||||||
|
picker.SuggestedFileName = suggestedFileName;
|
||||||
|
}
|
||||||
|
|
||||||
var hwnd = WindowNative.GetWindowHandle(window);
|
var hwnd = WindowNative.GetWindowHandle(window);
|
||||||
InitializeWithWindow.Initialize(picker, hwnd);
|
InitializeWithWindow.Initialize(picker, hwnd);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
public static class PlayerIDHelper
|
public static class PlayerIdHelper
|
||||||
{
|
{
|
||||||
private static readonly List<string> neteaseFamilyRegex =
|
private static readonly List<string> neteaseFamilyRegex =
|
||||||
[
|
[
|
||||||
@@ -25,64 +25,66 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsLXMusic(string? id) => id is PlayerID.LXMusic or PlayerID.LXMusicPortable;
|
public static bool IsLXMusic(string? id) => id is PlayerId.LXMusic or PlayerId.LXMusicPortable;
|
||||||
|
|
||||||
public static bool IsAppleMusic(string? id) => id is PlayerID.AppleMusic or PlayerID.AppleMusicAlternative;
|
public static bool IsAppleMusic(string? id) => id is PlayerId.AppleMusic or PlayerId.AppleMusicAlternative;
|
||||||
|
|
||||||
public static bool IsBetterLyrics(string? id) => id is PlayerID.BetterLyrics or PlayerID.BetterLyricsDebug;
|
public static bool IsBetterLyrics(string? id) => id is PlayerId.BetterLyrics or PlayerId.BetterLyricsDebug;
|
||||||
|
|
||||||
public static string? GetDisplayName(string? id) => id switch
|
public static string? GetDisplayName(string? id) => id switch
|
||||||
{
|
{
|
||||||
PlayerID.Spotify => PlayerName.Spotify,
|
PlayerId.Spotify => PlayerName.Spotify,
|
||||||
PlayerID.AppleMusic => PlayerName.AppleMusic,
|
PlayerId.AppleMusic => PlayerName.AppleMusic,
|
||||||
PlayerID.iTunes => PlayerName.iTunes,
|
PlayerId.iTunes => PlayerName.iTunes,
|
||||||
PlayerID.KugouMusic => PlayerName.KugouMusic,
|
PlayerId.KugouMusic => PlayerName.KugouMusic,
|
||||||
PlayerID.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic,
|
PlayerId.NetEaseCloudMusic => PlayerName.NetEaseCloudMusic,
|
||||||
PlayerID.QQMusic => PlayerName.QQMusic,
|
PlayerId.QQMusic => PlayerName.QQMusic,
|
||||||
PlayerID.LXMusic => PlayerName.LXMusic,
|
PlayerId.LXMusic => PlayerName.LXMusic,
|
||||||
PlayerID.LXMusicPortable => PlayerName.LXMusicPortable,
|
PlayerId.LXMusicPortable => PlayerName.LXMusicPortable,
|
||||||
PlayerID.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11,
|
PlayerId.MediaPlayerWindows11 => PlayerName.MediaPlayerWindows11,
|
||||||
PlayerID.AIMP => PlayerName.AIMP,
|
PlayerId.AIMP => PlayerName.AIMP,
|
||||||
PlayerID.Foobar2000 => PlayerName.Foobar2000,
|
PlayerId.Foobar2000 => PlayerName.Foobar2000,
|
||||||
PlayerID.MusicBee => PlayerName.MusicBee,
|
PlayerId.MusicBee => PlayerName.MusicBee,
|
||||||
PlayerID.PotPlayer => PlayerName.PotPlayer,
|
PlayerId.PotPlayer => PlayerName.PotPlayer,
|
||||||
PlayerID.Chrome => PlayerName.Chrome,
|
PlayerId.Chrome => PlayerName.Chrome,
|
||||||
PlayerID.Edge => PlayerName.Edge,
|
PlayerId.Edge => PlayerName.Edge,
|
||||||
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
|
PlayerId.BetterLyrics => PlayerName.BetterLyrics,
|
||||||
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
PlayerId.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
||||||
PlayerID.SaltPlayerForWindowsMS => PlayerName.SaltPlayerForWindowsMS,
|
PlayerId.SaltPlayerForWindowsMS => PlayerName.SaltPlayerForWindowsMS,
|
||||||
PlayerID.SaltPlayerForWindowsSteam => PlayerName.SaltPlayerForWindowsSteam,
|
PlayerId.SaltPlayerForWindowsSteam => PlayerName.SaltPlayerForWindowsSteam,
|
||||||
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
PlayerId.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
||||||
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
PlayerId.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
||||||
PlayerID.Listen1 => PlayerName.Listen1,
|
PlayerId.Listen1 => PlayerName.Listen1,
|
||||||
|
PlayerId.OriginalSoundHQPlayer => PlayerName.OriginalSoundHQPlayer,
|
||||||
_ => id,
|
_ => id,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string GetLogoPath(string? id) => id switch
|
public static string GetLogoPath(string? id) => id switch
|
||||||
{
|
{
|
||||||
PlayerID.Spotify => PathHelper.SpotifyLogoPath,
|
PlayerId.Spotify => PathHelper.SpotifyLogoPath,
|
||||||
PlayerID.AppleMusic => PathHelper.AppleMusicLogoPath,
|
PlayerId.AppleMusic => PathHelper.AppleMusicLogoPath,
|
||||||
PlayerID.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
|
PlayerId.AppleMusicAlternative => PathHelper.AppleMusicLogoPath,
|
||||||
PlayerID.iTunes => PathHelper.iTunesLogoPath,
|
PlayerId.iTunes => PathHelper.iTunesLogoPath,
|
||||||
PlayerID.KugouMusic => PathHelper.KugouMusicLogoPath,
|
PlayerId.KugouMusic => PathHelper.KugouMusicLogoPath,
|
||||||
PlayerID.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,
|
PlayerId.NetEaseCloudMusic => PathHelper.NetEaseCloudMusicLogoPath,
|
||||||
PlayerID.QQMusic => PathHelper.QQMusicLogoPath,
|
PlayerId.QQMusic => PathHelper.QQMusicLogoPath,
|
||||||
PlayerID.LXMusic => PathHelper.LXMusicLogoPath,
|
PlayerId.LXMusic => PathHelper.LXMusicLogoPath,
|
||||||
PlayerID.LXMusicPortable => PathHelper.LXMusicLogoPath,
|
PlayerId.LXMusicPortable => PathHelper.LXMusicLogoPath,
|
||||||
PlayerID.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath,
|
PlayerId.MediaPlayerWindows11 => PathHelper.MediaPlayerWindows11LogoPath,
|
||||||
PlayerID.AIMP => PathHelper.AIMPLogoPath,
|
PlayerId.AIMP => PathHelper.AIMPLogoPath,
|
||||||
PlayerID.Foobar2000 => PathHelper.Foobar2000LogoPath,
|
PlayerId.Foobar2000 => PathHelper.Foobar2000LogoPath,
|
||||||
PlayerID.MusicBee => PathHelper.MusicBeeLogoPath,
|
PlayerId.MusicBee => PathHelper.MusicBeeLogoPath,
|
||||||
PlayerID.PotPlayer => PathHelper.PotPlayerLogoPath,
|
PlayerId.PotPlayer => PathHelper.PotPlayerLogoPath,
|
||||||
PlayerID.Chrome => PathHelper.ChromeLogoPath,
|
PlayerId.Chrome => PathHelper.ChromeLogoPath,
|
||||||
PlayerID.Edge => PathHelper.EdgeLogoPath,
|
PlayerId.Edge => PathHelper.EdgeLogoPath,
|
||||||
PlayerID.BetterLyrics => PathHelper.LogoPath,
|
PlayerId.BetterLyrics => PathHelper.LogoPath,
|
||||||
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
|
PlayerId.BetterLyricsDebug => PathHelper.LogoPath,
|
||||||
PlayerID.SaltPlayerForWindowsMS => PathHelper.SaltPlayerForWindowsLogoPath,
|
PlayerId.SaltPlayerForWindowsMS => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||||
PlayerID.SaltPlayerForWindowsSteam => PathHelper.SaltPlayerForWindowsLogoPath,
|
PlayerId.SaltPlayerForWindowsSteam => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||||
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
PlayerId.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
||||||
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
PlayerId.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
||||||
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
|
PlayerId.Listen1 => PathHelper.Listen1LogoPath,
|
||||||
|
PlayerId.OriginalSoundHQPlayer => PathHelper.OriginalSoundHQPlayerLogoPath,
|
||||||
_ => PathHelper.UnknownPlayerLogoPath,
|
_ => PathHelper.UnknownPlayerLogoPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
|
{
|
||||||
|
public class StreamFileAbstraction : TagLib.File.IFileAbstraction
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly bool _closeStreamOnDispose;
|
||||||
|
|
||||||
|
public StreamFileAbstraction(string path, Stream? stream, bool closeStreamOnDispose = false)
|
||||||
|
{
|
||||||
|
_name = Path.GetFileName(path);
|
||||||
|
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||||
|
_closeStreamOnDispose = closeStreamOnDispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
public Stream ReadStream => _stream;
|
||||||
|
|
||||||
|
public Stream WriteStream
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_stream.CanWrite)
|
||||||
|
{
|
||||||
|
return _stream;
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException("The underlying stream is read-only. Tag saving is not supported for this source.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseStream(Stream stream)
|
||||||
|
{
|
||||||
|
if (_closeStreamOnDispose)
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.Windows.AppNotifications;
|
using Microsoft.Windows.AppNotifications;
|
||||||
@@ -8,12 +8,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public class ToastHelper
|
public class ToastHelper
|
||||||
{
|
{
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||||
|
|
||||||
public static void ShowToast(string localizedTitleKey, string? description, InfoBarSeverity severity)
|
public static void ShowToast(string localizedTitleKey, string? description, InfoBarSeverity severity)
|
||||||
{
|
{
|
||||||
AppNotification notification = new AppNotificationBuilder()
|
AppNotification notification = new AppNotificationBuilder()
|
||||||
.AddText(_resourceService.GetLocalizedString(localizedTitleKey))
|
.AddText(_localizationService.GetLocalizedString(localizedTitleKey))
|
||||||
.AddText(description)
|
.AddText(description)
|
||||||
.BuildNotification();
|
.BuildNotification();
|
||||||
|
|
||||||
|
|||||||
@@ -1,153 +1,241 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Enums;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
public class ValueTransition<T>
|
public class ValueTransition<T> where T : struct
|
||||||
where T : struct
|
|
||||||
{
|
{
|
||||||
|
// 状态变量
|
||||||
private T _currentValue;
|
private T _currentValue;
|
||||||
private double _durationSeconds;
|
|
||||||
private double _delaySeconds;
|
|
||||||
private double _delayRemaining;
|
|
||||||
private EasingType? _easingType;
|
|
||||||
private Func<T, T, double, T> _interpolator;
|
|
||||||
private bool _isTransitioning;
|
|
||||||
private double _progress;
|
|
||||||
private T _startValue;
|
private T _startValue;
|
||||||
private T _targetValue;
|
private T _targetValue;
|
||||||
|
|
||||||
public double DurationSeconds => _durationSeconds;
|
// 核心队列
|
||||||
public double DelaySeconds => _delaySeconds;
|
private readonly Queue<Keyframe<T>> _keyframeQueue = new Queue<Keyframe<T>>();
|
||||||
|
|
||||||
public bool IsTransitioning => _isTransitioning;
|
// 时间控制
|
||||||
|
private double _stepDuration; // 当前这一段的时长 (动态变化)
|
||||||
|
private double _totalDurationForAutoSplit; // 自动均分模式的总时长
|
||||||
|
private double _configuredDelaySeconds; // 配置的延迟时长
|
||||||
|
|
||||||
|
// 动画状态
|
||||||
|
private Enums.EasingType? _easingType;
|
||||||
|
private Func<T, T, double, T> _interpolator;
|
||||||
|
private bool _isTransitioning;
|
||||||
|
private double _progress; // 当前段的进度 (0.0 ~ 1.0)
|
||||||
|
|
||||||
|
// 公开属性
|
||||||
public T Value => _currentValue;
|
public T Value => _currentValue;
|
||||||
public T StartValue => _startValue;
|
public bool IsTransitioning => _isTransitioning;
|
||||||
public T TargetValue => _targetValue;
|
public T TargetValue => _targetValue; // 获取当前段的目标值
|
||||||
public EasingType? EasingType => _easingType;
|
public Enums.EasingType? EasingType => _easingType;
|
||||||
public double Progress => _progress;
|
public double DurationSeconds => _totalDurationForAutoSplit;
|
||||||
|
|
||||||
public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null, double delaySeconds = 0)
|
public ValueTransition(T initialValue, double defaultTotalDuration = 0.3, EasingType? defaultEasingType = null, Func<T, T, double, T>? interpolator = null)
|
||||||
{
|
{
|
||||||
_currentValue = initialValue;
|
_currentValue = initialValue;
|
||||||
_startValue = initialValue;
|
_startValue = initialValue;
|
||||||
_targetValue = initialValue;
|
_targetValue = initialValue;
|
||||||
_durationSeconds = durationSeconds;
|
_totalDurationForAutoSplit = defaultTotalDuration;
|
||||||
_delaySeconds = delaySeconds;
|
|
||||||
_delayRemaining = 0;
|
|
||||||
_progress = 1f;
|
|
||||||
_isTransitioning = false;
|
|
||||||
|
|
||||||
if (interpolator != null)
|
if (interpolator != null)
|
||||||
{
|
{
|
||||||
_interpolator = interpolator;
|
_interpolator = interpolator;
|
||||||
_easingType = null;
|
_easingType = null;
|
||||||
}
|
}
|
||||||
else if (easingType.HasValue)
|
else if (defaultEasingType != null)
|
||||||
{
|
{
|
||||||
_easingType = easingType;
|
SetEasingType(defaultEasingType);
|
||||||
_interpolator = GetInterpolatorByEasingType(_easingType.Value);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_easingType = Enums.EasingType.EaseInOutQuad;
|
SetEasingType(Enums.EasingType.EaseInOutQuad);
|
||||||
_interpolator = GetInterpolatorByEasingType(_easingType.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Configuration
|
||||||
|
|
||||||
public void SetDuration(double seconds)
|
public void SetDuration(double seconds)
|
||||||
{
|
{
|
||||||
if (seconds < 0)
|
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
|
||||||
throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive.");
|
_totalDurationForAutoSplit = seconds;
|
||||||
_durationSeconds = seconds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDurationMs(double millionSeconds) => SetDuration(millionSeconds / 1000.0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置启动延迟。
|
||||||
|
/// 原理:在动画队列最前方插入一个“数值不变”的关键帧。
|
||||||
|
/// </summary>
|
||||||
public void SetDelay(double seconds)
|
public void SetDelay(double seconds)
|
||||||
{
|
{
|
||||||
_delaySeconds = seconds;
|
_configuredDelaySeconds = seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void JumpTo(T value)
|
public void SetEasingType(Enums.EasingType? easingType)
|
||||||
{
|
{
|
||||||
|
_easingType = easingType;
|
||||||
|
_interpolator = GetInterpolatorByEasingType(easingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Control Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 立即跳转到指定值(停止动画)
|
||||||
|
/// </summary>
|
||||||
|
public void JumpTo(T value)
|
||||||
|
{
|
||||||
|
_keyframeQueue.Clear();
|
||||||
_currentValue = value;
|
_currentValue = value;
|
||||||
_startValue = value;
|
_startValue = value;
|
||||||
_targetValue = value;
|
_targetValue = value;
|
||||||
_progress = 1f;
|
|
||||||
_delayRemaining = 0;
|
|
||||||
_isTransitioning = false;
|
_isTransitioning = false;
|
||||||
|
_progress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(T value)
|
/// <summary>
|
||||||
|
/// 模式 A: 精确控制模式
|
||||||
|
/// 显式指定每一段的目标值和时长。
|
||||||
|
/// </summary>
|
||||||
|
public void Start(params Keyframe<T>[] keyframes)
|
||||||
{
|
{
|
||||||
_currentValue = value;
|
if (keyframes == null || keyframes.Length == 0) return;
|
||||||
_startValue = value;
|
|
||||||
_targetValue = value;
|
PrepareStart();
|
||||||
_progress = 0f;
|
|
||||||
_delayRemaining = 0;
|
// 1. 处理延迟 (插入静止帧)
|
||||||
_isTransitioning = false;
|
if (_configuredDelaySeconds > 0)
|
||||||
|
{
|
||||||
|
_keyframeQueue.Enqueue(new Keyframe<T>(_currentValue, _configuredDelaySeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartTransition(T targetValue, bool jumpTo = false)
|
// 2. 入队用户帧
|
||||||
|
foreach (var kf in keyframes)
|
||||||
{
|
{
|
||||||
if (jumpTo)
|
_keyframeQueue.Enqueue(kf);
|
||||||
{
|
|
||||||
JumpTo(targetValue);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetValue.Equals(_currentValue))
|
MoveToNextSegment(firstStart: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模式 B: 自动均分模式 (兼容旧写法)
|
||||||
|
/// 指定一串目标值,系统根据 SetDuration 的总时长平均分配。
|
||||||
|
/// </summary>
|
||||||
|
public void Start(params T[] values)
|
||||||
{
|
{
|
||||||
_startValue = _currentValue;
|
if (values == null || values.Length == 0) return;
|
||||||
_targetValue = targetValue;
|
|
||||||
_progress = 0f;
|
// 如果目标就是当前值且只有1帧,直接跳过以省性能
|
||||||
_delayRemaining = _delaySeconds;
|
if (values.Length == 1 && values[0].Equals(_currentValue) && _configuredDelaySeconds <= 0) return;
|
||||||
|
|
||||||
|
PrepareStart();
|
||||||
|
|
||||||
|
// 1. 处理延迟
|
||||||
|
if (_configuredDelaySeconds > 0)
|
||||||
|
{
|
||||||
|
_keyframeQueue.Enqueue(new Keyframe<T>(_currentValue, _configuredDelaySeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 计算均分时长
|
||||||
|
double autoStepDuration = _totalDurationForAutoSplit / values.Length;
|
||||||
|
|
||||||
|
// 3. 入队生成帧
|
||||||
|
foreach (var val in values)
|
||||||
|
{
|
||||||
|
_keyframeQueue.Enqueue(new Keyframe<T>(val, autoStepDuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveToNextSegment(firstStart: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Core Logic
|
||||||
|
|
||||||
|
private void PrepareStart()
|
||||||
|
{
|
||||||
|
_keyframeQueue.Clear();
|
||||||
_isTransitioning = true;
|
_isTransitioning = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Equals(double x, double y, double tolerance)
|
private void MoveToNextSegment(bool firstStart = false)
|
||||||
{
|
{
|
||||||
var diff = Math.Abs(x - y);
|
if (_keyframeQueue.Count > 0)
|
||||||
return diff <= tolerance || diff <= Math.Max(Math.Abs(x), Math.Abs(y)) * tolerance;
|
{
|
||||||
|
var kf = _keyframeQueue.Dequeue();
|
||||||
|
|
||||||
|
// 起点逻辑:如果是刚开始,起点是当前值;如果是中间切换,起点是上一段的终点
|
||||||
|
_startValue = firstStart ? _currentValue : _targetValue;
|
||||||
|
_targetValue = kf.Value;
|
||||||
|
_stepDuration = kf.Duration;
|
||||||
|
|
||||||
|
if (firstStart) _progress = 0f;
|
||||||
|
// 注意:非 firstStart 时不重置 _progress,保留溢出值以平滑过渡
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 队列耗尽,动画结束
|
||||||
|
_currentValue = _targetValue;
|
||||||
|
_isTransitioning = false;
|
||||||
|
_progress = 1f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(TimeSpan elapsedTime)
|
public void Update(TimeSpan elapsedTime)
|
||||||
{
|
{
|
||||||
if (!_isTransitioning) return;
|
if (!_isTransitioning) return;
|
||||||
|
|
||||||
if (_delayRemaining > 0)
|
double timeStep = elapsedTime.TotalSeconds;
|
||||||
{
|
|
||||||
double consume = Math.Min(_delayRemaining, elapsedTime.TotalSeconds);
|
|
||||||
_delayRemaining -= consume;
|
|
||||||
if (_delayRemaining > 0)
|
|
||||||
return;
|
|
||||||
elapsedTime = TimeSpan.FromSeconds(elapsedTime.TotalSeconds - consume);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_durationSeconds <= 0)
|
// 使用 while 处理单帧时间过长跨越多段的情况
|
||||||
|
while (timeStep > 0 && _isTransitioning)
|
||||||
{
|
{
|
||||||
_progress = 1f;
|
// 计算当前帧的步进比例
|
||||||
}
|
// 极小值保护,防止除以0
|
||||||
else
|
double progressDelta = (_stepDuration > 0.000001) ? (timeStep / _stepDuration) : 1.0;
|
||||||
{
|
|
||||||
_progress += elapsedTime.TotalSeconds / _durationSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_progress >= 1f)
|
if (_progress + progressDelta >= 1.0)
|
||||||
{
|
{
|
||||||
_progress = 1f;
|
// === 当前段结束 ===
|
||||||
|
|
||||||
|
// 1. 计算这一段实际消耗的时间
|
||||||
|
double timeConsumed = (1.0 - _progress) * _stepDuration;
|
||||||
|
|
||||||
|
// 2. 剩余时间留给下一段
|
||||||
|
timeStep -= timeConsumed;
|
||||||
|
|
||||||
|
// 3. 修正当前值到目标值
|
||||||
|
_progress = 1.0;
|
||||||
_currentValue = _targetValue;
|
_currentValue = _targetValue;
|
||||||
_isTransitioning = false;
|
|
||||||
|
// 4. 切换到下一段
|
||||||
|
MoveToNextSegment();
|
||||||
|
|
||||||
|
// 5. 如果还有下一段,进度归零
|
||||||
|
if (_isTransitioning) _progress = 0f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// === 当前段进行中 ===
|
||||||
|
_progress += progressDelta;
|
||||||
|
timeStep = 0; // 时间耗尽
|
||||||
|
|
||||||
|
// 插值计算
|
||||||
_currentValue = _interpolator(_startValue, _targetValue, _progress);
|
_currentValue = _interpolator(_startValue, _targetValue, _progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType? type)
|
#endregion
|
||||||
|
|
||||||
|
#region Interpolators
|
||||||
|
|
||||||
|
private Func<T, T, double, T> GetInterpolatorByEasingType(Enums.EasingType? type)
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(double))
|
if (typeof(T) == typeof(double))
|
||||||
{
|
{
|
||||||
@@ -156,58 +244,32 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
double s = (double)(object)start;
|
double s = (double)(object)start;
|
||||||
double e = (double)(object)end;
|
double e = (double)(object)end;
|
||||||
double t = progress;
|
double t = progress;
|
||||||
|
|
||||||
|
// 使用 EasingHelper (假设您的项目中已有此辅助类)
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case Enums.EasingType.EaseInOutSine:
|
case Enums.EasingType.EaseInOutSine: t = EasingHelper.EaseInOutSine(t); break;
|
||||||
t = EasingHelper.EaseInOutSine(t);
|
case Enums.EasingType.EaseInOutQuad: t = EasingHelper.EaseInOutQuad(t); break;
|
||||||
break;
|
case Enums.EasingType.EaseInOutCubic: t = EasingHelper.EaseInOutCubic(t); break;
|
||||||
case Enums.EasingType.EaseInOutQuad:
|
case Enums.EasingType.EaseInOutQuart: t = EasingHelper.EaseInOutQuart(t); break;
|
||||||
t = EasingHelper.EaseInOutQuad(t);
|
case Enums.EasingType.EaseInOutQuint: t = EasingHelper.EaseInOutQuint(t); break;
|
||||||
break;
|
case Enums.EasingType.EaseInOutExpo: t = EasingHelper.EaseInOutExpo(t); break;
|
||||||
case Enums.EasingType.EaseInOutCubic:
|
case Enums.EasingType.EaseInOutCirc: t = EasingHelper.EaseInOutCirc(t); break;
|
||||||
t = EasingHelper.EaseInOutCubic(t);
|
case Enums.EasingType.EaseInOutBack: t = EasingHelper.EaseInOutBack(t); break;
|
||||||
break;
|
case Enums.EasingType.EaseInOutElastic: t = EasingHelper.EaseInOutElastic(t); break;
|
||||||
case Enums.EasingType.EaseInOutQuart:
|
case Enums.EasingType.EaseInOutBounce: t = EasingHelper.EaseInOutBounce(t); break;
|
||||||
t = EasingHelper.EaseInOutQuart(t);
|
case Enums.EasingType.SmoothStep: t = EasingHelper.SmoothStep(t); break;
|
||||||
break;
|
case Enums.EasingType.Linear: t = EasingHelper.Linear(t); break;
|
||||||
case Enums.EasingType.EaseInOutQuint:
|
default: t = EasingHelper.EaseInOutQuad(t); break;
|
||||||
t = EasingHelper.EaseInOutQuint(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.EaseInOutExpo:
|
|
||||||
t = EasingHelper.EaseInOutExpo(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.EaseInOutCirc:
|
|
||||||
t = EasingHelper.EaseInOutCirc(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.EaseInOutBack:
|
|
||||||
t = EasingHelper.EaseInOutBack(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.EaseInOutElastic:
|
|
||||||
t = EasingHelper.EaseInOutElastic(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.EaseInOutBounce:
|
|
||||||
t = EasingHelper.EaseInOutBounce(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.SmoothStep:
|
|
||||||
t = EasingHelper.SmoothStep(t);
|
|
||||||
break;
|
|
||||||
case Enums.EasingType.Linear:
|
|
||||||
t = EasingHelper.Linear(t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
t = EasingHelper.EaseInOutQuad(t);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (T)(object)(s + (e - s) * t);
|
return (T)(object)(s + (e - s) * t);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}.");
|
|
||||||
|
throw new NotSupportedException($"Type {typeof(T)} is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEasingType(EasingType? easingType)
|
#endregion
|
||||||
{
|
|
||||||
_easingType = easingType;
|
|
||||||
_interpolator = GetInterpolatorByEasingType(easingType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user