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 +32 -0
- package/build/structures/Aqua.js +24 -11
- package/build/structures/Connection.js +14 -12
- package/build/structures/Node.js +22 -38
- package/build/structures/Player.js +58 -26
- package/build/structures/Rest.js +21 -57
- package/build/structures/Track.js +6 -2
- package/package.json +5 -2
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
|
|
package/build/structures/Aqua.js
CHANGED
|
@@ -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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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 };
|
package/build/structures/Node.js
CHANGED
|
@@ -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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
120
|
-
.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.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("
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
+
|
package/build/structures/Rest.js
CHANGED
|
@@ -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
|
-
|
|
28
|
-
method,
|
|
29
|
-
headers,
|
|
30
|
-
body: body ? JSON.stringify(body) : null,
|
|
31
|
-
};
|
|
26
|
+
|
|
32
27
|
try {
|
|
33
|
-
const response = await undiciFetch(`${this.url}${endpoint}`,
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
}
|