aqualink 1.8.1-beta1 → 1.8.1-beta3

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
@@ -31,8 +31,12 @@ This code is based in riffy, but its an 100% Rewrite made from scratch...
31
31
 
32
32
  # Brick by brick, 1.8.0 Update (yay)
33
33
 
34
- - Misc changes on FetchImage (improves the overall checking and speed)
34
+ ### 1.8.1-beta3 Update:
35
+ - Use pool for connections (Experimental, help me improve it.)
36
+ - Remade some stuff on node related to caching
35
37
 
38
+ ### 1.8.0
39
+ - Misc changes on FetchImage (improves the overall checking and speed)
36
40
  - Rewrite `AQUA` module
37
41
  - Remade the resolve logic (improves the speed by a lot)
38
42
  - Fixes many memory usages related to nodes
@@ -109,7 +113,7 @@ const nodes = [
109
113
  }
110
114
  ];
111
115
 
112
- const aqua = new Aqua(client, nodes, {
116
+ const aqua = Aqua(client, nodes, {
113
117
  defaultSearchPlatform: "ytsearch",
114
118
  restVersion: "v4",
115
119
  autoResume: false,
package/build/index.js CHANGED
@@ -1,11 +1,11 @@
1
- const { Connection } = require("./structures/Connection");
2
- const { Filters } = require("./structures/Filters");
3
- const { Node } = require("./structures/Node");
4
- const { Aqua } = require("./structures/Aqua");
5
- const { Player } = require("./structures/Player");
6
- const { Plugin } = require("./structures/Plugins");
7
- const { Queue } = require("./structures/Queue");
8
- const { Rest } = require("./structures/Rest");
9
- const { Track } = require("./structures/Track");
1
+ const Connection = require("./structures/Connection");
2
+ const Filters = require("./structures/Filters");
3
+ const Node = require("./structures/Node");
4
+ const Aqua = require("./structures/Aqua");
5
+ const Player = require("./structures/Player");
6
+ const Plugin = require("./structures/Plugins");
7
+ const Queue = require("./structures/Queue");
8
+ const Rest = require("./structures/Rest");
9
+ const Track = require("./structures/Track");
10
10
 
11
11
  module.exports = { Connection, Filters, Node, Aqua, Player, Plugin, Queue, Rest, Track };
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  const { EventEmitter } = require("node:events");
4
- const Node = require("./Node");
5
- const Player = require("./Player");
6
- const Track = require("./Track");
4
+ const Node = require("./Node");
5
+ const Player = require("./Player");
6
+ const Track = require("./Track");
7
7
  const { version: pkgVersion } = require("../../package.json");
8
8
  const URL_REGEX = /^https?:\/\//;
9
9
 
@@ -37,7 +37,7 @@ class Connection {
37
37
  : `Voice Server: ${newRegion}`;
38
38
 
39
39
  this.aqua.emit("debug", `[Player ${this.guildId} - CONNECTION] ${message}`);
40
- this._updatePlayerVoiceData();
40
+ this._updatePlayerVoiceData();
41
41
  }
42
42
  }
43
43
 
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
-
3
2
  const WebSocket = require("ws");
4
- const Rest = require("./Rest");
3
+ const Rest = require("./Rest");
5
4
 
6
5
  class Node {
7
6
  #ws = null;
8
- #lastStatsRequest = 0;
9
7
  #reconnectAttempted = 0;
10
8
  #reconnectTimeoutId = null;
9
+ static BACKOFF_MULTIPLIER = 1.5;
10
+ static MAX_BACKOFF = 60000;
11
11
 
12
12
  constructor(aqua, connOptions, options = {}) {
13
13
  const {
@@ -38,15 +38,15 @@ class Node {
38
38
  this.infiniteReconnects = options.infiniteReconnects || false;
39
39
  this.connected = false;
40
40
  this.info = null;
41
- this.stats = this.#createStats();
42
-
41
+ this.defaultStats = this.#createDefaultStats();
42
+ this.stats = { ...this.defaultStats };
43
43
  this._onOpen = this.#onOpen.bind(this);
44
44
  this._onError = this.#onError.bind(this);
45
45
  this._onMessage = this.#onMessage.bind(this);
46
46
  this._onClose = this.#onClose.bind(this);
47
47
  }
48
48
 
49
- #createStats() {
49
+ #createDefaultStats() {
50
50
  return {
51
51
  players: 0,
52
52
  playingPlayers: 0,
@@ -62,14 +62,11 @@ class Node {
62
62
  this.#ws = new WebSocket(this.wsUrl.href, {
63
63
  headers: this.#constructHeaders(),
64
64
  perMessageDeflate: false,
65
- handshakeTimeout: 30000
66
65
  });
67
-
68
66
  this.#ws.once("open", this._onOpen);
69
67
  this.#ws.once("error", this._onError);
70
68
  this.#ws.on("message", this._onMessage);
71
69
  this.#ws.once("close", this._onClose);
72
- this.aqua.emit("debug", this.name, "Connecting...");
73
70
  }
74
71
 
75
72
  #constructHeaders() {
@@ -86,30 +83,46 @@ class Node {
86
83
  this.connected = true;
87
84
  this.#reconnectAttempted = 0;
88
85
  this.aqua.emit("debug", this.name, `Connected to ${this.wsUrl.href}`);
89
- try {
90
- this.info = await this.rest.makeRequest("GET", "/v4/info");
91
- if (this.autoResume) await this.resumePlayers();
92
- } catch (err) {
93
- this.info = null;
94
- if (!this.aqua.bypassChecks?.nodeFetchInfo) {
95
- this.aqua.emit("error", `Failed to fetch node info: ${err.message}`);
86
+
87
+ if (this.autoResume) {
88
+ try {
89
+ this.info = await this.rest.makeRequest("GET", "/v4/info");
90
+ await this.resumePlayers();
91
+ } catch (err) {
92
+ this.info = null;
93
+ if (!this.aqua.bypassChecks?.nodeFetchInfo) {
94
+ this.aqua.emit("error", `Failed to fetch node info: ${err.message}`);
95
+ }
96
96
  }
97
97
  }
98
98
  }
99
99
 
100
100
  async getStats() {
101
- if (!this.connected) return this.stats;
102
- const now = Date.now();
103
- const STATS_COOLDOWN = 60000;
104
- if (now - this.#lastStatsRequest < STATS_COOLDOWN) return this.stats;
101
+ const stats = await this.rest.makeRequest("GET", "/v4/stats");
102
+ this.stats = { ...this.defaultStats, ...stats };
103
+ return this.stats;
104
+ }
105
+
106
+ async #onMessage(msg) {
107
+ let payload;
105
108
  try {
106
- const stats = await this.rest.makeRequest("GET", "/v4/stats");
107
- this.stats = { ...this.#createStats(), ...stats };
108
- this.#lastStatsRequest = now;
109
- } catch (err) {
110
- this.aqua.emit("debug", `Stats fetch error: ${err.message}`);
109
+ payload = JSON.parse(msg);
110
+ } catch {
111
+ return;
112
+ }
113
+ const op = payload?.op;
114
+ if (!op) return;
115
+
116
+ switch (op) {
117
+ case "stats":
118
+ this.#updateStats(payload);
119
+ break;
120
+ case "ready":
121
+ this.#handleReadyOp(payload);
122
+ break;
123
+ default:
124
+ this.#handlePlayerOp(payload);
111
125
  }
112
- return this.stats;
113
126
  }
114
127
 
115
128
  #updateStats(payload) {
@@ -120,7 +133,7 @@ class Node {
120
133
  memory: this.#updateMemoryStats(payload.memory),
121
134
  cpu: this.#updateCpuStats(payload.cpu),
122
135
  frameStats: this.#updateFrameStats(payload.frameStats)
123
- };
136
+ };
124
137
  }
125
138
 
126
139
  #updateMemoryStats(memory = {}) {
@@ -156,30 +169,6 @@ class Node {
156
169
  };
157
170
  }
158
171
 
159
- #onMessage(msg) {
160
- let payload;
161
- try {
162
- payload = JSON.parse(msg);
163
- } catch {
164
- return;
165
- }
166
-
167
- const op = payload?.op;
168
- if (!op) return;
169
-
170
- // Use switch for better performance with multiple conditions
171
- switch (op) {
172
- case "stats":
173
- this.#updateStats(payload);
174
- break;
175
- case "ready":
176
- this.#handleReadyOp(payload);
177
- break;
178
- default:
179
- this.#handlePlayerOp(payload);
180
- }
181
- }
182
-
183
172
  #handleReadyOp(payload) {
184
173
  if (this.sessionId !== payload.sessionId) {
185
174
  this.sessionId = payload.sessionId;
@@ -209,22 +198,18 @@ class Node {
209
198
  setTimeout(() => this.connect(), 10000);
210
199
  return;
211
200
  }
212
-
213
201
  if (this.#reconnectAttempted >= this.reconnectTries) {
214
202
  this.aqua.emit("nodeError", this,
215
203
  new Error(`Max reconnection attempts reached (${this.reconnectTries})`));
216
204
  this.destroy(true);
217
205
  return;
218
206
  }
219
-
220
207
  clearTimeout(this.#reconnectTimeoutId);
221
-
222
208
  const jitter = Math.random() * 10000;
223
209
  const backoffTime = Math.min(
224
210
  this.reconnectTimeout * Math.pow(Node.BACKOFF_MULTIPLIER, this.#reconnectAttempted) + jitter,
225
211
  Node.MAX_BACKOFF
226
212
  );
227
-
228
213
  this.#reconnectTimeoutId = setTimeout(() => {
229
214
  this.#reconnectAttempted++;
230
215
  this.aqua.emit("nodeReconnect", {
@@ -236,22 +221,6 @@ class Node {
236
221
  }, backoffTime);
237
222
  }
238
223
 
239
- get penalties() {
240
- if (!this.connected) return Number.MAX_SAFE_INTEGER;
241
-
242
- let penalties = this.stats.players;
243
-
244
- const { cpu, frameStats } = this.stats;
245
- if (cpu?.systemLoad) {
246
- penalties += Math.round(Math.pow(1.05, 100 * cpu.systemLoad) * 10 - 10);
247
- }
248
- if (frameStats) {
249
- penalties += frameStats.deficit + (frameStats.nulled * 2);
250
- }
251
-
252
- return penalties;
253
- }
254
-
255
224
  destroy(clean = false) {
256
225
  if (clean) {
257
226
  this.aqua.emit("nodeDestroy", this);
@@ -269,9 +238,7 @@ class Node {
269
238
  this.aqua.nodeMap.delete(this.name);
270
239
  this.aqua.emit("nodeDestroy", this);
271
240
  this.info = null;
272
- this.#lastStatsRequest = 0;
273
- this.stats = this.#createStats();
274
241
  }
275
242
  }
276
243
 
277
- module.exports = Node
244
+ module.exports = Node;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
- const { request } = require("undici");
2
+ const { Pool } = require("undici");
3
3
 
4
4
  class Rest {
5
- constructor(aqua, { secure, host, port, sessionId, password }) {
5
+ constructor(aqua, { secure, host, port, sessionId, password,}) {
6
6
  this.aqua = aqua;
7
7
  this.sessionId = sessionId;
8
8
  this.version = "v4";
@@ -11,6 +11,9 @@ class Rest {
11
11
  "Content-Type": "application/json",
12
12
  Authorization: password,
13
13
  };
14
+ this.client = new Pool(this.baseUrl, {
15
+ pipelining: 1,
16
+ });
14
17
  }
15
18
 
16
19
  setSessionId(sessionId) {
@@ -19,21 +22,15 @@ class Rest {
19
22
 
20
23
  async makeRequest(method, endpoint, body = null) {
21
24
  const options = {
25
+ path: endpoint,
22
26
  method,
23
27
  headers: this.headers,
24
- body: body ? JSON.stringify(body) : undefined,
28
+ ...(body && { body: JSON.stringify(body) }),
25
29
  };
26
-
27
30
  try {
28
- const { statusCode, headers, body: responseBody } = await request(`${this.baseUrl}${endpoint}`, options);
29
- this.aqua.emit("apiResponse", endpoint, { status: statusCode, headers: headers });
30
-
31
- if (statusCode === 204) {
32
- return null;
33
- }
34
-
35
- const data = await responseBody.text();
36
- return data ? JSON.parse(data) : null;
31
+ const response = await this.client.request(options);
32
+ const { statusCode } = response;
33
+ return statusCode === 204 ? null : await response.body.json();
37
34
  } catch (error) {
38
35
  throw new Error(`Request to ${endpoint} failed: ${error.message}`);
39
36
  }
@@ -43,16 +40,21 @@ class Rest {
43
40
  const validSegments = segments.filter(segment => segment && segment.trim());
44
41
  return '/' + validSegments.join('/');
45
42
  }
43
+
46
44
  validateSessionId() {
47
45
  if (!this.sessionId) {
48
46
  throw new Error("Session ID is not set.");
49
47
  }
50
48
  }
51
49
 
52
- updatePlayer({ guildId, data }) {
53
- if ((data.track?.encoded && data.track?.identifier) || (data.encodedTrack && data.identifier)) {
50
+ async updatePlayer({ guildId, data }) {
51
+ const hasEncodedTrack = data.track?.encoded && data.track?.identifier;
52
+ const hasEncodedTrackAlt = data.encodedTrack && data.identifier;
53
+
54
+ if (hasEncodedTrack || hasEncodedTrackAlt) {
54
55
  throw new Error("Cannot provide both 'encoded' and 'identifier' for track");
55
56
  }
57
+
56
58
  this.validateSessionId();
57
59
  const endpoint = this.buildEndpoint(this.version, "sessions", this.sessionId, "players", guildId) + "?noReplace=false";
58
60
  return this.makeRequest("PATCH", endpoint, data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "1.8.1-beta1",
3
+ "version": "1.8.1-beta3",
4
4
  "description": "An Lavalink wrapper, focused in speed, performance, and features, Based in Riffy!",
5
5
  "main": "build/index.js",
6
6
  "types": "index.d.ts",