mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
Compare commits
9 Commits
4e33444d8e
...
85f67c2ec6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f67c2ec6 | ||
|
|
bdbeb391ea | ||
|
|
3357e7aaf4 | ||
|
|
e43461d624 | ||
|
|
3e1907ad8c | ||
|
|
74eeffc8a6 | ||
|
|
c32eb3b877 | ||
|
|
047e53b830 | ||
|
|
fdb7bd16f6 |
@@ -6,8 +6,4 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Interfaces\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace BetterLyrics.Core
|
|
||||||
{
|
|
||||||
public class Class1
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.2.253.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"/>
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using BetterLyrics.WinUI3.Services.LocalizationService;
|
|||||||
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
||||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||||
using BetterLyrics.WinUI3.Services.PlayHistoryService;
|
using BetterLyrics.WinUI3.Services.PlayHistoryService;
|
||||||
|
using BetterLyrics.WinUI3.Services.PluginService;
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.Services.SMTCService;
|
using BetterLyrics.WinUI3.Services.SMTCService;
|
||||||
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||||
@@ -248,6 +249,7 @@ namespace BetterLyrics.WinUI3
|
|||||||
.AddSingleton<IPlayHistoryService, PlayHistoryService>()
|
.AddSingleton<IPlayHistoryService, PlayHistoryService>()
|
||||||
.AddSingleton<ILyricsCacheService, LyricsCacheService>()
|
.AddSingleton<ILyricsCacheService, LyricsCacheService>()
|
||||||
.AddSingleton<ISongSearchMapService, SongSearchMapService>()
|
.AddSingleton<ISongSearchMapService, SongSearchMapService>()
|
||||||
|
.AddSingleton<IPluginService, PluginService>()
|
||||||
|
|
||||||
// ViewModels
|
// ViewModels
|
||||||
.AddSingleton<AppSettingsControlViewModel>()
|
.AddSingleton<AppSettingsControlViewModel>()
|
||||||
|
|||||||
@@ -96,6 +96,10 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" 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.Relational" Version="10.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" 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" />
|
||||||
@@ -121,6 +125,7 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ namespace BetterLyrics.WinUI3.Enums
|
|||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
AppleMusic,
|
AppleMusic,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,5 +13,6 @@
|
|||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
LibreTranslate,
|
LibreTranslate,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
LocalEslrcFile,
|
LocalEslrcFile,
|
||||||
LocalTtmlFile,
|
LocalTtmlFile,
|
||||||
BetterLyrics,
|
BetterLyrics,
|
||||||
CutletDocker
|
CutletDocker,
|
||||||
|
Plugin = 999,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
new LyricsLine
|
new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = 0,
|
StartMs = 0,
|
||||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds,
|
||||||
PrimaryText = "● ● ●",
|
PrimaryText = "● ● ●",
|
||||||
PrimarySyllables = [new BaseLyrics { Text = "● ● ●", StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds }],
|
PrimarySyllables = [new BaseLyrics { Text = "● ● ●", StartMs = 0, EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds }],
|
||||||
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
LanguageCode = "N/A",
|
LanguageCode = "N/A",
|
||||||
|
|||||||
@@ -68,11 +68,17 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
for (int i = safeStart; i <= safeEnd; i++)
|
for (int i = safeStart; i <= safeEnd; i++)
|
||||||
{
|
{
|
||||||
var line = lines[i];
|
var line = lines[i];
|
||||||
|
|
||||||
var lineHeight = line.PrimaryLineHeight;
|
var lineHeight = line.PrimaryLineHeight;
|
||||||
|
|
||||||
if (lineHeight == null || lineHeight <= 0) continue;
|
if (lineHeight == null || lineHeight <= 0) continue;
|
||||||
|
|
||||||
|
bool isWordAnimationEnabled = lyricsEffect.WordByWordEffectMode switch
|
||||||
|
{
|
||||||
|
Enums.WordByWordEffectMode.Auto => line.IsPrimaryHasRealSyllableInfo,
|
||||||
|
Enums.WordByWordEffectMode.Always => true,
|
||||||
|
Enums.WordByWordEffectMode.Never => false,
|
||||||
|
_ => line.IsPrimaryHasRealSyllableInfo
|
||||||
|
};
|
||||||
|
|
||||||
double targetCharFloat = lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust
|
double targetCharFloat = lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust
|
||||||
? lineHeight.Value * 0.1
|
? lineHeight.Value * 0.1
|
||||||
: lyricsEffect.LyricsFloatAnimationAmount;
|
: lyricsEffect.LyricsFloatAnimationAmount;
|
||||||
@@ -83,7 +89,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
? 1.15
|
? 1.15
|
||||||
: lyricsEffect.LyricsScaleEffectAmount / 100.0;
|
: lyricsEffect.LyricsScaleEffectAmount / 100.0;
|
||||||
|
|
||||||
var maxAnimationDurationMs = Math.Max(line.EndMs - currentPositionMs, 0);
|
var maxAnimationDurationMs = Math.Max(line.EndMs ?? 0 - currentPositionMs, 0);
|
||||||
|
|
||||||
bool isSecondaryLinePlaying = line.GetIsPlaying(currentPositionMs);
|
bool isSecondaryLinePlaying = line.GetIsPlaying(currentPositionMs);
|
||||||
bool isSecondaryLinePlayingChanged = line.IsPlayingLastFrame != isSecondaryLinePlaying;
|
bool isSecondaryLinePlayingChanged = line.IsPlayingLastFrame != isSecondaryLinePlaying;
|
||||||
@@ -176,7 +182,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
line.AngleTransition.SetDelay(yScrollDelay);
|
line.AngleTransition.SetDelay(yScrollDelay);
|
||||||
line.AngleTransition.Start(
|
line.AngleTransition.Start(
|
||||||
(isFanEnabled && !isMouseScrolling) ?
|
(isFanEnabled && !isMouseScrolling) ?
|
||||||
Math.PI * (fanAngleRad / 180.0) * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) :
|
fanAngleRad * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) :
|
||||||
0);
|
0);
|
||||||
|
|
||||||
line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
||||||
@@ -187,98 +193,100 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
line.YOffsetTransition.Start(targetYScrollOffset);
|
line.YOffsetTransition.Start(targetYScrollOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLayoutChanged || isSecondaryLinePlayingChanged)
|
if (isWordAnimationEnabled)
|
||||||
{
|
{
|
||||||
// 辉光动画
|
if (isSecondaryLinePlayingChanged)
|
||||||
if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar
|
|
||||||
&& isSecondaryLinePlaying)
|
|
||||||
{
|
{
|
||||||
foreach (var renderChar in line.PrimaryRenderChars)
|
// 辉光动画
|
||||||
|
if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar
|
||||||
|
&& isSecondaryLinePlaying)
|
||||||
{
|
{
|
||||||
var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0;
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
|
|
||||||
renderChar.GlowTransition.Start(
|
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepInOutDuration),
|
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepLastingDuration),
|
|
||||||
new Models.Keyframe<double>(0, stepInOutDuration)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 浮动动画
|
|
||||||
if (isFloatEnabled)
|
|
||||||
{
|
|
||||||
foreach (var renderChar in line.PrimaryRenderChars)
|
|
||||||
{
|
|
||||||
renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetCharFloat : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字符动画
|
|
||||||
foreach (var renderChar in line.PrimaryRenderChars)
|
|
||||||
{
|
|
||||||
renderChar.ProgressPlayed = renderChar.GetPlayProgress(currentPositionMs);
|
|
||||||
|
|
||||||
bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs);
|
|
||||||
bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying;
|
|
||||||
|
|
||||||
if (isCharPlayingChanged)
|
|
||||||
{
|
|
||||||
if (isFloatEnabled)
|
|
||||||
{
|
|
||||||
renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs));
|
|
||||||
renderChar.FloatTransition.Start(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChar.IsPlayingLastFrame = isCharPlaying;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 音节动画
|
|
||||||
foreach (var syllable in line.PrimaryRenderSyllables)
|
|
||||||
{
|
|
||||||
bool isSyllablePlaying = syllable.GetIsPlaying(currentPositionMs);
|
|
||||||
bool isSyllablePlayingChanged = syllable.IsPlayingLastFrame != isSyllablePlaying;
|
|
||||||
|
|
||||||
if (isSyllablePlayingChanged)
|
|
||||||
{
|
|
||||||
if (isScaleEnabled && isSyllablePlaying)
|
|
||||||
{
|
|
||||||
foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
|
|
||||||
{
|
{
|
||||||
if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration)
|
var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0;
|
||||||
{
|
var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
|
||||||
var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
|
|
||||||
renderChar.ScaleTransition.Start(
|
|
||||||
new Models.Keyframe<double>(targetCharScale, stepDuration),
|
|
||||||
new Models.Keyframe<double>(1.0, stepDuration)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGlowEnabled && isSyllablePlaying && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LongDurationSyllable
|
|
||||||
&& syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration)
|
|
||||||
{
|
|
||||||
foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
|
|
||||||
{
|
|
||||||
var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
|
|
||||||
renderChar.GlowTransition.Start(
|
renderChar.GlowTransition.Start(
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepDuration),
|
new Models.Keyframe<double>(targetCharGlow, stepInOutDuration),
|
||||||
new Models.Keyframe<double>(0, stepDuration)
|
new Models.Keyframe<double>(targetCharGlow, stepLastingDuration),
|
||||||
|
new Models.Keyframe<double>(0, stepInOutDuration)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syllable.IsPlayingLastFrame = isSyllablePlaying;
|
// 浮动动画
|
||||||
|
if (isFloatEnabled)
|
||||||
|
{
|
||||||
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
|
{
|
||||||
|
renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetCharFloat : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 使动画步进一帧
|
// 字符动画
|
||||||
foreach (var renderChar in line.PrimaryRenderChars)
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
{
|
{
|
||||||
renderChar.Update(elapsedTime);
|
renderChar.ProgressPlayed = renderChar.GetPlayProgress(currentPositionMs);
|
||||||
|
|
||||||
|
bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs);
|
||||||
|
bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying;
|
||||||
|
|
||||||
|
if (isCharPlayingChanged)
|
||||||
|
{
|
||||||
|
if (isFloatEnabled)
|
||||||
|
{
|
||||||
|
renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs));
|
||||||
|
renderChar.FloatTransition.Start(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderChar.IsPlayingLastFrame = isCharPlaying;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 音节动画
|
||||||
|
foreach (var syllable in line.PrimaryRenderSyllables)
|
||||||
|
{
|
||||||
|
bool isSyllablePlaying = syllable.GetIsPlaying(currentPositionMs);
|
||||||
|
bool isSyllablePlayingChanged = syllable.IsPlayingLastFrame != isSyllablePlaying;
|
||||||
|
|
||||||
|
if (isSyllablePlayingChanged)
|
||||||
|
{
|
||||||
|
if (isScaleEnabled && isSyllablePlaying)
|
||||||
|
{
|
||||||
|
foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
|
||||||
|
{
|
||||||
|
if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration)
|
||||||
|
{
|
||||||
|
var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
|
||||||
|
renderChar.ScaleTransition.Start(
|
||||||
|
new Models.Keyframe<double>(targetCharScale, stepDuration),
|
||||||
|
new Models.Keyframe<double>(1.0, stepDuration)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGlowEnabled && isSyllablePlaying && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LongDurationSyllable
|
||||||
|
&& syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration)
|
||||||
|
{
|
||||||
|
foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
|
||||||
|
{
|
||||||
|
var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
|
||||||
|
renderChar.GlowTransition.Start(
|
||||||
|
new Models.Keyframe<double>(targetCharGlow, stepDuration),
|
||||||
|
new Models.Keyframe<double>(0, stepDuration)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syllable.IsPlayingLastFrame = isSyllablePlaying;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
|
{
|
||||||
|
renderChar.Update(elapsedTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line.Update(elapsedTime);
|
line.Update(elapsedTime);
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
lanesEndMs.Add(0);
|
lanesEndMs.Add(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
lanesEndMs[assignedLane] = end;
|
lanesEndMs[assignedLane] = end ?? 0;
|
||||||
line.LaneIndex = assignedLane;
|
line.LaneIndex = assignedLane;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
|
|
||||||
if (line == null) return state;
|
if (line == null) return state;
|
||||||
|
|
||||||
double lineEndMs = line.EndMs;
|
double lineEndMs = line.EndMs ?? 0;
|
||||||
|
|
||||||
// 还没到
|
// 还没到
|
||||||
if (currentTimeMs < line.StartMs) return state;
|
if (currentTimeMs < line.StartMs) return state;
|
||||||
@@ -91,7 +91,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
switch (wordByWordEffectMode)
|
switch (wordByWordEffectMode)
|
||||||
{
|
{
|
||||||
case WordByWordEffectMode.Auto:
|
case WordByWordEffectMode.Auto:
|
||||||
if (line.PrimaryRenderSyllables.Count > 1)
|
if (line.IsPrimaryHasRealSyllableInfo)
|
||||||
{
|
{
|
||||||
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
state.SyllableProgress = 1f;
|
state.SyllableProgress = 1f;
|
||||||
return state;
|
return state;
|
||||||
case WordByWordEffectMode.Always:
|
case WordByWordEffectMode.Always:
|
||||||
if (line.PrimaryRenderSyllables.Count > 1)
|
if (line.IsPrimaryHasRealSyllableInfo)
|
||||||
{
|
{
|
||||||
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
||||||
}
|
}
|
||||||
@@ -129,8 +129,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
var timing = line.PrimaryRenderSyllables[i];
|
var timing = line.PrimaryRenderSyllables[i];
|
||||||
var nextTiming = (i + 1 < count) ? line.PrimaryRenderSyllables[i + 1] : null;
|
var nextTiming = (i + 1 < count) ? line.PrimaryRenderSyllables[i + 1] : null;
|
||||||
|
|
||||||
//double timingEndMs = timing.EndMs ?? nextTiming?.StartMs ?? lineEndMs;
|
double timingEndMs = timing.EndMs ?? 0;
|
||||||
double timingEndMs = timing.EndMs;
|
|
||||||
|
|
||||||
// 在当前字范围内
|
// 在当前字范围内
|
||||||
if (time >= timing.StartMs && time <= timingEndMs)
|
if (time >= timing.StartMs && time <= timingEndMs)
|
||||||
|
|||||||
@@ -47,10 +47,15 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
|
|
||||||
[NotMapped][JsonIgnore] public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
[NotMapped][JsonIgnore] public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
||||||
|
|
||||||
|
[MaxLength(128)]
|
||||||
|
public string? PluginId { get; set; }
|
||||||
|
|
||||||
public object Clone()
|
public object Clone()
|
||||||
{
|
{
|
||||||
return new LyricsCacheItem()
|
return new LyricsCacheItem()
|
||||||
{
|
{
|
||||||
|
PluginId = this.PluginId,
|
||||||
|
|
||||||
Provider = this.Provider,
|
Provider = this.Provider,
|
||||||
TranslationProvider = this.TranslationProvider,
|
TranslationProvider = this.TranslationProvider,
|
||||||
TransliterationProvider = this.TransliterationProvider,
|
TransliterationProvider = this.TransliterationProvider,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
public class BaseLyrics
|
public class BaseLyrics
|
||||||
{
|
{
|
||||||
public int StartMs { get; set; }
|
public int StartMs { get; set; }
|
||||||
public int EndMs { get; set; }
|
public int? EndMs { get; set; } = null;
|
||||||
public int DurationMs => EndMs - StartMs;
|
public int DurationMs => Math.Max((EndMs ?? 0) - StartMs, 0);
|
||||||
|
|
||||||
public string Text { get; set; } = "";
|
public string Text { get; set; } = "";
|
||||||
public int Length => Text.Length;
|
public int Length => Text.Length;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
public new string Text => PrimaryText;
|
public new string Text => PrimaryText;
|
||||||
public new int StartIndex = 0;
|
public new int StartIndex = 0;
|
||||||
|
|
||||||
|
public bool IsPrimaryHasRealSyllableInfo { get; set; } = false;
|
||||||
|
|
||||||
public LyricsLine()
|
public LyricsLine()
|
||||||
{
|
{
|
||||||
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)
|
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
|
|
||||||
public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height;
|
public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height;
|
||||||
|
|
||||||
|
public bool IsPrimaryHasRealSyllableInfo { get; set; }
|
||||||
|
|
||||||
public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine)
|
public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine)
|
||||||
{
|
{
|
||||||
AngleTransition = new(
|
AngleTransition = new(
|
||||||
@@ -130,6 +132,7 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
PrimaryText = lyricsLine.PrimaryText;
|
PrimaryText = lyricsLine.PrimaryText;
|
||||||
SecondaryText = lyricsLine.SecondaryText;
|
SecondaryText = lyricsLine.SecondaryText;
|
||||||
PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList();
|
PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList();
|
||||||
|
IsPrimaryHasRealSyllableInfo = lyricsLine.IsPrimaryHasRealSyllableInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
||||||
|
|||||||
@@ -40,26 +40,14 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
startIndex += text.Length;
|
startIndex += text.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lineEndMs = 0;
|
|
||||||
|
|
||||||
if (syllables.Count > 0)
|
|
||||||
{
|
|
||||||
var lastSyllable = syllables[syllables.Count - 1];
|
|
||||||
if (string.IsNullOrWhiteSpace(lastSyllable.Text))
|
|
||||||
{
|
|
||||||
lineEndMs = lastSyllable.StartMs;
|
|
||||||
syllables.RemoveAt(syllables.Count - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syllables.Count > 1)
|
if (syllables.Count > 1)
|
||||||
{
|
{
|
||||||
lrcLines.Add(new LyricsLine
|
lrcLines.Add(new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = syllables[0].StartMs,
|
StartMs = syllables[0].StartMs,
|
||||||
EndMs = lineEndMs,
|
|
||||||
PrimaryText = string.Concat(syllables.Select(s => s.Text)),
|
PrimaryText = string.Concat(syllables.Select(s => s.Text)),
|
||||||
PrimarySyllables = syllables
|
PrimarySyllables = syllables,
|
||||||
|
IsPrimaryHasRealSyllableInfo = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -81,7 +69,13 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
content = bracketRegex!.Replace(line, "").Trim();
|
content = bracketRegex!.Replace(line, "").Trim();
|
||||||
if (content == "//") content = "";
|
if (content == "//") content = "";
|
||||||
|
|
||||||
lrcLines.Add(new LyricsLine { StartMs = lineStartMs, PrimaryText = content });
|
var lyricsLine = new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = lineStartMs,
|
||||||
|
PrimaryText = content,
|
||||||
|
IsPrimaryHasRealSyllableInfo = false
|
||||||
|
};
|
||||||
|
lrcLines.Add(lyricsLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,5 +119,6 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
var lineWrite = new LyricsLine
|
var lineWrite = new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = lineRead.StartTime ?? 0,
|
StartMs = lineRead.StartTime ?? 0,
|
||||||
EndMs = lineRead.EndTime ?? (nextLineRead?.StartTime ?? 0),
|
|
||||||
PrimaryText = lineRead.Text,
|
PrimaryText = lineRead.Text,
|
||||||
PrimarySyllables = [],
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
StartMs = containerStartMs,
|
StartMs = containerStartMs,
|
||||||
EndMs = containerEndMs,
|
EndMs = containerEndMs,
|
||||||
PrimaryText = fullOriginalText,
|
PrimaryText = fullOriginalText,
|
||||||
PrimarySyllables = syllables
|
PrimarySyllables = syllables,
|
||||||
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var transSpan = container.Elements()
|
var transSpan = container.Elements()
|
||||||
@@ -151,7 +152,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = startMs,
|
StartMs = startMs,
|
||||||
EndMs = endMs,
|
EndMs = endMs,
|
||||||
PrimaryText = text
|
PrimaryText = text,
|
||||||
|
IsPrimaryHasRealSyllableInfo = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -160,7 +162,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = startMs,
|
StartMs = startMs,
|
||||||
EndMs = endMs,
|
EndMs = endMs,
|
||||||
PrimaryText = ""
|
PrimaryText = "",
|
||||||
|
IsPrimaryHasRealSyllableInfo = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
LoadTransliteration(lyricsSearchResult);
|
LoadTransliteration(lyricsSearchResult);
|
||||||
GenerateTransliterationLyricsData();
|
GenerateTransliterationLyricsData();
|
||||||
|
|
||||||
|
EnsureEndMs(lyricsSearchResult?.Duration);
|
||||||
|
EnsureSyllables();
|
||||||
|
|
||||||
return _lyricsDataArr;
|
return _lyricsDataArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,5 +274,84 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureEndMs(double? duration)
|
||||||
|
{
|
||||||
|
foreach (var lyricsData in _lyricsDataArr)
|
||||||
|
{
|
||||||
|
var lines = lyricsData.LyricsLines;
|
||||||
|
// 计算结束时间
|
||||||
|
for (int i = 0; i < lines.Count; i++)
|
||||||
|
{
|
||||||
|
// 计算行结束时间
|
||||||
|
if (lines[i].EndMs == null)
|
||||||
|
{
|
||||||
|
if (i + 1 < lines.Count)
|
||||||
|
{
|
||||||
|
lines[i].EndMs = lines[i + 1].StartMs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines[i].EndMs = (int)(duration ?? 0) * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 计算音节结束时间
|
||||||
|
for (int j = 0; j < lines[i].PrimarySyllables.Count; j++)
|
||||||
|
{
|
||||||
|
var syllable = lines[i].PrimarySyllables[j];
|
||||||
|
if (syllable.EndMs == null)
|
||||||
|
{
|
||||||
|
if (j < lines[i].PrimarySyllables.Count - 1)
|
||||||
|
{
|
||||||
|
syllable.EndMs = lines[i].PrimarySyllables[j + 1].StartMs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syllable.EndMs = lines[i].EndMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke this after <see cref="EnsureEndMs"/>
|
||||||
|
/// </summary>
|
||||||
|
private void EnsureSyllables()
|
||||||
|
{
|
||||||
|
foreach (var lyricsData in _lyricsDataArr)
|
||||||
|
{
|
||||||
|
if (lyricsData == null) continue;
|
||||||
|
|
||||||
|
var lines = lyricsData.LyricsLines;
|
||||||
|
if (lines == null) continue;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (line == null) continue;
|
||||||
|
if (line.IsPrimaryHasRealSyllableInfo) continue;
|
||||||
|
if (line.PrimarySyllables.Count > 0) continue;
|
||||||
|
|
||||||
|
var content = line.PrimaryText;
|
||||||
|
var length = content.Length;
|
||||||
|
if (length == 0) continue;
|
||||||
|
|
||||||
|
var avgSyllableDuration = line.DurationMs / length;
|
||||||
|
if (avgSyllableDuration == 0) continue;
|
||||||
|
|
||||||
|
for (int j = 0; j < length; j++)
|
||||||
|
{
|
||||||
|
line.PrimarySyllables.Add(new BaseLyrics
|
||||||
|
{
|
||||||
|
Text = content[j].ToString(),
|
||||||
|
StartIndex = j,
|
||||||
|
StartMs = line.StartMs + avgSyllableDuration * j,
|
||||||
|
EndMs = line.StartMs + avgSyllableDuration * (j + 1),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
|
using BetterLyrics.Core.Interfaces;
|
||||||
using BetterLyrics.WinUI3.Constants;
|
using BetterLyrics.WinUI3.Constants;
|
||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Extensions;
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
@@ -10,6 +11,7 @@ using BetterLyrics.WinUI3.Models.Settings;
|
|||||||
using BetterLyrics.WinUI3.Providers;
|
using BetterLyrics.WinUI3.Providers;
|
||||||
using BetterLyrics.WinUI3.Services.FileSystemService;
|
using BetterLyrics.WinUI3.Services.FileSystemService;
|
||||||
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
||||||
|
using BetterLyrics.WinUI3.Services.PluginService;
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||||
using Lyricify.Lyrics.Helpers;
|
using Lyricify.Lyrics.Helpers;
|
||||||
@@ -36,6 +38,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
|||||||
private readonly IFileSystemService _fileSystemService;
|
private readonly IFileSystemService _fileSystemService;
|
||||||
private readonly ILyricsCacheService _lyricsCacheService;
|
private readonly ILyricsCacheService _lyricsCacheService;
|
||||||
private readonly ISongSearchMapService _songSearchMapService;
|
private readonly ISongSearchMapService _songSearchMapService;
|
||||||
|
private readonly IPluginService _pluginService;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public LyricsSearchService(
|
public LyricsSearchService(
|
||||||
@@ -43,6 +46,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
|||||||
IFileSystemService fileSystemService,
|
IFileSystemService fileSystemService,
|
||||||
ILyricsCacheService lyricsCacheService,
|
ILyricsCacheService lyricsCacheService,
|
||||||
ISongSearchMapService songSearchMapService,
|
ISongSearchMapService songSearchMapService,
|
||||||
|
IPluginService pluginService,
|
||||||
ILogger<LyricsSearchService> logger
|
ILogger<LyricsSearchService> logger
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -50,6 +54,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
|||||||
_fileSystemService = fileSystemService;
|
_fileSystemService = fileSystemService;
|
||||||
_lyricsCacheService = lyricsCacheService;
|
_lyricsCacheService = lyricsCacheService;
|
||||||
_songSearchMapService = songSearchMapService;
|
_songSearchMapService = songSearchMapService;
|
||||||
|
_pluginService = pluginService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
_lrcLibHttpClient = new();
|
_lrcLibHttpClient = new();
|
||||||
@@ -207,11 +212,26 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("SearchAllAsync {SongInfo}", songInfo);
|
_logger.LogInformation("SearchAllAsync {SongInfo}", songInfo);
|
||||||
var results = new List<LyricsCacheItem>();
|
var results = new List<LyricsCacheItem>();
|
||||||
|
|
||||||
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
|
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
|
||||||
{
|
{
|
||||||
|
if (provider == LyricsSearchProvider.Plugin) continue;
|
||||||
|
|
||||||
var searchResult = await SearchSingleAsync(songInfo, provider, checkCache, token);
|
var searchResult = await SearchSingleAsync(songInfo, provider, checkCache, token);
|
||||||
results.Add(searchResult);
|
results.Add(searchResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_pluginService.Providers.Any())
|
||||||
|
{
|
||||||
|
foreach (var plugin in _pluginService.Providers)
|
||||||
|
{
|
||||||
|
if (token.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
var pluginResult = await SearchPluginAsync(songInfo, plugin, token);
|
||||||
|
results.Add(pluginResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,5 +693,41 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
|||||||
|
|
||||||
return lyricsSearchResult;
|
return lyricsSearchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<LyricsCacheItem> SearchPluginAsync(SongInfo songInfo, ILyricsProvider plugin, CancellationToken token)
|
||||||
|
{
|
||||||
|
var cacheItem = new LyricsCacheItem
|
||||||
|
{
|
||||||
|
Provider = LyricsSearchProvider.Plugin,
|
||||||
|
PluginId = plugin.Id,
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await plugin.GetLyricsAsync(songInfo.Title, songInfo.Artist, songInfo.Album, songInfo.Duration);
|
||||||
|
|
||||||
|
if (result != null && !string.IsNullOrEmpty(result.Raw))
|
||||||
|
{
|
||||||
|
cacheItem.Title = result.Title;
|
||||||
|
cacheItem.Artist = result.Artist;
|
||||||
|
cacheItem.Album = result.Album;
|
||||||
|
cacheItem.Duration = result.Duration;
|
||||||
|
|
||||||
|
cacheItem.Raw = result.Raw;
|
||||||
|
cacheItem.Translation = result.Translation;
|
||||||
|
cacheItem.Transliteration = result.Transliteration;
|
||||||
|
|
||||||
|
cacheItem.Reference = result.Reference ?? "about:blank";
|
||||||
|
cacheItem.MatchPercentage = MetadataComparer.CalculateScore(songInfo, cacheItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Plugin {PluginName} failed to search", plugin.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheItem;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using BetterLyrics.Core.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Services.PluginService
|
||||||
|
{
|
||||||
|
public interface IPluginService
|
||||||
|
{
|
||||||
|
IReadOnlyList<ILyricsProvider> Providers { get; }
|
||||||
|
|
||||||
|
void LoadPlugins();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Services.PluginService
|
||||||
|
{
|
||||||
|
public class PluginLoadContext : AssemblyLoadContext
|
||||||
|
{
|
||||||
|
private AssemblyDependencyResolver _resolver;
|
||||||
|
|
||||||
|
public PluginLoadContext(string pluginPath)
|
||||||
|
{
|
||||||
|
_resolver = new AssemblyDependencyResolver(pluginPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Assembly? Load(AssemblyName assemblyName)
|
||||||
|
{
|
||||||
|
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||||
|
if (assemblyPath != null)
|
||||||
|
{
|
||||||
|
return LoadFromAssemblyPath(assemblyPath);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using BetterLyrics.Core.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Services.PluginService
|
||||||
|
{
|
||||||
|
public class PluginService : IPluginService
|
||||||
|
{
|
||||||
|
private List<ILyricsProvider> _providers = new();
|
||||||
|
|
||||||
|
public IReadOnlyList<ILyricsProvider> Providers => _providers;
|
||||||
|
|
||||||
|
public void LoadPlugins()
|
||||||
|
{
|
||||||
|
// 在涉及加载程序集的地方:
|
||||||
|
// var context = new PluginLoadContext(pluginPath);
|
||||||
|
// 它是本文件夹下的 internal 或者是 public 类,直接用即可。
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 18
|
# Visual Studio Version 18
|
||||||
VisualStudioVersion = 18.1.11312.151 d18.0
|
VisualStudioVersion = 18.1.11312.151
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "BetterLyrics.WinUI3 (Package)", "BetterLyrics.WinUI3\BetterLyrics.WinUI3 (Package)\BetterLyrics.WinUI3 (Package).wapproj", "{6576CD19-EF92-4099-B37D-E2D8EBDB6BF5}"
|
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "BetterLyrics.WinUI3 (Package)", "BetterLyrics.WinUI3\BetterLyrics.WinUI3 (Package)\BetterLyrics.WinUI3 (Package).wapproj", "{6576CD19-EF92-4099-B37D-E2D8EBDB6BF5}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorThief.WinUI3", "ColorT
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Core", "BetterLyrics.Core\BetterLyrics.Core.csproj", "{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Core", "BetterLyrics.Core\BetterLyrics.Core.csproj", "{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Plugins.Demo", "BetterLyrics.Plugins.Demo\BetterLyrics.Plugins.Demo.csproj", "{87D235CA-4311-4766-8186-AD9B193DFABC}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|ARM64 = Debug|ARM64
|
Debug|ARM64 = Debug|ARM64
|
||||||
@@ -89,6 +91,18 @@ Global
|
|||||||
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x64.Build.0 = Release|Any CPU
|
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x86.ActiveCfg = Release|Any CPU
|
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x86.Build.0 = Release|Any CPU
|
{0F47FE6F-D0AA-49E5-8F33-78DFDEB1F810}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|ARM64.Build.0 = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user