aqualink 2.7.2 → 2.7.3-hotfix

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.
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const REGION_REGEX = /^([a-z]+)/;
4
+
3
5
  class Connection {
4
6
  constructor(player) {
5
7
  this.player = player;
@@ -20,51 +22,75 @@ class Connection {
20
22
  if (!data?.endpoint) return;
21
23
 
22
24
  const { endpoint, token } = data;
23
- const newRegion = endpoint.split('.')[0];
24
- if (!newRegion) return;
25
-
26
- if (this.region !== newRegion) {
27
- const oldRegion = this.region;
28
- this.endpoint = endpoint;
29
- this.token = token;
30
- this.region = newRegion;
31
-
32
- this.aqua.emit(
33
- "debug",
34
- `[Player ${this.guildId} - CONNECTION] Voice Server: ${
35
- oldRegion ? `Changed from ${oldRegion} to ${newRegion}` : newRegion
36
- }`
37
- );
25
+
26
+ const regionMatch = REGION_REGEX.exec(endpoint);
27
+ if (!regionMatch) return;
28
+
29
+ const newRegion = regionMatch[1];
30
+
31
+ if (this.endpoint === endpoint && this.token === token && this.region === newRegion) {
32
+ return;
33
+ }
38
34
 
39
- this._updatePlayerVoiceData();
35
+ const oldRegion = this.region;
36
+ this.endpoint = endpoint;
37
+ this.token = token;
38
+ this.region = newRegion;
39
+
40
+ this.aqua.emit("debug",
41
+ `[Player ${this.guildId} - CONNECTION] Voice Server: ${oldRegion ? `Changed from ${oldRegion} to ${newRegion}` : newRegion}`
42
+ );
43
+
44
+ if (this.player.paused) {
45
+ this.player.paused = false;
40
46
  }
47
+
48
+ this._updatePlayerVoiceData();
41
49
  }
42
50
 
43
51
  setStateUpdate(data) {
44
52
  if (!data) {
45
- this.player?.destroy();
53
+ this._destroyPlayer();
46
54
  return;
47
55
  }
48
56
 
49
57
  const { channel_id, session_id, self_deaf, self_mute } = data;
58
+
50
59
  if (!channel_id || !session_id) {
51
- this.player?.destroy();
60
+ this._destroyPlayer();
52
61
  return;
53
62
  }
54
63
 
55
64
  if (this.voiceChannel !== channel_id) {
56
65
  this.aqua.emit("playerMove", this.voiceChannel, channel_id);
57
66
  this.voiceChannel = channel_id;
67
+ this.player.voiceChannel = channel_id;
58
68
  }
59
69
 
60
- this.selfDeaf = Boolean(self_deaf);
61
- this.selfMute = Boolean(self_mute);
62
- this.sessionId = session_id;
70
+ this.selfDeaf = !!self_deaf;
71
+ this.selfMute = !!self_mute;
72
+
73
+ if (this.sessionId !== session_id) {
74
+ this.sessionId = session_id;
75
+ this._updatePlayerVoiceData();
76
+ }
77
+ }
78
+
79
+ _destroyPlayer() {
80
+ if (this.player) {
81
+ this.player.destroy();
82
+ this.aqua.emit("playerDestroy", this.player);
83
+ }
63
84
  }
64
85
 
65
86
  _updatePlayerVoiceData() {
66
87
  if (!this.player) return;
67
88
 
89
+ if (!this.sessionId || !this.endpoint || !this.token) {
90
+ this.aqua.emit("debug", `[Player ${this.guildId}] Incomplete voice data, waiting for complete data`);
91
+ return;
92
+ }
93
+
68
94
  const voiceData = {
69
95
  sessionId: this.sessionId,
70
96
  endpoint: this.endpoint,
@@ -87,6 +113,7 @@ class Connection {
87
113
  });
88
114
  }
89
115
  }
116
+
90
117
  }
91
118
 
92
- module.exports = Connection;
119
+ module.exports = Connection;
@@ -18,14 +18,14 @@ const EVENT_HANDLERS = Object.freeze({
18
18
  TrackChangeEvent: "trackChange",
19
19
  WebSocketClosedEvent: "socketClosed",
20
20
  LyricsLineEvent: "lyricsLine",
21
- LyricsFoundEvent: "lyricsFound" ,
21
+ LyricsFoundEvent: "lyricsFound",
22
22
  LyricsNotFoundEvent: "lyricsNotFound"
23
23
  });
24
24
 
25
25
 
26
26
  const VALID_MODES = new Set(Object.values(LOOP_MODES));
27
27
  const FAILURE_REASONS = new Set(["LOAD_FAILED", "CLEANUP"]);
28
- const RECONNECT_CODES = new Set([4015, 4009]);
28
+ const RECONNECT_CODES = new Set([4015, 4009, 4006, 1000]);
29
29
  const FAIL_LOAD_TYPES = new Set(["error", "empty", "LOAD_FAILED", "NO_MATCHES"]);
30
30
 
31
31
  class UpdateBatcher {
@@ -94,7 +94,7 @@ class Player extends EventEmitter {
94
94
 
95
95
  const vol = options.defaultVolume ?? 100;
96
96
  this.volume = vol < 0 ? 0 : vol > 200 ? 200 : vol;
97
-
97
+
98
98
  this.loop = VALID_MODES.has(options.loop) ? options.loop : LOOP_MODES.NONE;
99
99
  this.shouldDeleteMessage = Boolean(this.aqua.options.shouldDeleteMessage);
100
100
  this.leaveOnEnd = Boolean(this.aqua.options.leaveOnEnd);
@@ -249,47 +249,47 @@ class Player extends EventEmitter {
249
249
  return this;
250
250
  }
251
251
 
252
- destroy() {
253
- if (!this.connected) return this;
252
+ destroy() {
253
+ if (!this.connected) return this;
254
254
 
255
- const voiceChannelId = this.voiceChannel ? this.voiceChannel.id || this.voiceChannel : null;
256
- this._updateBatcher.destroy();
255
+ const voiceChannelId = this.voiceChannel ? this.voiceChannel.id || this.voiceChannel : null;
256
+ this._updateBatcher.destroy();
257
257
 
258
- this.send({ guild_id: this.guildId, channel_id: null });
259
- this._lastVoiceChannel = voiceChannelId;
260
- this.voiceChannel = null;
261
- this.connected = false;
262
- this.send({ guild_id: this.guildId, channel_id: null });
258
+ this.send({ guild_id: this.guildId, channel_id: null });
259
+ this._lastVoiceChannel = voiceChannelId;
260
+ this.voiceChannel = null;
261
+ this.connected = false;
262
+ this.send({ guild_id: this.guildId, channel_id: null });
263
263
 
264
- if (this.nowPlayingMessage) {
265
- this.nowPlayingMessage.delete().catch(() => { });
266
- this.nowPlayingMessage = null;
267
- }
264
+ if (this.nowPlayingMessage) {
265
+ this.nowPlayingMessage.delete().catch(() => { });
266
+ this.nowPlayingMessage = null;
267
+ }
268
268
 
269
- this.isAutoplay = false;
270
- this.aqua.destroyPlayer(this.guildId);
271
-
272
- if (this.nodes?.connected) {
273
- try {
274
- this.nodes.rest.destroyPlayer(this.guildId);
275
- } catch (error) {
276
- if (!error.message.includes('ECONNREFUSED')) {
277
- console.error('Error destroying player on node:', error);
269
+ this.isAutoplay = false;
270
+ this.aqua.destroyPlayer(this.guildId);
271
+
272
+ if (this.nodes?.connected) {
273
+ try {
274
+ this.nodes.rest.destroyPlayer(this.guildId);
275
+ } catch (error) {
276
+ if (!error.message.includes('ECONNREFUSED')) {
277
+ console.error('Error destroying player on node:', error);
278
+ }
278
279
  }
279
280
  }
280
- }
281
-
282
- this.previousTracksCount = 0;
283
- this._dataStore.clear();
284
- this.removeAllListeners();
285
281
 
286
- this.queue = null;
287
- this.previousTracks = null;
288
- this.connection = null;
289
- this.filters = null;
282
+ this.previousTracksCount = 0;
283
+ this._dataStore.clear();
284
+ this.removeAllListeners();
290
285
 
291
- return this;
292
- }
286
+ this.queue = null;
287
+ this.previousTracks = null;
288
+ this.connection = null;
289
+ this.filters = null;
290
+
291
+ return this;
292
+ }
293
293
 
294
294
  pause(paused) {
295
295
  if (this.paused === paused) return this;
@@ -335,16 +335,16 @@ class Player extends EventEmitter {
335
335
  return this.nodes.rest.unsubscribeLiveLyrics(this.guildId);
336
336
  }
337
337
 
338
- seek(position) {
339
- if (!this.playing) return this;
340
- if (position < 0) position = 0;
341
- if (this.current?.info?.length && position > this.current.info.length) {
342
- position = this.current.info.length;
338
+ seek(position) {
339
+ if (!this.playing) return this;
340
+ if (position < 0) position = 0;
341
+ if (this.current?.info?.length && position > this.current.info.length) {
342
+ position = this.current.info.length;
343
+ }
344
+ this.position = position;
345
+ this.batchUpdatePlayer({ position: this.position });
346
+ return this;
343
347
  }
344
- this.position = position;
345
- this.batchUpdatePlayer({ position: this.position });
346
- return this;
347
- }
348
348
 
349
349
  stop() {
350
350
  if (!this.playing) return this;
@@ -400,7 +400,7 @@ class Player extends EventEmitter {
400
400
  shuffle() {
401
401
  const queue = this.queue;
402
402
  const len = queue.length;
403
-
403
+
404
404
  if (len <= 1) return this;
405
405
 
406
406
  if (len < 200) {
@@ -411,18 +411,18 @@ class Player extends EventEmitter {
411
411
  } else {
412
412
  this._shuffleAsync(queue, len - 1);
413
413
  }
414
-
414
+
415
415
  return this;
416
416
  }
417
417
 
418
418
  _shuffleAsync(queue, i, chunkSize = 100) {
419
419
  const end = Math.max(0, i - chunkSize);
420
-
420
+
421
421
  for (; i > end; i--) {
422
422
  const j = Math.floor(Math.random() * (i + 1));
423
423
  [queue[i], queue[j]] = [queue[j], queue[i]];
424
424
  }
425
-
425
+
426
426
  if (i > 0) {
427
427
  setImmediate(() => this._shuffleAsync(queue, i, chunkSize));
428
428
  }
@@ -512,19 +512,58 @@ class Player extends EventEmitter {
512
512
  return this.stop();
513
513
  }
514
514
 
515
- async socketClosed(player, payload) {
516
- const { code, guildId } = payload || {};
515
+ async socketClosed(player, track, payload) {
516
+ const { code, guildId, reason } = payload || {};
517
+
517
518
  if (RECONNECT_CODES.has(code)) {
518
- this.send({
519
- guild_id: guildId,
520
- channel_id: this.voiceChannel,
521
- self_mute: this.mute,
522
- self_deaf: this.deaf
523
- });
519
+ try {
520
+ this.connected = false;
521
+
522
+ const voiceChannelId = this.voiceChannel?.id || this.voiceChannel;
523
+
524
+ if (!voiceChannelId) {
525
+ console.error(`Cannot reconnect: No voice channel available for guild ${guildId}`);
526
+ this.aqua.emit("socketClosed", player, payload);
527
+ return;
528
+ }
529
+
530
+ this.aqua.emit("debug", guildId, `Attempting to reconnect to voice channel: ${voiceChannelId}`);
531
+
532
+ this.send({
533
+ guild_id: guildId,
534
+ channel_id: voiceChannelId,
535
+ self_mute: this.mute || false,
536
+ self_deaf: this.deaf || true
537
+ });
538
+
539
+ setTimeout(() => {
540
+ if (!this.connected) {
541
+ console.error(`Reconnection failed for guild ${guildId} after timeout`);
542
+ this.aqua.emit("socketClosed", player, payload);
543
+ }
544
+ }, 5000);
545
+
546
+ this.aqua.emit("debug", guildId, `Reconnection attempt sent for voice channel: ${voiceChannelId}`);
547
+ return;
548
+
549
+ } catch (error) {
550
+ console.error(`Failed to reconnect socket for guild ${guildId}:`, error);
551
+ this.connected = false;
552
+ }
524
553
  }
554
+
555
+ this.connected = false;
525
556
  this.aqua.emit("socketClosed", player, payload);
526
- this.pause(true);
527
- this.aqua.emit("debug", this.guildId, "Player paused due to socket closure.");
557
+
558
+ if (this.playing && this.current && this.queue.length > 0) {
559
+ try {
560
+ this.aqua.emit("debug", guildId, "Attempting to resume playback after socket close");
561
+ await this.play();
562
+ } catch (error) {
563
+ console.error(`Failed to resume playback after socket close for guild ${guildId}:`, error);
564
+ this.stop();
565
+ }
566
+ }
528
567
  }
529
568
 
530
569
  async lyricsLine(player, track, payload) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "2.7.2",
3
+ "version": "2.7.3-hotfix",
4
4
  "description": "An Lavalink client, focused in pure performance and features",
5
5
  "main": "build/index.js",
6
6
  "types": "index.d.ts",