aqualink 1.9.0 → 2.0.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
@@ -4,7 +4,7 @@ An Stable, performant, Recourse friendly and fast lavalink wrapper
4
4
  This code is based in riffy, but its an 100% Rewrite made from scratch...
5
5
 
6
6
  # Why use AquaLink
7
- - Uses my modified fork of @performanc/pwsl-mini, for an way faster WebSocket
7
+ - Dependecy-free (0 dependencys), may change in future
8
8
  - Very Low memory comsuption
9
9
  - Built in Queue manager
10
10
  - Lots of features to use
@@ -13,86 +13,24 @@ This code is based in riffy, but its an 100% Rewrite made from scratch...
13
13
  - 1 Player created = ~1 - 0,5 mb per player
14
14
  - Auto clean Up memory when song finishes / bot leave the vc (Now Options supported!)
15
15
  - Plugin system
16
- - Lavalink v4 support (din't test v3)
16
+ - Lavalink v4.0.8 | v4.1.0 Support (Nodelink works, but only with play etc, more support soon)
17
17
  - Youtube and Spotify support (Soundcloud, deezer, vimeo, etc also works...)
18
18
  - Minimal Requests to the lavalink server (helps the lavalink recourses!)
19
- - Easy player, node, aqua manager
20
- - Fast responses from rest and node
21
19
  - Playlist support (My mix playlists, youtube playlists, spotify playlists, etc)
22
20
  - Lyrics Support by Lavalink
23
21
  - https://github.com/topi314/LavaLyrics (RECOMMENDED)
24
22
  - https://github.com/DRSchlaubi/lyrics.kt (?)
25
23
  - https://github.com/DuncteBot/java-timed-lyrics (RECOMMENDED)
26
24
 
27
- # Tralalero Tralala 1.9.0 Released
28
- **Whoa, lots of stuff to write here 😭**
29
-
30
- ---
31
-
32
- ### **Small changes on the `fetchImage` Handler**
33
- - Improves the overall speed, less memory overhead.
34
-
35
- ---
36
-
37
- ### **Remade some stuff on `AQUA` module**
38
- - This fixes some bugs related to destroying players.
39
- - Faster node connection speeds.
40
- - Uses an Array for getting the region instead (testing).
41
- - Small change on the Voice Handler.
42
- - Improved Error handling.
43
- - Use `node.destroy()` method directly.
44
-
45
- ---
46
-
47
- ### **Remade `Connection` module**
48
- - Removed lots of useless code.
49
- - Improved joining voice channel speed.
50
- - Improved configuration set/get speed.
51
- - Improved overall checking.
52
- - Improved debug messages.
53
-
54
- ---
55
-
56
- ### **Remade `Node` module (this one is good)**
57
- - Fixed the `autoResume` system (now will actually work, for 60 seconds).
58
- - New WebSocket System.
59
- - Improved the events handling speed.
60
- - Now does recalculation of the backoff time (for more efficiency on reconnect).
61
- - Now avoids reconnecting if the WebSocket is already open (sorry, I forgot to add this before).
62
- - Better cleaning system (improved, now removes listeners instead of setting to `null`).
63
- - Avoids re-binding the functions every time `connect` is called (yay).
64
- - This update also improves long-process running.
65
-
66
- ---
67
-
68
- ### **Remade the `Player` module (also a good one)**
69
- - Remade every method.
70
- - Fixed destroy system.
71
- - Better event handling, I think.
72
- - Made the events async.
73
- - Removed `trackChange` (does not exist in Lavalink API, use `trackStart` instead).
74
- - Uses a new listener system (way more efficient for creating/destroying players).
75
- - Faster shuffle in V8 Engine (Math stuff).
76
- - Improved overall configs (more precise).
77
- - Use `pop()` instead of disabling the track 50 on the length.
78
- - Improved overall speed on the check-ins and some stuff I forgot.
79
-
80
- ---
81
-
82
- ### **Remade the `Rest` module**
83
- - Better speed (removed useless `buildEndpoint`).
84
- - More compact code.
85
- - Removed `stats/all` in the stats (correct by using the Lavalink API).
86
- - Better `makeRequest`.
87
-
88
- ---
89
-
90
- ### **Small changes in `Track` module**
91
- - More efficient final result (`author` + `track`).
92
-
25
+ # Tralalero Tralala 2.0.0 Released
93
26
  ---
94
27
 
95
- That’s all for **1.9.0** atm. I’m a lazy dev. 😴
28
+ ### Now aqualink is 100% depedency free!
29
+ - Rewrite the rest to use https / http
30
+ - Removed undici usage
31
+ - Rewrite NODE to use built-in WebSocket (no need to upgrade to node 20 or more!)
32
+ - Uses my own made websocket system, so please report any bugs (i beg)
33
+ - more stuff soon, now bye
96
34
 
97
35
  # Docs (Wiki)
98
36
  - https://github.com/ToddyTheNoobDud/AquaLink/wiki
@@ -0,0 +1,259 @@
1
+ const https = require('node:https');
2
+ const http = require('node:http');
3
+ const crypto = require('node:crypto');
4
+ const { EventEmitter } = require('node:events');
5
+ const { URL } = require('node:url');
6
+ const { Buffer } = require('node:buffer');
7
+
8
+ let nativeWs = null;
9
+ if (process.isBun) nativeWs = require('ws');
10
+ const frameHeaderPool = Buffer.alloc(10);
11
+
12
+
13
+ class BufferPool {
14
+ constructor(initialSize = 8192) {
15
+ this.buffer = Buffer.allocUnsafe(initialSize);
16
+ this.used = 0;
17
+ }
18
+
19
+ append(data) {
20
+ const dataLength = data.length;
21
+ if (this.used + dataLength > this.buffer.length) {
22
+ const newSize = Math.max(this.buffer.length * 2, this.used + dataLength);
23
+ const newBuffer = Buffer.allocUnsafe(newSize);
24
+ this.buffer.copy(newBuffer, 0, 0, this.used);
25
+ this.buffer = newBuffer;
26
+ }
27
+ data.copy(this.buffer, this.used);
28
+ this.used += dataLength;
29
+ }
30
+
31
+ consume(bytes) {
32
+ if (bytes === this.used) {
33
+ this.used = 0;
34
+ return;
35
+ }
36
+
37
+ this.buffer.copy(this.buffer, 0, bytes, this.used);
38
+ this.used -= bytes;
39
+ }
40
+
41
+ get data() {
42
+ return this.buffer.subarray(0, this.used);
43
+ }
44
+ }
45
+
46
+ class WebSocket extends EventEmitter {
47
+ constructor(url, options = {}) {
48
+ super();
49
+ this.url = url;
50
+ this.options = options;
51
+ this.socket = null;
52
+ this.bufferPool = new BufferPool();
53
+ this.frameInfo = null;
54
+ this.reconnectAttempts = 0;
55
+ this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
56
+ this.baseReconnectDelay = options.baseReconnectDelay || 1000; // 1 second
57
+ this.connect();
58
+ }
59
+
60
+ connect() {
61
+ const { hostname, protocol, port, pathname, search } = new URL(this.url);
62
+ const isSecure = protocol === 'wss:';
63
+ const agent = isSecure ? https : http;
64
+ const key = crypto.randomBytes(16).toString('base64');
65
+
66
+ const request = agent.request({
67
+ hostname,
68
+ port: port || (isSecure ? 443 : 80),
69
+ path: pathname + search,
70
+ timeout: this.options.timeout || 30000,
71
+ headers: {
72
+ 'Sec-WebSocket-Key': key,
73
+ 'Sec-WebSocket-Version': 13,
74
+ 'Upgrade': 'websocket',
75
+ 'Connection': 'Upgrade',
76
+ ...this.options.headers
77
+ },
78
+ method: 'GET'
79
+ });
80
+
81
+ request.once('error', (err) => this._handleError(err));
82
+ request.once('upgrade', (res, socket, head) => this._handleUpgrade(res, socket, head, key));
83
+ request.end();
84
+ }
85
+
86
+ _handleUpgrade(res, socket, head, key) {
87
+ if (res.headers.upgrade.toLowerCase() !== 'websocket') {
88
+ return socket.destroy();
89
+ }
90
+
91
+ const expectedAccept = crypto.createHash('sha1')
92
+ .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
93
+ .digest('base64');
94
+
95
+ if (res.headers['sec-websocket-accept'] !== expectedAccept) {
96
+ return socket.destroy();
97
+ }
98
+
99
+ this.reconnectAttempts = 0;
100
+
101
+ this.socket = socket;
102
+ this.socket.setNoDelay(true);
103
+ this.socket.setKeepAlive(true);
104
+ this.socket.on('data', (data) => this._processData(data));
105
+ this.socket.once('close', () => this._handleClose(1006));
106
+ this.socket.once('error', (err) => this._handleError(err));
107
+ if (head.length) this._processData(head);
108
+ this.emit('open');
109
+ }
110
+
111
+ _processData(data) {
112
+ this.bufferPool.append(data);
113
+
114
+ while (this.bufferPool.used >= 2) {
115
+ const bufferData = this.bufferPool.data;
116
+ const lengthByte = bufferData[1] & 127;
117
+ let headerSize = 2 + ((bufferData[1] & 128) ? 4 : 0);
118
+ if (lengthByte === 126) headerSize += 2;
119
+ else if (lengthByte === 127) headerSize += 8;
120
+ if (this.bufferPool.used < headerSize) return;
121
+
122
+ const frame = this._parseFrame();
123
+ if (!frame) return;
124
+ this._handleFrame(frame);
125
+ }
126
+ }
127
+
128
+ _parseFrame() {
129
+ const bufferData = this.bufferPool.data;
130
+ if (bufferData.length < 2) return null;
131
+
132
+ const fin = (bufferData[0] & 128) !== 0;
133
+ const opcode = bufferData[0] & 15;
134
+ let payloadLength = bufferData[1] & 127;
135
+ let offset = 2;
136
+
137
+ if (payloadLength === 126) {
138
+ if (bufferData.length < offset + 2) return null;
139
+ payloadLength = bufferData.readUInt16BE(offset);
140
+ offset += 2;
141
+ } else if (payloadLength === 127) {
142
+ if (bufferData.length < offset + 8) return null;
143
+ payloadLength = Number(bufferData.readBigUInt64BE(offset));
144
+ offset += 8;
145
+ }
146
+
147
+ const hasMask = (bufferData[1] & 128) !== 0;
148
+ const mask = hasMask ? bufferData.subarray(offset, offset + 4) : null;
149
+ offset += hasMask ? 4 : 0;
150
+
151
+ if (bufferData.length < offset + payloadLength) return null;
152
+
153
+ let payload = Buffer.from(bufferData.subarray(offset, offset + payloadLength));
154
+
155
+ if (mask) {
156
+ for (let i = 0; i < payload.length; i++) {
157
+ payload[i] = payload[i] ^ mask[i % 4];
158
+ }
159
+ }
160
+
161
+ const totalFrameSize = offset + payloadLength;
162
+ this.bufferPool.consume(totalFrameSize);
163
+
164
+ return { fin, opcode, payload };
165
+ }
166
+
167
+ _handleFrame({ fin, opcode, payload }) {
168
+ if (opcode === 0x8) return this._handleClose(payload.length >= 2 ? payload.readUInt16BE(0) : 1006);
169
+ if (opcode === 0x9) return this._sendFrame(0xA, payload);
170
+ if (opcode === 0xA) return this.emit('pong', payload);
171
+ if (!fin) return;
172
+
173
+ if (opcode === 0x1) {
174
+ this.emit('message', payload.toString('utf-8'));
175
+ } else {
176
+ this.emit('message', payload);
177
+ }
178
+ }
179
+
180
+ _sendFrame(opcode, payload = Buffer.alloc(0)) {
181
+ if (!this.socket || this.socket.destroyed) return;
182
+ const length = payload.length;
183
+ let headerSize = length < 126 ? 2 : length < 65536 ? 4 : 10;
184
+
185
+ frameHeaderPool[0] = 0x80 | opcode;
186
+ if (length < 126) {
187
+ frameHeaderPool[1] = length;
188
+ } else if (length < 65536) {
189
+ frameHeaderPool[1] = 126;
190
+ frameHeaderPool.writeUInt16BE(length, 2);
191
+ } else {
192
+ frameHeaderPool[1] = 127;
193
+ frameHeaderPool.writeBigUInt64BE(BigInt(length), 2);
194
+ }
195
+
196
+ this.socket.write(frameHeaderPool.subarray(0, headerSize));
197
+ this.socket.write(payload);
198
+ }
199
+
200
+ send(data) {
201
+ if (typeof data === 'string') {
202
+ data = Buffer.from(data, 'utf-8');
203
+ } else if (!Buffer.isBuffer(data)) {
204
+ throw new Error('Data must be a string or Buffer');
205
+ }
206
+ this._sendFrame(0x1, data);
207
+ }
208
+
209
+ ping(data = '') {
210
+ const pingData = typeof data === 'string' ? Buffer.from(data, 'utf-8') : data;
211
+ this._sendFrame(0x9, pingData);
212
+ }
213
+
214
+ close(code = 1000) {
215
+ const codeBuffer = Buffer.allocUnsafe(2);
216
+ codeBuffer.writeUInt16BE(code, 0);
217
+ this._sendFrame(0x8, codeBuffer);
218
+ setTimeout(() => this._handleClose(code), 100);
219
+ }
220
+
221
+ _handleClose(code) {
222
+ if (this.socket) {
223
+ this.socket.destroy();
224
+ this.socket = null;
225
+ }
226
+ this.bufferPool = new BufferPool();
227
+ this.emit('close', code);
228
+ }
229
+
230
+ _handleError(err) {
231
+ if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') {
232
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
233
+ this.reconnectAttempts++;
234
+ const delay = Math.min(
235
+ this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
236
+ 30000
237
+ );
238
+
239
+ const jitter = Math.random() * 0.3 * delay;
240
+ const reconnectDelay = delay + jitter;
241
+
242
+ setTimeout(() => this.connect(), reconnectDelay);
243
+ this.emit('reconnecting', {
244
+ attempt: this.reconnectAttempts,
245
+ delay: reconnectDelay,
246
+ error: err
247
+ });
248
+ } else {
249
+ this.emit('error', new Error(`Maximum reconnection attempts (${this.maxReconnectAttempts}) exceeded: ${err.message}`));
250
+ this._handleClose(1006);
251
+ }
252
+ } else {
253
+ this.emit('error', err);
254
+ this._handleClose(1006);
255
+ }
256
+ }
257
+ }
258
+
259
+ module.exports = nativeWs || WebSocket;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const WebSocket = require("@toddynnn/pwsl-mini");
2
+ const WebSocket = require('../handlers/WebSocket');
3
3
  const Rest = require("./Rest");
4
4
 
5
5
  class Node {
@@ -58,8 +58,6 @@ class Node {
58
58
  }
59
59
 
60
60
  async connect() {
61
- if (this.#ws && this.#ws.readyState === WebSocket.OPEN) return;
62
-
63
61
  this.#ws = new WebSocket(this.wsUrl.href, {
64
62
  headers: this.#constructHeaders(),
65
63
  perMessageDeflate: false,
@@ -100,7 +98,9 @@ class Node {
100
98
 
101
99
  async getStats() {
102
100
  const stats = await this.rest.getStats();
103
- this.stats = { ...this.defaultStats, ...stats };
101
+ if (JSON.stringify(this.stats) !== JSON.stringify({ ...this.defaultStats, ...stats })) {
102
+ this.stats = { ...this.defaultStats, ...stats };
103
+ }
104
104
  return this.stats;
105
105
  }
106
106
 
@@ -109,7 +109,7 @@ class Node {
109
109
  try {
110
110
  payload = JSON.parse(msg);
111
111
  } catch {
112
- return; // Invalid JSON, ignore the message
112
+ return;
113
113
  }
114
114
 
115
115
  const op = payload?.op;
@@ -141,13 +141,16 @@ class Node {
141
141
 
142
142
  #updateStats(payload) {
143
143
  if (!payload) return;
144
- this.stats = {
144
+ const newStats = {
145
145
  ...this.stats,
146
146
  ...payload,
147
147
  memory: this.#updateMemoryStats(payload.memory),
148
148
  cpu: this.#updateCpuStats(payload.cpu),
149
149
  frameStats: this.#updateFrameStats(payload.frameStats)
150
- };
150
+ };
151
+ if (JSON.stringify(this.stats) !== JSON.stringify(newStats)) {
152
+ this.stats = newStats;
153
+ }
151
154
  }
152
155
 
153
156
  #updateMemoryStats(memory = {}) {
@@ -192,7 +195,6 @@ class Node {
192
195
  this.sessionId = payload.sessionId;
193
196
  this.rest.setSessionId(payload.sessionId);
194
197
 
195
- // Don't resume here - we'll handle resuming in the #onOpen method if autoResume is enabled
196
198
  this.aqua.emit("nodeConnect", this);
197
199
  }
198
200
 
@@ -213,9 +215,11 @@ class Node {
213
215
  }
214
216
 
215
217
  #reconnect() {
218
+ clearTimeout(this.#reconnectTimeoutId);
219
+
216
220
  if (this.infiniteReconnects) {
217
- this.aqua.emit("nodeReconnect", this, "Infinite reconnects enabled, trying non-stop...");
218
- setTimeout(() => this.connect(), 10000);
221
+ this.aqua.emit("nodeReconnect", this, "Infinite reconnects enabled, trying again in 10 seconds");
222
+ this.#reconnectTimeoutId = setTimeout(() => this.connect(), 10000);
219
223
  return;
220
224
  }
221
225
 
@@ -226,19 +230,17 @@ class Node {
226
230
  return;
227
231
  }
228
232
 
229
- clearTimeout(this.#reconnectTimeoutId);
230
- const jitter = Math.random() * 10000;
231
- const backoffTime = Math.min(
232
- this.reconnectTimeout * Math.pow(Node.BACKOFF_MULTIPLIER, this.#reconnectAttempted) + jitter,
233
- Node.MAX_BACKOFF
234
- );
233
+ const baseBackoff = this.reconnectTimeout * Math.pow(Node.BACKOFF_MULTIPLIER, this.#reconnectAttempted);
234
+ const jitter = Math.random() * Math.min(2000, baseBackoff * 0.2);
235
+ const backoffTime = Math.min(baseBackoff + jitter, Node.MAX_BACKOFF);
235
236
 
237
+ this.#reconnectAttempted++;
238
+ this.aqua.emit("nodeReconnect", this, {
239
+ attempt: this.#reconnectAttempted,
240
+ backoffTime
241
+ });
242
+
236
243
  this.#reconnectTimeoutId = setTimeout(() => {
237
- this.#reconnectAttempted++;
238
- this.aqua.emit("nodeReconnect", this, {
239
- attempt: this.#reconnectAttempted,
240
- backoffTime
241
- });
242
244
  this.connect();
243
245
  }, backoffTime);
244
246
  }
@@ -275,4 +277,4 @@ class Node {
275
277
  }
276
278
  }
277
279
 
278
- module.exports = Node;
280
+ module.exports = Node;
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
- const { Pool } = require("undici");
2
+ const https = require("https");
3
+ const http = require("http");
3
4
 
4
5
  class Rest {
5
6
  constructor(aqua, { secure, host, port, sessionId, password }) {
6
7
  this.aqua = aqua;
7
8
  this.sessionId = sessionId;
8
9
  this.version = "v4";
9
- this.baseUrl = `http${secure ? "s" : ""}://${host}:${port}`;
10
+ this.baseUrl = `${secure ? "https" : "http"}://${host}:${port}`;
10
11
  this.headers = {
11
12
  "Content-Type": "application/json",
12
13
  Authorization: password,
13
14
  };
14
- this.client = new Pool(this.baseUrl, { pipelining: 1 });
15
+ this.client = secure ? https : http;
15
16
  }
16
17
 
17
18
  setSessionId(sessionId) {
@@ -19,18 +20,33 @@ class Rest {
19
20
  }
20
21
 
21
22
  async makeRequest(method, endpoint, body = null) {
22
- try {
23
- const response = await this.client.request({
24
- path: endpoint,
25
- method,
26
- headers: this.headers,
27
- body: body ? JSON.stringify(body) : undefined,
23
+ const options = {
24
+ method,
25
+ headers: this.headers,
26
+ };
27
+
28
+ return new Promise((resolve, reject) => {
29
+ const req = this.client.request(`${this.baseUrl}${endpoint}`, options, (res) => {
30
+ let data = "";
31
+ res.setEncoding("utf8");
32
+
33
+ res.on("data", (chunk) => (data += chunk));
34
+ res.on("end", () => {
35
+ if (res.statusCode >= 200 && res.statusCode < 300) {
36
+ resolve(data ? JSON.parse(data) : null);
37
+ } else {
38
+ reject(new Error(`Request failed with status ${res.statusCode}: ${res.statusMessage}`));
39
+ }
40
+ });
28
41
  });
29
42
 
30
- return response.statusCode === 204 ? null : await response.body.json();
31
- } catch (error) {
32
- throw new Error(`Request failed (${method} ${endpoint}): ${error.message}`);
33
- }
43
+ req.on("error", (error) => reject(new Error(`Request failed (${method} ${endpoint}): ${error.message}`)));
44
+
45
+ if (body) {
46
+ req.write(JSON.stringify(body));
47
+ }
48
+ req.end();
49
+ });
34
50
  }
35
51
 
36
52
  validateSessionId() {
@@ -38,59 +54,63 @@ class Rest {
38
54
  }
39
55
 
40
56
  async updatePlayer({ guildId, data }) {
41
- if ((data.track?.encoded && data.track?.identifier) || (data.encodedTrack && data.identifier)) {
57
+ if (data.track?.encoded && data.track?.identifier) {
42
58
  throw new Error("Cannot provide both 'encoded' and 'identifier' for track");
43
59
  }
44
60
  this.validateSessionId();
45
61
  return this.makeRequest("PATCH", `/${this.version}/sessions/${this.sessionId}/players/${guildId}?noReplace=false`, data);
46
62
  }
47
63
 
48
- async getPlayers() {
64
+ getPlayers() {
49
65
  this.validateSessionId();
50
66
  return this.makeRequest("GET", `/${this.version}/sessions/${this.sessionId}/players`);
51
67
  }
52
68
 
53
- async destroyPlayer(guildId) {
69
+ destroyPlayer(guildId) {
54
70
  this.validateSessionId();
55
71
  return this.makeRequest("DELETE", `/${this.version}/sessions/${this.sessionId}/players/${guildId}`);
56
72
  }
57
73
 
58
- async getTracks(identifier) {
74
+ getTracks(identifier) {
59
75
  return this.makeRequest("GET", `/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`);
60
76
  }
61
77
 
62
- async decodeTrack(track) {
78
+ decodeTrack(track) {
63
79
  return this.makeRequest("GET", `/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`);
64
80
  }
65
81
 
66
- async decodeTracks(tracks) {
82
+ decodeTracks(tracks) {
67
83
  return this.makeRequest("POST", `/${this.version}/decodetracks`, tracks);
68
84
  }
69
85
 
70
- async getStats() {
86
+ getStats() {
71
87
  return this.makeRequest("GET", `/${this.version}/stats`);
72
88
  }
73
89
 
74
- async getInfo() {
90
+ getInfo() {
75
91
  return this.makeRequest("GET", `/${this.version}/info`);
76
92
  }
77
93
 
78
- async getRoutePlannerStatus() {
94
+ getRoutePlannerStatus() {
79
95
  return this.makeRequest("GET", `/${this.version}/routeplanner/status`);
80
96
  }
81
97
 
82
- async getRoutePlannerAddress(address) {
98
+ getRoutePlannerAddress(address) {
83
99
  return this.makeRequest("POST", `/${this.version}/routeplanner/free/address`, { address });
84
100
  }
85
101
 
86
102
  async getLyrics({ track }) {
87
103
  if (track.search) {
88
- const res = await this.makeRequest("GET", `/${this.version}/lyrics/search?query=${encodeURIComponent(track.encoded.info.title)}&source=genius`);
89
- if (res) return res;
104
+ try {
105
+ const res = await this.makeRequest("GET", `/${this.version}/lyrics/search?query=${encodeURIComponent(track.encoded.info.title)}&source=genius`);
106
+ if (res) return res;
107
+ } catch (error) {
108
+ console.error("Failed to fetch lyrics:", error.message);
109
+ }
90
110
  }
91
111
  this.validateSessionId();
92
112
  return this.makeRequest("GET", `/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/track/lyrics?skipTrackSource=false`);
93
113
  }
94
114
  }
95
115
 
96
- module.exports = Rest;
116
+ module.exports = Rest;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "1.9.0",
4
- "description": "An Lavalink wrapper, focused in speed, performance, and features, Based in Riffy!",
3
+ "version": "2.0.0",
4
+ "description": "An Lavalink client, focused in pure performance and features",
5
5
  "main": "build/index.js",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
@@ -36,10 +36,6 @@
36
36
  ],
37
37
  "author": "mushroom0162 (https://github.com/ToddyTheNoobDud)",
38
38
  "license": "ISC",
39
- "dependencies": {
40
- "undici": "^7.4.0",
41
- "@toddynnn/pwsl-mini": "github:ToddyTheNoobDud/websocket"
42
- },
43
39
  "repository": {
44
40
  "type": "git",
45
41
  "url": "https://github.com/ToddyTheNoobDud/AquaLink"