lavalink-client 2.5.0 → 2.5.2

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.
Files changed (31) hide show
  1. package/dist/cjs/structures/Filters.js +3 -2
  2. package/dist/cjs/structures/LavalinkManager.d.ts +1 -1
  3. package/dist/cjs/structures/LavalinkManager.js +15 -15
  4. package/dist/cjs/structures/LavalinkManagerStatics.js +15 -1
  5. package/dist/cjs/structures/Node.d.ts +1 -1
  6. package/dist/cjs/structures/Node.js +20 -10
  7. package/dist/cjs/structures/Player.js +56 -13
  8. package/dist/cjs/structures/Types/Manager.d.ts +2 -0
  9. package/dist/cjs/structures/Types/Track.d.ts +1 -1
  10. package/dist/cjs/structures/Types/Utils.d.ts +5 -5
  11. package/dist/cjs/structures/Utils.d.ts +2 -1
  12. package/dist/cjs/structures/Utils.js +46 -4
  13. package/dist/esm/structures/Filters.js +3 -2
  14. package/dist/esm/structures/LavalinkManager.d.ts +1 -1
  15. package/dist/esm/structures/LavalinkManager.js +16 -16
  16. package/dist/esm/structures/LavalinkManagerStatics.js +15 -1
  17. package/dist/esm/structures/Node.d.ts +1 -1
  18. package/dist/esm/structures/Node.js +21 -11
  19. package/dist/esm/structures/Player.js +57 -14
  20. package/dist/esm/structures/Types/Manager.d.ts +2 -0
  21. package/dist/esm/structures/Types/Track.d.ts +1 -1
  22. package/dist/esm/structures/Types/Utils.d.ts +5 -5
  23. package/dist/esm/structures/Utils.d.ts +2 -1
  24. package/dist/esm/structures/Utils.js +45 -4
  25. package/dist/types/structures/LavalinkManager.d.ts +1 -1
  26. package/dist/types/structures/Node.d.ts +1 -1
  27. package/dist/types/structures/Types/Manager.d.ts +2 -0
  28. package/dist/types/structures/Types/Track.d.ts +1 -1
  29. package/dist/types/structures/Types/Utils.d.ts +5 -5
  30. package/dist/types/structures/Utils.d.ts +2 -1
  31. package/package.json +11 -11
@@ -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,11 +491,11 @@ 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)
498
- console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, playerVoice: player.voice, update });
498
+ console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice });
499
499
  }
500
500
  else {
501
501
  await player.node.updatePlayer({
@@ -505,18 +505,18 @@ class LavalinkManager extends events_1.EventEmitter {
505
505
  token: update.token,
506
506
  endpoint: update.endpoint,
507
507
  sessionId: sessionId2Use,
508
- }
509
- }
508
+ },
509
+ },
510
510
  });
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
  }
518
518
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
519
- console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, } });
519
+ console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, playerVoice: player.voice, update });
520
520
  }
521
521
  return;
522
522
  }
@@ -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;
@@ -51,6 +51,12 @@ exports.DefaultSources = {
51
51
  "vkmusic": "vksearch",
52
52
  "vk music": "vksearch",
53
53
  "vkrec": "vkrec",
54
+ "vk": "vksearch",
55
+ // Qobuz (lavasrc)
56
+ "qbsearch": "qbsearch",
57
+ "qobuz": "qbsearch",
58
+ "qbisrc": "qbisrc",
59
+ "qbrec": "qbrec",
54
60
  // speak PLUGIN
55
61
  "speak": "speak",
56
62
  "tts": "tts",
@@ -73,6 +79,12 @@ exports.DefaultSources = {
73
79
  "https": "https",
74
80
  "link": "link",
75
81
  "uri": "uri",
82
+ // tidal
83
+ "tidal": "tdsearch",
84
+ "td": "tdsearch",
85
+ "tidal music": "tdsearch",
86
+ "tdsearch": "tdsearch",
87
+ "tdrec": "tdrec",
76
88
  // jiosaavn
77
89
  "jiosaavn": "jssearch",
78
90
  "js": "jssearch",
@@ -125,8 +137,10 @@ exports.SourceLinksRegexes = {
125
137
  SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
126
138
  AllSpotifyRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?(?<type>track|album|playlist|artist|episode|show)\/(?<identifier>[a-zA-Z0-9-_]+)/,
127
139
  appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,
140
+ /** From tidal */
141
+ tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
128
142
  /** From jiosaavn-plugin */
129
- 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-_/,]+)/,
130
144
  /** FROM DUNCTE BOT PLUGIN */
131
145
  tiktok: /https:\/\/www\.tiktok\.com\//,
132
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)
@@ -1271,8 +1274,15 @@ class LavalinkNode {
1271
1274
  }
1272
1275
  this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
1273
1276
  // If there are no songs in the queue
1274
- if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1275
- return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1277
+ if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
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
1279
+ await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1280
+ return;
1281
+ }
1282
+ catch {
1283
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1284
+ }
1285
+ }
1276
1286
  // remove the current track, and enqueue the next one
1277
1287
  await (0, Utils_1.queueTrackEnd)(player);
1278
1288
  // if no track available, end queue
@@ -1281,7 +1291,7 @@ class LavalinkNode {
1281
1291
  }
1282
1292
  // play track if autoSkip is true
1283
1293
  if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1284
- player.play({ noReplace: true });
1294
+ player.play({ track: player.queue.current, noReplace: false }); // Replace the stuck track with the new track.
1285
1295
  }
1286
1296
  return;
1287
1297
  }
@@ -1374,7 +1384,7 @@ class LavalinkNode {
1374
1384
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
1375
1385
  r.method = "PUT";
1376
1386
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
1377
- r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
1387
+ r.body = (0, Utils_1.safeStringify)(segments.map(v => v.toLowerCase()));
1378
1388
  });
1379
1389
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1380
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) {
@@ -270,6 +273,8 @@ class Player {
270
273
  delete options.clientTrack;
271
274
  if (options && "track" in options)
272
275
  delete options.track;
276
+ // get rid of the current song without shifting the queue, so that the shifting can happen inside the next .play() call when "autoSkipOnResolveError" is true
277
+ await (0, Utils_1.queueTrackEnd)(this, true);
273
278
  // try to play the next track if possible
274
279
  if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
275
280
  return this.play(options);
@@ -290,7 +295,11 @@ class Player {
290
295
  track: {
291
296
  encoded: this.queue.current?.encoded || null,
292
297
  // identifier: options.identifier,
293
- 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
+ },
294
303
  },
295
304
  volume: this.lavalinkVolume,
296
305
  position: options?.position ?? 0,
@@ -405,6 +414,8 @@ class Player {
405
414
  const now = performance.now();
406
415
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
407
416
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
417
+ // emit the event
418
+ this.LavalinkManager.emit("playerPaused", this, this.queue.current);
408
419
  return this;
409
420
  }
410
421
  /**
@@ -417,6 +428,8 @@ class Player {
417
428
  const now = performance.now();
418
429
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
419
430
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
431
+ // emit the event
432
+ this.LavalinkManager.emit("playerResumed", this, this.queue.current);
420
433
  return this;
421
434
  }
422
435
  /**
@@ -682,6 +695,7 @@ class Player {
682
695
  }
683
696
  const data = this.toJSON();
684
697
  const currentTrack = this.queue.current;
698
+ const segments = await this.getSponsorBlock().catch(() => []);
685
699
  const voiceData = this.voice;
686
700
  if (!voiceData.endpoint ||
687
701
  !voiceData.sessionId ||
@@ -698,7 +712,7 @@ class Player {
698
712
  await this.node.request(endpoint, r => {
699
713
  r.method = "PATCH";
700
714
  r.headers["Content-Type"] = "application/json";
701
- r.body = JSON.stringify({
715
+ r.body = (0, Utils_1.safeStringify)({
702
716
  voice: {
703
717
  token: voiceData.token,
704
718
  endpoint: voiceData.endpoint,
@@ -706,6 +720,33 @@ class Player {
706
720
  }
707
721
  });
708
722
  });
723
+ const hasSponsorBlock = this.node.info?.plugins?.find(v => v.name === "sponsorblock-plugin");
724
+ if (hasSponsorBlock) {
725
+ if (segments.length) {
726
+ await this.setSponsorBlock(segments).catch(error => {
727
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
728
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerChangeNode, {
729
+ state: "error",
730
+ error: error,
731
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
732
+ functionLayer: "Player > changeNode()",
733
+ });
734
+ }
735
+ });
736
+ }
737
+ else {
738
+ await this.setSponsorBlock().catch(error => {
739
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
740
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerChangeNode, {
741
+ state: "error",
742
+ error: error,
743
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
744
+ functionLayer: "Player > changeNode()",
745
+ });
746
+ }
747
+ });
748
+ }
749
+ }
709
750
  if (currentTrack) { // If there is a current track, send it to the new node.
710
751
  await this.node.updatePlayer({
711
752
  guildId: this.guildId,
@@ -719,6 +760,8 @@ class Player {
719
760
  }
720
761
  });
721
762
  }
763
+ this.paused = data.paused;
764
+ this.playing = data.playing;
722
765
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
723
766
  return this.node.id;
724
767
  }
@@ -172,6 +172,8 @@ export interface LavalinkManagerEvents {
172
172
  * @event Manager#LyricsNotFound
173
173
  */
174
174
  "LyricsNotFound": (player: Player, track: Track | UnresolvedTrack | null, payload: LyricsNotFoundEvent) => void;
175
+ "playerResumed": (player: Player, track: Track | UnresolvedTrack | null) => void;
176
+ "playerPaused": (player: Player, track: Track | UnresolvedTrack | null) => void;
175
177
  }
176
178
  /**
177
179
  * The Bot client Options needed for the manager
@@ -4,7 +4,7 @@ import type { Base64 } from "./Utils.js";
4
4
  /** Sourcenames provided by lavalink server */
5
5
  export type LavalinkSourceNames = "youtube" | "youtubemusic" | "soundcloud" | "bandcamp" | "twitch";
6
6
  /** Source Names provided by lava src plugin */
7
- export type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts";
7
+ export type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts" | "vkmusic" | "tidal" | "qobuz";
8
8
  /** Source Names provided by jiosaavan plugin */
9
9
  export type LavalinkPlugin_JioSaavn_SourceNames = "jiosaavn";
10
10
  /** The SourceNames provided by lavalink */
@@ -11,7 +11,7 @@ export type Opaque<T, K> = T & {
11
11
  export type IntegerNumber = Opaque<number, 'Int'>;
12
12
  /** Opqaue tyep for floatnumber */
13
13
  export type FloatNumber = Opaque<number, 'Float'>;
14
- export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "dzrec" | "ymsearch" | "ymrec" | "vksearch" | "vkrec";
14
+ export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "dzrec" | "ymsearch" | "ymrec" | "vksearch" | "vkrec" | "tdsearch" | "tdrec" | "qbsearch" | "qbisrc" | "qbrec";
15
15
  export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
16
16
  export type JioSaavnSearchPlatform = "jssearch" | "jsrec";
17
17
  export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
@@ -20,9 +20,9 @@ export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
20
20
  export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | JioSaavnSearchPlatform | LavalinkClientSearchPlatform;
21
21
  export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
22
22
  export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
23
- "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "vk music" | "vkmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform | "js" | "jiosaavn";
23
+ "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "vk" | "vk music" | "vkmusic" | "tidal" | "tidal music" | "qobuz" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform | "js" | "jiosaavn" | "td" | "tidal" | "tdrec";
24
24
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
25
- export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "jiosaavn" | "appleMusic" | "TwitchTv" | "vimeo";
25
+ export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "jiosaavn" | "appleMusic" | "tidal" | "TwitchTv" | "vimeo";
26
26
  export interface PlaylistInfo {
27
27
  /** The playlist name */
28
28
  name: string;
@@ -92,13 +92,13 @@ export interface TrackEndEvent extends PlayerEvent {
92
92
  export interface TrackExceptionEvent extends PlayerEvent {
93
93
  type: "TrackExceptionEvent";
94
94
  exception?: Exception;
95
- tracK: LavalinkTrack;
95
+ track: LavalinkTrack;
96
96
  error: string;
97
97
  }
98
98
  export interface TrackStuckEvent extends PlayerEvent {
99
99
  type: "TrackStuckEvent";
100
100
  thresholdMs: number;
101
- tracK: LavalinkTrack;
101
+ track: LavalinkTrack;
102
102
  }
103
103
  export interface WebSocketClosedEvent extends PlayerEvent {
104
104
  type: "WebSocketClosedEvent";
@@ -112,4 +112,5 @@ export declare class MiniMap<K, V> extends Map<K, V> {
112
112
  map<T>(fn: (value: V, key: K, miniMap: this) => T): T[];
113
113
  map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
114
114
  }
115
- export declare function queueTrackEnd(player: Player): Promise<Track>;
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");
@@ -307,6 +308,9 @@ class ManagerUtils {
307
308
  if (LavalinkManagerStatics_1.SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
308
309
  throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled");
309
310
  }
311
+ if (LavalinkManagerStatics_1.SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
312
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
313
+ }
310
314
  return;
311
315
  }
312
316
  transformQuery(query) {
@@ -371,6 +375,12 @@ class ManagerUtils {
371
375
  if (source === "speak" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
372
376
  throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
373
377
  }
378
+ if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
379
+ throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdsearch' work");
380
+ }
381
+ if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
382
+ throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
383
+ }
374
384
  if (source === "tts" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
375
385
  throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
376
386
  }
@@ -386,6 +396,21 @@ class ManagerUtils {
386
396
  if (source === "ytsearch" && !node.info?.sourceManagers?.includes("youtube")) {
387
397
  throw new Error("Lavalink Node has not 'youtube' enabled, which is required to have 'ytsearch' work");
388
398
  }
399
+ if (source === "vksearch" && !node.info?.sourceManagers?.includes("vkmusic")) {
400
+ throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vksearch' work");
401
+ }
402
+ if (source === "vkrec" && !node.info?.sourceManagers?.includes("vkmusic")) {
403
+ throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vkrec' work");
404
+ }
405
+ if (source === "qbsearch" && !node.info?.sourceManagers?.includes("qobuz")) {
406
+ throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbsearch' work");
407
+ }
408
+ if (source === "qbisrc" && !node.info?.sourceManagers?.includes("qobuz")) {
409
+ throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbisrc' work");
410
+ }
411
+ if (source === "qbrec" && !node.info?.sourceManagers?.includes("qobuz")) {
412
+ throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbrec' work");
413
+ }
389
414
  return;
390
415
  }
391
416
  }
@@ -418,7 +443,7 @@ class MiniMap extends Map {
418
443
  }
419
444
  }
420
445
  exports.MiniMap = MiniMap;
421
- async function queueTrackEnd(player) {
446
+ async function queueTrackEnd(player, dontShiftQueue = false) {
422
447
  if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
423
448
  player.queue.previous.unshift(player.queue.current);
424
449
  if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
@@ -429,9 +454,9 @@ async function queueTrackEnd(player) {
429
454
  if (player.repeatMode === "queue" && player.queue.current)
430
455
  player.queue.tracks.push(player.queue.current);
431
456
  // change the current Track to the next upcoming one
432
- const nextSong = player.queue.tracks.shift();
457
+ const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
433
458
  try {
434
- if (player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
459
+ if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
435
460
  await nextSong.resolve(player);
436
461
  player.queue.current = nextSong || null;
437
462
  // save it in the DB
@@ -448,7 +473,7 @@ async function queueTrackEnd(player) {
448
473
  }
449
474
  player.LavalinkManager.emit("trackError", player, player.queue.current, error);
450
475
  // try to play the next track if possible
451
- if (player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
476
+ if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
452
477
  return queueTrackEnd(player);
453
478
  }
454
479
  // return the new current Track
@@ -523,3 +548,20 @@ async function getClosestTrack(data, player) {
523
548
  return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
524
549
  });
525
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
+ }