@uimaxbai/am-lyrics 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/AmLyrics.d.ts +2 -0
- package/dist/src/AmLyrics.d.ts.map +1 -1
- package/dist/src/am-lyrics.js +319 -44
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +319 -44
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +363 -48
- package/test.cjs +13 -0
- package/test.js +13 -0
- package/ttml_test.js +149 -0
package/dist/src/AmLyrics.d.ts
CHANGED
|
@@ -85,6 +85,8 @@ export declare class AmLyrics extends LitElement {
|
|
|
85
85
|
*/
|
|
86
86
|
private static fetchLyricsFromLrclib;
|
|
87
87
|
private static fetchLyricsFromGenius;
|
|
88
|
+
private static calculateLineAlignments;
|
|
89
|
+
private static parseTTML;
|
|
88
90
|
private static convertKPoeLyrics;
|
|
89
91
|
private static toMilliseconds;
|
|
90
92
|
firstUpdated(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AmLyrics.d.ts","sourceRoot":"","sources":["../../src/AmLyrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAO,MAAM,KAAK,CAAC;AA4FjD,qBAAa,QAAS,SAAQ,UAAU;IACtC,MAAM,CAAC,MAAM,0BA4pCX;IAGF,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,OAAO,CAAC,cAAc,CAAmC;IAGzD,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,cAAc,SAAa;IAG3B,oBAAoB,SAA+B;IAGnD,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,UAAU,UAAQ;IAGlB,WAAW,UAAQ;IAGnB,OAAO,CAAC,gBAAgB,CAAS;IAGjC,OAAO,CAAC,eAAe,CAAS;YAElB,kBAAkB;YAKlB,iBAAiB;YAsBjB,iBAAiB;YAKjB,gBAAgB;IAyC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,YAAY,CAAK;IAEzB,IACI,WAAW,CAAC,KAAK,EAAE,MAAM,EAM5B;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAGD,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,MAAM,CAAC,CAAe;IAE9B,OAAO,CAAC,iBAAiB,CAAgB;IAEzC,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,2BAA2B,CAAkC;IAErE,OAAO,CAAC,gBAAgB,CAAkC;IAE1D,OAAO,CAAC,sBAAsB,CAAkC;IAGhE,OAAO,CAAC,YAAY,CAAuB;IAG3C,OAAO,CAAC,gBAAgB,CAAiD;IAGzE,OAAO,CAAC,kBAAkB,CAAK;IAG/B,OAAO,CAAC,sBAAsB,CAAS;IAGvC,OAAO,CAAC,sBAAsB,CAAS;IAEvC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAElC,OAAO,CAAC,kBAAkB,CAGZ;IAEd,OAAO,CAAC,wBAAwB,CAGlB;IAGd,OAAO,CAAC,eAAe,CAAC,CAAc;IAEtC,OAAO,CAAC,qBAAqB,CAAuB;IAEpD,OAAO,CAAC,mBAAmB,CAAC,CAAS;IAGrC,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IAGzD,OAAO,CAAC,iBAAiB,CAAqB;IAG9C,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,wBAAwB,CAA4B;IAE5D,OAAO,CAAC,qBAAqB,CAA4B;IAGzD,OAAO,CAAC,oBAAoB,CAGZ;IAEhB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,OAAO,CAAC,cAAc,CAAqB;IAE3C,OAAO,CAAC,mBAAmB,CAAC,CAAgC;IAE5D,OAAO,CAAC,sBAAsB,CAAC,CAAgC;IAG/D,OAAO,CAAC,eAAe,CAAK;IAE5B,OAAO,CAAC,cAAc,CAA0B;IAEhD,iBAAiB;IAKjB,oBAAoB;YAUN,WAAW;YAkGX,cAAc;YAoBd,iBAAiB;IAS/B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAsClC,OAAO,CAAC,MAAM,CAAC,mBAAmB;YA8BpB,YAAY;YA+EZ,mBAAmB;IAiGjC,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBAkCZ,uBAAuB;mBA6CvB,wBAAwB;
|
|
1
|
+
{"version":3,"file":"AmLyrics.d.ts","sourceRoot":"","sources":["../../src/AmLyrics.ts"],"names":[],"mappings":"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"}
|
package/dist/src/am-lyrics.js
CHANGED
|
@@ -309,7 +309,7 @@ class GoogleService {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
const VERSION = '1.1.
|
|
312
|
+
const VERSION = '1.1.4';
|
|
313
313
|
const INSTRUMENTAL_THRESHOLD_MS = 7000; // Show dots for gaps >= 7s
|
|
314
314
|
const KPOE_SERVERS = [
|
|
315
315
|
'https://lyricsplus.binimum.org',
|
|
@@ -851,6 +851,80 @@ class AmLyrics extends i {
|
|
|
851
851
|
return 10;
|
|
852
852
|
};
|
|
853
853
|
const allResults = [];
|
|
854
|
+
// Try cache API first
|
|
855
|
+
try {
|
|
856
|
+
const cacheParams = new URLSearchParams({
|
|
857
|
+
track: title,
|
|
858
|
+
artist,
|
|
859
|
+
});
|
|
860
|
+
if (metadata.album) {
|
|
861
|
+
cacheParams.append('album', metadata.album);
|
|
862
|
+
}
|
|
863
|
+
if (metadata.durationMs && metadata.durationMs > 0) {
|
|
864
|
+
cacheParams.append('duration', Math.round(metadata.durationMs / 1000).toString());
|
|
865
|
+
}
|
|
866
|
+
const cacheUrl = `https://lyrics-api.binimum.org/?${cacheParams.toString()}`;
|
|
867
|
+
const cacheRes = await fetch(cacheUrl);
|
|
868
|
+
if (cacheRes.ok) {
|
|
869
|
+
const cacheData = await cacheRes.json();
|
|
870
|
+
if (cacheData.results && cacheData.results.length > 0) {
|
|
871
|
+
const result = cacheData.results[0];
|
|
872
|
+
if (result.timing_type === 'word' && result.lyricsUrl) {
|
|
873
|
+
const ttmlRes = await fetch(result.lyricsUrl);
|
|
874
|
+
if (ttmlRes.ok) {
|
|
875
|
+
const ttmlText = await ttmlRes.text();
|
|
876
|
+
const lines = AmLyrics.parseTTML(ttmlText);
|
|
877
|
+
if (lines && lines.length > 0) {
|
|
878
|
+
allResults.push({ lines, source: 'BiniLyrics' });
|
|
879
|
+
return allResults;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
// Not word type, try QQ
|
|
885
|
+
const qqParams = new URLSearchParams(params);
|
|
886
|
+
qqParams.set('source', 'qq');
|
|
887
|
+
const qqUrl = `https://lyricsplus.binimum.org/v2/lyrics/get?${qqParams.toString()}`;
|
|
888
|
+
try {
|
|
889
|
+
const qqRes = await fetch(qqUrl);
|
|
890
|
+
if (qqRes.ok) {
|
|
891
|
+
const payload = await qqRes.json();
|
|
892
|
+
const lines = AmLyrics.convertKPoeLyrics(payload);
|
|
893
|
+
const hasWordSync = lines?.some((line) => line.text &&
|
|
894
|
+
Array.isArray(line.text) &&
|
|
895
|
+
line.text.length > 1);
|
|
896
|
+
if (lines && lines.length > 0 && hasWordSync) {
|
|
897
|
+
allResults.push({ lines, source: 'QQ (Cache Fallback)' });
|
|
898
|
+
return allResults;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
catch (qqError) {
|
|
903
|
+
// Ignore QQ fetch error
|
|
904
|
+
}
|
|
905
|
+
// If QQ fails or has no word sync, fall back to bini lyrics
|
|
906
|
+
if (result.lyricsUrl) {
|
|
907
|
+
const ttmlRes = await fetch(result.lyricsUrl);
|
|
908
|
+
if (ttmlRes.ok) {
|
|
909
|
+
const ttmlText = await ttmlRes.text();
|
|
910
|
+
const lines = AmLyrics.parseTTML(ttmlText);
|
|
911
|
+
if (lines && lines.length > 0) {
|
|
912
|
+
allResults.push({
|
|
913
|
+
lines,
|
|
914
|
+
source: 'Apple Music (Cache Fallback)',
|
|
915
|
+
});
|
|
916
|
+
return allResults;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
catch (e) {
|
|
925
|
+
// eslint-disable-next-line no-console
|
|
926
|
+
console.error('Cache API failed', e);
|
|
927
|
+
}
|
|
854
928
|
// Shuffle servers so we pick a random one first, with all others as fallback
|
|
855
929
|
// Limit to 2 servers to prevent unnecessary API spam when Apple lyrics are missing
|
|
856
930
|
const shuffledServers = [...KPOE_SERVERS]
|
|
@@ -930,6 +1004,7 @@ class AmLyrics extends i {
|
|
|
930
1004
|
if (!match) {
|
|
931
1005
|
// Skip non-timestamped lines (headers like [ti:], [ar:], etc.)
|
|
932
1006
|
// eslint-disable-next-line no-continue
|
|
1007
|
+
// eslint-disable-next-line no-continue
|
|
933
1008
|
continue;
|
|
934
1009
|
}
|
|
935
1010
|
const minutes = parseInt(match[1], 10);
|
|
@@ -949,6 +1024,7 @@ class AmLyrics extends i {
|
|
|
949
1024
|
const endtime = i + 1 < parsed.length ? parsed[i + 1].timestamp : timestamp + 5000;
|
|
950
1025
|
// Skip empty lines (instrumental gaps)
|
|
951
1026
|
if (!text.trim()) {
|
|
1027
|
+
// eslint-disable-next-line no-continue
|
|
952
1028
|
// eslint-disable-next-line no-continue
|
|
953
1029
|
continue;
|
|
954
1030
|
}
|
|
@@ -992,6 +1068,7 @@ class AmLyrics extends i {
|
|
|
992
1068
|
// eslint-disable-next-line no-await-in-loop
|
|
993
1069
|
const searchResponse = await fetch(`${normalizedBase}/search/?${searchParams.toString()}`);
|
|
994
1070
|
if (!searchResponse.ok) {
|
|
1071
|
+
// eslint-disable-next-line no-continue
|
|
995
1072
|
// eslint-disable-next-line no-continue
|
|
996
1073
|
continue;
|
|
997
1074
|
}
|
|
@@ -999,6 +1076,7 @@ class AmLyrics extends i {
|
|
|
999
1076
|
const searchData = await searchResponse.json();
|
|
1000
1077
|
const items = searchData?.data?.items;
|
|
1001
1078
|
if (!Array.isArray(items) || items.length === 0) {
|
|
1079
|
+
// eslint-disable-next-line no-continue
|
|
1002
1080
|
// eslint-disable-next-line no-continue
|
|
1003
1081
|
continue;
|
|
1004
1082
|
}
|
|
@@ -1012,6 +1090,7 @@ class AmLyrics extends i {
|
|
|
1012
1090
|
}
|
|
1013
1091
|
const trackId = bestTrack?.id;
|
|
1014
1092
|
if (!trackId) {
|
|
1093
|
+
// eslint-disable-next-line no-continue
|
|
1015
1094
|
// eslint-disable-next-line no-continue
|
|
1016
1095
|
continue;
|
|
1017
1096
|
}
|
|
@@ -1019,6 +1098,7 @@ class AmLyrics extends i {
|
|
|
1019
1098
|
// eslint-disable-next-line no-await-in-loop
|
|
1020
1099
|
const lyricsResponse = await fetch(`${normalizedBase}/lyrics/?id=${trackId}`);
|
|
1021
1100
|
if (!lyricsResponse.ok) {
|
|
1101
|
+
// eslint-disable-next-line no-continue
|
|
1022
1102
|
// eslint-disable-next-line no-continue
|
|
1023
1103
|
continue;
|
|
1024
1104
|
}
|
|
@@ -1143,10 +1223,230 @@ class AmLyrics extends i {
|
|
|
1143
1223
|
}
|
|
1144
1224
|
}
|
|
1145
1225
|
catch {
|
|
1226
|
+
// eslint-disable-next-line no-console
|
|
1146
1227
|
console.error('No Genius lyrics found');
|
|
1147
1228
|
}
|
|
1148
1229
|
return null;
|
|
1149
1230
|
}
|
|
1231
|
+
static calculateLineAlignments(lineSingers, agentTypes) {
|
|
1232
|
+
const lineSideAssignments = new Array(lineSingers.length).fill(undefined);
|
|
1233
|
+
let currentSideIsLeft = true;
|
|
1234
|
+
let lastPersonSingerId = null;
|
|
1235
|
+
let rightCount = 0;
|
|
1236
|
+
let totalCount = 0;
|
|
1237
|
+
lineSingers.forEach((singerId, index) => {
|
|
1238
|
+
let sideClass;
|
|
1239
|
+
if (singerId) {
|
|
1240
|
+
let type = agentTypes[singerId];
|
|
1241
|
+
if (!type) {
|
|
1242
|
+
if (singerId === 'v1000') {
|
|
1243
|
+
type = 'group';
|
|
1244
|
+
}
|
|
1245
|
+
else if (singerId === 'v2000') {
|
|
1246
|
+
type = 'other';
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
type = 'person';
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
if (type === 'group') {
|
|
1253
|
+
sideClass = 'start';
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
if (lastPersonSingerId === null) {
|
|
1257
|
+
if (type === 'other') {
|
|
1258
|
+
currentSideIsLeft = false;
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
currentSideIsLeft = true;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
else if (singerId !== lastPersonSingerId) {
|
|
1265
|
+
currentSideIsLeft = !currentSideIsLeft;
|
|
1266
|
+
}
|
|
1267
|
+
sideClass = currentSideIsLeft ? 'start' : 'end';
|
|
1268
|
+
lastPersonSingerId = singerId;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (sideClass) {
|
|
1272
|
+
totalCount += 1;
|
|
1273
|
+
if (sideClass === 'end')
|
|
1274
|
+
rightCount += 1;
|
|
1275
|
+
}
|
|
1276
|
+
lineSideAssignments[index] = sideClass;
|
|
1277
|
+
});
|
|
1278
|
+
if (totalCount > 0 && Math.round((rightCount / totalCount) * 100) >= 85) {
|
|
1279
|
+
const flip = (s) => {
|
|
1280
|
+
if (s === 'start')
|
|
1281
|
+
return 'end';
|
|
1282
|
+
if (s === 'end')
|
|
1283
|
+
return 'start';
|
|
1284
|
+
return s;
|
|
1285
|
+
};
|
|
1286
|
+
for (let i = 0; i < lineSideAssignments.length; i += 1) {
|
|
1287
|
+
lineSideAssignments[i] = flip(lineSideAssignments[i]);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return lineSideAssignments;
|
|
1291
|
+
}
|
|
1292
|
+
static parseTTML(ttmlString) {
|
|
1293
|
+
try {
|
|
1294
|
+
const parser = new DOMParser();
|
|
1295
|
+
const doc = parser.parseFromString(ttmlString, 'text/xml');
|
|
1296
|
+
const translations = {};
|
|
1297
|
+
const transliterations = {};
|
|
1298
|
+
const agentMap = {};
|
|
1299
|
+
const agents = doc.getElementsByTagName('ttm:agent');
|
|
1300
|
+
for (let i = 0; i < agents.length; i += 1) {
|
|
1301
|
+
const agent = agents[i];
|
|
1302
|
+
const id = agent.getAttribute('xml:id');
|
|
1303
|
+
const type = agent.getAttribute('type');
|
|
1304
|
+
if (id && type) {
|
|
1305
|
+
agentMap[id] = type;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
const translationNodes = doc.getElementsByTagName('translation');
|
|
1309
|
+
for (let i = 0; i < translationNodes.length; i += 1) {
|
|
1310
|
+
const texts = translationNodes[i].getElementsByTagName('text');
|
|
1311
|
+
for (let j = 0; j < texts.length; j += 1) {
|
|
1312
|
+
const textNode = texts[j];
|
|
1313
|
+
const key = textNode.getAttribute('for');
|
|
1314
|
+
if (key && textNode.textContent) {
|
|
1315
|
+
translations[key] = textNode.textContent;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
const transliterationNodes = doc.getElementsByTagName('transliteration');
|
|
1320
|
+
for (let i = 0; i < transliterationNodes.length; i += 1) {
|
|
1321
|
+
const texts = transliterationNodes[i].getElementsByTagName('text');
|
|
1322
|
+
for (let j = 0; j < texts.length; j += 1) {
|
|
1323
|
+
const textNode = texts[j];
|
|
1324
|
+
const key = textNode.getAttribute('for');
|
|
1325
|
+
if (key && textNode.textContent) {
|
|
1326
|
+
transliterations[key] = textNode.textContent
|
|
1327
|
+
.trim()
|
|
1328
|
+
.replace(/\s+/g, ' ');
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const timeToMs = (timeStr) => {
|
|
1333
|
+
if (!timeStr)
|
|
1334
|
+
return 0;
|
|
1335
|
+
const parts = timeStr.split(':');
|
|
1336
|
+
let seconds = 0;
|
|
1337
|
+
if (parts.length === 2) {
|
|
1338
|
+
seconds = parseInt(parts[0], 10) * 60 + parseFloat(parts[1]);
|
|
1339
|
+
}
|
|
1340
|
+
else if (parts.length === 3) {
|
|
1341
|
+
seconds =
|
|
1342
|
+
parseInt(parts[0], 10) * 3600 +
|
|
1343
|
+
parseInt(parts[1], 10) * 60 +
|
|
1344
|
+
parseFloat(parts[2]);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
seconds = parseFloat(parts[0]);
|
|
1348
|
+
}
|
|
1349
|
+
return Math.round(seconds * 1000);
|
|
1350
|
+
};
|
|
1351
|
+
const lines = [];
|
|
1352
|
+
const pNodes = doc.getElementsByTagName('p');
|
|
1353
|
+
const lineSingers = [];
|
|
1354
|
+
for (let i = 0; i < pNodes.length; i += 1) {
|
|
1355
|
+
lineSingers.push(pNodes[i].getAttribute('ttm:agent') || undefined);
|
|
1356
|
+
}
|
|
1357
|
+
const alignments = AmLyrics.calculateLineAlignments(lineSingers, agentMap);
|
|
1358
|
+
for (let i = 0; i < pNodes.length; i += 1) {
|
|
1359
|
+
const p = pNodes[i];
|
|
1360
|
+
const key = p.getAttribute('itunes:key');
|
|
1361
|
+
const beginMs = timeToMs(p.getAttribute('begin'));
|
|
1362
|
+
const endMs = timeToMs(p.getAttribute('end'));
|
|
1363
|
+
let songPart;
|
|
1364
|
+
if (p.parentNode && p.parentNode.tagName === 'div') {
|
|
1365
|
+
songPart =
|
|
1366
|
+
p.parentNode.getAttribute('itunes:songPart') ||
|
|
1367
|
+
undefined;
|
|
1368
|
+
}
|
|
1369
|
+
const mainSyllables = [];
|
|
1370
|
+
const bgSyllables = [];
|
|
1371
|
+
const spans = p.getElementsByTagName('span');
|
|
1372
|
+
if (spans.length > 0) {
|
|
1373
|
+
for (let j = 0; j < spans.length; j += 1) {
|
|
1374
|
+
const span = spans[j];
|
|
1375
|
+
if (span.getAttribute('ttm:role') === 'x-bg') {
|
|
1376
|
+
const bgInnerSpans = span.getElementsByTagName('span');
|
|
1377
|
+
for (let k = 0; k < bgInnerSpans.length; k += 1) {
|
|
1378
|
+
const bgSpan = bgInnerSpans[k];
|
|
1379
|
+
let bgText = bgSpan.textContent || '';
|
|
1380
|
+
const nextNode = bgSpan.nextSibling;
|
|
1381
|
+
if (nextNode &&
|
|
1382
|
+
nextNode.nodeType === 3 &&
|
|
1383
|
+
/^\s/.test(nextNode.textContent || '') &&
|
|
1384
|
+
!bgText.endsWith(' ')) {
|
|
1385
|
+
bgText += ' ';
|
|
1386
|
+
}
|
|
1387
|
+
bgSyllables.push({
|
|
1388
|
+
text: bgText,
|
|
1389
|
+
timestamp: timeToMs(bgSpan.getAttribute('begin')),
|
|
1390
|
+
endtime: timeToMs(bgSpan.getAttribute('end')),
|
|
1391
|
+
part: false,
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
// eslint-disable-next-line no-continue
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (span.parentNode &&
|
|
1398
|
+
span.parentNode.getAttribute?.('ttm:role') === 'x-bg') {
|
|
1399
|
+
// eslint-disable-next-line no-continue
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
let text = span.textContent || '';
|
|
1403
|
+
const nextNode = span.nextSibling;
|
|
1404
|
+
if (nextNode &&
|
|
1405
|
+
nextNode.nodeType === 3 &&
|
|
1406
|
+
/^\s/.test(nextNode.textContent || '') &&
|
|
1407
|
+
!text.endsWith(' ')) {
|
|
1408
|
+
text += ' ';
|
|
1409
|
+
}
|
|
1410
|
+
mainSyllables.push({
|
|
1411
|
+
text,
|
|
1412
|
+
timestamp: timeToMs(span.getAttribute('begin')),
|
|
1413
|
+
endtime: timeToMs(span.getAttribute('end')),
|
|
1414
|
+
part: false,
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
mainSyllables.push({
|
|
1420
|
+
text: p.textContent?.trim() || '',
|
|
1421
|
+
timestamp: beginMs,
|
|
1422
|
+
endtime: endMs,
|
|
1423
|
+
part: false,
|
|
1424
|
+
lineSynced: true,
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
const alignment = alignments[i];
|
|
1428
|
+
lines.push({
|
|
1429
|
+
text: mainSyllables,
|
|
1430
|
+
background: bgSyllables.length > 0,
|
|
1431
|
+
backgroundText: bgSyllables,
|
|
1432
|
+
timestamp: beginMs,
|
|
1433
|
+
endtime: endMs,
|
|
1434
|
+
isWordSynced: spans.length > 0,
|
|
1435
|
+
alignment,
|
|
1436
|
+
songPart,
|
|
1437
|
+
translation: key ? translations[key] : undefined,
|
|
1438
|
+
romanizedText: key ? transliterations[key] : undefined,
|
|
1439
|
+
oppositeTurn: alignment === 'end',
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
return lines;
|
|
1443
|
+
}
|
|
1444
|
+
catch (e) {
|
|
1445
|
+
// eslint-disable-next-line no-console
|
|
1446
|
+
console.error('Failed to parse TTML', e);
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1150
1450
|
static convertKPoeLyrics(payload) {
|
|
1151
1451
|
if (!payload) {
|
|
1152
1452
|
return null;
|
|
@@ -1168,45 +1468,21 @@ class AmLyrics extends i {
|
|
|
1168
1468
|
const lines = [];
|
|
1169
1469
|
// If type is 'Line', we revert to line-by-line highlighting by skipping syllabus parsing
|
|
1170
1470
|
const isLineType = payload.type === 'Line' || payload.type === 'line';
|
|
1171
|
-
// Convert metadata.agents to
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const personAgents = agentEntries.filter(
|
|
1178
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1179
|
-
([_, agentData]) => agentData.type === 'person');
|
|
1180
|
-
const personIndexMap = new Map();
|
|
1181
|
-
personAgents.forEach(([agentKey], personIndex) => {
|
|
1182
|
-
personIndexMap.set(agentKey, personIndex);
|
|
1183
|
-
});
|
|
1184
|
-
agentEntries.forEach(([agentKey, agentData]) => {
|
|
1185
|
-
const mappedKey = agentData.alias || agentKey;
|
|
1186
|
-
if (agentData.type === 'group') {
|
|
1187
|
-
singerAlignmentMap[mappedKey] = 'start';
|
|
1188
|
-
}
|
|
1189
|
-
else if (agentData.type === 'other') {
|
|
1190
|
-
singerAlignmentMap[mappedKey] = 'end';
|
|
1191
|
-
}
|
|
1192
|
-
else if (agentData.type === 'person') {
|
|
1193
|
-
const personIndex = personIndexMap.get(agentKey);
|
|
1194
|
-
if (personIndex !== undefined) {
|
|
1195
|
-
singerAlignmentMap[mappedKey] =
|
|
1196
|
-
personIndex % 2 === 0 ? 'start' : 'end';
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1471
|
+
// Convert metadata.agents to type map
|
|
1472
|
+
const agentTypes = {};
|
|
1473
|
+
if (payload.metadata?.agents) {
|
|
1474
|
+
Object.entries(payload.metadata.agents).forEach(([key, agent]) => {
|
|
1475
|
+
const mappedKey = agent.alias || key;
|
|
1476
|
+
agentTypes[mappedKey] = agent.type;
|
|
1199
1477
|
});
|
|
1200
1478
|
}
|
|
1201
|
-
|
|
1479
|
+
const lineSingers = sanitizedEntries.map((entry) => entry.element?.singer);
|
|
1480
|
+
const alignments = AmLyrics.calculateLineAlignments(lineSingers, agentTypes);
|
|
1481
|
+
for (let i = 0; i < sanitizedEntries.length; i += 1) {
|
|
1482
|
+
const entry = sanitizedEntries[i];
|
|
1202
1483
|
const start = AmLyrics.toMilliseconds(entry.time);
|
|
1203
1484
|
const duration = AmLyrics.toMilliseconds(entry.duration);
|
|
1204
|
-
|
|
1205
|
-
let alignment;
|
|
1206
|
-
const singerId = entry.element?.singer;
|
|
1207
|
-
if (singerId && singerAlignmentMap[singerId]) {
|
|
1208
|
-
alignment = singerAlignmentMap[singerId];
|
|
1209
|
-
}
|
|
1485
|
+
const alignment = alignments[i];
|
|
1210
1486
|
const lineText = typeof entry.text === 'string' ? entry.text : '';
|
|
1211
1487
|
const lineStart = AmLyrics.toMilliseconds(entry.time);
|
|
1212
1488
|
const lineDuration = AmLyrics.toMilliseconds(entry.duration);
|
|
@@ -1257,10 +1533,8 @@ class AmLyrics extends i {
|
|
|
1257
1533
|
// If syllabus data matches, map it to main syllables
|
|
1258
1534
|
if (Array.isArray(transliteration.syllabus) &&
|
|
1259
1535
|
transliteration.syllabus.length === mainSyllables.length) {
|
|
1260
|
-
transliteration.syllabus.forEach((s,
|
|
1261
|
-
|
|
1262
|
-
mainSyllables[i].romanizedText = s.text;
|
|
1263
|
-
}
|
|
1536
|
+
transliteration.syllabus.forEach((s, idx) => {
|
|
1537
|
+
mainSyllables[idx].romanizedText = s.text;
|
|
1264
1538
|
});
|
|
1265
1539
|
}
|
|
1266
1540
|
}
|
|
@@ -1270,10 +1544,11 @@ class AmLyrics extends i {
|
|
|
1270
1544
|
text: mainSyllables,
|
|
1271
1545
|
background: backgroundSyllables.length > 0,
|
|
1272
1546
|
backgroundText: backgroundSyllables,
|
|
1273
|
-
oppositeTurn:
|
|
1274
|
-
|
|
1275
|
-
entry.element.includes('
|
|
1276
|
-
|
|
1547
|
+
oppositeTurn: alignment === 'end' ||
|
|
1548
|
+
(Array.isArray(entry.element)
|
|
1549
|
+
? entry.element.includes('opposite') ||
|
|
1550
|
+
entry.element.includes('right')
|
|
1551
|
+
: false),
|
|
1277
1552
|
timestamp: lineStart,
|
|
1278
1553
|
endtime: start + duration,
|
|
1279
1554
|
isWordSynced: isLineType ? false : hasWordSync,
|