magmastream 2.10.3-alpha.3 → 2.10.3-alpha.5

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.
@@ -250,10 +250,14 @@ export declare enum AvailableFilters {
250
250
  Party = "party",
251
251
  Pop = "pop",
252
252
  Radio = "radio",
253
+ PluginFilters = "pluginFilters",
254
+ SetChannelMix = "setChannelMix",
253
255
  SetDistortion = "setDistortion",
254
256
  SetKaraoke = "setKaraoke",
257
+ SetLowPass = "setLowPass",
255
258
  SetRotation = "setRotation",
256
259
  SetTimescale = "setTimescale",
260
+ SetTremolo = "setTremolo",
257
261
  Slowmo = "slowmo",
258
262
  Soft = "soft",
259
263
  TrebleBass = "trebleBass",
@@ -267,10 +267,14 @@ var AvailableFilters;
267
267
  AvailableFilters["Party"] = "party";
268
268
  AvailableFilters["Pop"] = "pop";
269
269
  AvailableFilters["Radio"] = "radio";
270
+ AvailableFilters["PluginFilters"] = "pluginFilters";
271
+ AvailableFilters["SetChannelMix"] = "setChannelMix";
270
272
  AvailableFilters["SetDistortion"] = "setDistortion";
271
273
  AvailableFilters["SetKaraoke"] = "setKaraoke";
274
+ AvailableFilters["SetLowPass"] = "setLowPass";
272
275
  AvailableFilters["SetRotation"] = "setRotation";
273
276
  AvailableFilters["SetTimescale"] = "setTimescale";
277
+ AvailableFilters["SetTremolo"] = "setTremolo";
274
278
  AvailableFilters["Slowmo"] = "slowmo";
275
279
  AvailableFilters["Soft"] = "soft";
276
280
  AvailableFilters["TrebleBass"] = "trebleBass";
@@ -2,14 +2,18 @@ import { Band } from "../utils/filtersEqualizers";
2
2
  import { AvailableFilters } from "./Enums";
3
3
  import { Manager } from "./Manager";
4
4
  import { Player } from "./Player";
5
- import { DistortionOptions, KaraokeOptions, ReverbOptions, RotationOptions, TimescaleOptions, VibratoOptions } from "./Types";
5
+ import { ChannelMixOptions, DistortionOptions, KaraokeOptions, LowPassOptions, ReverbOptions, RotationOptions, TimescaleOptions, TremoloOptions, VibratoOptions } from "./Types";
6
6
  export declare class Filters {
7
7
  distortion: DistortionOptions | null;
8
8
  equalizer: Band[];
9
9
  karaoke: KaraokeOptions | null;
10
10
  rotation: RotationOptions | null;
11
11
  timescale: TimescaleOptions | null;
12
+ tremoloOptions: TremoloOptions | null;
12
13
  vibrato: VibratoOptions | null;
14
+ channelMix: ChannelMixOptions | null;
15
+ lowPass: LowPassOptions | null;
16
+ pluginFilters: Record<string, unknown>;
13
17
  reverb: ReverbOptions | null;
14
18
  volume: number;
15
19
  bassBoostlevel: number;
@@ -112,6 +116,34 @@ export declare class Filters {
112
116
  * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
113
117
  */
114
118
  setVibrato(vibrato?: VibratoOptions): Promise<this>;
119
+ /**
120
+ * Sets the tremolo options on the audio.
121
+ *
122
+ * @param {TremoloOptions} [tremolo] - The tremolo settings to apply (frequency, depth).
123
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
124
+ */
125
+ setTremolo(tremolo?: TremoloOptions): Promise<this>;
126
+ /**
127
+ * Sets the stereo channel mix options on the audio.
128
+ *
129
+ * @param {ChannelMixOptions} [channelMix] - The channel mix settings to apply.
130
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
131
+ */
132
+ setChannelMix(channelMix?: ChannelMixOptions): Promise<this>;
133
+ /**
134
+ * Sets the low pass filter options on the audio.
135
+ *
136
+ * @param {LowPassOptions} [lowPass] - The low pass settings to apply.
137
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
138
+ */
139
+ setLowPass(lowPass?: LowPassOptions): Promise<this>;
140
+ /**
141
+ * Sets plugin-provided filter options on the audio.
142
+ *
143
+ * @param {Record<string, unknown>} [pluginFilters] - Plugin filter settings keyed by plugin filter name.
144
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
145
+ */
146
+ setPluginFilters(pluginFilters?: Record<string, unknown>): Promise<this>;
115
147
  /**
116
148
  * Sets the own rotation options effect to the audio.
117
149
  *
@@ -10,7 +10,11 @@ class Filters {
10
10
  karaoke;
11
11
  rotation;
12
12
  timescale;
13
+ tremoloOptions;
13
14
  vibrato;
15
+ channelMix;
16
+ lowPass;
17
+ pluginFilters;
14
18
  reverb;
15
19
  volume;
16
20
  bassBoostlevel;
@@ -23,7 +27,11 @@ class Filters {
23
27
  this.karaoke = null;
24
28
  this.rotation = null;
25
29
  this.timescale = null;
30
+ this.tremoloOptions = null;
26
31
  this.vibrato = null;
32
+ this.channelMix = null;
33
+ this.lowPass = null;
34
+ this.pluginFilters = {};
27
35
  this.reverb = null;
28
36
  this.volume = 1.0;
29
37
  this.bassBoostlevel = 0;
@@ -48,7 +56,7 @@ class Filters {
48
56
  * of the Filters class for method chaining.
49
57
  */
50
58
  async updateFilters() {
51
- const { distortion, equalizer, karaoke, rotation, timescale, vibrato, volume } = this;
59
+ const { channelMix, distortion, equalizer, karaoke, lowPass, pluginFilters, rotation, timescale, tremoloOptions, vibrato, volume } = this;
52
60
  try {
53
61
  await this.player.node.rest.updatePlayer({
54
62
  data: {
@@ -56,10 +64,14 @@ class Filters {
56
64
  distortion,
57
65
  equalizer,
58
66
  karaoke,
67
+ lowPass,
68
+ pluginFilters,
59
69
  rotation,
60
70
  timescale,
71
+ tremolo: tremoloOptions,
61
72
  vibrato,
62
73
  volume,
74
+ channelMix,
63
75
  },
64
76
  },
65
77
  guildId: this.player.guildId,
@@ -146,7 +158,11 @@ class Filters {
146
158
  await this.setKaraoke(null);
147
159
  await this.setRotation(null);
148
160
  await this.setTimescale(null);
161
+ await this.setTremolo(null);
149
162
  await this.setVibrato(null);
163
+ await this.setChannelMix(null);
164
+ await this.setLowPass(null);
165
+ await this.setPluginFilters({});
150
166
  await this.updateFilters();
151
167
  this.emitPlayersTasteUpdate(oldPlayer);
152
168
  return this;
@@ -215,6 +231,59 @@ class Filters {
215
231
  this.emitPlayersTasteUpdate(oldPlayer);
216
232
  return this;
217
233
  }
234
+ /**
235
+ * Sets the tremolo options on the audio.
236
+ *
237
+ * @param {TremoloOptions} [tremolo] - The tremolo settings to apply (frequency, depth).
238
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
239
+ */
240
+ async setTremolo(tremolo) {
241
+ const oldPlayer = { ...this };
242
+ await this.applyFilter({ property: "tremoloOptions", value: tremolo ?? null });
243
+ this.setFilterStatus(Enums_1.AvailableFilters.SetTremolo, !!tremolo);
244
+ this.emitPlayersTasteUpdate(oldPlayer);
245
+ return this;
246
+ }
247
+ /**
248
+ * Sets the stereo channel mix options on the audio.
249
+ *
250
+ * @param {ChannelMixOptions} [channelMix] - The channel mix settings to apply.
251
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
252
+ */
253
+ async setChannelMix(channelMix) {
254
+ const oldPlayer = { ...this };
255
+ await this.applyFilter({ property: "channelMix", value: channelMix ?? null });
256
+ this.setFilterStatus(Enums_1.AvailableFilters.SetChannelMix, !!channelMix);
257
+ this.emitPlayersTasteUpdate(oldPlayer);
258
+ return this;
259
+ }
260
+ /**
261
+ * Sets the low pass filter options on the audio.
262
+ *
263
+ * @param {LowPassOptions} [lowPass] - The low pass settings to apply.
264
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
265
+ */
266
+ async setLowPass(lowPass) {
267
+ const oldPlayer = { ...this };
268
+ await this.applyFilter({ property: "lowPass", value: lowPass ?? null });
269
+ this.setFilterStatus(Enums_1.AvailableFilters.SetLowPass, !!lowPass);
270
+ this.emitPlayersTasteUpdate(oldPlayer);
271
+ return this;
272
+ }
273
+ /**
274
+ * Sets plugin-provided filter options on the audio.
275
+ *
276
+ * @param {Record<string, unknown>} [pluginFilters] - Plugin filter settings keyed by plugin filter name.
277
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
278
+ */
279
+ async setPluginFilters(pluginFilters) {
280
+ const oldPlayer = { ...this };
281
+ const nextPluginFilters = pluginFilters ?? {};
282
+ await this.applyFilter({ property: "pluginFilters", value: nextPluginFilters });
283
+ this.setFilterStatus(Enums_1.AvailableFilters.PluginFilters, Object.keys(nextPluginFilters).length > 0);
284
+ this.emitPlayersTasteUpdate(oldPlayer);
285
+ return this;
286
+ }
218
287
  /**
219
288
  * Sets the own rotation options effect to the audio.
220
289
  *
@@ -557,7 +626,7 @@ class Filters {
557
626
  */
558
627
  async tremolo(status) {
559
628
  const oldPlayer = { ...this };
560
- await this.applyFilter({ property: "vibrato", value: status ? { frequency: 5, depth: 0.5 } : null });
629
+ await this.applyFilter({ property: "tremoloOptions", value: status ? { frequency: 5, depth: 0.5 } : null });
561
630
  this.setFilterStatus(Enums_1.AvailableFilters.Tremolo, status);
562
631
  this.emitPlayersTasteUpdate(oldPlayer);
563
632
  return this;
@@ -250,7 +250,8 @@ class Manager extends events_1.EventEmitter {
250
250
  tracks,
251
251
  playlist: {
252
252
  name: playlistData.info.name,
253
- playlistInfo: playlistData.pluginInfo,
253
+ playlistInfo: playlistData.info,
254
+ pluginInfo: playlistData.pluginInfo,
254
255
  requester: requester,
255
256
  tracks,
256
257
  duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
@@ -259,11 +260,11 @@ class Manager extends events_1.EventEmitter {
259
260
  break;
260
261
  }
261
262
  default:
262
- result = { loadType: lavalinkResponse.loadType };
263
+ result = { loadType: lavalinkResponse.loadType, tracks: [] };
263
264
  }
264
265
  if (this.options.normalizeYouTubeTitles && "tracks" in result) {
265
266
  const processTrack = (track) => {
266
- if (!YOUTUBE_URL_PATTERN.test(track.uri))
267
+ if (!track.uri || !YOUTUBE_URL_PATTERN.test(track.uri))
267
268
  return track;
268
269
  const { cleanTitle, cleanAuthor } = this.parseYouTubeTitle(track.title, track.author);
269
270
  track.title = cleanTitle;
@@ -406,23 +407,21 @@ class Manager extends events_1.EventEmitter {
406
407
  */
407
408
  async decodeTracks(tracks) {
408
409
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Decoding tracks: ${Utils_1.JSONUtils.safe(tracks, 2)}`);
409
- return new Promise(async (resolve, reject) => {
410
- const node = this.useableNode;
411
- if (!node) {
412
- throw new MagmastreamError_1.MagmaStreamError({
413
- code: Enums_1.MagmaStreamErrorCode.MANAGER_NO_NODES,
414
- message: "No available nodes to decode tracks.",
415
- });
416
- }
417
- const decodedTrackData = (await node.rest.post("/v4/decodetracks", Utils_1.JSONUtils.safe(tracks, 2)).catch((err) => reject(err)));
418
- if (!decodedTrackData) {
419
- throw new MagmastreamError_1.MagmaStreamError({
420
- code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
421
- message: "No decoded tracks returned from node.",
422
- });
423
- }
424
- return resolve(decodedTrackData);
425
- });
410
+ const node = this.useableNode;
411
+ if (!node) {
412
+ throw new MagmastreamError_1.MagmaStreamError({
413
+ code: Enums_1.MagmaStreamErrorCode.MANAGER_NO_NODES,
414
+ message: "No available nodes to decode tracks.",
415
+ });
416
+ }
417
+ const decodedTrackData = await node.rest.decodeTracks(tracks);
418
+ if (!decodedTrackData) {
419
+ throw new MagmastreamError_1.MagmaStreamError({
420
+ code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
421
+ message: "No decoded tracks returned from node.",
422
+ });
423
+ }
424
+ return decodedTrackData;
426
425
  }
427
426
  /**
428
427
  * Decodes a base64 encoded track and returns a TrackData.
@@ -510,7 +509,7 @@ class Manager extends events_1.EventEmitter {
510
509
  const player = this.create(playerOptions);
511
510
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId}`);
512
511
  if (state.isAutoplay) {
513
- const savedUser = state.data?.clientUser;
512
+ const savedUser = (state.data?.Internal_AutoplayUser || state.data?.clientUser);
514
513
  if (savedUser) {
515
514
  const autoPlayUser = await player.manager.resolveUser(savedUser);
516
515
  player.setAutoplay(true, autoPlayUser, state.autoplayTries);
@@ -607,14 +606,18 @@ class Manager extends events_1.EventEmitter {
607
606
  const filterActions = {
608
607
  bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
609
608
  distort: (isEnabled) => player.filters.distort(isEnabled),
609
+ pluginFilters: () => player.filters.setPluginFilters(state.filters.pluginFilters),
610
+ setChannelMix: () => player.filters.setChannelMix(state.filters.channelMix),
610
611
  setDistortion: () => player.filters.setDistortion(state.filters.distortion),
611
612
  eightD: (isEnabled) => player.filters.eightD(isEnabled),
612
613
  setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
614
+ setLowPass: () => player.filters.setLowPass(state.filters.lowPass),
613
615
  nightcore: (isEnabled) => player.filters.nightcore(isEnabled),
614
616
  slowmo: (isEnabled) => player.filters.slowmo(isEnabled),
615
617
  soft: (isEnabled) => player.filters.soft(isEnabled),
616
618
  trebleBass: (isEnabled) => player.filters.trebleBass(isEnabled),
617
619
  setTimescale: () => player.filters.setTimescale(state.filters.timescale),
620
+ setTremolo: () => player.filters.setTremolo(state.filters.tremolo),
618
621
  tv: (isEnabled) => player.filters.tv(isEnabled),
619
622
  vibrato: () => player.filters.setVibrato(state.filters.vibrato),
620
623
  vaporwave: (isEnabled) => player.filters.vaporwave(isEnabled),
@@ -1079,7 +1079,7 @@ class Node {
1079
1079
  * @returns {Promise<LavalinkInfo>} A promise that resolves to the Lavalink node information.
1080
1080
  */
1081
1081
  async fetchInfo() {
1082
- return (await this.rest.get(`/v4/info`));
1082
+ return await this.rest.getInfo();
1083
1083
  }
1084
1084
  /**
1085
1085
  * Gets the current sponsorblock segments for a player.
@@ -55,7 +55,7 @@ export declare class Player {
55
55
  /** Should only be used when the node is a NodeLink */
56
56
  protected voiceReceiverWsClient: WebSocket | null;
57
57
  protected isConnectToVoiceReceiver: boolean;
58
- protected voiceReceiverReconnectTimeout: NodeJS.Timeout | null;
58
+ protected voiceReceiverReconnectTimeout?: NodeJS.Timeout;
59
59
  protected voiceReceiverAttempt: number;
60
60
  protected voiceReceiverReconnectTries: number;
61
61
  /**
@@ -263,7 +263,7 @@ class Player {
263
263
  }
264
264
  if (this.voiceReceiverReconnectTimeout) {
265
265
  clearTimeout(this.voiceReceiverReconnectTimeout);
266
- this.voiceReceiverReconnectTimeout = null;
266
+ this.voiceReceiverReconnectTimeout = undefined;
267
267
  }
268
268
  if (this.voiceReceiverWsClient) {
269
269
  this.voiceReceiverWsClient.removeAllListeners();
@@ -1118,7 +1118,7 @@ class Player {
1118
1118
  async openVoiceReceiver() {
1119
1119
  if (this.voiceReceiverReconnectTimeout)
1120
1120
  clearTimeout(this.voiceReceiverReconnectTimeout);
1121
- this.voiceReceiverReconnectTimeout = null;
1121
+ this.voiceReceiverReconnectTimeout = undefined;
1122
1122
  this.isConnectToVoiceReceiver = true;
1123
1123
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Opened voice receiver for player ${this.guildId}`);
1124
1124
  this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverConnect, this);
@@ -1,6 +1,6 @@
1
1
  import { Node } from "./Node";
2
2
  import { Manager } from "./Manager";
3
- import { LavalinkSession, LavaPlayer, RestPlayOptions } from "./Types";
3
+ import { LavalinkInfo, LavalinkSession, LavaPlayer, NodeStats, RestPlayOptions, RoutePlannerStatus, TrackData } from "./Types";
4
4
  /** Handles the requests sent to the Lavalink REST API. */
5
5
  export declare class Rest {
6
6
  /** The Node that this Rest instance is connected to. */
@@ -32,6 +32,11 @@ export declare class Rest {
32
32
  * @returns {Promise<LavaPlayer>} Returns the player.
33
33
  */
34
34
  getPlayer(guildId: string): Promise<LavaPlayer>;
35
+ /**
36
+ * Retrieves all players that belong to the current session.
37
+ * @returns {Promise<LavaPlayer[]>} Returns the players.
38
+ */
39
+ getPlayers(): Promise<LavaPlayer[]>;
35
40
  /**
36
41
  * Sends a PATCH request to update player related data.
37
42
  * @param {RestPlayOptions} options The options to update the player with.
@@ -51,6 +56,44 @@ export declare class Rest {
51
56
  * @returns {Promise<LavalinkSession>} The updated session.
52
57
  */
53
58
  updateSession(resuming: boolean, timeout: number): Promise<LavalinkSession>;
59
+ /**
60
+ * Decodes a single Lavalink track.
61
+ * @param {string} encodedTrack The encoded track string.
62
+ * @returns {Promise<TrackData>} The decoded track.
63
+ */
64
+ decodeTrack(encodedTrack: string): Promise<TrackData>;
65
+ /**
66
+ * Decodes multiple Lavalink tracks.
67
+ * @param {string[]} encodedTracks The encoded track strings.
68
+ * @returns {Promise<TrackData[]>} The decoded tracks.
69
+ */
70
+ decodeTracks(encodedTracks: string[]): Promise<TrackData[]>;
71
+ /**
72
+ * Retrieves this node's Lavalink info.
73
+ * @returns {Promise<LavalinkInfo>} The node info.
74
+ */
75
+ getInfo(): Promise<LavalinkInfo>;
76
+ /**
77
+ * Retrieves this node's current stats.
78
+ * @returns {Promise<NodeStats>} The node stats.
79
+ */
80
+ getStats(): Promise<NodeStats>;
81
+ /**
82
+ * Retrieves the route planner status.
83
+ * @returns {Promise<RoutePlannerStatus>} The route planner status.
84
+ */
85
+ getRoutePlannerStatus(): Promise<RoutePlannerStatus>;
86
+ /**
87
+ * Unmarks a failed route planner address.
88
+ * @param {string} address The address to free.
89
+ * @returns {Promise<void>} Void.
90
+ */
91
+ freeRoutePlannerAddress(address: string): Promise<void>;
92
+ /**
93
+ * Unmarks all failed route planner addresses.
94
+ * @returns {Promise<void>} Void.
95
+ */
96
+ freeAllRoutePlannerAddresses(): Promise<void>;
54
97
  /**
55
98
  * Sends a request to the specified endpoint and returns the response data.
56
99
  * @param {string} method The HTTP method to use for the request.
@@ -78,7 +121,7 @@ export declare class Rest {
78
121
  * @param {unknown} body The data to send in the request body.
79
122
  * @returns {Promise<T>} The response data of the POST request.
80
123
  */
81
- post<T>(endpoint: string, body: unknown): Promise<T>;
124
+ post<T>(endpoint: string, body?: unknown): Promise<T>;
82
125
  /**
83
126
  * Sends a PUT request to the specified endpoint and returns the response data.
84
127
  * @param {string} endpoint The endpoint to send the PUT request to.
@@ -55,6 +55,18 @@ class Rest {
55
55
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] Getting player on node: ${this.node.options.identifier} : ${Utils_1.JSONUtils.safe(result, 2)}`);
56
56
  return result;
57
57
  }
58
+ /**
59
+ * Retrieves all players that belong to the current session.
60
+ * @returns {Promise<LavaPlayer[]>} Returns the players.
61
+ */
62
+ async getPlayers() {
63
+ if (!this.sessionId) {
64
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] Skipping getPlayers - no session ID on node ${this.node.options.identifier}`);
65
+ return [];
66
+ }
67
+ const result = await this.get(`/v4/sessions/${this.sessionId}/players`);
68
+ return Array.isArray(result) ? result : (result?.players ?? []);
69
+ }
58
70
  /**
59
71
  * Sends a PATCH request to update player related data.
60
72
  * @param {RestPlayOptions} options The options to update the player with.
@@ -95,6 +107,77 @@ class Rest {
95
107
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] Updating session: ${this.sessionId}`);
96
108
  return await this.patch(`/v4/sessions/${this.sessionId}`, { resuming, timeout });
97
109
  }
110
+ /**
111
+ * Decodes a single Lavalink track.
112
+ * @param {string} encodedTrack The encoded track string.
113
+ * @returns {Promise<TrackData>} The decoded track.
114
+ */
115
+ async decodeTrack(encodedTrack) {
116
+ if (typeof encodedTrack !== "string" || encodedTrack.length === 0) {
117
+ throw new MagmastreamError_1.MagmaStreamError({
118
+ code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
119
+ message: "Cannot decode an empty track.",
120
+ });
121
+ }
122
+ return await this.get(`/v4/decodetrack?encodedTrack=${encodeURIComponent(encodedTrack)}`);
123
+ }
124
+ /**
125
+ * Decodes multiple Lavalink tracks.
126
+ * @param {string[]} encodedTracks The encoded track strings.
127
+ * @returns {Promise<TrackData[]>} The decoded tracks.
128
+ */
129
+ async decodeTracks(encodedTracks) {
130
+ if (!Array.isArray(encodedTracks) || encodedTracks.length === 0) {
131
+ throw new MagmastreamError_1.MagmaStreamError({
132
+ code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
133
+ message: "Cannot decode an empty track list.",
134
+ });
135
+ }
136
+ const result = await this.post("/v4/decodetracks", encodedTracks);
137
+ return Array.isArray(result) ? result : (result?.tracks ?? []);
138
+ }
139
+ /**
140
+ * Retrieves this node's Lavalink info.
141
+ * @returns {Promise<LavalinkInfo>} The node info.
142
+ */
143
+ async getInfo() {
144
+ return await this.get("/v4/info");
145
+ }
146
+ /**
147
+ * Retrieves this node's current stats.
148
+ * @returns {Promise<NodeStats>} The node stats.
149
+ */
150
+ async getStats() {
151
+ return await this.get("/v4/stats");
152
+ }
153
+ /**
154
+ * Retrieves the route planner status.
155
+ * @returns {Promise<RoutePlannerStatus>} The route planner status.
156
+ */
157
+ async getRoutePlannerStatus() {
158
+ return await this.get("/v4/routeplanner/status");
159
+ }
160
+ /**
161
+ * Unmarks a failed route planner address.
162
+ * @param {string} address The address to free.
163
+ * @returns {Promise<void>} Void.
164
+ */
165
+ async freeRoutePlannerAddress(address) {
166
+ if (typeof address !== "string" || address.length === 0) {
167
+ throw new MagmastreamError_1.MagmaStreamError({
168
+ code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
169
+ message: "Cannot free an empty route planner address.",
170
+ });
171
+ }
172
+ await this.post("/v4/routeplanner/free/address", { address });
173
+ }
174
+ /**
175
+ * Unmarks all failed route planner addresses.
176
+ * @returns {Promise<void>} Void.
177
+ */
178
+ async freeAllRoutePlannerAddresses() {
179
+ await this.post("/v4/routeplanner/free/all");
180
+ }
98
181
  /**
99
182
  * Sends a request to the specified endpoint and returns the response data.
100
183
  * @param {string} method The HTTP method to use for the request.
@@ -132,7 +215,7 @@ class Rest {
132
215
  return data;
133
216
  }
134
217
  if (status === 404) {
135
- if (data?.message === "Guild not found") {
218
+ if (data?.message === "Guild not found" || data?.message === "Player not found") {
136
219
  return [];
137
220
  }
138
221
  if (data?.message === "Session not found") {
@@ -388,13 +388,13 @@ export interface Track {
388
388
  /** The duration of the track. */
389
389
  readonly duration: number;
390
390
  /** The ISRC of the track. */
391
- readonly isrc: string;
391
+ readonly isrc: string | null;
392
392
  /** If the track is seekable. */
393
393
  readonly isSeekable: boolean;
394
394
  /** If the track is a stream.. */
395
395
  readonly isStream: boolean;
396
396
  /** The uri of the track. */
397
- readonly uri: string;
397
+ readonly uri: string | null;
398
398
  /** The thumbnail of the track or null if it's a unsupported source. */
399
399
  readonly thumbnail: string | null;
400
400
  /** The user that requested the track. */
@@ -412,6 +412,7 @@ export interface Track {
412
412
  * Track Plugin Info
413
413
  */
414
414
  export interface TrackPluginInfo {
415
+ [key: string]: unknown;
415
416
  albumName?: string;
416
417
  albumUrl?: string;
417
418
  artistArtworkUrl?: string;
@@ -433,7 +434,7 @@ export interface SearchQuery {
433
434
  */
434
435
  export interface LavalinkResponse {
435
436
  loadType: LoadTypes;
436
- data: TrackData[] | PlaylistRawData;
437
+ data: LavalinkLoadResultData;
437
438
  }
438
439
  /**
439
440
  * Track Data
@@ -444,7 +445,9 @@ export interface TrackData {
444
445
  /** The detailed information of the track. */
445
446
  info: TrackDataInfo;
446
447
  /** Additional track info provided by plugins. */
447
- pluginInfo: Record<string, string>;
448
+ pluginInfo: Record<string, unknown>;
449
+ /** Additional track data sent back from Lavalink. */
450
+ userData: Record<string, unknown>;
448
451
  }
449
452
  /**
450
453
  * Playlist Raw Data
@@ -453,12 +456,15 @@ export interface PlaylistRawData {
453
456
  info: {
454
457
  /** The playlist name. */
455
458
  name: string;
459
+ /** The selected track index, or -1 if none is selected. */
460
+ selectedTrack: number;
456
461
  };
457
462
  /** Addition info provided by plugins. */
458
- pluginInfo: object;
463
+ pluginInfo: Record<string, unknown>;
459
464
  /** The tracks of the playlist */
460
465
  tracks: TrackData[];
461
466
  }
467
+ export type LavalinkLoadResultData = TrackData | TrackData[] | PlaylistRawData | Exception | null;
462
468
  /**
463
469
  * Track Data Info
464
470
  */
@@ -467,19 +473,20 @@ export interface TrackDataInfo {
467
473
  isSeekable: boolean;
468
474
  author: string;
469
475
  length: number;
470
- isrc?: string;
476
+ position: number;
477
+ isrc: string | null;
471
478
  isStream: boolean;
472
479
  title: string;
473
- uri?: string;
474
- artworkUrl?: string;
475
- sourceName?: TrackSourceName;
480
+ uri: string | null;
481
+ artworkUrl: string | null;
482
+ sourceName: TrackSourceName;
476
483
  }
477
484
  /**
478
485
  * LavaPlayer
479
486
  */
480
487
  export interface LavaPlayer {
481
488
  guildId: string;
482
- track: TrackData;
489
+ track: TrackData | null;
483
490
  volume: number;
484
491
  paused: boolean;
485
492
  state: {
@@ -489,7 +496,7 @@ export interface LavaPlayer {
489
496
  ping: number;
490
497
  };
491
498
  voice: LavalinkVoiceStateResponse;
492
- filters: Record<string, unknown>;
499
+ filters: LavalinkFilters;
493
500
  }
494
501
  /**
495
502
  * Error or Empty Search Result
@@ -497,6 +504,8 @@ export interface LavaPlayer {
497
504
  export interface ErrorOrEmptySearchResult {
498
505
  /** The load type of the result. */
499
506
  loadType: LoadTypes.Empty | LoadTypes.Error;
507
+ /** Always an empty array for error/empty results. */
508
+ tracks: [];
500
509
  }
501
510
  /**
502
511
  * Track Search Result
@@ -582,8 +591,10 @@ export interface PlaylistData {
582
591
  name: string;
583
592
  /** Requester of playlist. */
584
593
  requester: AnyUser;
585
- /** More playlist information. */
586
- playlistInfo: PlaylistInfoData[];
594
+ /** Lavalink playlist information. */
595
+ playlistInfo: PlaylistInfoData;
596
+ /** Additional playlist information provided by plugins. */
597
+ pluginInfo: PlaylistPluginInfo;
587
598
  /** The length of the playlist. */
588
599
  duration: number;
589
600
  /** The songs of the playlist. */
@@ -593,16 +604,17 @@ export interface PlaylistData {
593
604
  * Playlist Info Data
594
605
  */
595
606
  export interface PlaylistInfoData {
596
- /** Url to playlist. */
597
- url: string;
598
- /** Type is always playlist in that case. */
599
- type: string;
600
- /** ArtworkUrl of playlist */
601
- artworkUrl: string;
602
- /** Number of total tracks in playlist */
603
- totalTracks: number;
604
- /** Author of playlist */
605
- author: string;
607
+ /** The playlist name. */
608
+ name: string;
609
+ /** The selected track index, or -1 if none is selected. */
610
+ selectedTrack: number;
611
+ }
612
+ export interface PlaylistPluginInfo extends Record<string, unknown> {
613
+ url?: string;
614
+ type?: string;
615
+ artworkUrl?: string;
616
+ totalTracks?: number;
617
+ author?: string;
606
618
  }
607
619
  /**
608
620
  * Manager Events
@@ -701,7 +713,7 @@ export interface NodeStats {
701
713
  /** The cpu stats for the node. */
702
714
  cpu: CPUStats;
703
715
  /** The frame stats for the node. */
704
- frameStats: FrameStats;
716
+ frameStats: FrameStats | null;
705
717
  }
706
718
  /**
707
719
  * Node Message
@@ -723,9 +735,10 @@ export interface PlayerEvent {
723
735
  * Exception interface
724
736
  */
725
737
  export interface Exception {
726
- message: string;
738
+ message: string | null;
727
739
  severity: SeverityTypes;
728
740
  cause: string;
741
+ causeStackTrace?: string;
729
742
  }
730
743
  /**
731
744
  * TrackStartEvent interface
@@ -831,7 +844,7 @@ export interface NodeStats {
831
844
  /** The cpu stats for the node. */
832
845
  cpu: CPUStats;
833
846
  /** The frame stats for the node. */
834
- frameStats: FrameStats;
847
+ frameStats: FrameStats | null;
835
848
  }
836
849
  /**
837
850
  * MemoryStats interface
@@ -877,7 +890,7 @@ export interface LavalinkInfo {
877
890
  major: number;
878
891
  minor: number;
879
892
  patch: number;
880
- preRelease: string;
893
+ preRelease: string | null;
881
894
  };
882
895
  buildTime: number;
883
896
  git: {
@@ -1099,7 +1112,7 @@ export interface RestPlayOptions {
1099
1112
  /** Whether the player is paused. */
1100
1113
  paused?: boolean;
1101
1114
  /** The audio effects. */
1102
- filters?: object;
1115
+ filters?: LavalinkFilters;
1103
1116
  /** voice payload. */
1104
1117
  voice?: LavalinkVoiceStateUpdate;
1105
1118
  };
@@ -1138,11 +1151,15 @@ export interface SerializedPlayerState {
1138
1151
  filters: {
1139
1152
  bassBoostlevel: number | null;
1140
1153
  equalizer: EqualizerBand[];
1141
- distortion: DistortionOptions;
1142
- karaoke: KaraokeOptions;
1143
- timescale: TimescaleOptions;
1144
- vibrato: VibratoOptions;
1145
- rotation: RotationOptions;
1154
+ distortion: DistortionOptions | null;
1155
+ karaoke: KaraokeOptions | null;
1156
+ timescale: TimescaleOptions | null;
1157
+ tremolo: TremoloOptions | null;
1158
+ vibrato: VibratoOptions | null;
1159
+ rotation: RotationOptions | null;
1160
+ channelMix: ChannelMixOptions | null;
1161
+ lowPass: LowPassOptions | null;
1162
+ pluginFilters: Record<string, unknown>;
1146
1163
  reverb: ReverbOptions;
1147
1164
  volume: number;
1148
1165
  filterStatus: Record<string, boolean>;
@@ -1181,6 +1198,26 @@ export interface LavalinkSession {
1181
1198
  resuming?: boolean;
1182
1199
  timeout?: number;
1183
1200
  }
1201
+ export interface RoutePlannerStatus {
1202
+ class: "RotatingIpRoutePlanner" | "NanoIpRoutePlanner" | "RotatingNanoIpRoutePlanner" | "BalancingIpRoutePlanner";
1203
+ details: RoutePlannerDetails;
1204
+ }
1205
+ export interface RoutePlannerDetails {
1206
+ ipBlock: {
1207
+ type: "Inet4Address" | "Inet6Address";
1208
+ size: string;
1209
+ };
1210
+ failingAddresses: {
1211
+ failingAddress: string;
1212
+ failingTimestamp: number;
1213
+ failingTime: string;
1214
+ }[];
1215
+ rotateIndex?: string;
1216
+ ipIndex?: string;
1217
+ currentAddress?: string;
1218
+ currentAddressIndex?: string;
1219
+ blockIndex?: string;
1220
+ }
1184
1221
  /**
1185
1222
  * ManagerInitOptions interface
1186
1223
  */
@@ -1203,10 +1240,15 @@ export interface TimescaleOptions {
1203
1240
  pitch?: number;
1204
1241
  rate?: number;
1205
1242
  }
1243
+ /** Options for applying tremolo effect to audio. */
1244
+ export interface TremoloOptions {
1245
+ frequency?: number;
1246
+ depth?: number;
1247
+ }
1206
1248
  /** Options for applying vibrato effect to audio. */
1207
1249
  export interface VibratoOptions {
1208
- frequency: number;
1209
- depth: number;
1250
+ frequency?: number;
1251
+ depth?: number;
1210
1252
  }
1211
1253
  /** Options for applying rotation effect to audio. */
1212
1254
  export interface RotationOptions {
@@ -1230,6 +1272,30 @@ export interface DistortionOptions {
1230
1272
  offset?: number;
1231
1273
  scale?: number;
1232
1274
  }
1275
+ /** Options for mixing stereo channels. */
1276
+ export interface ChannelMixOptions {
1277
+ leftToLeft?: number;
1278
+ leftToRight?: number;
1279
+ rightToLeft?: number;
1280
+ rightToRight?: number;
1281
+ }
1282
+ /** Options for applying a low pass filter. */
1283
+ export interface LowPassOptions {
1284
+ smoothing?: number;
1285
+ }
1286
+ export interface LavalinkFilters {
1287
+ volume?: number;
1288
+ equalizer?: EqualizerBand[];
1289
+ karaoke?: KaraokeOptions | null;
1290
+ timescale?: TimescaleOptions | null;
1291
+ tremolo?: TremoloOptions | null;
1292
+ vibrato?: VibratoOptions | null;
1293
+ distortion?: DistortionOptions | null;
1294
+ rotation?: RotationOptions | null;
1295
+ channelMix?: ChannelMixOptions | null;
1296
+ lowPass?: LowPassOptions | null;
1297
+ pluginFilters?: Record<string, unknown>;
1298
+ }
1233
1299
  /** Options for applying reverb effect to audio. */
1234
1300
  export interface ReverbOptions {
1235
1301
  wet?: number;
@@ -1303,7 +1369,7 @@ export type Sizes = "0" | "1" | "2" | "3" | "default" | "mqdefault" | "hqdefault
1303
1369
  /**
1304
1370
  * Track Source Name Enum type
1305
1371
  */
1306
- export type TrackSourceName = keyof typeof TrackSourceTypes;
1372
+ export type TrackSourceName = `${TrackSourceTypes}` | string;
1307
1373
  /**
1308
1374
  * Use Node Option Enum type
1309
1375
  */
@@ -63,7 +63,7 @@ class TrackUtils {
63
63
  if (typeof track !== "object" || track === null)
64
64
  return false;
65
65
  const trackRecord = track;
66
- return REQUIRED_TRACK_KEYS.every((key) => typeof trackRecord[key] === "string");
66
+ return REQUIRED_TRACK_KEYS.every((key) => (key === "uri" ? typeof trackRecord[key] === "string" || trackRecord[key] === null : typeof trackRecord[key] === "string"));
67
67
  }
68
68
  /**
69
69
  * Checks if the provided argument is a valid Track array.
@@ -131,14 +131,14 @@ class TrackUtils {
131
131
  uri: data.info.uri,
132
132
  artworkUrl: data.info?.artworkUrl ?? null,
133
133
  sourceName: sourceNameMap[data.info?.sourceName?.toLowerCase() ?? ""] ?? data.info?.sourceName,
134
- thumbnail: data.info.uri.includes("youtube") ? `https://img.youtube.com/vi/${data.info.identifier}/default.jpg` : null,
134
+ thumbnail: data.info.uri?.includes("youtube") ? `https://img.youtube.com/vi/${data.info.identifier}/default.jpg` : null,
135
135
  displayThumbnail(size = "default") {
136
136
  const finalSize = SIZES.find((s) => s === size) ?? "default";
137
- return this.uri.includes("youtube") ? `https://img.youtube.com/vi/${data.info.identifier}/${finalSize}.jpg` : null;
137
+ return this.uri?.includes("youtube") ? `https://img.youtube.com/vi/${data.info.identifier}/${finalSize}.jpg` : null;
138
138
  },
139
139
  requester: requester,
140
140
  pluginInfo: data.pluginInfo,
141
- customData: {},
141
+ customData: data.userData ?? {},
142
142
  isAutoplay: isAutoplay,
143
143
  };
144
144
  track.displayThumbnail = track.displayThumbnail.bind(track);
@@ -180,7 +180,7 @@ class TrackUtils {
180
180
  return track;
181
181
  track.displayThumbnail = function (size = "default") {
182
182
  const finalSize = SIZES.find((s) => s === size) ?? "default";
183
- return this.uri.includes("youtube") ? `https://img.youtube.com/vi/${this.identifier}/${finalSize}.jpg` : null;
183
+ return this.uri?.includes("youtube") ? `https://img.youtube.com/vi/${this.identifier}/${finalSize}.jpg` : null;
184
184
  }.bind(track);
185
185
  return track;
186
186
  }
@@ -318,6 +318,8 @@ class AutoPlayUtils {
318
318
  */
319
319
  static async getRecommendedTracksFromSource(track, platform) {
320
320
  const requester = track.requester;
321
+ if (!track.uri)
322
+ return [];
321
323
  const parsedURL = new URL(track.uri);
322
324
  switch (platform) {
323
325
  case Enums_1.AutoPlayPlatform.Spotify: {
@@ -412,14 +414,14 @@ class AutoPlayUtils {
412
414
  }
413
415
  case Enums_1.AutoPlayPlatform.YouTube: {
414
416
  const allowedYouTubeHosts = ["youtube.com", "youtu.be"];
415
- const hasYouTubeURL = allowedYouTubeHosts.some((url) => track.uri.includes(url));
417
+ const hasYouTubeURL = allowedYouTubeHosts.some((url) => track.uri?.includes(url));
416
418
  let videoID = null;
417
419
  if (hasYouTubeURL) {
418
- videoID = track.uri.split("=").pop();
420
+ videoID = track.uri?.split("=").pop() ?? null;
419
421
  }
420
422
  else {
421
423
  const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.YouTube, requester);
422
- if (!resolvedTrack)
424
+ if (!resolvedTrack?.uri)
423
425
  return [];
424
426
  videoID = resolvedTrack.uri.split("=").pop();
425
427
  }
@@ -431,7 +433,7 @@ class AutoPlayUtils {
431
433
  do {
432
434
  randomIndex = Math.floor(Math.random() * 23) + 2;
433
435
  searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
434
- } while (track.uri.includes(searchURI));
436
+ } while (track.uri?.includes(searchURI));
435
437
  const resolvedTracks = await this.resolveTracksFromQuery(searchURI, Enums_1.SearchPlatform.YouTube, requester);
436
438
  const filteredTracks = resolvedTracks.filter((resolvedTrack) => resolvedTrack.uri !== track.uri);
437
439
  return filteredTracks;
@@ -721,6 +723,8 @@ class PlayerUtils {
721
723
  paused: player.paused,
722
724
  playing: player.playing,
723
725
  position: player.position,
726
+ isAutoplay: player.isAutoplay,
727
+ autoplayTries: player.autoplayTries,
724
728
  trackRepeat: player.trackRepeat,
725
729
  queueRepeat: player.queueRepeat,
726
730
  dynamicRepeat: player.dynamicRepeat,
@@ -737,7 +741,11 @@ class PlayerUtils {
737
741
  karaoke: player.filters.karaoke ?? null,
738
742
  rotation: player.filters.rotation ?? null,
739
743
  timescale: player.filters.timescale ?? null,
744
+ tremolo: player.filters.tremoloOptions ?? null,
740
745
  vibrato: player.filters.vibrato ?? null,
746
+ channelMix: player.filters.channelMix ?? null,
747
+ lowPass: player.filters.lowPass ?? null,
748
+ pluginFilters: player.filters.pluginFilters ?? {},
741
749
  reverb: player.filters.reverb ?? null,
742
750
  volume: player.filters.volume ?? 1.0,
743
751
  bassBoostlevel: player.filters.bassBoostlevel ?? null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.10.3-alpha.3",
3
+ "version": "2.10.3-alpha.5",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",