aqualink 2.4.0 → 2.6.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/build/structures/Aqua.js +16 -20
- package/build/structures/Connection.js +39 -34
- package/build/structures/Filters.js +84 -124
- package/build/structures/Node.js +116 -81
- package/build/structures/Player.js +90 -33
- package/build/structures/Queue.js +16 -8
- package/build/structures/Rest.js +55 -40
- package/build/structures/Track.js +2 -1
- package/package.json +1 -1
package/build/structures/Aqua.js
CHANGED
|
@@ -233,7 +233,7 @@ class Aqua extends EventEmitter {
|
|
|
233
233
|
const response = await requestNode.rest.makeRequest("GET", endpoint);
|
|
234
234
|
|
|
235
235
|
if (["empty", "NO_MATCHES"].includes(response.loadType)) {
|
|
236
|
-
return await this.handleNoMatches(
|
|
236
|
+
return await this.handleNoMatches(query);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
return this.constructResponse(response, requester, requestNode);
|
|
@@ -244,33 +244,29 @@ class Aqua extends EventEmitter {
|
|
|
244
244
|
throw new Error(`Failed to resolve track: ${error.message}`);
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
|
|
247
|
+
|
|
248
248
|
getRequestNode(nodes) {
|
|
249
249
|
if (!nodes) return this.leastUsedNodes[0];
|
|
250
250
|
|
|
251
251
|
if (nodes instanceof Node) return nodes;
|
|
252
|
-
if (typeof nodes === "string")
|
|
252
|
+
if (typeof nodes === "string") {
|
|
253
|
+
const mappedNode = this.nodeMap.get(nodes);
|
|
254
|
+
return mappedNode || this.leastUsedNodes[0];
|
|
255
|
+
}
|
|
253
256
|
|
|
254
257
|
throw new TypeError(`'nodes' must be a string or Node instance, received: ${typeof nodes}`);
|
|
255
258
|
}
|
|
256
|
-
|
|
257
|
-
async handleNoMatches(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const spotifyEndpoint = `/v4/loadtracks?identifier=https://open.spotify.com/track/${query}`;
|
|
267
|
-
return await rest.makeRequest("GET", spotifyEndpoint);
|
|
268
|
-
} catch (error) {
|
|
269
|
-
console.error(`Failed to resolve track: ${error.message}`);
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
259
|
+
|
|
260
|
+
async handleNoMatches(query) {
|
|
261
|
+
return {
|
|
262
|
+
loadType: "empty",
|
|
263
|
+
exception: null,
|
|
264
|
+
playlistInfo: null,
|
|
265
|
+
pluginInfo: {},
|
|
266
|
+
tracks: []
|
|
267
|
+
};
|
|
272
268
|
}
|
|
273
|
-
|
|
269
|
+
|
|
274
270
|
constructResponse(response, requester, requestNode) {
|
|
275
271
|
const baseResponse = {
|
|
276
272
|
loadType: response.loadType,
|
|
@@ -2,40 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
class Connection {
|
|
4
4
|
constructor(player) {
|
|
5
|
-
this.
|
|
5
|
+
this.player = player;
|
|
6
6
|
|
|
7
7
|
this.sessionId = null;
|
|
8
8
|
this.endpoint = null;
|
|
9
9
|
this.token = null;
|
|
10
|
-
|
|
11
10
|
this.region = null;
|
|
12
|
-
|
|
13
|
-
this.selfMute = false;
|
|
11
|
+
|
|
14
12
|
this.voiceChannel = player.voiceChannel;
|
|
15
13
|
this.guildId = player.guildId;
|
|
16
|
-
|
|
17
14
|
this.aqua = player.aqua;
|
|
18
15
|
this.nodes = player.nodes;
|
|
16
|
+
|
|
17
|
+
this.selfDeaf = false;
|
|
18
|
+
this.selfMute = false;
|
|
19
|
+
|
|
20
|
+
this.hasDebugListeners = this.aqua.listenerCount('debug') > 0;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
setServerUpdate(data) {
|
|
22
|
-
if (!data
|
|
24
|
+
if (!data || !data.endpoint) return;
|
|
23
25
|
|
|
24
26
|
const { endpoint, token } = data;
|
|
25
|
-
const dotIndex = endpoint.indexOf('.');
|
|
26
27
|
|
|
28
|
+
const dotIndex = endpoint.indexOf('.');
|
|
27
29
|
if (dotIndex === -1) return;
|
|
30
|
+
|
|
28
31
|
const newRegion = endpoint.substring(0, dotIndex);
|
|
29
32
|
|
|
30
33
|
if (this.region !== newRegion) {
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
this.endpoint = endpoint;
|
|
35
|
+
this.token = token;
|
|
36
|
+
this.region = newRegion;
|
|
33
37
|
|
|
34
|
-
if (this.
|
|
38
|
+
if (this.hasDebugListeners) {
|
|
35
39
|
this.aqua.emit(
|
|
36
40
|
"debug",
|
|
37
41
|
`[Player ${this.guildId} - CONNECTION] Voice Server: ${
|
|
38
|
-
|
|
42
|
+
this.region ? `Changed from ${this.region} to ${newRegion}` : newRegion
|
|
39
43
|
}`
|
|
40
44
|
);
|
|
41
45
|
}
|
|
@@ -45,10 +49,12 @@ class Connection {
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
setStateUpdate(data) {
|
|
48
|
-
|
|
52
|
+
if (!data) return this.player?.destroy();
|
|
53
|
+
|
|
54
|
+
const { channel_id, session_id, self_deaf, self_mute } = data;
|
|
49
55
|
|
|
50
56
|
if (!channel_id || !session_id) {
|
|
51
|
-
this.
|
|
57
|
+
this.player?.destroy();
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -57,36 +63,35 @@ class Connection {
|
|
|
57
63
|
this.voiceChannel = channel_id;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
this.selfDeaf =
|
|
61
|
-
this.selfMute =
|
|
66
|
+
this.selfDeaf = Boolean(self_deaf);
|
|
67
|
+
this.selfMute = Boolean(self_mute);
|
|
62
68
|
this.sessionId = session_id;
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
_updatePlayerVoiceData() {
|
|
72
|
+
if (!this.player) return;
|
|
73
|
+
|
|
74
|
+
const voiceData = {
|
|
75
|
+
sessionId: this.sessionId,
|
|
76
|
+
endpoint: this.endpoint,
|
|
77
|
+
token: this.token
|
|
78
|
+
};
|
|
68
79
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
},
|
|
78
|
-
volume: player.volume
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
} catch (error) {
|
|
82
|
-
if (this.aqua.listenerCount('apiError')) {
|
|
80
|
+
this.nodes.rest.updatePlayer({
|
|
81
|
+
guildId: this.guildId,
|
|
82
|
+
data: {
|
|
83
|
+
voice: voiceData,
|
|
84
|
+
volume: this.player.volume
|
|
85
|
+
}
|
|
86
|
+
}).catch(error => {
|
|
87
|
+
if (this.aqua.listenerCount('apiError') > 0) {
|
|
83
88
|
this.aqua.emit("apiError", "updatePlayer", {
|
|
84
89
|
error,
|
|
85
90
|
guildId: this.guildId,
|
|
86
|
-
voiceData
|
|
91
|
+
voiceData
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
|
-
}
|
|
94
|
+
});
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -3,205 +3,165 @@
|
|
|
3
3
|
class Filters {
|
|
4
4
|
constructor(player, options = {}) {
|
|
5
5
|
this.player = player;
|
|
6
|
-
this.volume = options.volume || 1;
|
|
7
|
-
this.equalizer = options.equalizer || [];
|
|
8
|
-
this.karaoke = options.karaoke || null;
|
|
9
|
-
this.timescale = options.timescale || null;
|
|
10
|
-
this.tremolo = options.tremolo || null;
|
|
11
|
-
this.vibrato = options.vibrato || null;
|
|
12
|
-
this.rotation = options.rotation || null;
|
|
13
|
-
this.distortion = options.distortion || null;
|
|
14
|
-
this.channelMix = options.channelMix || null;
|
|
15
|
-
this.lowPass = options.lowPass || null;
|
|
16
|
-
this.bassboost = options.bassboost || null;
|
|
17
|
-
this.slowmode = options.slowmode || null;
|
|
18
|
-
this.nightcore = options.nightcore || null;
|
|
19
|
-
this.vaporwave = options.vaporwave || null;
|
|
20
|
-
this._8d = options._8d || null;
|
|
21
6
|
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
channelMix: null,
|
|
32
|
-
lowPass: null
|
|
7
|
+
this.defaults = {
|
|
8
|
+
karaoke: { level: 1.0, monoLevel: 1.0, filterBand: 220.0, filterWidth: 100.0 },
|
|
9
|
+
timescale: { speed: 1.0, pitch: 1.0, rate: 1.0 },
|
|
10
|
+
tremolo: { frequency: 2.0, depth: 0.5 },
|
|
11
|
+
vibrato: { frequency: 2.0, depth: 0.5 },
|
|
12
|
+
rotation: { rotationHz: 0.0 },
|
|
13
|
+
distortion: { sinOffset: 0.0, sinScale: 1.0, cosOffset: 0.0, cosScale: 1.0, tanOffset: 0.0, tanScale: 1.0, offset: 0.0, scale: 1.0 },
|
|
14
|
+
channelMix: { leftToLeft: 1.0, leftToRight: 0.0, rightToLeft: 0.0, rightToRight: 1.0 },
|
|
15
|
+
lowPass: { smoothing: 20.0 }
|
|
33
16
|
};
|
|
17
|
+
|
|
18
|
+
this.filters = {
|
|
19
|
+
volume: options.volume || 1,
|
|
20
|
+
equalizer: options.equalizer || [],
|
|
21
|
+
karaoke: options.karaoke || null,
|
|
22
|
+
timescale: options.timescale || null,
|
|
23
|
+
tremolo: options.tremolo || null,
|
|
24
|
+
vibrato: options.vibrato || null,
|
|
25
|
+
rotation: options.rotation || null,
|
|
26
|
+
distortion: options.distortion || null,
|
|
27
|
+
channelMix: options.channelMix || null,
|
|
28
|
+
lowPass: options.lowPass || null
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
this.presets = {
|
|
32
|
+
bassboost: options.bassboost !== undefined ? options.bassboost : null,
|
|
33
|
+
slowmode: options.slowmode !== undefined ? options.slowmode : null,
|
|
34
|
+
nightcore: options.nightcore !== undefined ? options.nightcore : null,
|
|
35
|
+
vaporwave: options.vaporwave !== undefined ? options.vaporwave : null,
|
|
36
|
+
_8d: options._8d !== undefined ? options._8d : null
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this._pendingUpdate = false;
|
|
40
|
+
this._updateTimeout = null;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
_setFilter(filterName, enabled, options,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
_setFilter(filterName, enabled, options = {}, defaultKey = filterName) {
|
|
44
|
+
this.filters[filterName] = enabled ? { ...this.defaults[defaultKey], ...options } : null;
|
|
45
|
+
return this._scheduleUpdate();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_scheduleUpdate() {
|
|
49
|
+
this._pendingUpdate = true;
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
filterObj[key] = options[key] !== undefined ? options[key] : defaultValue;
|
|
51
|
+
if (this._updateTimeout) {
|
|
52
|
+
clearTimeout(this._updateTimeout);
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
this
|
|
48
|
-
return this
|
|
55
|
+
this._updateTimeout = setTimeout(() => this.updateFilters(), 0);
|
|
56
|
+
return this;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
setEqualizer(bands) {
|
|
52
|
-
this.equalizer = bands;
|
|
53
|
-
return this.
|
|
60
|
+
this.filters.equalizer = bands || [];
|
|
61
|
+
return this._scheduleUpdate();
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
setKaraoke(enabled, options = {}) {
|
|
57
|
-
return this._setFilter('karaoke', enabled, options
|
|
58
|
-
level: 1.0,
|
|
59
|
-
monoLevel: 1.0,
|
|
60
|
-
filterBand: 220.0,
|
|
61
|
-
filterWidth: 100.0
|
|
62
|
-
});
|
|
65
|
+
return this._setFilter('karaoke', enabled, options);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
setTimescale(enabled, options = {}) {
|
|
66
|
-
return this._setFilter('timescale', enabled, options
|
|
67
|
-
speed: 1.0,
|
|
68
|
-
pitch: 1.0,
|
|
69
|
-
rate: 1.0
|
|
70
|
-
});
|
|
69
|
+
return this._setFilter('timescale', enabled, options);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
setTremolo(enabled, options = {}) {
|
|
74
|
-
return this._setFilter('tremolo', enabled, options
|
|
75
|
-
frequency: 2.0,
|
|
76
|
-
depth: 0.5
|
|
77
|
-
});
|
|
73
|
+
return this._setFilter('tremolo', enabled, options);
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
setVibrato(enabled, options = {}) {
|
|
81
|
-
return this._setFilter('vibrato', enabled, options
|
|
82
|
-
frequency: 2.0,
|
|
83
|
-
depth: 0.5
|
|
84
|
-
});
|
|
77
|
+
return this._setFilter('vibrato', enabled, options);
|
|
85
78
|
}
|
|
86
79
|
|
|
87
80
|
setRotation(enabled, options = {}) {
|
|
88
|
-
return this._setFilter('rotation', enabled, options
|
|
89
|
-
rotationHz: 0.0
|
|
90
|
-
});
|
|
81
|
+
return this._setFilter('rotation', enabled, options);
|
|
91
82
|
}
|
|
92
83
|
|
|
93
84
|
setDistortion(enabled, options = {}) {
|
|
94
|
-
return this._setFilter('distortion', enabled, options
|
|
95
|
-
sinOffset: 0.0,
|
|
96
|
-
sinScale: 1.0,
|
|
97
|
-
cosOffset: 0.0,
|
|
98
|
-
cosScale: 1.0,
|
|
99
|
-
tanOffset: 0.0,
|
|
100
|
-
tanScale: 1.0,
|
|
101
|
-
offset: 0.0,
|
|
102
|
-
scale: 1.0
|
|
103
|
-
});
|
|
85
|
+
return this._setFilter('distortion', enabled, options);
|
|
104
86
|
}
|
|
105
87
|
|
|
106
88
|
setChannelMix(enabled, options = {}) {
|
|
107
|
-
return this._setFilter('channelMix', enabled, options
|
|
108
|
-
leftToLeft: 1.0,
|
|
109
|
-
leftToRight: 0.0,
|
|
110
|
-
rightToLeft: 0.0,
|
|
111
|
-
rightToRight: 1.0
|
|
112
|
-
});
|
|
89
|
+
return this._setFilter('channelMix', enabled, options);
|
|
113
90
|
}
|
|
114
91
|
|
|
115
92
|
setLowPass(enabled, options = {}) {
|
|
116
|
-
return this._setFilter('lowPass', enabled, options
|
|
117
|
-
smoothing: 20.0
|
|
118
|
-
});
|
|
93
|
+
return this._setFilter('lowPass', enabled, options);
|
|
119
94
|
}
|
|
120
95
|
|
|
121
96
|
setBassboost(enabled, options = {}) {
|
|
122
97
|
if (!enabled) {
|
|
123
|
-
this.bassboost = null;
|
|
98
|
+
this.presets.bassboost = null;
|
|
124
99
|
return this.setEqualizer([]);
|
|
125
100
|
}
|
|
126
101
|
|
|
127
102
|
const value = options.value || 5;
|
|
128
103
|
if (value < 0 || value > 5) throw new Error("Bassboost value must be between 0 and 5");
|
|
129
104
|
|
|
130
|
-
this.bassboost = value;
|
|
131
|
-
const
|
|
105
|
+
this.presets.bassboost = value;
|
|
106
|
+
const gain = (value - 1) * (1.25 / 9) - 0.25;
|
|
132
107
|
|
|
133
|
-
const eq =
|
|
134
|
-
for (let i = 0; i < 13; i++) {
|
|
135
|
-
eq[i] = { band: i, gain: num };
|
|
136
|
-
}
|
|
108
|
+
const eq = Array.from({ length: 13 }, (_, i) => ({ band: i, gain }));
|
|
137
109
|
|
|
138
110
|
return this.setEqualizer(eq);
|
|
139
111
|
}
|
|
140
112
|
|
|
141
113
|
setSlowmode(enabled, options = {}) {
|
|
142
|
-
this.slowmode = enabled;
|
|
114
|
+
this.presets.slowmode = enabled;
|
|
143
115
|
return this.setTimescale(enabled, { rate: enabled ? (options.rate || 0.8) : 1.0 });
|
|
144
116
|
}
|
|
145
117
|
|
|
146
118
|
setNightcore(enabled, options = {}) {
|
|
147
|
-
this.nightcore = enabled;
|
|
119
|
+
this.presets.nightcore = enabled;
|
|
148
120
|
return this.setTimescale(enabled, { rate: enabled ? (options.rate || 1.5) : 1.0 });
|
|
149
121
|
}
|
|
150
122
|
|
|
151
123
|
setVaporwave(enabled, options = {}) {
|
|
152
|
-
this.vaporwave = enabled;
|
|
124
|
+
this.presets.vaporwave = enabled;
|
|
153
125
|
return this.setTimescale(enabled, { pitch: enabled ? (options.pitch || 0.5) : 1.0 });
|
|
154
126
|
}
|
|
155
127
|
|
|
156
128
|
set8D(enabled, options = {}) {
|
|
157
|
-
this._8d = enabled;
|
|
129
|
+
this.presets._8d = enabled;
|
|
158
130
|
return this.setRotation(enabled, { rotationHz: enabled ? (options.rotationHz || 0.2) : 0.0 });
|
|
159
131
|
}
|
|
160
132
|
|
|
161
133
|
async clearFilters() {
|
|
162
|
-
this.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.distortion = null;
|
|
170
|
-
this.channelMix = null;
|
|
171
|
-
this.lowPass = null;
|
|
172
|
-
this.bassboost = null;
|
|
173
|
-
this.slowmode = null;
|
|
174
|
-
this.nightcore = null;
|
|
175
|
-
this.vaporwave = null;
|
|
176
|
-
this._8d = null;
|
|
134
|
+
Object.keys(this.filters).forEach(key => {
|
|
135
|
+
this.filters[key] = key === 'volume' ? 1 : (key === 'equalizer' ? [] : null);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
Object.keys(this.presets).forEach(key => {
|
|
139
|
+
this.presets[key] = null;
|
|
140
|
+
});
|
|
177
141
|
|
|
178
|
-
this.
|
|
179
|
-
this.
|
|
142
|
+
this._pendingUpdate = false;
|
|
143
|
+
if (this._updateTimeout) {
|
|
144
|
+
clearTimeout(this._updateTimeout);
|
|
145
|
+
this._updateTimeout = null;
|
|
146
|
+
}
|
|
180
147
|
|
|
181
148
|
await this.updateFilters();
|
|
182
149
|
return this;
|
|
183
150
|
}
|
|
184
151
|
|
|
185
152
|
async updateFilters() {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
distortion: this.distortion,
|
|
196
|
-
channelMix: this.channelMix,
|
|
197
|
-
lowPass: this.lowPass
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
this._filterDataTemplate = { ...filterData };
|
|
201
|
-
|
|
153
|
+
if (!this._pendingUpdate && this._updateTimeout) {
|
|
154
|
+
clearTimeout(this._updateTimeout);
|
|
155
|
+
this._updateTimeout = null;
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this._pendingUpdate = false;
|
|
160
|
+
this._updateTimeout = null;
|
|
161
|
+
|
|
202
162
|
await this.player.nodes.rest.updatePlayer({
|
|
203
163
|
guildId: this.player.guildId,
|
|
204
|
-
data: { filters:
|
|
164
|
+
data: { filters: { ...this.filters } }
|
|
205
165
|
});
|
|
206
166
|
|
|
207
167
|
return this;
|
package/build/structures/Node.js
CHANGED
|
@@ -6,24 +6,45 @@ class Node {
|
|
|
6
6
|
static BACKOFF_MULTIPLIER = 1.5;
|
|
7
7
|
static MAX_BACKOFF = 60000;
|
|
8
8
|
static WS_OPEN = WebSocket.OPEN;
|
|
9
|
+
static WS_CLOSE_NORMAL = 1000;
|
|
9
10
|
|
|
10
11
|
constructor(aqua, connOptions, options = {}) {
|
|
11
12
|
this.aqua = aqua;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
host = "localhost",
|
|
16
|
+
name = host,
|
|
17
|
+
port = 2333,
|
|
18
|
+
password = "youshallnotpass",
|
|
19
|
+
secure = false,
|
|
20
|
+
sessionId = null,
|
|
21
|
+
regions = []
|
|
22
|
+
} = connOptions;
|
|
23
|
+
|
|
24
|
+
this.host = host;
|
|
25
|
+
this.name = name;
|
|
26
|
+
this.port = port;
|
|
27
|
+
this.password = password;
|
|
28
|
+
this.secure = !!secure;
|
|
29
|
+
this.sessionId = sessionId;
|
|
30
|
+
this.regions = regions;
|
|
19
31
|
|
|
20
32
|
this.wsUrl = `ws${this.secure ? "s" : ""}://${this.host}:${this.port}/v4/websocket`;
|
|
21
33
|
this.rest = new Rest(aqua, this);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
resumeTimeout = 60,
|
|
37
|
+
autoResume = false,
|
|
38
|
+
reconnectTimeout = 2000,
|
|
39
|
+
reconnectTries = 3,
|
|
40
|
+
infiniteReconnects = false
|
|
41
|
+
} = options;
|
|
42
|
+
|
|
43
|
+
this.resumeTimeout = resumeTimeout;
|
|
44
|
+
this.autoResume = autoResume;
|
|
45
|
+
this.reconnectTimeout = reconnectTimeout;
|
|
46
|
+
this.reconnectTries = reconnectTries;
|
|
47
|
+
this.infiniteReconnects = infiniteReconnects;
|
|
27
48
|
|
|
28
49
|
this.connected = false;
|
|
29
50
|
this.info = null;
|
|
@@ -31,6 +52,13 @@ class Node {
|
|
|
31
52
|
this.reconnectAttempted = 0;
|
|
32
53
|
this.reconnectTimeoutId = null;
|
|
33
54
|
|
|
55
|
+
this._boundOnOpen = this._onOpen.bind(this);
|
|
56
|
+
this._boundOnError = this._onError.bind(this);
|
|
57
|
+
this._boundOnMessage = this._onMessage.bind(this);
|
|
58
|
+
this._boundOnClose = this._onClose.bind(this);
|
|
59
|
+
|
|
60
|
+
this._headers = this._constructHeaders();
|
|
61
|
+
|
|
34
62
|
this.initializeStats();
|
|
35
63
|
}
|
|
36
64
|
|
|
@@ -50,7 +78,7 @@ class Node {
|
|
|
50
78
|
const headers = {
|
|
51
79
|
Authorization: this.password,
|
|
52
80
|
"User-Id": this.aqua.clientId,
|
|
53
|
-
"Client-Name": `Aqua/${this.aqua.version}`
|
|
81
|
+
"Client-Name": `Aqua/${this.aqua.version} (https://github.com/ToddyTheNoobDud/AquaLink)`
|
|
54
82
|
};
|
|
55
83
|
|
|
56
84
|
if (this.sessionId) {
|
|
@@ -60,24 +88,27 @@ class Node {
|
|
|
60
88
|
return headers;
|
|
61
89
|
}
|
|
62
90
|
|
|
63
|
-
|
|
91
|
+
_updateHeaders() {
|
|
92
|
+
this._headers = this._constructHeaders();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async _onOpen() {
|
|
64
96
|
this.connected = true;
|
|
65
97
|
this.reconnectAttempted = 0;
|
|
66
98
|
this.emitDebug(`Connected to ${this.wsUrl}`);
|
|
67
99
|
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
100
|
+
if (this.aqua.bypassChecks?.nodeFetchInfo) return;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
this.info = await this.rest.makeRequest("GET", "/v4/info");
|
|
104
|
+
|
|
105
|
+
if (this.autoResume && this.sessionId) {
|
|
106
|
+
await this.resumePlayers();
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
this.info = null;
|
|
110
|
+
this.emitError(`Failed to fetch node info: ${err.message}`);
|
|
111
|
+
}
|
|
81
112
|
}
|
|
82
113
|
|
|
83
114
|
_onError(error) {
|
|
@@ -107,6 +138,7 @@ class Node {
|
|
|
107
138
|
const player = this.aqua.players.get(payload.guildId);
|
|
108
139
|
if (player) player.emit(op, payload);
|
|
109
140
|
}
|
|
141
|
+
break;
|
|
110
142
|
}
|
|
111
143
|
}
|
|
112
144
|
|
|
@@ -124,7 +156,7 @@ class Node {
|
|
|
124
156
|
scheduleReconnect(code) {
|
|
125
157
|
this.clearReconnectTimeout();
|
|
126
158
|
|
|
127
|
-
if (code ===
|
|
159
|
+
if (code === Node.WS_CLOSE_NORMAL) {
|
|
128
160
|
return;
|
|
129
161
|
}
|
|
130
162
|
|
|
@@ -166,31 +198,41 @@ class Node {
|
|
|
166
198
|
|
|
167
199
|
async connect() {
|
|
168
200
|
this.cleanupExistingConnection();
|
|
169
|
-
|
|
170
|
-
this.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
201
|
+
|
|
202
|
+
if (this.sessionId && !this._headers["Session-Id"]) {
|
|
203
|
+
this._updateHeaders();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.ws = new WebSocket(this.wsUrl, {
|
|
207
|
+
headers: this._headers,
|
|
208
|
+
perMessageDeflate: false
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
this.ws.once("open", this._boundOnOpen);
|
|
212
|
+
this.ws.once("error", this._boundOnError);
|
|
213
|
+
this.ws.on("message", this._boundOnMessage);
|
|
214
|
+
this.ws.once("close", this._boundOnClose);
|
|
174
215
|
}
|
|
175
216
|
|
|
176
217
|
cleanupExistingConnection() {
|
|
177
|
-
if (this.ws)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
218
|
+
if (!this.ws) return;
|
|
219
|
+
|
|
220
|
+
this.ws.removeAllListeners();
|
|
221
|
+
|
|
222
|
+
if (this.ws.readyState === Node.WS_OPEN) {
|
|
223
|
+
try {
|
|
224
|
+
this.ws.close();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
this.emitDebug(`Error closing WebSocket: ${err.message}`);
|
|
186
227
|
}
|
|
187
|
-
|
|
188
|
-
this.ws = null;
|
|
189
228
|
}
|
|
229
|
+
|
|
230
|
+
this.ws = null;
|
|
190
231
|
}
|
|
191
232
|
|
|
192
233
|
destroy(clean = false) {
|
|
193
234
|
this.clearReconnectTimeout();
|
|
235
|
+
|
|
194
236
|
this.cleanupExistingConnection();
|
|
195
237
|
|
|
196
238
|
if (clean) {
|
|
@@ -216,6 +258,7 @@ class Node {
|
|
|
216
258
|
async getStats() {
|
|
217
259
|
try {
|
|
218
260
|
const newStats = await this.rest.getStats();
|
|
261
|
+
|
|
219
262
|
Object.assign(this.stats, newStats);
|
|
220
263
|
return this.stats;
|
|
221
264
|
} catch (err) {
|
|
@@ -227,55 +270,46 @@ class Node {
|
|
|
227
270
|
_updateStats(payload) {
|
|
228
271
|
if (!payload) return;
|
|
229
272
|
|
|
230
|
-
this.
|
|
231
|
-
|
|
232
|
-
this.
|
|
273
|
+
this.stats.players = payload.players;
|
|
274
|
+
this.stats.playingPlayers = payload.playingPlayers;
|
|
275
|
+
this.stats.uptime = payload.uptime;
|
|
276
|
+
this.stats.ping = payload.ping;
|
|
233
277
|
|
|
234
|
-
this.
|
|
235
|
-
|
|
236
|
-
this._updateFrameStats(payload.frameStats);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
_updateBasicStats(payload) {
|
|
240
|
-
this.stats.players = payload.players || this.stats.players;
|
|
241
|
-
this.stats.playingPlayers = payload.playingPlayers || this.stats.playingPlayers;
|
|
242
|
-
this.stats.uptime = payload.uptime || this.stats.uptime;
|
|
243
|
-
this.stats.ping = payload.ping || this.stats.ping;
|
|
278
|
+
this._updateMemoryStats(payload.memory || {});
|
|
279
|
+
this._updateCpuStats(payload.cpu || {});
|
|
280
|
+
this._updateFrameStats(payload.frameStats || {});
|
|
244
281
|
}
|
|
245
282
|
|
|
246
|
-
_updateMemoryStats(memory
|
|
247
|
-
const
|
|
248
|
-
const free = memory.free || this.stats.memory.free;
|
|
249
|
-
const used = memory.used || this.stats.memory.used;
|
|
283
|
+
_updateMemoryStats(memory) {
|
|
284
|
+
const memoryStats = this.stats.memory;
|
|
250
285
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
286
|
+
memoryStats.free = memory.free;
|
|
287
|
+
memoryStats.used = memory.used;
|
|
288
|
+
memoryStats.allocated = memory.allocated;
|
|
289
|
+
memoryStats.reservable = memory.reservable;
|
|
255
290
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.stats.memory.usedPercentage = (used / allocated) * 100;
|
|
259
|
-
}
|
|
291
|
+
memoryStats.freePercentage = (memoryStats.free / memoryStats.allocated) * 100;
|
|
292
|
+
memoryStats.usedPercentage = (memoryStats.used / memoryStats.allocated) * 100;
|
|
260
293
|
}
|
|
261
294
|
|
|
262
|
-
_updateCpuStats(cpu
|
|
263
|
-
const
|
|
295
|
+
_updateCpuStats(cpu) {
|
|
296
|
+
const cpuStats = this.stats.cpu;
|
|
264
297
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
298
|
+
cpuStats.cores = cpu.cores;
|
|
299
|
+
cpuStats.systemLoad = cpu.systemLoad;
|
|
300
|
+
cpuStats.lavalinkLoad = cpu.lavalinkLoad;
|
|
268
301
|
|
|
269
|
-
|
|
270
|
-
this.stats.cpu.lavalinkLoadPercentage = (cpu.lavalinkLoad / cores) * 100;
|
|
271
|
-
}
|
|
302
|
+
cpuStats.lavalinkLoadPercentage = (cpuStats.lavalinkLoad / cpuStats.cores) * 100;
|
|
272
303
|
}
|
|
273
304
|
|
|
274
|
-
_updateFrameStats(frameStats
|
|
305
|
+
_updateFrameStats(frameStats) {
|
|
306
|
+
const stats = this.stats.frameStats;
|
|
307
|
+
|
|
275
308
|
if (!frameStats) return;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
309
|
+
|
|
310
|
+
stats.sent = frameStats.sent;
|
|
311
|
+
stats.nulled = frameStats.nulled;
|
|
312
|
+
stats.deficit = frameStats.deficit;
|
|
279
313
|
}
|
|
280
314
|
|
|
281
315
|
_handleReadyOp(payload) {
|
|
@@ -286,6 +320,7 @@ class Node {
|
|
|
286
320
|
|
|
287
321
|
this.sessionId = payload.sessionId;
|
|
288
322
|
this.rest.setSessionId(payload.sessionId);
|
|
323
|
+
this._updateHeaders();
|
|
289
324
|
this.aqua.emit("nodeConnect", this);
|
|
290
325
|
}
|
|
291
326
|
|
|
@@ -35,10 +35,10 @@ class Player extends EventEmitter {
|
|
|
35
35
|
this.filters = new Filters(this);
|
|
36
36
|
this.queue = new Queue();
|
|
37
37
|
|
|
38
|
-
this.volume = Math.
|
|
38
|
+
this.volume = Math.min(Math.max(options.defaultVolume ?? 100, 0), 200);
|
|
39
39
|
this.loop = Player.validModes.has(options.loop) ? options.loop : Player.LOOP_MODES.NONE;
|
|
40
|
-
this.shouldDeleteMessage =
|
|
41
|
-
this.leaveOnEnd =
|
|
40
|
+
this.shouldDeleteMessage = !!this.aqua.options.shouldDeleteMessage;
|
|
41
|
+
this.leaveOnEnd = !!this.aqua.options.leaveOnEnd;
|
|
42
42
|
|
|
43
43
|
this.previousTracks = new Array(50);
|
|
44
44
|
this.previousTracksIndex = 0;
|
|
@@ -55,6 +55,9 @@ class Player extends EventEmitter {
|
|
|
55
55
|
this.isAutoplayEnabled = false;
|
|
56
56
|
this.isAutoplay = false;
|
|
57
57
|
|
|
58
|
+
this._pendingUpdates = {};
|
|
59
|
+
this._updateTimeout = null;
|
|
60
|
+
|
|
58
61
|
this._boundHandlers = {
|
|
59
62
|
playerUpdate: this._handlePlayerUpdate.bind(this),
|
|
60
63
|
event: this._handleEvent.bind(this)
|
|
@@ -63,7 +66,7 @@ class Player extends EventEmitter {
|
|
|
63
66
|
this.on("playerUpdate", this._boundHandlers.playerUpdate);
|
|
64
67
|
this.on("event", this._boundHandlers.event);
|
|
65
68
|
|
|
66
|
-
this._dataStore =
|
|
69
|
+
this._dataStore = new Map();
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
get previous() {
|
|
@@ -75,8 +78,32 @@ class Player extends EventEmitter {
|
|
|
75
78
|
return this.current;
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
batchUpdatePlayer(data, immediate = false) {
|
|
82
|
+
this._pendingUpdates = { ...this._pendingUpdates, ...data };
|
|
83
|
+
|
|
84
|
+
if (this._updateTimeout) {
|
|
85
|
+
clearTimeout(this._updateTimeout);
|
|
86
|
+
this._updateTimeout = null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (immediate || data.track) {
|
|
90
|
+
const updates = this._pendingUpdates;
|
|
91
|
+
this._pendingUpdates = {};
|
|
92
|
+
return this.updatePlayer(updates);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this._updateTimeout = setTimeout(() => {
|
|
96
|
+
const updates = this._pendingUpdates;
|
|
97
|
+
this._pendingUpdates = {};
|
|
98
|
+
this.updatePlayer(updates);
|
|
99
|
+
this._updateTimeout = null;
|
|
100
|
+
}, 50);
|
|
101
|
+
|
|
102
|
+
return Promise.resolve();
|
|
103
|
+
}
|
|
104
|
+
|
|
78
105
|
async autoplay(player) {
|
|
79
|
-
if (!player) throw new Error("
|
|
106
|
+
if (!player) throw new Error("Player is undefined. const player = aqua.plaerers.get(guildId);");
|
|
80
107
|
if (!this.isAutoplayEnabled) {
|
|
81
108
|
this.aqua.emit("debug", this.guildId, "Autoplay is disabled.");
|
|
82
109
|
return this;
|
|
@@ -113,7 +140,8 @@ class Player extends EventEmitter {
|
|
|
113
140
|
const { query, source } = result;
|
|
114
141
|
const response = await this.aqua.resolve({ query, source, requester });
|
|
115
142
|
|
|
116
|
-
|
|
143
|
+
const failTypes = new Set(["error", "empty", "LOAD_FAILED", "NO_MATCHES"]);
|
|
144
|
+
if (!response?.tracks?.length || failTypes.has(response.loadType)) {
|
|
117
145
|
return this.stop();
|
|
118
146
|
}
|
|
119
147
|
|
|
@@ -145,12 +173,13 @@ class Player extends EventEmitter {
|
|
|
145
173
|
}
|
|
146
174
|
|
|
147
175
|
_handlePlayerUpdate({ state }) {
|
|
148
|
-
if (state)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
176
|
+
if (!state) return;
|
|
177
|
+
|
|
178
|
+
const { position, timestamp, ping } = state;
|
|
179
|
+
if (position !== undefined) this.position = position;
|
|
180
|
+
if (timestamp !== undefined) this.timestamp = timestamp;
|
|
181
|
+
if (ping !== undefined) this.ping = ping;
|
|
182
|
+
|
|
154
183
|
this.aqua.emit("playerUpdate", this, { state });
|
|
155
184
|
}
|
|
156
185
|
|
|
@@ -172,7 +201,7 @@ class Player extends EventEmitter {
|
|
|
172
201
|
this.position = 0;
|
|
173
202
|
|
|
174
203
|
this.aqua.emit("debug", this.guildId, `Playing track: ${this.current.track}`);
|
|
175
|
-
return this.
|
|
204
|
+
return this.batchUpdatePlayer({ track: { encoded: this.current.track } }, true);
|
|
176
205
|
}
|
|
177
206
|
|
|
178
207
|
connect({ voiceChannel, deaf = true, mute = false } = {}) {
|
|
@@ -192,6 +221,12 @@ class Player extends EventEmitter {
|
|
|
192
221
|
destroy() {
|
|
193
222
|
if (!this.connected) return this;
|
|
194
223
|
|
|
224
|
+
if (this._updateTimeout) {
|
|
225
|
+
clearTimeout(this._updateTimeout);
|
|
226
|
+
this._updateTimeout = null;
|
|
227
|
+
this._pendingUpdates = {};
|
|
228
|
+
}
|
|
229
|
+
|
|
195
230
|
this.disconnect();
|
|
196
231
|
if (this.nowPlayingMessage) {
|
|
197
232
|
this.nowPlayingMessage.delete().catch(() => {});
|
|
@@ -220,22 +255,36 @@ class Player extends EventEmitter {
|
|
|
220
255
|
pause(paused) {
|
|
221
256
|
if (this.paused === paused) return this;
|
|
222
257
|
this.paused = paused;
|
|
223
|
-
this.
|
|
258
|
+
this.batchUpdatePlayer({ paused });
|
|
224
259
|
return this;
|
|
225
260
|
}
|
|
226
261
|
|
|
262
|
+
async getLyrics(options = {}) {
|
|
263
|
+
const { query = null, useCurrentTrack = true } = options;
|
|
264
|
+
|
|
265
|
+
if (query) {
|
|
266
|
+
return this.nodes.rest.getLyrics({ track: { info: { title: query }, search: true } }) || null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (useCurrentTrack && this.playing) {
|
|
270
|
+
return this.nodes.rest.getLyrics({ track: { encoded: this.current.track, guild_id: this.guildId } }) || null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
227
276
|
async searchLyrics(query) {
|
|
228
|
-
return
|
|
277
|
+
return this.getLyrics({ query });
|
|
229
278
|
}
|
|
230
279
|
|
|
231
280
|
async lyrics() {
|
|
232
|
-
return this.
|
|
281
|
+
return this.getLyrics({ useCurrentTrack: true });
|
|
233
282
|
}
|
|
234
283
|
|
|
235
284
|
seek(position) {
|
|
236
285
|
if (!this.playing) return this;
|
|
237
286
|
this.position += position;
|
|
238
|
-
this.
|
|
287
|
+
this.batchUpdatePlayer({ position: this.position });
|
|
239
288
|
return this;
|
|
240
289
|
}
|
|
241
290
|
|
|
@@ -243,27 +292,27 @@ class Player extends EventEmitter {
|
|
|
243
292
|
if (!this.playing) return this;
|
|
244
293
|
this.playing = false;
|
|
245
294
|
this.position = 0;
|
|
246
|
-
this.
|
|
295
|
+
this.batchUpdatePlayer({ track: { encoded: null } }, true);
|
|
247
296
|
return this;
|
|
248
297
|
}
|
|
249
298
|
|
|
250
299
|
setVolume(volume) {
|
|
251
300
|
if (volume < 0 || volume > 200) throw new Error("Volume must be between 0 and 200.");
|
|
252
301
|
this.volume = volume;
|
|
253
|
-
this.
|
|
302
|
+
this.batchUpdatePlayer({ volume });
|
|
254
303
|
return this;
|
|
255
304
|
}
|
|
256
305
|
|
|
257
306
|
setLoop(mode) {
|
|
258
307
|
if (!Player.validModes.has(mode)) throw new Error("Loop mode must be 'none', 'track', or 'queue'.");
|
|
259
308
|
this.loop = mode;
|
|
260
|
-
this.
|
|
309
|
+
this.batchUpdatePlayer({ loop: mode });
|
|
261
310
|
return this;
|
|
262
311
|
}
|
|
263
312
|
|
|
264
313
|
setTextChannel(channel) {
|
|
265
314
|
this.textChannel = channel;
|
|
266
|
-
this.
|
|
315
|
+
this.batchUpdatePlayer({ text_channel: channel });
|
|
267
316
|
return this;
|
|
268
317
|
}
|
|
269
318
|
|
|
@@ -294,11 +343,14 @@ class Player extends EventEmitter {
|
|
|
294
343
|
}
|
|
295
344
|
|
|
296
345
|
shuffle() {
|
|
297
|
-
const
|
|
298
|
-
|
|
346
|
+
const queue = this.queue;
|
|
347
|
+
const length = queue.length;
|
|
348
|
+
|
|
349
|
+
for (let i = length - 1; i > 0; i--) {
|
|
299
350
|
const j = Math.floor(Math.random() * (i + 1));
|
|
300
351
|
[queue[i], queue[j]] = [queue[j], queue[i]];
|
|
301
352
|
}
|
|
353
|
+
|
|
302
354
|
return this;
|
|
303
355
|
}
|
|
304
356
|
|
|
@@ -327,14 +379,16 @@ class Player extends EventEmitter {
|
|
|
327
379
|
if (this.shouldDeleteMessage && this.nowPlayingMessage) {
|
|
328
380
|
try {
|
|
329
381
|
await this.nowPlayingMessage.delete();
|
|
330
|
-
this.nowPlayingMessage = null;
|
|
331
382
|
} catch (error) {
|
|
332
383
|
console.error("Error deleting now playing message:", error);
|
|
384
|
+
} finally {
|
|
385
|
+
this.nowPlayingMessage = null;
|
|
333
386
|
}
|
|
334
387
|
}
|
|
335
388
|
|
|
336
389
|
const reason = payload.reason;
|
|
337
|
-
|
|
390
|
+
const failureReasons = new Set(["LOAD_FAILED", "CLEANUP"]);
|
|
391
|
+
if (failureReasons.has(reason)) {
|
|
338
392
|
if (!player.queue.length) {
|
|
339
393
|
this.clearData();
|
|
340
394
|
this.aqua.emit("queueEnd", player);
|
|
@@ -345,10 +399,13 @@ class Player extends EventEmitter {
|
|
|
345
399
|
return;
|
|
346
400
|
}
|
|
347
401
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
402
|
+
switch (this.loop) {
|
|
403
|
+
case Player.LOOP_MODES.TRACK:
|
|
404
|
+
player.queue.unshift(track);
|
|
405
|
+
break;
|
|
406
|
+
case Player.LOOP_MODES.QUEUE:
|
|
407
|
+
player.queue.push(track);
|
|
408
|
+
break;
|
|
352
409
|
}
|
|
353
410
|
|
|
354
411
|
if (player.queue.isEmpty()) {
|
|
@@ -381,7 +438,8 @@ class Player extends EventEmitter {
|
|
|
381
438
|
async socketClosed(player, payload) {
|
|
382
439
|
const { code, guildId } = payload || {};
|
|
383
440
|
|
|
384
|
-
|
|
441
|
+
const reconnectCodes = new Set([4015, 4009]);
|
|
442
|
+
if (reconnectCodes.has(code)) {
|
|
385
443
|
this.send({
|
|
386
444
|
guild_id: guildId,
|
|
387
445
|
channel_id: this.voiceChannel,
|
|
@@ -400,17 +458,16 @@ class Player extends EventEmitter {
|
|
|
400
458
|
}
|
|
401
459
|
|
|
402
460
|
set(key, value) {
|
|
403
|
-
if (!this._dataStore) this._dataStore = new Map();
|
|
404
461
|
this._dataStore.set(key, value);
|
|
405
462
|
}
|
|
406
463
|
|
|
407
464
|
get(key) {
|
|
408
|
-
return this._dataStore
|
|
465
|
+
return this._dataStore.get(key);
|
|
409
466
|
}
|
|
410
467
|
|
|
411
468
|
clearData() {
|
|
412
469
|
if (this.previousTracks) this.previousTracksCount = 0;
|
|
413
|
-
this._dataStore
|
|
470
|
+
this._dataStore.clear();
|
|
414
471
|
return this;
|
|
415
472
|
}
|
|
416
473
|
|
|
@@ -38,23 +38,32 @@ class Queue extends Array {
|
|
|
38
38
|
remove(track) {
|
|
39
39
|
const index = this.indexOf(track);
|
|
40
40
|
if (index !== -1) {
|
|
41
|
-
|
|
41
|
+
if (index === this.length - 1) {
|
|
42
|
+
this.pop();
|
|
43
|
+
} else {
|
|
44
|
+
this.splice(index, 1);
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
42
47
|
}
|
|
48
|
+
return false;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
// Clear all tracks from the queue
|
|
46
52
|
clear() {
|
|
47
|
-
this.length = 0;
|
|
53
|
+
this.length = 0;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
// Shuffle the tracks in the queue
|
|
51
57
|
shuffle() {
|
|
52
|
-
|
|
58
|
+
const length = this.length;
|
|
59
|
+
for (let i = length - 1; i > 0; i--) {
|
|
53
60
|
const j = Math.floor(Math.random() * (i + 1));
|
|
54
|
-
|
|
61
|
+
if (i !== j) {
|
|
62
|
+
[this[i], this[j]] = [this[j], this[i]];
|
|
63
|
+
}
|
|
55
64
|
}
|
|
65
|
+
return this;
|
|
56
66
|
}
|
|
57
|
-
|
|
58
67
|
// Peek at the element at the front of the queue without removing it
|
|
59
68
|
peek() {
|
|
60
69
|
return this.first;
|
|
@@ -62,7 +71,7 @@ class Queue extends Array {
|
|
|
62
71
|
|
|
63
72
|
// Get all tracks in the queue as an array
|
|
64
73
|
toArray() {
|
|
65
|
-
return
|
|
74
|
+
return this.slice();
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/**
|
|
@@ -71,9 +80,8 @@ class Queue extends Array {
|
|
|
71
80
|
* @returns {*} The track at the specified index or null if out of bounds.
|
|
72
81
|
*/
|
|
73
82
|
at(index) {
|
|
74
|
-
return
|
|
83
|
+
return (index >= 0 && index < this.length) ? this[index] : null;
|
|
75
84
|
}
|
|
76
|
-
|
|
77
85
|
// Remove the first track from the queue
|
|
78
86
|
dequeue() {
|
|
79
87
|
return this.shift(); // Removes and returns the first element
|
package/build/structures/Rest.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
|
|
2
3
|
const https = require("https");
|
|
3
4
|
const http = require("http");
|
|
4
5
|
|
|
5
6
|
let http2;
|
|
7
|
+
|
|
6
8
|
try {
|
|
7
9
|
http2 = require("http2");
|
|
8
10
|
} catch (e) {
|
|
@@ -20,14 +22,20 @@ class Rest {
|
|
|
20
22
|
};
|
|
21
23
|
this.secure = secure;
|
|
22
24
|
this.timeout = timeout;
|
|
23
|
-
|
|
24
|
-
this.client = secure ?
|
|
25
|
+
|
|
26
|
+
this.client = secure ? http2 || https : http;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
setSessionId(sessionId) {
|
|
28
30
|
this.sessionId = sessionId;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
validateSessionId() {
|
|
34
|
+
if (!this.sessionId) {
|
|
35
|
+
throw new Error("Session ID is required but not set.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
async makeRequest(method, endpoint, body = null) {
|
|
32
40
|
const url = `${this.baseUrl}${endpoint}`;
|
|
33
41
|
const options = {
|
|
@@ -38,36 +46,36 @@ class Rest {
|
|
|
38
46
|
|
|
39
47
|
return new Promise((resolve, reject) => {
|
|
40
48
|
const req = this.client.request(url, options, (res) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
let data = "";
|
|
50
|
+
|
|
51
|
+
res.setEncoding("utf8");
|
|
52
|
+
|
|
45
53
|
res.on("data", (chunk) => {
|
|
46
54
|
data += chunk;
|
|
47
55
|
});
|
|
48
|
-
|
|
56
|
+
|
|
49
57
|
res.on("end", () => {
|
|
50
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
58
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
51
59
|
if (!data) {
|
|
52
60
|
resolve(null);
|
|
53
61
|
return;
|
|
54
62
|
}
|
|
55
|
-
|
|
56
63
|
try {
|
|
57
64
|
resolve(JSON.parse(data));
|
|
58
|
-
} catch (
|
|
59
|
-
reject(new Error(`Failed to parse response: ${
|
|
65
|
+
} catch (err) {
|
|
66
|
+
reject(new Error(`Failed to parse response: ${err.message}`));
|
|
60
67
|
}
|
|
61
68
|
} else {
|
|
62
|
-
|
|
69
|
+
const errorMessage = `Request failed with status ${res.statusCode}: ${res.statusMessage || "Unknown error"}`;
|
|
70
|
+
reject(new Error(errorMessage));
|
|
63
71
|
}
|
|
64
72
|
});
|
|
65
73
|
});
|
|
66
74
|
|
|
67
|
-
req.on("error", (
|
|
75
|
+
req.on("error", (err) => reject(new Error(`Request failed (${method} ${url}): ${err.message}`)));
|
|
68
76
|
req.on("timeout", () => {
|
|
69
77
|
req.destroy();
|
|
70
|
-
reject(new Error(`Request
|
|
78
|
+
reject(new Error(`Request timed out after ${this.timeout}ms (${method} ${url})`));
|
|
71
79
|
});
|
|
72
80
|
|
|
73
81
|
if (body) {
|
|
@@ -77,79 +85,86 @@ class Rest {
|
|
|
77
85
|
});
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
validateSessionId() {
|
|
81
|
-
if (!this.sessionId) throw new Error("Session ID is required but not set.");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
88
|
async updatePlayer({ guildId, data }) {
|
|
85
|
-
if (data.track
|
|
86
|
-
throw new Error("
|
|
89
|
+
if (data.track?.encoded && data.track?.identifier) {
|
|
90
|
+
throw new Error("You cannot provide both 'encoded' and 'identifier' for a track.");
|
|
87
91
|
}
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
this.validateSessionId();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
data
|
|
94
|
-
);
|
|
94
|
+
|
|
95
|
+
const endpoint = `/${this.version}/sessions/${this.sessionId}/players/${guildId}?noReplace=false`;
|
|
96
|
+
return this.makeRequest("PATCH", endpoint, data);
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
async getPlayers() {
|
|
98
100
|
this.validateSessionId();
|
|
99
|
-
|
|
101
|
+
const endpoint = `/${this.version}/sessions/${this.sessionId}/players`;
|
|
102
|
+
return this.makeRequest("GET", endpoint);
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
async destroyPlayer(guildId) {
|
|
103
106
|
this.validateSessionId();
|
|
104
|
-
|
|
107
|
+
const endpoint = `/${this.version}/sessions/${this.sessionId}/players/${guildId}`;
|
|
108
|
+
return this.makeRequest("DELETE", endpoint);
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
async getTracks(identifier) {
|
|
108
|
-
|
|
112
|
+
const endpoint = `/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`;
|
|
113
|
+
return this.makeRequest("GET", endpoint);
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
async decodeTrack(track) {
|
|
112
|
-
|
|
117
|
+
const endpoint = `/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`;
|
|
118
|
+
return this.makeRequest("GET", endpoint);
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
async decodeTracks(tracks) {
|
|
116
|
-
|
|
122
|
+
const endpoint = `/${this.version}/decodetracks`;
|
|
123
|
+
return this.makeRequest("POST", endpoint, tracks);
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
async getStats() {
|
|
120
|
-
|
|
127
|
+
const endpoint = `/${this.version}/stats`;
|
|
128
|
+
return this.makeRequest("GET", endpoint);
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
async getInfo() {
|
|
124
|
-
|
|
132
|
+
const endpoint = `/${this.version}/info`;
|
|
133
|
+
return this.makeRequest("GET", endpoint);
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
async getRoutePlannerStatus() {
|
|
128
|
-
|
|
137
|
+
const endpoint = `/${this.version}/routeplanner/status`;
|
|
138
|
+
return this.makeRequest("GET", endpoint);
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
async getRoutePlannerAddress(address) {
|
|
132
|
-
|
|
142
|
+
const endpoint = `/${this.version}/routeplanner/free/address`;
|
|
143
|
+
return this.makeRequest("POST", endpoint, { address });
|
|
133
144
|
}
|
|
134
145
|
|
|
135
146
|
async getLyrics({ track }) {
|
|
136
147
|
if (!track) return null;
|
|
137
|
-
|
|
148
|
+
|
|
138
149
|
try {
|
|
139
150
|
if (track.search) {
|
|
140
151
|
const query = encodeURIComponent(track.info.title);
|
|
141
152
|
try {
|
|
142
|
-
const res = await this.makeRequest(
|
|
153
|
+
const res = await this.makeRequest(
|
|
154
|
+
"GET",
|
|
155
|
+
`/${this.version}/lyrics/search?query=${query}&source=genius`
|
|
156
|
+
);
|
|
143
157
|
if (res) return res;
|
|
144
|
-
} catch (
|
|
158
|
+
} catch (_) {
|
|
159
|
+
// Silently handle any errors.
|
|
160
|
+
}
|
|
145
161
|
} else {
|
|
146
162
|
this.validateSessionId();
|
|
147
163
|
return await this.makeRequest(
|
|
148
|
-
"GET",
|
|
164
|
+
"GET",
|
|
149
165
|
`/${this.version}/sessions/${this.sessionId}/players/${track.guild_id}/track/lyrics?skipTrackSource=false`
|
|
150
166
|
);
|
|
151
167
|
}
|
|
152
|
-
|
|
153
168
|
} catch (error) {
|
|
154
169
|
console.error("Failed to fetch lyrics:", error.message);
|
|
155
170
|
return null;
|
|
@@ -10,7 +10,8 @@ class Track {
|
|
|
10
10
|
this.identifier = info.identifier || '';
|
|
11
11
|
this.isSeekable = Boolean(info.isSeekable);
|
|
12
12
|
this.author = info.author || '';
|
|
13
|
-
this.length = info.length
|
|
13
|
+
this.length = info.length || 0;
|
|
14
|
+
this.duration = info.length || 0;
|
|
14
15
|
this.isStream = Boolean(info.isStream);
|
|
15
16
|
this.title = info.title || '';
|
|
16
17
|
this.uri = info.uri || '';
|