@uimaxbai/am-lyrics 1.1.2 → 1.1.5
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 +347 -53
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +347 -53
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +399 -57
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.5';
|
|
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,82 @@ 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 fetching any word synced lyrics from lyricsplus
|
|
885
|
+
const fallbackParams = new URLSearchParams(params);
|
|
886
|
+
const fallbackUrl = `https://lyricsplus.binimum.org/v2/lyrics/get?${fallbackParams.toString()}`;
|
|
887
|
+
try {
|
|
888
|
+
const fallbackRes = await fetch(fallbackUrl);
|
|
889
|
+
if (fallbackRes.ok) {
|
|
890
|
+
const payload = await fallbackRes.json();
|
|
891
|
+
const lines = AmLyrics.convertKPoeLyrics(payload);
|
|
892
|
+
const hasWordSync = lines?.some((line) => line.text &&
|
|
893
|
+
Array.isArray(line.text) &&
|
|
894
|
+
line.text.length > 1);
|
|
895
|
+
if (lines && lines.length > 0 && hasWordSync) {
|
|
896
|
+
const sourceLabel = payload?.metadata?.source ||
|
|
897
|
+
payload?.metadata?.provider ||
|
|
898
|
+
'LyricsPlus (KPoe)';
|
|
899
|
+
allResults.push({ lines, source: sourceLabel });
|
|
900
|
+
return allResults;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
catch (fallbackError) {
|
|
905
|
+
// Ignore fallback fetch error
|
|
906
|
+
}
|
|
907
|
+
// If fallback fails or has no word sync, fall back to bini lyrics
|
|
908
|
+
if (result.lyricsUrl) {
|
|
909
|
+
const ttmlRes = await fetch(result.lyricsUrl);
|
|
910
|
+
if (ttmlRes.ok) {
|
|
911
|
+
const ttmlText = await ttmlRes.text();
|
|
912
|
+
const lines = AmLyrics.parseTTML(ttmlText);
|
|
913
|
+
if (lines && lines.length > 0) {
|
|
914
|
+
allResults.push({
|
|
915
|
+
lines,
|
|
916
|
+
source: 'BiniLyrics',
|
|
917
|
+
});
|
|
918
|
+
return allResults;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
catch (e) {
|
|
927
|
+
// eslint-disable-next-line no-console
|
|
928
|
+
console.error('Cache API failed', e);
|
|
929
|
+
}
|
|
854
930
|
// Shuffle servers so we pick a random one first, with all others as fallback
|
|
855
931
|
// Limit to 2 servers to prevent unnecessary API spam when Apple lyrics are missing
|
|
856
932
|
const shuffledServers = [...KPOE_SERVERS]
|
|
@@ -887,14 +963,13 @@ class AmLyrics extends i {
|
|
|
887
963
|
}
|
|
888
964
|
}
|
|
889
965
|
}
|
|
890
|
-
// If we haven't found a completely synced
|
|
891
|
-
// force an explicit query against lyricsplus.binimum.org looking for
|
|
966
|
+
// If we haven't found a completely synced result (rank 1 or 2) among the servers,
|
|
967
|
+
// force an explicit query against lyricsplus.binimum.org looking for word lyrics
|
|
892
968
|
const hasHighRankResult = allResults.some(r => getRank(r.source, r.lines) <= 2);
|
|
893
969
|
if (!hasHighRankResult) {
|
|
894
970
|
try {
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
const url = `https://lyricsplus.binimum.org/v2/lyrics/get?${qqParams.toString()}`;
|
|
971
|
+
const fallbackParams = new URLSearchParams(params);
|
|
972
|
+
const url = `https://lyricsplus.binimum.org/v2/lyrics/get?${fallbackParams.toString()}`;
|
|
898
973
|
const response = await fetch(url);
|
|
899
974
|
if (response.ok) {
|
|
900
975
|
const payload = await response.json();
|
|
@@ -903,14 +978,15 @@ class AmLyrics extends i {
|
|
|
903
978
|
const sourceLabel = payload?.metadata?.source ||
|
|
904
979
|
payload?.metadata?.provider ||
|
|
905
980
|
'LyricsPlus (KPoe)';
|
|
906
|
-
|
|
981
|
+
const hasWordSync = lines?.some((line) => line.text && Array.isArray(line.text) && line.text.length > 1);
|
|
982
|
+
if (lines && lines.length > 0 && hasWordSync) {
|
|
907
983
|
allResults.push({ lines, source: sourceLabel });
|
|
908
984
|
}
|
|
909
985
|
}
|
|
910
986
|
}
|
|
911
987
|
}
|
|
912
988
|
catch (error) {
|
|
913
|
-
// Explicit
|
|
989
|
+
// Explicit fallback failed, ignore
|
|
914
990
|
}
|
|
915
991
|
}
|
|
916
992
|
return allResults;
|
|
@@ -930,6 +1006,7 @@ class AmLyrics extends i {
|
|
|
930
1006
|
if (!match) {
|
|
931
1007
|
// Skip non-timestamped lines (headers like [ti:], [ar:], etc.)
|
|
932
1008
|
// eslint-disable-next-line no-continue
|
|
1009
|
+
// eslint-disable-next-line no-continue
|
|
933
1010
|
continue;
|
|
934
1011
|
}
|
|
935
1012
|
const minutes = parseInt(match[1], 10);
|
|
@@ -949,6 +1026,7 @@ class AmLyrics extends i {
|
|
|
949
1026
|
const endtime = i + 1 < parsed.length ? parsed[i + 1].timestamp : timestamp + 5000;
|
|
950
1027
|
// Skip empty lines (instrumental gaps)
|
|
951
1028
|
if (!text.trim()) {
|
|
1029
|
+
// eslint-disable-next-line no-continue
|
|
952
1030
|
// eslint-disable-next-line no-continue
|
|
953
1031
|
continue;
|
|
954
1032
|
}
|
|
@@ -992,6 +1070,7 @@ class AmLyrics extends i {
|
|
|
992
1070
|
// eslint-disable-next-line no-await-in-loop
|
|
993
1071
|
const searchResponse = await fetch(`${normalizedBase}/search/?${searchParams.toString()}`);
|
|
994
1072
|
if (!searchResponse.ok) {
|
|
1073
|
+
// eslint-disable-next-line no-continue
|
|
995
1074
|
// eslint-disable-next-line no-continue
|
|
996
1075
|
continue;
|
|
997
1076
|
}
|
|
@@ -999,6 +1078,7 @@ class AmLyrics extends i {
|
|
|
999
1078
|
const searchData = await searchResponse.json();
|
|
1000
1079
|
const items = searchData?.data?.items;
|
|
1001
1080
|
if (!Array.isArray(items) || items.length === 0) {
|
|
1081
|
+
// eslint-disable-next-line no-continue
|
|
1002
1082
|
// eslint-disable-next-line no-continue
|
|
1003
1083
|
continue;
|
|
1004
1084
|
}
|
|
@@ -1012,6 +1092,7 @@ class AmLyrics extends i {
|
|
|
1012
1092
|
}
|
|
1013
1093
|
const trackId = bestTrack?.id;
|
|
1014
1094
|
if (!trackId) {
|
|
1095
|
+
// eslint-disable-next-line no-continue
|
|
1015
1096
|
// eslint-disable-next-line no-continue
|
|
1016
1097
|
continue;
|
|
1017
1098
|
}
|
|
@@ -1019,6 +1100,7 @@ class AmLyrics extends i {
|
|
|
1019
1100
|
// eslint-disable-next-line no-await-in-loop
|
|
1020
1101
|
const lyricsResponse = await fetch(`${normalizedBase}/lyrics/?id=${trackId}`);
|
|
1021
1102
|
if (!lyricsResponse.ok) {
|
|
1103
|
+
// eslint-disable-next-line no-continue
|
|
1022
1104
|
// eslint-disable-next-line no-continue
|
|
1023
1105
|
continue;
|
|
1024
1106
|
}
|
|
@@ -1143,10 +1225,230 @@ class AmLyrics extends i {
|
|
|
1143
1225
|
}
|
|
1144
1226
|
}
|
|
1145
1227
|
catch {
|
|
1228
|
+
// eslint-disable-next-line no-console
|
|
1146
1229
|
console.error('No Genius lyrics found');
|
|
1147
1230
|
}
|
|
1148
1231
|
return null;
|
|
1149
1232
|
}
|
|
1233
|
+
static calculateLineAlignments(lineSingers, agentTypes) {
|
|
1234
|
+
const lineSideAssignments = new Array(lineSingers.length).fill(undefined);
|
|
1235
|
+
let currentSideIsLeft = true;
|
|
1236
|
+
let lastPersonSingerId = null;
|
|
1237
|
+
let rightCount = 0;
|
|
1238
|
+
let totalCount = 0;
|
|
1239
|
+
lineSingers.forEach((singerId, index) => {
|
|
1240
|
+
let sideClass;
|
|
1241
|
+
if (singerId) {
|
|
1242
|
+
let type = agentTypes[singerId];
|
|
1243
|
+
if (!type) {
|
|
1244
|
+
if (singerId === 'v1000') {
|
|
1245
|
+
type = 'group';
|
|
1246
|
+
}
|
|
1247
|
+
else if (singerId === 'v2000') {
|
|
1248
|
+
type = 'other';
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
type = 'person';
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
if (type === 'group') {
|
|
1255
|
+
sideClass = 'start';
|
|
1256
|
+
}
|
|
1257
|
+
else {
|
|
1258
|
+
if (lastPersonSingerId === null) {
|
|
1259
|
+
if (type === 'other') {
|
|
1260
|
+
currentSideIsLeft = false;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
currentSideIsLeft = true;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
else if (singerId !== lastPersonSingerId) {
|
|
1267
|
+
currentSideIsLeft = !currentSideIsLeft;
|
|
1268
|
+
}
|
|
1269
|
+
sideClass = currentSideIsLeft ? 'start' : 'end';
|
|
1270
|
+
lastPersonSingerId = singerId;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
if (sideClass) {
|
|
1274
|
+
totalCount += 1;
|
|
1275
|
+
if (sideClass === 'end')
|
|
1276
|
+
rightCount += 1;
|
|
1277
|
+
}
|
|
1278
|
+
lineSideAssignments[index] = sideClass;
|
|
1279
|
+
});
|
|
1280
|
+
if (totalCount > 0 && Math.round((rightCount / totalCount) * 100) >= 85) {
|
|
1281
|
+
const flip = (s) => {
|
|
1282
|
+
if (s === 'start')
|
|
1283
|
+
return 'end';
|
|
1284
|
+
if (s === 'end')
|
|
1285
|
+
return 'start';
|
|
1286
|
+
return s;
|
|
1287
|
+
};
|
|
1288
|
+
for (let i = 0; i < lineSideAssignments.length; i += 1) {
|
|
1289
|
+
lineSideAssignments[i] = flip(lineSideAssignments[i]);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return lineSideAssignments;
|
|
1293
|
+
}
|
|
1294
|
+
static parseTTML(ttmlString) {
|
|
1295
|
+
try {
|
|
1296
|
+
const parser = new DOMParser();
|
|
1297
|
+
const doc = parser.parseFromString(ttmlString, 'text/xml');
|
|
1298
|
+
const translations = {};
|
|
1299
|
+
const transliterations = {};
|
|
1300
|
+
const agentMap = {};
|
|
1301
|
+
const agents = doc.getElementsByTagName('ttm:agent');
|
|
1302
|
+
for (let i = 0; i < agents.length; i += 1) {
|
|
1303
|
+
const agent = agents[i];
|
|
1304
|
+
const id = agent.getAttribute('xml:id');
|
|
1305
|
+
const type = agent.getAttribute('type');
|
|
1306
|
+
if (id && type) {
|
|
1307
|
+
agentMap[id] = type;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
const translationNodes = doc.getElementsByTagName('translation');
|
|
1311
|
+
for (let i = 0; i < translationNodes.length; i += 1) {
|
|
1312
|
+
const texts = translationNodes[i].getElementsByTagName('text');
|
|
1313
|
+
for (let j = 0; j < texts.length; j += 1) {
|
|
1314
|
+
const textNode = texts[j];
|
|
1315
|
+
const key = textNode.getAttribute('for');
|
|
1316
|
+
if (key && textNode.textContent) {
|
|
1317
|
+
translations[key] = textNode.textContent;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const transliterationNodes = doc.getElementsByTagName('transliteration');
|
|
1322
|
+
for (let i = 0; i < transliterationNodes.length; i += 1) {
|
|
1323
|
+
const texts = transliterationNodes[i].getElementsByTagName('text');
|
|
1324
|
+
for (let j = 0; j < texts.length; j += 1) {
|
|
1325
|
+
const textNode = texts[j];
|
|
1326
|
+
const key = textNode.getAttribute('for');
|
|
1327
|
+
if (key && textNode.textContent) {
|
|
1328
|
+
transliterations[key] = textNode.textContent
|
|
1329
|
+
.trim()
|
|
1330
|
+
.replace(/\s+/g, ' ');
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
const timeToMs = (timeStr) => {
|
|
1335
|
+
if (!timeStr)
|
|
1336
|
+
return 0;
|
|
1337
|
+
const parts = timeStr.split(':');
|
|
1338
|
+
let seconds = 0;
|
|
1339
|
+
if (parts.length === 2) {
|
|
1340
|
+
seconds = parseInt(parts[0], 10) * 60 + parseFloat(parts[1]);
|
|
1341
|
+
}
|
|
1342
|
+
else if (parts.length === 3) {
|
|
1343
|
+
seconds =
|
|
1344
|
+
parseInt(parts[0], 10) * 3600 +
|
|
1345
|
+
parseInt(parts[1], 10) * 60 +
|
|
1346
|
+
parseFloat(parts[2]);
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
seconds = parseFloat(parts[0]);
|
|
1350
|
+
}
|
|
1351
|
+
return Math.round(seconds * 1000);
|
|
1352
|
+
};
|
|
1353
|
+
const lines = [];
|
|
1354
|
+
const pNodes = doc.getElementsByTagName('p');
|
|
1355
|
+
const lineSingers = [];
|
|
1356
|
+
for (let i = 0; i < pNodes.length; i += 1) {
|
|
1357
|
+
lineSingers.push(pNodes[i].getAttribute('ttm:agent') || undefined);
|
|
1358
|
+
}
|
|
1359
|
+
const alignments = AmLyrics.calculateLineAlignments(lineSingers, agentMap);
|
|
1360
|
+
for (let i = 0; i < pNodes.length; i += 1) {
|
|
1361
|
+
const p = pNodes[i];
|
|
1362
|
+
const key = p.getAttribute('itunes:key');
|
|
1363
|
+
const beginMs = timeToMs(p.getAttribute('begin'));
|
|
1364
|
+
const endMs = timeToMs(p.getAttribute('end'));
|
|
1365
|
+
let songPart;
|
|
1366
|
+
if (p.parentNode && p.parentNode.tagName === 'div') {
|
|
1367
|
+
songPart =
|
|
1368
|
+
p.parentNode.getAttribute('itunes:songPart') ||
|
|
1369
|
+
undefined;
|
|
1370
|
+
}
|
|
1371
|
+
const mainSyllables = [];
|
|
1372
|
+
const bgSyllables = [];
|
|
1373
|
+
const spans = p.getElementsByTagName('span');
|
|
1374
|
+
if (spans.length > 0) {
|
|
1375
|
+
for (let j = 0; j < spans.length; j += 1) {
|
|
1376
|
+
const span = spans[j];
|
|
1377
|
+
if (span.getAttribute('ttm:role') === 'x-bg') {
|
|
1378
|
+
const bgInnerSpans = span.getElementsByTagName('span');
|
|
1379
|
+
for (let k = 0; k < bgInnerSpans.length; k += 1) {
|
|
1380
|
+
const bgSpan = bgInnerSpans[k];
|
|
1381
|
+
let bgText = bgSpan.textContent || '';
|
|
1382
|
+
const nextNode = bgSpan.nextSibling;
|
|
1383
|
+
if (nextNode &&
|
|
1384
|
+
nextNode.nodeType === 3 &&
|
|
1385
|
+
/^\s/.test(nextNode.textContent || '') &&
|
|
1386
|
+
!bgText.endsWith(' ')) {
|
|
1387
|
+
bgText += ' ';
|
|
1388
|
+
}
|
|
1389
|
+
bgSyllables.push({
|
|
1390
|
+
text: bgText,
|
|
1391
|
+
timestamp: timeToMs(bgSpan.getAttribute('begin')),
|
|
1392
|
+
endtime: timeToMs(bgSpan.getAttribute('end')),
|
|
1393
|
+
part: false,
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
// eslint-disable-next-line no-continue
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (span.parentNode &&
|
|
1400
|
+
span.parentNode.getAttribute?.('ttm:role') === 'x-bg') {
|
|
1401
|
+
// eslint-disable-next-line no-continue
|
|
1402
|
+
continue;
|
|
1403
|
+
}
|
|
1404
|
+
let text = span.textContent || '';
|
|
1405
|
+
const nextNode = span.nextSibling;
|
|
1406
|
+
if (nextNode &&
|
|
1407
|
+
nextNode.nodeType === 3 &&
|
|
1408
|
+
/^\s/.test(nextNode.textContent || '') &&
|
|
1409
|
+
!text.endsWith(' ')) {
|
|
1410
|
+
text += ' ';
|
|
1411
|
+
}
|
|
1412
|
+
mainSyllables.push({
|
|
1413
|
+
text,
|
|
1414
|
+
timestamp: timeToMs(span.getAttribute('begin')),
|
|
1415
|
+
endtime: timeToMs(span.getAttribute('end')),
|
|
1416
|
+
part: false,
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
mainSyllables.push({
|
|
1422
|
+
text: p.textContent?.trim() || '',
|
|
1423
|
+
timestamp: beginMs,
|
|
1424
|
+
endtime: endMs,
|
|
1425
|
+
part: false,
|
|
1426
|
+
lineSynced: true,
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
const alignment = alignments[i];
|
|
1430
|
+
lines.push({
|
|
1431
|
+
text: mainSyllables,
|
|
1432
|
+
background: bgSyllables.length > 0,
|
|
1433
|
+
backgroundText: bgSyllables,
|
|
1434
|
+
timestamp: beginMs,
|
|
1435
|
+
endtime: endMs,
|
|
1436
|
+
isWordSynced: spans.length > 0,
|
|
1437
|
+
alignment,
|
|
1438
|
+
songPart,
|
|
1439
|
+
translation: key ? translations[key] : undefined,
|
|
1440
|
+
romanizedText: key ? transliterations[key] : undefined,
|
|
1441
|
+
oppositeTurn: alignment === 'end',
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
return lines;
|
|
1445
|
+
}
|
|
1446
|
+
catch (e) {
|
|
1447
|
+
// eslint-disable-next-line no-console
|
|
1448
|
+
console.error('Failed to parse TTML', e);
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1150
1452
|
static convertKPoeLyrics(payload) {
|
|
1151
1453
|
if (!payload) {
|
|
1152
1454
|
return null;
|
|
@@ -1168,45 +1470,21 @@ class AmLyrics extends i {
|
|
|
1168
1470
|
const lines = [];
|
|
1169
1471
|
// If type is 'Line', we revert to line-by-line highlighting by skipping syllabus parsing
|
|
1170
1472
|
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
|
-
}
|
|
1473
|
+
// Convert metadata.agents to type map
|
|
1474
|
+
const agentTypes = {};
|
|
1475
|
+
if (payload.metadata?.agents) {
|
|
1476
|
+
Object.entries(payload.metadata.agents).forEach(([key, agent]) => {
|
|
1477
|
+
const mappedKey = agent.alias || key;
|
|
1478
|
+
agentTypes[mappedKey] = agent.type;
|
|
1199
1479
|
});
|
|
1200
1480
|
}
|
|
1201
|
-
|
|
1481
|
+
const lineSingers = sanitizedEntries.map((entry) => entry.element?.singer);
|
|
1482
|
+
const alignments = AmLyrics.calculateLineAlignments(lineSingers, agentTypes);
|
|
1483
|
+
for (let i = 0; i < sanitizedEntries.length; i += 1) {
|
|
1484
|
+
const entry = sanitizedEntries[i];
|
|
1202
1485
|
const start = AmLyrics.toMilliseconds(entry.time);
|
|
1203
1486
|
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
|
-
}
|
|
1487
|
+
const alignment = alignments[i];
|
|
1210
1488
|
const lineText = typeof entry.text === 'string' ? entry.text : '';
|
|
1211
1489
|
const lineStart = AmLyrics.toMilliseconds(entry.time);
|
|
1212
1490
|
const lineDuration = AmLyrics.toMilliseconds(entry.duration);
|
|
@@ -1257,10 +1535,8 @@ class AmLyrics extends i {
|
|
|
1257
1535
|
// If syllabus data matches, map it to main syllables
|
|
1258
1536
|
if (Array.isArray(transliteration.syllabus) &&
|
|
1259
1537
|
transliteration.syllabus.length === mainSyllables.length) {
|
|
1260
|
-
transliteration.syllabus.forEach((s,
|
|
1261
|
-
|
|
1262
|
-
mainSyllables[i].romanizedText = s.text;
|
|
1263
|
-
}
|
|
1538
|
+
transliteration.syllabus.forEach((s, idx) => {
|
|
1539
|
+
mainSyllables[idx].romanizedText = s.text;
|
|
1264
1540
|
});
|
|
1265
1541
|
}
|
|
1266
1542
|
}
|
|
@@ -1270,10 +1546,11 @@ class AmLyrics extends i {
|
|
|
1270
1546
|
text: mainSyllables,
|
|
1271
1547
|
background: backgroundSyllables.length > 0,
|
|
1272
1548
|
backgroundText: backgroundSyllables,
|
|
1273
|
-
oppositeTurn:
|
|
1274
|
-
|
|
1275
|
-
entry.element.includes('
|
|
1276
|
-
|
|
1549
|
+
oppositeTurn: alignment === 'end' ||
|
|
1550
|
+
(Array.isArray(entry.element)
|
|
1551
|
+
? entry.element.includes('opposite') ||
|
|
1552
|
+
entry.element.includes('right')
|
|
1553
|
+
: false),
|
|
1277
1554
|
timestamp: lineStart,
|
|
1278
1555
|
endtime: start + duration,
|
|
1279
1556
|
isWordSynced: isLineType ? false : hasWordSync,
|
|
@@ -1386,15 +1663,32 @@ class AmLyrics extends i {
|
|
|
1386
1663
|
// Entering gap: remove any leftover exit state, add active
|
|
1387
1664
|
gap.classList.remove('gap-exiting');
|
|
1388
1665
|
gap.classList.add('active');
|
|
1389
|
-
// Mark
|
|
1390
|
-
//
|
|
1666
|
+
// Mark dots whose time has already passed as finished, and
|
|
1667
|
+
// trigger highlight on the dot currently in its time window
|
|
1668
|
+
// so the first dot always lights up even on late load.
|
|
1391
1669
|
const dotSyllables = gap.querySelectorAll('.lyrics-syllable');
|
|
1392
1670
|
dotSyllables.forEach(dot => {
|
|
1671
|
+
const dotStart = parseFloat(dot.getAttribute('data-start-time') || '0');
|
|
1393
1672
|
const dotEnd = parseFloat(dot.getAttribute('data-end-time') || '0');
|
|
1394
1673
|
if (newTime > dotEnd) {
|
|
1395
1674
|
dot.classList.add('finished');
|
|
1675
|
+
// Also ensure the highlight + animation fired so CSS state is correct
|
|
1676
|
+
if (!dot.classList.contains('highlight')) {
|
|
1677
|
+
AmLyrics.updateSyllableAnimation(dot);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
else if (newTime >= dotStart && newTime <= dotEnd) {
|
|
1681
|
+
// Currently within this dot's window — trigger its highlight
|
|
1682
|
+
AmLyrics.updateSyllableAnimation(dot);
|
|
1396
1683
|
}
|
|
1397
1684
|
});
|
|
1685
|
+
// Scroll to the gap element so dots animate in with
|
|
1686
|
+
// the staggered scroll rather than popping in.
|
|
1687
|
+
if (this.autoScroll &&
|
|
1688
|
+
!this.isUserScrolling &&
|
|
1689
|
+
!this.isClickSeeking) {
|
|
1690
|
+
this.scrollToActiveLineYouLy(gap);
|
|
1691
|
+
}
|
|
1398
1692
|
}
|
|
1399
1693
|
else if (shouldStartExiting) {
|
|
1400
1694
|
// Exiting gap: keep visible while dots animate out
|