aqualink 2.6.2 → 2.6.3
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/build/structures/Aqua.js +46 -38
- package/build/structures/Connection.js +18 -22
- package/build/structures/Player.js +158 -99
- package/build/structures/Rest.js +45 -17
- package/package.json +1 -1
package/build/structures/Aqua.js
CHANGED
|
@@ -349,26 +349,19 @@ class Aqua extends EventEmitter {
|
|
|
349
349
|
|
|
350
350
|
async savePlayer(filePath = "./AquaPlayers.json") {
|
|
351
351
|
const data = Array.from(this.players.values()).map(player => ({
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
duration: player.current.duration,
|
|
363
|
-
position: player.position,
|
|
364
|
-
} : null,
|
|
365
|
-
requester: player.requester || player.current?.requester,
|
|
366
|
-
volume: player.volume,
|
|
367
|
-
paused: player.paused
|
|
352
|
+
g: player.guildId,
|
|
353
|
+
t: player.textChannel,
|
|
354
|
+
v: player.voiceChannel,
|
|
355
|
+
u: player.current?.uri || null,
|
|
356
|
+
p: player.position || 0,
|
|
357
|
+
ts: player.timestamp || 0,
|
|
358
|
+
q: player.queue?.tracks?.map(tr => tr.uri).slice(0, 5) || [],
|
|
359
|
+
r: player.requester || player.current?.requester,
|
|
360
|
+
vol: player.volume,
|
|
361
|
+
pa: player.paused
|
|
368
362
|
}));
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
this.emit("debug", "Aqua", `Saved players to ${filePath}`);
|
|
363
|
+
await fs.writeFile(filePath, JSON.stringify(data), "utf8");
|
|
364
|
+
this.emit("debug", "Aqua", `Saved ${data.length} players to ${filePath}`);
|
|
372
365
|
}
|
|
373
366
|
|
|
374
367
|
async waitForFirstNode() {
|
|
@@ -386,45 +379,60 @@ class Aqua extends EventEmitter {
|
|
|
386
379
|
}
|
|
387
380
|
|
|
388
381
|
async loadPlayers(filePath = "./AquaPlayers.json") {
|
|
389
|
-
|
|
390
|
-
|
|
382
|
+
try {
|
|
383
|
+
await fs.promises.access(filePath);
|
|
384
|
+
} catch {
|
|
385
|
+
this.emit("debug", "Aqua", `File ${filePath} does not exist, skipping load.`);
|
|
391
386
|
return;
|
|
392
387
|
}
|
|
393
388
|
try {
|
|
394
389
|
await this.waitForFirstNode();
|
|
395
|
-
const data = await fs.
|
|
396
|
-
for (const
|
|
397
|
-
|
|
398
|
-
let player = this.players.get(guildId);
|
|
399
|
-
|
|
390
|
+
const data = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
391
|
+
for (const p of data) {
|
|
392
|
+
let player = this.players.get(p.g);
|
|
400
393
|
if (!player) {
|
|
401
394
|
player = await this.createConnection({
|
|
402
|
-
guildId:
|
|
403
|
-
textChannel:
|
|
404
|
-
voiceChannel:
|
|
405
|
-
defaultVolume:
|
|
395
|
+
guildId: p.g,
|
|
396
|
+
textChannel: p.t,
|
|
397
|
+
voiceChannel: p.v,
|
|
398
|
+
defaultVolume: p.vol || 65,
|
|
406
399
|
deaf: true
|
|
407
400
|
});
|
|
408
401
|
}
|
|
409
402
|
|
|
410
|
-
if (
|
|
411
|
-
const resolved = await this.resolve({ query:
|
|
403
|
+
if (p.u && player) {
|
|
404
|
+
const resolved = await this.resolve({ query: p.u, requester: p.r });
|
|
412
405
|
if (resolved.tracks && resolved.tracks.length > 0) {
|
|
413
406
|
player.queue.add(resolved.tracks[0]);
|
|
414
|
-
player.position =
|
|
415
|
-
|
|
416
|
-
|
|
407
|
+
player.position = p.p || 0;
|
|
408
|
+
if (typeof p.ts === "number") {
|
|
409
|
+
player.timestamp = p.ts;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (Array.isArray(p.q) && player) {
|
|
414
|
+
for (const uri of p.q) {
|
|
415
|
+
if (!p.u || uri !== p.u) {
|
|
416
|
+
const resolved = await this.resolve({ query: uri, requester: p.r });
|
|
417
|
+
if (resolved.tracks && resolved.tracks.length > 0) {
|
|
418
|
+
player.queue.add(resolved.tracks[0]);
|
|
419
|
+
player.position = p.p || 0;
|
|
420
|
+
if (typeof p.ts === "number") {
|
|
421
|
+
player.timestamp = p.ts;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
417
425
|
}
|
|
418
426
|
}
|
|
419
|
-
|
|
420
427
|
if (player) {
|
|
421
|
-
player.paused =
|
|
428
|
+
player.paused = !!p.pa;
|
|
422
429
|
if (!player.playing && !player.paused && player.queue.size > 0) {
|
|
423
430
|
player.play();
|
|
424
431
|
}
|
|
425
432
|
}
|
|
426
433
|
}
|
|
427
|
-
|
|
434
|
+
await fs.writeFile(filePath, "[]", "utf8");
|
|
435
|
+
this.emit("debug", "Aqua", `Loaded players from ${filePath} and cleared its content.`);
|
|
428
436
|
} catch (error) {
|
|
429
437
|
console.error(`Failed to load players from ${filePath}:`, error);
|
|
430
438
|
}
|
|
@@ -14,29 +14,27 @@ class Connection {
|
|
|
14
14
|
this.region = null;
|
|
15
15
|
this.selfDeaf = false;
|
|
16
16
|
this.selfMute = false;
|
|
17
|
-
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
setServerUpdate(data) {
|
|
21
|
-
if (!data
|
|
20
|
+
if (!data?.endpoint) return;
|
|
22
21
|
|
|
23
22
|
const { endpoint, token } = data;
|
|
24
|
-
|
|
25
|
-
const [newRegion] = endpoint.split('.');
|
|
23
|
+
const newRegion = endpoint.split('.')[0];
|
|
26
24
|
if (!newRegion) return;
|
|
27
25
|
|
|
28
26
|
if (this.region !== newRegion) {
|
|
27
|
+
const oldRegion = this.region;
|
|
29
28
|
this.endpoint = endpoint;
|
|
30
29
|
this.token = token;
|
|
31
30
|
this.region = newRegion;
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
this.aqua.emit(
|
|
33
|
+
"debug",
|
|
34
|
+
`[Player ${this.guildId} - CONNECTION] Voice Server: ${
|
|
35
|
+
oldRegion ? `Changed from ${oldRegion} to ${newRegion}` : newRegion
|
|
36
|
+
}`
|
|
37
|
+
);
|
|
40
38
|
|
|
41
39
|
this._updatePlayerVoiceData();
|
|
42
40
|
}
|
|
@@ -49,7 +47,6 @@ class Connection {
|
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
const { channel_id, session_id, self_deaf, self_mute } = data;
|
|
52
|
-
|
|
53
50
|
if (!channel_id || !session_id) {
|
|
54
51
|
this.player?.destroy();
|
|
55
52
|
return;
|
|
@@ -60,8 +57,8 @@ class Connection {
|
|
|
60
57
|
this.voiceChannel = channel_id;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
this.selfDeaf =
|
|
64
|
-
this.selfMute =
|
|
60
|
+
this.selfDeaf = Boolean(self_deaf);
|
|
61
|
+
this.selfMute = Boolean(self_mute);
|
|
65
62
|
this.sessionId = session_id;
|
|
66
63
|
}
|
|
67
64
|
|
|
@@ -73,6 +70,7 @@ class Connection {
|
|
|
73
70
|
endpoint: this.endpoint,
|
|
74
71
|
token: this.token
|
|
75
72
|
};
|
|
73
|
+
|
|
76
74
|
try {
|
|
77
75
|
this.nodes.rest.updatePlayer({
|
|
78
76
|
guildId: this.guildId,
|
|
@@ -82,15 +80,13 @@ class Connection {
|
|
|
82
80
|
}
|
|
83
81
|
});
|
|
84
82
|
} catch (error) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
}
|
|
83
|
+
this.aqua.emit("apiError", "updatePlayer", {
|
|
84
|
+
error,
|
|
85
|
+
guildId: this.guildId,
|
|
86
|
+
voiceData
|
|
87
|
+
});
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
module.exports = Connection;
|
|
92
|
+
module.exports = Connection;
|
|
@@ -6,21 +6,75 @@ const Queue = require("./Queue");
|
|
|
6
6
|
const Filters = require("./Filters");
|
|
7
7
|
const { spAutoPlay, scAutoPlay } = require('../handlers/autoplay');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const LOOP_MODES = Object.freeze({
|
|
10
|
+
NONE: "none", TRACK: "track", QUEUE: "queue"
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const EVENT_HANDLERS = Object.freeze({
|
|
14
|
+
TrackStartEvent: "trackStart",
|
|
15
|
+
TrackEndEvent: "trackEnd",
|
|
16
|
+
TrackExceptionEvent: "trackError",
|
|
17
|
+
TrackStuckEvent: "trackStuck",
|
|
18
|
+
TrackChangeEvent: "trackChange",
|
|
19
|
+
WebSocketClosedEvent: "socketClosed",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const VALID_MODES = new Set(Object.values(LOOP_MODES));
|
|
23
|
+
const FAILURE_REASONS = new Set(["LOAD_FAILED", "CLEANUP"]);
|
|
24
|
+
const RECONNECT_CODES = new Set([4015, 4009]);
|
|
25
|
+
const FAIL_LOAD_TYPES = new Set(["error", "empty", "LOAD_FAILED", "NO_MATCHES"]);
|
|
26
|
+
|
|
27
|
+
class UpdateBatcher {
|
|
28
|
+
constructor(player) {
|
|
29
|
+
this.player = player;
|
|
30
|
+
this.pendingUpdates = {};
|
|
31
|
+
this.timeout = null;
|
|
32
|
+
this.hasUpdates = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
batch(data, immediate = false) {
|
|
36
|
+
Object.assign(this.pendingUpdates, data);
|
|
37
|
+
this.hasUpdates = true;
|
|
38
|
+
|
|
39
|
+
if (this.timeout) {
|
|
40
|
+
clearTimeout(this.timeout);
|
|
41
|
+
this.timeout = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (immediate || data.track) {
|
|
45
|
+
const updates = this.pendingUpdates;
|
|
46
|
+
this.pendingUpdates = {};
|
|
47
|
+
this.hasUpdates = false;
|
|
48
|
+
return this.player.updatePlayer(updates);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.timeout = setTimeout(() => {
|
|
52
|
+
if (this.hasUpdates) {
|
|
53
|
+
const updates = this.pendingUpdates;
|
|
54
|
+
this.pendingUpdates = {};
|
|
55
|
+
this.hasUpdates = false;
|
|
56
|
+
this.player.updatePlayer(updates);
|
|
57
|
+
}
|
|
58
|
+
this.timeout = null;
|
|
59
|
+
}, 32);
|
|
60
|
+
|
|
61
|
+
return Promise.resolve();
|
|
62
|
+
}
|
|
13
63
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
64
|
+
destroy() {
|
|
65
|
+
if (this.timeout) {
|
|
66
|
+
clearTimeout(this.timeout);
|
|
67
|
+
this.timeout = null;
|
|
68
|
+
}
|
|
69
|
+
this.pendingUpdates = {};
|
|
70
|
+
this.hasUpdates = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
22
73
|
|
|
23
|
-
|
|
74
|
+
class Player extends EventEmitter {
|
|
75
|
+
static LOOP_MODES = LOOP_MODES;
|
|
76
|
+
static EVENT_HANDLERS = EVENT_HANDLERS;
|
|
77
|
+
static validModes = VALID_MODES;
|
|
24
78
|
|
|
25
79
|
constructor(aqua, nodes, options = {}) {
|
|
26
80
|
super();
|
|
@@ -34,10 +88,12 @@ class Player extends EventEmitter {
|
|
|
34
88
|
this.filters = new Filters(this);
|
|
35
89
|
this.queue = new Queue();
|
|
36
90
|
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
this.
|
|
91
|
+
const vol = options.defaultVolume ?? 100;
|
|
92
|
+
this.volume = vol < 0 ? 0 : vol > 200 ? 200 : vol;
|
|
93
|
+
|
|
94
|
+
this.loop = VALID_MODES.has(options.loop) ? options.loop : LOOP_MODES.NONE;
|
|
95
|
+
this.shouldDeleteMessage = Boolean(this.aqua.options.shouldDeleteMessage);
|
|
96
|
+
this.leaveOnEnd = Boolean(this.aqua.options.leaveOnEnd);
|
|
41
97
|
|
|
42
98
|
this.previousTracks = new Array(50);
|
|
43
99
|
this.previousTracksIndex = 0;
|
|
@@ -54,32 +110,36 @@ class Player extends EventEmitter {
|
|
|
54
110
|
this.isAutoplayEnabled = false;
|
|
55
111
|
this.isAutoplay = false;
|
|
56
112
|
|
|
57
|
-
this.
|
|
58
|
-
this._updateTimeout = null;
|
|
113
|
+
this._updateBatcher = new UpdateBatcher(this);
|
|
59
114
|
this._dataStore = new Map();
|
|
60
115
|
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
this.connected = packet.state.connected;
|
|
64
|
-
this.ping = packet.state.ping;
|
|
65
|
-
this.timestamp = packet.state.timestamp;
|
|
116
|
+
this._handlePlayerUpdate = this._handlePlayerUpdate.bind(this);
|
|
117
|
+
this._handleEvent = this._handleEvent.bind(this);
|
|
66
118
|
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
this.on("playerUpdate", this._handlePlayerUpdate);
|
|
120
|
+
this.on("event", this._handleEvent);
|
|
121
|
+
}
|
|
69
122
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
_handlePlayerUpdate(packet) {
|
|
124
|
+
this.position = packet.state.position;
|
|
125
|
+
this.connected = packet.state.connected;
|
|
126
|
+
this.ping = packet.state.ping;
|
|
127
|
+
this.timestamp = packet.state.time;
|
|
128
|
+
this.aqua.emit("playerUpdate", this, packet);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async _handleEvent(payload) {
|
|
132
|
+
try {
|
|
133
|
+
const handlerName = EVENT_HANDLERS[payload.type];
|
|
134
|
+
if (handlerName && typeof this[handlerName] === "function") {
|
|
135
|
+
await this[handlerName](this, this.current, payload);
|
|
136
|
+
} else {
|
|
137
|
+
this.aqua.emit("nodeError", this, new Error(`Node encountered an unknown event: '${payload.type}'`));
|
|
81
138
|
}
|
|
82
|
-
})
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(`Error handling event ${payload.type}:`, error);
|
|
141
|
+
this.aqua.emit("error", error);
|
|
142
|
+
}
|
|
83
143
|
}
|
|
84
144
|
|
|
85
145
|
get previous() {
|
|
@@ -91,23 +151,7 @@ class Player extends EventEmitter {
|
|
|
91
151
|
}
|
|
92
152
|
|
|
93
153
|
batchUpdatePlayer(data, immediate = false) {
|
|
94
|
-
|
|
95
|
-
if (this._updateTimeout) clearTimeout(this._updateTimeout);
|
|
96
|
-
|
|
97
|
-
if (immediate || data.track) {
|
|
98
|
-
const updates = this._pendingUpdates;
|
|
99
|
-
this._pendingUpdates = {};
|
|
100
|
-
return this.updatePlayer(updates);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
this._updateTimeout = setTimeout(() => {
|
|
104
|
-
const updates = this._pendingUpdates;
|
|
105
|
-
this._pendingUpdates = {};
|
|
106
|
-
this.updatePlayer(updates);
|
|
107
|
-
this._updateTimeout = null;
|
|
108
|
-
}, 50);
|
|
109
|
-
|
|
110
|
-
return Promise.resolve();
|
|
154
|
+
return this._updateBatcher.batch(data, immediate);
|
|
111
155
|
}
|
|
112
156
|
|
|
113
157
|
async autoplay(player) {
|
|
@@ -124,36 +168,37 @@ class Player extends EventEmitter {
|
|
|
124
168
|
const { sourceName, identifier, uri, requester } = this.previous.info;
|
|
125
169
|
this.aqua.emit("debug", this.guildId, `Attempting autoplay for ${sourceName}`);
|
|
126
170
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
171
|
+
let query, source;
|
|
172
|
+
|
|
173
|
+
switch (sourceName) {
|
|
174
|
+
case "youtube":
|
|
175
|
+
query = `https://www.youtube.com/watch?v=${identifier}&list=RD${identifier}`;
|
|
176
|
+
source = "ytmsearch";
|
|
177
|
+
break;
|
|
178
|
+
case "soundcloud":
|
|
133
179
|
const scResults = await scAutoPlay(uri);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
180
|
+
if (!scResults?.length) return this;
|
|
181
|
+
query = scResults[0];
|
|
182
|
+
source = "scsearch";
|
|
183
|
+
break;
|
|
184
|
+
case "spotify":
|
|
137
185
|
const spResult = await spAutoPlay(identifier);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const result = await handler();
|
|
146
|
-
if (!result) return this;
|
|
186
|
+
if (!spResult) return this;
|
|
187
|
+
query = `https://open.spotify.com/track/${spResult}`;
|
|
188
|
+
source = "spotify";
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
147
193
|
|
|
148
|
-
const { query, source } = result;
|
|
149
194
|
const response = await this.aqua.resolve({ query, source, requester });
|
|
150
195
|
|
|
151
|
-
|
|
152
|
-
if (!response?.tracks?.length || failTypes.has(response.loadType)) {
|
|
196
|
+
if (!response?.tracks?.length || FAIL_LOAD_TYPES.has(response.loadType)) {
|
|
153
197
|
return this.stop();
|
|
154
198
|
}
|
|
155
199
|
|
|
156
|
-
const
|
|
200
|
+
const tracks = response.tracks;
|
|
201
|
+
const track = tracks[Math.floor(Math.random() * tracks.length)];
|
|
157
202
|
if (!track?.info?.title) throw new Error("Invalid track object: missing title or info.");
|
|
158
203
|
|
|
159
204
|
track.requester = this.previous.requester || { id: "Unknown" };
|
|
@@ -204,11 +249,7 @@ class Player extends EventEmitter {
|
|
|
204
249
|
if (!this.connected) return this;
|
|
205
250
|
|
|
206
251
|
const voiceChannelId = this.voiceChannel ? this.voiceChannel.id || this.voiceChannel : null;
|
|
207
|
-
|
|
208
|
-
clearTimeout(this._updateTimeout);
|
|
209
|
-
this._updateTimeout = null;
|
|
210
|
-
this._pendingUpdates = {};
|
|
211
|
-
}
|
|
252
|
+
this._updateBatcher.destroy();
|
|
212
253
|
|
|
213
254
|
this.send({ guild_id: this.guildId, channel_id: null });
|
|
214
255
|
this._lastVoiceChannel = voiceChannelId;
|
|
@@ -244,9 +285,9 @@ class Player extends EventEmitter {
|
|
|
244
285
|
}
|
|
245
286
|
|
|
246
287
|
async getLyrics(options = {}) {
|
|
247
|
-
const { query = null, useCurrentTrack = true } = options;
|
|
248
|
-
if (query) return this.nodes.rest.getLyrics({ track: { info: { title: query }, search: true } }) || null;
|
|
249
|
-
if (useCurrentTrack && this.playing) return this.nodes.rest.getLyrics({ track: { encoded: this.current.track, guild_id: this.guildId } }) || null;
|
|
288
|
+
const { query = null, useCurrentTrack = true, skipTrackSource = false } = options;
|
|
289
|
+
if (query) return this.nodes.rest.getLyrics({ track: { info: { title: query }, search: true }, skipTrackSource }) || null;
|
|
290
|
+
if (useCurrentTrack && this.playing) return this.nodes.rest.getLyrics({ track: { encoded: this.current.track, guild_id: this.guildId }, skipTrackSource }) || null;
|
|
250
291
|
return null;
|
|
251
292
|
}
|
|
252
293
|
|
|
@@ -273,7 +314,7 @@ class Player extends EventEmitter {
|
|
|
273
314
|
}
|
|
274
315
|
|
|
275
316
|
setLoop(mode) {
|
|
276
|
-
if (!
|
|
317
|
+
if (!VALID_MODES.has(mode)) throw new Error("Loop mode must be 'none', 'track', or 'queue'.");
|
|
277
318
|
this.loop = mode;
|
|
278
319
|
this.batchUpdatePlayer({ loop: mode });
|
|
279
320
|
return this;
|
|
@@ -310,11 +351,33 @@ class Player extends EventEmitter {
|
|
|
310
351
|
|
|
311
352
|
shuffle() {
|
|
312
353
|
const queue = this.queue;
|
|
313
|
-
|
|
354
|
+
const len = queue.length;
|
|
355
|
+
|
|
356
|
+
if (len <= 1) return this;
|
|
357
|
+
|
|
358
|
+
if (len < 200) {
|
|
359
|
+
for (let i = len - 1; i > 0; i--) {
|
|
360
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
361
|
+
[queue[i], queue[j]] = [queue[j], queue[i]];
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
this._shuffleAsync(queue, len - 1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return this;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
_shuffleAsync(queue, i, chunkSize = 100) {
|
|
371
|
+
const end = Math.max(0, i - chunkSize);
|
|
372
|
+
|
|
373
|
+
for (; i > end; i--) {
|
|
314
374
|
const j = Math.floor(Math.random() * (i + 1));
|
|
315
375
|
[queue[i], queue[j]] = [queue[j], queue[i]];
|
|
316
376
|
}
|
|
317
|
-
|
|
377
|
+
|
|
378
|
+
if (i > 0) {
|
|
379
|
+
setImmediate(() => this._shuffleAsync(queue, i, chunkSize));
|
|
380
|
+
}
|
|
318
381
|
}
|
|
319
382
|
|
|
320
383
|
getQueue() {
|
|
@@ -354,8 +417,7 @@ class Player extends EventEmitter {
|
|
|
354
417
|
}
|
|
355
418
|
|
|
356
419
|
const reason = payload.reason;
|
|
357
|
-
|
|
358
|
-
if (failureReasons.has(reason)) {
|
|
420
|
+
if (FAILURE_REASONS.has(reason)) {
|
|
359
421
|
if (!player.queue.length) {
|
|
360
422
|
this.previousTracksCount = 0;
|
|
361
423
|
this._dataStore.clear();
|
|
@@ -367,15 +429,13 @@ class Player extends EventEmitter {
|
|
|
367
429
|
return;
|
|
368
430
|
}
|
|
369
431
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
case Player.LOOP_MODES.QUEUE:
|
|
375
|
-
player.queue.push(track);
|
|
376
|
-
break;
|
|
432
|
+
if (this.loop === LOOP_MODES.TRACK) {
|
|
433
|
+
player.queue.unshift(track);
|
|
434
|
+
} else if (this.loop === LOOP_MODES.QUEUE) {
|
|
435
|
+
player.queue.push(track);
|
|
377
436
|
}
|
|
378
437
|
|
|
438
|
+
|
|
379
439
|
if (player.queue.isEmpty()) {
|
|
380
440
|
if (this.isAutoplayEnabled) {
|
|
381
441
|
await player.autoplay(player);
|
|
@@ -406,8 +466,7 @@ class Player extends EventEmitter {
|
|
|
406
466
|
|
|
407
467
|
async socketClosed(player, payload) {
|
|
408
468
|
const { code, guildId } = payload || {};
|
|
409
|
-
|
|
410
|
-
if (reconnectCodes.has(code)) {
|
|
469
|
+
if (RECONNECT_CODES.has(code)) {
|
|
411
470
|
this.send({
|
|
412
471
|
guild_id: guildId,
|
|
413
472
|
channel_id: this.voiceChannel,
|
package/build/structures/Rest.js
CHANGED
|
@@ -34,28 +34,48 @@ class Rest {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
async makeRequest(method, endpoint, body = null) {
|
|
38
38
|
const url = `${this.baseUrl}${endpoint}`;
|
|
39
|
+
|
|
40
|
+
if (!this.agent) {
|
|
41
|
+
const AgentClass = this.secure ? https.Agent : http.Agent;
|
|
42
|
+
this.agent = new AgentClass({
|
|
43
|
+
keepAlive: true,
|
|
44
|
+
maxSockets: 10,
|
|
45
|
+
maxFreeSockets: 5,
|
|
46
|
+
timeout: this.timeout,
|
|
47
|
+
freeSocketTimeout: 30000
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
const options = {
|
|
40
52
|
method,
|
|
41
53
|
headers: this.headers,
|
|
42
54
|
timeout: this.timeout,
|
|
43
|
-
|
|
55
|
+
agent: this.agent
|
|
44
56
|
};
|
|
45
57
|
|
|
46
58
|
return new Promise((resolve, reject) => {
|
|
47
|
-
const
|
|
59
|
+
const client = this.secure ? https : http;
|
|
60
|
+
const req = client.request(url, options, (res) => {
|
|
61
|
+
if (res.statusCode === 204) return;
|
|
62
|
+
|
|
48
63
|
const chunks = [];
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
let totalLength = 0;
|
|
65
|
+
|
|
66
|
+
res.on('data', (chunk) => {
|
|
51
67
|
chunks.push(chunk);
|
|
68
|
+
totalLength += chunk.length;
|
|
52
69
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
resolve(null);
|
|
57
|
-
return;
|
|
70
|
+
|
|
71
|
+
res.on('end', () => {
|
|
72
|
+
if (totalLength === 0) {
|
|
73
|
+
return resolve(null);
|
|
58
74
|
}
|
|
75
|
+
|
|
76
|
+
const buffer = Buffer.concat(chunks, totalLength);
|
|
77
|
+
const data = buffer.toString('utf8');
|
|
78
|
+
|
|
59
79
|
try {
|
|
60
80
|
resolve(JSON.parse(data));
|
|
61
81
|
} catch (err) {
|
|
@@ -64,15 +84,20 @@ class Rest {
|
|
|
64
84
|
});
|
|
65
85
|
});
|
|
66
86
|
|
|
67
|
-
req.on(
|
|
68
|
-
|
|
87
|
+
req.on('error', (err) => {
|
|
88
|
+
reject(new Error(`Request failed (${method} ${url}): ${err.message}`));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
req.on('timeout', () => {
|
|
69
92
|
req.destroy();
|
|
70
93
|
reject(new Error(`Request timed out after ${this.timeout}ms (${method} ${url})`));
|
|
71
94
|
});
|
|
72
95
|
|
|
73
96
|
if (body) {
|
|
74
|
-
|
|
97
|
+
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
|
|
98
|
+
req.write(bodyStr);
|
|
75
99
|
}
|
|
100
|
+
|
|
76
101
|
req.end();
|
|
77
102
|
});
|
|
78
103
|
}
|
|
@@ -134,27 +159,30 @@ class Rest {
|
|
|
134
159
|
const endpoint = `/${this.version}/routeplanner/free/address`;
|
|
135
160
|
return this.makeRequest("POST", endpoint, { address });
|
|
136
161
|
}
|
|
137
|
-
|
|
138
162
|
async getLyrics({ track }) {
|
|
139
163
|
if (!track) return null;
|
|
140
164
|
|
|
165
|
+
|
|
141
166
|
try {
|
|
142
167
|
if (track.search) {
|
|
143
168
|
const query = encodeURIComponent(track.info.title);
|
|
144
169
|
try {
|
|
145
170
|
const res = await this.makeRequest(
|
|
146
|
-
"GET",
|
|
171
|
+
"GET",
|
|
147
172
|
`/${this.version}/lyrics/search?query=${query}&source=genius`
|
|
148
173
|
);
|
|
174
|
+
|
|
149
175
|
if (res) return res;
|
|
150
176
|
} catch (_) {
|
|
151
177
|
}
|
|
152
178
|
} else {
|
|
153
179
|
this.validateSessionId();
|
|
154
|
-
|
|
180
|
+
const res = await this.makeRequest(
|
|
155
181
|
"GET",
|
|
156
|
-
`/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/
|
|
182
|
+
`/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/lyrics`
|
|
157
183
|
);
|
|
184
|
+
console.log(res);
|
|
185
|
+
return res;
|
|
158
186
|
}
|
|
159
187
|
} catch (error) {
|
|
160
188
|
console.error("Failed to fetch lyrics:", error.message);
|