@uimaxbai/am-lyrics 0.5.0 → 0.5.1

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 CHANGED
@@ -18,9 +18,11 @@ Or, just use the CDN.
18
18
  </script>
19
19
 
20
20
  <am-lyrics
21
- query="Uptown Funk"
22
- music-id=""
23
- isrc=""
21
+ song-title="Uptown Funk"
22
+ song-artist="Mark Ronson"
23
+ song-album="Uptown Special"
24
+ song-duration="269000"
25
+ query="Uptown Funk Mark Ronson"
24
26
  current-time="0"
25
27
  duration=""
26
28
  highlight-color="#f00"
@@ -36,11 +38,15 @@ Or, just use the CDN.
36
38
 
37
39
  | Property/Attribute | Type | Default | Description |
38
40
  |-------------------|------|---------|-------------|
39
- | `query` | `string` | `undefined` | Search query for Apple Music song |
40
- | `music-id` | `string` | `undefined` | Specific Apple Music song ID (rarely used) |
41
+ | `query` | `string` | `undefined` | Search phrase that resolves metadata via LyricsPlus catalog (falls back to Apple Music search) |
42
+ | `music-id` | `string` | `undefined` | Specific Apple Music song ID (served through the backup Apple endpoint) |
41
43
  | `isrc` | `string` | `undefined` | ISRC code to verify correct song match |
44
+ | `song-title` | `string` | `undefined` | Preferred title for LyricsPlus (primary) provider |
45
+ | `song-artist` | `string` | `undefined` | Preferred artist name for LyricsPlus provider |
46
+ | `song-album` | `string` | `undefined` | Optional album name passed to LyricsPlus provider |
47
+ | `song-duration` | `number` | `undefined` | Optional song duration in milliseconds sent to LyricsPlus |
42
48
  | `current-time` | `number` | `0` | Current playback time in milliseconds |
43
- | `duration` | `number` | `undefined` | Song duration in milliseconds. **Set to `-1` to reset/stop playback** |
49
+ | `duration` | `number` | `undefined` | Playback timer duration in milliseconds. **Set to `-1` to reset/stop playback** |
44
50
  | `highlight-color` | `string` | `"#000"` | Color for highlighted/active lyrics |
45
51
  | `hover-background-color` | `string` | `"#f0f0f0"` | Background color on line hover |
46
52
  | `hide-source-footer` | `boolean` | `false` | Hide/show the source attribution footer |
@@ -68,6 +74,15 @@ am-lyrics {
68
74
  **Note**: The CSS variables take precedent over the set properties above.
69
75
 
70
76
 
77
+ ## Lyrics providers
78
+
79
+ The component now favours the LyricsPlus (KPoe) API that powers [YouLyPlus](https://github.com/ibratabian17/YouLyPlus).
80
+
81
+ 1. Provide `song-title` and `song-artist` (plus optional `song-album`/`song-duration`) to request word-synced lyrics from LyricsPlus. A standalone `query` such as `"Bad Habit - Steve Lacy"` also works—the component looks up the metadata through LyricsPlus' `/v1/songlist/search` endpoint.
82
+ 2. If LyricsPlus cannot serve lyrics or metadata is missing, the component automatically falls back to the legacy Apple Music endpoint using the best available identifiers (`query`, `music-id`, `isrc`). Requests that rely solely on `music-id` are handled exclusively by this backup service because LyricsPlus does not support Apple IDs.
83
+
84
+ The footer shows the active provider (e.g. “LyricsPlus (KPoe)” or “Apple Music”) so you always know which service responded. Supplying both metadata *and* a `query` gives the best results because the query remains available for the Apple Music backup.
85
+
71
86
 
72
87
  ## Events
73
88
 
@@ -151,7 +166,9 @@ export default function App() {
151
166
  <div>
152
167
  <audio ref={audioRef} src="/uptown_funk.flac" controls />
153
168
  <AmLyrics
154
- query="Uptown Funk"
169
+ songTitle="Uptown Funk"
170
+ songArtist="Mark Ronson"
171
+ query="Uptown Funk Mark Ronson"
155
172
  currentTime={currentTime}
156
173
  onLineClick={handleLineClick}
157
174
  autoScroll
@@ -218,7 +235,15 @@ The timer needs to be defined by yourself. For example:
218
235
  const searchInput = document.querySelector('#search-input');
219
236
  const amLyrics = document.querySelector('am-lyrics');
220
237
  if (searchInput && amLyrics) {
221
- amLyrics.query = searchInput.value;
238
+ // Expect "Title - Artist" in the search field for optimal LyricsPlus results
239
+ const userInput = searchInput.value.trim();
240
+ const [titlePart = '', artistPart = ''] = userInput
241
+ .split(' - ')
242
+ .map(part => part.trim());
243
+
244
+ amLyrics.songTitle = titlePart || userInput;
245
+ amLyrics.songArtist = artistPart;
246
+ amLyrics.query = userInput;
222
247
  amLyrics.isrc = '';
223
248
  amLyrics.musicId = '';
224
249
  }
@@ -250,7 +275,11 @@ You can synchronize the lyrics with an HTML `<audio>` element.
250
275
 
251
276
  ```html
252
277
  <audio id="audio-player" src="path/to/your/song.mp3" controls></audio>
253
- <am-lyrics query="Uptown Funk"></am-lyrics>
278
+ <am-lyrics
279
+ song-title="Uptown Funk"
280
+ song-artist="Mark Ronson"
281
+ query="Uptown Funk Mark Ronson"
282
+ ></am-lyrics>
254
283
 
255
284
  <script>
256
285
  document.addEventListener('DOMContentLoaded', () => {
package/demo/index.html CHANGED
@@ -17,15 +17,21 @@
17
17
  </head>
18
18
  <body>
19
19
  <div>
20
- <input id="search-input" type="text" placeholder="Search for a song" />
21
- <button id="search-button">Search</button>
20
+ <input id="title-input" type="text" placeholder="Song title" />
21
+ <input id="artist-input" type="text" placeholder="Artist" />
22
+ <input id="query-input" type="text" placeholder="Search query (e.g. Title - Artist)" />
23
+ <input id="duration-input" type="number" placeholder="Duration (ms)" />
24
+ <button id="load-button">Load lyrics</button>
22
25
  </div>
23
26
 
24
27
  <am-lyrics
25
- query="Uptown Funk"
28
+ song-title="Uptown Funk"
29
+ song-artist="Mark Ronson"
30
+ song-album="Uptown Special"
31
+ song-duration="269000"
32
+ query="Uptown Funk Mark Ronson"
26
33
  music-id=""
27
34
  isrc=""
28
- duration=""
29
35
  highlight-color="#f00"
30
36
  hover-background-color="#e0e0e0"
31
37
  hide-source-footer="true"
@@ -35,10 +41,14 @@
35
41
  ></am-lyrics>
36
42
  <!--
37
43
  <am-lyrics
38
- query="Uptown Funk" // Search Apple Music for a song
44
+ song-title="Uptown Funk" // Preferred: provide title for LyricsPlus API
45
+ song-artist="Mark Ronson" // Preferred: provide artist for LyricsPlus API
46
+ song-album="Uptown Special" // Optional album metadata sent to LyricsPlus
47
+ song-duration="269000" // Optional song duration (ms) sent to LyricsPlus
48
+ query="Uptown Funk Mark Ronson" // Fallback search string for Apple Music backup API
39
49
  music-id="" // Use this if you have a specific song ID from Apple Music (almost never)
40
50
  isrc="" // To be used WITH a query, just to double check if it is correct
41
- duration="" // Duration of your timer (the component takes it in and syncs to the words. See JS below)
51
+ duration="" // Playback timer duration (component sync). See JS below
42
52
  highlight-color="#000" // Color of the highlighted words
43
53
  hover-background-color="#f0f0f0" // Color of the line when you hover over it
44
54
  hide-source-footer="false" // Controls whether the footer at the bottom is a larger one or a more compact GitHub link.
@@ -90,14 +100,44 @@
90
100
  animate();
91
101
  }
92
102
 
93
- function handleSearch() {
94
- const searchInput = document.querySelector('#search-input');
103
+ function handleLoadMetadata() {
104
+ const titleInput = document.querySelector('#title-input');
105
+ const artistInput = document.querySelector('#artist-input');
106
+ const durationInput = document.querySelector('#duration-input');
107
+ const queryInput = document.querySelector('#query-input');
95
108
  const amLyrics = document.querySelector('am-lyrics');
96
- if (searchInput && amLyrics) {
97
- amLyrics.query = searchInput.value;
98
- amLyrics.isrc = '';
99
- amLyrics.musicId = '';
109
+
110
+ if (!amLyrics) return;
111
+
112
+ const title = titleInput?.value.trim() ?? '';
113
+ const artist = artistInput?.value.trim() ?? '';
114
+ const queryText = queryInput?.value.trim() ?? '';
115
+ const durationText = durationInput?.value.trim() ?? '';
116
+ const durationMs = durationText ? Number(durationText) : NaN;
117
+
118
+ if (titleInput) {
119
+ amLyrics.songTitle = title;
120
+ }
121
+
122
+ if (artistInput) {
123
+ amLyrics.songArtist = artist;
124
+ }
125
+
126
+ if (!Number.isNaN(durationMs) && durationMs > 0) {
127
+ amLyrics.songDurationMs = durationMs;
128
+ } else if (durationText === '') {
129
+ amLyrics.songDurationMs = undefined;
130
+ }
131
+
132
+ const fallbackQuery = queryText || [title, artist].filter(Boolean).join(' ');
133
+ if (fallbackQuery) {
134
+ amLyrics.query = fallbackQuery;
135
+ } else {
136
+ amLyrics.query = '';
100
137
  }
138
+
139
+ amLyrics.isrc = '';
140
+ amLyrics.musicId = '';
101
141
  }
102
142
 
103
143
  function resetPlayback() {
@@ -112,16 +152,36 @@
112
152
 
113
153
  document.addEventListener('DOMContentLoaded', () => {
114
154
  const amLyrics = document.querySelector('am-lyrics');
115
- const searchButton = document.querySelector('#search-button');
155
+ const loadButton = document.querySelector('#load-button');
116
156
  const startButton = document.querySelector('#start-button');
117
157
  const resetButton = document.querySelector('#reset-button');
158
+ const titleInput = document.querySelector('#title-input');
159
+ const artistInput = document.querySelector('#artist-input');
160
+ const queryInput = document.querySelector('#query-input');
161
+ const durationInput = document.querySelector('#duration-input');
118
162
 
119
163
  if (amLyrics) {
120
164
  amLyrics.addEventListener('line-click', handleLineClick);
121
165
  }
122
166
 
123
- if (searchButton) {
124
- searchButton.addEventListener('click', handleSearch);
167
+ if (titleInput && amLyrics?.songTitle) {
168
+ titleInput.value = amLyrics.songTitle;
169
+ }
170
+
171
+ if (artistInput && amLyrics?.songArtist) {
172
+ artistInput.value = amLyrics.songArtist;
173
+ }
174
+
175
+ if (queryInput && amLyrics?.query) {
176
+ queryInput.value = amLyrics.query;
177
+ }
178
+
179
+ if (durationInput && typeof amLyrics?.songDurationMs === 'number') {
180
+ durationInput.value = String(amLyrics.songDurationMs);
181
+ }
182
+
183
+ if (loadButton) {
184
+ loadButton.addEventListener('click', handleLoadMetadata);
125
185
  }
126
186
 
127
187
  if (startButton) {
@@ -4,6 +4,10 @@ export declare class AmLyrics extends LitElement {
4
4
  query?: string;
5
5
  musicId?: string;
6
6
  isrc?: string;
7
+ songTitle?: string;
8
+ songArtist?: string;
9
+ songAlbum?: string;
10
+ songDurationMs?: number;
7
11
  highlightColor: string;
8
12
  hoverBackgroundColor: string;
9
13
  hideSourceFooter: boolean;
@@ -19,6 +23,7 @@ export declare class AmLyrics extends LitElement {
19
23
  private activeBackgroundWordIndices;
20
24
  private mainWordProgress;
21
25
  private backgroundWordProgress;
26
+ private lyricsSource;
22
27
  private animationFrameId?;
23
28
  private mainWordAnimations;
24
29
  private backgroundWordAnimations;
@@ -30,6 +35,19 @@ export declare class AmLyrics extends LitElement {
30
35
  connectedCallback(): void;
31
36
  disconnectedCallback(): void;
32
37
  private fetchLyrics;
38
+ private onLyricsLoaded;
39
+ private resolveSongMetadata;
40
+ private searchAppleMusic;
41
+ private static parseQueryMetadata;
42
+ private static searchLyricsPlusCatalog;
43
+ private static fetchLyricsFromYouLyPlus;
44
+ private fetchLyricsFromApple;
45
+ private static convertKPoeLyrics;
46
+ private static ensureAppleWordSpacing;
47
+ private static applySpacingToSyllables;
48
+ private static startsWithPunctuation;
49
+ private static endsWithNoSpaceMarker;
50
+ private static toMilliseconds;
33
51
  firstUpdated(): void;
34
52
  updated(changedProperties: Map<string | number | symbol, unknown>): void;
35
53
  private static arraysEqual;
@@ -1 +1 @@
1
- {"version":3,"file":"AmLyrics.d.ts","sourceRoot":"","sources":["../../src/AmLyrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AA4B5C,qBAAa,QAAS,SAAQ,UAAU;IACtC,MAAM,CAAC,MAAM,0BA+KX;IAGF,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,cAAc,SAAU;IAGxB,oBAAoB,SAAa;IAGjC,gBAAgB,UAAS;IAGzB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,UAAU,UAAQ;IAGlB,WAAW,UAAQ;IAGnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,WAAW,SAAK;IAGhB,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,MAAM,CAAC,CAAe;IAG9B,OAAO,CAAC,iBAAiB,CAAgB;IAGzC,OAAO,CAAC,qBAAqB,CAAkC;IAG/D,OAAO,CAAC,2BAA2B,CAAkC;IAGrE,OAAO,CAAC,gBAAgB,CAAkC;IAG1D,OAAO,CAAC,sBAAsB,CAAkC;IAEhE,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,iBAAiB;IAKjB,oBAAoB;YAUN,WAAW;IAqDzB,YAAY;IASZ,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC;IAoFjE,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,OAAO,CAAC,gBAAgB;IA0BxB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,sBAAsB;IA8D9B,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,eAAe;IA4CvB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAkBzC,OAAO,CAAC,kBAAkB;IA2C1B,OAAO,CAAC,oBAAoB;IAsC5B,OAAO,CAAC,eAAe;IA4HvB,MAAM;CAwKP"}
1
+ {"version":3,"file":"AmLyrics.d.ts","sourceRoot":"","sources":["../../src/AmLyrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AA0E5C,qBAAa,QAAS,SAAQ,UAAU;IACtC,MAAM,CAAC,MAAM,0BAgLX;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,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,cAAc,SAAU;IAGxB,oBAAoB,SAAa;IAGjC,gBAAgB,UAAS;IAGzB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,UAAU,UAAQ;IAGlB,WAAW,UAAQ;IAGnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,WAAW,SAAK;IAGhB,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,MAAM,CAAC,CAAe;IAG9B,OAAO,CAAC,iBAAiB,CAAgB;IAGzC,OAAO,CAAC,qBAAqB,CAAkC;IAG/D,OAAO,CAAC,2BAA2B,CAAkC;IAGrE,OAAO,CAAC,gBAAgB,CAAkC;IAG1D,OAAO,CAAC,sBAAsB,CAAkC;IAGhE,OAAO,CAAC,YAAY,CAAuB;IAE3C,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,iBAAiB;IAKjB,oBAAoB;YAUN,WAAW;IA4DzB,OAAO,CAAC,cAAc;YAkBR,mBAAmB;YA4JnB,gBAAgB;IAwC9B,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBAkCZ,uBAAuB;mBA6CvB,wBAAwB;YAyD/B,oBAAoB;IA8BlC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAiFhC,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAUrC,OAAO,CAAC,MAAM,CAAC,uBAAuB;IA0BtC,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAIpC,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAIpC,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,YAAY;IAWZ,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC;IAwFjE,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,OAAO,CAAC,gBAAgB;IA0BxB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,sBAAsB;IA8D9B,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,eAAe;IA4CvB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAkBzC,OAAO,CAAC,kBAAkB;IA2C1B,OAAO,CAAC,oBAAoB;IAsC5B,OAAO,CAAC,eAAe;IA4HvB,MAAM;CA4KP"}