@uimaxbai/am-lyrics 1.2.9 → 1.4.0
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/README.md +0 -5
- package/dist/src/AmLyrics.d.ts +5 -7
- package/dist/src/AmLyrics.d.ts.map +1 -1
- package/dist/src/am-lyrics.js +470 -350
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +470 -350
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +549 -401
- package/fix.cjs +0 -7
- package/patch.diff +0 -210
- package/patch2.diff +0 -26
package/src/AmLyrics.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { css, html, LitElement, svg } from 'lit';
|
|
|
2
2
|
import { property, query, state } from 'lit/decorators.js';
|
|
3
3
|
import { GoogleService } from './GoogleService.js';
|
|
4
4
|
|
|
5
|
-
const VERSION = '1.
|
|
5
|
+
const VERSION = '1.4.0';
|
|
6
6
|
const INSTRUMENTAL_THRESHOLD_MS = 7000; // Show dots for gaps >= 7s
|
|
7
7
|
const FETCH_TIMEOUT_MS = 8000; // Timeout for all lyrics fetch requests
|
|
8
8
|
const SEEK_THRESHOLD_MS = 500;
|
|
@@ -34,7 +34,6 @@ function fetchWithTimeout(
|
|
|
34
34
|
|
|
35
35
|
const KPOE_SERVERS = [
|
|
36
36
|
'https://lyricsplus.binimum.org',
|
|
37
|
-
'https://lyricsplus.atomix.one',
|
|
38
37
|
'https://lyricsplus-seven.vercel.app',
|
|
39
38
|
'https://lyricsplus.prjktla.workers.dev',
|
|
40
39
|
'https://lyrics-plus-backend.vercel.app',
|
|
@@ -42,19 +41,6 @@ const KPOE_SERVERS = [
|
|
|
42
41
|
const DEFAULT_KPOE_SOURCE_ORDER =
|
|
43
42
|
'apple,lyricsplus,musixmatch,spotify,qq,deezer,musixmatch-word';
|
|
44
43
|
|
|
45
|
-
const TIDAL_SERVERS = [
|
|
46
|
-
'https://arran.monochrome.tf',
|
|
47
|
-
'https://api.monochrome.tf/',
|
|
48
|
-
'https://triton.squid.wtf',
|
|
49
|
-
'https://wolf.qqdl.site',
|
|
50
|
-
'https://maus.qqdl.site',
|
|
51
|
-
'https://vogel.qqdl.site',
|
|
52
|
-
'https://katze.qqdl.site',
|
|
53
|
-
'https://hund.qqdl.site',
|
|
54
|
-
'https://tidal.kinoplus.online',
|
|
55
|
-
'https://hifi-one.spotisaver.net',
|
|
56
|
-
'https://hifi-two.spotisaver.net',
|
|
57
|
-
];
|
|
58
44
|
const GENIUS_WORKER_URL = 'https://fetch-genius.samidy.workers.dev/';
|
|
59
45
|
|
|
60
46
|
interface Syllable {
|
|
@@ -85,6 +71,7 @@ interface SongMetadata {
|
|
|
85
71
|
artist: string;
|
|
86
72
|
album?: string;
|
|
87
73
|
durationMs?: number;
|
|
74
|
+
songwriters?: string;
|
|
88
75
|
}
|
|
89
76
|
|
|
90
77
|
interface SongCatalogResult {
|
|
@@ -92,6 +79,7 @@ interface SongCatalogResult {
|
|
|
92
79
|
artist?: string;
|
|
93
80
|
album?: string;
|
|
94
81
|
durationMs?: number;
|
|
82
|
+
songwriters?: string;
|
|
95
83
|
id?: {
|
|
96
84
|
appleMusic?: string;
|
|
97
85
|
[key: string]: unknown;
|
|
@@ -108,6 +96,7 @@ interface ParsedQueryMetadata {
|
|
|
108
96
|
interface YouLyPlusLyricsResult {
|
|
109
97
|
lines: LyricsLine[];
|
|
110
98
|
source: string;
|
|
99
|
+
songwriters?: string;
|
|
111
100
|
}
|
|
112
101
|
|
|
113
102
|
interface ResolvedMetadata {
|
|
@@ -145,6 +134,7 @@ export class AmLyrics extends LitElement {
|
|
|
145
134
|
--lyplus-font-size-base: 32px;
|
|
146
135
|
--lyplus-font-size-base-grow: 24.5;
|
|
147
136
|
--lyplus-font-size-subtext: 0.6em;
|
|
137
|
+
--char-rise-y: calc(-0.035 * var(--lyplus-font-size-base));
|
|
148
138
|
|
|
149
139
|
--lyplus-blur-amount: 0.07em;
|
|
150
140
|
--lyplus-blur-amount-near: 0.035em;
|
|
@@ -178,7 +168,6 @@ export class AmLyrics extends LitElement {
|
|
|
178
168
|
-webkit-overflow-scrolling: touch;
|
|
179
169
|
box-sizing: border-box;
|
|
180
170
|
scrollbar-width: none;
|
|
181
|
-
transform: translateZ(0);
|
|
182
171
|
}
|
|
183
172
|
|
|
184
173
|
.lyrics-container::-webkit-scrollbar {
|
|
@@ -189,11 +178,13 @@ export class AmLyrics extends LitElement {
|
|
|
189
178
|
.lyrics-container.touch-scrolling .lyrics-line,
|
|
190
179
|
.lyrics-container.touch-scrolling .lyrics-plus-metadata {
|
|
191
180
|
transition: none !important;
|
|
181
|
+
filter: none !important;
|
|
192
182
|
}
|
|
193
183
|
|
|
194
184
|
/* Apply smooth gliding transition for mouse-wheel scrolling */
|
|
195
185
|
.lyrics-container.wheel-scrolling .lyrics-line {
|
|
196
186
|
transition: transform 0.3s ease-out !important;
|
|
187
|
+
filter: none !important;
|
|
197
188
|
}
|
|
198
189
|
|
|
199
190
|
.lyrics-line.scroll-animate {
|
|
@@ -220,18 +211,13 @@ export class AmLyrics extends LitElement {
|
|
|
220
211
|
font-size: var(--lyplus-font-size-base);
|
|
221
212
|
cursor: pointer;
|
|
222
213
|
transform-origin: left;
|
|
223
|
-
transform: translateZ(1px);
|
|
224
214
|
transition:
|
|
225
215
|
opacity 0.3s ease,
|
|
226
216
|
transform 0.4s cubic-bezier(0.41, 0, 0.12, 0.99)
|
|
227
217
|
var(--lyrics-line-delay, 0ms),
|
|
228
218
|
filter 0.3s ease;
|
|
229
|
-
will-change: transform, filter, opacity;
|
|
230
219
|
content-visibility: auto;
|
|
231
220
|
text-rendering: optimizeLegibility;
|
|
232
|
-
overflow-wrap: break-word;
|
|
233
|
-
mix-blend-mode: lighten;
|
|
234
|
-
border-radius: var(--lyplus-border-radius-base);
|
|
235
221
|
}
|
|
236
222
|
|
|
237
223
|
.lyrics-line:not(.scroll-animate) {
|
|
@@ -251,8 +237,7 @@ export class AmLyrics extends LitElement {
|
|
|
251
237
|
|
|
252
238
|
.lyrics-line.active .lyrics-line-container,
|
|
253
239
|
.lyrics-line.pre-active .lyrics-line-container {
|
|
254
|
-
transform: scale3d(1.001, 1.001, 1);
|
|
255
|
-
will-change: transform;
|
|
240
|
+
transform: scale3d(1.001, 1.001, 1) translateZ(0);
|
|
256
241
|
transition:
|
|
257
242
|
transform 0.5s ease,
|
|
258
243
|
background-color 0.18s,
|
|
@@ -297,12 +282,10 @@ export class AmLyrics extends LitElement {
|
|
|
297
282
|
.lyrics-line.active {
|
|
298
283
|
opacity: 1;
|
|
299
284
|
color: var(--lyplus-text-primary);
|
|
300
|
-
will-change: transform, opacity;
|
|
301
285
|
}
|
|
302
286
|
|
|
303
287
|
.lyrics-line.pre-active {
|
|
304
288
|
opacity: 1;
|
|
305
|
-
will-change: transform, opacity;
|
|
306
289
|
}
|
|
307
290
|
|
|
308
291
|
.lyrics-line.singer-right {
|
|
@@ -316,6 +299,18 @@ export class AmLyrics extends LitElement {
|
|
|
316
299
|
|
|
317
300
|
.lyrics-line.rtl-text {
|
|
318
301
|
direction: rtl;
|
|
302
|
+
text-align: right !important;
|
|
303
|
+
transform-origin: right;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.lyrics-line.rtl-text .lyrics-line-container,
|
|
307
|
+
.lyrics-line.rtl-text .main-vocal-container {
|
|
308
|
+
transform-origin: right;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.lyrics-line.rtl-text .lyrics-romanization-container,
|
|
312
|
+
.lyrics-line.rtl-text .lyrics-translation-container {
|
|
313
|
+
text-align: right;
|
|
319
314
|
}
|
|
320
315
|
|
|
321
316
|
/* --- Unsynced (Plain Text) Lyrics Overrides --- */
|
|
@@ -347,7 +342,8 @@ export class AmLyrics extends LitElement {
|
|
|
347
342
|
|
|
348
343
|
@media (hover: hover) and (pointer: fine) {
|
|
349
344
|
.lyrics-line:hover {
|
|
350
|
-
|
|
345
|
+
filter: none !important;
|
|
346
|
+
opacity: 1 !important;
|
|
351
347
|
}
|
|
352
348
|
.lyrics-container.is-unsynced .lyrics-line:hover {
|
|
353
349
|
background: transparent !important;
|
|
@@ -377,6 +373,7 @@ export class AmLyrics extends LitElement {
|
|
|
377
373
|
|
|
378
374
|
/* Unblur all lines when user is scrolling */
|
|
379
375
|
.lyrics-container.user-scrolling .lyrics-line {
|
|
376
|
+
transition: none !important;
|
|
380
377
|
filter: none !important;
|
|
381
378
|
opacity: 0.8 !important;
|
|
382
379
|
}
|
|
@@ -393,6 +390,7 @@ export class AmLyrics extends LitElement {
|
|
|
393
390
|
.lyrics-word:not(.allow-break) {
|
|
394
391
|
display: inline-block;
|
|
395
392
|
vertical-align: baseline;
|
|
393
|
+
white-space: nowrap;
|
|
396
394
|
}
|
|
397
395
|
|
|
398
396
|
.lyrics-word.allow-break {
|
|
@@ -403,7 +401,7 @@ export class AmLyrics extends LitElement {
|
|
|
403
401
|
display: inline;
|
|
404
402
|
}
|
|
405
403
|
|
|
406
|
-
.lyrics-syllable-wrap
|
|
404
|
+
.lyrics-syllable-wrap.has-transliteration {
|
|
407
405
|
display: inline-flex;
|
|
408
406
|
flex-direction: column;
|
|
409
407
|
align-items: start;
|
|
@@ -431,7 +429,7 @@ export class AmLyrics extends LitElement {
|
|
|
431
429
|
transition: transform 1s ease !important;
|
|
432
430
|
}
|
|
433
431
|
|
|
434
|
-
.lyrics-syllable.finished
|
|
432
|
+
.lyrics-syllable.finished.has-chars {
|
|
435
433
|
background-color: transparent;
|
|
436
434
|
}
|
|
437
435
|
|
|
@@ -440,19 +438,16 @@ export class AmLyrics extends LitElement {
|
|
|
440
438
|
}
|
|
441
439
|
|
|
442
440
|
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable {
|
|
443
|
-
transform: translateY(0.001%) translateZ(1px);
|
|
444
441
|
transition:
|
|
445
442
|
transform 1s ease,
|
|
446
443
|
background-color 0.5s,
|
|
447
444
|
color 0.5s;
|
|
448
|
-
will-change: transform, background;
|
|
449
445
|
}
|
|
450
446
|
|
|
451
447
|
/* --- Wipe Highlight Effect --- */
|
|
448
|
+
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable.highlight.no-chars,
|
|
452
449
|
.lyrics-line.active:not(.lyrics-gap)
|
|
453
|
-
.lyrics-syllable.highlight
|
|
454
|
-
.lyrics-line.active:not(.lyrics-gap)
|
|
455
|
-
.lyrics-syllable.pre-highlight:not(:has(.char)) {
|
|
450
|
+
.lyrics-syllable.pre-highlight.no-chars {
|
|
456
451
|
background-repeat: no-repeat;
|
|
457
452
|
background-image:
|
|
458
453
|
linear-gradient(
|
|
@@ -494,11 +489,19 @@ export class AmLyrics extends LitElement {
|
|
|
494
489
|
right;
|
|
495
490
|
}
|
|
496
491
|
|
|
492
|
+
/* Non-growable words float up with a gentle curve */
|
|
497
493
|
.lyrics-line.active:not(.lyrics-gap)
|
|
498
494
|
.lyrics-word:not(.growable)
|
|
499
|
-
.lyrics-syllable.highlight
|
|
495
|
+
.lyrics-syllable.highlight {
|
|
496
|
+
transform: translateY(-3.5%);
|
|
497
|
+
transition:
|
|
498
|
+
transform var(--rise-duration, 1.5s) cubic-bezier(0.22, 1, 0.36, 1),
|
|
499
|
+
background-color 0.5s,
|
|
500
|
+
color 0.5s;
|
|
501
|
+
}
|
|
502
|
+
|
|
500
503
|
.lyrics-word.growable .lyrics-syllable.cleanup .char {
|
|
501
|
-
transform: translateY(-3.5%)
|
|
504
|
+
transform: translateY(-3.5%);
|
|
502
505
|
}
|
|
503
506
|
|
|
504
507
|
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable.highlight.finished {
|
|
@@ -525,7 +528,7 @@ export class AmLyrics extends LitElement {
|
|
|
525
528
|
}
|
|
526
529
|
|
|
527
530
|
/* Syllable with chars: make syllable transparent, chars handle color */
|
|
528
|
-
.lyrics-line .lyrics-syllable
|
|
531
|
+
.lyrics-line .lyrics-syllable.has-chars:not(.finished) {
|
|
529
532
|
background-color: transparent;
|
|
530
533
|
color: transparent;
|
|
531
534
|
}
|
|
@@ -538,6 +541,7 @@ export class AmLyrics extends LitElement {
|
|
|
538
541
|
font-feature-settings: 'liga' 0;
|
|
539
542
|
background-clip: text;
|
|
540
543
|
-webkit-background-clip: text;
|
|
544
|
+
backface-visibility: hidden;
|
|
541
545
|
transition:
|
|
542
546
|
color 0.7s,
|
|
543
547
|
background-color 0.7s,
|
|
@@ -573,11 +577,9 @@ export class AmLyrics extends LitElement {
|
|
|
573
577
|
-0.5em 0%,
|
|
574
578
|
-0.25em 0%;
|
|
575
579
|
transform-origin: 50% 80%;
|
|
576
|
-
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
|
577
580
|
transition:
|
|
578
581
|
transform 0.7s ease,
|
|
579
582
|
color 0.18s;
|
|
580
|
-
will-change: background, transform;
|
|
581
583
|
}
|
|
582
584
|
|
|
583
585
|
.lyrics-line.active .lyrics-syllable span.char.highlight {
|
|
@@ -629,6 +631,8 @@ export class AmLyrics extends LitElement {
|
|
|
629
631
|
box-sizing: content-box;
|
|
630
632
|
background-clip: unset;
|
|
631
633
|
transform-origin: top;
|
|
634
|
+
content-visibility: visible !important;
|
|
635
|
+
contain: none !important;
|
|
632
636
|
transition:
|
|
633
637
|
opacity 160ms ease-out,
|
|
634
638
|
transform var(--scroll-duration, 280ms) var(--lyrics-line-delay, 0ms);
|
|
@@ -639,41 +643,35 @@ export class AmLyrics extends LitElement {
|
|
|
639
643
|
transition:
|
|
640
644
|
opacity 160ms ease-out,
|
|
641
645
|
transform var(--scroll-duration, 280ms);
|
|
642
|
-
will-change: opacity;
|
|
643
646
|
}
|
|
644
647
|
|
|
645
648
|
/* Exiting state: quickly collapse width and height so dots don't distort page, or remove max-height transition */
|
|
646
649
|
.lyrics-gap.gap-exiting {
|
|
647
650
|
opacity: 1;
|
|
648
|
-
transition: transform var(--scroll-duration, 280ms);
|
|
649
651
|
}
|
|
650
652
|
|
|
651
653
|
.lyrics-gap .main-vocal-container {
|
|
652
|
-
transform: translateY(-25%) scale(1)
|
|
654
|
+
transform: translateY(-25%) scale(1);
|
|
653
655
|
transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
654
656
|
}
|
|
655
657
|
|
|
656
|
-
/* Jump animation plays during exit */
|
|
657
|
-
.lyrics-gap.gap-exiting .main-vocal-container {
|
|
658
|
-
animation: gap-ended var(--gap-exit-duration, 360ms)
|
|
659
|
-
cubic-bezier(0.33, 1, 0.68, 1) forwards;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
658
|
.lyrics-gap:not(.active):not(.gap-exiting) .main-vocal-container {
|
|
663
|
-
transform: translateY(-25%) scale(0)
|
|
659
|
+
transform: translateY(-25%) scale(0);
|
|
664
660
|
}
|
|
665
661
|
|
|
666
|
-
.
|
|
667
|
-
|
|
668
|
-
.lyrics-word {
|
|
669
|
-
animation-play-state: paused;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
.lyrics-gap.active .main-vocal-container .lyrics-word {
|
|
662
|
+
/* Pulse — must come BEFORE .gap-exiting so exiting wins via specificity+order */
|
|
663
|
+
.lyrics-gap.active .main-vocal-container {
|
|
673
664
|
animation: gap-loop var(--gap-pulse-duration, 4000ms) ease-in-out infinite
|
|
674
665
|
alternate;
|
|
675
666
|
animation-delay: var(--gap-loop-delay, 0ms);
|
|
676
|
-
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* Jump animation plays during exit — disable transition so animation wins.
|
|
670
|
+
Placed AFTER .active so it wins when both classes are present briefly. */
|
|
671
|
+
.lyrics-gap.gap-exiting .main-vocal-container {
|
|
672
|
+
animation: gap-ended var(--gap-exit-duration, 360ms)
|
|
673
|
+
cubic-bezier(0.33, 1, 0.68, 1) forwards;
|
|
674
|
+
transition: none !important;
|
|
677
675
|
}
|
|
678
676
|
|
|
679
677
|
.lyrics-gap .lyrics-syllable {
|
|
@@ -724,20 +722,17 @@ export class AmLyrics extends LitElement {
|
|
|
724
722
|
background-clip: unset;
|
|
725
723
|
}
|
|
726
724
|
|
|
727
|
-
.lyrics-gap.active .lyrics-syllable.highlight,
|
|
728
725
|
.lyrics-gap.active .lyrics-syllable.finished,
|
|
729
|
-
.lyrics-gap.gap-exiting .lyrics-syllable,
|
|
730
|
-
.lyrics-gap:not(.active).post-active-line
|
|
731
|
-
|
|
726
|
+
.lyrics-gap.gap-exiting .lyrics-syllable.finished,
|
|
727
|
+
.lyrics-gap:not(.active):not(.gap-exiting).post-active-line
|
|
728
|
+
.lyrics-syllable,
|
|
729
|
+
.lyrics-gap:not(.active):not(.gap-exiting).lyrics-activest
|
|
730
|
+
.lyrics-syllable {
|
|
732
731
|
background-color: var(--lyplus-text-primary);
|
|
733
732
|
animation: none !important;
|
|
734
733
|
opacity: 1;
|
|
735
734
|
}
|
|
736
735
|
|
|
737
|
-
.lyrics-gap.active .lyrics-syllable.finished {
|
|
738
|
-
animation: none !important;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
736
|
/* ==========================================================================
|
|
742
737
|
METADATA & FOOTER STYLES
|
|
743
738
|
========================================================================== */
|
|
@@ -766,12 +761,49 @@ export class AmLyrics extends LitElement {
|
|
|
766
761
|
align-items: center;
|
|
767
762
|
flex-wrap: wrap;
|
|
768
763
|
text-align: left;
|
|
769
|
-
font-size:
|
|
770
|
-
color: rgba(255, 255, 255, 0.
|
|
771
|
-
padding:
|
|
772
|
-
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
764
|
+
font-size: 1.2em;
|
|
765
|
+
color: rgba(255, 255, 255, 0.6);
|
|
766
|
+
padding: 20px 0 50vh 0;
|
|
773
767
|
margin-top: 10px;
|
|
774
|
-
font-weight:
|
|
768
|
+
font-weight: 400;
|
|
769
|
+
opacity: 0.8;
|
|
770
|
+
transition:
|
|
771
|
+
opacity 0.3s ease,
|
|
772
|
+
transform 0.5s cubic-bezier(0.41, 0, 0.12, 0.99),
|
|
773
|
+
filter 0.3s ease;
|
|
774
|
+
transform-origin: left;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.lyrics-footer.lyrics-line {
|
|
778
|
+
font-size: 1.2em;
|
|
779
|
+
padding: 20px var(--lyplus-padding-line) 50vh var(--lyplus-padding-line);
|
|
780
|
+
cursor: default;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.lyrics-footer.active {
|
|
784
|
+
opacity: 1;
|
|
785
|
+
color: rgba(255, 255, 255, 0.5); /* Grey instead of primary */
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.lyrics-footer.scroll-animate {
|
|
789
|
+
transition: none !important;
|
|
790
|
+
animation-name: lyrics-scroll;
|
|
791
|
+
animation-duration: var(--scroll-duration, 280ms);
|
|
792
|
+
animation-timing-function: cubic-bezier(0.41, 0, 0.12, 0.99);
|
|
793
|
+
animation-fill-mode: both;
|
|
794
|
+
animation-delay: var(--lyrics-line-delay, 0ms);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.lyrics-container.blur-inactive-enabled:not(.not-focused)
|
|
798
|
+
.lyrics-footer:not(.active) {
|
|
799
|
+
filter: blur(var(--lyplus-blur-amount));
|
|
800
|
+
opacity: 0.5;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.lyrics-container.user-scrolling .lyrics-footer {
|
|
804
|
+
transition: none !important;
|
|
805
|
+
filter: none !important;
|
|
806
|
+
opacity: 0.8 !important;
|
|
775
807
|
}
|
|
776
808
|
|
|
777
809
|
.lyrics-footer p {
|
|
@@ -779,12 +811,14 @@ export class AmLyrics extends LitElement {
|
|
|
779
811
|
}
|
|
780
812
|
|
|
781
813
|
.lyrics-footer a {
|
|
782
|
-
color:
|
|
783
|
-
text-
|
|
814
|
+
color: var(--lyplus-text-primary); /* Stand out using primary color */
|
|
815
|
+
text-underline-offset: 2px;
|
|
816
|
+
opacity: 0.8;
|
|
817
|
+
transition: opacity 0.2s;
|
|
784
818
|
}
|
|
785
819
|
|
|
786
820
|
.lyrics-footer a:hover {
|
|
787
|
-
|
|
821
|
+
opacity: 1;
|
|
788
822
|
}
|
|
789
823
|
|
|
790
824
|
.footer-content {
|
|
@@ -908,6 +942,7 @@ export class AmLyrics extends LitElement {
|
|
|
908
942
|
|
|
909
943
|
.lyrics-romanization-container.rtl-text {
|
|
910
944
|
direction: rtl !important;
|
|
945
|
+
text-align: right;
|
|
911
946
|
}
|
|
912
947
|
|
|
913
948
|
.lyrics-romanization-container .lyrics-syllable {
|
|
@@ -1121,23 +1156,22 @@ export class AmLyrics extends LitElement {
|
|
|
1121
1156
|
/* Gap dot animations */
|
|
1122
1157
|
@keyframes gap-loop {
|
|
1123
1158
|
from {
|
|
1124
|
-
transform: scale(1.12);
|
|
1159
|
+
transform: translateY(-25%) scale(1.12);
|
|
1125
1160
|
}
|
|
1126
1161
|
to {
|
|
1127
|
-
transform: scale(var(--gap-exit-scale, 0.85));
|
|
1162
|
+
transform: translateY(-25%) scale(var(--gap-exit-scale, 0.85));
|
|
1128
1163
|
}
|
|
1129
1164
|
}
|
|
1130
1165
|
|
|
1131
1166
|
@keyframes gap-ended {
|
|
1132
1167
|
0% {
|
|
1133
|
-
transform: translateY(-25%) scale(var(--gap-exit-scale, 0.85))
|
|
1134
|
-
translateZ(0);
|
|
1168
|
+
transform: translateY(-25%) scale(var(--gap-exit-scale, 0.85));
|
|
1135
1169
|
}
|
|
1136
1170
|
35% {
|
|
1137
|
-
transform: translateY(-
|
|
1171
|
+
transform: translateY(-25%) scale(1.2);
|
|
1138
1172
|
}
|
|
1139
1173
|
100% {
|
|
1140
|
-
transform: translateY(-25%) scale(0)
|
|
1174
|
+
transform: translateY(-25%) scale(0);
|
|
1141
1175
|
}
|
|
1142
1176
|
}
|
|
1143
1177
|
|
|
@@ -1154,17 +1188,18 @@ export class AmLyrics extends LitElement {
|
|
|
1154
1188
|
reflow in between) to reliably restart the animation each time */
|
|
1155
1189
|
@keyframes lyrics-scroll {
|
|
1156
1190
|
from {
|
|
1157
|
-
transform:
|
|
1191
|
+
transform: translate3d(0, var(--scroll-delta), 0);
|
|
1158
1192
|
}
|
|
1159
1193
|
to {
|
|
1160
|
-
transform:
|
|
1194
|
+
transform: translate3d(0, 0, 0);
|
|
1161
1195
|
}
|
|
1162
1196
|
}
|
|
1163
1197
|
|
|
1164
|
-
/* Character grow animation
|
|
1198
|
+
/* Character grow animation — translate3d+scale3d for smooth transform,
|
|
1199
|
+
drop-shadow for glow (text-shadow doesn't work with background-clip:text) */
|
|
1165
1200
|
@keyframes grow-dynamic {
|
|
1166
1201
|
0% {
|
|
1167
|
-
transform:
|
|
1202
|
+
transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
|
|
1168
1203
|
filter: drop-shadow(
|
|
1169
1204
|
0 0 0
|
|
1170
1205
|
color-mix(in srgb, var(--lyplus-lyrics-palette), transparent 100%)
|
|
@@ -1172,27 +1207,12 @@ export class AmLyrics extends LitElement {
|
|
|
1172
1207
|
}
|
|
1173
1208
|
25%,
|
|
1174
1209
|
30% {
|
|
1175
|
-
transform:
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
calc(var(--max-scale) * calc(var(--lyplus-font-size-base-grow) / 25)),
|
|
1182
|
-
0,
|
|
1183
|
-
0,
|
|
1184
|
-
0,
|
|
1185
|
-
0,
|
|
1186
|
-
1,
|
|
1187
|
-
0,
|
|
1188
|
-
calc(
|
|
1189
|
-
var(--char-offset-x, 0) *
|
|
1190
|
-
calc(var(--lyplus-font-size-base-grow) / 25)
|
|
1191
|
-
),
|
|
1192
|
-
var(--translate-y-peak, -2),
|
|
1193
|
-
0,
|
|
1194
|
-
1
|
|
1195
|
-
);
|
|
1210
|
+
transform: translate3d(
|
|
1211
|
+
var(--char-offset-x, 0px),
|
|
1212
|
+
var(--translate-y-peak, -2px),
|
|
1213
|
+
0
|
|
1214
|
+
)
|
|
1215
|
+
scale3d(var(--matrix-scale, 1.1), var(--matrix-scale, 1.1), 1);
|
|
1196
1216
|
filter: drop-shadow(
|
|
1197
1217
|
0 0 0.1em
|
|
1198
1218
|
color-mix(
|
|
@@ -1202,8 +1222,10 @@ export class AmLyrics extends LitElement {
|
|
|
1202
1222
|
)
|
|
1203
1223
|
);
|
|
1204
1224
|
}
|
|
1225
|
+
75%,
|
|
1205
1226
|
100% {
|
|
1206
|
-
transform:
|
|
1227
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0)
|
|
1228
|
+
scale3d(1, 1, 1);
|
|
1207
1229
|
filter: drop-shadow(
|
|
1208
1230
|
0 0 0
|
|
1209
1231
|
color-mix(in srgb, var(--lyplus-lyrics-palette), transparent 100%)
|
|
@@ -1336,17 +1358,17 @@ export class AmLyrics extends LitElement {
|
|
|
1336
1358
|
@property({ type: String, attribute: 'song-album' })
|
|
1337
1359
|
songAlbum?: string;
|
|
1338
1360
|
|
|
1361
|
+
@property({ type: String, attribute: 'songwriters' })
|
|
1362
|
+
songwriters?: string;
|
|
1363
|
+
|
|
1339
1364
|
@property({ type: Number, attribute: 'song-duration' })
|
|
1340
1365
|
songDurationMs?: number;
|
|
1341
1366
|
|
|
1342
1367
|
@property({ type: String, attribute: 'highlight-color' })
|
|
1343
1368
|
highlightColor = '#ffffff';
|
|
1344
1369
|
|
|
1345
|
-
@property({ type: String, attribute: 'hover-background-color' })
|
|
1346
|
-
hoverBackgroundColor = 'rgba(255, 255, 255, 0.13)';
|
|
1347
|
-
|
|
1348
1370
|
@property({ type: String, attribute: 'font-family' })
|
|
1349
|
-
fontFamily
|
|
1371
|
+
fontFamily: string | undefined;
|
|
1350
1372
|
|
|
1351
1373
|
@property({ type: Boolean })
|
|
1352
1374
|
autoScroll = true;
|
|
@@ -1440,6 +1462,42 @@ export class AmLyrics extends LitElement {
|
|
|
1440
1462
|
@property({ type: Number, attribute: 'currenttime', hasChanged: () => false })
|
|
1441
1463
|
set currentTime(value: number) {
|
|
1442
1464
|
const oldValue = this._currentTime;
|
|
1465
|
+
|
|
1466
|
+
// If the new time is significantly smaller than the old time (e.g. song looped)
|
|
1467
|
+
if (value < oldValue && oldValue - value > 1000 && this.lyrics) {
|
|
1468
|
+
this.activeLineIndices = [];
|
|
1469
|
+
this.activeMainWordIndices.clear();
|
|
1470
|
+
this.activeBackgroundWordIndices.clear();
|
|
1471
|
+
this.mainWordProgress.clear();
|
|
1472
|
+
this.backgroundWordProgress.clear();
|
|
1473
|
+
this.mainWordAnimations.clear();
|
|
1474
|
+
this.backgroundWordAnimations.clear();
|
|
1475
|
+
this.preActiveLineElements = [];
|
|
1476
|
+
this.positionedLineElements = [];
|
|
1477
|
+
this.activeGapLineElements = [];
|
|
1478
|
+
|
|
1479
|
+
// Stop all running animations and clear highlights immediately
|
|
1480
|
+
if (this.lyricsContainer) {
|
|
1481
|
+
const activeLines = this.lyricsContainer.querySelectorAll(
|
|
1482
|
+
'.lyrics-line.active, .lyrics-line.pre-active',
|
|
1483
|
+
);
|
|
1484
|
+
activeLines.forEach(line => {
|
|
1485
|
+
line.classList.remove('active', 'pre-active');
|
|
1486
|
+
AmLyrics.resetSyllables(line as HTMLElement);
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
const activeGaps = this.lyricsContainer.querySelectorAll(
|
|
1490
|
+
'.lyrics-gap.active, .lyrics-gap.gap-exiting',
|
|
1491
|
+
);
|
|
1492
|
+
activeGaps.forEach(gap =>
|
|
1493
|
+
gap.classList.remove('active', 'gap-exiting'),
|
|
1494
|
+
);
|
|
1495
|
+
|
|
1496
|
+
// Reset gap cache since we manually messed with the elements
|
|
1497
|
+
this.gapElementCache.clear();
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1443
1501
|
this._currentTime = value;
|
|
1444
1502
|
if (oldValue !== value && this.lyrics) {
|
|
1445
1503
|
this._onTimeChanged(oldValue, value);
|
|
@@ -1470,7 +1528,7 @@ export class AmLyrics extends LitElement {
|
|
|
1470
1528
|
private lyricsSource: string | null = null;
|
|
1471
1529
|
|
|
1472
1530
|
@state()
|
|
1473
|
-
private availableSources:
|
|
1531
|
+
private availableSources: YouLyPlusLyricsResult[] = [];
|
|
1474
1532
|
|
|
1475
1533
|
@state()
|
|
1476
1534
|
private currentSourceIndex = 0;
|
|
@@ -1556,6 +1614,7 @@ export class AmLyrics extends LitElement {
|
|
|
1556
1614
|
vwCharOffset: number[];
|
|
1557
1615
|
vwStartMs: number[];
|
|
1558
1616
|
vwEndMs: number[];
|
|
1617
|
+
lineIsRTL: boolean;
|
|
1559
1618
|
}> | null = null;
|
|
1560
1619
|
|
|
1561
1620
|
// Active line tracking
|
|
@@ -1587,6 +1646,13 @@ export class AmLyrics extends LitElement {
|
|
|
1587
1646
|
|
|
1588
1647
|
private visibleLineIds: Set<string> = new Set();
|
|
1589
1648
|
|
|
1649
|
+
// Cached element tracking to avoid repeated querySelectorAll calls
|
|
1650
|
+
private preActiveLineElements: HTMLElement[] = [];
|
|
1651
|
+
|
|
1652
|
+
private positionedLineElements: HTMLElement[] = [];
|
|
1653
|
+
|
|
1654
|
+
private activeGapLineElements: HTMLElement[] = [];
|
|
1655
|
+
|
|
1590
1656
|
// Bound handler references for proper event listener removal
|
|
1591
1657
|
private _boundHandleUserScroll = this.handleUserScroll.bind(this);
|
|
1592
1658
|
|
|
@@ -1633,6 +1699,9 @@ export class AmLyrics extends LitElement {
|
|
|
1633
1699
|
this._boundHandleUserScroll,
|
|
1634
1700
|
);
|
|
1635
1701
|
}
|
|
1702
|
+
this.preActiveLineElements = [];
|
|
1703
|
+
this.positionedLineElements = [];
|
|
1704
|
+
this.activeGapLineElements = [];
|
|
1636
1705
|
}
|
|
1637
1706
|
|
|
1638
1707
|
private async fetchLyrics() {
|
|
@@ -1661,7 +1730,7 @@ export class AmLyrics extends LitElement {
|
|
|
1661
1730
|
!this.query &&
|
|
1662
1731
|
!this.isrc;
|
|
1663
1732
|
|
|
1664
|
-
const collectedSources:
|
|
1733
|
+
const collectedSources: YouLyPlusLyricsResult[] = [];
|
|
1665
1734
|
|
|
1666
1735
|
if (resolvedMetadata?.metadata && !isMusicIdOnlyRequest) {
|
|
1667
1736
|
const title = resolvedMetadata.metadata.title?.trim() || '';
|
|
@@ -1680,20 +1749,7 @@ export class AmLyrics extends LitElement {
|
|
|
1680
1749
|
}
|
|
1681
1750
|
|
|
1682
1751
|
if (collectedSources.length === 0 && resolvedMetadata?.metadata) {
|
|
1683
|
-
|
|
1684
|
-
resolvedMetadata.metadata,
|
|
1685
|
-
resolvedMetadata.catalogIsrc,
|
|
1686
|
-
);
|
|
1687
|
-
if (tidalResult && tidalResult.lines.length > 0) {
|
|
1688
|
-
collectedSources.push({
|
|
1689
|
-
lines: tidalResult.lines,
|
|
1690
|
-
source: 'Tidal',
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// Fallback: LRCLIB
|
|
1696
|
-
if (collectedSources.length === 0 && resolvedMetadata?.metadata) {
|
|
1752
|
+
// Fallback: LRCLIB
|
|
1697
1753
|
const lrclibResult = await AmLyrics.fetchLyricsFromLrclib(
|
|
1698
1754
|
resolvedMetadata.metadata,
|
|
1699
1755
|
);
|
|
@@ -1720,10 +1776,7 @@ export class AmLyrics extends LitElement {
|
|
|
1720
1776
|
this.hasFetchedAllProviders =
|
|
1721
1777
|
collectedSources.length === 0 ||
|
|
1722
1778
|
collectedSources.some(
|
|
1723
|
-
s =>
|
|
1724
|
-
s.source === 'LRCLIB' ||
|
|
1725
|
-
s.source === 'Tidal' ||
|
|
1726
|
-
s.source === 'Genius',
|
|
1779
|
+
s => s.source === 'LRCLIB' || s.source === 'Genius',
|
|
1727
1780
|
);
|
|
1728
1781
|
this._updateFooter();
|
|
1729
1782
|
|
|
@@ -1731,8 +1784,12 @@ export class AmLyrics extends LitElement {
|
|
|
1731
1784
|
this.availableSources = AmLyrics.mergeAndSortSources(collectedSources);
|
|
1732
1785
|
|
|
1733
1786
|
this.currentSourceIndex = 0;
|
|
1734
|
-
|
|
1735
|
-
this.
|
|
1787
|
+
const sourceResult = this.availableSources[0];
|
|
1788
|
+
this.lyrics = sourceResult.lines;
|
|
1789
|
+
this.lyricsSource = sourceResult.source;
|
|
1790
|
+
if (sourceResult.songwriters) {
|
|
1791
|
+
this.songwriters = sourceResult.songwriters;
|
|
1792
|
+
}
|
|
1736
1793
|
await this.onLyricsLoaded();
|
|
1737
1794
|
return;
|
|
1738
1795
|
}
|
|
@@ -1755,6 +1812,9 @@ export class AmLyrics extends LitElement {
|
|
|
1755
1812
|
this.backgroundWordProgress.clear();
|
|
1756
1813
|
this.mainWordAnimations.clear();
|
|
1757
1814
|
this.backgroundWordAnimations.clear();
|
|
1815
|
+
this.preActiveLineElements = [];
|
|
1816
|
+
this.positionedLineElements = [];
|
|
1817
|
+
this.activeGapLineElements = [];
|
|
1758
1818
|
|
|
1759
1819
|
if (this.lyricsContainer) {
|
|
1760
1820
|
this.isProgrammaticScroll = true;
|
|
@@ -1795,23 +1855,20 @@ export class AmLyrics extends LitElement {
|
|
|
1795
1855
|
if (lower.includes('apple') && hasWordSync) return 1;
|
|
1796
1856
|
if (isQQ && hasWordSync) return 2;
|
|
1797
1857
|
if (lower.includes('musixmatch') && hasWordSync) return 3;
|
|
1798
|
-
if (lower.includes('
|
|
1799
|
-
if (
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
if (
|
|
1803
|
-
if (
|
|
1804
|
-
if (lower.includes('
|
|
1805
|
-
if (
|
|
1806
|
-
|
|
1807
|
-
if (
|
|
1808
|
-
|
|
1809
|
-
if (lower.includes('
|
|
1810
|
-
if (
|
|
1811
|
-
if (lower.includes('
|
|
1812
|
-
if (lower.includes('tidal') && isUnsynced) return 16;
|
|
1813
|
-
if (lower.includes('lrclib') && isUnsynced) return 17;
|
|
1814
|
-
if (lower.includes('genius')) return 18;
|
|
1858
|
+
if (lower.includes('lrclib') && hasWordSync) return 4;
|
|
1859
|
+
if (hasWordSync) return 5;
|
|
1860
|
+
|
|
1861
|
+
if (lower.includes('apple') && !hasWordSync && !isUnsynced) return 6;
|
|
1862
|
+
if (isQQ && !hasWordSync && !isUnsynced) return 7;
|
|
1863
|
+
if (lower.includes('musixmatch') && !hasWordSync && !isUnsynced) return 8;
|
|
1864
|
+
if (lower.includes('lrclib') && !hasWordSync && !isUnsynced) return 9;
|
|
1865
|
+
if (!hasWordSync && !isUnsynced) return 10;
|
|
1866
|
+
|
|
1867
|
+
if (lower.includes('apple') && isUnsynced) return 11;
|
|
1868
|
+
if (isQQ && isUnsynced) return 12;
|
|
1869
|
+
if (lower.includes('musixmatch') && isUnsynced) return 13;
|
|
1870
|
+
if (lower.includes('lrclib') && isUnsynced) return 14;
|
|
1871
|
+
if (lower.includes('genius')) return 15;
|
|
1815
1872
|
|
|
1816
1873
|
return 20;
|
|
1817
1874
|
}
|
|
@@ -1855,22 +1912,7 @@ export class AmLyrics extends LitElement {
|
|
|
1855
1912
|
try {
|
|
1856
1913
|
const resolvedMetadata = await this.resolveSongMetadata();
|
|
1857
1914
|
if (resolvedMetadata?.metadata) {
|
|
1858
|
-
const newSources:
|
|
1859
|
-
|
|
1860
|
-
// Try Tidal if not fetched
|
|
1861
|
-
if (
|
|
1862
|
-
!this.availableSources.some(s =>
|
|
1863
|
-
s.source.toLowerCase().includes('tidal'),
|
|
1864
|
-
)
|
|
1865
|
-
) {
|
|
1866
|
-
const tidalResult = await AmLyrics.fetchLyricsFromTidal(
|
|
1867
|
-
resolvedMetadata.metadata,
|
|
1868
|
-
resolvedMetadata.catalogIsrc,
|
|
1869
|
-
);
|
|
1870
|
-
if (tidalResult && tidalResult.lines.length > 0) {
|
|
1871
|
-
newSources.push({ lines: tidalResult.lines, source: 'Tidal' });
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1915
|
+
const newSources: YouLyPlusLyricsResult[] = [];
|
|
1874
1916
|
|
|
1875
1917
|
// Try LRCLIB if not fetched
|
|
1876
1918
|
if (
|
|
@@ -1921,8 +1963,12 @@ export class AmLyrics extends LitElement {
|
|
|
1921
1963
|
if (this.availableSources.length > 1) {
|
|
1922
1964
|
this.currentSourceIndex =
|
|
1923
1965
|
(this.currentSourceIndex + 1) % this.availableSources.length;
|
|
1924
|
-
|
|
1925
|
-
this.
|
|
1966
|
+
const sourceResult = this.availableSources[this.currentSourceIndex];
|
|
1967
|
+
this.lyrics = sourceResult.lines;
|
|
1968
|
+
this.lyricsSource = sourceResult.source;
|
|
1969
|
+
if (sourceResult.songwriters) {
|
|
1970
|
+
this.songwriters = sourceResult.songwriters;
|
|
1971
|
+
}
|
|
1926
1972
|
await this.onLyricsLoaded();
|
|
1927
1973
|
}
|
|
1928
1974
|
}
|
|
@@ -1932,6 +1978,7 @@ export class AmLyrics extends LitElement {
|
|
|
1932
1978
|
title: this.songTitle?.trim() ?? '',
|
|
1933
1979
|
artist: this.songArtist?.trim() ?? '',
|
|
1934
1980
|
album: this.songAlbum?.trim() || undefined,
|
|
1981
|
+
songwriters: this.songwriters?.trim() || undefined,
|
|
1935
1982
|
durationMs: undefined,
|
|
1936
1983
|
};
|
|
1937
1984
|
|
|
@@ -1978,6 +2025,9 @@ export class AmLyrics extends LitElement {
|
|
|
1978
2025
|
if (!metadata.album && catalogResult.album) {
|
|
1979
2026
|
metadata.album = catalogResult.album;
|
|
1980
2027
|
}
|
|
2028
|
+
if (!metadata.songwriters && catalogResult.songwriters) {
|
|
2029
|
+
metadata.songwriters = catalogResult.songwriters;
|
|
2030
|
+
}
|
|
1981
2031
|
if (
|
|
1982
2032
|
metadata.durationMs == null &&
|
|
1983
2033
|
typeof catalogResult.durationMs === 'number' &&
|
|
@@ -2214,9 +2264,13 @@ export class AmLyrics extends LitElement {
|
|
|
2214
2264
|
const ttmlRes = await fetchWithTimeout(result.lyricsUrl);
|
|
2215
2265
|
if (ttmlRes.ok) {
|
|
2216
2266
|
const ttmlText = await ttmlRes.text();
|
|
2217
|
-
const
|
|
2218
|
-
if (
|
|
2219
|
-
allResults.push({
|
|
2267
|
+
const parseResult = AmLyrics.parseTTML(ttmlText);
|
|
2268
|
+
if (parseResult && parseResult.lines.length > 0) {
|
|
2269
|
+
allResults.push({
|
|
2270
|
+
lines: parseResult.lines,
|
|
2271
|
+
source: 'BiniLyrics',
|
|
2272
|
+
songwriters: parseResult.songwriters,
|
|
2273
|
+
});
|
|
2220
2274
|
return allResults;
|
|
2221
2275
|
}
|
|
2222
2276
|
}
|
|
@@ -2251,11 +2305,12 @@ export class AmLyrics extends LitElement {
|
|
|
2251
2305
|
const ttmlRes = await fetchWithTimeout(result.lyricsUrl);
|
|
2252
2306
|
if (ttmlRes.ok) {
|
|
2253
2307
|
const ttmlText = await ttmlRes.text();
|
|
2254
|
-
const
|
|
2255
|
-
if (
|
|
2308
|
+
const parseResult = AmLyrics.parseTTML(ttmlText);
|
|
2309
|
+
if (parseResult && parseResult.lines.length > 0) {
|
|
2256
2310
|
allResults.push({
|
|
2257
|
-
lines,
|
|
2311
|
+
lines: parseResult.lines,
|
|
2258
2312
|
source: 'BiniLyrics',
|
|
2313
|
+
songwriters: parseResult.songwriters,
|
|
2259
2314
|
});
|
|
2260
2315
|
return allResults;
|
|
2261
2316
|
}
|
|
@@ -2412,100 +2467,6 @@ export class AmLyrics extends LitElement {
|
|
|
2412
2467
|
return lines;
|
|
2413
2468
|
}
|
|
2414
2469
|
|
|
2415
|
-
/**
|
|
2416
|
-
* Fetch lyrics from Tidal API.
|
|
2417
|
-
* Picks 2 random servers, tries search + lyrics on each.
|
|
2418
|
-
*/
|
|
2419
|
-
private static async fetchLyricsFromTidal(
|
|
2420
|
-
metadata: SongMetadata,
|
|
2421
|
-
isrc?: string,
|
|
2422
|
-
): Promise<YouLyPlusLyricsResult | null> {
|
|
2423
|
-
const title = metadata.title?.trim();
|
|
2424
|
-
const artist = metadata.artist?.trim();
|
|
2425
|
-
|
|
2426
|
-
if (!title || !artist) return null;
|
|
2427
|
-
|
|
2428
|
-
// Pick 3 random unique servers for better reliability
|
|
2429
|
-
const shuffled = [...TIDAL_SERVERS].sort(() => Math.random() - 0.5);
|
|
2430
|
-
const serversToTry = shuffled.slice(0, 3);
|
|
2431
|
-
|
|
2432
|
-
for (const base of serversToTry) {
|
|
2433
|
-
try {
|
|
2434
|
-
const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
2435
|
-
|
|
2436
|
-
// Step 1: Search for the track
|
|
2437
|
-
const searchQuery = `${title} ${artist}`;
|
|
2438
|
-
const searchParams = new URLSearchParams({ s: searchQuery });
|
|
2439
|
-
// eslint-disable-next-line no-await-in-loop
|
|
2440
|
-
const searchResponse = await fetchWithTimeout(
|
|
2441
|
-
`${normalizedBase}/search/?${searchParams.toString()}`,
|
|
2442
|
-
);
|
|
2443
|
-
|
|
2444
|
-
if (!searchResponse.ok) {
|
|
2445
|
-
// eslint-disable-next-line no-continue
|
|
2446
|
-
continue;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
// eslint-disable-next-line no-await-in-loop
|
|
2450
|
-
const searchData = await searchResponse.json();
|
|
2451
|
-
const items = searchData?.data?.items;
|
|
2452
|
-
|
|
2453
|
-
if (!Array.isArray(items) || items.length === 0) {
|
|
2454
|
-
// eslint-disable-next-line no-continue
|
|
2455
|
-
continue;
|
|
2456
|
-
}
|
|
2457
|
-
|
|
2458
|
-
// Find best match: prefer ISRC match, then first result
|
|
2459
|
-
let bestTrack = items[0];
|
|
2460
|
-
if (isrc) {
|
|
2461
|
-
const isrcMatch = items.find(
|
|
2462
|
-
(item: any) =>
|
|
2463
|
-
item.isrc && item.isrc.toLowerCase() === isrc.toLowerCase(),
|
|
2464
|
-
);
|
|
2465
|
-
if (isrcMatch) {
|
|
2466
|
-
bestTrack = isrcMatch;
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
const trackId = bestTrack?.id;
|
|
2471
|
-
if (!trackId) {
|
|
2472
|
-
// eslint-disable-next-line no-continue
|
|
2473
|
-
continue;
|
|
2474
|
-
}
|
|
2475
|
-
|
|
2476
|
-
// Step 2: Fetch lyrics
|
|
2477
|
-
// eslint-disable-next-line no-await-in-loop
|
|
2478
|
-
const lyricsResponse = await fetchWithTimeout(
|
|
2479
|
-
`${normalizedBase}/lyrics/?id=${trackId}`,
|
|
2480
|
-
);
|
|
2481
|
-
|
|
2482
|
-
if (!lyricsResponse.ok) {
|
|
2483
|
-
// eslint-disable-next-line no-continue
|
|
2484
|
-
continue;
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
// eslint-disable-next-line no-await-in-loop
|
|
2488
|
-
const lyricsData = await lyricsResponse.json();
|
|
2489
|
-
const subtitles = lyricsData?.lyrics?.subtitles;
|
|
2490
|
-
|
|
2491
|
-
if (subtitles && typeof subtitles === 'string') {
|
|
2492
|
-
const lines = AmLyrics.parseLrcSubtitles(subtitles);
|
|
2493
|
-
if (lines.length > 0) {
|
|
2494
|
-
const provider = lyricsData?.lyrics?.lyricsProvider || 'Tidal';
|
|
2495
|
-
return {
|
|
2496
|
-
lines,
|
|
2497
|
-
source: `Tidal (${provider})`,
|
|
2498
|
-
};
|
|
2499
|
-
}
|
|
2500
|
-
}
|
|
2501
|
-
} catch {
|
|
2502
|
-
// Try next server
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
return null;
|
|
2507
|
-
}
|
|
2508
|
-
|
|
2509
2470
|
/**
|
|
2510
2471
|
* Fetch lyrics from LRCLIB.
|
|
2511
2472
|
* Uses search endpoint, prefers synced lyrics.
|
|
@@ -2701,7 +2662,9 @@ export class AmLyrics extends LitElement {
|
|
|
2701
2662
|
return lineSideAssignments;
|
|
2702
2663
|
}
|
|
2703
2664
|
|
|
2704
|
-
private static parseTTML(
|
|
2665
|
+
private static parseTTML(
|
|
2666
|
+
ttmlString: string,
|
|
2667
|
+
): { lines: LyricsLine[]; songwriters?: string } | null {
|
|
2705
2668
|
try {
|
|
2706
2669
|
const parser = new DOMParser();
|
|
2707
2670
|
const doc = parser.parseFromString(ttmlString, 'text/xml');
|
|
@@ -2720,6 +2683,20 @@ export class AmLyrics extends LitElement {
|
|
|
2720
2683
|
}
|
|
2721
2684
|
}
|
|
2722
2685
|
|
|
2686
|
+
let songwriters: string | undefined;
|
|
2687
|
+
const songwritersNodes = doc.getElementsByTagName('songwriter');
|
|
2688
|
+
if (songwritersNodes.length > 0) {
|
|
2689
|
+
const names: string[] = [];
|
|
2690
|
+
for (let i = 0; i < songwritersNodes.length; i += 1) {
|
|
2691
|
+
if (songwritersNodes[i].textContent) {
|
|
2692
|
+
names.push(songwritersNodes[i].textContent!);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (names.length > 0) {
|
|
2696
|
+
songwriters = names.join(', ');
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2723
2700
|
const translationNodes = doc.getElementsByTagName('translation');
|
|
2724
2701
|
for (let i = 0; i < translationNodes.length; i += 1) {
|
|
2725
2702
|
const texts = translationNodes[i].getElementsByTagName('text');
|
|
@@ -2853,7 +2830,7 @@ export class AmLyrics extends LitElement {
|
|
|
2853
2830
|
text: bgText,
|
|
2854
2831
|
timestamp: timeToMs(bgSpan.getAttribute('begin')),
|
|
2855
2832
|
endtime: timeToMs(bgSpan.getAttribute('end')),
|
|
2856
|
-
part:
|
|
2833
|
+
part: !/\s$/.test(bgText),
|
|
2857
2834
|
});
|
|
2858
2835
|
}
|
|
2859
2836
|
// eslint-disable-next-line no-continue
|
|
@@ -2882,7 +2859,7 @@ export class AmLyrics extends LitElement {
|
|
|
2882
2859
|
text,
|
|
2883
2860
|
timestamp: timeToMs(span.getAttribute('begin')),
|
|
2884
2861
|
endtime: timeToMs(span.getAttribute('end')),
|
|
2885
|
-
part:
|
|
2862
|
+
part: !/\s$/.test(text),
|
|
2886
2863
|
});
|
|
2887
2864
|
}
|
|
2888
2865
|
} else {
|
|
@@ -2980,7 +2957,7 @@ export class AmLyrics extends LitElement {
|
|
|
2980
2957
|
});
|
|
2981
2958
|
}
|
|
2982
2959
|
|
|
2983
|
-
return lines;
|
|
2960
|
+
return { lines, songwriters };
|
|
2984
2961
|
} catch (e) {
|
|
2985
2962
|
// eslint-disable-next-line no-console
|
|
2986
2963
|
console.error('Failed to parse TTML', e);
|
|
@@ -3191,7 +3168,9 @@ export class AmLyrics extends LitElement {
|
|
|
3191
3168
|
if (!newActiveLines.includes(lineIndex)) {
|
|
3192
3169
|
const lineElement = this._getLineElement(lineIndex);
|
|
3193
3170
|
if (lineElement) {
|
|
3194
|
-
lineElement.classList.remove('active');
|
|
3171
|
+
lineElement.classList.remove('active', 'pre-active');
|
|
3172
|
+
const preIdx = this.preActiveLineElements.indexOf(lineElement);
|
|
3173
|
+
if (preIdx !== -1) this.preActiveLineElements.splice(preIdx, 1);
|
|
3195
3174
|
AmLyrics.resetSyllables(lineElement);
|
|
3196
3175
|
}
|
|
3197
3176
|
}
|
|
@@ -3203,6 +3182,8 @@ export class AmLyrics extends LitElement {
|
|
|
3203
3182
|
if (lineElement) {
|
|
3204
3183
|
lineElement.classList.add('active');
|
|
3205
3184
|
lineElement.classList.remove('pre-active');
|
|
3185
|
+
const preIdx = this.preActiveLineElements.indexOf(lineElement);
|
|
3186
|
+
if (preIdx !== -1) this.preActiveLineElements.splice(preIdx, 1);
|
|
3206
3187
|
}
|
|
3207
3188
|
}
|
|
3208
3189
|
}
|
|
@@ -3227,11 +3208,9 @@ export class AmLyrics extends LitElement {
|
|
|
3227
3208
|
}
|
|
3228
3209
|
|
|
3229
3210
|
// Also update syllables in active gap lines (breathing dots)
|
|
3230
|
-
const
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
AmLyrics.updateSyllablesForLine(gapLine as HTMLElement, newTime);
|
|
3234
|
-
});
|
|
3211
|
+
for (const gapLine of this.activeGapLineElements) {
|
|
3212
|
+
AmLyrics.updateSyllablesForLine(gapLine, newTime);
|
|
3213
|
+
}
|
|
3235
3214
|
|
|
3236
3215
|
// Imperatively manage gap active state
|
|
3237
3216
|
if (this.gapElementCache.size > 0) {
|
|
@@ -3250,9 +3229,24 @@ export class AmLyrics extends LitElement {
|
|
|
3250
3229
|
const shouldStartExiting =
|
|
3251
3230
|
isActive && !isExiting && newTime >= gapEndTime - exitLeadMs;
|
|
3252
3231
|
|
|
3253
|
-
if (shouldBeActive && !isActive && !isExiting) {
|
|
3232
|
+
if (shouldBeActive && (!isActive || isSeek) && !isExiting) {
|
|
3254
3233
|
gap.classList.remove('gap-exiting');
|
|
3234
|
+
if (isSeek && isActive) {
|
|
3235
|
+
gap.classList.remove('active');
|
|
3236
|
+
// eslint-disable-next-line no-void
|
|
3237
|
+
void (gap as HTMLElement).offsetWidth; // Force reflow
|
|
3238
|
+
}
|
|
3239
|
+
const gapDuration = gapEndTime - gapStartTime;
|
|
3240
|
+
const baseLoopDelay = AmLyrics.getGapLoopDelay(gapDuration);
|
|
3241
|
+
const totalDelay = baseLoopDelay + (newTime - gapStartTime);
|
|
3242
|
+
(gap as HTMLElement).style.setProperty(
|
|
3243
|
+
'--gap-loop-delay',
|
|
3244
|
+
`-${totalDelay}ms`,
|
|
3245
|
+
);
|
|
3255
3246
|
gap.classList.add('active');
|
|
3247
|
+
if (!this.activeGapLineElements.includes(gap as HTMLElement)) {
|
|
3248
|
+
this.activeGapLineElements.push(gap as HTMLElement);
|
|
3249
|
+
}
|
|
3256
3250
|
const dotSyllables = gap.querySelectorAll('.lyrics-syllable');
|
|
3257
3251
|
dotSyllables.forEach(dot => {
|
|
3258
3252
|
const dotStart = parseFloat(
|
|
@@ -3264,21 +3258,39 @@ export class AmLyrics extends LitElement {
|
|
|
3264
3258
|
if (newTime > dotEnd) {
|
|
3265
3259
|
dot.classList.add('finished');
|
|
3266
3260
|
if (!dot.classList.contains('highlight')) {
|
|
3267
|
-
AmLyrics.updateSyllableAnimation(
|
|
3261
|
+
AmLyrics.updateSyllableAnimation(
|
|
3262
|
+
dot as HTMLElement,
|
|
3263
|
+
newTime - dotStart,
|
|
3264
|
+
);
|
|
3268
3265
|
}
|
|
3269
3266
|
} else if (newTime >= dotStart && newTime <= dotEnd) {
|
|
3270
|
-
AmLyrics.updateSyllableAnimation(
|
|
3267
|
+
AmLyrics.updateSyllableAnimation(
|
|
3268
|
+
dot as HTMLElement,
|
|
3269
|
+
newTime - dotStart,
|
|
3270
|
+
);
|
|
3271
3271
|
}
|
|
3272
3272
|
});
|
|
3273
3273
|
} else if (shouldStartExiting) {
|
|
3274
|
-
gap
|
|
3274
|
+
// Cancel gap-loop first, force reflow, then start gap-ended
|
|
3275
|
+
// so the browser sees a clean animation swap
|
|
3275
3276
|
gap.classList.remove('active');
|
|
3277
|
+
// eslint-disable-next-line no-void
|
|
3278
|
+
void (gap as HTMLElement).offsetWidth;
|
|
3279
|
+
gap.classList.add('gap-exiting');
|
|
3280
|
+
const gapIdx = this.activeGapLineElements.indexOf(
|
|
3281
|
+
gap as HTMLElement,
|
|
3282
|
+
);
|
|
3283
|
+
if (gapIdx !== -1) this.activeGapLineElements.splice(gapIdx, 1);
|
|
3276
3284
|
setTimeout(() => {
|
|
3277
3285
|
gap.classList.remove('gap-exiting');
|
|
3278
3286
|
}, GAP_EXIT_LEAD_MS);
|
|
3279
|
-
} else if (
|
|
3287
|
+
} else if (!shouldBeActive && (isActive || isExiting)) {
|
|
3280
3288
|
gap.classList.remove('active');
|
|
3281
3289
|
gap.classList.remove('gap-exiting');
|
|
3290
|
+
const gapIdx = this.activeGapLineElements.indexOf(
|
|
3291
|
+
gap as HTMLElement,
|
|
3292
|
+
);
|
|
3293
|
+
if (gapIdx !== -1) this.activeGapLineElements.splice(gapIdx, 1);
|
|
3282
3294
|
} else if (isExiting && newTime < gapEndTime - exitLeadMs) {
|
|
3283
3295
|
gap.classList.remove('gap-exiting');
|
|
3284
3296
|
}
|
|
@@ -3301,18 +3313,44 @@ export class AmLyrics extends LitElement {
|
|
|
3301
3313
|
const shouldStartExiting =
|
|
3302
3314
|
isActive && !isExiting && newTime >= gapEndTime - exitLeadMs;
|
|
3303
3315
|
|
|
3304
|
-
if (shouldBeActive && !isActive && !isExiting) {
|
|
3316
|
+
if (shouldBeActive && (!isActive || isSeek) && !isExiting) {
|
|
3305
3317
|
gap.classList.remove('gap-exiting');
|
|
3318
|
+
if (isSeek && isActive) {
|
|
3319
|
+
gap.classList.remove('active');
|
|
3320
|
+
// eslint-disable-next-line no-void
|
|
3321
|
+
void (gap as HTMLElement).offsetWidth; // Force reflow
|
|
3322
|
+
}
|
|
3323
|
+
const gapDuration = gapEndTime - gapStartTime;
|
|
3324
|
+
const baseLoopDelay = AmLyrics.getGapLoopDelay(gapDuration);
|
|
3325
|
+
const totalDelay = baseLoopDelay + (newTime - gapStartTime);
|
|
3326
|
+
(gap as HTMLElement).style.setProperty(
|
|
3327
|
+
'--gap-loop-delay',
|
|
3328
|
+
`-${totalDelay}ms`,
|
|
3329
|
+
);
|
|
3306
3330
|
gap.classList.add('active');
|
|
3331
|
+
if (!this.activeGapLineElements.includes(gap as HTMLElement)) {
|
|
3332
|
+
this.activeGapLineElements.push(gap as HTMLElement);
|
|
3333
|
+
}
|
|
3307
3334
|
} else if (shouldStartExiting) {
|
|
3308
|
-
gap
|
|
3335
|
+
// Cancel gap-loop first, force reflow, then start gap-ended
|
|
3309
3336
|
gap.classList.remove('active');
|
|
3337
|
+
// eslint-disable-next-line no-void
|
|
3338
|
+
void (gap as HTMLElement).offsetWidth;
|
|
3339
|
+
gap.classList.add('gap-exiting');
|
|
3340
|
+
const gapIdx = this.activeGapLineElements.indexOf(
|
|
3341
|
+
gap as HTMLElement,
|
|
3342
|
+
);
|
|
3343
|
+
if (gapIdx !== -1) this.activeGapLineElements.splice(gapIdx, 1);
|
|
3310
3344
|
setTimeout(() => {
|
|
3311
3345
|
gap.classList.remove('gap-exiting');
|
|
3312
3346
|
}, GAP_EXIT_LEAD_MS);
|
|
3313
|
-
} else if (
|
|
3347
|
+
} else if (!shouldBeActive && (isActive || isExiting)) {
|
|
3314
3348
|
gap.classList.remove('active');
|
|
3315
3349
|
gap.classList.remove('gap-exiting');
|
|
3350
|
+
const gapIdx = this.activeGapLineElements.indexOf(
|
|
3351
|
+
gap as HTMLElement,
|
|
3352
|
+
);
|
|
3353
|
+
if (gapIdx !== -1) this.activeGapLineElements.splice(gapIdx, 1);
|
|
3316
3354
|
} else if (isExiting && newTime < gapEndTime - exitLeadMs) {
|
|
3317
3355
|
gap.classList.remove('gap-exiting');
|
|
3318
3356
|
}
|
|
@@ -3327,6 +3365,30 @@ export class AmLyrics extends LitElement {
|
|
|
3327
3365
|
this.lastInstrumentalIndex = null;
|
|
3328
3366
|
}
|
|
3329
3367
|
|
|
3368
|
+
// Check footer active state
|
|
3369
|
+
const lastLyric =
|
|
3370
|
+
this.lyrics && this.lyrics.length > 0
|
|
3371
|
+
? this.lyrics[this.lyrics.length - 1]
|
|
3372
|
+
: null;
|
|
3373
|
+
const footer = this.lyricsContainer.querySelector(
|
|
3374
|
+
'.lyrics-footer',
|
|
3375
|
+
) as HTMLElement;
|
|
3376
|
+
if (footer && lastLyric && lastLyric.endtime > 0) {
|
|
3377
|
+
const isFooterActive = newTime > lastLyric.endtime + 200; // Snappier 200ms buffer
|
|
3378
|
+
if (isFooterActive && !footer.classList.contains('active')) {
|
|
3379
|
+
footer.classList.add('active');
|
|
3380
|
+
if (
|
|
3381
|
+
this.autoScroll &&
|
|
3382
|
+
!this.isUserScrolling &&
|
|
3383
|
+
!this.isClickSeeking
|
|
3384
|
+
) {
|
|
3385
|
+
this.focusLine(footer);
|
|
3386
|
+
}
|
|
3387
|
+
} else if (!isFooterActive && footer.classList.contains('active')) {
|
|
3388
|
+
footer.classList.remove('active');
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3330
3392
|
// Pre-scroll: scroll to upcoming line ~0.5s before it starts
|
|
3331
3393
|
if (
|
|
3332
3394
|
this.autoScroll &&
|
|
@@ -3357,6 +3419,9 @@ export class AmLyrics extends LitElement {
|
|
|
3357
3419
|
|
|
3358
3420
|
if (!isBackToBack) {
|
|
3359
3421
|
nextLineEl.classList.add('pre-active');
|
|
3422
|
+
if (!this.preActiveLineElements.includes(nextLineEl)) {
|
|
3423
|
+
this.preActiveLineElements.push(nextLineEl);
|
|
3424
|
+
}
|
|
3360
3425
|
}
|
|
3361
3426
|
this.clearPreActiveClasses(i);
|
|
3362
3427
|
|
|
@@ -3396,6 +3461,10 @@ export class AmLyrics extends LitElement {
|
|
|
3396
3461
|
const lineEl = this._getLineElement(lineIndex);
|
|
3397
3462
|
if (lineEl) lineEl.classList.add('active');
|
|
3398
3463
|
}
|
|
3464
|
+
|
|
3465
|
+
// Trigger a faux time-change so that updateSyllablesForLine fires
|
|
3466
|
+
// to setup inline syllable CSS wipe animations for whatever the current time is
|
|
3467
|
+
this._onTimeChanged(0, this.currentTime);
|
|
3399
3468
|
}
|
|
3400
3469
|
}
|
|
3401
3470
|
|
|
@@ -3409,6 +3478,9 @@ export class AmLyrics extends LitElement {
|
|
|
3409
3478
|
this.backgroundWordProgress.clear();
|
|
3410
3479
|
this.mainWordAnimations.clear();
|
|
3411
3480
|
this.backgroundWordAnimations.clear();
|
|
3481
|
+
this.preActiveLineElements = [];
|
|
3482
|
+
this.positionedLineElements = [];
|
|
3483
|
+
this.activeGapLineElements = [];
|
|
3412
3484
|
this.setUserScrolling(false);
|
|
3413
3485
|
|
|
3414
3486
|
// Cancel any running animations
|
|
@@ -3487,8 +3559,8 @@ export class AmLyrics extends LitElement {
|
|
|
3487
3559
|
this.lyrics[prevPrimaryIndex].endtime;
|
|
3488
3560
|
if (gap > 200) {
|
|
3489
3561
|
scrollDuration = Math.min(
|
|
3490
|
-
Math.max(gap * 0.
|
|
3491
|
-
|
|
3562
|
+
Math.max(gap * 0.85, SCROLL_ANIMATION_DURATION_MS),
|
|
3563
|
+
4000,
|
|
3492
3564
|
);
|
|
3493
3565
|
}
|
|
3494
3566
|
}
|
|
@@ -3563,6 +3635,9 @@ export class AmLyrics extends LitElement {
|
|
|
3563
3635
|
this.cachedLineData = null;
|
|
3564
3636
|
this.lineElementCache.clear();
|
|
3565
3637
|
this.gapElementCache.clear();
|
|
3638
|
+
this.preActiveLineElements = [];
|
|
3639
|
+
this.positionedLineElements = [];
|
|
3640
|
+
this.activeGapLineElements = [];
|
|
3566
3641
|
}
|
|
3567
3642
|
|
|
3568
3643
|
private _updateCachedIsUnsynced() {
|
|
@@ -3577,12 +3652,28 @@ export class AmLyrics extends LitElement {
|
|
|
3577
3652
|
|
|
3578
3653
|
this.cachedLineData = this.lyrics.map(line => {
|
|
3579
3654
|
const wordGroups: Syllable[][] = [];
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3655
|
+
let currentGroupBuffer: Syllable[] = [];
|
|
3656
|
+
|
|
3657
|
+
line.text.forEach((syllable, idx) => {
|
|
3658
|
+
currentGroupBuffer.push(syllable);
|
|
3659
|
+
const nextSyllable = line.text[idx + 1];
|
|
3660
|
+
|
|
3661
|
+
const endsWithDelimiter =
|
|
3662
|
+
!nextSyllable ||
|
|
3663
|
+
syllable.part === false ||
|
|
3664
|
+
/\s$/.test(syllable.text) ||
|
|
3665
|
+
(nextSyllable &&
|
|
3666
|
+
(syllable as any).isBackground !==
|
|
3667
|
+
(nextSyllable as any).isBackground);
|
|
3668
|
+
|
|
3669
|
+
if (endsWithDelimiter) {
|
|
3670
|
+
wordGroups.push(currentGroupBuffer);
|
|
3671
|
+
currentGroupBuffer = [];
|
|
3585
3672
|
}
|
|
3673
|
+
});
|
|
3674
|
+
|
|
3675
|
+
if (currentGroupBuffer.length > 0) {
|
|
3676
|
+
wordGroups.push(currentGroupBuffer);
|
|
3586
3677
|
}
|
|
3587
3678
|
|
|
3588
3679
|
const groupGrowable: boolean[] = new Array(wordGroups.length).fill(false);
|
|
@@ -3593,6 +3684,7 @@ export class AmLyrics extends LitElement {
|
|
|
3593
3684
|
const vwStartMs: number[] = new Array(wordGroups.length).fill(0);
|
|
3594
3685
|
const vwEndMs: number[] = new Array(wordGroups.length).fill(0);
|
|
3595
3686
|
|
|
3687
|
+
let lineIsRTL = false;
|
|
3596
3688
|
let vwStart = 0;
|
|
3597
3689
|
while (vwStart < wordGroups.length) {
|
|
3598
3690
|
let vwEnd = vwStart;
|
|
@@ -3621,22 +3713,25 @@ export class AmLyrics extends LitElement {
|
|
|
3621
3713
|
/[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\u0590-\u05FF]/.test(
|
|
3622
3714
|
combinedText,
|
|
3623
3715
|
);
|
|
3716
|
+
if (isRTL) lineIsRTL = true;
|
|
3624
3717
|
const hasHyphen = combinedText.includes('-');
|
|
3625
3718
|
|
|
3626
3719
|
const wordLen = combinedText.length;
|
|
3627
3720
|
let isGrowableVW =
|
|
3628
|
-
!isCJK && !isRTL && !hasHyphen && wordLen > 0 && wordLen <=
|
|
3721
|
+
!isCJK && !isRTL && !hasHyphen && wordLen > 0 && wordLen <= 7;
|
|
3629
3722
|
if (isGrowableVW) {
|
|
3630
3723
|
if (wordLen < 3) {
|
|
3631
3724
|
isGrowableVW =
|
|
3632
|
-
combinedDuration >=
|
|
3725
|
+
combinedDuration >= 1050 && combinedDuration >= wordLen * 525;
|
|
3633
3726
|
} else {
|
|
3634
3727
|
isGrowableVW =
|
|
3635
|
-
combinedDuration >=
|
|
3728
|
+
combinedDuration >= 850 && combinedDuration >= wordLen * 190;
|
|
3636
3729
|
}
|
|
3637
3730
|
}
|
|
3638
3731
|
|
|
3639
|
-
const
|
|
3732
|
+
const isLineSynced =
|
|
3733
|
+
line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
3734
|
+
const isGlowingVW = isGrowableVW && !isLineSynced;
|
|
3640
3735
|
|
|
3641
3736
|
let charOff = 0;
|
|
3642
3737
|
for (let gi = vwStart; gi <= vwEnd; gi += 1) {
|
|
@@ -3663,6 +3758,7 @@ export class AmLyrics extends LitElement {
|
|
|
3663
3758
|
vwCharOffset,
|
|
3664
3759
|
vwStartMs,
|
|
3665
3760
|
vwEndMs,
|
|
3761
|
+
lineIsRTL,
|
|
3666
3762
|
};
|
|
3667
3763
|
});
|
|
3668
3764
|
}
|
|
@@ -3768,15 +3864,16 @@ export class AmLyrics extends LitElement {
|
|
|
3768
3864
|
private clearPreActiveClasses(exceptLineIndex: number | null = null): void {
|
|
3769
3865
|
if (!this.lyricsContainer) return;
|
|
3770
3866
|
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
.
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3867
|
+
const keptLines: HTMLElement[] = [];
|
|
3868
|
+
for (const lineElement of this.preActiveLineElements) {
|
|
3869
|
+
const lineIndex = AmLyrics.getLineIndexFromElement(lineElement);
|
|
3870
|
+
if (lineIndex === exceptLineIndex) {
|
|
3871
|
+
keptLines.push(lineElement);
|
|
3872
|
+
} else {
|
|
3873
|
+
lineElement.classList.remove('pre-active');
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
this.preActiveLineElements = keptLines;
|
|
3780
3877
|
}
|
|
3781
3878
|
|
|
3782
3879
|
private getPrimaryActiveLineIndex(activeIndices: number[]): number | null {
|
|
@@ -4342,6 +4439,7 @@ export class AmLyrics extends LitElement {
|
|
|
4342
4439
|
// Clean up any lingering scroll animations before smooth scroll
|
|
4343
4440
|
for (const line of animatingLines) {
|
|
4344
4441
|
line.classList.remove('scroll-animate');
|
|
4442
|
+
line.style.removeProperty('will-change');
|
|
4345
4443
|
line.style.removeProperty('--scroll-delta');
|
|
4346
4444
|
line.style.removeProperty('--lyrics-line-delay');
|
|
4347
4445
|
line.style.removeProperty('--scroll-duration');
|
|
@@ -4415,6 +4513,7 @@ export class AmLyrics extends LitElement {
|
|
|
4415
4513
|
// --- Step 4: Re-add scroll-animate class to start fresh animations ---
|
|
4416
4514
|
for (const line of newAnimatingLines) {
|
|
4417
4515
|
line.classList.add('scroll-animate');
|
|
4516
|
+
line.style.willChange = 'transform';
|
|
4418
4517
|
animatingLines.push(line);
|
|
4419
4518
|
}
|
|
4420
4519
|
|
|
@@ -4435,6 +4534,7 @@ export class AmLyrics extends LitElement {
|
|
|
4435
4534
|
for (let i = 0; i < animatingLines.length; i += 1) {
|
|
4436
4535
|
const line = animatingLines[i];
|
|
4437
4536
|
line.classList.remove('scroll-animate');
|
|
4537
|
+
line.style.removeProperty('will-change');
|
|
4438
4538
|
line.style.removeProperty('--scroll-delta');
|
|
4439
4539
|
line.style.removeProperty('--lyrics-line-delay');
|
|
4440
4540
|
line.style.removeProperty('--scroll-duration');
|
|
@@ -4466,13 +4566,15 @@ export class AmLyrics extends LitElement {
|
|
|
4466
4566
|
'next-4',
|
|
4467
4567
|
];
|
|
4468
4568
|
|
|
4469
|
-
// Remove old position classes
|
|
4470
|
-
this.
|
|
4471
|
-
.
|
|
4472
|
-
|
|
4569
|
+
// Remove old position classes from tracked elements
|
|
4570
|
+
for (const el of this.positionedLineElements) {
|
|
4571
|
+
el.classList.remove(...positionClasses);
|
|
4572
|
+
}
|
|
4573
|
+
this.positionedLineElements = [];
|
|
4473
4574
|
|
|
4474
4575
|
// Add new position classes
|
|
4475
4576
|
lineToScroll.classList.add('lyrics-activest');
|
|
4577
|
+
this.positionedLineElements.push(lineToScroll);
|
|
4476
4578
|
|
|
4477
4579
|
const lineElements = Array.from(
|
|
4478
4580
|
this.lyricsContainer.querySelectorAll('.lyrics-line'),
|
|
@@ -4492,6 +4594,7 @@ export class AmLyrics extends LitElement {
|
|
|
4492
4594
|
else if (position < 0)
|
|
4493
4595
|
element.classList.add(`prev-${Math.abs(position)}`);
|
|
4494
4596
|
else element.classList.add(`next-${position}`);
|
|
4597
|
+
this.positionedLineElements.push(element);
|
|
4495
4598
|
}
|
|
4496
4599
|
}
|
|
4497
4600
|
}
|
|
@@ -4524,7 +4627,7 @@ export class AmLyrics extends LitElement {
|
|
|
4524
4627
|
}
|
|
4525
4628
|
|
|
4526
4629
|
// Skip scroll if near the bottom of content (prevents footer jitter)
|
|
4527
|
-
if (!forceScroll) {
|
|
4630
|
+
if (!forceScroll && !activeLine.classList.contains('lyrics-footer')) {
|
|
4528
4631
|
const parent = this.lyricsContainer;
|
|
4529
4632
|
const atBottom =
|
|
4530
4633
|
parent.scrollTop + parent.clientHeight >= parent.scrollHeight - 50;
|
|
@@ -4554,7 +4657,10 @@ export class AmLyrics extends LitElement {
|
|
|
4554
4657
|
* Update syllable highlight animation - apply CSS wipe animation
|
|
4555
4658
|
* (Exact copy from YouLyPlus _updateSyllableAnimation)
|
|
4556
4659
|
*/
|
|
4557
|
-
private static updateSyllableAnimation(
|
|
4660
|
+
private static updateSyllableAnimation(
|
|
4661
|
+
syllable: HTMLElement,
|
|
4662
|
+
elapsedTimeMs = 0,
|
|
4663
|
+
): void {
|
|
4558
4664
|
if (syllable.classList.contains('highlight')) return;
|
|
4559
4665
|
|
|
4560
4666
|
const { classList } = syllable;
|
|
@@ -4597,10 +4703,8 @@ export class AmLyrics extends LitElement {
|
|
|
4597
4703
|
const growDurationMs = finalDuration * 1.5;
|
|
4598
4704
|
|
|
4599
4705
|
allWordCharSpans.forEach(span => {
|
|
4600
|
-
const
|
|
4601
|
-
|
|
4602
|
-
);
|
|
4603
|
-
const maxScale = span.dataset.maxScale || '1.1';
|
|
4706
|
+
const matrixScale = span.dataset.matrixScale || '1.1';
|
|
4707
|
+
const charOffsetX = span.dataset.charOffsetX || '0';
|
|
4604
4708
|
const shadowIntensity = span.dataset.shadowIntensity || '0.6';
|
|
4605
4709
|
const translateYPeak = span.dataset.translateYPeak || '-2';
|
|
4606
4710
|
|
|
@@ -4616,13 +4720,13 @@ export class AmLyrics extends LitElement {
|
|
|
4616
4720
|
|
|
4617
4721
|
styleUpdates.push({
|
|
4618
4722
|
element: span,
|
|
4619
|
-
property: '--
|
|
4620
|
-
value:
|
|
4723
|
+
property: '--matrix-scale',
|
|
4724
|
+
value: matrixScale,
|
|
4621
4725
|
});
|
|
4622
4726
|
styleUpdates.push({
|
|
4623
4727
|
element: span,
|
|
4624
|
-
property: '--
|
|
4625
|
-
value:
|
|
4728
|
+
property: '--char-offset-x',
|
|
4729
|
+
value: `${charOffsetX}px`,
|
|
4626
4730
|
});
|
|
4627
4731
|
styleUpdates.push({
|
|
4628
4732
|
element: span,
|
|
@@ -4632,7 +4736,7 @@ export class AmLyrics extends LitElement {
|
|
|
4632
4736
|
styleUpdates.push({
|
|
4633
4737
|
element: span,
|
|
4634
4738
|
property: '--translate-y-peak',
|
|
4635
|
-
value: `${translateYPeak}`,
|
|
4739
|
+
value: `${translateYPeak}px`,
|
|
4636
4740
|
});
|
|
4637
4741
|
});
|
|
4638
4742
|
}
|
|
@@ -4643,7 +4747,7 @@ export class AmLyrics extends LitElement {
|
|
|
4643
4747
|
const startPct = parseFloat(span.dataset.wipeStart || '0');
|
|
4644
4748
|
const durationPct = parseFloat(span.dataset.wipeDuration || '0');
|
|
4645
4749
|
|
|
4646
|
-
const wipeDelay = syllableDurationMs * startPct;
|
|
4750
|
+
const wipeDelay = syllableDurationMs * startPct - elapsedTimeMs;
|
|
4647
4751
|
const wipeDuration = syllableDurationMs * durationPct;
|
|
4648
4752
|
|
|
4649
4753
|
const useStartAnimation = isFirstInContainer && charIndex === 0;
|
|
@@ -4663,9 +4767,10 @@ export class AmLyrics extends LitElement {
|
|
|
4663
4767
|
animationParts.push(existingAnimation.split(',')[0].trim());
|
|
4664
4768
|
}
|
|
4665
4769
|
if (charIndex > 0) {
|
|
4666
|
-
const arrivalTime =
|
|
4667
|
-
|
|
4668
|
-
|
|
4770
|
+
const arrivalTime =
|
|
4771
|
+
(span.dataset.preWipeArrival
|
|
4772
|
+
? parseFloat(span.dataset.preWipeArrival)
|
|
4773
|
+
: syllableDurationMs * startPct) - elapsedTimeMs;
|
|
4669
4774
|
const constantDuration = parseFloat(
|
|
4670
4775
|
span.dataset.preWipeDuration || '100',
|
|
4671
4776
|
);
|
|
@@ -4706,7 +4811,7 @@ export class AmLyrics extends LitElement {
|
|
|
4706
4811
|
|
|
4707
4812
|
const currentWipeAnimation = isGap ? 'fade-gap' : wipeAnimation;
|
|
4708
4813
|
// eslint-disable-next-line no-param-reassign
|
|
4709
|
-
syllable.style.animation = `${currentWipeAnimation} ${visualDuration}ms ${isGap ? 'ease-out' : 'linear'} forwards`;
|
|
4814
|
+
syllable.style.animation = `${currentWipeAnimation} ${visualDuration}ms ${isGap ? 'ease-out' : 'linear'} ${-elapsedTimeMs}ms forwards`;
|
|
4710
4815
|
}
|
|
4711
4816
|
|
|
4712
4817
|
// --- WRITE PHASE ---
|
|
@@ -4714,6 +4819,7 @@ export class AmLyrics extends LitElement {
|
|
|
4714
4819
|
classList.add('highlight');
|
|
4715
4820
|
|
|
4716
4821
|
for (const [span, animationString] of charAnimationsMap.entries()) {
|
|
4822
|
+
span.style.willChange = 'transform';
|
|
4717
4823
|
span.style.animation = animationString;
|
|
4718
4824
|
}
|
|
4719
4825
|
|
|
@@ -4742,6 +4848,7 @@ export class AmLyrics extends LitElement {
|
|
|
4742
4848
|
syllable.querySelectorAll('span.char').forEach(span => {
|
|
4743
4849
|
const el = span as HTMLElement;
|
|
4744
4850
|
el.style.animation = '';
|
|
4851
|
+
el.style.willChange = '';
|
|
4745
4852
|
el.style.transition = 'none';
|
|
4746
4853
|
el.style.backgroundColor = 'var(--lyplus-text-secondary)';
|
|
4747
4854
|
});
|
|
@@ -4762,6 +4869,7 @@ export class AmLyrics extends LitElement {
|
|
|
4762
4869
|
const el = span as HTMLElement;
|
|
4763
4870
|
el.style.removeProperty('background-color');
|
|
4764
4871
|
el.style.removeProperty('transition');
|
|
4872
|
+
el.style.removeProperty('will-change');
|
|
4765
4873
|
});
|
|
4766
4874
|
});
|
|
4767
4875
|
}
|
|
@@ -4803,7 +4911,7 @@ export class AmLyrics extends LitElement {
|
|
|
4803
4911
|
);
|
|
4804
4912
|
const endTime = parseFloat(syllable.getAttribute('data-end-time') || '0');
|
|
4805
4913
|
|
|
4806
|
-
if (startTime) {
|
|
4914
|
+
if (Number.isFinite(startTime) && Number.isFinite(endTime)) {
|
|
4807
4915
|
const { classList } = syllable;
|
|
4808
4916
|
const hasHighlight = classList.contains('highlight');
|
|
4809
4917
|
const hasFinished = classList.contains('finished');
|
|
@@ -4830,7 +4938,10 @@ export class AmLyrics extends LitElement {
|
|
|
4830
4938
|
if (currentTimeMs >= startTime && currentTimeMs <= endTime) {
|
|
4831
4939
|
// Currently active
|
|
4832
4940
|
if (!hasHighlight) {
|
|
4833
|
-
AmLyrics.updateSyllableAnimation(
|
|
4941
|
+
AmLyrics.updateSyllableAnimation(
|
|
4942
|
+
syllable,
|
|
4943
|
+
currentTimeMs - startTime,
|
|
4944
|
+
);
|
|
4834
4945
|
}
|
|
4835
4946
|
if (hasFinished) {
|
|
4836
4947
|
classList.remove('finished');
|
|
@@ -4839,7 +4950,10 @@ export class AmLyrics extends LitElement {
|
|
|
4839
4950
|
// Finished
|
|
4840
4951
|
if (!hasFinished) {
|
|
4841
4952
|
if (!hasHighlight) {
|
|
4842
|
-
AmLyrics.updateSyllableAnimation(
|
|
4953
|
+
AmLyrics.updateSyllableAnimation(
|
|
4954
|
+
syllable,
|
|
4955
|
+
currentTimeMs - startTime,
|
|
4956
|
+
);
|
|
4843
4957
|
}
|
|
4844
4958
|
classList.add('finished');
|
|
4845
4959
|
}
|
|
@@ -5127,10 +5241,6 @@ export class AmLyrics extends LitElement {
|
|
|
5127
5241
|
|
|
5128
5242
|
// Set both old internal CSS variables (for backward compatibility)
|
|
5129
5243
|
// and new public CSS variables (which take precedence)
|
|
5130
|
-
this.style.setProperty(
|
|
5131
|
-
'--hover-background-color',
|
|
5132
|
-
this.hoverBackgroundColor,
|
|
5133
|
-
);
|
|
5134
5244
|
this.style.setProperty('--highlight-color', this.highlightColor);
|
|
5135
5245
|
|
|
5136
5246
|
const sourceLabel = this.lyricsSource ?? 'Unavailable';
|
|
@@ -5197,21 +5307,24 @@ export class AmLyrics extends LitElement {
|
|
|
5197
5307
|
>`
|
|
5198
5308
|
: '';
|
|
5199
5309
|
|
|
5200
|
-
return html`<span class="lyrics-word"
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5310
|
+
return html`<span class="lyrics-word"
|
|
5311
|
+
><span
|
|
5312
|
+
class="lyrics-syllable-wrap${bgRomanizedText
|
|
5313
|
+
? ' has-transliteration'
|
|
5314
|
+
: ''}"
|
|
5315
|
+
><span
|
|
5316
|
+
class="lyrics-syllable no-chars${syllable.lineSynced
|
|
5317
|
+
? ' line-synced'
|
|
5205
5318
|
: ''}"
|
|
5206
5319
|
data-start-time="${startTimeMs}"
|
|
5207
5320
|
data-end-time="${endTimeMs}"
|
|
5208
5321
|
data-duration="${durationMs}"
|
|
5209
5322
|
data-syllable-index="${syllableIndex}"
|
|
5323
|
+
data-wipe-ratio="1"
|
|
5210
5324
|
>${syllable.text}</span
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
</span>`;
|
|
5325
|
+
>${bgRomanizedText}</span
|
|
5326
|
+
></span
|
|
5327
|
+
>`;
|
|
5215
5328
|
})}
|
|
5216
5329
|
</p>`
|
|
5217
5330
|
: '';
|
|
@@ -5232,9 +5345,12 @@ export class AmLyrics extends LitElement {
|
|
|
5232
5345
|
const vwFullText = lineData?.vwFullText ?? [];
|
|
5233
5346
|
const vwFullDuration = lineData?.vwFullDuration ?? [];
|
|
5234
5347
|
const vwCharOffset = lineData?.vwCharOffset ?? [];
|
|
5348
|
+
const lineIsRTL = lineData?.lineIsRTL ?? false;
|
|
5235
5349
|
|
|
5236
5350
|
// Create main vocals using YouLyPlus syllable structure
|
|
5237
|
-
const mainVocalElement = html`<p
|
|
5351
|
+
const mainVocalElement = html`<p
|
|
5352
|
+
class="main-vocal-container ${lineIsRTL ? 'rtl-text' : ''}"
|
|
5353
|
+
>
|
|
5238
5354
|
${wordGroups.map((group, groupIdx) => {
|
|
5239
5355
|
const isGrowable = groupGrowable[groupIdx];
|
|
5240
5356
|
const isGlowing = groupGlowing[groupIdx];
|
|
@@ -5247,12 +5363,29 @@ export class AmLyrics extends LitElement {
|
|
|
5247
5363
|
|
|
5248
5364
|
let sylCharAccumulator = 0;
|
|
5249
5365
|
|
|
5366
|
+
const groupText = group.map(s => s.text).join('');
|
|
5367
|
+
const shouldAllowBreak =
|
|
5368
|
+
groupText.trim().length >= 16 ||
|
|
5369
|
+
/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(
|
|
5370
|
+
groupText,
|
|
5371
|
+
);
|
|
5372
|
+
|
|
5373
|
+
// Calculate dynamic rise duration based on the audio duration of the word
|
|
5374
|
+
const wordStartTimeMs = group[0].timestamp;
|
|
5375
|
+
const wordEndTimeMs = group[group.length - 1].endtime;
|
|
5376
|
+
const actualDurationMs = wordEndTimeMs - wordStartTimeMs;
|
|
5377
|
+
// Base float is 0.8s, plus a portion of the audio duration, capped between 1.0s and 2.5s
|
|
5378
|
+
const riseDuration = Math.max(
|
|
5379
|
+
1.2,
|
|
5380
|
+
Math.min(2.5, 1.2 + (actualDurationMs / 1000) * 0.6),
|
|
5381
|
+
);
|
|
5382
|
+
|
|
5250
5383
|
return html`<span
|
|
5251
|
-
class="lyrics-word
|
|
5252
|
-
? 'glowing'
|
|
5253
|
-
: ''}
|
|
5254
|
-
|
|
5255
|
-
|
|
5384
|
+
class="lyrics-word${isGrowable ? ' growable' : ''}${isGlowing
|
|
5385
|
+
? ' glowing'
|
|
5386
|
+
: ''}${shouldAllowBreak ? ' allow-break' : ''}"
|
|
5387
|
+
style="--rise-duration: ${riseDuration}s"
|
|
5388
|
+
>${group.map((syllable, sylIdx) => {
|
|
5256
5389
|
const startTimeMs = syllable.timestamp;
|
|
5257
5390
|
const endTimeMs = syllable.endtime;
|
|
5258
5391
|
const durationMs = endTimeMs - startTimeMs;
|
|
@@ -5367,6 +5500,10 @@ export class AmLyrics extends LitElement {
|
|
|
5367
5500
|
)}"
|
|
5368
5501
|
data-horizontal-offset="${horizontalOffset.toFixed(2)}"
|
|
5369
5502
|
data-max-scale="${charMaxScale.toFixed(3)}"
|
|
5503
|
+
data-matrix-scale="${(charMaxScale * 0.98).toFixed(3)}"
|
|
5504
|
+
data-char-offset-x="${(horizontalOffset * 0.98).toFixed(
|
|
5505
|
+
2,
|
|
5506
|
+
)}"
|
|
5370
5507
|
data-shadow-intensity="${charShadowIntensity.toFixed(3)}"
|
|
5371
5508
|
data-translate-y-peak="${charTranslateYPeak.toFixed(3)}"
|
|
5372
5509
|
>${char}</span
|
|
@@ -5374,11 +5511,14 @@ export class AmLyrics extends LitElement {
|
|
|
5374
5511
|
})}`;
|
|
5375
5512
|
}
|
|
5376
5513
|
|
|
5377
|
-
return html`<span
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5514
|
+
return html`<span
|
|
5515
|
+
class="lyrics-syllable-wrap${romanizedText
|
|
5516
|
+
? ' has-transliteration'
|
|
5517
|
+
: ''}"
|
|
5518
|
+
><span
|
|
5519
|
+
class="lyrics-syllable${groupLineSynced
|
|
5520
|
+
? ' line-synced'
|
|
5521
|
+
: ''}${isGrowable ? ' has-chars' : ' no-chars'}"
|
|
5382
5522
|
data-start-time="${startTimeMs}"
|
|
5383
5523
|
data-end-time="${endTimeMs}"
|
|
5384
5524
|
data-duration="${durationMs}"
|
|
@@ -5386,11 +5526,10 @@ export class AmLyrics extends LitElement {
|
|
|
5386
5526
|
data-syllable-index="${sylIdx}"
|
|
5387
5527
|
data-wipe-ratio="1"
|
|
5388
5528
|
>${syllableContent}</span
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
</span>`;
|
|
5529
|
+
>${romanizedText}</span
|
|
5530
|
+
>`;
|
|
5531
|
+
})}</span
|
|
5532
|
+
>`;
|
|
5394
5533
|
})}
|
|
5395
5534
|
</p>`;
|
|
5396
5535
|
|
|
@@ -5416,7 +5555,11 @@ export class AmLyrics extends LitElement {
|
|
|
5416
5555
|
line.romanizedText &&
|
|
5417
5556
|
!line.text.some(s => s.romanizedText) &&
|
|
5418
5557
|
line.romanizedText.trim() !== fullLineText
|
|
5419
|
-
? html`<div
|
|
5558
|
+
? html`<div
|
|
5559
|
+
class="lyrics-romanization-container ${lineIsRTL
|
|
5560
|
+
? 'rtl-text'
|
|
5561
|
+
: ''}"
|
|
5562
|
+
>
|
|
5420
5563
|
${line.romanizedText}
|
|
5421
5564
|
</div>`
|
|
5422
5565
|
: '';
|
|
@@ -5438,42 +5581,37 @@ export class AmLyrics extends LitElement {
|
|
|
5438
5581
|
data-end-time="${gapForLine.gapEnd}"
|
|
5439
5582
|
style="--gap-pulse-duration: ${GAP_PULSE_DURATION_MS}ms; --gap-loop-delay: -${gapLoopDelay}ms; --gap-exit-duration: ${GAP_EXIT_LEAD_MS}ms; --gap-exit-scale: ${GAP_MIN_SCALE};"
|
|
5440
5583
|
>
|
|
5441
|
-
<
|
|
5442
|
-
<
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
></span>
|
|
5473
|
-
</span>
|
|
5474
|
-
</span>
|
|
5475
|
-
</p>
|
|
5476
|
-
</div>
|
|
5584
|
+
<p class="main-vocal-container">
|
|
5585
|
+
<span class="lyrics-word"
|
|
5586
|
+
><span class="lyrics-syllable-wrap"
|
|
5587
|
+
><span
|
|
5588
|
+
class="lyrics-syllable"
|
|
5589
|
+
data-start-time="${gapForLine.gapStart}"
|
|
5590
|
+
data-end-time="${gapForLine.gapStart + dotDuration}"
|
|
5591
|
+
data-duration="${dotDuration}"
|
|
5592
|
+
data-wipe-ratio="1"
|
|
5593
|
+
data-syllable-index="0"
|
|
5594
|
+
></span></span
|
|
5595
|
+
><span class="lyrics-syllable-wrap"
|
|
5596
|
+
><span
|
|
5597
|
+
class="lyrics-syllable"
|
|
5598
|
+
data-start-time="${gapForLine.gapStart + dotDuration}"
|
|
5599
|
+
data-end-time="${gapForLine.gapStart + dotDuration * 2}"
|
|
5600
|
+
data-duration="${dotDuration}"
|
|
5601
|
+
data-wipe-ratio="1"
|
|
5602
|
+
data-syllable-index="1"
|
|
5603
|
+
></span></span
|
|
5604
|
+
><span class="lyrics-syllable-wrap"
|
|
5605
|
+
><span
|
|
5606
|
+
class="lyrics-syllable"
|
|
5607
|
+
data-start-time="${gapForLine.gapStart + dotDuration * 2}"
|
|
5608
|
+
data-end-time="${gapForLine.gapEnd}"
|
|
5609
|
+
data-duration="${dotDuration}"
|
|
5610
|
+
data-wipe-ratio="1"
|
|
5611
|
+
data-syllable-index="2"
|
|
5612
|
+
></span></span
|
|
5613
|
+
></span>
|
|
5614
|
+
</p>
|
|
5477
5615
|
</div>`;
|
|
5478
5616
|
}
|
|
5479
5617
|
|
|
@@ -5483,7 +5621,7 @@ export class AmLyrics extends LitElement {
|
|
|
5483
5621
|
id="${lineId}"
|
|
5484
5622
|
class="lyrics-line ${line.alignment === 'end'
|
|
5485
5623
|
? 'singer-right'
|
|
5486
|
-
: 'singer-left'}"
|
|
5624
|
+
: 'singer-left'} ${lineIsRTL ? 'rtl-text' : ''}"
|
|
5487
5625
|
data-start-time="${lineStartTime}"
|
|
5488
5626
|
data-end-time="${lineEndTime}"
|
|
5489
5627
|
@click=${() => this.handleLineClick(line)}
|
|
@@ -5494,11 +5632,11 @@ export class AmLyrics extends LitElement {
|
|
|
5494
5632
|
}
|
|
5495
5633
|
}}
|
|
5496
5634
|
>
|
|
5497
|
-
<div class="lyrics-line-container">
|
|
5635
|
+
<div class="lyrics-line-container ${lineIsRTL ? 'rtl-text' : ''}">
|
|
5498
5636
|
${bgPlacement === 'before' ? backgroundVocalElement : ''}
|
|
5499
5637
|
${mainVocalElement}
|
|
5500
5638
|
${bgPlacement === 'after' ? backgroundVocalElement : ''}
|
|
5501
|
-
${
|
|
5639
|
+
${lineRomanizationElement} ${translationElement}
|
|
5502
5640
|
</div>
|
|
5503
5641
|
</div>
|
|
5504
5642
|
`;
|
|
@@ -5612,13 +5750,13 @@ export class AmLyrics extends LitElement {
|
|
|
5612
5750
|
${renderContent()}
|
|
5613
5751
|
${!this.isLoading
|
|
5614
5752
|
? html`
|
|
5615
|
-
<footer class="lyrics-footer">
|
|
5753
|
+
<footer class="lyrics-footer lyrics-line">
|
|
5616
5754
|
<div class="footer-content">
|
|
5617
5755
|
<span
|
|
5618
5756
|
class="source-info"
|
|
5619
5757
|
style="display: flex; align-items: center; gap: 8px;"
|
|
5620
5758
|
>
|
|
5621
|
-
|
|
5759
|
+
<b style="font-weight: 750;">Source</b> ${sourceLabel}
|
|
5622
5760
|
${(this.availableSources &&
|
|
5623
5761
|
this.availableSources.length > 1) ||
|
|
5624
5762
|
!this.hasFetchedAllProviders
|
|
@@ -5661,15 +5799,25 @@ export class AmLyrics extends LitElement {
|
|
|
5661
5799
|
`
|
|
5662
5800
|
: ''}
|
|
5663
5801
|
</span>
|
|
5664
|
-
|
|
5665
|
-
|
|
5802
|
+
${this.songwriters
|
|
5803
|
+
? html`<span
|
|
5804
|
+
class="songwriters-info"
|
|
5805
|
+
style="margin-top: 4px; font-weight: normal; font-size: 0.9em;"
|
|
5806
|
+
>
|
|
5807
|
+
<b style="font-weight: 750;">Songwriters</b> ${this
|
|
5808
|
+
.songwriters}
|
|
5809
|
+
</span>`
|
|
5810
|
+
: ''}
|
|
5811
|
+
<span class="version-info" style="margin-top: 8px;">
|
|
5812
|
+
<b style="font-weight: 750;">am-lyrics</b> v${VERSION} •
|
|
5666
5813
|
|
|
5667
5814
|
<a
|
|
5668
5815
|
href="https://github.com/uimaxbai/apple-music-web-components"
|
|
5669
5816
|
target="_blank"
|
|
5670
5817
|
rel="noopener noreferrer"
|
|
5671
|
-
|
|
5672
|
-
|
|
5818
|
+
style="display: inline-flex; align-items: center; gap: 4px;"
|
|
5819
|
+
>Star me on GitHub
|
|
5820
|
+
</a>
|
|
5673
5821
|
</span>
|
|
5674
5822
|
</div>
|
|
5675
5823
|
</footer>
|