mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
fix
This commit is contained in:
@@ -68,6 +68,10 @@
|
||||
<Folder Include="Controls\" />
|
||||
<Folder Include="ViewModels\Lyrics\" />
|
||||
</ItemGroup>
|
||||
<!--Disable Trimming for Specific Packages-->
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
</ItemGroup>
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class LyricsParser
|
||||
{
|
||||
private List<LyricsLine> _lyricsLines = [];
|
||||
private List<List<LyricsLine>> _multiLangLyricsLines = [];
|
||||
|
||||
public List<LyricsLine> Parse(
|
||||
public List<List<LyricsLine>> Parse(
|
||||
string raw,
|
||||
LyricsFormat? lyricsFormat = null,
|
||||
string? title = null,
|
||||
@@ -21,7 +21,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
int durationMs = 0
|
||||
)
|
||||
{
|
||||
_lyricsLines = [];
|
||||
_multiLangLyricsLines = [];
|
||||
switch (lyricsFormat)
|
||||
{
|
||||
case LyricsFormat.Lrc:
|
||||
@@ -34,21 +34,24 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _multiLangLyricsLines;
|
||||
}
|
||||
|
||||
if (_lyricsLines.Count > 0 && _lyricsLines[0].StartMs > 0)
|
||||
private void PostProcessLyricsLines(List<LyricsLine> lines)
|
||||
{
|
||||
if (lines.Count > 0 && lines[0].StartMs > 0)
|
||||
{
|
||||
_lyricsLines.Insert(
|
||||
lines.Insert(
|
||||
0,
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = _lyricsLines[0].StartMs,
|
||||
Texts = [""],
|
||||
EndMs = lines[0].StartMs,
|
||||
Text = "",
|
||||
CharTimings = [],
|
||||
}
|
||||
);
|
||||
}
|
||||
return _lyricsLines;
|
||||
}
|
||||
|
||||
private void ParseLrc(string raw, int durationMs)
|
||||
@@ -66,13 +69,15 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
var matches = syllableRegex.Matches(line);
|
||||
var syllables = new List<(int, string)>();
|
||||
foreach (Match m in matches)
|
||||
for (int i = 0; i < matches.Count; i++)
|
||||
{
|
||||
var m = matches[i];
|
||||
int min = int.Parse(m.Groups[2].Value);
|
||||
int sec = int.Parse(m.Groups[3].Value);
|
||||
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
|
||||
int totalMs = min * 60_000 + sec * 1000 + ms;
|
||||
string text = m.Groups[6].Value;
|
||||
|
||||
syllables.Add((totalMs, text));
|
||||
}
|
||||
if (syllables.Count > 0)
|
||||
@@ -105,37 +110,71 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
// 按时间排序
|
||||
lrcLines = lrcLines.OrderBy(l => l.time).ToList();
|
||||
// 按时间分组
|
||||
var grouped = lrcLines.GroupBy(l => l.time).OrderBy(g => g.Key).ToList();
|
||||
int languageCount = grouped.Max(g => g.Count());
|
||||
|
||||
// 构建 LyricsLine
|
||||
for (int i = 0; i < lrcLines.Count; i++)
|
||||
// 初始化每种语言的歌词列表
|
||||
_multiLangLyricsLines.Clear();
|
||||
for (int i = 0; i < languageCount; i++)
|
||||
_multiLangLyricsLines.Add(new List<LyricsLine>());
|
||||
|
||||
// 遍历每个时间分组
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var (start, text, syllables) = lrcLines[i];
|
||||
var line = new LyricsLine
|
||||
var linesInGroup = group.ToList();
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
{
|
||||
StartMs = start,
|
||||
EndMs = (i + 1 < lrcLines.Count) ? lrcLines[i + 1].time : durationMs,
|
||||
Texts = [text],
|
||||
CharTimings = [],
|
||||
};
|
||||
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
for (int j = 0; j < syllables.Count; j++)
|
||||
// 如果该语言有翻译,取对应行,否则用原文(第一行)
|
||||
var (start, text, syllables) =
|
||||
langIdx < linesInGroup.Count ? linesInGroup[langIdx] : linesInGroup[0];
|
||||
var line = new LyricsLine
|
||||
{
|
||||
var (charStart, charText) = syllables[j];
|
||||
int charEnd =
|
||||
(j + 1 < syllables.Count) ? syllables[j + 1].Item1 : line.EndMs;
|
||||
if (!string.IsNullOrEmpty(charText))
|
||||
StartMs = start,
|
||||
EndMs = 0, // 稍后统一修正
|
||||
Text = text,
|
||||
CharTimings = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
for (int j = 0; j < syllables.Count; j++)
|
||||
{
|
||||
var (charStart, charText) = syllables[j];
|
||||
int charEnd = (j + 1 < syllables.Count) ? syllables[j + 1].Item1 : 0;
|
||||
line.CharTimings.Add(
|
||||
new CharTiming { StartMs = charStart, EndMs = charEnd }
|
||||
);
|
||||
}
|
||||
}
|
||||
_multiLangLyricsLines[langIdx].Add(line);
|
||||
}
|
||||
_lyricsLines.Add(line);
|
||||
}
|
||||
|
||||
// 修正 EndMs
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
{
|
||||
var linesInSingleLang = _multiLangLyricsLines[langIdx];
|
||||
for (int i = 0; i < linesInSingleLang.Count; i++)
|
||||
{
|
||||
if (i + 1 < linesInSingleLang.Count)
|
||||
linesInSingleLang[i].EndMs = linesInSingleLang[i + 1].StartMs;
|
||||
else
|
||||
linesInSingleLang[i].EndMs = durationMs;
|
||||
|
||||
// 修正 CharTimings 的最后一个 EndMs
|
||||
var timings = linesInSingleLang[i].CharTimings;
|
||||
if (timings.Count > 0)
|
||||
{
|
||||
for (int j = 0; j < timings.Count; j++)
|
||||
{
|
||||
if (j + 1 < timings.Count)
|
||||
timings[j].EndMs = timings[j + 1].StartMs;
|
||||
else
|
||||
timings[j].EndMs = linesInSingleLang[i].EndMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
PostProcessLyricsLines(linesInSingleLang);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +182,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
try
|
||||
{
|
||||
List<LyricsLine> singleLangLyricsLine = [];
|
||||
var xdoc = XDocument.Parse(raw);
|
||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||
if (body == null)
|
||||
@@ -191,16 +231,18 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
if (spans.Count == 0)
|
||||
text = p.Value.Trim();
|
||||
|
||||
_lyricsLines.Add(
|
||||
singleLangLyricsLine.Add(
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
Texts = [text],
|
||||
Text = text,
|
||||
CharTimings = charTimings,
|
||||
}
|
||||
);
|
||||
}
|
||||
PostProcessLyricsLines(singleLangLyricsLine);
|
||||
_multiLangLyricsLines.Add(singleLangLyricsLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
16
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs
Normal file
16
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsData.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsData
|
||||
{
|
||||
public int LanguageIndex { get; set; } = 0;
|
||||
|
||||
public List<LyricsLine> LyricsLines => MultiLangLyricsLines[LanguageIndex];
|
||||
public List<List<LyricsLine>> MultiLangLyricsLines { get; set; } = [];
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsLine
|
||||
{
|
||||
public List<string> Texts { get; set; } = [];
|
||||
|
||||
public int LanguageIndex { get; set; } = 0;
|
||||
|
||||
public string Text => Texts[LanguageIndex];
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
public List<CharTiming> CharTimings { get; set; } = [];
|
||||
|
||||
@@ -40,8 +36,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
return new LyricsLine
|
||||
{
|
||||
Texts = new List<string>(this.Texts),
|
||||
LanguageIndex = this.LanguageIndex,
|
||||
Text = this.Text,
|
||||
CharTimings = this.CharTimings,
|
||||
StartMs = this.StartMs,
|
||||
EndMs = this.EndMs,
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
)
|
||||
)
|
||||
{
|
||||
if (file.Contains(title) && file.Contains(artist))
|
||||
if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
{
|
||||
Track track = new(file);
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
@@ -152,6 +152,52 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
private static int LevenshteinDistance(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a))
|
||||
return b.Length;
|
||||
if (string.IsNullOrEmpty(b))
|
||||
return a.Length;
|
||||
int[,] d = new int[a.Length + 1, b.Length + 1];
|
||||
for (int i = 0; i <= a.Length; i++)
|
||||
d[i, 0] = i;
|
||||
for (int j = 0; j <= b.Length; j++)
|
||||
d[0, j] = j;
|
||||
for (int i = 1; i <= a.Length; i++)
|
||||
for (int j = 1; j <= b.Length; j++)
|
||||
d[i, j] = Math.Min(
|
||||
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
|
||||
d[i - 1, j - 1] + (a[i - 1] == b[j - 1] ? 0 : 1)
|
||||
);
|
||||
return d[a.Length, b.Length];
|
||||
}
|
||||
|
||||
// 判断相似度
|
||||
private static bool FuzzyMatch(string fileName, string title, string artist)
|
||||
{
|
||||
var normFile = Normalize(fileName);
|
||||
var normTarget1 = Normalize(title + artist);
|
||||
var normTarget2 = Normalize(artist + title);
|
||||
|
||||
int dist1 = LevenshteinDistance(normFile, normTarget1);
|
||||
int dist2 = LevenshteinDistance(normFile, normTarget2);
|
||||
|
||||
return dist1 <= 3 || dist2 <= 3; // 阈值可调整
|
||||
}
|
||||
|
||||
private static string Normalize(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
return "";
|
||||
var sb = new StringBuilder();
|
||||
foreach (var c in s.ToLowerInvariant())
|
||||
{
|
||||
if (char.IsLetterOrDigit(c))
|
||||
sb.Append(c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string? LocalLyricsSearchInMusicFiles(string title, string artist)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
@@ -166,13 +212,15 @@ namespace BetterLyrics.WinUI3.Services
|
||||
)
|
||||
)
|
||||
{
|
||||
if (file.Contains(title) && file.Contains(artist))
|
||||
if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
{
|
||||
//Track track = new(file);
|
||||
//var plain = track.Lyrics.UnsynchronizedLyrics;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: replace TagLib with ATL or another library that supports AOT
|
||||
string plain = TagLib.File.Create(file).Tag.Lyrics;
|
||||
if (plain != string.Empty)
|
||||
var plain = TagLib.File.Create(file).Tag.Lyrics;
|
||||
if (plain != null && plain != string.Empty)
|
||||
{
|
||||
return plain;
|
||||
}
|
||||
@@ -204,7 +252,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
)
|
||||
)
|
||||
{
|
||||
if (file.Contains(title) && file.Contains(artist))
|
||||
if (FuzzyMatch(Path.GetFileNameWithoutExtension(file), title, artist))
|
||||
{
|
||||
string? raw = await File.ReadAllTextAsync(
|
||||
file,
|
||||
|
||||
@@ -10,6 +10,7 @@ using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Media.Control;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
@@ -164,6 +165,15 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SourceAppUserModelId = _currentSession?.SourceAppUserModelId,
|
||||
};
|
||||
|
||||
if (
|
||||
SongInfo.SourceAppUserModelId?.Contains(Package.Current.Id.FamilyName)
|
||||
?? false
|
||||
)
|
||||
{
|
||||
SongInfo.Title = "甜度爆表";
|
||||
SongInfo.Artist = "AI";
|
||||
}
|
||||
|
||||
if (mediaProps?.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
SongInfo.AlbumArt = await ImageHelper.ToByteArrayAsync(streamReference);
|
||||
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>Local .TTML files</value>
|
||||
</data>
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>This folder contains added folders, please delete these folders to add the folder</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>ローカル.TTMLファイル</value>
|
||||
</data>
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>このフォルダーには追加されたフォルダーが含まれています。これらのフォルダを削除してフォルダーを追加してください</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>로컬 .TTML 파일</value>
|
||||
</data>
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>이 폴더에는 추가 된 폴더가 포함되어 있습니다. 폴더를 추가하려면이 폴더를 삭제하십시오.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>本地 .TTML 文件</value>
|
||||
</data>
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>该文件夹包含已添加文件夹,请删除这些文件夹以添加该文件夹</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>本地 .TTML 文件</value>
|
||||
</data>
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>該文件夾包含已添加文件夾,請刪除這些文件夾以添加該文件夾</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -63,7 +63,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; }
|
||||
|
||||
private List<LyricsLine> _lyrics = [];
|
||||
private List<List<LyricsLine>> _multiLangLyrics = [];
|
||||
private int _langIndex = 0;
|
||||
|
||||
private List<LyricsLine>? _lyricsForGlowEffect = [];
|
||||
|
||||
@@ -215,7 +216,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
/// <returns></returns>
|
||||
private async Task RefreshLyricsAsync()
|
||||
{
|
||||
_lyrics = [];
|
||||
_multiLangLyrics = [];
|
||||
_isRelayoutNeeded = true;
|
||||
LyricsStatus = LyricsStatus.Loading;
|
||||
string? lyricsRaw = null;
|
||||
@@ -237,7 +238,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else if (SongInfo != null)
|
||||
{
|
||||
_lyrics = new LyricsParser().Parse(
|
||||
_multiLangLyrics = new LyricsParser().Parse(
|
||||
lyricsRaw,
|
||||
lyricsFormat,
|
||||
SongInfo.Title,
|
||||
@@ -373,9 +374,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private int GetCurrentPlayingLineIndex()
|
||||
{
|
||||
for (int i = 0; i < _lyrics?.Count; i++)
|
||||
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
|
||||
{
|
||||
var line = _lyrics?[i];
|
||||
var line = _multiLangLyrics.SafeGet(_langIndex)?[i];
|
||||
if (line?.EndMs < TotalTime.TotalMilliseconds)
|
||||
{
|
||||
continue;
|
||||
@@ -394,12 +395,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private Tuple<int, int> GetMaxLyricsLineIndexBoundaries()
|
||||
{
|
||||
if (SongInfo == null || _lyrics == null || _lyrics.Count == 0)
|
||||
if (
|
||||
SongInfo == null
|
||||
|| _multiLangLyrics.SafeGet(_langIndex) == null
|
||||
|| _multiLangLyrics[_langIndex].Count == 0
|
||||
)
|
||||
{
|
||||
return new Tuple<int, int>(-1, -1);
|
||||
}
|
||||
|
||||
return new Tuple<int, int>(0, _lyrics.Count - 1);
|
||||
return new Tuple<int, int>(0, _multiLangLyrics[_langIndex].Count - 1);
|
||||
}
|
||||
|
||||
private void DrawLyrics(
|
||||
@@ -694,7 +699,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
DrawLyrics(
|
||||
control,
|
||||
lyricsDs,
|
||||
_lyrics,
|
||||
_multiLangLyrics.SafeGet(_langIndex),
|
||||
_defaultOpacity,
|
||||
LyricsHighlightType.LineByLine
|
||||
);
|
||||
@@ -870,9 +875,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float y = _topMargin;
|
||||
|
||||
// Init Positions
|
||||
for (int i = 0; i < _lyrics?.Count; i++)
|
||||
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
|
||||
{
|
||||
var line = _lyrics.SafeGet(i);
|
||||
var line = _multiLangLyrics[_langIndex].SafeGet(i);
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
@@ -933,13 +938,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isRelayoutNeeded = false;
|
||||
}
|
||||
|
||||
UpdateLinesProps(_lyrics, _defaultOpacity);
|
||||
UpdateLinesProps(_multiLangLyrics.SafeGet(_langIndex), _defaultOpacity);
|
||||
UpdateCanvasYScrollOffset(control);
|
||||
|
||||
if (IsLyricsGlowEffectEnabled)
|
||||
{
|
||||
// Deep copy lyrics lines for glow effect
|
||||
_lyricsForGlowEffect = _lyrics?.Select(line => line.Clone()).ToList();
|
||||
_lyricsForGlowEffect = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.Select(line => line.Clone())
|
||||
.ToList();
|
||||
switch (LyricsGlowEffectScope)
|
||||
{
|
||||
case LyricsGlowEffectScope.WholeLyrics:
|
||||
@@ -1124,7 +1132,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
// Set _scrollOffsetY
|
||||
LyricsLine? currentPlayingLine = _lyrics?[currentPlayingLineIndex];
|
||||
LyricsLine? currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?[currentPlayingLineIndex];
|
||||
|
||||
if (currentPlayingLine == null)
|
||||
{
|
||||
@@ -1142,7 +1152,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float targetYScrollOffset =
|
||||
(float?)(
|
||||
-currentPlayingLine.Position.Y
|
||||
+ _lyrics?[0].Position.Y
|
||||
+ _multiLangLyrics.SafeGet(_langIndex)?[0].Position.Y
|
||||
- playingTextLayout.LayoutBounds.Height / 2
|
||||
) ?? 0f;
|
||||
|
||||
@@ -1159,9 +1169,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_startVisibleLineIndex = _endVisibleLineIndex = -1;
|
||||
|
||||
// Update visible line indices
|
||||
for (int i = startLineIndex; i >= 0 && i <= endLineIndex && i < _lyrics?.Count; i++)
|
||||
for (int i = startLineIndex; i <= endLineIndex; i++)
|
||||
{
|
||||
var line = _lyrics?[i];
|
||||
var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using var textLayout = new CanvasTextLayout(
|
||||
control,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
@@ -13,8 +14,10 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Globalization;
|
||||
using Windows.Media;
|
||||
using Windows.Media.Playback;
|
||||
using Windows.System;
|
||||
using WinRT.Interop;
|
||||
@@ -68,11 +71,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial Enums.Language Language { get; set; }
|
||||
|
||||
private readonly MediaPlayer _mediaPlayer = new();
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
|
||||
public string Version { get; set; } = AppInfo.AppVersion;
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -81,6 +79,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial Thickness RootGridMargin { get; set; } = new(0, 0, 0, 0);
|
||||
|
||||
private readonly MediaPlayer _mediaPlayer = new();
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly IPlaybackService _playbackService;
|
||||
|
||||
public SettingsViewModel(
|
||||
ISettingsService settingsService,
|
||||
ILibWatcherService libWatcherService,
|
||||
@@ -89,6 +92,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
|
||||
RootGridMargin = new Thickness(0, _settingsService.TitleBarType.GetHeight(), 0, 0);
|
||||
|
||||
@@ -221,7 +225,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void AddFolderAsync(string path)
|
||||
{
|
||||
if (LocalLyricsFolders.Any(x => x.Path == path))
|
||||
var normalizedPath =
|
||||
Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar)
|
||||
+ Path.DirectorySeparatorChar;
|
||||
|
||||
if (
|
||||
LocalLyricsFolders.Any(x =>
|
||||
Path.GetFullPath(x.Path)
|
||||
.TrimEnd(Path.DirectorySeparatorChar)
|
||||
.Equals(
|
||||
normalizedPath.TrimEnd(Path.DirectorySeparatorChar),
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
@@ -231,8 +248,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (LocalLyricsFolders.Any((item) => path.StartsWith(item.Path)))
|
||||
else if (
|
||||
LocalLyricsFolders.Any(item =>
|
||||
normalizedPath.StartsWith(
|
||||
Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar)
|
||||
+ Path.DirectorySeparatorChar,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的子文件夹
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
new Notification(
|
||||
@@ -241,12 +267,19 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (LocalLyricsFolders.Any((item) => item.Path.StartsWith(path)))
|
||||
else if (
|
||||
LocalLyricsFolders.Any(item =>
|
||||
Path.GetFullPath(item.Path)
|
||||
.TrimEnd(Path.DirectorySeparatorChar)
|
||||
.StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的父文件夹
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
new Notification(
|
||||
App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo")
|
||||
App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
46
README.md
46
README.md
@@ -39,15 +39,43 @@ Your smooth dynamic local lyrics display built with WinUI 3
|
||||
|
||||
We provide more than one setting item to better align with your preference
|
||||
|
||||
- Theme (light, dark, follow system)
|
||||
- Theme
|
||||
- Light
|
||||
- Dark
|
||||
- Follow system
|
||||
|
||||
- Backdrop (none, mica, acrylic, transparent)
|
||||
- Backdrop
|
||||
- None
|
||||
- Mica
|
||||
- Acrylic
|
||||
- Transparent
|
||||
|
||||
- Album art as background (dynamic, blur amount, opacity)
|
||||
- Album art as background
|
||||
- Dynamic
|
||||
- Blur amount
|
||||
- Opacity
|
||||
|
||||
- Lyrics (alignment, font size, font color **(picked from album art accent color)** line spacing, opacity, blur amount, dynamic **glow** effect)
|
||||
- Album art as cover
|
||||
- Corner radius
|
||||
|
||||
- Language (English, Simplified Chinese, Traditional Chinese, Japanese, Korean)
|
||||
- Lyrics
|
||||
- Alignment
|
||||
- Font size
|
||||
- Font color **(from album art accent color)**
|
||||
- Line spacing
|
||||
- Opacity
|
||||
- Blur amount
|
||||
- Dynamic **glow** effect
|
||||
- Whole lyrics
|
||||
- Line by line
|
||||
- Word by word
|
||||
|
||||
- Language
|
||||
- English
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Japanese
|
||||
- Korean
|
||||
|
||||
|
||||
## Screenshots
|
||||
@@ -72,7 +100,7 @@ We provide more than one setting item to better align with your preference
|
||||
|
||||
## Demonstration
|
||||
|
||||
Watch our introduction video「BetterLyrics 阶段性开发成果展示」(uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
|
||||
Watch our introduction video (uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
|
||||
|
||||
## Try it now
|
||||
|
||||
@@ -109,11 +137,11 @@ To be added later.
|
||||
- [LRCLIB](https://lrclib.net/)
|
||||
- Online lyrics API provider
|
||||
- [Audio Tools Library (ATL) for .NET](https://github.com/Zeugma440/atldotnet)
|
||||
- Local music file metadata extractor
|
||||
- Used for extracting pictures in music files
|
||||
- [WinUIEx](https://github.com/dotMorten/WinUIEx)
|
||||
- Provide easy ways to access Win32 API regarding windowing
|
||||
- [TagLib#](https://github.com/mono/taglib-sharp)
|
||||
- Previously used it as metadata extractor
|
||||
- Used for reading original lyrics content
|
||||
- [Stackoverflow - How to animate Margin property in WPF](https://stackoverflow.com/a/21542882/11048731)
|
||||
- [DevWinUI](https://github.com/ghost1372/DevWinUI)
|
||||
- [Bilibili -【WinUI3】SystemBackdropController:定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
|
||||
@@ -149,7 +177,7 @@ To be added later.
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="WinUIEx" Version="2.5.1" />
|
||||
<PackageReference Include="z440.atl.core" Version="6.26.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="6.26.0" />S
|
||||
```
|
||||
|
||||
## Star History
|
||||
|
||||
Reference in New Issue
Block a user