aqualink 2.5.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.
@@ -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(requestNode.rest, query);
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") return this.nodeMap.get(nodes) || this.leastUsedNodes[0];
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(rest, query) {
258
- try {
259
- const ytEndpoint = `/v4/loadtracks?identifier=https://www.youtube.com/watch?v=${query}`;
260
- const youtubeResponse = await rest.makeRequest("GET", ytEndpoint);
261
-
262
- if (!["empty", "NO_MATCHES"].includes(youtubeResponse.loadType)) {
263
- return youtubeResponse;
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.playerRef = new WeakRef(player);
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
- this.selfDeaf = false;
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?.endpoint) return;
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
- const prevRegion = this.region;
32
- [this.endpoint, this.token, this.region] = [endpoint, token, newRegion];
34
+ this.endpoint = endpoint;
35
+ this.token = token;
36
+ this.region = newRegion;
33
37
 
34
- if (this.aqua.listenerCount('debug')) {
38
+ if (this.hasDebugListeners) {
35
39
  this.aqua.emit(
36
40
  "debug",
37
41
  `[Player ${this.guildId} - CONNECTION] Voice Server: ${
38
- prevRegion ? `Changed from ${prevRegion} to ${newRegion}` : newRegion
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
- const { channel_id, session_id, self_deaf, self_mute } = data || {};
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.playerRef.deref()?.destroy();
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 = !!self_deaf;
61
- this.selfMute = !!self_mute;
66
+ this.selfDeaf = Boolean(self_deaf);
67
+ this.selfMute = Boolean(self_mute);
62
68
  this.sessionId = session_id;
63
69
  }
64
70
 
65
- async _updatePlayerVoiceData() {
66
- const player = this.playerRef.deref();
67
- if (!player) return;
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
- try {
70
- await this.nodes.rest.updatePlayer({
71
- guildId: this.guildId,
72
- data: {
73
- voice: {
74
- sessionId: this.sessionId,
75
- endpoint: this.endpoint,
76
- token: this.token
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: { ...this }
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._filterDataTemplate = {
23
- volume: this.volume,
24
- equalizer: this.equalizer,
25
- karaoke: null,
26
- timescale: null,
27
- tremolo: null,
28
- vibrato: null,
29
- rotation: null,
30
- distortion: null,
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, defaults) {
37
- if (!enabled) {
38
- this[filterName] = null;
39
- return this.updateFilters();
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
- const filterObj = {};
43
- for (const [key, defaultValue] of Object.entries(defaults)) {
44
- filterObj[key] = options[key] !== undefined ? options[key] : defaultValue;
51
+ if (this._updateTimeout) {
52
+ clearTimeout(this._updateTimeout);
45
53
  }
46
54
 
47
- this[filterName] = filterObj;
48
- return this.updateFilters();
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.updateFilters();
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 num = (value - 1) * (1.25 / 9) - 0.25;
105
+ this.presets.bassboost = value;
106
+ const gain = (value - 1) * (1.25 / 9) - 0.25;
132
107
 
133
- const eq = new Array(13);
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.volume = 1;
163
- this.equalizer = [];
164
- this.karaoke = null;
165
- this.timescale = null;
166
- this.tremolo = null;
167
- this.vibrato = null;
168
- this.rotation = null;
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._filterDataTemplate.volume = 1;
179
- this._filterDataTemplate.equalizer = [];
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
- const filterData = {
187
- ...this._filterDataTemplate,
188
- volume: this.volume,
189
- equalizer: this.equalizer,
190
- karaoke: this.karaoke,
191
- timescale: this.timescale,
192
- tremolo: this.tremolo,
193
- vibrato: this.vibrato,
194
- rotation: this.rotation,
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: filterData }
164
+ data: { filters: { ...this.filters } }
205
165
  });
206
166
 
207
167
  return this;
@@ -37,8 +37,8 @@ class Player extends EventEmitter {
37
37
 
38
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 = Boolean(options.shouldDeleteMessage);
41
- this.leaveOnEnd = Boolean(options.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;
@@ -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
- this.splice(index, 1);
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; // More efficient memory handling
53
+ this.length = 0;
48
54
  }
49
55
 
50
56
  // Shuffle the tracks in the queue
51
57
  shuffle() {
52
- for (let i = this.length - 1; i > 0; i--) {
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
- [this[i], this[j]] = [this[j], this[i]];
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 [...this]; // Create a shallow copy of the queue
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 this[index] || null; // Return null if index is out of bounds
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
@@ -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 | 0;
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 || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "An Lavalink client, focused in pure performance and features",
5
5
  "main": "build/index.js",
6
6
  "types": "index.d.ts",