aqualink 1.0.5 → 1.2.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 +13 -0
- package/build/structures/Aqua.js +47 -29
- package/build/structures/Connection.js +6 -28
- package/build/structures/Node.js +24 -50
- package/build/structures/Player.js +153 -110
- package/build/structures/Queue.js +1 -0
- package/build/structures/Rest.js +20 -45
- package/build/structures/Track.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,19 @@ This code is based in riffy, but its an 100% Rewrite made from scratch...
|
|
|
20
20
|
# Docs (Wiki)
|
|
21
21
|
- https://github.com/ToddyTheNoobDud/AquaLink/wiki
|
|
22
22
|
|
|
23
|
+
- Example bot: https://github.com/ToddyTheNoobDud/Thorium-Music
|
|
24
|
+
|
|
25
|
+
# Yay, Version 1.2.0 is released ! aqualink so cool
|
|
26
|
+
|
|
27
|
+
+ Fixed all the loop system
|
|
28
|
+
+ Fixed could not play after ending (weird ahh bug)
|
|
29
|
+
+ Improved the general queue handler
|
|
30
|
+
+ Remade the REST system (improved speed, ram, and removed useless queue handling)
|
|
31
|
+
+ Updated player stuff (Fixed bugs, Added methods: shuffle, getQueue(), restart)
|
|
32
|
+
+ Updated the NODE system (misc improvements for speed)
|
|
33
|
+
+ Rewrited Aqua system (Fixed playlists support (finnaly), rewrited resolve(), Improved all the code, auto cleanup)
|
|
34
|
+
+ Remade the connection system for handling
|
|
35
|
+
|
|
23
36
|
# How to install
|
|
24
37
|
|
|
25
38
|
`npm install aqualink`
|
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 || [];
|
|
@@ -51,7 +53,7 @@ class Aqua extends EventEmitter {
|
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* Initializes Aqua with the provided client ID.
|
|
54
|
-
* @param {string} clientId - The client ID
|
|
56
|
+
* @param {string} clientId - The client ID
|
|
55
57
|
* @returns {Aqua} The Aqua instance.
|
|
56
58
|
*/
|
|
57
59
|
init(clientId) {
|
|
@@ -98,7 +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
|
}
|
|
101
|
-
|
|
103
|
+
|
|
102
104
|
/**
|
|
103
105
|
* Fetches nodes by the specified region.
|
|
104
106
|
* @param {string} region - The region to filter nodes by.
|
|
@@ -155,6 +157,7 @@ class Aqua extends EventEmitter {
|
|
|
155
157
|
*/
|
|
156
158
|
destroyPlayer(guildId) {
|
|
157
159
|
const player = this.players.get(guildId);
|
|
160
|
+
player.clearData();
|
|
158
161
|
if (!player) return;
|
|
159
162
|
player.destroy();
|
|
160
163
|
this.players.delete(guildId);
|
|
@@ -167,6 +170,7 @@ class Aqua extends EventEmitter {
|
|
|
167
170
|
*/
|
|
168
171
|
removeConnection(guildId) {
|
|
169
172
|
const player = this.players.get(guildId);
|
|
173
|
+
player.clearData();
|
|
170
174
|
if (player) {
|
|
171
175
|
player.destroy();
|
|
172
176
|
this.players.delete(guildId);
|
|
@@ -200,8 +204,7 @@ class Aqua extends EventEmitter {
|
|
|
200
204
|
response = await this.handleNoMatches(requestNode.rest, query);
|
|
201
205
|
}
|
|
202
206
|
|
|
203
|
-
this.
|
|
204
|
-
return this.constructResponse();
|
|
207
|
+
return this.constructorResponse(response, requester, requestNode);
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
/**
|
|
@@ -223,37 +226,52 @@ class Aqua extends EventEmitter {
|
|
|
223
226
|
* @param {Object} response - The response from the track resolution.
|
|
224
227
|
* @param {Object} requester - The requester of the tracks.
|
|
225
228
|
* @param {Node} requestNode - The node that handled the request.
|
|
229
|
+
* @returns {Object} The constructed response.
|
|
226
230
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
constructorResponse(response, requester, requestNode) {
|
|
232
|
+
switch (response.loadType) {
|
|
233
|
+
case "track":
|
|
234
|
+
if (response.data) {
|
|
235
|
+
return {
|
|
236
|
+
loadType: response.loadType,
|
|
237
|
+
exception: null,
|
|
238
|
+
playlistInfo: null,
|
|
239
|
+
pluginInfo: response.pluginInfo || {},
|
|
240
|
+
tracks: [new Track(response.data, requester, requestNode)],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case "playlist":
|
|
245
|
+
return {
|
|
246
|
+
loadType: response.loadType,
|
|
247
|
+
exception: null,
|
|
248
|
+
playlistInfo: {
|
|
249
|
+
name: response.data?.info?.name || response.data?.info?.title,
|
|
250
|
+
...response.data?.info,
|
|
251
|
+
},
|
|
252
|
+
pluginInfo: response.pluginInfo || {},
|
|
253
|
+
tracks: response.data?.tracks?.map(track => new Track(track, requester, requestNode)) || [],
|
|
254
|
+
};
|
|
255
|
+
case "search":
|
|
256
|
+
return {
|
|
257
|
+
loadType: response.loadType,
|
|
258
|
+
exception: null,
|
|
259
|
+
playlistInfo: null,
|
|
260
|
+
pluginInfo: response.pluginInfo || {},
|
|
261
|
+
tracks: response.data?.map(track => new Track(track, requester, requestNode)),
|
|
262
|
+
};
|
|
238
263
|
}
|
|
239
|
-
this.loadType = response.loadType;
|
|
240
|
-
this.pluginInfo = response.pluginInfo || {};
|
|
241
|
-
}
|
|
242
264
|
|
|
243
|
-
/**
|
|
244
|
-
* Constructs the response object for the resolved tracks.
|
|
245
|
-
* @returns {Object} The constructed response.
|
|
246
|
-
*/
|
|
247
|
-
constructResponse() {
|
|
248
265
|
return {
|
|
249
|
-
loadType:
|
|
250
|
-
exception:
|
|
251
|
-
playlistInfo:
|
|
252
|
-
pluginInfo:
|
|
253
|
-
tracks:
|
|
266
|
+
loadType: response.loadType,
|
|
267
|
+
exception: response.loadType === "error" ? response.loadType.data : (response.loadType === "LOAD_FAILED" ? response.loadType.exception : null),
|
|
268
|
+
playlistInfo: null,
|
|
269
|
+
pluginInfo: response.pluginInfo || {},
|
|
270
|
+
tracks: [],
|
|
254
271
|
};
|
|
255
272
|
}
|
|
256
273
|
|
|
274
|
+
|
|
257
275
|
/**
|
|
258
276
|
* Gets the player associated with the specified guild ID.
|
|
259
277
|
* @param {string} guildId - The ID of the guild.
|
|
@@ -280,4 +298,4 @@ class Aqua extends EventEmitter {
|
|
|
280
298
|
}
|
|
281
299
|
}
|
|
282
300
|
|
|
283
|
-
module.exports = { Aqua };
|
|
301
|
+
module.exports = { Aqua };
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Class representing a player connection.
|
|
3
|
-
* @param {Player} player The player instance of this connection.
|
|
4
|
-
*/
|
|
5
1
|
class Connection {
|
|
6
2
|
constructor(player) {
|
|
7
3
|
this.player = player;
|
|
@@ -10,42 +6,27 @@ class Connection {
|
|
|
10
6
|
this.selfDeaf = false;
|
|
11
7
|
this.selfMute = false;
|
|
12
8
|
this.voiceChannel = player.voiceChannel;
|
|
9
|
+
this.lastUpdateTime = 0;
|
|
10
|
+
this.updateThrottle = 1000;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
/**
|
|
16
|
-
* Sets the server update data (endpoint and token) for the player.
|
|
17
|
-
* @param {object} data The server update data from the VOICE_SERVER_UPDATE packet.
|
|
18
|
-
* @param {string} data.endpoint The endpoint URL of the voice server.
|
|
19
|
-
* @param {string} data.token The token for the voice server.
|
|
20
|
-
*/
|
|
21
13
|
setServerUpdate({ endpoint, token }) {
|
|
22
|
-
if (!endpoint)
|
|
23
|
-
throw new Error("Missing 'endpoint' property in VOICE_SERVER_UPDATE packet/payload. Please wait or disconnect the bot from the voice channel and try again.");
|
|
24
|
-
}
|
|
14
|
+
if (!endpoint) throw new Error("Missing 'endpoint' property in VOICE_SERVER_UPDATE");
|
|
25
15
|
|
|
26
16
|
const previousVoiceRegion = this.region;
|
|
27
17
|
this.voice.endpoint = endpoint;
|
|
28
18
|
this.voice.token = token;
|
|
29
19
|
this.region = endpoint.split(".")[0].replace(/[0-9]/g, "");
|
|
20
|
+
|
|
30
21
|
this.player.aqua.emit("debug", `[Player ${this.player.guildId} - CONNECTION] ${previousVoiceRegion ? `Changed Voice Region from ${previousVoiceRegion} to ${this.region}` : `Voice Server: ${this.region}`}`);
|
|
31
22
|
|
|
32
|
-
if (this.player.paused)
|
|
33
|
-
this.player.pause(false);
|
|
34
|
-
}
|
|
23
|
+
if (this.player.paused) this.player.pause(false);
|
|
35
24
|
|
|
36
25
|
this.updatePlayerVoiceData();
|
|
37
26
|
}
|
|
38
27
|
|
|
39
|
-
/**
|
|
40
|
-
* Sets the state update data (session_id, channel_id, self_deaf, and self_mute) for the player.
|
|
41
|
-
* @param {object} data The state update data from the VOICE_STATE_UPDATE packet.
|
|
42
|
-
* @param {string} data.session_id The session id of the voice server.
|
|
43
|
-
* @param {string} data.channel_id The voice channel id of the player.
|
|
44
|
-
* @param {boolean} data.self_deaf The self-deafened status of the player.
|
|
45
|
-
* @param {boolean} data.self_mute The self-muted status of the player.
|
|
46
|
-
*/
|
|
47
28
|
setStateUpdate({ session_id, channel_id, self_deaf, self_mute }) {
|
|
48
|
-
if (channel_id
|
|
29
|
+
if (!channel_id || !session_id) {
|
|
49
30
|
this.player.aqua.emit("playerLeave", this.player.voiceChannel);
|
|
50
31
|
this.player.voiceChannel = null;
|
|
51
32
|
this.voiceChannel = null;
|
|
@@ -67,9 +48,6 @@ class Connection {
|
|
|
67
48
|
this.updatePlayerVoiceData();
|
|
68
49
|
}
|
|
69
50
|
|
|
70
|
-
/**
|
|
71
|
-
* Updates the player voice data.
|
|
72
|
-
*/
|
|
73
51
|
updatePlayerVoiceData() {
|
|
74
52
|
this.player.nodes.rest.updatePlayer({
|
|
75
53
|
guildId: this.player.guildId,
|
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
|
|
|
@@ -19,22 +18,18 @@ class Node {
|
|
|
19
18
|
this.secure = nodes.secure || false;
|
|
20
19
|
this.sessionId = nodes.sessionId || null;
|
|
21
20
|
this.rest = new Rest(aqua, this);
|
|
22
|
-
|
|
23
21
|
this.wsUrl = `ws${this.secure ? 's' : ''}://${this.host}:${this.port}/v4/websocket`;
|
|
24
|
-
this.restUrl = `http${this.secure ? 's' : ''}://${this.host}:${this.port}`;
|
|
25
|
-
|
|
26
22
|
this.ws = null;
|
|
27
23
|
this.regions = nodes.regions || [];
|
|
28
24
|
this.info = null;
|
|
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() {
|
|
@@ -61,22 +56,14 @@ class Node {
|
|
|
61
56
|
};
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
/**
|
|
65
|
-
* Fetches the lavalink node's information.
|
|
66
|
-
* @param {Object} [options] Options to pass to the rest request.
|
|
67
|
-
* @param {boolean} [options.includeHeaders=false] Include headers in the response.
|
|
68
|
-
* @returns {Promise<Object>} The lavalink node's information.
|
|
69
|
-
*/
|
|
70
59
|
async fetchInfo(options = {}) {
|
|
71
60
|
return await this.rest.makeRequest("GET", `/v4/info`, null, options.includeHeaders);
|
|
72
61
|
}
|
|
73
|
-
|
|
74
62
|
|
|
75
63
|
async connect() {
|
|
76
64
|
if (this.ws) this.ws.close();
|
|
77
65
|
this.aqua.emit('debug', this.name, `Attempting to connect...`);
|
|
78
|
-
|
|
79
|
-
this.ws = new WebSocket(this.wsUrl, { headers });
|
|
66
|
+
this.ws = new WebSocket(this.wsUrl, { headers: this.constructHeaders() });
|
|
80
67
|
this.setupWebSocketListeners();
|
|
81
68
|
}
|
|
82
69
|
|
|
@@ -100,12 +87,12 @@ class Node {
|
|
|
100
87
|
async onOpen() {
|
|
101
88
|
this.connected = true;
|
|
102
89
|
this.aqua.emit('debug', this.name, `Connected to Lavalink at ${this.wsUrl}`);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
90
|
+
try {
|
|
91
|
+
this.info = await this.fetchInfo();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
this.aqua.emit('debug', `Failed to fetch info: ${err.message}`);
|
|
94
|
+
this.info = null;
|
|
95
|
+
}
|
|
109
96
|
|
|
110
97
|
if (!this.info && !this.aqua.bypassChecks.nodeFetchInfo) {
|
|
111
98
|
throw new Error(`Failed to fetch node info.`);
|
|
@@ -114,30 +101,27 @@ class Node {
|
|
|
114
101
|
if (this.autoResume) {
|
|
115
102
|
this.resumePlayers();
|
|
116
103
|
}
|
|
117
|
-
|
|
118
104
|
this.lastStats = 0;
|
|
119
105
|
}
|
|
120
106
|
|
|
121
107
|
async getStats() {
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
if (now - this.lastStatsRequest < 5000) {
|
|
110
|
+
return this.stats; // Return cached stats if requested too soon
|
|
124
111
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
.
|
|
129
|
-
this.aqua.emit('debug', `Error fetching stats: ${err.message}`);
|
|
130
|
-
return null;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
if (stats) {
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const response = await this.rest.makeRequest("GET", `/v4/stats`);
|
|
115
|
+
const stats = await response.json();
|
|
134
116
|
this.stats = { ...this.stats, ...stats };
|
|
135
|
-
this.
|
|
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
|
|
136
122
|
}
|
|
137
|
-
return stats;
|
|
138
123
|
}
|
|
139
124
|
|
|
140
|
-
|
|
141
125
|
resumePlayers() {
|
|
142
126
|
for (const player of this.aqua.players.values()) {
|
|
143
127
|
if (player.node === this) {
|
|
@@ -153,13 +137,10 @@ class Node {
|
|
|
153
137
|
onMessage(msg) {
|
|
154
138
|
if (Array.isArray(msg)) msg = Buffer.concat(msg);
|
|
155
139
|
if (msg instanceof ArrayBuffer) msg = Buffer.from(msg);
|
|
156
|
-
|
|
157
140
|
const payload = JSON.parse(msg.toString());
|
|
158
141
|
if (!payload.op) return;
|
|
159
|
-
|
|
160
142
|
this.aqua.emit("raw", "Node", payload);
|
|
161
143
|
this.aqua.emit("debug", this.name, `Received update: ${JSON.stringify(payload)}`);
|
|
162
|
-
|
|
163
144
|
this.handlePayload(payload);
|
|
164
145
|
}
|
|
165
146
|
|
|
@@ -175,9 +156,7 @@ class Node {
|
|
|
175
156
|
break;
|
|
176
157
|
default:
|
|
177
158
|
const player = this.aqua.players.get(payload.guildId);
|
|
178
|
-
if (payload.
|
|
179
|
-
player.emit(payload.op, payload);
|
|
180
|
-
}
|
|
159
|
+
if (player) player.emit(payload.op, payload);
|
|
181
160
|
break;
|
|
182
161
|
}
|
|
183
162
|
}
|
|
@@ -196,8 +175,7 @@ class Node {
|
|
|
196
175
|
}
|
|
197
176
|
|
|
198
177
|
reconnect() {
|
|
199
|
-
this.reconnectAttempted
|
|
200
|
-
if (this.reconnectAttempted > this.reconnectTries) {
|
|
178
|
+
if (this.reconnectAttempted++ >= this.reconnectTries) {
|
|
201
179
|
this.aqua.emit("nodeError", this, new Error(`Unable to connect after ${this.reconnectTries} attempts.`));
|
|
202
180
|
return this.destroy();
|
|
203
181
|
}
|
|
@@ -216,17 +194,13 @@ class Node {
|
|
|
216
194
|
this.aqua.nodes.delete(this.name);
|
|
217
195
|
return;
|
|
218
196
|
}
|
|
219
|
-
|
|
220
197
|
if (!this.connected) return;
|
|
221
|
-
|
|
222
198
|
this.aqua.players.forEach((player) => {
|
|
223
199
|
if (player.node === this) player.destroy();
|
|
224
200
|
});
|
|
225
|
-
|
|
226
|
-
if (this.ws) this.ws.close(1000, "destroy");
|
|
201
|
+
this.ws?.close(1000, "destroy");
|
|
227
202
|
this.ws?.removeAllListeners();
|
|
228
203
|
this.ws = null;
|
|
229
|
-
|
|
230
204
|
this.aqua.emit("nodeDestroy", this);
|
|
231
205
|
this.aqua.nodeMap.delete(this.name);
|
|
232
206
|
this.connected = false;
|
|
@@ -255,4 +229,4 @@ class Node {
|
|
|
255
229
|
}
|
|
256
230
|
}
|
|
257
231
|
|
|
258
|
-
module.exports = { Node };
|
|
232
|
+
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;
|
|
@@ -33,13 +33,16 @@ class Player extends EventEmitter {
|
|
|
33
33
|
this.queue = new Queue();
|
|
34
34
|
this.position = 0;
|
|
35
35
|
this.current = null;
|
|
36
|
-
this.previousTracks = [];
|
|
37
36
|
this.playing = false;
|
|
38
37
|
this.paused = false;
|
|
39
38
|
this.connected = false;
|
|
40
39
|
this.timestamp = 0;
|
|
41
40
|
this.ping = 0;
|
|
42
41
|
this.isAutoplay = false;
|
|
42
|
+
this.nowPlayingMessage = null;
|
|
43
|
+
this.previousTracks = new Array();
|
|
44
|
+
|
|
45
|
+
this.shouldDeleteMessage = options.shouldDeleteMessage ?? true;
|
|
43
46
|
|
|
44
47
|
this.setupEventListeners();
|
|
45
48
|
}
|
|
@@ -62,9 +65,10 @@ class Player extends EventEmitter {
|
|
|
62
65
|
this.position = state.position;
|
|
63
66
|
this.ping = state.ping;
|
|
64
67
|
this.timestamp = state.time;
|
|
65
|
-
|
|
68
|
+
this.aqua.emit("playerUpdate", this, packet);
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
|
|
68
72
|
/**
|
|
69
73
|
* Gets the previous track.
|
|
70
74
|
* @returns {Object|null} The previous track or null if none exists.
|
|
@@ -83,64 +87,68 @@ class Player extends EventEmitter {
|
|
|
83
87
|
|
|
84
88
|
/**
|
|
85
89
|
* Plays the next track in the queue.
|
|
90
|
+
* @param {Object} options - Options for playing the next track.
|
|
91
|
+
* @param {string} options.query - The query to search for the next track.
|
|
92
|
+
* @param {boolean} options.force - Whether to force play the next track even if the queue is empty.
|
|
86
93
|
* @returns {Promise<Player>} The player instance.
|
|
87
94
|
* @throws {Error} If the player is not connected.
|
|
95
|
+
* @throws {Error} If the queue is empty and force is not set to true.
|
|
96
|
+
* @description This method plays the next track in the queue.
|
|
97
|
+
* @event play
|
|
88
98
|
*/
|
|
89
99
|
async play() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!this.current) return this;
|
|
100
|
+
if (!this.connected) throw new Error("Bro go on and use the connection first");
|
|
101
|
+
if (!this.queue.length) return;
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
this.current = this.queue.shift();
|
|
104
|
+
this.current = this.current.track ? this.current : await this.current.resolve(this.aqua);
|
|
105
|
+
this.playing = true;
|
|
106
|
+
this.position = 0;
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
this.aqua.emit("debug", this.guildId, `Playing track: ${this.current.track}`);
|
|
109
|
+
await this.updatePlayer({ track: { encoded: this.current.track } });
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
100
112
|
|
|
101
|
-
this.aqua.emit("debug", this.guildId, `Playing track: ${this.current.track}`);
|
|
102
|
-
await this.updatePlayer({ track: { encoded: this.current.track } });
|
|
103
|
-
return this;
|
|
104
|
-
}
|
|
105
113
|
/**
|
|
106
|
-
* Connects the player to
|
|
107
|
-
* @param {Object}
|
|
108
|
-
* @param {string} options.guildId - The ID
|
|
109
|
-
* @param {string} options.voiceChannel - The ID of the voice channel.
|
|
110
|
-
* @param {boolean} [options.deaf=true] - Whether
|
|
111
|
-
* @param {boolean} [options.mute=false] - Whether
|
|
114
|
+
* Connects the player to a voice channel.
|
|
115
|
+
* @param {Object} options - Options for connecting the player.
|
|
116
|
+
* @param {string} options.guildId - The guild ID to connect to.
|
|
117
|
+
* @param {string} options.voiceChannel - The ID of the voice channel to connect to.
|
|
118
|
+
* @param {boolean} [options.deaf=true] - Whether the player should be deafened.
|
|
119
|
+
* @param {boolean} [options.mute=false] - Whether the player should be muted.
|
|
112
120
|
* @returns {Promise<Player>} The player instance.
|
|
121
|
+
* @throws {Error} If the player is already connected.
|
|
122
|
+
* @description This method connects the player to a voice channel.
|
|
123
|
+
* @event ready
|
|
113
124
|
*/
|
|
114
125
|
async connect(options = this) {
|
|
115
126
|
const { guildId, voiceChannel, deaf = true, mute = false } = options;
|
|
116
|
-
await this.send({
|
|
117
|
-
guild_id: guildId,
|
|
118
|
-
channel_id: voiceChannel,
|
|
119
|
-
self_deaf: deaf,
|
|
120
|
-
self_mute: mute,
|
|
121
|
-
});
|
|
127
|
+
await this.send({ guild_id: guildId, channel_id: voiceChannel, self_deaf: deaf, self_mute: mute });
|
|
122
128
|
this.connected = true;
|
|
123
129
|
this.aqua.emit("debug", this.guildId, `Player has connected to voice channel: ${voiceChannel}.`);
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
/**
|
|
127
|
-
*
|
|
128
|
-
* @returns {Promise<
|
|
133
|
+
* Destroys the player instance.
|
|
134
|
+
* @returns {Promise<void>} The result of the destroy method.
|
|
135
|
+
* @description This method destroys the player instance and clears all data.
|
|
136
|
+
* @event destroy
|
|
129
137
|
*/
|
|
130
138
|
async destroy() {
|
|
131
|
-
await this.updatePlayer({ track: { encoded: null }
|
|
139
|
+
await this.updatePlayer({ track: { encoded: null } });
|
|
132
140
|
this.connected = false;
|
|
133
|
-
await this.send({
|
|
134
|
-
|
|
135
|
-
channel_id: null,
|
|
136
|
-
});
|
|
137
|
-
this.clearData(); // Clear data when destroyed
|
|
141
|
+
await this.send({ guild_id: this.guildId, channel_id: null });
|
|
142
|
+
this.clearData();
|
|
138
143
|
this.aqua.emit("debug", this.guildId, "Player has disconnected from voice channel.");
|
|
139
144
|
}
|
|
145
|
+
|
|
140
146
|
/**
|
|
141
|
-
* Pauses
|
|
142
|
-
* @param {boolean} paused - Whether
|
|
147
|
+
* Pauses the player.
|
|
148
|
+
* @param {boolean} paused - Whether the player should be paused.
|
|
143
149
|
* @returns {Promise<Player>} The player instance.
|
|
150
|
+
* @description This method pauses the player.
|
|
151
|
+
* @event pause
|
|
144
152
|
*/
|
|
145
153
|
async pause(paused) {
|
|
146
154
|
this.paused = paused;
|
|
@@ -149,9 +157,11 @@ class Player extends EventEmitter {
|
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
/**
|
|
152
|
-
* Seeks to a specific position
|
|
153
|
-
* @param {number} position - The position
|
|
160
|
+
* Seeks the player to a specific position.
|
|
161
|
+
* @param {number} position - The position to seek to in milliseconds.
|
|
154
162
|
* @returns {Promise<Player>} The player instance.
|
|
163
|
+
* @description This method seeks the player to a specific position.
|
|
164
|
+
* @event seek
|
|
155
165
|
*/
|
|
156
166
|
async seek(position) {
|
|
157
167
|
this.position = position;
|
|
@@ -160,26 +170,27 @@ class Player extends EventEmitter {
|
|
|
160
170
|
}
|
|
161
171
|
|
|
162
172
|
/**
|
|
163
|
-
* Stops
|
|
173
|
+
* Stops the player.
|
|
164
174
|
* @returns {Promise<Player>} The player instance.
|
|
175
|
+
* @description This method stops the player.
|
|
176
|
+
* @event stop
|
|
165
177
|
*/
|
|
166
178
|
async stop() {
|
|
167
|
-
if (!this.playing) return this;
|
|
179
|
+
if (!this.playing) return this;
|
|
168
180
|
this.playing = false;
|
|
169
181
|
this.current = null;
|
|
170
182
|
this.position = 0;
|
|
171
|
-
await this.updatePlayer({ track:
|
|
172
|
-
{
|
|
173
|
-
encoded: null,
|
|
174
|
-
}
|
|
175
|
-
});
|
|
183
|
+
await this.updatePlayer({ track: { encoded: null } });
|
|
176
184
|
return this;
|
|
177
185
|
}
|
|
186
|
+
|
|
178
187
|
/**
|
|
179
188
|
* Sets the volume of the player.
|
|
180
|
-
* @param {number} volume - The volume
|
|
189
|
+
* @param {number} volume - The volume to set between 0 and 200.
|
|
181
190
|
* @returns {Promise<Player>} The player instance.
|
|
182
|
-
* @throws {Error} If the volume is
|
|
191
|
+
* @throws {Error} If the volume is not between 0 and 200.
|
|
192
|
+
* @description This method sets the volume of the player.
|
|
193
|
+
* @event volumeChange
|
|
183
194
|
*/
|
|
184
195
|
async setVolume(volume) {
|
|
185
196
|
if (volume < 0 || volume > 200) throw new Error("[Volume] Volume must be between 0 and 200.");
|
|
@@ -189,10 +200,12 @@ class Player extends EventEmitter {
|
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
/**
|
|
192
|
-
* Sets the loop mode
|
|
193
|
-
* @param {string} mode - The loop mode
|
|
203
|
+
* Sets the loop mode of the player.
|
|
204
|
+
* @param {string} mode - The loop mode to set, either 'none', 'track', or 'queue'.
|
|
194
205
|
* @returns {Promise<Player>} The player instance.
|
|
195
|
-
* @throws {Error} If the loop mode is
|
|
206
|
+
* @throws {Error} If the loop mode is not 'none', 'track', or 'queue'.
|
|
207
|
+
* @description This method sets the loop mode of the player.
|
|
208
|
+
* @event loopChange
|
|
196
209
|
*/
|
|
197
210
|
async setLoop(mode) {
|
|
198
211
|
if (!["none", "track", "queue"].includes(mode)) throw new Error("Loop mode must be 'none', 'track', or 'queue'.");
|
|
@@ -202,9 +215,11 @@ class Player extends EventEmitter {
|
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
/**
|
|
205
|
-
* Sends
|
|
206
|
-
* @param {Object} data - The data to send.
|
|
218
|
+
* Sends an update to the player.
|
|
219
|
+
* @param {Object} data - The data to send to the player.
|
|
207
220
|
* @returns {Promise<Player>} The player instance.
|
|
221
|
+
* @description This method sends an update to the player.
|
|
222
|
+
* @event update
|
|
208
223
|
*/
|
|
209
224
|
async send(data) {
|
|
210
225
|
await this.updatePlayer(data);
|
|
@@ -212,9 +227,11 @@ class Player extends EventEmitter {
|
|
|
212
227
|
}
|
|
213
228
|
|
|
214
229
|
/**
|
|
215
|
-
* Sets the text channel
|
|
216
|
-
* @param {string} channel - The ID of the text channel.
|
|
230
|
+
* Sets the text channel of the player.
|
|
231
|
+
* @param {string} channel - The ID of the text channel to set.
|
|
217
232
|
* @returns {Promise<Player>} The player instance.
|
|
233
|
+
* @description This method sets the text channel of the player.
|
|
234
|
+
* @event textChannelChange
|
|
218
235
|
*/
|
|
219
236
|
async setTextChannel(channel) {
|
|
220
237
|
await this.updatePlayer({ text_channel: channel });
|
|
@@ -222,11 +239,11 @@ class Player extends EventEmitter {
|
|
|
222
239
|
}
|
|
223
240
|
|
|
224
241
|
/**
|
|
225
|
-
* Sets the voice channel
|
|
226
|
-
* @param {string} channel - The ID of the voice channel.
|
|
242
|
+
* Sets the voice channel of the player.
|
|
243
|
+
* @param {string} channel - The ID of the voice channel to set.
|
|
227
244
|
* @returns {Promise<Player>} The player instance.
|
|
228
|
-
* @
|
|
229
|
-
* @
|
|
245
|
+
* @description This method sets the voice channel of the player.
|
|
246
|
+
* @event voiceChannelChange
|
|
230
247
|
*/
|
|
231
248
|
async setVoiceChannel(channel) {
|
|
232
249
|
if (typeof channel !== "string") throw new TypeError("Channel must be a non-empty string.");
|
|
@@ -240,48 +257,77 @@ class Player extends EventEmitter {
|
|
|
240
257
|
|
|
241
258
|
/**
|
|
242
259
|
* Disconnects the player from the voice channel.
|
|
243
|
-
* @returns {Promise<
|
|
260
|
+
* @returns {Promise<void>} The result of the disconnect method.
|
|
261
|
+
* @description This method disconnects the player from the voice channel.
|
|
262
|
+
* @event disconnect
|
|
244
263
|
*/
|
|
245
264
|
async disconnect() {
|
|
246
|
-
await this.updatePlayer({ track: { encoded: null }});
|
|
265
|
+
await this.updatePlayer({ track: { encoded: null } });
|
|
247
266
|
await this.send({ guild_id: this.guildId, channel_id: null });
|
|
248
267
|
this.connected = false;
|
|
249
268
|
this.aqua.emit("debug", this.guildId, "Player has disconnected from voice channel.");
|
|
250
269
|
}
|
|
251
270
|
|
|
252
271
|
/**
|
|
253
|
-
*
|
|
254
|
-
* @
|
|
272
|
+
* Shuffles the queue of the player.
|
|
273
|
+
* @returns {Promise<Player>} The player instance.
|
|
274
|
+
* @description This method shuffles the queue of the player.
|
|
275
|
+
* @event shuffle
|
|
255
276
|
*/
|
|
256
|
-
async
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
277
|
+
async shuffle() {
|
|
278
|
+
this.queue.shuffle();
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Gets the queue of the player.
|
|
284
|
+
* @returns {Array<Object>} The queue of the player.
|
|
285
|
+
* @description This method gets the queue of the player.
|
|
286
|
+
* @event getQueue
|
|
287
|
+
*/
|
|
288
|
+
async getQueue() {
|
|
289
|
+
return this.queue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Replays the current track from the start.
|
|
294
|
+
* @returns {Promise<Player>} The player instance.
|
|
295
|
+
* @description This method replays the current track from the start.
|
|
296
|
+
* @event replay
|
|
297
|
+
*/
|
|
298
|
+
async replay() {
|
|
299
|
+
return this.seek(0);
|
|
300
|
+
}
|
|
301
|
+
async handleEvent(payload) {
|
|
302
|
+
const player = this.aqua.players.get(payload.guildId);
|
|
303
|
+
if (!player) return;
|
|
304
|
+
|
|
305
|
+
const track = this.current;
|
|
306
|
+
|
|
307
|
+
switch (payload.type) {
|
|
308
|
+
case "TrackStartEvent":
|
|
309
|
+
this.trackStart(player, track, payload);
|
|
310
|
+
break;
|
|
311
|
+
case "TrackEndEvent":
|
|
312
|
+
this.trackEnd(player, track, payload);
|
|
313
|
+
break;
|
|
314
|
+
case "TrackExceptionEvent":
|
|
315
|
+
this.trackError(player, track, payload);
|
|
316
|
+
break;
|
|
317
|
+
case "TrackStuckEvent":
|
|
318
|
+
this.trackStuck(player, track, payload);
|
|
319
|
+
break;
|
|
320
|
+
case "TrackChangeEvent":
|
|
321
|
+
this.trackChange(player, track, payload);
|
|
322
|
+
break;
|
|
323
|
+
case "WebSocketClosedEvent":
|
|
324
|
+
this.socketClosed(player, track, payload);
|
|
325
|
+
break;
|
|
326
|
+
default:
|
|
327
|
+
this.handleUnknownEvent(payload);
|
|
328
|
+
break;
|
|
284
329
|
}
|
|
330
|
+
}
|
|
285
331
|
|
|
286
332
|
/**
|
|
287
333
|
* Handles track start events.
|
|
@@ -297,7 +343,7 @@ class Player extends EventEmitter {
|
|
|
297
343
|
|
|
298
344
|
trackChange(player, track, payload) {
|
|
299
345
|
this.playing = true;
|
|
300
|
-
this.paused =
|
|
346
|
+
this.paused = false
|
|
301
347
|
this.aqua.emit("trackChange", player, track, payload);
|
|
302
348
|
}
|
|
303
349
|
|
|
@@ -307,31 +353,27 @@ class Player extends EventEmitter {
|
|
|
307
353
|
* @param {Object} payload - The event payload.
|
|
308
354
|
*/
|
|
309
355
|
trackEnd(player, track, payload) {
|
|
310
|
-
|
|
356
|
+
|
|
357
|
+
if (this.shouldDeleteMessage && this.nowPlayingMessage) {
|
|
358
|
+
this.nowPlayingMessage.delete();
|
|
359
|
+
this.nowPlayingMessage = null;
|
|
360
|
+
|
|
361
|
+
}
|
|
311
362
|
if (["loadfailed", "cleanup"].includes(payload.reason.replace("_", "").toLowerCase())) {
|
|
312
|
-
|
|
313
|
-
this.playing = false;
|
|
314
|
-
return this.aqua.emit("queueEnd", player);
|
|
315
|
-
}
|
|
316
|
-
this.aqua.emit("trackEnd", player, payload);
|
|
317
|
-
return player.play();
|
|
363
|
+
return player.queue.length === 0 ? this.aqua.emit("queueEnd", player) : player.play();
|
|
318
364
|
}
|
|
365
|
+
this.addToPreviousTrack(track)
|
|
319
366
|
if (this.loop === "track") {
|
|
320
|
-
player.queue.
|
|
321
|
-
this.aqua.emit("trackEnd", player, payload);
|
|
322
|
-
return player.play();
|
|
323
|
-
} else if (this.loop === "queue") {
|
|
324
|
-
player.queue.push(player.previous);
|
|
325
|
-
this.aqua.emit("trackEnd", player, payload);
|
|
367
|
+
player.queue.push(this.previous);
|
|
326
368
|
return player.play();
|
|
327
369
|
}
|
|
328
370
|
if (player.queue.length === 0) {
|
|
329
371
|
this.playing = false;
|
|
330
372
|
return this.aqua.emit("queueEnd", player);
|
|
331
|
-
} else {
|
|
332
|
-
this.aqua.emit("trackEnd", player, payload);
|
|
333
|
-
return player.play();
|
|
334
373
|
}
|
|
374
|
+
this.cleanup();
|
|
375
|
+
this.clearData();
|
|
376
|
+
return player.play();
|
|
335
377
|
}
|
|
336
378
|
|
|
337
379
|
/**
|
|
@@ -403,7 +445,7 @@ class Player extends EventEmitter {
|
|
|
403
445
|
* @returns {Player} The player instance.
|
|
404
446
|
*/
|
|
405
447
|
clearData() {
|
|
406
|
-
this.data = {}
|
|
448
|
+
this.data = {};
|
|
407
449
|
return this;
|
|
408
450
|
}
|
|
409
451
|
|
|
@@ -439,4 +481,5 @@ class Player extends EventEmitter {
|
|
|
439
481
|
}
|
|
440
482
|
}
|
|
441
483
|
|
|
442
|
-
module.exports = { Player };
|
|
484
|
+
module.exports = { Player };
|
|
485
|
+
|
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,6 @@ 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;
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
setSessionId(sessionId) {
|
|
@@ -24,24 +19,22 @@ class Rest {
|
|
|
24
19
|
"Content-Type": "application/json",
|
|
25
20
|
Authorization: this.password,
|
|
26
21
|
};
|
|
27
|
-
|
|
22
|
+
|
|
23
|
+
const response = await undiciFetch(`${this.url}${endpoint}`, {
|
|
28
24
|
method,
|
|
29
25
|
headers,
|
|
30
|
-
body: body
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
return includeHeaders ? { data, headers: response.headers } : data;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
throw new Error(`Network error during request: ${method} ${this.url}${endpoint}`, { cause: error });
|
|
26
|
+
body: body && JSON.stringify(body),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
this.calls++;
|
|
30
|
+
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
this.aqua.emit("apiResponse", endpoint, response);
|
|
33
|
+
|
|
34
|
+
if (includeHeaders) {
|
|
35
|
+
return { data, headers: response.headers };
|
|
44
36
|
}
|
|
37
|
+
return data;
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
async getPlayers() {
|
|
@@ -50,15 +43,18 @@ class Rest {
|
|
|
50
43
|
|
|
51
44
|
async updatePlayer(options) {
|
|
52
45
|
const requestBody = { ...options.data };
|
|
46
|
+
|
|
53
47
|
if ((requestBody.track && requestBody.track.encoded && requestBody.track.identifier) ||
|
|
54
48
|
(requestBody.encodedTrack && requestBody.identifier)) {
|
|
55
49
|
throw new Error(`Cannot provide both 'encoded' and 'identifier' for track in Update Player Endpoint`);
|
|
56
50
|
}
|
|
51
|
+
|
|
57
52
|
if (this.version === "v3" && options.data?.track) {
|
|
58
53
|
const { track } = requestBody;
|
|
59
54
|
delete requestBody.track;
|
|
60
55
|
Object.assign(requestBody, track.encoded ? { encodedTrack: track.encoded } : { identifier: track.identifier });
|
|
61
56
|
}
|
|
57
|
+
|
|
62
58
|
return this.makeRequest("PATCH", `/${this.version}/sessions/${this.sessionId}/players/${options.guildId}?noReplace=false`, requestBody);
|
|
63
59
|
}
|
|
64
60
|
|
|
@@ -70,7 +66,7 @@ class Rest {
|
|
|
70
66
|
return this.makeRequest("GET", `/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`);
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
async decodeTrack(track
|
|
69
|
+
async decodeTrack(track) {
|
|
74
70
|
return this.makeRequest("GET", `/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`);
|
|
75
71
|
}
|
|
76
72
|
|
|
@@ -79,10 +75,7 @@ class Rest {
|
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
async getStats() {
|
|
82
|
-
|
|
83
|
-
return this.makeRequest("GET", `/${this.version}/stats`);
|
|
84
|
-
}
|
|
85
|
-
return this.makeRequest("GET", `/${this.version}/stats/all`);
|
|
78
|
+
return this.makeRequest("GET", this.version === "v3" ? `/${this.version}/stats` : `/${this.version}/stats/all`);
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
async getInfo() {
|
|
@@ -96,25 +89,7 @@ class Rest {
|
|
|
96
89
|
async getRoutePlannerAddress(address) {
|
|
97
90
|
return this.makeRequest("POST", `/${this.version}/routeplanner/free/address`, { address });
|
|
98
91
|
}
|
|
99
|
-
|
|
100
|
-
async parseResponse(response) {
|
|
101
|
-
if (response.status === 204) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
const contentType = response.headers.get("Content-Type");
|
|
106
|
-
return await response[contentType.includes("text/plain") ? "text" : "json"]();
|
|
107
|
-
} catch (error) {
|
|
108
|
-
this.aqua.emit("debug", `[Rest - Error] Failed to process response from ${response.url}: ${error}`);
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Cleans up resources related to the queue.
|
|
114
|
-
*/
|
|
115
|
-
cleanupQueue() {
|
|
116
|
-
this.queue = [];
|
|
117
|
-
}
|
|
118
92
|
}
|
|
119
93
|
|
|
120
|
-
module.exports = { Rest };
|
|
94
|
+
module.exports = { Rest };
|
|
95
|
+
|
|
@@ -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,14 +63,18 @@ 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
|
|
|
75
79
|
module.exports = { Track };
|
|
80
|
+
|