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/Aqua.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
2
|
const { EventEmitter } = require("node:events");
|
|
4
3
|
const Node = require("./Node");
|
|
5
4
|
const Player = require("./Player");
|
|
@@ -14,14 +13,12 @@ class Aqua extends EventEmitter {
|
|
|
14
13
|
if (!Array.isArray(nodes) || !nodes.length) {
|
|
15
14
|
throw new Error(`Nodes must be a non-empty Array (Received ${typeof nodes})`);
|
|
16
15
|
}
|
|
17
|
-
|
|
18
16
|
this.client = client;
|
|
19
17
|
this.nodes = nodes;
|
|
20
18
|
this.nodeMap = new Map();
|
|
21
19
|
this.players = new Map();
|
|
22
20
|
this.clientId = null;
|
|
23
21
|
this.initiated = false;
|
|
24
|
-
|
|
25
22
|
this.shouldDeleteMessage = options.shouldDeleteMessage ?? false;
|
|
26
23
|
this.defaultSearchPlatform = options.defaultSearchPlatform ?? 'ytsearch';
|
|
27
24
|
this.leaveOnEnd = options.leaveOnEnd ?? true;
|
|
@@ -32,88 +29,72 @@ class Aqua extends EventEmitter {
|
|
|
32
29
|
this.autoResume = options.autoResume ?? false;
|
|
33
30
|
this.infiniteReconnects = options.infiniteReconnects ?? false;
|
|
34
31
|
this.options = options;
|
|
35
|
-
|
|
36
32
|
this.setMaxListeners(0);
|
|
37
33
|
this._leastUsedCache = { nodes: [], timestamp: 0 };
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
|
|
41
36
|
defaultSendFunction(payload) {
|
|
42
37
|
const guild = this.client.guilds.cache.get(payload.d.guild_id);
|
|
43
38
|
if (guild) guild.shard.send(payload);
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
validateInputs(client, nodes) {
|
|
47
|
-
if (!client) throw new Error("Client is required to initialize Aqua");
|
|
48
|
-
if (!Array.isArray(nodes) || !nodes.length) {
|
|
49
|
-
throw new Error(`Nodes must be a non-empty Array (Received ${typeof nodes})`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
41
|
get leastUsedNodes() {
|
|
54
42
|
const now = Date.now();
|
|
55
43
|
if (now - this._leastUsedCache.timestamp < 50) return this._leastUsedCache.nodes;
|
|
56
|
-
|
|
57
|
-
const nodes = [];
|
|
58
|
-
for (const node of this.nodeMap.values()) {
|
|
59
|
-
if (node.connected) nodes.push(node);
|
|
60
|
-
}
|
|
44
|
+
const nodes = Array.from(this.nodeMap.values()).filter(node => node.connected);
|
|
61
45
|
nodes.sort((a, b) => a.rest.calls - b.rest.calls);
|
|
62
|
-
|
|
63
46
|
this._leastUsedCache = { nodes, timestamp: now };
|
|
64
47
|
return nodes;
|
|
65
48
|
}
|
|
66
|
-
init(clientId) {
|
|
67
|
-
if (this.initiated) return this;
|
|
68
49
|
|
|
50
|
+
async init(clientId) {
|
|
51
|
+
if (this.initiated) return this;
|
|
69
52
|
this.clientId = clientId;
|
|
70
|
-
|
|
71
53
|
try {
|
|
72
|
-
for (let i = 0; i < this.nodes.length; i++) {
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
55
|
+
const node = this.nodes[i];
|
|
56
|
+
await this.createNode(node);
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < this.plugins.length; i++) {
|
|
59
|
+
const plugin = this.plugins[i];
|
|
60
|
+
plugin.load(this);
|
|
61
|
+
}
|
|
75
62
|
this.initiated = true;
|
|
76
63
|
} catch (error) {
|
|
77
64
|
this.initiated = false;
|
|
78
65
|
throw error;
|
|
79
66
|
}
|
|
80
|
-
|
|
81
67
|
return this;
|
|
82
68
|
}
|
|
83
69
|
|
|
84
|
-
createNode(options) {
|
|
70
|
+
async createNode(options) {
|
|
85
71
|
const nodeId = options.name || options.host;
|
|
86
72
|
this.destroyNode(nodeId);
|
|
87
|
-
|
|
88
73
|
const node = new Node(this, options, this.options);
|
|
89
74
|
this.nodeMap.set(nodeId, node);
|
|
90
75
|
this._leastUsedCache.timestamp = 0;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
76
|
+
try {
|
|
77
|
+
await node.connect();
|
|
78
|
+
this.emit("nodeCreate", node);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.nodeMap.delete(nodeId);
|
|
81
|
+
console.error("Failed to connect node:", error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
100
84
|
return node;
|
|
101
85
|
}
|
|
102
86
|
|
|
103
87
|
destroyNode(identifier) {
|
|
104
88
|
const node = this.nodeMap.get(identifier);
|
|
105
89
|
if (!node) return;
|
|
106
|
-
|
|
107
90
|
node.destroy();
|
|
108
91
|
this.nodeMap.delete(identifier);
|
|
109
92
|
this.emit("nodeDestroy", node);
|
|
110
93
|
}
|
|
111
94
|
|
|
112
|
-
|
|
113
95
|
updateVoiceState({ d, t }) {
|
|
114
96
|
const player = this.players.get(d.guild_id);
|
|
115
97
|
if (!player) return;
|
|
116
|
-
|
|
117
98
|
const updateMethod = t === "VOICE_SERVER_UPDATE" ? "setServerUpdate" : "setStateUpdate";
|
|
118
99
|
if (t === "VOICE_SERVER_UPDATE" || (t === "VOICE_STATE_UPDATE" && d.user_id === this.clientId)) {
|
|
119
100
|
if (player.connection && typeof player.connection[updateMethod] === "function") {
|
|
@@ -127,14 +108,12 @@ class Aqua extends EventEmitter {
|
|
|
127
108
|
|
|
128
109
|
fetchRegion(region) {
|
|
129
110
|
if (!region) return this.leastUsedNodes;
|
|
130
|
-
|
|
131
111
|
const lowerRegion = region.toLowerCase();
|
|
132
|
-
const
|
|
112
|
+
const nodes = Array.from(this.nodeMap.values()).filter(node =>
|
|
133
113
|
node.connected && node.regions?.includes(lowerRegion)
|
|
134
114
|
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return regionNodes;
|
|
115
|
+
nodes.sort((a, b) => this.calculateLoad(a) - this.calculateLoad(b));
|
|
116
|
+
return nodes;
|
|
138
117
|
}
|
|
139
118
|
|
|
140
119
|
calculateLoad(node) {
|
|
@@ -147,11 +126,9 @@ class Aqua extends EventEmitter {
|
|
|
147
126
|
this.ensureInitialized();
|
|
148
127
|
const existingPlayer = this.players.get(options.guildId);
|
|
149
128
|
if (existingPlayer && existingPlayer.voiceChannel) return existingPlayer;
|
|
150
|
-
|
|
151
129
|
const availableNodes = options.region ? this.fetchRegion(options.region) : this.leastUsedNodes;
|
|
152
130
|
const node = availableNodes[0];
|
|
153
131
|
if (!node) throw new Error("No nodes are available");
|
|
154
|
-
|
|
155
132
|
return this.createPlayer(node, options);
|
|
156
133
|
}
|
|
157
134
|
|
|
@@ -159,11 +136,9 @@ class Aqua extends EventEmitter {
|
|
|
159
136
|
this.destroyPlayer(options.guildId);
|
|
160
137
|
const player = new Player(this, node, options);
|
|
161
138
|
this.players.set(options.guildId, player);
|
|
162
|
-
|
|
163
139
|
player.once("destroy", () => {
|
|
164
140
|
this.players.delete(options.guildId);
|
|
165
141
|
});
|
|
166
|
-
|
|
167
142
|
player.connect(options);
|
|
168
143
|
this.emit("playerCreate", player);
|
|
169
144
|
return player;
|
|
@@ -172,7 +147,6 @@ class Aqua extends EventEmitter {
|
|
|
172
147
|
async destroyPlayer(guildId) {
|
|
173
148
|
const player = this.players.get(guildId);
|
|
174
149
|
if (!player) return;
|
|
175
|
-
|
|
176
150
|
try {
|
|
177
151
|
await player.clearData();
|
|
178
152
|
player.removeAllListeners();
|
|
@@ -187,7 +161,6 @@ class Aqua extends EventEmitter {
|
|
|
187
161
|
this.ensureInitialized();
|
|
188
162
|
const requestNode = this.getRequestNode(nodes);
|
|
189
163
|
const formattedQuery = this.formatQuery(query, source);
|
|
190
|
-
|
|
191
164
|
try {
|
|
192
165
|
const response = await requestNode.rest.makeRequest("GET", `/v4/loadtracks?identifier=${encodeURIComponent(formattedQuery)}`);
|
|
193
166
|
if (["empty", "NO_MATCHES"].includes(response.loadType)) {
|
|
@@ -204,11 +177,9 @@ class Aqua extends EventEmitter {
|
|
|
204
177
|
|
|
205
178
|
getRequestNode(nodes) {
|
|
206
179
|
if (!nodes) return this.leastUsedNodes[0];
|
|
207
|
-
|
|
208
180
|
if (!(typeof nodes === "string" || nodes instanceof Node)) {
|
|
209
181
|
throw new TypeError(`'nodes' must be a string or Node instance, received: ${typeof nodes}`);
|
|
210
182
|
}
|
|
211
|
-
|
|
212
183
|
return (typeof nodes === "string" ? this.nodeMap.get(nodes) : nodes) ?? this.leastUsedNodes[0];
|
|
213
184
|
}
|
|
214
185
|
|
|
@@ -224,11 +195,9 @@ class Aqua extends EventEmitter {
|
|
|
224
195
|
try {
|
|
225
196
|
const ytIdentifier = `/v4/loadtracks?identifier=https://www.youtube.com/watch?v=${query}`;
|
|
226
197
|
const youtubeResponse = await rest.makeRequest("GET", ytIdentifier);
|
|
227
|
-
|
|
228
198
|
if (!["empty", "NO_MATCHES"].includes(youtubeResponse.loadType)) {
|
|
229
199
|
return youtubeResponse;
|
|
230
200
|
}
|
|
231
|
-
|
|
232
201
|
const spotifyIdentifier = `/v4/loadtracks?identifier=https://open.spotify.com/track/${query}`;
|
|
233
202
|
return await rest.makeRequest("GET", spotifyIdentifier);
|
|
234
203
|
} catch (error) {
|
|
@@ -245,14 +214,11 @@ class Aqua extends EventEmitter {
|
|
|
245
214
|
pluginInfo: response.pluginInfo ?? {},
|
|
246
215
|
tracks: []
|
|
247
216
|
};
|
|
248
|
-
|
|
249
217
|
if (response.loadType === "error" || response.loadType === "LOAD_FAILED") {
|
|
250
218
|
baseResponse.exception = response.data ?? response.exception;
|
|
251
219
|
return baseResponse;
|
|
252
220
|
}
|
|
253
|
-
|
|
254
221
|
const trackFactory = (trackData) => new Track(trackData, requester, requestNode);
|
|
255
|
-
|
|
256
222
|
switch (response.loadType) {
|
|
257
223
|
case "track":
|
|
258
224
|
if (response.data) {
|
|
@@ -289,7 +255,6 @@ class Aqua extends EventEmitter {
|
|
|
289
255
|
}
|
|
290
256
|
break;
|
|
291
257
|
}
|
|
292
|
-
|
|
293
258
|
return baseResponse;
|
|
294
259
|
}
|
|
295
260
|
|
|
@@ -301,7 +266,6 @@ class Aqua extends EventEmitter {
|
|
|
301
266
|
|
|
302
267
|
async search(query, requester, source = this.defaultSearchPlatform) {
|
|
303
268
|
if (!query || !requester) return null;
|
|
304
|
-
|
|
305
269
|
try {
|
|
306
270
|
const { tracks } = await this.resolve({ query, source, requester });
|
|
307
271
|
return tracks || null;
|
|
@@ -311,11 +275,33 @@ class Aqua extends EventEmitter {
|
|
|
311
275
|
}
|
|
312
276
|
}
|
|
313
277
|
|
|
314
|
-
cleanupPlayer(player) {
|
|
315
|
-
if (player
|
|
278
|
+
async cleanupPlayer(player) {
|
|
279
|
+
if (!player) return;
|
|
280
|
+
try {
|
|
281
|
+
if (player.connection) {
|
|
282
|
+
try {
|
|
283
|
+
await player.connection.disconnect();
|
|
284
|
+
player.connection = null;
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error(`Error disconnecting player connection: ${error.message}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (player.queue) {
|
|
290
|
+
player.queue.clear();
|
|
291
|
+
}
|
|
292
|
+
if (typeof player.stop === 'function') {
|
|
293
|
+
try {
|
|
294
|
+
await player.stop();
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error(`Error stopping player: ${error.message}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
player.removeAllListeners();
|
|
316
300
|
this.players.delete(player.guildId);
|
|
301
|
+
this.emit("playerCleanup", player.guildId);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error(`Error during player cleanup: ${error.message}`);
|
|
317
304
|
}
|
|
318
305
|
}
|
|
319
306
|
}
|
|
320
|
-
|
|
321
307
|
module.exports = Aqua;
|