lavalink-client 2.3.0 → 2.3.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.
Files changed (55) hide show
  1. package/README.md +31 -0
  2. package/dist/cjs/index.d.ts +1 -0
  3. package/dist/cjs/index.js +1 -0
  4. package/dist/cjs/structures/Constants.d.ts +44 -1
  5. package/dist/cjs/structures/Constants.js +45 -1
  6. package/dist/cjs/structures/LavalinkManager.js +80 -5
  7. package/dist/cjs/structures/Node.js +202 -20
  8. package/dist/cjs/structures/Player.d.ts +0 -1
  9. package/dist/cjs/structures/Player.js +73 -6
  10. package/dist/cjs/structures/Queue.js +29 -2
  11. package/dist/cjs/structures/Types/Manager.d.ts +25 -0
  12. package/dist/cjs/structures/Types/Queue.d.ts +1 -1
  13. package/dist/cjs/structures/Utils.js +68 -18
  14. package/dist/esm/index.d.ts +1 -0
  15. package/dist/esm/index.js +1 -0
  16. package/dist/esm/structures/Constants.d.ts +44 -1
  17. package/dist/esm/structures/Constants.js +44 -0
  18. package/dist/esm/structures/LavalinkManager.js +81 -6
  19. package/dist/esm/structures/Node.js +203 -21
  20. package/dist/esm/structures/Player.d.ts +0 -1
  21. package/dist/esm/structures/Player.js +73 -6
  22. package/dist/esm/structures/Queue.js +29 -2
  23. package/dist/esm/structures/Types/Manager.d.ts +25 -0
  24. package/dist/esm/structures/Types/Queue.d.ts +1 -1
  25. package/dist/esm/structures/Utils.js +68 -18
  26. package/dist/types/index.d.ts +1 -0
  27. package/dist/types/structures/Constants.d.ts +44 -1
  28. package/dist/types/structures/Player.d.ts +0 -1
  29. package/dist/types/structures/Types/Manager.d.ts +25 -0
  30. package/dist/types/structures/Types/Queue.d.ts +1 -1
  31. package/package.json +1 -1
  32. package/dist/index.d.ts +0 -10
  33. package/dist/index.js +0 -13
  34. package/dist/structures/Filters.d.ts +0 -230
  35. package/dist/structures/Filters.js +0 -472
  36. package/dist/structures/LavalinkManager.d.ts +0 -47
  37. package/dist/structures/LavalinkManager.js +0 -36
  38. package/dist/structures/LavalinkManagerStatics.d.ts +0 -3
  39. package/dist/structures/LavalinkManagerStatics.js +0 -76
  40. package/dist/structures/Node.d.ts +0 -171
  41. package/dist/structures/Node.js +0 -462
  42. package/dist/structures/NodeManager.d.ts +0 -58
  43. package/dist/structures/NodeManager.js +0 -25
  44. package/dist/structures/Player.d.ts +0 -101
  45. package/dist/structures/Player.js +0 -232
  46. package/dist/structures/PlayerManager.d.ts +0 -62
  47. package/dist/structures/PlayerManager.js +0 -26
  48. package/dist/structures/Queue.d.ts +0 -93
  49. package/dist/structures/Queue.js +0 -160
  50. package/dist/structures/QueueManager.d.ts +0 -77
  51. package/dist/structures/QueueManager.js +0 -74
  52. package/dist/structures/Track.d.ts +0 -27
  53. package/dist/structures/Track.js +0 -2
  54. package/dist/structures/Utils.d.ts +0 -183
  55. package/dist/structures/Utils.js +0 -43
@@ -192,7 +192,8 @@ class LavalinkNode {
192
192
  }
193
193
  let uri = `/loadtracks?identifier=`;
194
194
  if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
195
- uri += encodeURIComponent(Query.query);
195
+ const url = encodeURIComponent(Query.query);
196
+ uri += url;
196
197
  }
197
198
  else { // if not make a query out of it
198
199
  if (Query.source !== "local")
@@ -209,8 +210,16 @@ class LavalinkNode {
209
210
  });
210
211
  // transform the data which can be Error, Track or Track[] to enfore [Track]
211
212
  const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
212
- if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length))
213
+ if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length)) {
214
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
215
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.SearchNothingFound, {
216
+ state: "warn",
217
+ message: `Search found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
218
+ functionLayer: "(LavalinkNode > node | player) > search()",
219
+ });
220
+ }
213
221
  throw new Error("Nothing found");
222
+ }
214
223
  return {
215
224
  loadType: res.loadType,
216
225
  exception: res.loadType === "error" ? res.data : null,
@@ -254,8 +263,16 @@ class LavalinkNode {
254
263
  throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
255
264
  const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
256
265
  const res = (request.status === 204 ? {} : await request.json());
257
- if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length)
266
+ if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
267
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
268
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.LavaSearchNothingFound, {
269
+ state: "warn",
270
+ message: `LavaSearch found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
271
+ functionLayer: "(LavalinkNode > node | player) > lavaSearch()",
272
+ });
273
+ }
258
274
  throw new Error("Nothing found");
275
+ }
259
276
  return {
260
277
  tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
261
278
  albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
@@ -291,6 +308,13 @@ class LavalinkNode {
291
308
  r.path = url.pathname + url.search;
292
309
  }
293
310
  });
311
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
312
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerUpdateSuccess, {
313
+ state: "log",
314
+ message: `Player get's updated with following payload :: ${JSON.stringify(data.playerOptions, null, 3)}`,
315
+ functionLayer: "LavalinkNode > node > updatePlayer()",
316
+ });
317
+ }
294
318
  return this.syncPlayerData({}, res), res;
295
319
  }
296
320
  /**
@@ -323,8 +347,16 @@ class LavalinkNode {
323
347
  * ```
324
348
  */
325
349
  connect(sessionId) {
326
- if (this.connected)
350
+ if (this.connected) {
351
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
352
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TryingConnectWhileConnected, {
353
+ state: "warn",
354
+ message: `Tryed to connect to node, but it's already connected!`,
355
+ functionLayer: "LavalinkNode > node > connect()",
356
+ });
357
+ }
327
358
  return;
359
+ }
328
360
  const headers = {
329
361
  Authorization: this.options.authorization,
330
362
  "User-Id": this.NodeManager.LavalinkManager.options.client.id,
@@ -342,11 +374,33 @@ class LavalinkNode {
342
374
  // this.socket.on("ping", () => this.heartBeat("ping")); // lavalink doesn'T send ping periodically, therefore we use the stats message
343
375
  }
344
376
  heartBeat() {
377
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
378
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.HeartBeatTriggered, {
379
+ state: "log",
380
+ message: `Node Socket Heartbeat triggered, resetting old Timeout to 65000ms (should happen every 60s due to /stats event)`,
381
+ functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat()",
382
+ });
383
+ }
345
384
  if (this.pingTimeout)
346
385
  clearTimeout(this.pingTimeout);
347
386
  this.pingTimeout = setTimeout(() => {
348
- if (!this.socket)
349
- return console.error("Node-Ping-Acknowledge-Timeout - Socket not available - maybe reconnecting?");
387
+ if (!this.socket) {
388
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
389
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.NoSocketOnDestroy, {
390
+ state: "error",
391
+ message: `Heartbeat registered a disconnect, but socket didn't exist therefore can't terminate`,
392
+ functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat() > timeoutHit",
393
+ });
394
+ }
395
+ return;
396
+ }
397
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
398
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.SocketTerminateHeartBeatTimeout, {
399
+ state: "warn",
400
+ message: `Heartbeat registered a disconnect, because timeout wasn't resetted in time. Terminating Web-Socket`,
401
+ functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat() > timeoutHit",
402
+ });
403
+ }
350
404
  this.isAlive = false;
351
405
  this.socket.terminate();
352
406
  }, 65000); // the stats endpoint get's sent every 60s. se wee add a 5s buffer to make sure we don't miss any stats message
@@ -838,8 +892,16 @@ class LavalinkNode {
838
892
  case "playerUpdate":
839
893
  {
840
894
  const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
841
- if (!player)
895
+ if (!player) {
896
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
897
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerUpdateNoPlayer, {
898
+ state: "error",
899
+ message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
900
+ functionLayer: "LavalinkNode > nodeEvent > playerUpdate",
901
+ });
902
+ }
842
903
  return;
904
+ }
843
905
  const oldPlayer = player?.toJSON();
844
906
  player.lastPositionChange = Date.now();
845
907
  player.lastPosition = payload.state.position || 0;
@@ -849,6 +911,13 @@ class LavalinkNode {
849
911
  player.createdTimeStamp = payload.state.time;
850
912
  if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600000) || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))) {
851
913
  player.filterManager.filterUpdatedState = false;
914
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
915
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerUpdateFilterFixApply, {
916
+ state: "log",
917
+ message: `Fixing FilterState on "${player.guildId}" because player.options.instaUpdateFiltersFix === true`,
918
+ functionLayer: "LavalinkNode > nodeEvent > playerUpdate",
919
+ });
920
+ }
852
921
  await player.seek(player.position);
853
922
  }
854
923
  this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
@@ -865,7 +934,14 @@ class LavalinkNode {
865
934
  this.NodeManager.emit("resumed", this, payload, await this.fetchAllPlayers());
866
935
  }
867
936
  catch (e) {
868
- console.error("Failed to fetch players for resumed event, falling back without players array", e);
937
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
938
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.ResumingFetchingError, {
939
+ state: "error",
940
+ message: `Failed to fetch players for resumed event, falling back without players array`,
941
+ error: e,
942
+ functionLayer: "LavalinkNode > nodeEvent > resumed",
943
+ });
944
+ }
869
945
  this.NodeManager.emit("resumed", this, payload, []);
870
946
  }
871
947
  }
@@ -926,12 +1002,30 @@ class LavalinkNode {
926
1002
  player.playing = true;
927
1003
  player.paused = false;
928
1004
  // don't emit the event if previous track == new track aka track loop
929
- if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier)
1005
+ if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier) {
1006
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1007
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStartNewSongsOnly, {
1008
+ state: "log",
1009
+ message: `TrackStart not Emitting, because playing the previous song again.`,
1010
+ functionLayer: "LavalinkNode > trackStart()",
1011
+ });
1012
+ }
930
1013
  return;
1014
+ }
931
1015
  if (!player.queue.current) {
932
1016
  player.queue.current = await this.getTrackOfPayload(payload);
933
- if (player.queue.current)
1017
+ if (player.queue.current) {
934
1018
  await player.queue.utils.save();
1019
+ }
1020
+ else {
1021
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1022
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStartNoTrack, {
1023
+ state: "warn",
1024
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1025
+ functionLayer: "LavalinkNode > trackStart()",
1026
+ });
1027
+ }
1028
+ }
935
1029
  }
936
1030
  return this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
937
1031
  }
@@ -940,6 +1034,13 @@ class LavalinkNode {
940
1034
  const trackToUse = track || await this.getTrackOfPayload(payload);
941
1035
  // If a track was forcibly played
942
1036
  if (payload.reason === "replaced") {
1037
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1038
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackEndReplaced, {
1039
+ state: "warn",
1040
+ message: `TrackEnd Event does not handle any playback, because the track was replaced.`,
1041
+ functionLayer: "LavalinkNode > trackEnd()",
1042
+ });
1043
+ }
943
1044
  return this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
944
1045
  }
945
1046
  // If there are no songs in the queue
@@ -965,10 +1066,10 @@ class LavalinkNode {
965
1066
  player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
966
1067
  await player.queue.utils.save();
967
1068
  }
968
- player.set("internal_skipped", false);
969
1069
  // if no track available, end queue
970
1070
  if (!player.queue.current)
971
1071
  return this.queueEnd(player, trackToUse, payload);
1072
+ player.set("internal_skipped", false);
972
1073
  // fire event
973
1074
  this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
974
1075
  // play track if autoSkip is true
@@ -976,6 +1077,21 @@ class LavalinkNode {
976
1077
  }
977
1078
  /** @private util function for handling trackStuck event */
978
1079
  async trackStuck(player, track, payload) {
1080
+ if (this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
1081
+ const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || [])
1082
+ .filter(v => Date.now() - v < this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold);
1083
+ player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
1084
+ if (oldTimestamps.length > this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
1085
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1086
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStuckMaxTracksErroredPerTime, {
1087
+ state: "log",
1088
+ message: `trackStuck Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
1089
+ functionLayer: "LavalinkNode > trackStuck()",
1090
+ });
1091
+ }
1092
+ return player.destroy(Constants_1.DestroyReasons.TrackStuckMaxTracksErroredPerTime);
1093
+ }
1094
+ }
979
1095
  this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
980
1096
  // If there are no songs in the queue
981
1097
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
@@ -983,13 +1099,29 @@ class LavalinkNode {
983
1099
  // remove the current track, and enqueue the next one
984
1100
  await (0, Utils_1.queueTrackEnd)(player);
985
1101
  // if no track available, end queue
986
- if (!player.queue.current)
1102
+ if (!player.queue.current) {
987
1103
  return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1104
+ }
988
1105
  // play track if autoSkip is true
989
1106
  return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
990
1107
  }
991
1108
  /** @private util function for handling trackError event */
992
1109
  async trackError(player, track, payload) {
1110
+ if (this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
1111
+ const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || [])
1112
+ .filter(v => Date.now() - v < this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold);
1113
+ player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
1114
+ if (oldTimestamps.length > this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
1115
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1116
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackErrorMaxTracksErroredPerTime, {
1117
+ state: "log",
1118
+ message: `TrackError Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
1119
+ functionLayer: "LavalinkNode > trackError()",
1120
+ });
1121
+ }
1122
+ return player.destroy(Constants_1.DestroyReasons.TrackErrorMaxTracksErroredPerTime);
1123
+ }
1124
+ }
993
1125
  this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
994
1126
  return; // get's handled by trackEnd
995
1127
  // If there are no songs in the queue
@@ -1073,6 +1205,13 @@ class LavalinkNode {
1073
1205
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
1074
1206
  r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
1075
1207
  });
1208
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1209
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.SetSponsorBlock, {
1210
+ state: "log",
1211
+ message: `SponsorBlock was set for Player: ${player.guildId} to: ${segments.map(v => `'${v.toLowerCase()}'`).join(", ")}`,
1212
+ functionLayer: "LavalinkNode > setSponsorBlock()",
1213
+ });
1214
+ }
1076
1215
  return;
1077
1216
  }
1078
1217
  /**
@@ -1094,6 +1233,13 @@ class LavalinkNode {
1094
1233
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
1095
1234
  r.method = "DELETE";
1096
1235
  });
1236
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1237
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.DeleteSponsorBlock, {
1238
+ state: "log",
1239
+ message: `SponsorBlock was deleted for Player: ${player.guildId}`,
1240
+ functionLayer: "LavalinkNode > deleteSponsorBlock()",
1241
+ });
1242
+ }
1097
1243
  return;
1098
1244
  }
1099
1245
  /** private util function for handling the queue end event */
@@ -1102,17 +1248,46 @@ class LavalinkNode {
1102
1248
  player.queue.current = null;
1103
1249
  player.playing = false;
1104
1250
  player.set("internal_stopPlaying", undefined);
1251
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1252
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.QueueEnded, {
1253
+ state: "log",
1254
+ message: `Queue Ended because no more Tracks were in the Queue, due to EventName: "${payload.type}"`,
1255
+ functionLayer: "LavalinkNode > queueEnd()",
1256
+ });
1257
+ }
1105
1258
  if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.get("internal_autoplayStopPlaying") === "undefined") {
1106
- await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
1107
- if (player.queue.tracks.length > 0)
1108
- await (0, Utils_1.queueTrackEnd)(player);
1109
- if (player.queue.current) {
1110
- if (payload.type === "TrackEndEvent")
1111
- this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
1112
- return player.play({ noReplace: true, paused: false });
1259
+ const previousAutoplayTime = player.get("internal_previousautoplay");
1260
+ const duration = previousAutoplayTime ? Date.now() - previousAutoplayTime : 0;
1261
+ if ((duration && duration > this.NodeManager.LavalinkManager.options.playerOptions.minAutoPlayMs) || !!player.get("internal_skipped")) {
1262
+ await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
1263
+ player.set("internal_previousautoplay", Date.now());
1264
+ if (player.queue.tracks.length > 0)
1265
+ await (0, Utils_1.queueTrackEnd)(player);
1266
+ else if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1267
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.AutoplayNoSongsAdded, {
1268
+ state: "warn",
1269
+ message: `Autoplay was triggered but no songs were added to the queue.`,
1270
+ functionLayer: "LavalinkNode > queueEnd() > autoplayFunction",
1271
+ });
1272
+ }
1273
+ if (player.queue.current) {
1274
+ if (payload.type === "TrackEndEvent")
1275
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
1276
+ return player.play({ noReplace: true, paused: false });
1277
+ }
1278
+ }
1279
+ else {
1280
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1281
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.AutoplayThresholdSpamLimiter, {
1282
+ state: "warn",
1283
+ message: `Autoplay was triggered after the previousautoplay too early. Threshold is: ${this.NodeManager.LavalinkManager.options.playerOptions.minAutoPlayMs}ms and the Duration was ${duration}ms`,
1284
+ functionLayer: "LavalinkNode > queueEnd() > autoplayFunction",
1285
+ });
1286
+ }
1113
1287
  }
1114
1288
  }
1115
- player.set("internal_autoplayStopPlaying", undefined);
1289
+ player.set("internal_skipped", false);
1290
+ player.set("internal_autoplayStopPlaying", Date.now());
1116
1291
  if (track && !track?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
1117
1292
  player.queue.previous.unshift(track);
1118
1293
  if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
@@ -1126,6 +1301,13 @@ class LavalinkNode {
1126
1301
  if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
1127
1302
  return player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1128
1303
  else {
1304
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1305
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TriggerQueueEmptyInterval, {
1306
+ state: "log",
1307
+ message: `Trigger Queue Empty Interval was Triggered because playerOptions.onEmptyQueue.destroyAfterMs is set to ${this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs}ms`,
1308
+ functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs",
1309
+ });
1310
+ }
1129
1311
  if (player.get("internal_queueempty")) {
1130
1312
  clearTimeout(player.get("internal_queueempty"));
1131
1313
  player.set("internal_queueempty", undefined);
@@ -134,7 +134,6 @@ export declare class Player {
134
134
  * @param repeatMode
135
135
  */
136
136
  setRepeatMode(repeatMode: RepeatMode): Promise<this>;
137
- 1: any;
138
137
  /**
139
138
  * Skip the current song, or a specific amount of songs
140
139
  * @param amount provide the index of the next track to skip to
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Player = void 0;
4
+ const Constants_1 = require("./Constants");
4
5
  const BandCampSearch_1 = require("./CustomSearches/BandCampSearch");
5
6
  const Filters_1 = require("./Filters");
6
7
  const Queue_1 = require("./Queue");
@@ -72,8 +73,17 @@ class Player {
72
73
  this.guildId = this.options.guildId;
73
74
  this.voiceChannelId = this.options.voiceChannelId;
74
75
  this.textChannelId = this.options.textChannelId || null;
75
- this.node = typeof this.options.node === "string" ? this.LavalinkManager.nodeManager.nodes.get(this.options.node) : this.options.node;
76
+ this.node = typeof this.options.node === "string"
77
+ ? this.LavalinkManager.nodeManager.nodes.get(this.options.node)
78
+ : this.options.node;
76
79
  if (!this.node || typeof this.node.request !== "function") {
80
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
81
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerCreateNodeNotFound, {
82
+ state: "warn",
83
+ message: `Player was created with provided node Id: ${this.options.node}, but no node with that Id was found.`,
84
+ functionLayer: "Player > constructor()",
85
+ });
86
+ }
77
87
  const least = this.LavalinkManager.nodeManager.leastUsedNodes();
78
88
  this.node = least.filter(v => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || least[0] || null;
79
89
  }
@@ -128,6 +138,13 @@ class Player {
128
138
  */
129
139
  async play(options = {}) {
130
140
  if (this.get("internal_queueempty")) {
141
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
142
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerPlayQueueEmptyTimeoutClear, {
143
+ state: "log",
144
+ message: `Player was called to play something, while there was a queueEmpty Timeout set, clearing the timeout.`,
145
+ functionLayer: "Player > play()",
146
+ });
147
+ }
131
148
  clearTimeout(this.get("internal_queueempty"));
132
149
  this.set("internal_queueempty", undefined);
133
150
  }
@@ -168,6 +185,13 @@ class Player {
168
185
  ...(track.userData || {}),
169
186
  requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
170
187
  };
188
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
189
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerPlayWithTrackReplace, {
190
+ state: "log",
191
+ message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
192
+ functionLayer: "Player > play()",
193
+ });
194
+ }
171
195
  return this.node.updatePlayer({
172
196
  guildId: this.guildId,
173
197
  noReplace: false,
@@ -185,6 +209,13 @@ class Player {
185
209
  if (!this.queue.current && this.queue.tracks.length)
186
210
  await (0, Utils_1.queueTrackEnd)(this);
187
211
  if (this.queue.current && this.LavalinkManager.utils.isUnresolvedTrack(this.queue.current)) {
212
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
213
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerPlayUnresolvedTrack, {
214
+ state: "log",
215
+ message: `Player Play was called, current Queue Song is unresolved, resolving the track.`,
216
+ functionLayer: "Player > play()",
217
+ });
218
+ }
188
219
  try {
189
220
  // resolve the unresolved track
190
221
  await this.queue.current.resolve(this);
@@ -192,6 +223,14 @@ class Player {
192
223
  this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
193
224
  }
194
225
  catch (error) {
226
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
227
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerPlayUnresolvedTrackFailed, {
228
+ state: "error",
229
+ error: error,
230
+ message: `Player Play was called, current Queue Song is unresolved, but couldn't resolve it`,
231
+ functionLayer: "Player > play() > resolve currentTrack",
232
+ });
233
+ }
195
234
  this.LavalinkManager.emit("trackError", this, this.queue.current, error);
196
235
  if (options && "clientTrack" in options)
197
236
  delete options.clientTrack;
@@ -258,6 +297,13 @@ class Player {
258
297
  : this.volume), 1000), 0));
259
298
  const now = performance.now();
260
299
  if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
300
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
301
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerVolumeAsFilter, {
302
+ state: "log",
303
+ message: `Player Volume was set as a Filter, because LavalinkManager option "playerOptions.applyVolumeAsFilter" is true`,
304
+ functionLayer: "Player > setVolume()",
305
+ });
306
+ }
261
307
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { filters: { volume: this.lavalinkVolume / 100 } } });
262
308
  }
263
309
  else {
@@ -302,8 +348,16 @@ class Player {
302
348
  */
303
349
  async search(query, requestUser, throwOnEmpty = false) {
304
350
  const Query = this.LavalinkManager.utils.transformQuery(query);
305
- if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
351
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp")) {
352
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
353
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.BandcampSearchLokalEngine, {
354
+ state: "log",
355
+ message: `Player.search was called with a Bandcamp Query, but no bandcamp search was enabled on lavalink, searching with the custom Search Engine.`,
356
+ functionLayer: "Player > search()",
357
+ });
358
+ }
306
359
  return await (0, BandCampSearch_1.bandCampSearch)(this, Query.query, requestUser);
360
+ }
307
361
  return this.node.search(Query, requestUser, throwOnEmpty);
308
362
  }
309
363
  /**
@@ -362,7 +416,6 @@ class Player {
362
416
  this.repeatMode = repeatMode;
363
417
  return this;
364
418
  }
365
- 1;
366
419
  /**
367
420
  * Skip the current song, or a specific amount of songs
368
421
  * @param amount provide the index of the next track to skip to
@@ -468,6 +521,13 @@ class Player {
468
521
  if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
469
522
  console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
470
523
  if (this.get("internal_destroystatus") === true) {
524
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
525
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerDestroyingSomewhereElse, {
526
+ state: "warn",
527
+ message: `Player is already destroying somewhere else..`,
528
+ functionLayer: "Player > destroy()",
529
+ });
530
+ }
471
531
  if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
472
532
  console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
473
533
  return;
@@ -499,10 +559,19 @@ class Player {
499
559
  const updateNode = typeof newNode === "string" ? this.LavalinkManager.nodeManager.nodes.get(newNode) : newNode;
500
560
  if (!updateNode)
501
561
  throw new Error("Could not find the new Node");
562
+ if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
563
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerChangeNode, {
564
+ state: "log",
565
+ message: `Player.changeNode() was executed, trying to change from "${this.node.id}" to "${updateNode.id}"`,
566
+ functionLayer: "Player > changeNode()",
567
+ });
568
+ }
502
569
  const data = this.toJSON();
570
+ const currentTrack = this.queue.current;
503
571
  await this.node.destroyPlayer(this.guildId);
504
572
  this.node = updateNode;
505
573
  const now = performance.now();
574
+ await this.connect();
506
575
  await this.node.updatePlayer({
507
576
  guildId: this.guildId,
508
577
  noReplace: false,
@@ -511,9 +580,7 @@ class Player {
511
580
  volume: Math.round(Math.max(Math.min(data.volume, 1000), 0)),
512
581
  paused: data.paused,
513
582
  filters: { ...data.filters, equalizer: data.equalizer },
514
- voice: this.voice,
515
- track: this.queue.current ?? undefined
516
- // track: this.queue.current,
583
+ track: currentTrack ?? undefined
517
584
  },
518
585
  });
519
586
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
@@ -291,11 +291,18 @@ class Queue {
291
291
  * ```
292
292
  */
293
293
  async remove(removeQueryTrack) {
294
+ const oldStored = typeof this.queueChanges?.tracksRemoved === "function" ? this.utils.toJSON() : null;
294
295
  if (typeof removeQueryTrack === "number") {
295
296
  const toRemove = this.tracks[removeQueryTrack];
296
297
  if (!toRemove)
297
298
  return null;
298
299
  const removed = this.tracks.splice(removeQueryTrack, 1);
300
+ // Log if available
301
+ if (typeof this.queueChanges?.tracksRemoved === "function")
302
+ try {
303
+ this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
304
+ }
305
+ catch (e) { /* */ }
299
306
  await this.utils.save();
300
307
  return { removed };
301
308
  }
@@ -303,11 +310,18 @@ class Queue {
303
310
  if (removeQueryTrack.every(v => typeof v === "number")) {
304
311
  const removed = [];
305
312
  for (const i of removeQueryTrack) {
306
- if (this.tracks[i])
313
+ if (this.tracks[i]) {
307
314
  removed.push(...this.tracks.splice(i, 1));
315
+ }
308
316
  }
309
317
  if (!removed.length)
310
318
  return null;
319
+ // Log if available
320
+ if (typeof this.queueChanges?.tracksRemoved === "function")
321
+ try {
322
+ this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
323
+ }
324
+ catch (e) { /* */ }
311
325
  await this.utils.save();
312
326
  return { removed };
313
327
  }
@@ -322,9 +336,16 @@ class Queue {
322
336
  return null;
323
337
  const removed = [];
324
338
  for (const { i } of tracksToRemove) {
325
- if (this.tracks[i])
339
+ if (this.tracks[i]) {
326
340
  removed.push(...this.tracks.splice(i, 1));
341
+ }
327
342
  }
343
+ // Log if available
344
+ if (typeof this.queueChanges?.tracksRemoved === "function")
345
+ try {
346
+ this.queueChanges.tracksRemoved(this.guildId, removed, tracksToRemove.map(v => v.i), oldStored, this.utils.toJSON());
347
+ }
348
+ catch (e) { /* */ }
328
349
  await this.utils.save();
329
350
  return { removed };
330
351
  }
@@ -337,6 +358,12 @@ class Queue {
337
358
  if (toRemove < 0)
338
359
  return null;
339
360
  const removed = this.tracks.splice(toRemove, 1);
361
+ // Log if available
362
+ if (typeof this.queueChanges?.tracksRemoved === "function")
363
+ try {
364
+ this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
365
+ }
366
+ catch (e) { /* */ }
340
367
  await this.utils.save();
341
368
  return { removed };
342
369
  }