aqualink 1.0.4 → 1.1.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 CHANGED
@@ -17,6 +17,30 @@ This code is based in riffy, but its an 100% Rewrite made from scratch...
17
17
  - Youtube and Spotify support
18
18
  - Minimal Requests to the lavalink server (helps the lavalink recourses!)
19
19
 
20
+ # Docs (Wiki)
21
+ - https://github.com/ToddyTheNoobDud/AquaLink/wiki
22
+
23
+ # Yay, Version 1.1.0 is released ! aqualink so cool
24
+
25
+ + Fixed stop
26
+ + Fixed Destroy
27
+ + Fixed disconnect
28
+ + Improved events
29
+ + Optimize more
30
+ + Improved queue system
31
+ + Improved speed
32
+ + Remove useless code
33
+ + Add more features...
34
+ + Fixed some resolve() methods
35
+ + Improved debug / logging
36
+ + Add new option: shouldDeleteMessage (true, false, if true will delete the nowPlayingMessage, false will not delete)
37
+ + Add nowPlayingMessage
38
+ + Rewrited REST Manager (1,5x faster, less memory usage, less cpu usage, less latency)
39
+ + Rewrited NODE Manager (A bit faster, less memory usage, less cpu, Less temp objects, better error handling)
40
+ + Rewrite Connection Manager (Faster, less bugs, less useless checking, fixed an random memory leak)
41
+ + Updated Aqua.js (added shouldDeleteMessage, some misc update for playlist)
42
+ + Added playlist support for Track (testing)
43
+
20
44
  # How to install
21
45
 
22
46
  `npm install aqualink`
@@ -26,6 +50,14 @@ This code is based in riffy, but its an 100% Rewrite made from scratch...
26
50
  # Basic usage
27
51
 
28
52
  ```javascript
53
+ // If you're using Module, use this:
54
+ // import { createRequire } from 'module';
55
+ // const require = createRequire(import.meta.url);
56
+
57
+ //const { Aqua } = require('aqualink');
58
+
59
+
60
+
29
61
  const { Aqua } = require("aqualink");
30
62
  const { Client, Collection, GatewayDispatchEvents } = require("discord.js");
31
63
 
@@ -13,6 +13,7 @@ class Aqua extends EventEmitter {
13
13
  * @param {string} [options.defaultSearchPlatform="ytsearch"] - Default search platform.
14
14
  * @param {string} [options.restVersion="v4"] - Version of the REST API.
15
15
  * @param {Array<Object>} [options.plugins=[]] - Plugins to load.
16
+ * @param {string} [options.shouldDeleteMessage='none'] - Should delete your message? (true, false)
16
17
  */
17
18
  constructor(client, nodes, options) {
18
19
  super();
@@ -27,6 +28,7 @@ class Aqua extends EventEmitter {
27
28
  this.clientId = null;
28
29
  this.initiated = false;
29
30
  this.sessionId = null;
31
+ this.shouldDeleteMessage = options.shouldDeleteMessage || "false";
30
32
  this.defaultSearchPlatform = options.defaultSearchPlatform || "ytmsearch";
31
33
  this.restVersion = options.restVersion || "v3";
32
34
  this.plugins = options.plugins || [];
@@ -98,6 +100,7 @@ class Aqua extends EventEmitter {
98
100
  if (packet.t === "VOICE_SERVER_UPDATE") player.connection.setServerUpdate(packet.d);
99
101
  else if (packet.t === "VOICE_STATE_UPDATE" && packet.d.user_id === this.clientId) player.connection.setStateUpdate(packet.d);
100
102
  }
103
+
101
104
  /**
102
105
  * Fetches nodes by the specified region.
103
106
  * @param {string} region - The region to filter nodes by.
@@ -133,6 +136,7 @@ class Aqua extends EventEmitter {
133
136
 
134
137
  return this.createPlayer(node, options);
135
138
  }
139
+
136
140
  /**
137
141
  * Creates a player using the specified node.
138
142
  * @param {Node} node - The node to create the player with.
@@ -146,6 +150,7 @@ class Aqua extends EventEmitter {
146
150
  this.emit("playerCreate", player);
147
151
  return player;
148
152
  }
153
+
149
154
  /**
150
155
  * Destroys the player associated with the given guild ID.
151
156
  * @param {string} guildId - The ID of the guild.
@@ -222,21 +227,28 @@ class Aqua extends EventEmitter {
222
227
  * @param {Node} requestNode - The node that handled the request.
223
228
  */
224
229
  loadTracks(response, requester, requestNode) {
225
- this.tracks = [];
226
- if (response.loadType === "track") {
227
- if (response.data) {
228
- this.tracks.push(new Track(response.data, requester, requestNode));
229
- }
230
- } else if (response.loadType === "playlist") {
231
- this.tracks = response.data?.tracks.map(track => new Track(track, requester, requestNode)) || [];
232
- this.playlistInfo = response.data?.info || null;
233
- } else if (response.loadType === "search") {
234
- this.tracks = response.data.map(track => new Track(track, requester, requestNode));
230
+
231
+ switch (response.loadType) {
232
+ case "track":
233
+ if (response.data) {
234
+ this.tracks.push(new Track(response.data, requester, requestNode));
235
+ }
236
+ break;
237
+ case "playlist":
238
+ this.tracks = response.data?.tracks?.map(track => new Track(track, requester, requestNode)) || [];
239
+ this.playlistInfo = {
240
+ name: response.data?.info?.name || response.data?.info?.title,
241
+ ...response.data?.info,
242
+ } || null;
243
+ break;
244
+ case "search":
245
+ this.tracks = response.data?.map(track => new Track(track, requester, requestNode));
246
+ break;
235
247
  }
248
+
236
249
  this.loadType = response.loadType;
237
250
  this.pluginInfo = response.pluginInfo || {};
238
251
  }
239
-
240
252
  /**
241
253
  * Constructs the response object for the resolved tracks.
242
254
  * @returns {Object} The constructed response.
@@ -251,6 +263,7 @@ class Aqua extends EventEmitter {
251
263
  };
252
264
  }
253
265
 
266
+
254
267
  /**
255
268
  * Gets the player associated with the specified guild ID.
256
269
  * @param {string} guildId - The ID of the guild.
@@ -10,6 +10,8 @@ class Connection {
10
10
  this.selfDeaf = false;
11
11
  this.selfMute = false;
12
12
  this.voiceChannel = player.voiceChannel;
13
+ this.lastUpdateTime = 0; // Track the last update time to throttle updates
14
+ this.updateThrottle = 1000; // Throttle updates to every 1000 ms (1 second)
13
15
  }
14
16
 
15
17
  /**
@@ -27,6 +29,7 @@ class Connection {
27
29
  this.voice.endpoint = endpoint;
28
30
  this.voice.token = token;
29
31
  this.region = endpoint.split(".")[0].replace(/[0-9]/g, "");
32
+
30
33
  this.player.aqua.emit("debug", `[Player ${this.player.guildId} - CONNECTION] ${previousVoiceRegion ? `Changed Voice Region from ${previousVoiceRegion} to ${this.region}` : `Voice Server: ${this.region}`}`);
31
34
 
32
35
  if (this.player.paused) {
@@ -70,16 +73,15 @@ class Connection {
70
73
  /**
71
74
  * Updates the player voice data.
72
75
  */
73
- updatePlayerVoiceData() {
74
- this.player.nodes.rest.updatePlayer({
75
- guildId: this.player.guildId,
76
- data: {
77
- voice: this.voice,
78
- volume: this.player.volume
79
- }
80
- });
81
- }
82
- }
83
-
84
- module.exports = { Connection };
76
+ updatePlayerVoiceData() {
77
+ this.player.nodes.rest.updatePlayer({
78
+ guildId: this.player.guildId,
79
+ data: {
80
+ voice: this.voice,
81
+ volume: this.player.volume
82
+ }
83
+ });
84
+ }
85
+ }
85
86
 
87
+ module.exports = { Connection };
@@ -1,4 +1,3 @@
1
-
2
1
  const WebSocket = require("ws");
3
2
  const { Rest } = require("./Rest");
4
3
 
@@ -14,27 +13,23 @@ class Node {
14
13
  this.host = nodes.host || "localhost";
15
14
  this.port = nodes.port || 2333;
16
15
  this.password = nodes.password || "youshallnotpass";
16
+ this.stats = this.initializeStats();
17
17
  this.restVersion = "v4"; // Fixed to the specified version
18
18
  this.secure = nodes.secure || false;
19
19
  this.sessionId = nodes.sessionId || null;
20
20
  this.rest = new Rest(aqua, this);
21
-
22
21
  this.wsUrl = `ws${this.secure ? 's' : ''}://${this.host}:${this.port}/v4/websocket`;
23
- this.restUrl = `http${this.secure ? 's' : ''}://${this.host}:${this.port}`;
24
-
25
22
  this.ws = null;
26
- this.regions = nodes.regions;
23
+ this.regions = nodes.regions || [];
27
24
  this.info = null;
28
- this.stats = this.initializeStats();
29
25
  this.connected = false;
30
-
31
26
  this.resumeKey = options.resumeKey || null;
32
27
  this.resumeTimeout = options.resumeTimeout || 60;
33
28
  this.autoResume = options.autoResume || false;
34
-
35
29
  this.reconnectTimeout = options.reconnectTimeout || 5000;
36
30
  this.reconnectTries = options.reconnectTries || 3;
37
31
  this.reconnectAttempted = 0;
32
+ this.lastStatsRequest = 0; // Track the last time stats were requested
38
33
  }
39
34
 
40
35
  initializeStats() {
@@ -93,12 +88,12 @@ class Node {
93
88
  async onOpen() {
94
89
  this.connected = true;
95
90
  this.aqua.emit('debug', this.name, `Connected to Lavalink at ${this.wsUrl}`);
96
-
97
- this.info = await this.fetchInfo()
98
- .catch(err => {
99
- this.aqua.emit('debug', `Failed to fetch info: ${err.message}`);
100
- return null;
101
- });
91
+ try {
92
+ this.info = await this.fetchInfo();
93
+ } catch (err) {
94
+ this.aqua.emit('debug', `Failed to fetch info: ${err.message}`);
95
+ this.info = null;
96
+ }
102
97
 
103
98
  if (!this.info && !this.aqua.bypassChecks.nodeFetchInfo) {
104
99
  throw new Error(`Failed to fetch node info.`);
@@ -107,27 +102,24 @@ class Node {
107
102
  if (this.autoResume) {
108
103
  this.resumePlayers();
109
104
  }
110
-
111
105
  this.lastStats = 0;
112
106
  }
113
107
 
114
108
  async getStats() {
115
- if (Date.now() - this.lastStats < 5000) {
116
- return this.stats;
109
+ const now = Date.now();
110
+ if (now - this.lastStatsRequest < 5000) {
111
+ return this.stats; // Return cached stats if requested too soon
117
112
  }
118
113
 
119
- const stats = await this.rest.makeRequest("GET", `/v4/stats`)
120
- .catch(err => {
121
- this.aqua.emit('debug', `Error fetching stats: ${err.message}`);
122
- return null;
123
- });
124
-
125
- if (stats) {
126
- this.stats = stats;
127
- this.lastStats = Date.now();
114
+ try {
115
+ const stats = await this.rest.makeRequest("GET", `/v4/stats`);
116
+ this.stats = { ...this.stats, ...stats };
117
+ this.lastStatsRequest = now; // Update last request time
118
+ return stats;
119
+ } catch (err) {
120
+ this.aqua.emit('debug', `Error fetching stats: ${err.message}`);
121
+ return this.stats; // Return last known stats on error
128
122
  }
129
-
130
- return stats;
131
123
  }
132
124
 
133
125
  resumePlayers() {
@@ -145,20 +137,17 @@ class Node {
145
137
  onMessage(msg) {
146
138
  if (Array.isArray(msg)) msg = Buffer.concat(msg);
147
139
  if (msg instanceof ArrayBuffer) msg = Buffer.from(msg);
148
-
149
140
  const payload = JSON.parse(msg.toString());
150
141
  if (!payload.op) return;
151
-
152
142
  this.aqua.emit("raw", "Node", payload);
153
143
  this.aqua.emit("debug", this.name, `Received update: ${JSON.stringify(payload)}`);
154
-
155
144
  this.handlePayload(payload);
156
145
  }
157
146
 
158
147
  handlePayload(payload) {
159
148
  switch (payload.op) {
160
149
  case "stats":
161
- this.stats = { ...payload };
150
+ this.stats = { ...this.stats, ...payload };
162
151
  this.lastStats = Date.now();
163
152
  break;
164
153
  case "ready":
@@ -193,7 +182,6 @@ class Node {
193
182
  this.aqua.emit("nodeError", this, new Error(`Unable to connect after ${this.reconnectTries} attempts.`));
194
183
  return this.destroy();
195
184
  }
196
-
197
185
  setTimeout(() => {
198
186
  this.aqua.emit("nodeReconnect", this);
199
187
  this.connect();
@@ -208,17 +196,13 @@ class Node {
208
196
  this.aqua.nodes.delete(this.name);
209
197
  return;
210
198
  }
211
-
212
199
  if (!this.connected) return;
213
-
214
200
  this.aqua.players.forEach((player) => {
215
201
  if (player.node === this) player.destroy();
216
202
  });
217
-
218
203
  if (this.ws) this.ws.close(1000, "destroy");
219
204
  this.ws?.removeAllListeners();
220
205
  this.ws = null;
221
-
222
206
  this.aqua.emit("nodeDestroy", this);
223
207
  this.aqua.nodeMap.delete(this.name);
224
208
  this.connected = false;
@@ -247,4 +231,4 @@ class Node {
247
231
  }
248
232
  }
249
233
 
250
- module.exports = { Node };
234
+ module.exports = { Node };
@@ -16,7 +16,7 @@ class Player extends EventEmitter {
16
16
  * @param {number} [options.defaultVolume=100] - The default volume level (0-200).
17
17
  * @param {string} [options.loop='none'] - The loop mode ('none', 'track', 'queue').
18
18
  */
19
- constructor(aqua, nodes, options) {
19
+ constructor(aqua, nodes, options = {}) {
20
20
  super();
21
21
  this.aqua = aqua;
22
22
  this.nodes = nodes;
@@ -40,6 +40,9 @@ class Player extends EventEmitter {
40
40
  this.timestamp = 0;
41
41
  this.ping = 0;
42
42
  this.isAutoplay = false;
43
+ this.nowPlayingMessage = null;
44
+
45
+ this.shouldDeleteMessage = options.shouldDeleteMessage ?? true;
43
46
 
44
47
  this.setupEventListeners();
45
48
  }
@@ -62,7 +65,6 @@ class Player extends EventEmitter {
62
65
  this.position = state.position;
63
66
  this.ping = state.ping;
64
67
  this.timestamp = state.time;
65
-
66
68
  }
67
69
 
68
70
  /**
@@ -83,18 +85,24 @@ class Player extends EventEmitter {
83
85
 
84
86
  /**
85
87
  * Plays the next track in the queue.
88
+ * @param {Object} options - Options for playing the next track.
89
+ * @param {string} options.query - The query to search for the next track.
90
+ * @param {boolean} options.force - Whether to force play the next track even if the queue is empty.
86
91
  * @returns {Promise<Player>} The player instance.
87
92
  * @throws {Error} If the player is not connected.
93
+ * @throws {Error} If the queue is empty and force is not set to true.
94
+ * @description This method plays the next track in the queue.
95
+ * @event play
88
96
  */
89
97
  async play() {
90
- if (!this.connected) throw new Error("Player connection is not established. Please connect first.");
98
+ if (!this.connected) throw new Error("Bro go on and use the connection first");
99
+ if (!this.queue.length) return;
100
+
91
101
  this.current = this.queue.shift();
92
- if (!this.current) return this;
93
102
 
94
103
  if (!this.current.track) {
95
104
  this.current = await this.current.resolve(this.aqua);
96
105
  }
97
-
98
106
  this.playing = true;
99
107
  this.position = 0;
100
108
 
@@ -102,6 +110,7 @@ class Player extends EventEmitter {
102
110
  await this.updatePlayer({ track: { encoded: this.current.track } });
103
111
  return this;
104
112
  }
113
+
105
114
  /**
106
115
  * Connects the player to the voice channel.
107
116
  * @param {Object} [options=this] - Connection options.
@@ -124,16 +133,19 @@ class Player extends EventEmitter {
124
133
  }
125
134
 
126
135
  /**
127
- * Disconnects the player from the voice channel and cleans up resources.
136
+ * Disconnects the player from the voice channel and leaves the channel.
128
137
  * @returns {Promise<Player>} The player instance.
129
138
  */
130
139
  async destroy() {
131
- await this.updatePlayer({ track: null });
140
+ await this.updatePlayer({ track: { encoded: null }, });
132
141
  this.connected = false;
142
+ await this.send({
143
+ guild_id: this.guildId,
144
+ channel_id: null,
145
+ });
133
146
  this.clearData(); // Clear data when destroyed
134
147
  this.aqua.emit("debug", this.guildId, "Player has disconnected from voice channel.");
135
148
  }
136
-
137
149
  /**
138
150
  * Pauses or resumes the player.
139
151
  * @param {boolean} paused - Whether to pause the player.
@@ -161,10 +173,17 @@ class Player extends EventEmitter {
161
173
  * @returns {Promise<Player>} The player instance.
162
174
  */
163
175
  async stop() {
164
- await this.updatePlayer({ track: null });
176
+ if (!this.playing) return this; // If not playing, return early
177
+ this.playing = false;
178
+ this.current = null;
179
+ this.position = 0;
180
+ await this.updatePlayer({ track:
181
+ {
182
+ encoded: null,
183
+ }
184
+ });
165
185
  return this;
166
186
  }
167
-
168
187
  /**
169
188
  * Sets the volume of the player.
170
189
  * @param {number} volume - The volume level (0-200).
@@ -233,7 +252,8 @@ class Player extends EventEmitter {
233
252
  * @returns {Promise<Player>} The player instance.
234
253
  */
235
254
  async disconnect() {
236
- await this.updatePlayer({ track: null });
255
+ await this.updatePlayer({ track: { encoded: null }});
256
+ await this.send({ guild_id: this.guildId, channel_id: null });
237
257
  this.connected = false;
238
258
  this.aqua.emit("debug", this.guildId, "Player has disconnected from voice channel.");
239
259
  }
@@ -246,21 +266,25 @@ class Player extends EventEmitter {
246
266
  const player = this.aqua.players.get(payload.guildId);
247
267
  if (!player) return;
248
268
 
269
+ const track = this.current;
270
+
249
271
  switch (payload.type) {
250
272
  case "TrackStartEvent":
251
- this.trackStart(player, payload);
273
+ this.trackStart(player, track, payload);
252
274
  break;
253
275
  case "TrackEndEvent":
254
- this.trackEnd(player, payload);
276
+ this.trackEnd(player, track, payload);
255
277
  break;
256
278
  case "TrackExceptionEvent":
257
- this.trackError(player, payload);
279
+ this.trackError(player, track, payload);
258
280
  break;
259
281
  case "TrackStuckEvent":
260
- this.trackStuck(player, payload);
282
+ this.trackStuck(player, track, payload);
261
283
  break;
284
+ case "TrackChangeEvent":
285
+ this.trackChange(player, track, payload);
262
286
  case "WebSocketClosedEvent":
263
- this.socketClosed(player, payload);
287
+ this.socketClosed(player, track, payload);
264
288
  break;
265
289
  default:
266
290
  this.handleUnknownEvent(payload);
@@ -272,11 +296,18 @@ class Player extends EventEmitter {
272
296
  * Handles track start events.
273
297
  * @param {Object} player - The player instance.
274
298
  * @param {Object} payload - The event payload.
299
+ * @param {Object} track - The track object.
275
300
  */
276
- trackStart(player, payload) {
301
+ trackStart(player, track, payload) {
277
302
  this.playing = true;
278
303
  this.paused = false;
279
- this.aqua.emit("trackStart", player, payload);
304
+ this.aqua.emit("trackStart", player, track, payload);
305
+ }
306
+
307
+ trackChange(player, track, payload) {
308
+ this.playing = true;
309
+ this.paused = this.player.this.paused
310
+ this.aqua.emit("trackChange", player, track, payload);
280
311
  }
281
312
 
282
313
  /**
@@ -284,11 +315,13 @@ class Player extends EventEmitter {
284
315
  * @param {Object} player - The player instance.
285
316
  * @param {Object} payload - The event payload.
286
317
  */
287
- trackEnd(player, payload) {
288
- this.addToPreviousTrack(this.current);
318
+ trackEnd(player, track, payload) {
319
+ if (this.shouldDeleteMessage && this.nowPlayingMessage) {
320
+ this.nowPlayingMessage.delete();
321
+ this.nowPlayingMessage = null;
322
+ }
289
323
  if (["loadfailed", "cleanup"].includes(payload.reason.replace("_", "").toLowerCase())) {
290
324
  if (player.queue.length === 0) {
291
- this.playing = false;
292
325
  return this.aqua.emit("queueEnd", player);
293
326
  }
294
327
  this.aqua.emit("trackEnd", player, payload);
@@ -304,7 +337,6 @@ class Player extends EventEmitter {
304
337
  return player.play();
305
338
  }
306
339
  if (player.queue.length === 0) {
307
- this.playing = false;
308
340
  return this.aqua.emit("queueEnd", player);
309
341
  } else {
310
342
  this.aqua.emit("trackEnd", player, payload);
@@ -317,7 +349,7 @@ class Player extends EventEmitter {
317
349
  * @param {Object} player - The player instance.
318
350
  * @param {Object} payload - The event payload.
319
351
  */
320
- trackError(player, payload) {
352
+ trackError(player, track, payload) {
321
353
  this.aqua.emit("trackError", player, payload);
322
354
  this.stop();
323
355
  }
@@ -327,7 +359,7 @@ class Player extends EventEmitter {
327
359
  * @param {Object} player - The player instance.
328
360
  * @param {Object} payload - The event payload.
329
361
  */
330
- trackStuck(player, payload) {
362
+ trackStuck(player, track, payload) {
331
363
  this.aqua.emit("trackStuck", player, payload);
332
364
  this.stop();
333
365
  }
@@ -338,7 +370,7 @@ class Player extends EventEmitter {
338
370
  * @param {Object} payload - The event payload.
339
371
  */
340
372
  socketClosed(player, payload) {
341
- if ([4015, 4009].includes(payload.code)) {
373
+ if (payload && [4015, 4009].includes(payload.code)) {
342
374
  this.send({
343
375
  guild_id: payload.guildId,
344
376
  channel_id: this.voiceChannel,
@@ -350,7 +382,6 @@ class Player extends EventEmitter {
350
382
  this.pause(true);
351
383
  this.aqua.emit("debug", this.guildId, "Player paused due to socket closure.");
352
384
  }
353
-
354
385
  /**
355
386
  * Sends data to the Aqua instance.
356
387
  * @param {Object} data - The data to send.
@@ -419,3 +450,4 @@ class Player extends EventEmitter {
419
450
  }
420
451
 
421
452
  module.exports = { Player };
453
+
@@ -1,5 +1,4 @@
1
1
  const { fetch: undiciFetch } = require("undici");
2
- const nodeUtil = require("node:util");
3
2
 
4
3
  class Rest {
5
4
  constructor(aqua, options) {
@@ -9,10 +8,10 @@ class Rest {
9
8
  this.password = options.password;
10
9
  this.version = options.restVersion;
11
10
  this.calls = 0;
12
- this.queue = [];
13
- this.maxQueueSize = options.maxQueueSize || 100;
14
- this.maxConcurrentRequests = options.maxConcurrentRequests || 5;
15
- this.activeRequests = 0;
11
+ this.queue = [];
12
+ this.maxQueueSize = options.maxQueueSize || 100;
13
+ this.maxConcurrentRequests = options.maxConcurrentRequests || 5;
14
+ this.activeRequests = 0;
16
15
  }
17
16
 
18
17
  setSessionId(sessionId) {
@@ -24,22 +23,23 @@ class Rest {
24
23
  "Content-Type": "application/json",
25
24
  Authorization: this.password,
26
25
  };
27
- const requestOptions = {
28
- method,
29
- headers,
30
- body: body ? JSON.stringify(body) : null,
31
- };
26
+
32
27
  try {
33
- const response = await undiciFetch(`${this.url}${endpoint}`, requestOptions);
28
+ const response = await undiciFetch(`${this.url}${endpoint}`, {
29
+ method,
30
+ headers,
31
+ body: body && JSON.stringify(body),
32
+ });
34
33
  this.calls++;
35
34
  const data = await this.parseResponse(response);
36
35
  this.aqua.emit("apiResponse", endpoint, response);
37
36
  this.aqua.emit(
38
37
  "debug",
39
- `[Rest] ${method} ${endpoint} ${body ? `body: ${JSON.stringify(body)}` : ""} -> Status Code: ${response.status} Response: ${nodeUtil.inspect(data)}`
38
+ `[Rest] ${method} ${endpoint} ${body ? `body: ${JSON.stringify(body)}` : ""} -> Status Code: ${response.status} Response(body): ${JSON.stringify(data)}`
40
39
  );
41
40
  return includeHeaders ? { data, headers: response.headers } : data;
42
41
  } catch (error) {
42
+ this.aqua.emit("debug", `Network error during request: ${method} ${this.url}${endpoint}`, { cause: error });
43
43
  throw new Error(`Network error during request: ${method} ${this.url}${endpoint}`, { cause: error });
44
44
  }
45
45
  }
@@ -50,15 +50,18 @@ class Rest {
50
50
 
51
51
  async updatePlayer(options) {
52
52
  const requestBody = { ...options.data };
53
+
53
54
  if ((requestBody.track && requestBody.track.encoded && requestBody.track.identifier) ||
54
55
  (requestBody.encodedTrack && requestBody.identifier)) {
55
56
  throw new Error(`Cannot provide both 'encoded' and 'identifier' for track in Update Player Endpoint`);
56
57
  }
58
+
57
59
  if (this.version === "v3" && options.data?.track) {
58
60
  const { track } = requestBody;
59
61
  delete requestBody.track;
60
62
  Object.assign(requestBody, track.encoded ? { encodedTrack: track.encoded } : { identifier: track.identifier });
61
63
  }
64
+
62
65
  return this.makeRequest("PATCH", `/${this.version}/sessions/${this.sessionId}/players/${options.guildId}?noReplace=false`, requestBody);
63
66
  }
64
67
 
@@ -70,7 +73,7 @@ class Rest {
70
73
  return this.makeRequest("GET", `/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`);
71
74
  }
72
75
 
73
- async decodeTrack(track, node) {
76
+ async decodeTrack(track) {
74
77
  return this.makeRequest("GET", `/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`);
75
78
  }
76
79
 
@@ -79,10 +82,7 @@ class Rest {
79
82
  }
80
83
 
81
84
  async getStats() {
82
- if (this.version === "v3") {
83
- return this.makeRequest("GET", `/${this.version}/stats`);
84
- }
85
- return this.makeRequest("GET", `/${this.version}/stats/all`);
85
+ return this.makeRequest("GET", this.version === "v3" ? `/${this.version}/stats` : `/${this.version}/stats/all`);
86
86
  }
87
87
 
88
88
  async getInfo() {
@@ -98,55 +98,19 @@ class Rest {
98
98
  }
99
99
 
100
100
  async parseResponse(response) {
101
- if (response.status === 204) {
102
- return null;
103
- }
101
+ if (response.status === 204) return null;
104
102
  try {
105
- const contentType = response.headers.get("Content-Type");
106
- return await response[contentType.includes("text/plain") ? "text" : "json"]();
103
+ return response.headers.get("Content-Type").includes("text/plain") ? await response.text() : await response.json();
107
104
  } catch (error) {
108
105
  this.aqua.emit("debug", `[Rest - Error] Failed to process response from ${response.url}: ${error}`);
109
106
  return null;
110
107
  }
111
108
  }
112
109
 
113
- /**
114
- * Adds a request to the queue and processes it.
115
- * @param {function} requestFunction - The request function to execute.
116
- */
117
- async queueRequest(requestFunction) {
118
- if (this.queue.length >= this.maxQueueSize) {
119
- this.aqua.emit("debug", "[Rest] Queue is full, discarding oldest request.");
120
- this.queue.shift();
121
- }
122
- this.queue.push(requestFunction);
123
- this.processQueue();
124
- }
125
-
126
- /**
127
- * Processes the queue of requests with concurrency control.
128
- */
129
- async processQueue() {
130
- while (this.activeRequests < this.maxConcurrentRequests && this.queue.length > 0) {
131
- const requestFunction = this.queue.shift();
132
- this.activeRequests++;
133
- try {
134
- await requestFunction();
135
- } catch (error) {
136
- this.aqua.emit("error", error);
137
- } finally {
138
- this.activeRequests--;
139
- this.processQueue();
140
- }
141
- }
142
- }
143
-
144
- /**
145
- * Cleans up resources related to the queue.
146
- */
147
110
  cleanupQueue() {
148
- this.queue = [];
111
+ this.queue = [];
149
112
  }
150
113
  }
151
114
 
152
115
  module.exports = { Rest };
116
+
@@ -7,7 +7,7 @@ const { getImageUrl } = require("../handlers/fetchImage");
7
7
  */
8
8
  class Track {
9
9
  /**
10
- * @param {{ encoded: string, info: { identifier: string, isSeekable: boolean, author: string, length: number, isStream: boolean, position: number, title: string, uri: string, sourceName: string, thumbnail: string, track: string, tracks: Array<Track>, playlist: { name: string, selectedTrack: number } } }} data
10
+ * @param {{ encoded: string, info: { identifier: string, isSeekable: boolean, author: string, length: number, isStream: boolean, position: number, title: string, uri: string, sourceName: string, artworkUrl: string, track: string, tracks: Array<Track>, playlist: { name: string, selectedTrack: number } } }} data
11
11
  * @param {Player} requester
12
12
  * @param {Node} nodes
13
13
  */
@@ -17,6 +17,7 @@ class Track {
17
17
  this.requester = requester;
18
18
  this.nodes = nodes;
19
19
  this.track = data.encoded || Buffer.from(data.track, "base64").toString("utf8");
20
+ this.playlist = data.playlist || null;
20
21
  }
21
22
 
22
23
  /**
@@ -62,13 +63,16 @@ class Track {
62
63
  updateTrackInfo(track) {
63
64
  this.info.identifier = track.info.identifier;
64
65
  this.track = track.track;
66
+ if (track.playlist) {
67
+ this.playlist = track.playlist;
68
+ }
65
69
  }
66
70
 
67
71
  /**
68
72
  * @private
69
73
  */
70
74
  cleanup() {
71
- this.rawData = this.track = this.info = null;
75
+ this.rawData = this.track = this.info = this.playlist = null;
72
76
  }
73
77
  }
74
78
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "An Lavalink wrapper, focused in speed, performance, and features, Based in Riffy!",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -44,7 +44,10 @@
44
44
  "type": "git",
45
45
  "url": "https://github.com/ToddyTheNoobDud/AquaLink"
46
46
  },
47
-
47
+ "maintainers": [ {
48
+ "name": "mushroom0162",
49
+ "url": "https://github.com/ToddyTheNoobDud"
50
+ }],
48
51
  "devDependencies": {
49
52
  "discord.js": "^14.16.3"
50
53
  }