lavalink-client 2.2.1 → 2.3.0

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 (90) hide show
  1. package/README.md +140 -20
  2. package/dist/cjs/index.d.ts +7 -1
  3. package/dist/cjs/index.js +7 -1
  4. package/dist/cjs/structures/Constants.d.ts +40 -0
  5. package/dist/cjs/structures/Constants.js +244 -0
  6. package/dist/cjs/structures/CustomSearches/BandCampSearch.d.ts +2 -2
  7. package/dist/cjs/structures/Filters.d.ts +2 -217
  8. package/dist/cjs/structures/Filters.js +12 -236
  9. package/dist/cjs/structures/LavalinkManager.d.ts +31 -166
  10. package/dist/cjs/structures/LavalinkManager.js +60 -8
  11. package/dist/cjs/structures/LavalinkManagerStatics.d.ts +1 -1
  12. package/dist/cjs/structures/LavalinkManagerStatics.js +1 -1
  13. package/dist/cjs/structures/Node.d.ts +15 -156
  14. package/dist/cjs/structures/Node.js +140 -51
  15. package/dist/cjs/structures/NodeManager.d.ts +54 -52
  16. package/dist/cjs/structures/NodeManager.js +77 -5
  17. package/dist/cjs/structures/Player.d.ts +31 -124
  18. package/dist/cjs/structures/Player.js +78 -43
  19. package/dist/cjs/structures/Queue.d.ts +113 -42
  20. package/dist/cjs/structures/Queue.js +169 -8
  21. package/dist/cjs/structures/Types/Filters.d.ts +190 -0
  22. package/dist/cjs/structures/Types/Manager.d.ts +184 -0
  23. package/dist/cjs/structures/Types/Manager.js +2 -0
  24. package/dist/cjs/structures/Types/Node.d.ts +216 -0
  25. package/dist/cjs/structures/Types/Node.js +2 -0
  26. package/dist/cjs/structures/Types/Player.d.ts +108 -0
  27. package/dist/cjs/structures/Types/Player.js +2 -0
  28. package/dist/cjs/structures/Types/Queue.d.ts +34 -0
  29. package/dist/cjs/structures/Types/Queue.js +2 -0
  30. package/dist/cjs/structures/{Track.d.ts → Types/Track.d.ts} +4 -2
  31. package/dist/cjs/structures/Types/Track.js +2 -0
  32. package/dist/cjs/structures/Types/Utils.d.ts +367 -0
  33. package/dist/cjs/structures/Types/Utils.js +2 -0
  34. package/dist/cjs/structures/Utils.d.ts +13 -369
  35. package/dist/cjs/structures/Utils.js +40 -18
  36. package/dist/esm/index.d.ts +7 -1
  37. package/dist/esm/index.js +7 -1
  38. package/dist/esm/structures/Constants.d.ts +40 -0
  39. package/dist/esm/structures/Constants.js +241 -0
  40. package/dist/esm/structures/CustomSearches/BandCampSearch.d.ts +2 -2
  41. package/dist/esm/structures/Filters.d.ts +2 -217
  42. package/dist/esm/structures/Filters.js +7 -231
  43. package/dist/esm/structures/LavalinkManager.d.ts +31 -166
  44. package/dist/esm/structures/LavalinkManager.js +58 -6
  45. package/dist/esm/structures/LavalinkManagerStatics.d.ts +1 -1
  46. package/dist/esm/structures/LavalinkManagerStatics.js +1 -1
  47. package/dist/esm/structures/Node.d.ts +15 -156
  48. package/dist/esm/structures/Node.js +132 -43
  49. package/dist/esm/structures/NodeManager.d.ts +54 -52
  50. package/dist/esm/structures/NodeManager.js +74 -2
  51. package/dist/esm/structures/Player.d.ts +31 -124
  52. package/dist/esm/structures/Player.js +77 -42
  53. package/dist/esm/structures/Queue.d.ts +113 -42
  54. package/dist/esm/structures/Queue.js +169 -8
  55. package/dist/esm/structures/Types/Filters.d.ts +190 -0
  56. package/dist/esm/structures/Types/Manager.d.ts +184 -0
  57. package/dist/esm/structures/Types/Manager.js +1 -0
  58. package/dist/esm/structures/Types/Node.d.ts +216 -0
  59. package/dist/esm/structures/Types/Node.js +1 -0
  60. package/dist/esm/structures/Types/Player.d.ts +108 -0
  61. package/dist/esm/structures/Types/Player.js +1 -0
  62. package/dist/esm/structures/Types/Queue.d.ts +34 -0
  63. package/dist/esm/structures/Types/Queue.js +1 -0
  64. package/dist/{types/structures → esm/structures/Types}/Track.d.ts +4 -2
  65. package/dist/esm/structures/Types/Track.js +1 -0
  66. package/dist/esm/structures/Types/Utils.d.ts +367 -0
  67. package/dist/esm/structures/Types/Utils.js +1 -0
  68. package/dist/esm/structures/Utils.d.ts +13 -369
  69. package/dist/esm/structures/Utils.js +40 -18
  70. package/dist/types/index.d.ts +7 -1
  71. package/dist/types/structures/Constants.d.ts +40 -0
  72. package/dist/types/structures/CustomSearches/BandCampSearch.d.ts +2 -2
  73. package/dist/types/structures/Filters.d.ts +2 -217
  74. package/dist/types/structures/LavalinkManager.d.ts +31 -166
  75. package/dist/types/structures/LavalinkManagerStatics.d.ts +1 -1
  76. package/dist/types/structures/Node.d.ts +15 -156
  77. package/dist/types/structures/NodeManager.d.ts +54 -52
  78. package/dist/types/structures/Player.d.ts +31 -124
  79. package/dist/types/structures/Queue.d.ts +113 -42
  80. package/dist/types/structures/Types/Filters.d.ts +190 -0
  81. package/dist/types/structures/Types/Manager.d.ts +184 -0
  82. package/dist/types/structures/Types/Node.d.ts +216 -0
  83. package/dist/types/structures/Types/Player.d.ts +108 -0
  84. package/dist/types/structures/Types/Queue.d.ts +34 -0
  85. package/dist/{esm/structures → types/structures/Types}/Track.d.ts +4 -2
  86. package/dist/types/structures/Types/Utils.d.ts +367 -0
  87. package/dist/types/structures/Utils.d.ts +13 -369
  88. package/package.json +1 -1
  89. /package/dist/cjs/structures/{Track.js → Types/Filters.js} +0 -0
  90. /package/dist/esm/structures/{Track.js → Types/Filters.js} +0 -0
@@ -1,16 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LavalinkNode = exports.validSponsorBlocks = void 0;
3
+ exports.LavalinkNode = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path_1 = require("path");
6
6
  const ws_1 = tslib_1.__importDefault(require("ws"));
7
- const Player_1 = require("./Player");
7
+ const Constants_1 = require("./Constants");
8
8
  const Utils_1 = require("./Utils");
9
- exports.validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
10
9
  /**
11
10
  * Lavalink Node creator class
12
11
  */
13
12
  class LavalinkNode {
13
+ heartBeatPingTimestamp = 0;
14
+ heartBeatPongTimestamp = 0;
15
+ get heartBeatPing() {
16
+ return this.heartBeatPongTimestamp - this.heartBeatPingTimestamp;
17
+ }
18
+ heartBeatInterval;
19
+ pingTimeout;
14
20
  /** The provided Options of the Node */
15
21
  options;
16
22
  /** The amount of rest calls the node has made. */
@@ -70,8 +76,11 @@ class LavalinkNode {
70
76
  this.options = {
71
77
  secure: false,
72
78
  retryAmount: 5,
73
- retryDelay: 30e3,
79
+ retryDelay: 10e3,
74
80
  requestSignalTimeoutMS: 10000,
81
+ heartBeatInterval: 30000,
82
+ closeOnError: true,
83
+ enablePingOnStatsCheck: true,
75
84
  ...options
76
85
  };
77
86
  this.NodeManager = manager;
@@ -150,6 +159,8 @@ class LavalinkNode {
150
159
  * ```
151
160
  */
152
161
  async request(endpoint, modify, parseAsText = false) {
162
+ if (!this.connected)
163
+ throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
153
164
  const { request, options } = await this.rawRequest(endpoint, modify);
154
165
  if (["DELETE", "PUT"].includes(options.method))
155
166
  return;
@@ -234,7 +245,7 @@ class LavalinkNode {
234
245
  if (Query.source)
235
246
  this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
236
247
  if (/^https?:\/\//.test(Query.query))
237
- return await this.search({ query: Query.query, source: Query.source }, requestUser);
248
+ return this.search({ query: Query.query, source: Query.source }, requestUser);
238
249
  if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
239
250
  throw new SyntaxError(`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`);
240
251
  if (!this.info.plugins.find(v => v.name === "lavasearch-plugin"))
@@ -296,7 +307,7 @@ class LavalinkNode {
296
307
  async destroyPlayer(guildId) {
297
308
  if (!this.sessionId)
298
309
  throw new Error("The Lavalink-Node is either not ready, or not up to date!");
299
- return await this.request(`/sessions/${this.sessionId}/players/${guildId}`, r => { r.method = "DELETE"; });
310
+ return this.request(`/sessions/${this.sessionId}/players/${guildId}`, r => { r.method = "DELETE"; });
300
311
  }
301
312
  /**
302
313
  * Connect to the Lavalink Node
@@ -328,6 +339,17 @@ class LavalinkNode {
328
339
  this.socket.on("close", (code, reason) => this.close(code, reason?.toString()));
329
340
  this.socket.on("message", this.message.bind(this));
330
341
  this.socket.on("error", this.error.bind(this));
342
+ // this.socket.on("ping", () => this.heartBeat("ping")); // lavalink doesn'T send ping periodically, therefore we use the stats message
343
+ }
344
+ heartBeat() {
345
+ if (this.pingTimeout)
346
+ clearTimeout(this.pingTimeout);
347
+ this.pingTimeout = setTimeout(() => {
348
+ if (!this.socket)
349
+ return console.error("Node-Ping-Acknowledge-Timeout - Socket not available - maybe reconnecting?");
350
+ this.isAlive = false;
351
+ this.socket.terminate();
352
+ }, 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
331
353
  }
332
354
  /**
333
355
  * Get the id of the node
@@ -355,9 +377,11 @@ class LavalinkNode {
355
377
  destroy(destroyReason, deleteNode = true) {
356
378
  if (!this.connected)
357
379
  return;
358
- const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id == this.id);
380
+ const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id === this.id);
359
381
  if (players)
360
- players.forEach(p => p.destroy(destroyReason || Player_1.DestroyReasons.NodeDestroy));
382
+ players.forEach(p => {
383
+ p.destroy(destroyReason || Constants_1.DestroyReasons.NodeDestroy);
384
+ });
361
385
  this.socket.close(1000, "Node-Destroy");
362
386
  this.socket.removeAllListeners();
363
387
  this.socket = null;
@@ -386,6 +410,7 @@ class LavalinkNode {
386
410
  return false;
387
411
  return this.socket.readyState === ws_1.default.OPEN;
388
412
  }
413
+ isAlive = false;
389
414
  /**
390
415
  * Returns the current ConnectionStatus
391
416
  *
@@ -417,11 +442,7 @@ class LavalinkNode {
417
442
  async fetchAllPlayers() {
418
443
  if (!this.sessionId)
419
444
  throw new Error("The Lavalink-Node is either not ready, or not up to date!");
420
- const players = await this.request(`/sessions/${this.sessionId}/players`);
421
- if (!Array.isArray(players))
422
- return [];
423
- else
424
- return players;
445
+ return this.request(`/sessions/${this.sessionId}/players`) || [];
425
446
  }
426
447
  /**
427
448
  * Gets specific Player Information
@@ -436,7 +457,7 @@ class LavalinkNode {
436
457
  async fetchPlayer(guildId) {
437
458
  if (!this.sessionId)
438
459
  throw new Error("The Lavalink-Node is either not ready, or not up to date!");
439
- return await this.request(`/sessions/${this.sessionId}/players/${guildId}`);
460
+ return this.request(`/sessions/${this.sessionId}/players/${guildId}`);
440
461
  }
441
462
  /**
442
463
  * Updates the session with and enables/disables resuming and timeout
@@ -462,7 +483,7 @@ class LavalinkNode {
462
483
  enabled: typeof resuming === "boolean" ? resuming : false,
463
484
  timeout: typeof resuming === "boolean" && resuming === true ? timeout : null,
464
485
  };
465
- return await this.request(`/sessions/${this.sessionId}`, r => {
486
+ return this.request(`/sessions/${this.sessionId}`, r => {
466
487
  r.method = "PATCH";
467
488
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
468
489
  r.body = JSON.stringify(data);
@@ -686,7 +707,7 @@ class LavalinkNode {
686
707
  if (!player)
687
708
  return;
688
709
  if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false)
689
- return player.destroy(Player_1.DestroyReasons.LavalinkNoVoice);
710
+ return player.destroy(Constants_1.DestroyReasons.LavalinkNoVoice);
690
711
  player.ping.ws = res?.voice?.ping || player?.ping.ws;
691
712
  }
692
713
  return true;
@@ -708,11 +729,12 @@ class LavalinkNode {
708
729
  * ```
709
730
  */
710
731
  reconnect(instaReconnect = false) {
732
+ this.NodeManager.emit("reconnectinprogress", this);
711
733
  if (instaReconnect) {
712
734
  if (this.reconnectAttempts >= this.options.retryAmount) {
713
735
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
714
736
  this.NodeManager.emit("error", this, error);
715
- return this.destroy(Player_1.DestroyReasons.NodeReconnectFail);
737
+ return this.destroy(Constants_1.DestroyReasons.NodeReconnectFail);
716
738
  }
717
739
  this.socket.removeAllListeners();
718
740
  this.socket = null;
@@ -725,7 +747,7 @@ class LavalinkNode {
725
747
  if (this.reconnectAttempts >= this.options.retryAmount) {
726
748
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
727
749
  this.NodeManager.emit("error", this, error);
728
- return this.destroy(Player_1.DestroyReasons.NodeReconnectFail);
750
+ return this.destroy(Constants_1.DestroyReasons.NodeReconnectFail);
729
751
  }
730
752
  this.socket.removeAllListeners();
731
753
  this.socket = null;
@@ -736,6 +758,29 @@ class LavalinkNode {
736
758
  }
737
759
  /** @private util function for handling opening events from websocket */
738
760
  async open() {
761
+ this.isAlive = true;
762
+ // trigger heartbeat-ping timeout - this is to check wether the client lost connection without knowing it
763
+ if (this.options.enablePingOnStatsCheck)
764
+ this.heartBeat();
765
+ if (this.heartBeatInterval)
766
+ clearInterval(this.heartBeatInterval);
767
+ if (this.options.heartBeatInterval > 0) {
768
+ // everytime a pong happens, set this.isAlive to true
769
+ this.socket.on("pong", () => {
770
+ this.heartBeatPongTimestamp = performance.now();
771
+ this.isAlive = true;
772
+ });
773
+ // every x ms send a ping to lavalink to retrieve a pong later on
774
+ this.heartBeatInterval = setInterval(() => {
775
+ if (!this.socket)
776
+ return console.error("Node-Heartbeat-Interval - Socket not available - maybe reconnecting?");
777
+ if (!this.isAlive)
778
+ this.close(500, "Node-Heartbeat-Timeout");
779
+ this.isAlive = false;
780
+ this.heartBeatPingTimestamp = performance.now();
781
+ this.socket.ping();
782
+ }, this.options.heartBeatInterval || 30000);
783
+ }
739
784
  if (this.reconnectTimeout)
740
785
  clearTimeout(this.reconnectTimeout);
741
786
  // reset the reconnect attempts amount
@@ -749,6 +794,12 @@ class LavalinkNode {
749
794
  }
750
795
  /** @private util function for handling closing events from websocket */
751
796
  close(code, reason) {
797
+ if (this.pingTimeout)
798
+ clearTimeout(this.pingTimeout);
799
+ if (this.heartBeatInterval)
800
+ clearInterval(this.heartBeatInterval);
801
+ if (code === 1006 && !reason)
802
+ reason = "Socket got terminated due to no ping connection";
752
803
  this.NodeManager.emit("disconnect", this, { code, reason });
753
804
  if (code !== 1000 || reason !== "Node-Destroy")
754
805
  this.reconnect();
@@ -758,6 +809,14 @@ class LavalinkNode {
758
809
  if (!error)
759
810
  return;
760
811
  this.NodeManager.emit("error", this, error);
812
+ if (this.options.closeOnError) {
813
+ if (this.heartBeatInterval)
814
+ clearInterval(this.heartBeatInterval);
815
+ if (this.pingTimeout)
816
+ clearTimeout(this.pingTimeout);
817
+ this.socket?.close(500, "Node-Error - Force Reconnect");
818
+ }
819
+ ;
761
820
  }
762
821
  /** @private util function for handling message events from websocket */
763
822
  async message(d) {
@@ -771,6 +830,8 @@ class LavalinkNode {
771
830
  this.NodeManager.emit("raw", this, payload);
772
831
  switch (payload.op) {
773
832
  case "stats":
833
+ if (this.options.enablePingOnStatsCheck)
834
+ this.heartBeat(); // lavalink doesn'T send "ping" periodically, therefore we use the stats message to check for a ping
774
835
  delete payload.op;
775
836
  this.stats = { ...payload };
776
837
  break;
@@ -800,7 +861,13 @@ class LavalinkNode {
800
861
  this.sessionId = payload.sessionId;
801
862
  this.resuming.enabled = payload.resumed;
802
863
  if (payload.resumed === true) {
803
- this.NodeManager.emit("resumed", this, payload, await this.fetchAllPlayers());
864
+ try {
865
+ this.NodeManager.emit("resumed", this, payload, await this.fetchAllPlayers());
866
+ }
867
+ catch (e) {
868
+ console.error("Failed to fetch players for resumed event, falling back without players array", e);
869
+ this.NodeManager.emit("resumed", this, payload, []);
870
+ }
804
871
  }
805
872
  break;
806
873
  default:
@@ -810,7 +877,7 @@ class LavalinkNode {
810
877
  }
811
878
  /** @private middleware util function for handling all kind of events from websocket */
812
879
  async handleEvent(payload) {
813
- if (!payload.guildId)
880
+ if (!payload?.guildId)
814
881
  return;
815
882
  const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
816
883
  if (!player)
@@ -849,39 +916,51 @@ class LavalinkNode {
849
916
  }
850
917
  return;
851
918
  }
919
+ async getTrackOfPayload(payload) {
920
+ return "track" in payload
921
+ ? this.NodeManager.LavalinkManager.utils.buildTrack(payload.track, undefined)
922
+ : null;
923
+ }
852
924
  /** @private util function for handling trackStart event */
853
- trackStart(player, track, payload) {
925
+ async trackStart(player, track, payload) {
854
926
  player.playing = true;
855
927
  player.paused = false;
856
928
  // don't emit the event if previous track == new track aka track loop
857
929
  if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier)
858
930
  return;
859
- return this.NodeManager.LavalinkManager.emit("trackStart", player, track, payload);
931
+ if (!player.queue.current) {
932
+ player.queue.current = await this.getTrackOfPayload(payload);
933
+ if (player.queue.current)
934
+ await player.queue.utils.save();
935
+ }
936
+ return this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
860
937
  }
861
938
  /** @private util function for handling trackEnd event */
862
939
  async trackEnd(player, track, payload) {
940
+ const trackToUse = track || await this.getTrackOfPayload(payload);
941
+ // If a track was forcibly played
942
+ if (payload.reason === "replaced") {
943
+ return this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
944
+ }
863
945
  // If there are no songs in the queue
864
946
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
865
947
  return this.queueEnd(player, track, payload);
866
- // If a track was forcibly played
867
- if (payload.reason === "replaced")
868
- return this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
869
948
  // If a track had an error while starting
870
949
  if (["loadFailed", "cleanup"].includes(payload.reason)) {
871
950
  await (0, Utils_1.queueTrackEnd)(player);
872
951
  // if no track available, end queue
873
952
  if (!player.queue.current)
874
- return this.queueEnd(player, track, payload);
953
+ return this.queueEnd(player, trackToUse, payload);
875
954
  // fire event
876
- this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
955
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
877
956
  // play track if autoSkip is true
878
957
  return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
879
958
  }
880
959
  // remove tracks from the queue
881
960
  if (player.repeatMode !== "track" || player.get("internal_skipped"))
882
961
  await (0, Utils_1.queueTrackEnd)(player);
883
- else if (player.queue.current) { // If there was a current Track already and repeatmode === true, add it to the queue.
884
- player.queue.previous.unshift(player.queue.current);
962
+ else if (trackToUse && !trackToUse?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
963
+ player.queue.previous.unshift(trackToUse);
885
964
  if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
886
965
  player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
887
966
  await player.queue.utils.save();
@@ -889,60 +968,65 @@ class LavalinkNode {
889
968
  player.set("internal_skipped", false);
890
969
  // if no track available, end queue
891
970
  if (!player.queue.current)
892
- return this.queueEnd(player, track, payload);
971
+ return this.queueEnd(player, trackToUse, payload);
893
972
  // fire event
894
- this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
973
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
895
974
  // play track if autoSkip is true
896
975
  return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
897
976
  }
898
977
  /** @private util function for handling trackStuck event */
899
978
  async trackStuck(player, track, payload) {
900
- this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
979
+ this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
901
980
  // If there are no songs in the queue
902
981
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
903
- return this.queueEnd(player, track, payload);
982
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
904
983
  // remove the current track, and enqueue the next one
905
984
  await (0, Utils_1.queueTrackEnd)(player);
906
985
  // if no track available, end queue
907
986
  if (!player.queue.current)
908
- return this.queueEnd(player, track, payload);
987
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
909
988
  // play track if autoSkip is true
910
989
  return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
911
990
  }
912
991
  /** @private util function for handling trackError event */
913
992
  async trackError(player, track, payload) {
914
- this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
993
+ this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
915
994
  return; // get's handled by trackEnd
916
995
  // If there are no songs in the queue
917
996
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
918
- return this.queueEnd(player, track, payload);
997
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
919
998
  // remove the current track, and enqueue the next one
920
999
  await (0, Utils_1.queueTrackEnd)(player);
921
1000
  // if no track available, end queue
922
1001
  if (!player.queue.current)
923
- return this.queueEnd(player, track, payload);
1002
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
924
1003
  // play track if autoSkip is true
925
1004
  return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
926
1005
  }
927
1006
  /** @private util function for handling socketClosed event */
928
1007
  socketClosed(player, payload) {
929
- return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
1008
+ this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
1009
+ // i don't think this is needed.
1010
+ // this.socket = null;
1011
+ // // causing a socket reconnect
1012
+ // this.connect();
1013
+ return;
930
1014
  }
931
1015
  /** @private util function for handling SponsorBlock Segmentloaded event */
932
- SponsorBlockSegmentLoaded(player, track, payload) {
933
- return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
1016
+ async SponsorBlockSegmentLoaded(player, track, payload) {
1017
+ return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || await this.getTrackOfPayload(payload), payload);
934
1018
  }
935
1019
  /** @private util function for handling SponsorBlock SegmentSkipped event */
936
- SponsorBlockSegmentSkipped(player, track, payload) {
937
- return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
1020
+ async SponsorBlockSegmentSkipped(player, track, payload) {
1021
+ return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || await this.getTrackOfPayload(payload), payload);
938
1022
  }
939
1023
  /** @private util function for handling SponsorBlock Chaptersloaded event */
940
- SponsorBlockChaptersLoaded(player, track, payload) {
941
- return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
1024
+ async SponsorBlockChaptersLoaded(player, track, payload) {
1025
+ return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || await this.getTrackOfPayload(payload), payload);
942
1026
  }
943
1027
  /** @private util function for handling SponsorBlock Chaptersstarted event */
944
- SponsorBlockChapterStarted(player, track, payload) {
945
- return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
1028
+ async SponsorBlockChapterStarted(player, track, payload) {
1029
+ return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || await this.getTrackOfPayload(payload), payload);
946
1030
  }
947
1031
  /**
948
1032
  * Get the current sponsorblocks for the sponsorblock plugin
@@ -981,8 +1065,8 @@ class LavalinkNode {
981
1065
  if (!segments.length)
982
1066
  throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
983
1067
  // a not valid segment
984
- if (segments.some(v => !exports.validSponsorBlocks.includes(v.toLowerCase())))
985
- throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${exports.validSponsorBlocks.map(v => `'${v}'`).join(", ")}`);
1068
+ if (segments.some(v => !Constants_1.validSponsorBlocks.includes(v.toLowerCase())))
1069
+ throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${Constants_1.validSponsorBlocks.map(v => `'${v}'`).join(", ")}`);
986
1070
  // do the request
987
1071
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
988
1072
  r.method = "PUT";
@@ -1029,20 +1113,25 @@ class LavalinkNode {
1029
1113
  }
1030
1114
  }
1031
1115
  player.set("internal_autoplayStopPlaying", undefined);
1032
- player.queue.previous.unshift(track);
1116
+ if (track && !track?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
1117
+ player.queue.previous.unshift(track);
1118
+ if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
1119
+ player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
1120
+ await player.queue.utils.save();
1121
+ }
1033
1122
  if (payload?.reason !== "stopped") {
1034
1123
  await player.queue.utils.save();
1035
1124
  }
1036
1125
  if (typeof this.NodeManager.LavalinkManager.options.playerOptions?.onEmptyQueue?.destroyAfterMs === "number" && !isNaN(this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs) && this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs >= 0) {
1037
1126
  if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
1038
- return player.destroy(Player_1.DestroyReasons.QueueEmpty);
1127
+ return player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1039
1128
  else {
1040
1129
  if (player.get("internal_queueempty")) {
1041
1130
  clearTimeout(player.get("internal_queueempty"));
1042
1131
  player.set("internal_queueempty", undefined);
1043
1132
  }
1044
1133
  player.set("internal_queueempty", setTimeout(() => {
1045
- player.destroy(Player_1.DestroyReasons.QueueEmpty);
1134
+ player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1046
1135
  }, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
1047
1136
  }
1048
1137
  }
@@ -1,68 +1,56 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from "stream";
3
- import { LavalinkManager } from "./LavalinkManager";
4
- import { LavalinkNode, LavalinkNodeOptions } from "./Node";
5
- import { DestroyReasonsType } from "./Player";
6
- import { LavalinkPlayer, MiniMap } from "./Utils";
7
- type LavalinkNodeIdentifier = string;
8
- export interface NodeManagerEvents {
3
+ import { LavalinkNode } from "./Node";
4
+ import { MiniMap } from "./Utils";
5
+ import type { LavalinkNodeIdentifier, LavalinkNodeOptions, NodeManagerEvents } from "./Types/Node";
6
+ import type { LavalinkManager } from "./LavalinkManager";
7
+ export declare class NodeManager extends EventEmitter {
9
8
  /**
10
- * Emitted when a Node is created.
11
- * @event Manager.nodeManager#create
9
+ * Emit an event
10
+ * @param event The event to emit
11
+ * @param args The arguments to pass to the event
12
+ * @returns
12
13
  */
13
- "create": (node: LavalinkNode) => void;
14
+ emit<Event extends keyof NodeManagerEvents>(event: Event, ...args: Parameters<NodeManagerEvents[Event]>): boolean;
14
15
  /**
15
- * Emitted when a Node is destroyed.
16
- * @event Manager.nodeManager#destroy
16
+ * Add an event listener
17
+ * @param event The event to listen to
18
+ * @param listener The listener to add
19
+ * @returns
17
20
  */
18
- "destroy": (node: LavalinkNode, destroyReason?: DestroyReasonsType) => void;
21
+ on<Event extends keyof NodeManagerEvents>(event: Event, listener: NodeManagerEvents[Event]): this;
19
22
  /**
20
- * Emitted when a Node is connected.
21
- * @event Manager.nodeManager#connect
23
+ * Add an event listener that only fires once
24
+ * @param event The event to listen to
25
+ * @param listener The listener to add
26
+ * @returns
22
27
  */
23
- "connect": (node: LavalinkNode) => void;
24
- /**
25
- * Emitted when a Node is reconnecting.
26
- * @event Manager.nodeManager#reconnecting
27
- */
28
- "reconnecting": (node: LavalinkNode) => void;
29
- /**
30
- * Emitted when a Node is disconnects.
31
- * @event Manager.nodeManager#disconnect
32
- */
33
- "disconnect": (node: LavalinkNode, reason: {
34
- code?: number;
35
- reason?: string;
36
- }) => void;
28
+ once<Event extends keyof NodeManagerEvents>(event: Event, listener: NodeManagerEvents[Event]): this;
37
29
  /**
38
- * Emitted when a Node is error.
39
- * @event Manager.nodeManager#error
40
- */
41
- "error": (node: LavalinkNode, error: Error, payload?: unknown) => void;
30
+ * Remove an event listener
31
+ * @param event The event to remove the listener from
32
+ * @param listener The listener to remove
33
+ * @returns
34
+ */
35
+ off<Event extends keyof NodeManagerEvents>(event: Event, listener: NodeManagerEvents[Event]): this;
42
36
  /**
43
- * Emits every single Node event.
44
- * @event Manager.nodeManager#raw
45
- */
46
- "raw": (node: LavalinkNode, payload: unknown) => void;
37
+ * Remove an event listener
38
+ * @param event The event to remove the listener from
39
+ * @param listener The listener to remove
40
+ * @returns
41
+ */
42
+ removeListener<Event extends keyof NodeManagerEvents>(event: Event, listener: NodeManagerEvents[Event]): this;
47
43
  /**
48
- * Emits when the node connects resumed. You then need to create all players within this event for your usecase.
49
- * Aka for that you need to be able to save player data like vc channel + text channel in a db and then sync it again
50
- * @event Manager.nodeManager#nodeResumed
44
+ * The LavalinkManager that created this NodeManager
51
45
  */
52
- "resumed": (node: LavalinkNode, paylaod: {
53
- resumed: true;
54
- sessionId: string;
55
- op: "ready";
56
- }, players: LavalinkPlayer[]) => void;
57
- }
58
- export declare interface NodeManager {
59
- on<U extends keyof NodeManagerEvents>(event: U, listener: NodeManagerEvents[U]): this;
60
- emit<U extends keyof NodeManagerEvents>(event: U, ...args: Parameters<NodeManagerEvents[U]>): boolean;
61
- /** @private */
62
46
  LavalinkManager: LavalinkManager;
63
- }
64
- export declare class NodeManager extends EventEmitter {
47
+ /**
48
+ * A map of all nodes in the nodeManager
49
+ */
65
50
  nodes: MiniMap<string, LavalinkNode>;
51
+ /**
52
+ * @param LavalinkManager The LavalinkManager that created this NodeManager
53
+ */
66
54
  constructor(LavalinkManager: LavalinkManager);
67
55
  /**
68
56
  * Disconnects all Nodes from lavalink ws sockets
@@ -80,8 +68,22 @@ export declare class NodeManager extends EventEmitter {
80
68
  * @returns amount of nodes
81
69
  */
82
70
  reconnectAll(): Promise<number>;
71
+ /**
72
+ * Create a node and add it to the nodeManager
73
+ * @param options The options for the node
74
+ * @returns The node that was created
75
+ */
83
76
  createNode(options: LavalinkNodeOptions): LavalinkNode;
77
+ /**
78
+ * Get the nodes sorted for the least usage, by a sorttype
79
+ * @param sortType The type of sorting to use
80
+ * @returns
81
+ */
84
82
  leastUsedNodes(sortType?: "memory" | "cpuLavalink" | "cpuSystem" | "calls" | "playingPlayers" | "players"): LavalinkNode[];
83
+ /**
84
+ * Delete a node from the nodeManager and destroy it
85
+ * @param node The node to delete
86
+ * @returns
87
+ */
85
88
  deleteNode(node: LavalinkNodeIdentifier | LavalinkNode): void;
86
89
  }
87
- export {};