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 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>
@@ -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
- if (query) return this.nodes.rest.getLyrics({ track: { info: { title: query }, search: true }, skipTrackSource }) || null;
290
- if (useCurrentTrack && this.playing) return this.nodes.rest.getLyrics({ track: { encoded: this.current.track, guild_id: this.guildId }, skipTrackSource }) || null;
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
  }
@@ -34,7 +34,7 @@ class Rest {
34
34
  }
35
35
  }
36
36
 
37
- async makeRequest(method, endpoint, body = null) {
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
- try {
167
- if (track.search) {
168
- const query = encodeURIComponent(track.info.title);
169
- try {
170
- const res = await this.makeRequest(
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/search?query=${query}&source=genius`
173
- );
180
+ `/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/track/lyrics?skipTrackSource=${skipTrackSource}`
181
+ );
182
+ });
174
183
 
175
- if (res) return res;
176
- } catch (_) {
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
- } else {
179
- this.validateSessionId();
180
- const res = await this.makeRequest(
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}/sessions/${this.sessionId}/players/${track.guild_id}/lyrics`
200
+ `/${this.version}/lyrics/${encodeURIComponent(track.identifier)}`
183
201
  );
184
- console.log(res);
185
- return res;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "2.6.3",
3
+ "version": "2.6.4",
4
4
  "description": "An Lavalink client, focused in pure performance and features",
5
5
  "main": "build/index.js",
6
6
  "types": "index.d.ts",