aqualink 2.2.0 → 2.3.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/README.md +25 -28
- package/build/handlers/fetchImage.js +23 -30
- package/build/index.d.ts +255 -1211
- package/build/structures/Aqua.js +48 -62
- package/build/structures/Node.js +207 -171
- package/build/structures/Player.js +58 -33
- package/build/structures/Rest.js +40 -44
- package/package.json +1 -1
package/build/structures/Node.js
CHANGED
|
@@ -8,11 +8,9 @@ class Node {
|
|
|
8
8
|
static WS_OPEN = WebSocket.OPEN;
|
|
9
9
|
|
|
10
10
|
constructor(aqua, connOptions, options = {}) {
|
|
11
|
-
const host = connOptions.host || "localhost";
|
|
12
|
-
|
|
13
11
|
this.aqua = aqua;
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
12
|
+
this.host = connOptions.host || "localhost";
|
|
13
|
+
this.name = connOptions.name || this.host;
|
|
16
14
|
this.port = connOptions.port || 2333;
|
|
17
15
|
this.password = connOptions.password || "youshallnotpass";
|
|
18
16
|
this.secure = !!connOptions.secure;
|
|
@@ -20,16 +18,23 @@ class Node {
|
|
|
20
18
|
this.regions = connOptions.regions || [];
|
|
21
19
|
|
|
22
20
|
this.wsUrl = `ws${this.secure ? "s" : ""}://${this.host}:${this.port}/v4/websocket`;
|
|
23
|
-
|
|
24
21
|
this.rest = new Rest(aqua, this);
|
|
25
22
|
this.resumeTimeout = options.resumeTimeout || 60;
|
|
26
23
|
this.autoResume = !!options.autoResume;
|
|
27
24
|
this.reconnectTimeout = options.reconnectTimeout || 2000;
|
|
28
25
|
this.reconnectTries = options.reconnectTries || 3;
|
|
29
26
|
this.infiniteReconnects = !!options.infiniteReconnects;
|
|
27
|
+
|
|
30
28
|
this.connected = false;
|
|
31
29
|
this.info = null;
|
|
30
|
+
this.ws = null;
|
|
31
|
+
this.reconnectAttempted = 0;
|
|
32
|
+
this.reconnectTimeoutId = null;
|
|
32
33
|
|
|
34
|
+
this.initializeStats();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
initializeStats() {
|
|
33
38
|
this.stats = {
|
|
34
39
|
players: 0,
|
|
35
40
|
playingPlayers: 0,
|
|
@@ -39,189 +44,211 @@ class Node {
|
|
|
39
44
|
frameStats: { sent: 0, nulled: 0, deficit: 0 },
|
|
40
45
|
ping: 0
|
|
41
46
|
};
|
|
42
|
-
|
|
43
|
-
let ws = null;
|
|
44
|
-
let reconnectAttempted = 0;
|
|
45
|
-
let reconnectTimeoutId = null;
|
|
46
|
-
|
|
47
|
-
this._onOpen = () => {
|
|
48
|
-
this.connected = true;
|
|
49
|
-
reconnectAttempted = 0;
|
|
50
|
-
if (aqua.listenerCount('debug') > 0) {
|
|
51
|
-
aqua.emit("debug", this.name, `Connected to ${this.wsUrl}`);
|
|
52
|
-
}
|
|
47
|
+
}
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
.catch(err => {
|
|
62
|
-
this.info = null;
|
|
63
|
-
if (!aqua.bypassChecks?.nodeFetchInfo && aqua.listenerCount('error') > 0) {
|
|
64
|
-
aqua.emit("error", this, `Failed to fetch node info: ${err.message}`);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
49
|
+
_constructHeaders() {
|
|
50
|
+
const headers = {
|
|
51
|
+
Authorization: this.password,
|
|
52
|
+
"User-Id": this.aqua.clientId,
|
|
53
|
+
"Client-Name": `Aqua/${this.aqua.version}`
|
|
67
54
|
};
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
};
|
|
56
|
+
if (this.sessionId) {
|
|
57
|
+
headers["Session-Id"] = this.sessionId;
|
|
58
|
+
}
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
payload = JSON.parse(msg);
|
|
79
|
-
} catch {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
60
|
+
return headers;
|
|
61
|
+
}
|
|
82
62
|
|
|
83
|
-
|
|
84
|
-
|
|
63
|
+
_onOpen() {
|
|
64
|
+
this.connected = true;
|
|
65
|
+
this.reconnectAttempted = 0;
|
|
66
|
+
this.emitDebug(`Connected to ${this.wsUrl}`);
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this._handleReadyOp(payload);
|
|
92
|
-
break;
|
|
93
|
-
default:
|
|
94
|
-
if (payload.guildId) {
|
|
95
|
-
const player = aqua.players.get(payload.guildId);
|
|
96
|
-
if (player) player.emit(op, payload);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
this._onClose = (code, reason) => {
|
|
102
|
-
this.connected = false;
|
|
103
|
-
|
|
104
|
-
if (aqua.listenerCount('nodeDisconnect') > 0) {
|
|
105
|
-
aqua.emit("nodeDisconnect", this, {
|
|
106
|
-
code,
|
|
107
|
-
reason: reason?.toString() || "No reason provided"
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
clearTimeout(reconnectTimeoutId);
|
|
112
|
-
|
|
113
|
-
if (this.infiniteReconnects) {
|
|
114
|
-
if (aqua.listenerCount('nodeReconnect') > 0) {
|
|
115
|
-
aqua.emit("nodeReconnect", this, "Infinite reconnects enabled, trying again in 10 seconds");
|
|
68
|
+
this.rest.makeRequest("GET", "/v4/info")
|
|
69
|
+
.then(info => {
|
|
70
|
+
this.info = info;
|
|
71
|
+
if (this.autoResume && this.sessionId) {
|
|
72
|
+
return this.resumePlayers();
|
|
116
73
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
74
|
+
})
|
|
75
|
+
.catch(err => {
|
|
76
|
+
this.info = null;
|
|
77
|
+
if (!this.aqua.bypassChecks?.nodeFetchInfo) {
|
|
78
|
+
this.emitError(`Failed to fetch node info: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_onError(error) {
|
|
84
|
+
this.aqua.emit("nodeError", this, error);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_onMessage(msg) {
|
|
88
|
+
let payload;
|
|
89
|
+
try {
|
|
90
|
+
payload = JSON.parse(msg);
|
|
91
|
+
} catch {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
120
94
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
95
|
+
const op = payload?.op;
|
|
96
|
+
if (!op) return;
|
|
97
|
+
|
|
98
|
+
switch (op) {
|
|
99
|
+
case "stats":
|
|
100
|
+
this._updateStats(payload);
|
|
101
|
+
break;
|
|
102
|
+
case "ready":
|
|
103
|
+
this._handleReadyOp(payload);
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
if (payload.guildId) {
|
|
107
|
+
const player = this.aqua.players.get(payload.guildId);
|
|
108
|
+
if (player) player.emit(op, payload);
|
|
125
109
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_onClose(code, reason) {
|
|
114
|
+
this.connected = false;
|
|
115
|
+
|
|
116
|
+
this.aqua.emit("nodeDisconnect", this, {
|
|
117
|
+
code,
|
|
118
|
+
reason: reason?.toString() || "No reason provided"
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.scheduleReconnect(code);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
scheduleReconnect(code) {
|
|
125
|
+
this.clearReconnectTimeout();
|
|
126
|
+
|
|
127
|
+
if (code === 1000) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (this.infiniteReconnects) {
|
|
132
|
+
this.aqua.emit("nodeReconnect", this, "Infinite reconnects enabled, trying again in 10 seconds");
|
|
133
|
+
this.reconnectTimeoutId = setTimeout(() => this.connect(), 10000);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
if (this.reconnectAttempted >= this.reconnectTries) {
|
|
138
|
+
this.emitError(new Error(`Max reconnection attempts reached (${this.reconnectTries})`));
|
|
139
|
+
this.destroy(true);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
133
142
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (aqua.listenerCount('nodeReconnect') > 0) {
|
|
137
|
-
aqua.emit("nodeReconnect", this, {
|
|
138
|
-
attempt: reconnectAttempted,
|
|
139
|
-
backoffTime
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
reconnectTimeoutId = setTimeout(() => this.connect(), backoffTime);
|
|
144
|
-
};
|
|
143
|
+
const backoffTime = this.calculateBackoff();
|
|
144
|
+
this.reconnectAttempted++;
|
|
145
145
|
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
ws.once("open", this._onOpen);
|
|
153
|
-
ws.once("error", this._onError);
|
|
154
|
-
ws.on("message", this._onMessage);
|
|
155
|
-
ws.once("close", this._onClose);
|
|
156
|
-
};
|
|
146
|
+
this.aqua.emit("nodeReconnect", this, {
|
|
147
|
+
attempt: this.reconnectAttempted,
|
|
148
|
+
backoffTime
|
|
149
|
+
});
|
|
157
150
|
|
|
158
|
-
this.
|
|
159
|
-
|
|
151
|
+
this.reconnectTimeoutId = setTimeout(() => this.connect(), backoffTime);
|
|
152
|
+
}
|
|
160
153
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
ws = null;
|
|
167
|
-
}
|
|
154
|
+
calculateBackoff() {
|
|
155
|
+
const baseBackoff = this.reconnectTimeout * Math.pow(Node.BACKOFF_MULTIPLIER, this.reconnectAttempted);
|
|
156
|
+
const jitter = Math.random() * Math.min(2000, baseBackoff * 0.2);
|
|
157
|
+
return Math.min(baseBackoff + jitter, Node.MAX_BACKOFF);
|
|
158
|
+
}
|
|
168
159
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
160
|
+
clearReconnectTimeout() {
|
|
161
|
+
if (this.reconnectTimeoutId) {
|
|
162
|
+
clearTimeout(this.reconnectTimeoutId);
|
|
163
|
+
this.reconnectTimeoutId = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
176
166
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
167
|
+
async connect() {
|
|
168
|
+
this.cleanupExistingConnection();
|
|
169
|
+
|
|
170
|
+
this.ws = new WebSocket(this.wsUrl, {
|
|
171
|
+
headers: this._constructHeaders(),
|
|
172
|
+
perMessageDeflate: false,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.ws.once("open", this._onOpen.bind(this));
|
|
176
|
+
this.ws.once("error", this._onError.bind(this));
|
|
177
|
+
this.ws.on("message", this._onMessage.bind(this));
|
|
178
|
+
this.ws.once("close", this._onClose.bind(this));
|
|
179
|
+
}
|
|
184
180
|
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
cleanupExistingConnection() {
|
|
182
|
+
if (this.ws) {
|
|
183
|
+
this.ws.removeAllListeners();
|
|
187
184
|
|
|
188
|
-
if (
|
|
189
|
-
|
|
185
|
+
if (this.ws.readyState === Node.WS_OPEN) {
|
|
186
|
+
try {
|
|
187
|
+
this.ws.close();
|
|
188
|
+
} catch (err) {
|
|
189
|
+
this.emitDebug(`Error closing WebSocket: ${err.message}`);
|
|
190
|
+
}
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
this.
|
|
193
|
-
}
|
|
193
|
+
this.ws = null;
|
|
194
|
+
}
|
|
194
195
|
}
|
|
196
|
+
|
|
197
|
+
destroy(clean = false) {
|
|
198
|
+
this.clearReconnectTimeout();
|
|
199
|
+
this.cleanupExistingConnection();
|
|
195
200
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
"Client-Name": `Aqua/${this.aqua.version}`
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
if (this.sessionId) {
|
|
204
|
-
headers["Session-Id"] = this.sessionId;
|
|
201
|
+
if (clean) {
|
|
202
|
+
this.aqua.emit("nodeDestroy", this);
|
|
203
|
+
this.aqua.nodes.delete(this.name);
|
|
204
|
+
return;
|
|
205
205
|
}
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
|
|
207
|
+
if (this.connected) {
|
|
208
|
+
for (const player of this.aqua.players.values()) {
|
|
209
|
+
if (player.nodes === this) {
|
|
210
|
+
player.destroy();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.connected = false;
|
|
216
|
+
this.aqua.nodes.delete(this.name);
|
|
217
|
+
this.aqua.emit("nodeDestroy", this);
|
|
218
|
+
this.info = null;
|
|
208
219
|
}
|
|
209
220
|
|
|
210
221
|
async getStats() {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
222
|
+
try {
|
|
223
|
+
const newStats = await this.rest.getStats();
|
|
224
|
+
Object.assign(this.stats, newStats);
|
|
225
|
+
return this.stats;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
this.emitError(`Failed to fetch node stats: ${err.message}`);
|
|
228
|
+
return this.stats;
|
|
229
|
+
}
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
_updateStats(payload) {
|
|
217
233
|
if (!payload) return;
|
|
218
234
|
|
|
235
|
+
this._updateBasicStats(payload);
|
|
236
|
+
|
|
237
|
+
this._updateMemoryStats(payload.memory);
|
|
238
|
+
|
|
239
|
+
this._updateCpuStats(payload.cpu);
|
|
240
|
+
|
|
241
|
+
this._updateFrameStats(payload.frameStats);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
_updateBasicStats(payload) {
|
|
219
245
|
this.stats.players = payload.players || this.stats.players;
|
|
220
246
|
this.stats.playingPlayers = payload.playingPlayers || this.stats.playingPlayers;
|
|
221
247
|
this.stats.uptime = payload.uptime || this.stats.uptime;
|
|
222
248
|
this.stats.ping = payload.ping || this.stats.ping;
|
|
223
|
-
|
|
224
|
-
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
_updateMemoryStats(memory = {}) {
|
|
225
252
|
const allocated = memory.allocated || this.stats.memory.allocated;
|
|
226
253
|
const free = memory.free || this.stats.memory.free;
|
|
227
254
|
const used = memory.used || this.stats.memory.used;
|
|
@@ -235,8 +262,9 @@ class Node {
|
|
|
235
262
|
this.stats.memory.freePercentage = (free / allocated) * 100;
|
|
236
263
|
this.stats.memory.usedPercentage = (used / allocated) * 100;
|
|
237
264
|
}
|
|
238
|
-
|
|
239
|
-
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
_updateCpuStats(cpu = {}) {
|
|
240
268
|
const cores = cpu.cores || this.stats.cpu.cores;
|
|
241
269
|
|
|
242
270
|
this.stats.cpu.cores = cores;
|
|
@@ -246,9 +274,10 @@ class Node {
|
|
|
246
274
|
if (cores) {
|
|
247
275
|
this.stats.cpu.lavalinkLoadPercentage = (cpu.lavalinkLoad / cores) * 100;
|
|
248
276
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
_updateFrameStats(frameStats = {}) {
|
|
280
|
+
if (!frameStats) return;
|
|
252
281
|
this.stats.frameStats.sent = frameStats.sent || this.stats.frameStats.sent;
|
|
253
282
|
this.stats.frameStats.nulled = frameStats.nulled || this.stats.frameStats.nulled;
|
|
254
283
|
this.stats.frameStats.deficit = frameStats.deficit || this.stats.frameStats.deficit;
|
|
@@ -256,18 +285,13 @@ class Node {
|
|
|
256
285
|
|
|
257
286
|
_handleReadyOp(payload) {
|
|
258
287
|
if (!payload.sessionId) {
|
|
259
|
-
|
|
260
|
-
this.aqua.emit("error", this, "Ready payload missing sessionId");
|
|
261
|
-
}
|
|
288
|
+
this.emitError("Ready payload missing sessionId");
|
|
262
289
|
return;
|
|
263
290
|
}
|
|
264
291
|
|
|
265
292
|
this.sessionId = payload.sessionId;
|
|
266
293
|
this.rest.setSessionId(payload.sessionId);
|
|
267
|
-
|
|
268
|
-
if (this.aqua.listenerCount('nodeConnect') > 0) {
|
|
269
|
-
this.aqua.emit("nodeConnect", this);
|
|
270
|
-
}
|
|
294
|
+
this.aqua.emit("nodeConnect", this);
|
|
271
295
|
}
|
|
272
296
|
|
|
273
297
|
async resumePlayers() {
|
|
@@ -277,13 +301,25 @@ class Node {
|
|
|
277
301
|
timeout: this.resumeTimeout
|
|
278
302
|
});
|
|
279
303
|
|
|
280
|
-
|
|
281
|
-
this.aqua.emit("debug", this.name, `Successfully resumed session ${this.sessionId}`);
|
|
282
|
-
}
|
|
304
|
+
this.emitDebug(`Successfully resumed session ${this.sessionId}`);
|
|
283
305
|
} catch (err) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
306
|
+
this.emitError(`Failed to resume session: ${err.message}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
emitDebug(message) {
|
|
311
|
+
if (this.aqua.listenerCount('debug') > 0) {
|
|
312
|
+
this.aqua.emit("debug", this.name, message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
emitError(error) {
|
|
317
|
+
const errorObj = error instanceof Error ? error : new Error(error);
|
|
318
|
+
|
|
319
|
+
console.error(`[Aqua] [${this.name}] Error:`, errorObj);
|
|
320
|
+
|
|
321
|
+
if (this.aqua.listenerCount('error') > 0) {
|
|
322
|
+
this.aqua.emit("error", this, errorObj);
|
|
287
323
|
}
|
|
288
324
|
}
|
|
289
325
|
}
|
|
@@ -40,8 +40,10 @@ class Player extends EventEmitter {
|
|
|
40
40
|
this.loop = Player.validModes.has(options.loop) ? options.loop : Player.LOOP_MODES.NONE;
|
|
41
41
|
|
|
42
42
|
this.queue = new Queue();
|
|
43
|
-
this.previousTracks = Array(50);
|
|
43
|
+
this.previousTracks = new Array(50);
|
|
44
|
+
this.previousTracksIndex = 0;
|
|
44
45
|
this.previousTracksCount = 0;
|
|
46
|
+
|
|
45
47
|
this.shouldDeleteMessage = Boolean(options.shouldDeleteMessage);
|
|
46
48
|
this.leaveOnEnd = Boolean(options.leaveOnEnd);
|
|
47
49
|
|
|
@@ -56,15 +58,19 @@ class Player extends EventEmitter {
|
|
|
56
58
|
this.isAutoplayEnabled = false;
|
|
57
59
|
this.isAutoplay = false;
|
|
58
60
|
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
+
this._boundHandlers = {
|
|
62
|
+
playerUpdate: this._handlePlayerUpdate.bind(this),
|
|
63
|
+
event: this._handleEvent.bind(this)
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
this.on("playerUpdate", this._boundHandlers.playerUpdate);
|
|
67
|
+
this.on("event", this._boundHandlers.event);
|
|
61
68
|
|
|
62
|
-
this.
|
|
63
|
-
this.on("event", this._handleEvent);
|
|
69
|
+
this._dataStore = null;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
get previous() {
|
|
67
|
-
return this.previousTracksCount > 0 ? this.previousTracks[
|
|
73
|
+
return this.previousTracksCount > 0 ? this.previousTracks[this.previousTracksIndex] : null;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
get currenttrack() {
|
|
@@ -154,17 +160,11 @@ class Player extends EventEmitter {
|
|
|
154
160
|
addToPreviousTrack(track) {
|
|
155
161
|
if (!track) return;
|
|
156
162
|
|
|
163
|
+
this.previousTracks[this.previousTracksIndex] = track;
|
|
164
|
+
this.previousTracksIndex = (this.previousTracksIndex + 1) % 50;
|
|
165
|
+
|
|
157
166
|
if (this.previousTracksCount < 50) {
|
|
158
|
-
for (let i = this.previousTracksCount; i > 0; i--) {
|
|
159
|
-
this.previousTracks[i] = this.previousTracks[i-1];
|
|
160
|
-
}
|
|
161
|
-
this.previousTracks[0] = track;
|
|
162
167
|
this.previousTracksCount++;
|
|
163
|
-
} else {
|
|
164
|
-
for (let i = 49; i > 0; i--) {
|
|
165
|
-
this.previousTracks[i] = this.previousTracks[i-1];
|
|
166
|
-
}
|
|
167
|
-
this.previousTracks[0] = track;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -222,19 +222,34 @@ class Player extends EventEmitter {
|
|
|
222
222
|
|
|
223
223
|
this.disconnect();
|
|
224
224
|
|
|
225
|
-
|
|
226
|
-
this.nowPlayingMessage.delete().catch(() => {});
|
|
227
|
-
this.nowPlayingMessage = null;
|
|
228
|
-
}
|
|
225
|
+
this._cleanupNowPlayingMessage();
|
|
229
226
|
|
|
230
227
|
this.isAutoplay = false;
|
|
231
228
|
|
|
229
|
+
this.off("playerUpdate", this._boundHandlers.playerUpdate);
|
|
230
|
+
this.off("event", this._boundHandlers.event);
|
|
231
|
+
|
|
232
232
|
this.aqua.destroyPlayer(this.guildId);
|
|
233
233
|
this.nodes.rest.destroyPlayer(this.guildId);
|
|
234
|
+
|
|
234
235
|
this.clearData();
|
|
235
236
|
this.removeAllListeners();
|
|
237
|
+
|
|
238
|
+
this._boundHandlers = null;
|
|
239
|
+
this.queue = null;
|
|
240
|
+
this.previousTracks = null;
|
|
241
|
+
this.connection = null;
|
|
242
|
+
this.filters = null;
|
|
243
|
+
|
|
236
244
|
return this;
|
|
237
245
|
}
|
|
246
|
+
|
|
247
|
+
_cleanupNowPlayingMessage() {
|
|
248
|
+
if (this.nowPlayingMessage) {
|
|
249
|
+
this.nowPlayingMessage.delete().catch(() => {});
|
|
250
|
+
this.nowPlayingMessage = null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
238
253
|
|
|
239
254
|
pause(paused) {
|
|
240
255
|
if (this.paused === paused) return this;
|
|
@@ -312,6 +327,8 @@ class Player extends EventEmitter {
|
|
|
312
327
|
}
|
|
313
328
|
|
|
314
329
|
disconnect() {
|
|
330
|
+
if (!this.connected) return this;
|
|
331
|
+
|
|
315
332
|
this.connected = false;
|
|
316
333
|
this.send({ guild_id: this.guildId, channel_id: null });
|
|
317
334
|
this.voiceChannel = null;
|
|
@@ -372,26 +389,34 @@ class Player extends EventEmitter {
|
|
|
372
389
|
return;
|
|
373
390
|
}
|
|
374
391
|
|
|
392
|
+
await this._handleTrackLooping(player, track);
|
|
393
|
+
|
|
394
|
+
if (player.queue.isEmpty()) {
|
|
395
|
+
await this._handleEmptyQueue(player);
|
|
396
|
+
} else {
|
|
397
|
+
this.aqua.emit("trackEnd", player, track, reason);
|
|
398
|
+
await player.play();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async _handleTrackLooping(player, track) {
|
|
375
403
|
if (this.loop === Player.LOOP_MODES.TRACK) {
|
|
376
404
|
player.queue.unshift(track);
|
|
377
405
|
} else if (this.loop === Player.LOOP_MODES.QUEUE) {
|
|
378
406
|
player.queue.push(track);
|
|
379
407
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
this.playing = false;
|
|
386
|
-
if (this.leaveOnEnd) {
|
|
387
|
-
this.clearData();
|
|
388
|
-
this.cleanup();
|
|
389
|
-
}
|
|
390
|
-
this.aqua.emit("queueEnd", player);
|
|
391
|
-
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async _handleEmptyQueue(player) {
|
|
411
|
+
if (this.isAutoplayEnabled) {
|
|
412
|
+
await player.autoplay(player);
|
|
392
413
|
} else {
|
|
393
|
-
this.
|
|
394
|
-
|
|
414
|
+
this.playing = false;
|
|
415
|
+
if (this.leaveOnEnd) {
|
|
416
|
+
this.clearData();
|
|
417
|
+
this.cleanup();
|
|
418
|
+
}
|
|
419
|
+
this.aqua.emit("queueEnd", player);
|
|
395
420
|
}
|
|
396
421
|
}
|
|
397
422
|
|