lavalink-client 2.5.1 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FilterManager = void 0;
4
4
  const Constants_1 = require("./Constants.js");
5
+ const Utils_1 = require("./Utils.js");
5
6
  /**
6
7
  * The FilterManager for each player
7
8
  */
@@ -673,7 +674,7 @@ class FilterManager {
673
674
  async setEQ(bands) {
674
675
  if (!Array.isArray(bands))
675
676
  bands = [bands];
676
- if (!bands.length || !bands.every((band) => JSON.stringify(Object.keys(band).sort()) === '["band","gain"]'))
677
+ if (!bands.length || !bands.every((band) => (0, Utils_1.safeStringify)(Object.keys(band).sort()) === '["band","gain"]'))
677
678
  throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
678
679
  for (const { band, gain } of bands)
679
680
  this.equalizerBands[band] = { band, gain };
@@ -693,7 +694,7 @@ class FilterManager {
693
694
  }
694
695
  /** Clears the equalizer bands. */
695
696
  async clearEQ() {
696
- return this.setEQ(new Array(15).fill(0.0).map((gain, band) => ({ band, gain })));
697
+ return this.setEQ(Array.from({ length: 15 }, () => ({ band: 0, gain: 0 })));
697
698
  }
698
699
  }
699
700
  exports.FilterManager = FilterManager;
@@ -2,9 +2,9 @@ import { EventEmitter } from "events";
2
2
  import { NodeManager } from "./NodeManager.js";
3
3
  import { Player } from "./Player.js";
4
4
  import { ManagerUtils, MiniMap } from "./Utils.js";
5
+ import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
5
6
  import type { BotClientOptions, LavalinkManagerEvents, ManagerOptions } from "./Types/Manager.js";
6
7
  import type { PlayerOptions } from "./Types/Player.js";
7
- import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
8
8
  export declare class LavalinkManager extends EventEmitter {
9
9
  /**
10
10
  * Emit an event
@@ -71,7 +71,7 @@ class LavalinkManager extends events_1.EventEmitter {
71
71
  applyOptions(options) {
72
72
  this.options = {
73
73
  client: {
74
- ...(options?.client || {}),
74
+ ...options?.client,
75
75
  id: options?.client?.id,
76
76
  username: options?.client?.username ?? "lavalink-client"
77
77
  },
@@ -314,7 +314,7 @@ class LavalinkManager extends events_1.EventEmitter {
314
314
  // oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
315
315
  if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
316
316
  if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
317
- throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
317
+ throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${(0, Utils_1.safeStringify)(oldPlayer.toJSON?.())}`);
318
318
  else if (this.options?.advancedOptions?.enableDebugEvents) {
319
319
  this.emit("debug", Constants_1.DebugEvents.PlayerDeleteInsteadOfDestroy, {
320
320
  state: "warn",
@@ -356,13 +356,13 @@ class LavalinkManager extends events_1.EventEmitter {
356
356
  if (this.initiated)
357
357
  return this;
358
358
  clientData = clientData ?? {};
359
- this.options.client = { ...(this.options?.client || {}), ...clientData };
359
+ this.options.client = { ...this.options?.client, ...clientData };
360
360
  if (!this.options?.client.id)
361
361
  throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
362
362
  if (typeof this.options?.client.id !== "string")
363
363
  throw new Error('"client.id" set is not type of "string"');
364
364
  let success = 0;
365
- for (const node of [...this.nodeManager.nodes.values()]) {
365
+ for (const node of this.nodeManager.nodes.values()) {
366
366
  try {
367
367
  await node.connect();
368
368
  success++;
@@ -439,7 +439,7 @@ class LavalinkManager extends events_1.EventEmitter {
439
439
  if (this.options?.advancedOptions?.enableDebugEvents) {
440
440
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
441
441
  state: "warn",
442
- message: `No Update data found in payload :: ${JSON.stringify(data, null, 2)}`,
442
+ message: `No Update data found in payload :: ${(0, Utils_1.safeStringify)(data, 2)}`,
443
443
  functionLayer: "LavalinkManager > sendRawData()",
444
444
  });
445
445
  }
@@ -451,7 +451,7 @@ class LavalinkManager extends events_1.EventEmitter {
451
451
  if (this.options?.advancedOptions?.enableDebugEvents) {
452
452
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
453
453
  state: "error",
454
- message: `No 'token' nor 'session_id' found in payload :: ${JSON.stringify(data, null, 2)}`,
454
+ message: `No 'token' nor 'session_id' found in payload :: ${(0, Utils_1.safeStringify)(data, 2)}`,
455
455
  functionLayer: "LavalinkManager > sendRawData()",
456
456
  });
457
457
  }
@@ -464,7 +464,7 @@ class LavalinkManager extends events_1.EventEmitter {
464
464
  if (this.options?.advancedOptions?.enableDebugEvents) {
465
465
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
466
466
  state: "warn",
467
- message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${JSON.stringify(update, null, 2)}`,
467
+ message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${(0, Utils_1.safeStringify)(update, 2)}`,
468
468
  functionLayer: "LavalinkManager > sendRawData()",
469
469
  });
470
470
  }
@@ -491,7 +491,7 @@ class LavalinkManager extends events_1.EventEmitter {
491
491
  if (!sessionId2Use) {
492
492
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
493
493
  state: "error",
494
- message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${JSON.stringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, null, 2)}`,
494
+ message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${(0, Utils_1.safeStringify)({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
495
495
  functionLayer: "LavalinkManager > sendRawData()",
496
496
  });
497
497
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
@@ -511,7 +511,7 @@ class LavalinkManager extends events_1.EventEmitter {
511
511
  if (this.options?.advancedOptions?.enableDebugEvents) {
512
512
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
513
513
  state: "log",
514
- message: `Sent updatePlayer for voice token session :: ${JSON.stringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, null, 2)}`,
514
+ message: `Sent updatePlayer for voice token session :: ${(0, Utils_1.safeStringify)({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
515
515
  functionLayer: "LavalinkManager > sendRawData()",
516
516
  });
517
517
  }
@@ -544,12 +544,12 @@ class LavalinkManager extends events_1.EventEmitter {
544
544
  if (this.options?.advancedOptions?.enableDebugEvents) {
545
545
  this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
546
546
  state: "warn",
547
- message: `Function to assing sessionId provided, but no found in Payload: ${JSON.stringify({ update, playerVoice: player.voice }, null, 2)}`,
547
+ message: `Function to assing sessionId provided, but no found in Payload: ${(0, Utils_1.safeStringify)({ update, playerVoice: player.voice }, 2)}`,
548
548
  functionLayer: "LavalinkManager > sendRawData()",
549
549
  });
550
550
  }
551
551
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
552
- console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${JSON.stringify(update, null, 2)}`);
552
+ console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${(0, Utils_1.safeStringify)(update, 2)}`);
553
553
  }
554
554
  player.voiceChannelId = update.channel_id;
555
555
  const selfMuteChanged = typeof update.self_mute === "boolean" && player.voiceState.selfMute !== update.self_mute;
@@ -140,7 +140,7 @@ exports.SourceLinksRegexes = {
140
140
  /** From tidal */
141
141
  tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
142
142
  /** From jiosaavn-plugin */
143
- jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,
143
+ jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
144
144
  /** FROM DUNCTE BOT PLUGIN */
145
145
  tiktok: /https:\/\/www\.tiktok\.com\//,
146
146
  mixcloud: /https:\/\/www\.mixcloud\.com\//,
@@ -1,7 +1,7 @@
1
+ import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
1
2
  import type { Player } from "./Player.js";
2
3
  import type { DestroyReasonsType, DisconnectReasonsType } from "./Types/Player.js";
3
4
  import type { Track } from "./Types/Track.js";
4
- import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
5
5
  import type { NodeManager } from "./NodeManager.js";
6
6
  import type { BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, LyricsResult, ModifyRequest, NodeStats, SponsorBlockSegment } from "./Types/Node.js";
7
7
  /**
@@ -134,7 +134,7 @@ class LavalinkNode {
134
134
  if (["DELETE", "PUT"].includes(options.method))
135
135
  return;
136
136
  if (response.status === 404)
137
- throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(response.headers)}`);
137
+ throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${(0, Utils_1.safeStringify)(response.headers)}`);
138
138
  return parseAsText ? await response.text() : await response.json();
139
139
  }
140
140
  /**
@@ -266,7 +266,7 @@ class LavalinkNode {
266
266
  const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
267
267
  r.method = "PATCH";
268
268
  r.headers["Content-Type"] = "application/json";
269
- r.body = JSON.stringify(data.playerOptions);
269
+ r.body = (0, Utils_1.safeStringify)(data.playerOptions);
270
270
  if (data.noReplace) {
271
271
  const url = new URL(`${this.restAddress}${r.path}`);
272
272
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
@@ -276,7 +276,7 @@ class LavalinkNode {
276
276
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
277
277
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerUpdateSuccess, {
278
278
  state: "log",
279
- message: `Player get's updated with following payload :: ${JSON.stringify(data.playerOptions, null, 3)}`,
279
+ message: `Player get's updated with following payload :: ${(0, Utils_1.safeStringify)(data.playerOptions, 3)}`,
280
280
  functionLayer: "LavalinkNode > node > updatePlayer()",
281
281
  });
282
282
  }
@@ -588,7 +588,7 @@ class LavalinkNode {
588
588
  return this.request(`/sessions/${this.sessionId}`, r => {
589
589
  r.method = "PATCH";
590
590
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
591
- r.body = JSON.stringify(data);
591
+ r.body = (0, Utils_1.safeStringify)(data);
592
592
  });
593
593
  }
594
594
  /**
@@ -632,7 +632,7 @@ class LavalinkNode {
632
632
  // return the decoded + builded tracks
633
633
  return await this.request(`/decodetracks`, r => {
634
634
  r.method = "POST";
635
- r.body = JSON.stringify(encodeds);
635
+ r.body = (0, Utils_1.safeStringify)(encodeds);
636
636
  r.headers["Content-Type"] = "application/json";
637
637
  }).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
638
638
  }
@@ -808,7 +808,7 @@ class LavalinkNode {
808
808
  return await this.request(`/routeplanner/free/address`, r => {
809
809
  r.method = "POST";
810
810
  r.headers["Content-Type"] = "application/json";
811
- r.body = JSON.stringify({ address });
811
+ r.body = (0, Utils_1.safeStringify)({ address });
812
812
  });
813
813
  },
814
814
  /**
@@ -1218,6 +1218,9 @@ class LavalinkNode {
1218
1218
  return this.queueEnd(player, track, payload);
1219
1219
  // If a track had an error while starting
1220
1220
  if (["loadFailed", "cleanup"].includes(payload.reason)) {
1221
+ //Dont add tracks if the player is already destroying.
1222
+ if (player.get("internal_destroystatus") === true)
1223
+ return;
1221
1224
  await (0, Utils_1.queueTrackEnd)(player);
1222
1225
  // if no track available, end queue
1223
1226
  if (!player.queue.current)
@@ -1273,7 +1276,7 @@ class LavalinkNode {
1273
1276
  // If there are no songs in the queue
1274
1277
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
1275
1278
  try { //Sometimes the trackStuck event triggers from the Lavalink server, but the track continues playing or resumes after. We explicitly end the track in such cases
1276
- await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1279
+ await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1277
1280
  return;
1278
1281
  }
1279
1282
  catch {
@@ -1381,7 +1384,7 @@ class LavalinkNode {
1381
1384
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
1382
1385
  r.method = "PUT";
1383
1386
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
1384
- r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
1387
+ r.body = (0, Utils_1.safeStringify)(segments.map(v => v.toLowerCase()));
1385
1388
  });
1386
1389
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1387
1390
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.SetSponsorBlock, {
@@ -187,7 +187,11 @@ class Player {
187
187
  }
188
188
  }
189
189
  if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
190
- options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
190
+ options.clientTrack.userData = {
191
+ ...(typeof options?.clientTrack?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.clientTrack?.requester || {}) } : {}),
192
+ ...options?.clientTrack.userData,
193
+ ...options.track?.userData,
194
+ };
191
195
  options.track = {
192
196
  encoded: options.clientTrack?.encoded,
193
197
  requester: options.clientTrack?.requester,
@@ -209,16 +213,11 @@ class Player {
209
213
  const track = Object.fromEntries(Object.entries({
210
214
  encoded: options.track.encoded,
211
215
  identifier: options.track.identifier,
216
+ userData: {
217
+ ...(typeof options?.track?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {}) } : {}),
218
+ ...options.track.userData,
219
+ }
212
220
  }).filter(v => typeof v[1] !== "undefined"));
213
- if (typeof options.track.userData === "object")
214
- track.userData = {
215
- ...(options.track.userData || {})
216
- };
217
- if (typeof options?.track?.requester === "object")
218
- track.userData = {
219
- ...(track.userData || {}),
220
- requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
221
- };
222
221
  if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
223
222
  this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerPlayWithTrackReplace, {
224
223
  state: "log",
@@ -254,7 +253,11 @@ class Player {
254
253
  // resolve the unresolved track
255
254
  await this.queue.current.resolve(this);
256
255
  if (typeof options.track?.userData === "object" && this.queue.current)
257
- this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
256
+ this.queue.current.userData = {
257
+ ...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
258
+ ...this.queue.current?.userData,
259
+ ...options.track?.userData
260
+ };
258
261
  }
259
262
  catch (error) {
260
263
  if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
@@ -292,7 +295,11 @@ class Player {
292
295
  track: {
293
296
  encoded: this.queue.current?.encoded || null,
294
297
  // identifier: options.identifier,
295
- userData: options?.track?.userData || {},
298
+ userData: {
299
+ ...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
300
+ ...options?.track?.userData,
301
+ ...this.queue.current?.userData,
302
+ },
296
303
  },
297
304
  volume: this.lavalinkVolume,
298
305
  position: options?.position ?? 0,
@@ -705,7 +712,7 @@ class Player {
705
712
  await this.node.request(endpoint, r => {
706
713
  r.method = "PATCH";
707
714
  r.headers["Content-Type"] = "application/json";
708
- r.body = JSON.stringify({
715
+ r.body = (0, Utils_1.safeStringify)({
709
716
  voice: {
710
717
  token: voiceData.token,
711
718
  endpoint: voiceData.endpoint,
@@ -207,15 +207,16 @@ class Queue {
207
207
  * @returns {number} Queue-Size (for the next Tracks)
208
208
  */
209
209
  async add(TrackOrTracks, index) {
210
- if (typeof index === "number" && index >= 0 && index < this.tracks.length)
211
- return await this.splice(index, 0, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
210
+ if (typeof index === "number" && index >= 0 && index < this.tracks.length) {
211
+ return await this.splice(index, 0, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
212
+ }
212
213
  const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
213
214
  // add the track(s)
214
- this.tracks.push(...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
215
+ this.tracks.push(...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
215
216
  // log if available
216
217
  if (typeof this.queueChanges?.tracksAdd === "function")
217
218
  try {
218
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), this.tracks.length, oldStored, this.utils.toJSON());
219
+ this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), this.tracks.length, oldStored, this.utils.toJSON());
219
220
  }
220
221
  catch { /* */ }
221
222
  // save the queue
@@ -241,11 +242,11 @@ class Queue {
241
242
  // Log if available
242
243
  if ((TrackOrTracks) && typeof this.queueChanges?.tracksAdd === "function")
243
244
  try {
244
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), index, oldStored, this.utils.toJSON());
245
+ this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), index, oldStored, this.utils.toJSON());
245
246
  }
246
247
  catch { /* */ }
247
248
  // remove the tracks (and add the new ones)
248
- let spliced = TrackOrTracks ? this.tracks.splice(index, amount, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))) : this.tracks.splice(index, amount);
249
+ let spliced = TrackOrTracks ? this.tracks.splice(index, amount, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))) : this.tracks.splice(index, amount);
249
250
  // get the spliced array
250
251
  spliced = (Array.isArray(spliced) ? spliced : [spliced]);
251
252
  // Log if available
@@ -113,3 +113,4 @@ export declare class MiniMap<K, V> extends Map<K, V> {
113
113
  map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
114
114
  }
115
115
  export declare function queueTrackEnd(player: Player, dontShiftQueue?: boolean): Promise<Track>;
116
+ export declare function safeStringify(obj: any, padding?: number): string;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MiniMap = exports.ManagerUtils = exports.NodeSymbol = exports.QueueSymbol = exports.UnresolvedTrackSymbol = exports.TrackSymbol = void 0;
4
4
  exports.parseLavalinkConnUrl = parseLavalinkConnUrl;
5
5
  exports.queueTrackEnd = queueTrackEnd;
6
+ exports.safeStringify = safeStringify;
6
7
  const node_url_1 = require("node:url");
7
8
  const types_1 = require("node:util/types");
8
9
  const Constants_1 = require("./Constants.js");
@@ -452,7 +453,7 @@ async function queueTrackEnd(player, dontShiftQueue = false) {
452
453
  // and if repeatMode == queue, add it back to the queue!
453
454
  if (player.repeatMode === "queue" && player.queue.current)
454
455
  player.queue.tracks.push(player.queue.current);
455
- // change the current Track to the next upcoming one
456
+ // change the current Track to the next upcoming one
456
457
  const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
457
458
  try {
458
459
  if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
@@ -547,3 +548,20 @@ async function getClosestTrack(data, player) {
547
548
  return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
548
549
  });
549
550
  }
551
+ function safeStringify(obj, padding = 0) {
552
+ const seen = new WeakSet();
553
+ return JSON.stringify(obj, (key, value) => {
554
+ if (typeof value === "function")
555
+ return undefined; // Funktion skippen
556
+ if (typeof value === "symbol")
557
+ return undefined; // Symbol skippen
558
+ if (typeof value === "bigint")
559
+ return value.toString(); // BigInt to String
560
+ if (typeof value === "object" && value !== null) {
561
+ if (seen.has(value))
562
+ return "[Circular]";
563
+ seen.add(value);
564
+ }
565
+ return value;
566
+ }, padding);
567
+ }
@@ -1,4 +1,5 @@
1
1
  import { audioOutputsData } from "./Constants.js";
2
+ import { safeStringify } from "./Utils.js";
2
3
  /**
3
4
  * The FilterManager for each player
4
5
  */
@@ -670,7 +671,7 @@ export class FilterManager {
670
671
  async setEQ(bands) {
671
672
  if (!Array.isArray(bands))
672
673
  bands = [bands];
673
- if (!bands.length || !bands.every((band) => JSON.stringify(Object.keys(band).sort()) === '["band","gain"]'))
674
+ if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
674
675
  throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
675
676
  for (const { band, gain } of bands)
676
677
  this.equalizerBands[band] = { band, gain };
@@ -690,6 +691,6 @@ export class FilterManager {
690
691
  }
691
692
  /** Clears the equalizer bands. */
692
693
  async clearEQ() {
693
- return this.setEQ(new Array(15).fill(0.0).map((gain, band) => ({ band, gain })));
694
+ return this.setEQ(Array.from({ length: 15 }, () => ({ band: 0, gain: 0 })));
694
695
  }
695
696
  }
@@ -2,9 +2,9 @@ import { EventEmitter } from "events";
2
2
  import { NodeManager } from "./NodeManager.js";
3
3
  import { Player } from "./Player.js";
4
4
  import { ManagerUtils, MiniMap } from "./Utils.js";
5
+ import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
5
6
  import type { BotClientOptions, LavalinkManagerEvents, ManagerOptions } from "./Types/Manager.js";
6
7
  import type { PlayerOptions } from "./Types/Player.js";
7
- import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
8
8
  export declare class LavalinkManager extends EventEmitter {
9
9
  /**
10
10
  * Emit an event
@@ -3,7 +3,7 @@ import { DebugEvents, DestroyReasons } from "./Constants.js";
3
3
  import { NodeManager } from "./NodeManager.js";
4
4
  import { Player } from "./Player.js";
5
5
  import { DefaultQueueStore } from "./Queue.js";
6
- import { ManagerUtils, MiniMap } from "./Utils.js";
6
+ import { ManagerUtils, MiniMap, safeStringify } from "./Utils.js";
7
7
  export class LavalinkManager extends EventEmitter {
8
8
  /**
9
9
  * Emit an event
@@ -68,7 +68,7 @@ export class LavalinkManager extends EventEmitter {
68
68
  applyOptions(options) {
69
69
  this.options = {
70
70
  client: {
71
- ...(options?.client || {}),
71
+ ...options?.client,
72
72
  id: options?.client?.id,
73
73
  username: options?.client?.username ?? "lavalink-client"
74
74
  },
@@ -311,7 +311,7 @@ export class LavalinkManager extends EventEmitter {
311
311
  // oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
312
312
  if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
313
313
  if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
314
- throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
314
+ throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`);
315
315
  else if (this.options?.advancedOptions?.enableDebugEvents) {
316
316
  this.emit("debug", DebugEvents.PlayerDeleteInsteadOfDestroy, {
317
317
  state: "warn",
@@ -353,13 +353,13 @@ export class LavalinkManager extends EventEmitter {
353
353
  if (this.initiated)
354
354
  return this;
355
355
  clientData = clientData ?? {};
356
- this.options.client = { ...(this.options?.client || {}), ...clientData };
356
+ this.options.client = { ...this.options?.client, ...clientData };
357
357
  if (!this.options?.client.id)
358
358
  throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
359
359
  if (typeof this.options?.client.id !== "string")
360
360
  throw new Error('"client.id" set is not type of "string"');
361
361
  let success = 0;
362
- for (const node of [...this.nodeManager.nodes.values()]) {
362
+ for (const node of this.nodeManager.nodes.values()) {
363
363
  try {
364
364
  await node.connect();
365
365
  success++;
@@ -436,7 +436,7 @@ export class LavalinkManager extends EventEmitter {
436
436
  if (this.options?.advancedOptions?.enableDebugEvents) {
437
437
  this.emit("debug", DebugEvents.NoAudioDebug, {
438
438
  state: "warn",
439
- message: `No Update data found in payload :: ${JSON.stringify(data, null, 2)}`,
439
+ message: `No Update data found in payload :: ${safeStringify(data, 2)}`,
440
440
  functionLayer: "LavalinkManager > sendRawData()",
441
441
  });
442
442
  }
@@ -448,7 +448,7 @@ export class LavalinkManager extends EventEmitter {
448
448
  if (this.options?.advancedOptions?.enableDebugEvents) {
449
449
  this.emit("debug", DebugEvents.NoAudioDebug, {
450
450
  state: "error",
451
- message: `No 'token' nor 'session_id' found in payload :: ${JSON.stringify(data, null, 2)}`,
451
+ message: `No 'token' nor 'session_id' found in payload :: ${safeStringify(data, 2)}`,
452
452
  functionLayer: "LavalinkManager > sendRawData()",
453
453
  });
454
454
  }
@@ -461,7 +461,7 @@ export class LavalinkManager extends EventEmitter {
461
461
  if (this.options?.advancedOptions?.enableDebugEvents) {
462
462
  this.emit("debug", DebugEvents.NoAudioDebug, {
463
463
  state: "warn",
464
- message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${JSON.stringify(update, null, 2)}`,
464
+ message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${safeStringify(update, 2)}`,
465
465
  functionLayer: "LavalinkManager > sendRawData()",
466
466
  });
467
467
  }
@@ -488,7 +488,7 @@ export class LavalinkManager extends EventEmitter {
488
488
  if (!sessionId2Use) {
489
489
  this.emit("debug", DebugEvents.NoAudioDebug, {
490
490
  state: "error",
491
- message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${JSON.stringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, null, 2)}`,
491
+ message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
492
492
  functionLayer: "LavalinkManager > sendRawData()",
493
493
  });
494
494
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
@@ -508,7 +508,7 @@ export class LavalinkManager extends EventEmitter {
508
508
  if (this.options?.advancedOptions?.enableDebugEvents) {
509
509
  this.emit("debug", DebugEvents.NoAudioDebug, {
510
510
  state: "log",
511
- message: `Sent updatePlayer for voice token session :: ${JSON.stringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, null, 2)}`,
511
+ message: `Sent updatePlayer for voice token session :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
512
512
  functionLayer: "LavalinkManager > sendRawData()",
513
513
  });
514
514
  }
@@ -541,12 +541,12 @@ export class LavalinkManager extends EventEmitter {
541
541
  if (this.options?.advancedOptions?.enableDebugEvents) {
542
542
  this.emit("debug", DebugEvents.NoAudioDebug, {
543
543
  state: "warn",
544
- message: `Function to assing sessionId provided, but no found in Payload: ${JSON.stringify({ update, playerVoice: player.voice }, null, 2)}`,
544
+ message: `Function to assing sessionId provided, but no found in Payload: ${safeStringify({ update, playerVoice: player.voice }, 2)}`,
545
545
  functionLayer: "LavalinkManager > sendRawData()",
546
546
  });
547
547
  }
548
548
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
549
- console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${JSON.stringify(update, null, 2)}`);
549
+ console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`);
550
550
  }
551
551
  player.voiceChannelId = update.channel_id;
552
552
  const selfMuteChanged = typeof update.self_mute === "boolean" && player.voiceState.selfMute !== update.self_mute;
@@ -137,7 +137,7 @@ export const SourceLinksRegexes = {
137
137
  /** From tidal */
138
138
  tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
139
139
  /** From jiosaavn-plugin */
140
- jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,
140
+ jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
141
141
  /** FROM DUNCTE BOT PLUGIN */
142
142
  tiktok: /https:\/\/www\.tiktok\.com\//,
143
143
  mixcloud: /https:\/\/www\.mixcloud\.com\//,
@@ -1,7 +1,7 @@
1
+ import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
1
2
  import type { Player } from "./Player.js";
2
3
  import type { DestroyReasonsType, DisconnectReasonsType } from "./Types/Player.js";
3
4
  import type { Track } from "./Types/Track.js";
4
- import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
5
5
  import type { NodeManager } from "./NodeManager.js";
6
6
  import type { BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, LyricsResult, ModifyRequest, NodeStats, SponsorBlockSegment } from "./Types/Node.js";
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { isAbsolute } from "path";
2
2
  import WebSocket from "ws";
3
3
  import { DebugEvents, DestroyReasons, validSponsorBlocks } from "./Constants.js";
4
- import { NodeSymbol, queueTrackEnd } from "./Utils.js";
4
+ import { NodeSymbol, queueTrackEnd, safeStringify } from "./Utils.js";
5
5
  /**
6
6
  * Lavalink Node creator class
7
7
  */
@@ -130,7 +130,7 @@ export class LavalinkNode {
130
130
  if (["DELETE", "PUT"].includes(options.method))
131
131
  return;
132
132
  if (response.status === 404)
133
- throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(response.headers)}`);
133
+ throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`);
134
134
  return parseAsText ? await response.text() : await response.json();
135
135
  }
136
136
  /**
@@ -262,7 +262,7 @@ export class LavalinkNode {
262
262
  const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
263
263
  r.method = "PATCH";
264
264
  r.headers["Content-Type"] = "application/json";
265
- r.body = JSON.stringify(data.playerOptions);
265
+ r.body = safeStringify(data.playerOptions);
266
266
  if (data.noReplace) {
267
267
  const url = new URL(`${this.restAddress}${r.path}`);
268
268
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
@@ -272,7 +272,7 @@ export class LavalinkNode {
272
272
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
273
273
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateSuccess, {
274
274
  state: "log",
275
- message: `Player get's updated with following payload :: ${JSON.stringify(data.playerOptions, null, 3)}`,
275
+ message: `Player get's updated with following payload :: ${safeStringify(data.playerOptions, 3)}`,
276
276
  functionLayer: "LavalinkNode > node > updatePlayer()",
277
277
  });
278
278
  }
@@ -584,7 +584,7 @@ export class LavalinkNode {
584
584
  return this.request(`/sessions/${this.sessionId}`, r => {
585
585
  r.method = "PATCH";
586
586
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
587
- r.body = JSON.stringify(data);
587
+ r.body = safeStringify(data);
588
588
  });
589
589
  }
590
590
  /**
@@ -628,7 +628,7 @@ export class LavalinkNode {
628
628
  // return the decoded + builded tracks
629
629
  return await this.request(`/decodetracks`, r => {
630
630
  r.method = "POST";
631
- r.body = JSON.stringify(encodeds);
631
+ r.body = safeStringify(encodeds);
632
632
  r.headers["Content-Type"] = "application/json";
633
633
  }).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
634
634
  }
@@ -804,7 +804,7 @@ export class LavalinkNode {
804
804
  return await this.request(`/routeplanner/free/address`, r => {
805
805
  r.method = "POST";
806
806
  r.headers["Content-Type"] = "application/json";
807
- r.body = JSON.stringify({ address });
807
+ r.body = safeStringify({ address });
808
808
  });
809
809
  },
810
810
  /**
@@ -1214,6 +1214,9 @@ export class LavalinkNode {
1214
1214
  return this.queueEnd(player, track, payload);
1215
1215
  // If a track had an error while starting
1216
1216
  if (["loadFailed", "cleanup"].includes(payload.reason)) {
1217
+ //Dont add tracks if the player is already destroying.
1218
+ if (player.get("internal_destroystatus") === true)
1219
+ return;
1217
1220
  await queueTrackEnd(player);
1218
1221
  // if no track available, end queue
1219
1222
  if (!player.queue.current)
@@ -1269,7 +1272,7 @@ export class LavalinkNode {
1269
1272
  // If there are no songs in the queue
1270
1273
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
1271
1274
  try { //Sometimes the trackStuck event triggers from the Lavalink server, but the track continues playing or resumes after. We explicitly end the track in such cases
1272
- await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1275
+ await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1273
1276
  return;
1274
1277
  }
1275
1278
  catch {
@@ -1377,7 +1380,7 @@ export class LavalinkNode {
1377
1380
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
1378
1381
  r.method = "PUT";
1379
1382
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
1380
- r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
1383
+ r.body = safeStringify(segments.map(v => v.toLowerCase()));
1381
1384
  });
1382
1385
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1383
1386
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.SetSponsorBlock, {
@@ -2,7 +2,7 @@ import { DebugEvents } from "./Constants.js";
2
2
  import { bandCampSearch } from "./CustomSearches/BandCampSearch.js";
3
3
  import { FilterManager } from "./Filters.js";
4
4
  import { Queue, QueueSaver } from "./Queue.js";
5
- import { queueTrackEnd } from "./Utils.js";
5
+ import { queueTrackEnd, safeStringify } from "./Utils.js";
6
6
  export class Player {
7
7
  /** Filter Manager per player */
8
8
  filterManager;
@@ -184,7 +184,11 @@ export class Player {
184
184
  }
185
185
  }
186
186
  if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
187
- options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
187
+ options.clientTrack.userData = {
188
+ ...(typeof options?.clientTrack?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.clientTrack?.requester || {}) } : {}),
189
+ ...options?.clientTrack.userData,
190
+ ...options.track?.userData,
191
+ };
188
192
  options.track = {
189
193
  encoded: options.clientTrack?.encoded,
190
194
  requester: options.clientTrack?.requester,
@@ -206,16 +210,11 @@ export class Player {
206
210
  const track = Object.fromEntries(Object.entries({
207
211
  encoded: options.track.encoded,
208
212
  identifier: options.track.identifier,
213
+ userData: {
214
+ ...(typeof options?.track?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {}) } : {}),
215
+ ...options.track.userData,
216
+ }
209
217
  }).filter(v => typeof v[1] !== "undefined"));
210
- if (typeof options.track.userData === "object")
211
- track.userData = {
212
- ...(options.track.userData || {})
213
- };
214
- if (typeof options?.track?.requester === "object")
215
- track.userData = {
216
- ...(track.userData || {}),
217
- requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
218
- };
219
218
  if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
220
219
  this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayWithTrackReplace, {
221
220
  state: "log",
@@ -251,7 +250,11 @@ export class Player {
251
250
  // resolve the unresolved track
252
251
  await this.queue.current.resolve(this);
253
252
  if (typeof options.track?.userData === "object" && this.queue.current)
254
- this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
253
+ this.queue.current.userData = {
254
+ ...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
255
+ ...this.queue.current?.userData,
256
+ ...options.track?.userData
257
+ };
255
258
  }
256
259
  catch (error) {
257
260
  if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
@@ -289,7 +292,11 @@ export class Player {
289
292
  track: {
290
293
  encoded: this.queue.current?.encoded || null,
291
294
  // identifier: options.identifier,
292
- userData: options?.track?.userData || {},
295
+ userData: {
296
+ ...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
297
+ ...options?.track?.userData,
298
+ ...this.queue.current?.userData,
299
+ },
293
300
  },
294
301
  volume: this.lavalinkVolume,
295
302
  position: options?.position ?? 0,
@@ -702,7 +709,7 @@ export class Player {
702
709
  await this.node.request(endpoint, r => {
703
710
  r.method = "PATCH";
704
711
  r.headers["Content-Type"] = "application/json";
705
- r.body = JSON.stringify({
712
+ r.body = safeStringify({
706
713
  voice: {
707
714
  token: voiceData.token,
708
715
  endpoint: voiceData.endpoint,
@@ -202,15 +202,16 @@ export class Queue {
202
202
  * @returns {number} Queue-Size (for the next Tracks)
203
203
  */
204
204
  async add(TrackOrTracks, index) {
205
- if (typeof index === "number" && index >= 0 && index < this.tracks.length)
206
- return await this.splice(index, 0, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
205
+ if (typeof index === "number" && index >= 0 && index < this.tracks.length) {
206
+ return await this.splice(index, 0, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
207
+ }
207
208
  const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
208
209
  // add the track(s)
209
- this.tracks.push(...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
210
+ this.tracks.push(...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
210
211
  // log if available
211
212
  if (typeof this.queueChanges?.tracksAdd === "function")
212
213
  try {
213
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), this.tracks.length, oldStored, this.utils.toJSON());
214
+ this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), this.tracks.length, oldStored, this.utils.toJSON());
214
215
  }
215
216
  catch { /* */ }
216
217
  // save the queue
@@ -236,11 +237,11 @@ export class Queue {
236
237
  // Log if available
237
238
  if ((TrackOrTracks) && typeof this.queueChanges?.tracksAdd === "function")
238
239
  try {
239
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), index, oldStored, this.utils.toJSON());
240
+ this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), index, oldStored, this.utils.toJSON());
240
241
  }
241
242
  catch { /* */ }
242
243
  // remove the tracks (and add the new ones)
243
- let spliced = TrackOrTracks ? this.tracks.splice(index, amount, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))) : this.tracks.splice(index, amount);
244
+ let spliced = TrackOrTracks ? this.tracks.splice(index, amount, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter(v => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))) : this.tracks.splice(index, amount);
244
245
  // get the spliced array
245
246
  spliced = (Array.isArray(spliced) ? spliced : [spliced]);
246
247
  // Log if available
@@ -113,3 +113,4 @@ export declare class MiniMap<K, V> extends Map<K, V> {
113
113
  map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
114
114
  }
115
115
  export declare function queueTrackEnd(player: Player, dontShiftQueue?: boolean): Promise<Track>;
116
+ export declare function safeStringify(obj: any, padding?: number): string;
@@ -445,7 +445,7 @@ export async function queueTrackEnd(player, dontShiftQueue = false) {
445
445
  // and if repeatMode == queue, add it back to the queue!
446
446
  if (player.repeatMode === "queue" && player.queue.current)
447
447
  player.queue.tracks.push(player.queue.current);
448
- // change the current Track to the next upcoming one
448
+ // change the current Track to the next upcoming one
449
449
  const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
450
450
  try {
451
451
  if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
@@ -540,3 +540,20 @@ async function getClosestTrack(data, player) {
540
540
  return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
541
541
  });
542
542
  }
543
+ export function safeStringify(obj, padding = 0) {
544
+ const seen = new WeakSet();
545
+ return JSON.stringify(obj, (key, value) => {
546
+ if (typeof value === "function")
547
+ return undefined; // Funktion skippen
548
+ if (typeof value === "symbol")
549
+ return undefined; // Symbol skippen
550
+ if (typeof value === "bigint")
551
+ return value.toString(); // BigInt to String
552
+ if (typeof value === "object" && value !== null) {
553
+ if (seen.has(value))
554
+ return "[Circular]";
555
+ seen.add(value);
556
+ }
557
+ return value;
558
+ }, padding);
559
+ }
@@ -2,9 +2,9 @@ import { EventEmitter } from "events";
2
2
  import { NodeManager } from "./NodeManager";
3
3
  import { Player } from "./Player";
4
4
  import { ManagerUtils, MiniMap } from "./Utils";
5
+ import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils";
5
6
  import type { BotClientOptions, LavalinkManagerEvents, ManagerOptions } from "./Types/Manager";
6
7
  import type { PlayerOptions } from "./Types/Player";
7
- import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils";
8
8
  export declare class LavalinkManager extends EventEmitter {
9
9
  /**
10
10
  * Emit an event
@@ -1,7 +1,7 @@
1
+ import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils";
1
2
  import type { Player } from "./Player";
2
3
  import type { DestroyReasonsType, DisconnectReasonsType } from "./Types/Player";
3
4
  import type { Track } from "./Types/Track";
4
- import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils";
5
5
  import type { NodeManager } from "./NodeManager";
6
6
  import type { BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, LyricsResult, ModifyRequest, NodeStats, SponsorBlockSegment } from "./Types/Node";
7
7
  /**
@@ -113,3 +113,4 @@ export declare class MiniMap<K, V> extends Map<K, V> {
113
113
  map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
114
114
  }
115
115
  export declare function queueTrackEnd(player: Player, dontShiftQueue?: boolean): Promise<Track>;
116
+ export declare function safeStringify(obj: any, padding?: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -59,18 +59,18 @@
59
59
  "homepage": "https://tomato6966.github.io/lavalink-client/",
60
60
  "devDependencies": {
61
61
  "@eslint/eslintrc": "^3.3.1",
62
- "@eslint/js": "^9.25.0",
63
- "@types/node": "^22.14.1",
62
+ "@eslint/js": "^9.27.0",
63
+ "@types/node": "^22.15.18",
64
64
  "@types/ws": "^8.18.1",
65
- "@typescript-eslint/eslint-plugin": "^8.30.1",
66
- "@typescript-eslint/parser": "^8.30.1",
67
- "eslint": "^9.25.0",
68
- "tsc-alias": "^1.8.15",
65
+ "@typescript-eslint/eslint-plugin": "^8.32.1",
66
+ "@typescript-eslint/parser": "^8.32.1",
67
+ "eslint": "^9.27.0",
68
+ "tsc-alias": "^1.8.16",
69
69
  "typescript": "^5.8.3"
70
70
  },
71
71
  "dependencies": {
72
72
  "tslib": "^2.8.1",
73
- "ws": "^8.18.1"
73
+ "ws": "^8.18.2"
74
74
  },
75
75
  "engines": {
76
76
  "node": ">=18.0.0",