lavalink-client 2.3.5 → 2.4.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 (62) hide show
  1. package/README.md +73 -6
  2. package/dist/cjs/structures/Constants.d.ts +4 -0
  3. package/dist/cjs/structures/Constants.js +7 -2
  4. package/dist/cjs/structures/Filters.d.ts +24 -0
  5. package/dist/cjs/structures/Filters.js +34 -10
  6. package/dist/cjs/structures/LavalinkManager.d.ts +4 -5
  7. package/dist/cjs/structures/LavalinkManager.js +36 -14
  8. package/dist/cjs/structures/LavalinkManagerStatics.d.ts +2 -0
  9. package/dist/cjs/structures/LavalinkManagerStatics.js +11 -2
  10. package/dist/cjs/structures/Node.d.ts +107 -13
  11. package/dist/cjs/structures/Node.js +294 -76
  12. package/dist/cjs/structures/NodeManager.d.ts +2 -2
  13. package/dist/cjs/structures/NodeManager.js +19 -19
  14. package/dist/cjs/structures/Player.d.ts +51 -1
  15. package/dist/cjs/structures/Player.js +62 -0
  16. package/dist/cjs/structures/Queue.d.ts +9 -10
  17. package/dist/cjs/structures/Queue.js +3 -3
  18. package/dist/cjs/structures/Types/Manager.d.ts +59 -1
  19. package/dist/cjs/structures/Types/Node.d.ts +23 -1
  20. package/dist/cjs/structures/Types/Player.d.ts +5 -1
  21. package/dist/cjs/structures/Types/Queue.d.ts +6 -6
  22. package/dist/cjs/structures/Types/Track.d.ts +3 -1
  23. package/dist/cjs/structures/Types/Utils.d.ts +81 -8
  24. package/dist/cjs/structures/Utils.js +11 -9
  25. package/dist/esm/structures/Constants.d.ts +4 -0
  26. package/dist/esm/structures/Constants.js +6 -1
  27. package/dist/esm/structures/Filters.d.ts +24 -0
  28. package/dist/esm/structures/Filters.js +34 -10
  29. package/dist/esm/structures/LavalinkManager.d.ts +4 -5
  30. package/dist/esm/structures/LavalinkManager.js +36 -14
  31. package/dist/esm/structures/LavalinkManagerStatics.d.ts +2 -0
  32. package/dist/esm/structures/LavalinkManagerStatics.js +11 -2
  33. package/dist/esm/structures/Node.d.ts +107 -13
  34. package/dist/esm/structures/Node.js +294 -76
  35. package/dist/esm/structures/NodeManager.d.ts +2 -2
  36. package/dist/esm/structures/NodeManager.js +20 -20
  37. package/dist/esm/structures/Player.d.ts +51 -1
  38. package/dist/esm/structures/Player.js +62 -0
  39. package/dist/esm/structures/Queue.d.ts +9 -10
  40. package/dist/esm/structures/Queue.js +3 -3
  41. package/dist/esm/structures/Types/Manager.d.ts +59 -1
  42. package/dist/esm/structures/Types/Node.d.ts +23 -1
  43. package/dist/esm/structures/Types/Player.d.ts +5 -1
  44. package/dist/esm/structures/Types/Queue.d.ts +6 -6
  45. package/dist/esm/structures/Types/Track.d.ts +3 -1
  46. package/dist/esm/structures/Types/Utils.d.ts +81 -8
  47. package/dist/esm/structures/Utils.js +8 -6
  48. package/dist/types/structures/Constants.d.ts +4 -0
  49. package/dist/types/structures/Filters.d.ts +24 -0
  50. package/dist/types/structures/LavalinkManager.d.ts +4 -5
  51. package/dist/types/structures/LavalinkManagerStatics.d.ts +2 -0
  52. package/dist/types/structures/Node.d.ts +107 -13
  53. package/dist/types/structures/NodeManager.d.ts +2 -2
  54. package/dist/types/structures/Player.d.ts +51 -1
  55. package/dist/types/structures/Queue.d.ts +9 -10
  56. package/dist/types/structures/Types/Manager.d.ts +59 -1
  57. package/dist/types/structures/Types/Node.d.ts +23 -1
  58. package/dist/types/structures/Types/Player.d.ts +5 -1
  59. package/dist/types/structures/Types/Queue.d.ts +6 -6
  60. package/dist/types/structures/Types/Track.d.ts +3 -1
  61. package/dist/types/structures/Types/Utils.d.ts +81 -8
  62. package/package.json +29 -18
@@ -17,6 +17,7 @@ class LavalinkNode {
17
17
  }
18
18
  heartBeatInterval;
19
19
  pingTimeout;
20
+ isAlive = false;
20
21
  /** The provided Options of the Node */
21
22
  options;
22
23
  /** The amount of rest calls the node has made. */
@@ -78,7 +79,7 @@ class LavalinkNode {
78
79
  retryAmount: 5,
79
80
  retryDelay: 10e3,
80
81
  requestSignalTimeoutMS: 10000,
81
- heartBeatInterval: 30000,
82
+ heartBeatInterval: 30_000,
82
83
  closeOnError: true,
83
84
  enablePingOnStatsCheck: true,
84
85
  ...options
@@ -141,32 +142,22 @@ class LavalinkNode {
141
142
  const url = new URL(`${this.restAddress}${options.path}`);
142
143
  url.searchParams.append("trace", "true");
143
144
  const urlToUse = this.getRequestingUrl(url, options?.extraQueryUrlParams);
145
+ const originalOptions = structuredClone(options);
144
146
  delete options.path;
145
147
  delete options.extraQueryUrlParams;
146
- const request = await fetch(urlToUse, options);
148
+ const response = await fetch(urlToUse, options);
147
149
  this.calls++;
148
- return { request, options };
150
+ return { response, options: originalOptions };
149
151
  }
150
- /**
151
- * Makes an API call to the Node. Should only be used for manual parsing like for not supported plugins
152
- * @param endpoint The endpoint that we will make the call to
153
- * @param modify Used to modify the request before being sent
154
- * @returns The returned data
155
- *
156
- * @example
157
- * ```ts
158
- * player.node.request(`/loadtracks?identifier=Never gonna give you up`, (options) => options.method = "GET", false);
159
- * ```
160
- */
161
- async request(endpoint, modify, parseAsText = false) {
152
+ async request(endpoint, modify, parseAsText) {
162
153
  if (!this.connected)
163
154
  throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
164
- const { request, options } = await this.rawRequest(endpoint, modify);
155
+ const { response, options } = await this.rawRequest(endpoint, modify);
165
156
  if (["DELETE", "PUT"].includes(options.method))
166
157
  return;
167
- if (request.status === 404)
168
- throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(request.headers)}`);
169
- return parseAsText ? await request.text() : await request.json();
158
+ if (response.status === 404)
159
+ throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(response.headers)}`);
160
+ return parseAsText ? await response.text() : await response.json();
170
161
  }
171
162
  /**
172
163
  * Search something raw on the node, please note only add tracks to players of that node
@@ -241,7 +232,7 @@ class LavalinkNode {
241
232
  * @param query LavaSearchQuery Object
242
233
  * @param requestUser Request User for creating the player(s)
243
234
  * @param throwOnEmpty Wether to throw on an empty result or not
244
- * @returns LavaSearchresult
235
+ * @returns LavaSearchresult (SearchResult if link is provided)
245
236
  *
246
237
  * @example
247
238
  * ```ts
@@ -261,8 +252,8 @@ class LavalinkNode {
261
252
  throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.id}`);
262
253
  if (!this.info.plugins.find(v => v.name === "lavasrc-plugin"))
263
254
  throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
264
- const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
265
- const res = (request.status === 204 ? {} : await request.json());
255
+ const { response } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
256
+ const res = (response.status === 204 ? {} : await response.json());
266
257
  if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
267
258
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
268
259
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.LavaSearchNothingFound, {
@@ -299,7 +290,6 @@ class LavalinkNode {
299
290
  this.syncPlayerData(data);
300
291
  const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
301
292
  r.method = "PATCH";
302
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
303
293
  r.headers["Content-Type"] = "application/json";
304
294
  r.body = JSON.stringify(data.playerOptions);
305
295
  if (data.noReplace) {
@@ -315,7 +305,8 @@ class LavalinkNode {
315
305
  functionLayer: "LavalinkNode > node > updatePlayer()",
316
306
  });
317
307
  }
318
- return this.syncPlayerData({}, res), res;
308
+ this.syncPlayerData({}, res);
309
+ return res;
319
310
  }
320
311
  /**
321
312
  * Destroys the Player on the Lavalink Server
@@ -384,6 +375,7 @@ class LavalinkNode {
384
375
  if (this.pingTimeout)
385
376
  clearTimeout(this.pingTimeout);
386
377
  this.pingTimeout = setTimeout(() => {
378
+ this.pingTimeout = null;
387
379
  if (!this.socket) {
388
380
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
389
381
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.NoSocketOnDestroy, {
@@ -403,7 +395,7 @@ class LavalinkNode {
403
395
  }
404
396
  this.isAlive = false;
405
397
  this.socket.terminate();
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
398
+ }, 65_000); // the stats endpoint get's sent every 60s. se wee add a 5s buffer to make sure we don't miss any stats message
407
399
  }
408
400
  /**
409
401
  * Get the id of the node
@@ -419,7 +411,7 @@ class LavalinkNode {
419
411
  }
420
412
  /**
421
413
  * Destroys the Node-Connection (Websocket) and all player's of the node
422
- * @param destroyReason Destroyreason to use when destroying the players
414
+ * @param destroyReason Destroy Reason to use when destroying the players
423
415
  * @param deleteNode wether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
424
416
  * @returns void
425
417
  *
@@ -450,6 +442,28 @@ class LavalinkNode {
450
442
  }
451
443
  return;
452
444
  }
445
+ /**
446
+ * Disconnects the Node-Connection (Websocket)
447
+ * @param disconnectReason Disconnect Reason to use when disconnecting Node
448
+ * @returns void
449
+ *
450
+ * Also the node will not get re-connected again.
451
+ *
452
+ * @example
453
+ * ```ts
454
+ * player.node.destroy("custom Player Destroy Reason", true);
455
+ * ```
456
+ */
457
+ disconnect(disconnectReason) {
458
+ if (!this.connected)
459
+ return;
460
+ this.socket.close(1000, "Node-Disconnect");
461
+ this.socket.removeAllListeners();
462
+ this.socket = null;
463
+ this.reconnectAttempts = 1;
464
+ clearTimeout(this.reconnectTimeout);
465
+ this.NodeManager.emit("disconnect", this, { code: 1000, reason: disconnectReason });
466
+ }
453
467
  /**
454
468
  * Returns if connected to the Node.
455
469
  *
@@ -460,11 +474,8 @@ class LavalinkNode {
460
474
  * ```
461
475
  */
462
476
  get connected() {
463
- if (!this.socket)
464
- return false;
465
- return this.socket.readyState === ws_1.default.OPEN;
477
+ return this.socket && this.socket.readyState === ws_1.default.OPEN;
466
478
  }
467
- isAlive = false;
468
479
  /**
469
480
  * Returns the current ConnectionStatus
470
481
  *
@@ -585,11 +596,108 @@ class LavalinkNode {
585
596
  return await this.request(`/decodetracks`, r => {
586
597
  r.method = "POST";
587
598
  r.body = JSON.stringify(encodeds);
588
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
589
599
  r.headers["Content-Type"] = "application/json";
590
600
  }).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
591
601
  }
592
602
  };
603
+ lyrics = {
604
+ /**
605
+ * Get the lyrics of a track
606
+ * @param track the track to get the lyrics for
607
+ * @param skipTrackSource wether to skip the track source or not
608
+ * @returns the lyrics of the track
609
+ * @example
610
+ *
611
+ * ```ts
612
+ * const lyrics = await player.node.lyrics.get(track, true);
613
+ * // use it of player instead:
614
+ * // const lyrics = await player.getLyrics(track, true);
615
+ * ```
616
+ */
617
+ get: async (track, skipTrackSource = false) => {
618
+ if (!this.sessionId)
619
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
620
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
621
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
622
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
623
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
624
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
625
+ const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
626
+ return (await this.request(url));
627
+ },
628
+ /**
629
+ * Get the lyrics of the current playing track
630
+ *
631
+ * @param guildId the guild id of the player
632
+ * @param skipTrackSource wether to skip the track source or not
633
+ * @returns the lyrics of the current playing track
634
+ * @example
635
+ * ```ts
636
+ * const lyrics = await player.node.lyrics.getCurrent(guildId);
637
+ * // use it of player instead:
638
+ * // const lyrics = await player.getCurrentLyrics();
639
+ * ```
640
+ */
641
+ getCurrent: async (guildId, skipTrackSource = false) => {
642
+ if (!this.sessionId)
643
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
644
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
645
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
646
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
647
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
648
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
649
+ const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
650
+ return (await this.request(url));
651
+ },
652
+ /**
653
+ * subscribe to lyrics updates for a guild
654
+ * @param guildId the guild id of the player
655
+ * @returns request data of the request
656
+ *
657
+ * @example
658
+ * ```ts
659
+ * await player.node.lyrics.subscribe(guildId);
660
+ * // use it of player instead:
661
+ * // const lyrics = await player.subscribeLyrics();
662
+ * ```
663
+ */
664
+ subscribe: async (guildId) => {
665
+ if (!this.sessionId)
666
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
667
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
668
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
669
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
670
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
671
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
672
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
673
+ options.method = "POST";
674
+ });
675
+ },
676
+ /**
677
+ * unsubscribe from lyrics updates for a guild
678
+ * @param guildId the guild id of the player
679
+ * @returns request data of the request
680
+ *
681
+ * @example
682
+ * ```ts
683
+ * await player.node.lyrics.unsubscribe(guildId);
684
+ * // use it of player instead:
685
+ * // const lyrics = await player.unsubscribeLyrics();
686
+ * ```
687
+ */
688
+ unsubscribe: async (guildId) => {
689
+ if (!this.sessionId)
690
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
691
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
692
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
693
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
694
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
695
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
696
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/unsubscribe`, (options) => {
697
+ options.method = "DELETE";
698
+ });
699
+ },
700
+ };
593
701
  /**
594
702
  * Request Lavalink statistics.
595
703
  * @returns the lavalink node stats
@@ -662,9 +770,8 @@ class LavalinkNode {
662
770
  unmarkFailedAddress: async (address) => {
663
771
  if (!this.sessionId)
664
772
  throw new Error("the Lavalink-Node is either not ready, or not up to date!");
665
- await this.request(`/routeplanner/free/address`, r => {
773
+ return await this.request(`/routeplanner/free/address`, r => {
666
774
  r.method = "POST";
667
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
668
775
  r.headers["Content-Type"] = "application/json";
669
776
  r.body = JSON.stringify({ address });
670
777
  });
@@ -683,7 +790,6 @@ class LavalinkNode {
683
790
  throw new Error("the Lavalink-Node is either not ready, or not up to date!");
684
791
  return await this.request(`/routeplanner/free/all`, r => {
685
792
  r.method = "POST";
686
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
687
793
  r.headers["Content-Type"] = "application/json";
688
794
  });
689
795
  }
@@ -696,6 +802,7 @@ class LavalinkNode {
696
802
  throw new SyntaxError("LavalinkNode requires 'host'");
697
803
  if (!this.options.port)
698
804
  throw new SyntaxError("LavalinkNode requires 'port'");
805
+ // TODO add more validations
699
806
  }
700
807
  /**
701
808
  * Sync the data of the player you make an action to lavalink to
@@ -760,11 +867,13 @@ class LavalinkNode {
760
867
  const player = this.NodeManager.LavalinkManager.getPlayer(data.guildId);
761
868
  if (!player)
762
869
  return;
763
- if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false)
764
- return player.destroy(Constants_1.DestroyReasons.LavalinkNoVoice);
870
+ if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false) {
871
+ player.destroy(Constants_1.DestroyReasons.LavalinkNoVoice);
872
+ return;
873
+ }
765
874
  player.ping.ws = res?.voice?.ping || player?.ping.ws;
766
875
  }
767
- return true;
876
+ return;
768
877
  }
769
878
  /**
770
879
  * Get the rest Adress for making requests
@@ -798,6 +907,7 @@ class LavalinkNode {
798
907
  return;
799
908
  }
800
909
  this.reconnectTimeout = setTimeout(() => {
910
+ this.reconnectTimeout = null;
801
911
  if (this.reconnectAttempts >= this.options.retryAmount) {
802
912
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
803
913
  this.NodeManager.emit("error", this, error);
@@ -833,7 +943,7 @@ class LavalinkNode {
833
943
  this.isAlive = false;
834
944
  this.heartBeatPingTimestamp = performance.now();
835
945
  this.socket.ping();
836
- }, this.options.heartBeatInterval || 30000);
946
+ }, this.options.heartBeatInterval || 30_000);
837
947
  }
838
948
  if (this.reconnectTimeout)
839
949
  clearTimeout(this.reconnectTimeout);
@@ -854,6 +964,8 @@ class LavalinkNode {
854
964
  clearInterval(this.heartBeatInterval);
855
965
  if (code === 1006 && !reason)
856
966
  reason = "Socket got terminated due to no ping connection";
967
+ if (code === 1000 && reason === "Node-Disconnect")
968
+ return; // manually disconnected and already emitted the event.
857
969
  this.NodeManager.emit("disconnect", this, { code, reason });
858
970
  if (code !== 1000 || reason !== "Node-Destroy")
859
971
  this.reconnect();
@@ -878,7 +990,14 @@ class LavalinkNode {
878
990
  d = Buffer.concat(d);
879
991
  else if (d instanceof ArrayBuffer)
880
992
  d = Buffer.from(d);
881
- const payload = JSON.parse(d.toString());
993
+ let payload;
994
+ try {
995
+ payload = JSON.parse(d.toString());
996
+ }
997
+ catch (e) {
998
+ this.NodeManager.emit("error", this, e);
999
+ return;
1000
+ }
882
1001
  if (!payload.op)
883
1002
  return;
884
1003
  this.NodeManager.emit("raw", this, payload);
@@ -909,7 +1028,7 @@ class LavalinkNode {
909
1028
  player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
910
1029
  if (!player.createdTimeStamp && payload.state.time)
911
1030
  player.createdTimeStamp = payload.state.time;
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))) {
1031
+ if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600_000) || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))) {
913
1032
  player.filterManager.filterUpdatedState = false;
914
1033
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
915
1034
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerUpdateFilterFixApply, {
@@ -986,13 +1105,22 @@ class LavalinkNode {
986
1105
  case "ChapterStarted":
987
1106
  this.SponsorBlockChapterStarted(player, player.queue.current, payload);
988
1107
  break;
1108
+ case "LyricsLineEvent":
1109
+ this.LyricsLine(player, player.queue.current, payload);
1110
+ break;
1111
+ case "LyricsFoundEvent":
1112
+ this.LyricsFound(player, player.queue.current, payload);
1113
+ break;
1114
+ case "LyricsNotFoundEvent":
1115
+ this.LyricsNotFound(player, player.queue.current, payload);
1116
+ break;
989
1117
  default:
990
1118
  this.NodeManager.emit("error", this, new Error(`Node#event unknown event '${payload.type}'.`), payload);
991
1119
  break;
992
1120
  }
993
1121
  return;
994
1122
  }
995
- async getTrackOfPayload(payload) {
1123
+ getTrackOfPayload(payload) {
996
1124
  return "track" in payload
997
1125
  ? this.NodeManager.LavalinkManager.utils.buildTrack(payload.track, undefined)
998
1126
  : null;
@@ -1013,7 +1141,7 @@ class LavalinkNode {
1013
1141
  return;
1014
1142
  }
1015
1143
  if (!player.queue.current) {
1016
- player.queue.current = await this.getTrackOfPayload(payload);
1144
+ player.queue.current = this.getTrackOfPayload(payload);
1017
1145
  if (player.queue.current) {
1018
1146
  await player.queue.utils.save();
1019
1147
  }
@@ -1027,11 +1155,12 @@ class LavalinkNode {
1027
1155
  }
1028
1156
  }
1029
1157
  }
1030
- return this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
1158
+ this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
1159
+ return;
1031
1160
  }
1032
1161
  /** @private util function for handling trackEnd event */
1033
1162
  async trackEnd(player, track, payload) {
1034
- const trackToUse = track || await this.getTrackOfPayload(payload);
1163
+ const trackToUse = track || this.getTrackOfPayload(payload);
1035
1164
  // If a track was forcibly played
1036
1165
  if (payload.reason === "replaced") {
1037
1166
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
@@ -1041,7 +1170,8 @@ class LavalinkNode {
1041
1170
  functionLayer: "LavalinkNode > trackEnd()",
1042
1171
  });
1043
1172
  }
1044
- return this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1173
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1174
+ return;
1045
1175
  }
1046
1176
  // If there are no songs in the queue
1047
1177
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
@@ -1055,7 +1185,10 @@ class LavalinkNode {
1055
1185
  // fire event
1056
1186
  this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1057
1187
  // play track if autoSkip is true
1058
- return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
1188
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1189
+ player.play({ noReplace: true });
1190
+ }
1191
+ return;
1059
1192
  }
1060
1193
  // remove tracks from the queue
1061
1194
  if (player.repeatMode !== "track" || player.get("internal_skipped"))
@@ -1073,7 +1206,10 @@ class LavalinkNode {
1073
1206
  // fire event
1074
1207
  this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1075
1208
  // play track if autoSkip is true
1076
- return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
1209
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1210
+ player.play({ noReplace: true });
1211
+ }
1212
+ return;
1077
1213
  }
1078
1214
  /** @private util function for handling trackStuck event */
1079
1215
  async trackStuck(player, track, payload) {
@@ -1089,21 +1225,25 @@ class LavalinkNode {
1089
1225
  functionLayer: "LavalinkNode > trackStuck()",
1090
1226
  });
1091
1227
  }
1092
- return player.destroy(Constants_1.DestroyReasons.TrackStuckMaxTracksErroredPerTime);
1228
+ player.destroy(Constants_1.DestroyReasons.TrackStuckMaxTracksErroredPerTime);
1229
+ return;
1093
1230
  }
1094
1231
  }
1095
- this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
1232
+ this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
1096
1233
  // If there are no songs in the queue
1097
1234
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1098
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1235
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1099
1236
  // remove the current track, and enqueue the next one
1100
1237
  await (0, Utils_1.queueTrackEnd)(player);
1101
1238
  // if no track available, end queue
1102
1239
  if (!player.queue.current) {
1103
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1240
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1104
1241
  }
1105
1242
  // play track if autoSkip is true
1106
- return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
1243
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1244
+ player.play({ noReplace: true });
1245
+ }
1246
+ return;
1107
1247
  }
1108
1248
  /** @private util function for handling trackError event */
1109
1249
  async trackError(player, track, payload) {
@@ -1119,46 +1259,37 @@ class LavalinkNode {
1119
1259
  functionLayer: "LavalinkNode > trackError()",
1120
1260
  });
1121
1261
  }
1122
- return player.destroy(Constants_1.DestroyReasons.TrackErrorMaxTracksErroredPerTime);
1262
+ player.destroy(Constants_1.DestroyReasons.TrackErrorMaxTracksErroredPerTime);
1263
+ return;
1123
1264
  }
1124
1265
  }
1125
- this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
1126
- return; // get's handled by trackEnd
1127
- // If there are no songs in the queue
1128
- if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1129
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1130
- // remove the current track, and enqueue the next one
1131
- await (0, Utils_1.queueTrackEnd)(player);
1132
- // if no track available, end queue
1133
- if (!player.queue.current)
1134
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1135
- // play track if autoSkip is true
1136
- return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
1266
+ this.NodeManager.LavalinkManager.emit("trackError", player, track || this.getTrackOfPayload(payload), payload);
1267
+ return;
1137
1268
  }
1138
1269
  /** @private util function for handling socketClosed event */
1139
1270
  socketClosed(player, payload) {
1140
1271
  this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
1141
- // i don't think this is needed.
1142
- // this.socket = null;
1143
- // // causing a socket reconnect
1144
- // this.connect();
1145
1272
  return;
1146
1273
  }
1147
1274
  /** @private util function for handling SponsorBlock Segmentloaded event */
1148
1275
  async SponsorBlockSegmentLoaded(player, track, payload) {
1149
- return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || await this.getTrackOfPayload(payload), payload);
1276
+ this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || this.getTrackOfPayload(payload), payload);
1277
+ return;
1150
1278
  }
1151
1279
  /** @private util function for handling SponsorBlock SegmentSkipped event */
1152
1280
  async SponsorBlockSegmentSkipped(player, track, payload) {
1153
- return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || await this.getTrackOfPayload(payload), payload);
1281
+ this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || this.getTrackOfPayload(payload), payload);
1282
+ return;
1154
1283
  }
1155
1284
  /** @private util function for handling SponsorBlock Chaptersloaded event */
1156
1285
  async SponsorBlockChaptersLoaded(player, track, payload) {
1157
- return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || await this.getTrackOfPayload(payload), payload);
1286
+ this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || this.getTrackOfPayload(payload), payload);
1287
+ return;
1158
1288
  }
1159
1289
  /** @private util function for handling SponsorBlock Chaptersstarted event */
1160
1290
  async SponsorBlockChapterStarted(player, track, payload) {
1161
- return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || await this.getTrackOfPayload(payload), payload);
1291
+ this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || this.getTrackOfPayload(payload), payload);
1292
+ return;
1162
1293
  }
1163
1294
  /**
1164
1295
  * Get the current sponsorblocks for the sponsorblock plugin
@@ -1305,8 +1436,10 @@ class LavalinkNode {
1305
1436
  await player.queue.utils.save();
1306
1437
  }
1307
1438
  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) {
1308
- if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
1309
- return player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1439
+ if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0) {
1440
+ player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1441
+ return;
1442
+ }
1310
1443
  else {
1311
1444
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1312
1445
  this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TriggerQueueEmptyInterval, {
@@ -1315,16 +1448,101 @@ class LavalinkNode {
1315
1448
  functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs",
1316
1449
  });
1317
1450
  }
1451
+ this.NodeManager.LavalinkManager.emit("playerQueueEmptyStart", player, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs);
1318
1452
  if (player.get("internal_queueempty")) {
1319
1453
  clearTimeout(player.get("internal_queueempty"));
1320
1454
  player.set("internal_queueempty", undefined);
1321
1455
  }
1322
1456
  player.set("internal_queueempty", setTimeout(() => {
1457
+ player.set("internal_queueempty", undefined);
1458
+ if (player.queue.current) {
1459
+ return this.NodeManager.LavalinkManager.emit("playerQueueEmptyCancel", player);
1460
+ }
1461
+ this.NodeManager.LavalinkManager.emit("playerQueueEmptyEnd", player);
1323
1462
  player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1324
1463
  }, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
1325
1464
  }
1326
1465
  }
1327
- return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
1466
+ this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
1467
+ return;
1468
+ }
1469
+ /**
1470
+ * Emitted whenever a line of lyrics gets emitted
1471
+ * @event
1472
+ * @param {Player} player The player that emitted the event
1473
+ * @param {Track} track The track that emitted the event
1474
+ * @param {LyricsLineEvent} payload The payload of the event
1475
+ */
1476
+ async LyricsLine(player, track, payload) {
1477
+ if (!player.queue.current) {
1478
+ player.queue.current = this.getTrackOfPayload(payload);
1479
+ if (player.queue.current) {
1480
+ await player.queue.utils.save();
1481
+ }
1482
+ else {
1483
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1484
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStartNoTrack, {
1485
+ state: "warn",
1486
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1487
+ functionLayer: "LavalinkNode > trackStart()",
1488
+ });
1489
+ }
1490
+ }
1491
+ }
1492
+ this.NodeManager.LavalinkManager.emit("LyricsLine", player, track, payload);
1493
+ return;
1494
+ }
1495
+ /**
1496
+ * Emitted whenever the lyrics for a track got found
1497
+ * @event
1498
+ * @param {Player} player The player that emitted the event
1499
+ * @param {Track} track The track that emitted the event
1500
+ * @param {LyricsFoundEvent} payload The payload of the event
1501
+ */
1502
+ async LyricsFound(player, track, payload) {
1503
+ if (!player.queue.current) {
1504
+ player.queue.current = this.getTrackOfPayload(payload);
1505
+ if (player.queue.current) {
1506
+ await player.queue.utils.save();
1507
+ }
1508
+ else {
1509
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1510
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStartNoTrack, {
1511
+ state: "warn",
1512
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1513
+ functionLayer: "LavalinkNode > trackStart()",
1514
+ });
1515
+ }
1516
+ }
1517
+ }
1518
+ this.NodeManager.LavalinkManager.emit("LyricsFound", player, track, payload);
1519
+ return;
1520
+ }
1521
+ /**
1522
+ * Emitted whenever the lyrics for a track got not found
1523
+ * @event
1524
+ * @param {Player} player The player that emitted the event
1525
+ * @param {Track} track The track that emitted the event
1526
+ * @param {LyricsNotFoundEvent} payload The payload of the event
1527
+ */
1528
+ async LyricsNotFound(player, track, payload) {
1529
+ if (!player.queue.current) {
1530
+ player.queue.current = this.getTrackOfPayload(payload);
1531
+ if (player.queue.current) {
1532
+ await player.queue.utils.save();
1533
+ }
1534
+ else {
1535
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1536
+ this.NodeManager.LavalinkManager.emit("debug", Constants_1.DebugEvents.TrackStartNoTrack, {
1537
+ state: "warn",
1538
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1539
+ functionLayer: "LavalinkNode > trackStart()",
1540
+ });
1541
+ }
1542
+ }
1543
+ }
1544
+ this.NodeManager.LavalinkManager.emit("LyricsNotFound", player, track, payload);
1545
+ return;
1328
1546
  }
1329
1547
  }
1330
1548
  exports.LavalinkNode = LavalinkNode;
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { EventEmitter } from "events";
3
2
  import { LavalinkNode } from "./Node.js";
4
3
  import { MiniMap } from "./Utils.js";
@@ -55,9 +54,10 @@ export declare class NodeManager extends EventEmitter {
55
54
  /**
56
55
  * Disconnects all Nodes from lavalink ws sockets
57
56
  * @param deleteAllNodes if the nodes should also be deleted from nodeManager.nodes
57
+ * @param destroyPlayers if the players should be destroyed
58
58
  * @returns amount of disconnected Nodes
59
59
  */
60
- disconnectAll(deleteAllNodes?: boolean): Promise<number>;
60
+ disconnectAll(deleteAllNodes?: boolean, destroyPlayers?: boolean): Promise<number>;
61
61
  /**
62
62
  * Connects all not connected nodes
63
63
  * @returns Amount of connected Nodes