aqualink 2.6.3 → 2.6.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/README.md +9 -0
- package/build/structures/Node.js +12 -0
- package/build/structures/Player.js +43 -2
- package/build/structures/Rest.js +100 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -306,6 +306,15 @@ For detailed usage, API references, and examples, check out our official documen
|
|
|
306
306
|
<a href="#code-ToddyTheNoobDud" title="Code">💻</a>
|
|
307
307
|
<a href="#doc-ToddyTheNoobDud" title="Documentation">📖</a>
|
|
308
308
|
</td>
|
|
309
|
+
<td align="center" valign="top" width="80%">
|
|
310
|
+
<a href="https://github.com/SoulDevs">
|
|
311
|
+
<img src="https://avatars.githubusercontent.com/u/114820381?v=4" width="100px;" alt="SoulDevs"/>
|
|
312
|
+
<br />
|
|
313
|
+
<sub><b>SoulDevs</b></sub>
|
|
314
|
+
</a>
|
|
315
|
+
<br />
|
|
316
|
+
<a href="#code-SoulDevs title="Code">💻</a>
|
|
317
|
+
</td>
|
|
309
318
|
</tr>
|
|
310
319
|
</tbody>
|
|
311
320
|
</table>
|
package/build/structures/Node.js
CHANGED
|
@@ -127,6 +127,18 @@ class Node {
|
|
|
127
127
|
case "ready":
|
|
128
128
|
this._handleReadyOp(payload);
|
|
129
129
|
break;
|
|
130
|
+
case "LyricsLineEvent": {
|
|
131
|
+
const player = payload.guildId ? this.aqua.players.get(payload.guildId) : null;
|
|
132
|
+
const track = payload.track || null;
|
|
133
|
+
this.aqua.emit("LyricsLineEvent", player, track, payload);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "LyricsFoundEvent": {
|
|
137
|
+
const player = payload.guildId ? this.aqua.players.get(payload.guildId) : null;
|
|
138
|
+
const track = payload.track || null;
|
|
139
|
+
this.aqua.emit("LyricsFoundEvent", player, track, payload);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
130
142
|
default:
|
|
131
143
|
if (payload.guildId) {
|
|
132
144
|
const player = this.aqua.players.get(payload.guildId);
|
|
@@ -17,8 +17,11 @@ const EVENT_HANDLERS = Object.freeze({
|
|
|
17
17
|
TrackStuckEvent: "trackStuck",
|
|
18
18
|
TrackChangeEvent: "trackChange",
|
|
19
19
|
WebSocketClosedEvent: "socketClosed",
|
|
20
|
+
LyricsLineEvent: "lyricsLine",
|
|
21
|
+
LyricsFoundEvent: "lyricsFound" // <-- add this line
|
|
20
22
|
});
|
|
21
23
|
|
|
24
|
+
|
|
22
25
|
const VALID_MODES = new Set(Object.values(LOOP_MODES));
|
|
23
26
|
const FAILURE_REASONS = new Set(["LOAD_FAILED", "CLEANUP"]);
|
|
24
27
|
const RECONNECT_CODES = new Set([4015, 4009]);
|
|
@@ -286,11 +289,41 @@ class Player extends EventEmitter {
|
|
|
286
289
|
|
|
287
290
|
async getLyrics(options = {}) {
|
|
288
291
|
const { query = null, useCurrentTrack = true, skipTrackSource = false } = options;
|
|
289
|
-
|
|
290
|
-
if (
|
|
292
|
+
|
|
293
|
+
if (query) {
|
|
294
|
+
this.aqua.emit("debug", `[Aqua/Player] Searching lyrics for query: "${query}"`);
|
|
295
|
+
return this.nodes.rest.getLyrics({
|
|
296
|
+
track: {
|
|
297
|
+
info: { title: query }
|
|
298
|
+
},
|
|
299
|
+
skipTrackSource
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (useCurrentTrack && this.playing && this.current?.info) {
|
|
304
|
+
this.aqua.emit("debug", `[Aqua/Player] Getting lyrics for current track: "${this.current.info.title}"`);
|
|
305
|
+
return this.nodes.rest.getLyrics({
|
|
306
|
+
track: {
|
|
307
|
+
info: this.current.info,
|
|
308
|
+
identifier: this.current.info.identifier,
|
|
309
|
+
guild_id: this.guildId,
|
|
310
|
+
},
|
|
311
|
+
skipTrackSource
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this.aqua.emit("debug", `[Aqua/Player] getLyrics called but no query was provided and no track is playing.`);
|
|
291
316
|
return null;
|
|
292
317
|
}
|
|
293
318
|
|
|
319
|
+
async subscribeLiveLyrics() {
|
|
320
|
+
return this.nodes.rest.subscribeLiveLyrics(this.guildId, false);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async unsubscribeLiveLyrics() {
|
|
324
|
+
return this.nodes.rest.unsubscribeLiveLyrics(this.guildId);
|
|
325
|
+
}
|
|
326
|
+
|
|
294
327
|
seek(position) {
|
|
295
328
|
if (!this.playing) return this;
|
|
296
329
|
this.position += position;
|
|
@@ -479,6 +512,14 @@ class Player extends EventEmitter {
|
|
|
479
512
|
this.aqua.emit("debug", this.guildId, "Player paused due to socket closure.");
|
|
480
513
|
}
|
|
481
514
|
|
|
515
|
+
async lyricsLine(player, track, payload) {
|
|
516
|
+
this.aqua.emit("lyricsLine", player, track, payload);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async lyricsFound(player, track, payload) {
|
|
520
|
+
this.aqua.emit("lyricsFound", player, track, payload);
|
|
521
|
+
}
|
|
522
|
+
|
|
482
523
|
send(data) {
|
|
483
524
|
this.aqua.send({ op: 4, d: data });
|
|
484
525
|
}
|
package/build/structures/Rest.js
CHANGED
|
@@ -34,7 +34,7 @@ class Rest {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
async makeRequest(method, endpoint, body = null) {
|
|
38
38
|
const url = `${this.baseUrl}${endpoint}`;
|
|
39
39
|
|
|
40
40
|
if (!this.agent) {
|
|
@@ -58,7 +58,7 @@ class Rest {
|
|
|
58
58
|
return new Promise((resolve, reject) => {
|
|
59
59
|
const client = this.secure ? https : http;
|
|
60
60
|
const req = client.request(url, options, (res) => {
|
|
61
|
-
if (res.statusCode === 204) return;
|
|
61
|
+
if (res.statusCode === 204) return resolve(null);
|
|
62
62
|
|
|
63
63
|
const chunks = [];
|
|
64
64
|
let totalLength = 0;
|
|
@@ -159,37 +159,115 @@ class Rest {
|
|
|
159
159
|
const endpoint = `/${this.version}/routeplanner/free/address`;
|
|
160
160
|
return this.makeRequest("POST", endpoint, { address });
|
|
161
161
|
}
|
|
162
|
-
async getLyrics({ track }) {
|
|
163
|
-
if (!track) return null;
|
|
164
162
|
|
|
163
|
+
async getLyrics({ track, skipTrackSource = false }) {
|
|
164
|
+
if (!track || (!track.identifier && !track.info?.title && !track.guild_id)) {
|
|
165
|
+
this.aqua.emit("error", "[Aqua/Lyrics] Invalid or insufficient track object provided for lyrics search.");
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
// --- Attempt 1: Get lyrics via the Player endpoint ---
|
|
170
|
+
if (track.guild_id) {
|
|
171
|
+
try {
|
|
172
|
+
this.validateSessionId();
|
|
173
|
+
const playerLyrics = await this.makeRequest(
|
|
174
|
+
"GET",
|
|
175
|
+
`/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/lyrics?skipTrackSource=${skipTrackSource}`
|
|
176
|
+
).catch(() => {
|
|
177
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] First player endpoint failed, trying fallback path for Guild ${track.guild_id}`);
|
|
178
|
+
return this.makeRequest(
|
|
171
179
|
"GET",
|
|
172
|
-
`/${this.version}/lyrics
|
|
173
|
-
|
|
180
|
+
`/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/track/lyrics?skipTrackSource=${skipTrackSource}`
|
|
181
|
+
);
|
|
182
|
+
});
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
if (playerLyrics && !playerLyrics.error) {
|
|
185
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Fetched lyrics using Player endpoint for Guild: ${track.guild_id}`);
|
|
186
|
+
return playerLyrics;
|
|
187
|
+
} else if (playerLyrics && playerLyrics.error) {
|
|
188
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Player endpoint returned error for Guild ${track.guild_id}: ${playerLyrics.message || playerLyrics.error}`);
|
|
177
189
|
}
|
|
178
|
-
}
|
|
179
|
-
this.
|
|
180
|
-
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Player endpoint failed for Guild ${track.guild_id}: ${error.message}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// --- Attempt 2: Get lyrics using the track's direct identifier ---
|
|
196
|
+
if (track.identifier) {
|
|
197
|
+
try {
|
|
198
|
+
const identifierLyrics = await this.makeRequest(
|
|
181
199
|
"GET",
|
|
182
|
-
`/${this.version}/
|
|
200
|
+
`/${this.version}/lyrics/${encodeURIComponent(track.identifier)}`
|
|
183
201
|
);
|
|
184
|
-
|
|
185
|
-
|
|
202
|
+
if (
|
|
203
|
+
identifierLyrics &&
|
|
204
|
+
!(identifierLyrics.status === 404 && identifierLyrics.error === 'Not Found') &&
|
|
205
|
+
!(identifierLyrics.status === 500 && identifierLyrics.error === 'Internal Server Error')
|
|
206
|
+
) {
|
|
207
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Fetched lyrics using Identifier: ${track.identifier}`);
|
|
208
|
+
return identifierLyrics;
|
|
209
|
+
} else if (
|
|
210
|
+
identifierLyrics &&
|
|
211
|
+
(
|
|
212
|
+
(identifierLyrics.status === 404 && identifierLyrics.error === 'Not Found') ||
|
|
213
|
+
(identifierLyrics.status === 500 && identifierLyrics.error === 'Internal Server Error')
|
|
214
|
+
)
|
|
215
|
+
) {
|
|
216
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] No lyrics found for Identifier: ${track.identifier}`);
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Identifier endpoint failed for ${track.identifier}: ${error.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
// --- Attempt 3: Fallback to searching with track metadata ---
|
|
225
|
+
if (track.info?.title) {
|
|
226
|
+
try {
|
|
227
|
+
const title = track.info.title;
|
|
228
|
+
const author = track.info.author;
|
|
229
|
+
|
|
230
|
+
const searchLyrics = await this.makeRequest(
|
|
231
|
+
"GET",
|
|
232
|
+
`/${this.version}/lyrics/search?query=${title}&source=genius`
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (searchLyrics) {
|
|
236
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Fetched lyrics using Search Query: "${author ? `${title} ${author}` : title}"`);
|
|
237
|
+
return searchLyrics;
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Search endpoint failed: ${error.message}`);
|
|
186
241
|
}
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error("Failed to fetch lyrics:", error.message);
|
|
189
|
-
return null;
|
|
190
242
|
}
|
|
243
|
+
|
|
244
|
+
this.aqua.emit("debug", "[Aqua/Lyrics] All lyric fetch attempts failed for the track.");
|
|
191
245
|
return null;
|
|
192
246
|
}
|
|
247
|
+
|
|
248
|
+
async subscribeLiveLyrics(guildId, skipTrackSource = false) {
|
|
249
|
+
this.validateSessionId();
|
|
250
|
+
const endpoint = `/${this.version}/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe?skipTrackSource=${skipTrackSource}`;
|
|
251
|
+
try {
|
|
252
|
+
const res = await this.makeRequest("POST", endpoint);
|
|
253
|
+
return res === null;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Failed to subscribe to live lyrics for Guild ${guildId}: ${error.message}`);
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async unsubscribeLiveLyrics(guildId) {
|
|
261
|
+
this.validateSessionId();
|
|
262
|
+
const endpoint = `/${this.version}/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`;
|
|
263
|
+
try {
|
|
264
|
+
const res = await this.makeRequest("DELETE", endpoint);
|
|
265
|
+
return res === null;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.aqua.emit("debug", `[Aqua/Lyrics] Failed to unsubscribe from live lyrics for Guild ${guildId}: ${error.message}`);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
193
271
|
}
|
|
194
272
|
|
|
195
273
|
module.exports = Rest;
|