aqualink 2.5.0 → 2.6.1

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.
@@ -3,7 +3,7 @@ const Node = require("./Node");
3
3
  const Player = require("./Player");
4
4
  const Track = require("./Track");
5
5
  const { version: pkgVersion } = require("../../package.json");
6
- const { EventEmitter } = require('eventemitter3');
6
+ const { EventEmitter } = require('tseep');
7
7
 
8
8
  const URL_REGEX = /^https?:\/\//;
9
9
  const DEFAULT_OPTIONS = Object.freeze({
@@ -199,7 +199,7 @@ class Aqua extends EventEmitter {
199
199
  const player = new Player(this, node, options);
200
200
  this.players.set(options.guildId, player);
201
201
 
202
- player.once("destroy", () => {
202
+ player.on("destroy", () => {
203
203
  this.players.delete(options.guildId);
204
204
  });
205
205
 
@@ -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,
@@ -354,24 +350,13 @@ class Aqua extends EventEmitter {
354
350
  try {
355
351
  if (player.connection) {
356
352
  try {
357
- await player.connection.disconnect();
353
+ await player.destroy()
358
354
  player.connection = null;
359
355
  } catch (error) {
360
356
  console.error(`Error disconnecting player connection: ${error.message}`);
361
357
  }
362
358
  }
363
359
 
364
- player.queue?.clear();
365
-
366
- if (typeof player.stop === 'function') {
367
- try {
368
- await player.stop();
369
- } catch (error) {
370
- console.error(`Error stopping player: ${error.message}`);
371
- }
372
- }
373
-
374
- player.removeAllListeners();
375
360
  this.players.delete(player.guildId);
376
361
  this.emit("playerCleanup", player.guildId);
377
362
  } catch (error) {
@@ -2,88 +2,91 @@
2
2
 
3
3
  class Connection {
4
4
  constructor(player) {
5
- this.playerRef = new WeakRef(player);
6
-
5
+ this.player = player;
6
+ this.voiceChannel = player.voiceChannel;
7
+ this.guildId = player.guildId;
8
+ this.aqua = player.aqua;
9
+ this.nodes = player.nodes;
10
+
7
11
  this.sessionId = null;
8
12
  this.endpoint = null;
9
13
  this.token = null;
10
-
11
14
  this.region = null;
12
15
  this.selfDeaf = false;
13
16
  this.selfMute = false;
14
- this.voiceChannel = player.voiceChannel;
15
- this.guildId = player.guildId;
16
-
17
- this.aqua = player.aqua;
18
- this.nodes = player.nodes;
17
+
19
18
  }
20
19
 
21
20
  setServerUpdate(data) {
22
- if (!data?.endpoint) return;
23
-
21
+ if (!data || !data.endpoint) return;
22
+
24
23
  const { endpoint, token } = data;
25
- const dotIndex = endpoint.indexOf('.');
26
-
27
- if (dotIndex === -1) return;
28
- const newRegion = endpoint.substring(0, dotIndex);
29
-
24
+
25
+ const [newRegion] = endpoint.split('.');
26
+ if (!newRegion) return;
27
+
30
28
  if (this.region !== newRegion) {
31
- const prevRegion = this.region;
32
- [this.endpoint, this.token, this.region] = [endpoint, token, newRegion];
33
-
34
- if (this.aqua.listenerCount('debug')) {
29
+ this.endpoint = endpoint;
30
+ this.token = token;
31
+ this.region = newRegion;
32
+
35
33
  this.aqua.emit(
36
34
  "debug",
37
35
  `[Player ${this.guildId} - CONNECTION] Voice Server: ${
38
- prevRegion ? `Changed from ${prevRegion} to ${newRegion}` : newRegion
36
+ this.region ? `Changed from ${this.region} to ${newRegion}` : newRegion
39
37
  }`
40
38
  );
41
- }
42
39
 
40
+
43
41
  this._updatePlayerVoiceData();
44
42
  }
45
43
  }
46
44
 
47
45
  setStateUpdate(data) {
48
- const { channel_id, session_id, self_deaf, self_mute } = data || {};
49
-
46
+ if (!data) {
47
+ this.player?.destroy();
48
+ return;
49
+ }
50
+
51
+ const { channel_id, session_id, self_deaf, self_mute } = data;
52
+
50
53
  if (!channel_id || !session_id) {
51
- this.playerRef.deref()?.destroy();
54
+ this.player?.destroy();
52
55
  return;
53
56
  }
54
-
57
+
55
58
  if (this.voiceChannel !== channel_id) {
56
59
  this.aqua.emit("playerMove", this.voiceChannel, channel_id);
57
60
  this.voiceChannel = channel_id;
58
61
  }
59
-
62
+
60
63
  this.selfDeaf = !!self_deaf;
61
64
  this.selfMute = !!self_mute;
62
65
  this.sessionId = session_id;
63
66
  }
64
67
 
65
- async _updatePlayerVoiceData() {
66
- const player = this.playerRef.deref();
67
- if (!player) return;
68
+ _updatePlayerVoiceData() {
69
+ if (!this.player) return;
68
70
 
71
+ const voiceData = {
72
+ sessionId: this.sessionId,
73
+ endpoint: this.endpoint,
74
+ token: this.token
75
+ };
69
76
  try {
70
- await this.nodes.rest.updatePlayer({
77
+ this.nodes.rest.updatePlayer({
71
78
  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
+ data: {
80
+ voice: voiceData,
81
+ volume: this.player.volume
79
82
  }
80
83
  });
81
84
  } catch (error) {
82
- if (this.aqua.listenerCount('apiError')) {
83
- this.aqua.emit("apiError", "updatePlayer", {
85
+ if (this.aqua.listenerCount('apiError') > 0) {
86
+ this.aqua.emit("apiError", "updatePlayer", {
84
87
  error,
85
88
  guildId: this.guildId,
86
- voiceData: { ...this }
89
+ voiceData
87
90
  });
88
91
  }
89
92
  }
@@ -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;