@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 +38 -9
- package/demo/index.html +75 -15
- package/dist/src/AmLyrics.d.ts +18 -0
- package/dist/src/AmLyrics.d.ts.map +1 -1
- package/dist/src/am-lyrics.js +473 -35
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +473 -35
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +631 -39
package/README.md
CHANGED
|
@@ -18,9 +18,11 @@ Or, just use the CDN.
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<am-lyrics
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
40
|
-
| `music-id` | `string` | `undefined` | Specific Apple Music song ID (
|
|
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` |
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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="
|
|
21
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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="" //
|
|
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
|
|
94
|
-
const
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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 (
|
|
124
|
-
|
|
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) {
|
package/dist/src/AmLyrics.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|