mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-13 03:34:55 +08:00
add support for ttml format
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\AI - 甜度爆表.mp3">
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
LyricsSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString(
|
||||
"LyricsSearchProviderEslrcFile"
|
||||
),
|
||||
LyricsSearchProvider.LocalTtmlFile => App.ResourceLoader!.GetString(
|
||||
"LyricsSearchProviderTtmlFile"
|
||||
),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(provider), provider, null),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
LocalMusicFile,
|
||||
LocalLrcFile,
|
||||
LocalEslrcFile,
|
||||
LocalTtmlFile,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
|
||||
@@ -142,29 +141,63 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
private void ParseTtml(string raw, int durationMs)
|
||||
{
|
||||
// 简单 TTML 解析
|
||||
try
|
||||
{
|
||||
var xdoc = XDocument.Parse(raw);
|
||||
XNamespace ns = xdoc.Root?.Name.Namespace ?? "";
|
||||
var body = xdoc.Descendants(ns + "body").FirstOrDefault();
|
||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||
if (body == null)
|
||||
return;
|
||||
var ps = body.Descendants(ns + "p");
|
||||
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
||||
foreach (var p in ps)
|
||||
{
|
||||
string text = p.Value.Trim();
|
||||
string? begin = p.Attribute("begin")?.Value;
|
||||
string? end = p.Attribute("end")?.Value;
|
||||
int startMs = ParseTtmlTime(begin);
|
||||
int endMs = ParseTtmlTime(end);
|
||||
// 句级时间
|
||||
string? pBegin = p.Attribute("begin")?.Value;
|
||||
string? pEnd = p.Attribute("end")?.Value;
|
||||
int pStartMs = ParseTtmlTime(pBegin);
|
||||
int pEndMs = ParseTtmlTime(pEnd);
|
||||
|
||||
// 处理分词分时
|
||||
var spans = p.Elements()
|
||||
.Where(s =>
|
||||
s.Name.LocalName == "span"
|
||||
&& s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))
|
||||
== null
|
||||
)
|
||||
.ToList();
|
||||
|
||||
string text = string.Concat(spans.Select(s => s.Value));
|
||||
var charTimings = new List<CharTiming>();
|
||||
|
||||
for (int i = 0; i < spans.Count; i++)
|
||||
{
|
||||
var span = spans[i];
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
|
||||
if (sStartMs == 0 && sEndMs == 0)
|
||||
continue;
|
||||
|
||||
if (sEndMs == 0)
|
||||
sEndMs =
|
||||
(i + 1 < spans.Count)
|
||||
? ParseTtmlTime(spans[i + 1].Attribute("begin")?.Value)
|
||||
: pEndMs;
|
||||
|
||||
charTimings.Add(new CharTiming { StartMs = sStartMs, EndMs = sEndMs });
|
||||
}
|
||||
|
||||
if (spans.Count == 0)
|
||||
text = p.Value.Trim();
|
||||
|
||||
_lyricsLines.Add(
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = startMs,
|
||||
EndMs = endMs,
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
Texts = [text],
|
||||
CharTimings = [],
|
||||
CharTimings = charTimings,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -177,12 +210,22 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
private int ParseTtmlTime(string? t)
|
||||
{
|
||||
if (string.IsNullOrEmpty(t))
|
||||
if (string.IsNullOrWhiteSpace(t))
|
||||
return 0;
|
||||
// 支持 "00:00:01.000" 或 "1.000s"
|
||||
|
||||
t = t.Trim();
|
||||
|
||||
// 支持 "1.000s"
|
||||
if (t.EndsWith("s"))
|
||||
{
|
||||
if (double.TryParse(t.TrimEnd('s'), out double seconds))
|
||||
if (
|
||||
double.TryParse(
|
||||
t.TrimEnd('s'),
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double seconds
|
||||
)
|
||||
)
|
||||
return (int)(seconds * 1000);
|
||||
}
|
||||
else
|
||||
@@ -190,11 +233,38 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
var parts = t.Split(':');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
// hh:mm:ss.xxx
|
||||
int h = int.Parse(parts[0]);
|
||||
int m = int.Parse(parts[1]);
|
||||
double s = double.Parse(parts[2]);
|
||||
double s = double.Parse(
|
||||
parts[2],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((h * 3600 + m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
// mm:ss.xxx
|
||||
int m = int.Parse(parts[0]);
|
||||
double s = double.Parse(
|
||||
parts[1],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 1)
|
||||
{
|
||||
// ss.xxx
|
||||
if (
|
||||
double.TryParse(
|
||||
parts[0],
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double s
|
||||
)
|
||||
)
|
||||
return (int)(s * 1000);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -44,14 +44,10 @@ namespace BetterLyrics.WinUI3.Services
|
||||
if (file.Contains(title) && file.Contains(artist))
|
||||
{
|
||||
Track track = new(file);
|
||||
if (track.Lyrics.SynchronizedLyrics.Count > 0)
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
if (bytes != null)
|
||||
{
|
||||
// Get synchronized lyrics from the track (metadata)
|
||||
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
|
||||
if (bytes != null)
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +107,13 @@ namespace BetterLyrics.WinUI3.Services
|
||||
LyricsFormat.Eslrc
|
||||
);
|
||||
break;
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
searchedLyrics = await LocalLyricsSearchInLyricsFiles(
|
||||
title,
|
||||
artist,
|
||||
LyricsFormat.Ttml
|
||||
);
|
||||
break;
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
searchedLyrics = await SearchLrcLib(
|
||||
title,
|
||||
@@ -137,6 +140,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return (searchedLyrics, LyricsFormat.Lrc);
|
||||
case LyricsSearchProvider.LocalEslrcFile:
|
||||
return (searchedLyrics, LyricsFormat.Eslrc);
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
return (searchedLyrics, LyricsFormat.Ttml);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -480,4 +480,7 @@
|
||||
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
|
||||
<value>Local .ESLRC files</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>Local .TTML files</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -480,4 +480,7 @@
|
||||
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
|
||||
<value>ローカル.ESLRCファイル</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>ローカル.TTMLファイル</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -480,4 +480,7 @@
|
||||
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
|
||||
<value>로컬 .ESLRC 파일</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>로컬 .TTML 파일</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -480,4 +480,7 @@
|
||||
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
|
||||
<value>本地 .ESLRC 文件</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>本地 .TTML 文件</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -480,4 +480,7 @@
|
||||
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
|
||||
<value>本地 .ESLRC 文件</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
|
||||
<value>本地 .TTML 文件</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user