aqualink 1.7.0-beta5 → 1.7.0-beta6
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/structures/Aqua.js +41 -31
- package/build/structures/Node.js +68 -48
- package/build/structures/Player.js +7 -3
- package/package.json +1 -1
package/build/structures/Aqua.js
CHANGED
|
@@ -6,28 +6,27 @@ const { version: pkgVersion } = require("../../package.json");
|
|
|
6
6
|
const URL_REGEX = /^https?:\/\//;
|
|
7
7
|
|
|
8
8
|
class Aqua extends EventEmitter {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
constructor(client, nodes, options) {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} client - The client instance.
|
|
11
|
+
* @param {Array<Object>} nodes - An array of node configurations.
|
|
12
|
+
* @param {Object} options - Configuration options for Aqua.
|
|
13
|
+
* @param {Function} options.send - Function to send data.
|
|
14
|
+
* @param {string} [options.defaultSearchPlatform="ytsearch"] - Default search platform. Options include:
|
|
15
|
+
* - "youtube music": "ytmsearch"
|
|
16
|
+
* - "youtube": "ytsearch"
|
|
17
|
+
* - "spotify": "spsearch"
|
|
18
|
+
* - "jiosaavn": "jssearch"
|
|
19
|
+
* - "soundcloud": "scsearch"
|
|
20
|
+
* - "deezer": "dzsearch"
|
|
21
|
+
* - "tidal": "tdsearch"
|
|
22
|
+
* - "applemusic": "amsearch"
|
|
23
|
+
* - "bandcamp": "bcsearch"
|
|
24
|
+
* @param {string} [options.restVersion="v4"] - Version of the REST API.
|
|
25
|
+
* @param {Array<Object>} [options.plugins=[]] - Plugins to load.
|
|
26
|
+
* @param {boolean} [options.autoResume=false] - Automatically resume tracks on reconnect.
|
|
27
|
+
* @param {boolean} [options.infiniteReconnects=false] - Reconnect infinitely (default: false).
|
|
28
|
+
*/
|
|
29
|
+
constructor(client, nodes, options = {}) {
|
|
31
30
|
super();
|
|
32
31
|
this.validateInputs(client, nodes, options);
|
|
33
32
|
this.client = client;
|
|
@@ -36,22 +35,33 @@ class Aqua extends EventEmitter {
|
|
|
36
35
|
this.players = new Map();
|
|
37
36
|
this.clientId = null;
|
|
38
37
|
this.initiated = false;
|
|
39
|
-
this.shouldDeleteMessage = options.shouldDeleteMessage || false;
|
|
40
|
-
this.defaultSearchPlatform = options.defaultSearchPlatform || "ytsearch";
|
|
41
|
-
this.restVersion = options.restVersion || "v4";
|
|
42
|
-
this.plugins = options.plugins || [];
|
|
43
|
-
this.version = pkgVersion;
|
|
44
38
|
this.options = options;
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
39
|
+
|
|
40
|
+
this.shouldDeleteMessage = this.getOption(options, 'shouldDeleteMessage', false);
|
|
41
|
+
this.defaultSearchPlatform = this.getOption(options, 'defaultSearchPlatform', 'ytsearch');
|
|
42
|
+
this.leaveOnEnd = this.getOption(options, 'leaveOnEnd', true);
|
|
43
|
+
this.restVersion = this.getOption(options, 'restVersion', 'v4');
|
|
44
|
+
this.plugins = this.getOption(options, 'plugins', []);
|
|
45
|
+
this.version = pkgVersion;
|
|
46
|
+
this.send = options.send || this.defaultSendFunction;
|
|
47
|
+
this.autoResume = this.getOption(options, 'autoResume', false);
|
|
48
|
+
this.infiniteReconnects = this.getOption(options, 'infiniteReconnects', false);
|
|
49
|
+
|
|
48
50
|
this.setMaxListeners(0);
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
getOption(options, key, defaultValue) {
|
|
54
|
+
return options.hasOwnProperty(key) ? options[key] : defaultValue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
defaultSendFunction(payload) {
|
|
58
|
+
const guild = this.client.guilds.cache.get(payload.d.guild_id);
|
|
59
|
+
if (guild) guild.shard.send(payload);
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
validateInputs(client, nodes, options) {
|
|
52
63
|
if (!client) throw new Error("Client is required to initialize Aqua");
|
|
53
64
|
if (!Array.isArray(nodes) || !nodes.length) throw new Error(`Nodes must be a non-empty Array (Received ${typeof nodes})`);
|
|
54
|
-
if (typeof options?.send !== "function") throw new Error("Send function is required to initialize Aqua");
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
get leastUsedNodes() {
|
package/build/structures/Node.js
CHANGED
|
@@ -3,7 +3,7 @@ const { Rest } = require("./Rest");
|
|
|
3
3
|
|
|
4
4
|
class Node {
|
|
5
5
|
#ws = null;
|
|
6
|
-
#statsCache =
|
|
6
|
+
#statsCache = new Map();
|
|
7
7
|
#lastStatsRequest = 0;
|
|
8
8
|
#reconnectAttempted = 0;
|
|
9
9
|
#reconnectTimeoutId = null;
|
|
@@ -37,7 +37,7 @@ class Node {
|
|
|
37
37
|
this.infiniteReconnects = options?.infiniteReconnects ?? false;
|
|
38
38
|
this.connected = false;
|
|
39
39
|
this.info = null;
|
|
40
|
-
this.stats =
|
|
40
|
+
this.stats = this.#createStats();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
#createStats() {
|
|
@@ -47,24 +47,25 @@ class Node {
|
|
|
47
47
|
uptime: 0,
|
|
48
48
|
memory: { free: 0, used: 0, allocated: 0, reservable: 0, freePercentage: 0, usedPercentage: 0 },
|
|
49
49
|
cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0, lavalinkLoadPercentage: 0 },
|
|
50
|
-
frameStats: { sent:
|
|
50
|
+
frameStats: { sent: 0, nulled: 0, deficit: 0 },
|
|
51
51
|
ping: 0
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
async connect() {
|
|
56
56
|
this.#cleanup();
|
|
57
|
-
|
|
58
|
-
this.#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
this.#ws = new WebSocket(this.wsUrl.href, {
|
|
58
|
+
headers: this.#constructHeaders(),
|
|
59
|
+
perMessageDeflate: false,
|
|
60
|
+
handshakeTimeout: 5000
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
this.#ws.once('open', () => {
|
|
64
|
+
this.#onOpen();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.#setupWebSocketListeners();
|
|
68
|
+
this.aqua.emit('debug', this.name, 'Connecting...');
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
#cleanup() {
|
|
@@ -73,21 +74,28 @@ class Node {
|
|
|
73
74
|
this.#ws.terminate();
|
|
74
75
|
this.#ws = null;
|
|
75
76
|
}
|
|
77
|
+
this.info = null;
|
|
78
|
+
this.#statsCache.clear();
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
#constructHeaders() {
|
|
79
|
-
|
|
82
|
+
return {
|
|
80
83
|
Authorization: this.password,
|
|
81
84
|
"User-Id": this.aqua.clientId,
|
|
82
85
|
"Client-Name": `Aqua/${this.aqua.version}`,
|
|
86
|
+
...(this.sessionId && { "Session-Id": this.sessionId }),
|
|
87
|
+
...(this.resumeKey && { "Resume-Key": this.resumeKey })
|
|
83
88
|
};
|
|
84
|
-
if (this.sessionId) headers["Session-Id"] = this.sessionId;
|
|
85
|
-
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey;
|
|
86
|
-
return Object.freeze(headers);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
#setupWebSocketListeners() {
|
|
90
92
|
if (!this.#ws) return;
|
|
93
|
+
|
|
94
|
+
this.#ws.removeAllListeners('open');
|
|
95
|
+
this.#ws.removeAllListeners('error');
|
|
96
|
+
this.#ws.removeAllListeners('message');
|
|
97
|
+
this.#ws.removeAllListeners('close');
|
|
98
|
+
|
|
91
99
|
this.#ws.once("open", this.#onOpen.bind(this));
|
|
92
100
|
this.#ws.once("error", this.#onError.bind(this));
|
|
93
101
|
this.#ws.on("message", this.#onMessage.bind(this));
|
|
@@ -107,18 +115,20 @@ class Node {
|
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
|
-
|
|
111
118
|
async getStats() {
|
|
119
|
+
if (!this.connected) return this.stats;
|
|
120
|
+
|
|
112
121
|
const now = Date.now();
|
|
113
122
|
const STATS_COOLDOWN = 60000;
|
|
123
|
+
|
|
114
124
|
if (now - this.#lastStatsRequest < STATS_COOLDOWN) {
|
|
115
|
-
return this
|
|
125
|
+
return this.stats;
|
|
116
126
|
}
|
|
127
|
+
|
|
117
128
|
try {
|
|
118
129
|
const stats = await this.rest.makeRequest("GET", "/v4/stats");
|
|
119
|
-
this.#
|
|
130
|
+
this.stats = { ...this.#createStats(), ...stats };
|
|
120
131
|
this.#lastStatsRequest = now;
|
|
121
|
-
this.#statsCache[this.name] = this.stats;
|
|
122
132
|
return this.stats;
|
|
123
133
|
} catch (err) {
|
|
124
134
|
this.aqua.emit('debug', `Stats fetch error: ${err.message}`);
|
|
@@ -128,60 +138,58 @@ class Node {
|
|
|
128
138
|
|
|
129
139
|
#updateStats(payload) {
|
|
130
140
|
if (!payload) return;
|
|
131
|
-
this.stats =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
uptime: payload.uptime ?? 0,
|
|
135
|
-
ping: payload.ping ?? 0,
|
|
141
|
+
this.stats = {
|
|
142
|
+
...this.stats,
|
|
143
|
+
...payload,
|
|
136
144
|
memory: this.#updateMemoryStats(payload.memory),
|
|
137
145
|
cpu: this.#updateCpuStats(payload.cpu),
|
|
138
146
|
frameStats: this.#updateFrameStats(payload.frameStats)
|
|
139
|
-
}
|
|
147
|
+
};
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
#updateMemoryStats(memory = {}) {
|
|
143
151
|
const allocated = memory.allocated ?? 0;
|
|
144
152
|
const free = memory.free ?? 0;
|
|
145
153
|
const used = memory.used ?? 0;
|
|
146
|
-
return
|
|
154
|
+
return {
|
|
147
155
|
free,
|
|
148
156
|
used,
|
|
149
157
|
allocated,
|
|
150
158
|
reservable: memory.reservable ?? 0,
|
|
151
159
|
freePercentage: allocated ? (free / allocated) * 100 : 0,
|
|
152
160
|
usedPercentage: allocated ? (used / allocated) * 100 : 0
|
|
153
|
-
}
|
|
161
|
+
};
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
#updateCpuStats(cpu = {}) {
|
|
157
165
|
const cores = cpu.cores ?? 0;
|
|
158
|
-
return
|
|
166
|
+
return {
|
|
159
167
|
cores,
|
|
160
168
|
systemLoad: cpu.systemLoad ?? 0,
|
|
161
169
|
lavalinkLoad: cpu.lavalinkLoad ?? 0,
|
|
162
170
|
lavalinkLoadPercentage: cores ? (cpu.lavalinkLoad / cores) * 100 : 0
|
|
163
|
-
}
|
|
171
|
+
};
|
|
164
172
|
}
|
|
165
173
|
|
|
166
174
|
#updateFrameStats(frameStats = {}) {
|
|
167
|
-
if (!frameStats) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
175
|
+
if (!frameStats) return {};
|
|
176
|
+
return {
|
|
177
|
+
sent: frameStats.sent ?? 0,
|
|
178
|
+
nulled: frameStats.nulled ?? 0,
|
|
179
|
+
deficit: frameStats.deficit ?? 0
|
|
180
|
+
};
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
#onMessage(msg) {
|
|
177
184
|
try {
|
|
178
185
|
const payload = JSON.parse(msg.toString());
|
|
179
186
|
if (!payload?.op) return;
|
|
187
|
+
|
|
180
188
|
switch (payload.op) {
|
|
181
|
-
case
|
|
189
|
+
case 'stats':
|
|
182
190
|
this.#updateStats(payload);
|
|
183
191
|
break;
|
|
184
|
-
case
|
|
192
|
+
case 'ready':
|
|
185
193
|
this.#handleReadyOp(payload);
|
|
186
194
|
break;
|
|
187
195
|
default:
|
|
@@ -222,16 +230,28 @@ class Node {
|
|
|
222
230
|
return;
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
const jitter = Math.random() * 10000;
|
|
234
|
+
const backoffTime = Math.min(
|
|
235
|
+
this.reconnectTimeout * Math.pow(1.5, this.#reconnectAttempted) + jitter,
|
|
236
|
+
30000
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (this.#reconnectAttempted >= this.reconnectTries && !this.infiniteReconnects) {
|
|
227
240
|
this.aqua.emit("nodeError", this, new Error(`Max reconnection attempts reached (${this.reconnectTries})`));
|
|
228
|
-
|
|
241
|
+
this.destroy(true);
|
|
242
|
+
return;
|
|
229
243
|
}
|
|
230
244
|
|
|
245
|
+
clearTimeout(this.#reconnectTimeoutId);
|
|
231
246
|
this.#reconnectTimeoutId = setTimeout(() => {
|
|
232
|
-
this
|
|
247
|
+
this.#reconnectAttempted++;
|
|
248
|
+
this.aqua.emit("nodeReconnect", {
|
|
249
|
+
nodeName: this.name,
|
|
250
|
+
attempt: this.#reconnectAttempted,
|
|
251
|
+
backoffTime
|
|
252
|
+
});
|
|
233
253
|
this.connect();
|
|
234
|
-
},
|
|
254
|
+
}, backoffTime);
|
|
235
255
|
}
|
|
236
256
|
|
|
237
257
|
get penalties() {
|
|
@@ -264,8 +284,8 @@ class Node {
|
|
|
264
284
|
this.aqua.nodeMap.delete(this.name);
|
|
265
285
|
this.aqua.emit("nodeDestroy", this);
|
|
266
286
|
this.info = null;
|
|
267
|
-
this.#statsCache
|
|
268
|
-
this.stats =
|
|
287
|
+
this.#statsCache.clear();
|
|
288
|
+
this.stats = this.#createStats();
|
|
269
289
|
}
|
|
270
290
|
}
|
|
271
291
|
|
|
@@ -33,7 +33,8 @@ class Player extends EventEmitter {
|
|
|
33
33
|
this.ping = 0;
|
|
34
34
|
this.nowPlayingMessage = null;
|
|
35
35
|
this.previousTracks = [];
|
|
36
|
-
this.shouldDeleteMessage = options.shouldDeleteMessage
|
|
36
|
+
this.shouldDeleteMessage = options.shouldDeleteMessage;
|
|
37
|
+
this.leaveOnEnd = options.leaveOnEnd
|
|
37
38
|
|
|
38
39
|
this.boundHandleEvent = this.handleEvent.bind(this);
|
|
39
40
|
this.boundOnPlayerUpdate = this.onPlayerUpdate.bind(this);
|
|
@@ -277,9 +278,12 @@ class Player extends EventEmitter {
|
|
|
277
278
|
|
|
278
279
|
if (player.queue.isEmpty()) {
|
|
279
280
|
this.playing = false;
|
|
280
|
-
this.
|
|
281
|
-
|
|
281
|
+
if (this.leaveOnEnd) {
|
|
282
|
+
await this.cleanup();
|
|
283
|
+
}
|
|
284
|
+
return this.aqua.emit("queueEnd", player);
|
|
282
285
|
}
|
|
286
|
+
|
|
283
287
|
|
|
284
288
|
return player.play();
|
|
285
289
|
}
|