@uimaxbai/am-lyrics 1.5.2 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/react.js CHANGED
@@ -322,7 +322,7 @@ class GoogleService {
322
322
  }
323
323
  }
324
324
 
325
- const VERSION = '1.5.2';
325
+ const VERSION = '1.5.4';
326
326
  const INSTRUMENTAL_THRESHOLD_MS = 7000; // Show dots for gaps >= 7s
327
327
  const FETCH_TIMEOUT_MS = 8000; // Timeout for all lyrics fetch requests
328
328
  const SEEK_THRESHOLD_MS = 500;
@@ -583,6 +583,28 @@ let AmLyrics$1 = class AmLyrics extends i {
583
583
  this.hasFetchedAllProviders = false;
584
584
  this._updateFooter();
585
585
  try {
586
+ if (this.ttml) {
587
+ const parseResult = AmLyrics.parseTTML(this.ttml);
588
+ if (parseResult && parseResult.lines.length > 0) {
589
+ this.lyrics = parseResult.lines;
590
+ this.lyricsSource = 'Local';
591
+ if (parseResult.songwriters) {
592
+ this.songwriters = parseResult.songwriters;
593
+ }
594
+ this.availableSources = [
595
+ {
596
+ lines: this.lyrics,
597
+ source: 'Local',
598
+ songwriters: this.songwriters,
599
+ },
600
+ ];
601
+ this.currentSourceIndex = 0;
602
+ this.hasFetchedAllProviders = true;
603
+ this._updateFooter();
604
+ await this.onLyricsLoaded();
605
+ return;
606
+ }
607
+ }
586
608
  const resolvedMetadata = await this.resolveSongMetadata();
587
609
  // If a newer fetch was triggered while we awaited, bail out
588
610
  if (controller.signal.aborted)
@@ -2185,6 +2207,7 @@ let AmLyrics$1 = class AmLyrics extends i {
2185
2207
  if ((changedProperties.has('query') ||
2186
2208
  changedProperties.has('musicId') ||
2187
2209
  changedProperties.has('isrc') ||
2210
+ changedProperties.has('ttml') ||
2188
2211
  changedProperties.has('songTitle') ||
2189
2212
  changedProperties.has('songArtist') ||
2190
2213
  changedProperties.has('songAlbum') ||
@@ -2399,7 +2422,9 @@ let AmLyrics$1 = class AmLyrics extends i {
2399
2422
  lineIsRTL = true;
2400
2423
  const hasHyphen = combinedText.includes('-');
2401
2424
  const wordLen = combinedText.length;
2402
- let isGrowableVW = !isCJK && !isRTL && !hasHyphen && wordLen > 0 && wordLen <= 7;
2425
+ const canAnimateByChar = !isCJK && !isRTL && !hasHyphen && wordLen > 0;
2426
+ const isLineSynced = line.isWordSynced === false || line.text.some(s => s.lineSynced);
2427
+ let isGrowableVW = canAnimateByChar && wordLen > 0 && wordLen <= 7;
2403
2428
  if (isGrowableVW) {
2404
2429
  if (wordLen < 3) {
2405
2430
  isGrowableVW =
@@ -2410,9 +2435,14 @@ let AmLyrics$1 = class AmLyrics extends i {
2410
2435
  combinedDuration >= 850 && combinedDuration >= wordLen * 190;
2411
2436
  }
2412
2437
  }
2413
- const isLineSynced = line.isWordSynced === false || line.text.some(s => s.lineSynced);
2438
+ const hasCharRiseDuration = combinedDuration >= Math.max(700, wordLen * 85);
2439
+ const hasLongShortWordDuration = wordLen >= 4 && combinedDuration >= Math.max(1300, wordLen * 260);
2440
+ const isCharRiseVW = canAnimateByChar &&
2441
+ !isLineSynced &&
2442
+ !isGrowableVW &&
2443
+ ((wordLen >= 8 && hasCharRiseDuration) ||
2444
+ (wordLen < 8 && hasLongShortWordDuration));
2414
2445
  const isGlowingVW = isGrowableVW && !isLineSynced;
2415
- const isCharRiseVW = !isGrowableVW && !isLineSynced && !isCJK && !isRTL && wordLen >= 8;
2416
2446
  let charOff = 0;
2417
2447
  for (let gi = vwStart; gi <= vwEnd; gi += 1) {
2418
2448
  groupGrowable[gi] = isGrowableVW;
@@ -3317,12 +3347,24 @@ let AmLyrics$1 = class AmLyrics extends i {
3317
3347
  const isRTL = classList.contains('rtl-text');
3318
3348
  const charSpans = Array.from(syllable.querySelectorAll('span.char'));
3319
3349
  const wordElement = syllable.parentElement?.parentElement; // syllable-wrap -> word
3320
- const allWordCharSpans = wordElement
3321
- ? Array.from(wordElement.querySelectorAll('span.char'))
3322
- : [];
3350
+ const virtualWordId = wordElement?.dataset
3351
+ .virtualWordId;
3352
+ let wordElements = [];
3353
+ if (virtualWordId && wordElement?.parentElement) {
3354
+ wordElements = Array.from(wordElement.parentElement.querySelectorAll('.lyrics-word')).filter(el => el.dataset.virtualWordId === virtualWordId);
3355
+ }
3356
+ else if (wordElement) {
3357
+ wordElements = [wordElement];
3358
+ }
3359
+ const allWordCharSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
3323
3360
  const isGrowable = wordElement?.classList.contains('growable');
3324
3361
  const isCharRise = wordElement?.classList.contains('char-rise');
3325
3362
  const isFirstSyllable = syllable.getAttribute('data-syllable-index') === '0';
3363
+ const syllableStartMs = parseFloat(syllable.getAttribute('data-start-time') || '0');
3364
+ const virtualWordStartMs = parseFloat(wordElement?.dataset.virtualWordStart || '');
3365
+ const isFirstInVirtualWord = isFirstSyllable &&
3366
+ (!Number.isFinite(virtualWordStartMs) ||
3367
+ Math.abs(syllableStartMs - virtualWordStartMs) < 0.5);
3326
3368
  const isFirstInContainer = isFirstSyllable; // Simplified
3327
3369
  const isGap = syllable.closest('.lyrics-gap') !== null;
3328
3370
  // Get duration from data attribute
@@ -3334,7 +3376,7 @@ let AmLyrics$1 = class AmLyrics extends i {
3334
3376
  const charAnimationsMap = new Map();
3335
3377
  const styleUpdates = [];
3336
3378
  // Step 1: Grow Pass
3337
- if (isGrowable && isFirstSyllable && allWordCharSpans.length > 0) {
3379
+ if (isGrowable && isFirstInVirtualWord && allWordCharSpans.length > 0) {
3338
3380
  const finalDuration = wordDurationMs;
3339
3381
  const baseDelayPerChar = finalDuration * 0.09;
3340
3382
  const growDurationMs = finalDuration * 1.5;
@@ -3368,7 +3410,7 @@ let AmLyrics$1 = class AmLyrics extends i {
3368
3410
  });
3369
3411
  });
3370
3412
  }
3371
- if (isCharRise && isFirstSyllable && allWordCharSpans.length > 0) {
3413
+ if (isCharRise && isFirstInVirtualWord && allWordCharSpans.length > 0) {
3372
3414
  const finalDuration = Math.max(wordDurationMs, syllableDurationMs);
3373
3415
  const baseDelayPerChar = finalDuration * 0.09;
3374
3416
  const riseDurationMs = finalDuration * 1.5;
@@ -3400,14 +3442,15 @@ let AmLyrics$1 = class AmLyrics extends i {
3400
3442
  existingAnimation.includes('rise-char'))) {
3401
3443
  animationParts.push(existingAnimation.split(',')[0].trim());
3402
3444
  }
3403
- if (charIndex > 0) {
3445
+ if (charIndex > 0 && wipeDelay > 0 && wipeDuration > 0) {
3404
3446
  const arrivalTime = (span.dataset.preWipeArrival
3405
3447
  ? parseFloat(span.dataset.preWipeArrival)
3406
3448
  : syllableDurationMs * startPct) - elapsedTimeMs;
3407
- const constantDuration = parseFloat(span.dataset.preWipeDuration || '100');
3408
- const animDelay = arrivalTime - constantDuration;
3409
- if (constantDuration > 0) {
3410
- animationParts.push(`pre-wipe-char ${constantDuration}ms linear ${animDelay}ms forwards`);
3449
+ const measuredPreWipeDuration = parseFloat(span.dataset.preWipeDuration || '100');
3450
+ const preWipeDuration = Math.min(measuredPreWipeDuration, wipeDuration * 0.9, syllableDurationMs * 0.08, arrivalTime);
3451
+ const animDelay = arrivalTime - preWipeDuration;
3452
+ if (preWipeDuration >= 16) {
3453
+ animationParts.push(`pre-wipe-char ${preWipeDuration}ms linear ${animDelay}ms none`);
3411
3454
  }
3412
3455
  }
3413
3456
  if (wipeDuration > 0) {
@@ -3985,6 +4028,8 @@ let AmLyrics$1 = class AmLyrics extends i {
3985
4028
  const vwFullText = lineData?.vwFullText ?? [];
3986
4029
  const vwFullDuration = lineData?.vwFullDuration ?? [];
3987
4030
  const vwCharOffset = lineData?.vwCharOffset ?? [];
4031
+ const vwStartMs = lineData?.vwStartMs ?? [];
4032
+ const vwEndMs = lineData?.vwEndMs ?? [];
3988
4033
  const lineIsRTL = lineData?.lineIsRTL ?? false;
3989
4034
  // Create main vocals using YouLyPlus syllable structure
3990
4035
  const mainVocalElement = b `<p
@@ -4004,6 +4049,13 @@ let AmLyrics$1 = class AmLyrics extends i {
4004
4049
  const groupCharOffset = isAnimatedByChar
4005
4050
  ? vwCharOffset[groupIdx]
4006
4051
  : 0;
4052
+ const virtualWordId = isAnimatedByChar
4053
+ ? `${lineIndex}:${vwStartMs[groupIdx]}:${vwEndMs[groupIdx]}`
4054
+ : '';
4055
+ const virtualWordStart = isAnimatedByChar
4056
+ ? vwStartMs[groupIdx]
4057
+ : '';
4058
+ const virtualWordEnd = isAnimatedByChar ? vwEndMs[groupIdx] : '';
4007
4059
  let sylCharAccumulator = 0;
4008
4060
  const groupText = group.map(s => s.text).join('');
4009
4061
  const shouldAllowBreak = groupText.trim().length >= 16 ||
@@ -4020,6 +4072,9 @@ let AmLyrics$1 = class AmLyrics extends i {
4020
4072
  : ''}${isGlowing ? ' glowing' : ''}${shouldAllowBreak
4021
4073
  ? ' allow-break'
4022
4074
  : ''}"
4075
+ data-virtual-word-id="${virtualWordId}"
4076
+ data-virtual-word-start="${virtualWordStart}"
4077
+ data-virtual-word-end="${virtualWordEnd}"
4023
4078
  style="--rise-duration: ${riseDuration}s"
4024
4079
  >${group.map((syllable, sylIdx) => {
4025
4080
  const startTimeMs = syllable.timestamp;
@@ -4578,17 +4633,15 @@ AmLyrics$1.styles = i$3 `
4578
4633
 
4579
4634
  .background-vocal-container {
4580
4635
  max-height: 0;
4581
- overflow: visible;
4636
+ overflow: hidden;
4582
4637
  opacity: 0;
4583
4638
  font-size: var(--lyplus-font-size-subtext);
4584
4639
  line-height: 1.15;
4585
4640
  color: color-mix(in srgb, var(--lyplus-text-secondary) 80%, transparent);
4586
- /* Fast exit (0.25 s) so bg vocals collapse quickly and feel snappy.
4587
- The scroll takes ~0.4 s; finishing the collapse first prevents
4588
- the container from trailing behind the motion. */
4589
4641
  transition:
4590
- max-height 250ms cubic-bezier(0.41, 0, 0.12, 0.99),
4591
- opacity 250ms cubic-bezier(0.41, 0, 0.12, 0.99);
4642
+ max-height var(--scroll-duration, 400ms)
4643
+ cubic-bezier(0.41, 0, 0.12, 0.99),
4644
+ opacity var(--scroll-duration, 400ms) cubic-bezier(0.41, 0, 0.12, 0.99);
4592
4645
  margin: 0;
4593
4646
  pointer-events: none;
4594
4647
  }
@@ -4597,7 +4650,8 @@ AmLyrics$1.styles = i$3 `
4597
4650
  display: block;
4598
4651
  padding-top: 0;
4599
4652
  padding-bottom: 0;
4600
- transition: padding-top 250ms cubic-bezier(0.41, 0, 0.12, 0.99);
4653
+ transition: padding-top var(--scroll-duration, 400ms)
4654
+ cubic-bezier(0.41, 0, 0.12, 0.99);
4601
4655
  }
4602
4656
 
4603
4657
  .lyrics-line.singer-right .background-vocal-container,
@@ -4612,16 +4666,11 @@ AmLyrics$1.styles = i$3 `
4612
4666
  .lyrics-line.bg-expanded .background-vocal-container {
4613
4667
  max-height: 4em;
4614
4668
  opacity: 1;
4615
- /* Slower entry (0.6 s) so bg vocals expand smoothly. */
4616
- transition:
4617
- max-height 0.6s ease,
4618
- opacity 0.6s ease;
4619
4669
  will-change: max-height, opacity;
4620
4670
  }
4621
4671
 
4622
4672
  .lyrics-line.bg-expanded .background-vocal-wrap {
4623
4673
  padding-top: 0.26em;
4624
- transition: padding-top 0.6s ease;
4625
4674
  }
4626
4675
 
4627
4676
  /* --- Line States & Modifiers --- */
@@ -5860,6 +5909,9 @@ __decorate([
5860
5909
  __decorate([
5861
5910
  n({ type: String })
5862
5911
  ], AmLyrics$1.prototype, "isrc", void 0);
5912
+ __decorate([
5913
+ n({ type: String })
5914
+ ], AmLyrics$1.prototype, "ttml", void 0);
5863
5915
  __decorate([
5864
5916
  n({ type: String, attribute: 'song-title' })
5865
5917
  ], AmLyrics$1.prototype, "songTitle", void 0);