aqualink 2.7.3 → 2.8.0
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/build/handlers/fetchImage.js +121 -23
- package/build/index.d.ts +169 -10
- package/build/structures/Aqua.js +240 -317
- package/build/structures/Connection.js +20 -24
- package/build/structures/Node.js +113 -88
- package/build/structures/Player.js +155 -169
- package/build/structures/Queue.js +13 -22
- package/build/structures/Rest.js +107 -136
- package/build/structures/Track.js +113 -33
- package/package.json +1 -1
|
@@ -22,56 +22,58 @@ const EVENT_HANDLERS = Object.freeze({
|
|
|
22
22
|
LyricsNotFoundEvent: "lyricsNotFound"
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// Use Sets for O(1) lookups
|
|
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, 4006
|
|
28
|
+
const RECONNECT_CODES = new Set([4015, 4009, 4006]);
|
|
29
29
|
const FAIL_LOAD_TYPES = new Set(["error", "empty", "LOAD_FAILED", "NO_MATCHES"]);
|
|
30
30
|
|
|
31
|
-
class
|
|
31
|
+
class OptimizedUpdateBatcher {
|
|
32
32
|
constructor(player) {
|
|
33
33
|
this.player = player;
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
34
|
+
this.updates = Object.create(null); // Faster than {}
|
|
35
|
+
this.timeoutId = 0;
|
|
36
|
+
this.hasPending = false;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
batch(data, immediate = false) {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Merge updates efficiently
|
|
41
|
+
for (const key in data) {
|
|
42
|
+
this.updates[key] = data[key];
|
|
43
|
+
}
|
|
44
|
+
this.hasPending = true;
|
|
42
45
|
|
|
43
|
-
if (this.
|
|
44
|
-
clearTimeout(this.
|
|
45
|
-
this.
|
|
46
|
+
if (this.timeoutId) {
|
|
47
|
+
clearTimeout(this.timeoutId);
|
|
48
|
+
this.timeoutId = 0;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
if (immediate || data.track) {
|
|
49
|
-
|
|
50
|
-
this.pendingUpdates = {};
|
|
51
|
-
this.hasUpdates = false;
|
|
52
|
-
return this.player.updatePlayer(updates);
|
|
52
|
+
return this._flush();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
this.
|
|
56
|
-
if (this.hasUpdates) {
|
|
57
|
-
const updates = this.pendingUpdates;
|
|
58
|
-
this.pendingUpdates = {};
|
|
59
|
-
this.hasUpdates = false;
|
|
60
|
-
this.player.updatePlayer(updates);
|
|
61
|
-
}
|
|
62
|
-
this.timeout = null;
|
|
63
|
-
}, 32);
|
|
64
|
-
|
|
55
|
+
this.timeoutId = setTimeout(() => this._flush(), 32);
|
|
65
56
|
return Promise.resolve();
|
|
66
57
|
}
|
|
67
58
|
|
|
59
|
+
_flush() {
|
|
60
|
+
if (!this.hasPending) return Promise.resolve();
|
|
61
|
+
|
|
62
|
+
const updates = this.updates;
|
|
63
|
+
this.updates = Object.create(null);
|
|
64
|
+
this.hasPending = false;
|
|
65
|
+
this.timeoutId = 0;
|
|
66
|
+
|
|
67
|
+
return this.player.updatePlayer(updates);
|
|
68
|
+
}
|
|
69
|
+
|
|
68
70
|
destroy() {
|
|
69
|
-
if (this.
|
|
70
|
-
clearTimeout(this.
|
|
71
|
-
this.
|
|
71
|
+
if (this.timeoutId) {
|
|
72
|
+
clearTimeout(this.timeoutId);
|
|
73
|
+
this.timeoutId = 0;
|
|
72
74
|
}
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
75
|
+
this.updates = Object.create(null);
|
|
76
|
+
this.hasPending = false;
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -82,74 +84,87 @@ class Player extends EventEmitter {
|
|
|
82
84
|
|
|
83
85
|
constructor(aqua, nodes, options = {}) {
|
|
84
86
|
super();
|
|
87
|
+
|
|
88
|
+
// Core references
|
|
85
89
|
this.aqua = aqua;
|
|
86
90
|
this.nodes = nodes;
|
|
87
91
|
this.guildId = options.guildId;
|
|
88
92
|
this.textChannel = options.textChannel;
|
|
89
93
|
this.voiceChannel = options.voiceChannel;
|
|
90
94
|
|
|
95
|
+
// Initialize components
|
|
91
96
|
this.connection = new Connection(this);
|
|
92
97
|
this.filters = new Filters(this);
|
|
93
98
|
this.queue = new Queue();
|
|
94
99
|
|
|
100
|
+
// Optimized volume clamping
|
|
95
101
|
const vol = options.defaultVolume ?? 100;
|
|
96
|
-
this.volume =
|
|
102
|
+
this.volume = Math.max(0, Math.min(200, vol));
|
|
97
103
|
|
|
104
|
+
// Direct assignment with validation
|
|
98
105
|
this.loop = VALID_MODES.has(options.loop) ? options.loop : LOOP_MODES.NONE;
|
|
99
|
-
this.shouldDeleteMessage =
|
|
100
|
-
this.leaveOnEnd =
|
|
106
|
+
this.shouldDeleteMessage = !!this.aqua.options.shouldDeleteMessage;
|
|
107
|
+
this.leaveOnEnd = !!this.aqua.options.leaveOnEnd;
|
|
101
108
|
|
|
109
|
+
// Circular buffer for previous tracks (more memory efficient)
|
|
102
110
|
this.previousTracks = new Array(50);
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
111
|
+
this.previousIndex = 0;
|
|
112
|
+
this.previousCount = 0;
|
|
105
113
|
|
|
114
|
+
// State flags - grouped for better cache locality
|
|
106
115
|
this.playing = false;
|
|
107
116
|
this.paused = false;
|
|
108
117
|
this.connected = false;
|
|
118
|
+
this.isAutoplayEnabled = false;
|
|
119
|
+
this.isAutoplay = false;
|
|
120
|
+
|
|
121
|
+
// Track state
|
|
109
122
|
this.current = null;
|
|
110
123
|
this.position = 0;
|
|
111
124
|
this.timestamp = 0;
|
|
112
125
|
this.ping = 0;
|
|
113
126
|
this.nowPlayingMessage = null;
|
|
114
|
-
this.isAutoplayEnabled = false;
|
|
115
|
-
this.isAutoplay = false;
|
|
116
127
|
|
|
117
|
-
|
|
128
|
+
// Optimized components
|
|
129
|
+
this._updateBatcher = new OptimizedUpdateBatcher(this);
|
|
118
130
|
this._dataStore = new Map();
|
|
119
131
|
|
|
120
|
-
|
|
121
|
-
this.
|
|
132
|
+
// Bind methods once
|
|
133
|
+
this._boundPlayerUpdate = this._handlePlayerUpdate.bind(this);
|
|
134
|
+
this._boundEvent = this._handleEvent.bind(this);
|
|
122
135
|
|
|
123
|
-
this.on("playerUpdate", this.
|
|
124
|
-
this.on("event", this.
|
|
136
|
+
this.on("playerUpdate", this._boundPlayerUpdate);
|
|
137
|
+
this.on("event", this._boundEvent);
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
_handlePlayerUpdate(packet) {
|
|
128
|
-
|
|
129
|
-
this.
|
|
130
|
-
this.
|
|
131
|
-
this.
|
|
141
|
+
const state = packet.state;
|
|
142
|
+
this.position = state.position;
|
|
143
|
+
this.connected = state.connected;
|
|
144
|
+
this.ping = state.ping;
|
|
145
|
+
this.timestamp = state.time;
|
|
132
146
|
this.aqua.emit("playerUpdate", this, packet);
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
async _handleEvent(payload) {
|
|
136
|
-
|
|
137
|
-
const handlerName = EVENT_HANDLERS[payload.type];
|
|
150
|
+
const handlerName = EVENT_HANDLERS[payload.type];
|
|
138
151
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
if (!handlerName || typeof this[handlerName] !== "function") {
|
|
153
|
+
this.aqua.emit("nodeError", this, new Error(`Unknown event: ${payload.type}`));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await this[handlerName](this, this.current, payload);
|
|
145
159
|
} catch (error) {
|
|
146
|
-
console.error(`Error handling event ${payload.type}:`, error);
|
|
147
160
|
this.aqua.emit("error", error);
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
get previous() {
|
|
152
|
-
return this.
|
|
165
|
+
return this.previousCount > 0
|
|
166
|
+
? this.previousTracks[(this.previousIndex - 1 + 50) % 50]
|
|
167
|
+
: null;
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
get currenttrack() {
|
|
@@ -161,19 +176,13 @@ class Player extends EventEmitter {
|
|
|
161
176
|
}
|
|
162
177
|
|
|
163
178
|
async autoplay(player) {
|
|
164
|
-
if (!
|
|
165
|
-
if (!this.isAutoplayEnabled) {
|
|
166
|
-
this.aqua.emit("debug", this.guildId, "Autoplay is disabled.");
|
|
167
|
-
return this;
|
|
168
|
-
}
|
|
179
|
+
if (!this.isAutoplayEnabled || !this.previous) return this;
|
|
169
180
|
|
|
170
181
|
this.isAutoplay = true;
|
|
171
|
-
|
|
182
|
+
const prevInfo = this.previous.info;
|
|
183
|
+
const { sourceName, identifier, uri, requester } = prevInfo;
|
|
172
184
|
|
|
173
185
|
try {
|
|
174
|
-
const { sourceName, identifier, uri, requester } = this.previous.info;
|
|
175
|
-
this.aqua.emit("debug", this.guildId, `Attempting autoplay for ${sourceName}`);
|
|
176
|
-
|
|
177
186
|
let query, source;
|
|
178
187
|
|
|
179
188
|
switch (sourceName) {
|
|
@@ -205,7 +214,10 @@ class Player extends EventEmitter {
|
|
|
205
214
|
|
|
206
215
|
const tracks = response.tracks;
|
|
207
216
|
const track = tracks[Math.floor(Math.random() * tracks.length)];
|
|
208
|
-
|
|
217
|
+
|
|
218
|
+
if (!track?.info?.title) {
|
|
219
|
+
throw new Error("Invalid track object");
|
|
220
|
+
}
|
|
209
221
|
|
|
210
222
|
track.requester = this.previous.requester || { id: "Unknown" };
|
|
211
223
|
this.queue.push(track);
|
|
@@ -213,14 +225,12 @@ class Player extends EventEmitter {
|
|
|
213
225
|
|
|
214
226
|
return this;
|
|
215
227
|
} catch (error) {
|
|
216
|
-
console.error("Autoplay error:", error);
|
|
217
228
|
return this.stop();
|
|
218
229
|
}
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
setAutoplay(enabled) {
|
|
222
|
-
this.isAutoplayEnabled =
|
|
223
|
-
this.aqua.emit("debug", this.guildId, `Autoplay has been ${enabled ? "enabled" : "disabled"}.`);
|
|
233
|
+
this.isAutoplayEnabled = !!enabled;
|
|
224
234
|
return this;
|
|
225
235
|
}
|
|
226
236
|
|
|
@@ -232,36 +242,34 @@ class Player extends EventEmitter {
|
|
|
232
242
|
this.playing = true;
|
|
233
243
|
this.position = 0;
|
|
234
244
|
|
|
235
|
-
this.aqua.emit("debug", this.guildId, `Playing track: ${this.current.track}`);
|
|
236
245
|
return this.batchUpdatePlayer({ track: { encoded: this.current.track } }, true);
|
|
237
246
|
}
|
|
238
247
|
|
|
239
248
|
connect(options = this) {
|
|
240
249
|
const { guildId, voiceChannel, deaf = true, mute = false } = options;
|
|
250
|
+
|
|
241
251
|
this.deaf = deaf;
|
|
242
252
|
this.mute = mute;
|
|
253
|
+
this.connected = true;
|
|
254
|
+
|
|
243
255
|
this.send({
|
|
244
256
|
guild_id: guildId,
|
|
245
257
|
channel_id: voiceChannel,
|
|
246
258
|
self_deaf: deaf,
|
|
247
259
|
self_mute: mute,
|
|
248
260
|
});
|
|
249
|
-
|
|
250
|
-
this.aqua.emit("debug", guildId, `Player connected to voice channel: ${voiceChannel}.`);
|
|
261
|
+
|
|
251
262
|
return this;
|
|
252
263
|
}
|
|
253
264
|
|
|
254
265
|
destroy() {
|
|
255
266
|
if (!this.connected) return this;
|
|
256
267
|
|
|
257
|
-
const voiceChannelId = this.voiceChannel ? this.voiceChannel.id || this.voiceChannel : null;
|
|
258
268
|
this._updateBatcher.destroy();
|
|
259
269
|
|
|
260
270
|
this.send({ guild_id: this.guildId, channel_id: null });
|
|
261
|
-
this._lastVoiceChannel = voiceChannelId;
|
|
262
|
-
this.voiceChannel = null;
|
|
263
271
|
this.connected = false;
|
|
264
|
-
this.
|
|
272
|
+
this.voiceChannel = null;
|
|
265
273
|
|
|
266
274
|
if (this.nowPlayingMessage) {
|
|
267
275
|
this.nowPlayingMessage.delete().catch(() => { });
|
|
@@ -276,15 +284,17 @@ class Player extends EventEmitter {
|
|
|
276
284
|
this.nodes.rest.destroyPlayer(this.guildId);
|
|
277
285
|
} catch (error) {
|
|
278
286
|
if (!error.message.includes('ECONNREFUSED')) {
|
|
279
|
-
console.error('Error destroying player
|
|
287
|
+
console.error('Error destroying player:', error);
|
|
280
288
|
}
|
|
281
289
|
}
|
|
282
290
|
}
|
|
283
291
|
|
|
284
|
-
|
|
292
|
+
// Clean up efficiently
|
|
293
|
+
this.previousCount = 0;
|
|
285
294
|
this._dataStore.clear();
|
|
286
295
|
this.removeAllListeners();
|
|
287
296
|
|
|
297
|
+
// Nullify references
|
|
288
298
|
this.queue = null;
|
|
289
299
|
this.previousTracks = null;
|
|
290
300
|
this.connection = null;
|
|
@@ -301,20 +311,16 @@ class Player extends EventEmitter {
|
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
async getLyrics(options = {}) {
|
|
304
|
-
const { query
|
|
314
|
+
const { query, useCurrentTrack = true, skipTrackSource = false } = options;
|
|
305
315
|
|
|
306
316
|
if (query) {
|
|
307
|
-
this.aqua.emit("debug", `[Aqua/Player] Searching lyrics for query: "${query}"`);
|
|
308
317
|
return this.nodes.rest.getLyrics({
|
|
309
|
-
track: {
|
|
310
|
-
info: { title: query }
|
|
311
|
-
},
|
|
318
|
+
track: { info: { title: query } },
|
|
312
319
|
skipTrackSource
|
|
313
320
|
});
|
|
314
321
|
}
|
|
315
322
|
|
|
316
323
|
if (useCurrentTrack && this.playing && this.current?.info) {
|
|
317
|
-
this.aqua.emit("debug", `[Aqua/Player] Getting lyrics for current track: "${this.current.info.title}"`);
|
|
318
324
|
return this.nodes.rest.getLyrics({
|
|
319
325
|
track: {
|
|
320
326
|
info: this.current.info,
|
|
@@ -325,25 +331,24 @@ class Player extends EventEmitter {
|
|
|
325
331
|
});
|
|
326
332
|
}
|
|
327
333
|
|
|
328
|
-
this.aqua.emit("debug", `[Aqua/Player] getLyrics called but no query was provided and no track is playing.`);
|
|
329
334
|
return null;
|
|
330
335
|
}
|
|
331
336
|
|
|
332
|
-
|
|
337
|
+
subscribeLiveLyrics() {
|
|
333
338
|
return this.nodes.rest.subscribeLiveLyrics(this.guildId, false);
|
|
334
339
|
}
|
|
335
340
|
|
|
336
|
-
|
|
341
|
+
unsubscribeLiveLyrics() {
|
|
337
342
|
return this.nodes.rest.unsubscribeLiveLyrics(this.guildId);
|
|
338
343
|
}
|
|
339
344
|
|
|
340
345
|
seek(position) {
|
|
341
346
|
if (!this.playing) return this;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
+
|
|
348
|
+
// Clamp position efficiently
|
|
349
|
+
const maxPos = this.current?.info?.length;
|
|
350
|
+
this.position = Math.max(0, maxPos ? Math.min(position, maxPos) : position);
|
|
351
|
+
|
|
347
352
|
this.batchUpdatePlayer({ position: this.position });
|
|
348
353
|
return this;
|
|
349
354
|
}
|
|
@@ -357,14 +362,18 @@ class Player extends EventEmitter {
|
|
|
357
362
|
}
|
|
358
363
|
|
|
359
364
|
setVolume(volume) {
|
|
360
|
-
|
|
361
|
-
this.volume
|
|
362
|
-
|
|
365
|
+
const vol = Math.max(0, Math.min(200, volume));
|
|
366
|
+
if (this.volume === vol) return this;
|
|
367
|
+
|
|
368
|
+
this.volume = vol;
|
|
369
|
+
this.batchUpdatePlayer({ volume: vol });
|
|
363
370
|
return this;
|
|
364
371
|
}
|
|
365
372
|
|
|
366
373
|
setLoop(mode) {
|
|
367
|
-
if (!VALID_MODES.has(mode))
|
|
374
|
+
if (!VALID_MODES.has(mode)) {
|
|
375
|
+
throw new Error("Invalid loop mode");
|
|
376
|
+
}
|
|
368
377
|
this.loop = mode;
|
|
369
378
|
this.batchUpdatePlayer({ loop: mode });
|
|
370
379
|
return this;
|
|
@@ -377,13 +386,16 @@ class Player extends EventEmitter {
|
|
|
377
386
|
}
|
|
378
387
|
|
|
379
388
|
setVoiceChannel(channel) {
|
|
380
|
-
if (!channel
|
|
381
|
-
if (this.connected && channel === this.voiceChannel)
|
|
389
|
+
if (!channel) throw new TypeError("Channel required");
|
|
390
|
+
if (this.connected && channel === this.voiceChannel) {
|
|
391
|
+
throw new ReferenceError(`Already connected to ${channel}`);
|
|
392
|
+
}
|
|
393
|
+
|
|
382
394
|
this.voiceChannel = channel;
|
|
383
395
|
this.connect({
|
|
384
396
|
deaf: this.deaf,
|
|
385
397
|
guildId: this.guildId,
|
|
386
|
-
voiceChannel:
|
|
398
|
+
voiceChannel: channel,
|
|
387
399
|
textChannel: this.textChannel,
|
|
388
400
|
mute: this.mute,
|
|
389
401
|
});
|
|
@@ -393,41 +405,27 @@ class Player extends EventEmitter {
|
|
|
393
405
|
disconnect() {
|
|
394
406
|
if (!this.connected) return this;
|
|
395
407
|
this.connected = false;
|
|
396
|
-
this.send({ guild_id: this.guildId, channel_id: null });
|
|
397
408
|
this.voiceChannel = null;
|
|
398
|
-
this.
|
|
409
|
+
this.send({ guild_id: this.guildId, channel_id: null });
|
|
399
410
|
return this;
|
|
400
411
|
}
|
|
401
412
|
|
|
413
|
+
// Optimized Fisher-Yates shuffle
|
|
402
414
|
shuffle() {
|
|
403
415
|
const queue = this.queue;
|
|
404
416
|
const len = queue.length;
|
|
405
417
|
|
|
406
418
|
if (len <= 1) return this;
|
|
407
419
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
411
|
-
[queue[i], queue[j]] = [queue[j], queue[i]];
|
|
412
|
-
}
|
|
413
|
-
} else {
|
|
414
|
-
this._shuffleAsync(queue, len - 1);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return this;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
_shuffleAsync(queue, i, chunkSize = 100) {
|
|
421
|
-
const end = Math.max(0, i - chunkSize);
|
|
422
|
-
|
|
423
|
-
for (; i > end; i--) {
|
|
420
|
+
// Inline shuffle for better performance
|
|
421
|
+
for (let i = len - 1; i > 0; i--) {
|
|
424
422
|
const j = Math.floor(Math.random() * (i + 1));
|
|
425
|
-
|
|
423
|
+
const temp = queue[i];
|
|
424
|
+
queue[i] = queue[j];
|
|
425
|
+
queue[j] = temp;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
|
|
429
|
-
setImmediate(() => this._shuffleAsync(queue, i, chunkSize));
|
|
430
|
-
}
|
|
428
|
+
return this;
|
|
431
429
|
}
|
|
432
430
|
|
|
433
431
|
getQueue() {
|
|
@@ -435,7 +433,7 @@ class Player extends EventEmitter {
|
|
|
435
433
|
}
|
|
436
434
|
|
|
437
435
|
replay() {
|
|
438
|
-
return this.seek(
|
|
436
|
+
return this.seek(0);
|
|
439
437
|
}
|
|
440
438
|
|
|
441
439
|
skip() {
|
|
@@ -451,25 +449,20 @@ class Player extends EventEmitter {
|
|
|
451
449
|
|
|
452
450
|
async trackEnd(player, track, payload) {
|
|
453
451
|
if (track) {
|
|
454
|
-
this.previousTracks[this.
|
|
455
|
-
this.
|
|
456
|
-
if (this.
|
|
452
|
+
this.previousTracks[this.previousIndex] = track;
|
|
453
|
+
this.previousIndex = (this.previousIndex + 1) % 50;
|
|
454
|
+
if (this.previousCount < 50) this.previousCount++;
|
|
457
455
|
}
|
|
458
456
|
|
|
459
457
|
if (this.shouldDeleteMessage && this.nowPlayingMessage) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
} catch (error) {
|
|
463
|
-
// Ignore
|
|
464
|
-
} finally {
|
|
465
|
-
this.nowPlayingMessage = null;
|
|
466
|
-
}
|
|
458
|
+
this.nowPlayingMessage.delete().catch(() => { });
|
|
459
|
+
this.nowPlayingMessage = null;
|
|
467
460
|
}
|
|
468
461
|
|
|
469
462
|
const reason = payload.reason;
|
|
470
463
|
if (FAILURE_REASONS.has(reason)) {
|
|
471
|
-
if (
|
|
472
|
-
this.
|
|
464
|
+
if (this.queue.length === 0) {
|
|
465
|
+
this.previousCount = 0;
|
|
473
466
|
this._dataStore.clear();
|
|
474
467
|
this.aqua.emit("queueEnd", player);
|
|
475
468
|
} else {
|
|
@@ -485,14 +478,13 @@ class Player extends EventEmitter {
|
|
|
485
478
|
player.queue.push(track);
|
|
486
479
|
}
|
|
487
480
|
|
|
488
|
-
|
|
489
481
|
if (player.queue.isEmpty()) {
|
|
490
482
|
if (this.isAutoplayEnabled) {
|
|
491
483
|
await player.autoplay(player);
|
|
492
484
|
} else {
|
|
493
485
|
this.playing = false;
|
|
494
486
|
if (this.leaveOnEnd) {
|
|
495
|
-
this.
|
|
487
|
+
this.previousCount = 0;
|
|
496
488
|
this._dataStore.clear();
|
|
497
489
|
this.destroy();
|
|
498
490
|
}
|
|
@@ -515,59 +507,51 @@ class Player extends EventEmitter {
|
|
|
515
507
|
}
|
|
516
508
|
|
|
517
509
|
async socketClosed(player, track, payload) {
|
|
518
|
-
const { code, guildId
|
|
510
|
+
const { code, guildId } = payload || {};
|
|
519
511
|
|
|
520
512
|
if (RECONNECT_CODES.has(code)) {
|
|
521
513
|
try {
|
|
522
|
-
this.connected = false;
|
|
523
|
-
|
|
524
514
|
const voiceChannelId = this.voiceChannel?.id || this.voiceChannel;
|
|
515
|
+
const textChannelId = this.textChannel?.id || this.textChannel;
|
|
516
|
+
const currentTrack = this.current;
|
|
525
517
|
|
|
526
518
|
if (!voiceChannelId) {
|
|
527
|
-
console.error(`Cannot reconnect: No voice channel available for guild ${guildId}`);
|
|
528
519
|
this.aqua.emit("socketClosed", player, payload);
|
|
529
520
|
return;
|
|
530
521
|
}
|
|
531
522
|
|
|
532
|
-
this.aqua.emit("debug", guildId, `Attempting to reconnect to voice channel: ${voiceChannelId}`);
|
|
533
523
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
self_deaf: this.deaf || true
|
|
539
|
-
});
|
|
524
|
+
if (!player.destroyed) {
|
|
525
|
+
await player.destroy();
|
|
526
|
+
this.aqua.emit("playerDestroy", player);
|
|
527
|
+
}
|
|
540
528
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
529
|
+
const newPlayer = await this.aqua.createConnection({
|
|
530
|
+
guildId,
|
|
531
|
+
voiceChannel: voiceChannelId,
|
|
532
|
+
textChannel: textChannelId,
|
|
533
|
+
deaf: this.deaf,
|
|
534
|
+
mute: this.mute,
|
|
535
|
+
defaultVolume: this.volume
|
|
536
|
+
});
|
|
547
537
|
|
|
548
|
-
|
|
538
|
+
if (track) {
|
|
539
|
+
newPlayer.queue.add(track);
|
|
540
|
+
await newPlayer.play();
|
|
541
|
+
}
|
|
549
542
|
return;
|
|
550
543
|
|
|
551
544
|
} catch (error) {
|
|
552
|
-
console.error(
|
|
553
|
-
this.
|
|
545
|
+
console.error("Reconnection failed:", error);
|
|
546
|
+
this.aqua.emit("socketClosed", player, payload);
|
|
554
547
|
}
|
|
548
|
+
return;
|
|
555
549
|
}
|
|
556
550
|
|
|
557
|
-
this.connected = false;
|
|
558
551
|
this.aqua.emit("socketClosed", player, payload);
|
|
559
|
-
|
|
560
|
-
if (this.playing && this.current && this.queue.length > 0) {
|
|
561
|
-
try {
|
|
562
|
-
this.aqua.emit("debug", guildId, "Attempting to resume playback after socket close");
|
|
563
|
-
await this.play();
|
|
564
|
-
} catch (error) {
|
|
565
|
-
console.error(`Failed to resume playback after socket close for guild ${guildId}:`, error);
|
|
566
|
-
this.stop();
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
552
|
}
|
|
570
553
|
|
|
554
|
+
|
|
571
555
|
async lyricsLine(player, track, payload) {
|
|
572
556
|
this.aqua.emit("lyricsLine", player, track, payload);
|
|
573
557
|
}
|
|
@@ -593,7 +577,7 @@ class Player extends EventEmitter {
|
|
|
593
577
|
}
|
|
594
578
|
|
|
595
579
|
clearData() {
|
|
596
|
-
this.
|
|
580
|
+
this.previousCount = 0;
|
|
597
581
|
this._dataStore.clear();
|
|
598
582
|
return this;
|
|
599
583
|
}
|
|
@@ -603,7 +587,9 @@ class Player extends EventEmitter {
|
|
|
603
587
|
}
|
|
604
588
|
|
|
605
589
|
async cleanup() {
|
|
606
|
-
if (!this.playing && !this.paused && this.queue.isEmpty())
|
|
590
|
+
if (!this.playing && !this.paused && this.queue.isEmpty()) {
|
|
591
|
+
this.destroy();
|
|
592
|
+
}
|
|
607
593
|
}
|
|
608
594
|
}
|
|
609
595
|
|