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
@@ -13,6 +13,7 @@ export class LavalinkNode {
13
13
  }
14
14
  heartBeatInterval;
15
15
  pingTimeout;
16
+ isAlive = false;
16
17
  /** The provided Options of the Node */
17
18
  options;
18
19
  /** The amount of rest calls the node has made. */
@@ -74,7 +75,7 @@ export class LavalinkNode {
74
75
  retryAmount: 5,
75
76
  retryDelay: 10e3,
76
77
  requestSignalTimeoutMS: 10000,
77
- heartBeatInterval: 30000,
78
+ heartBeatInterval: 30_000,
78
79
  closeOnError: true,
79
80
  enablePingOnStatsCheck: true,
80
81
  ...options
@@ -137,32 +138,22 @@ export class LavalinkNode {
137
138
  const url = new URL(`${this.restAddress}${options.path}`);
138
139
  url.searchParams.append("trace", "true");
139
140
  const urlToUse = this.getRequestingUrl(url, options?.extraQueryUrlParams);
141
+ const originalOptions = structuredClone(options);
140
142
  delete options.path;
141
143
  delete options.extraQueryUrlParams;
142
- const request = await fetch(urlToUse, options);
144
+ const response = await fetch(urlToUse, options);
143
145
  this.calls++;
144
- return { request, options };
146
+ return { response, options: originalOptions };
145
147
  }
146
- /**
147
- * Makes an API call to the Node. Should only be used for manual parsing like for not supported plugins
148
- * @param endpoint The endpoint that we will make the call to
149
- * @param modify Used to modify the request before being sent
150
- * @returns The returned data
151
- *
152
- * @example
153
- * ```ts
154
- * player.node.request(`/loadtracks?identifier=Never gonna give you up`, (options) => options.method = "GET", false);
155
- * ```
156
- */
157
- async request(endpoint, modify, parseAsText = false) {
148
+ async request(endpoint, modify, parseAsText) {
158
149
  if (!this.connected)
159
150
  throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
160
- const { request, options } = await this.rawRequest(endpoint, modify);
151
+ const { response, options } = await this.rawRequest(endpoint, modify);
161
152
  if (["DELETE", "PUT"].includes(options.method))
162
153
  return;
163
- if (request.status === 404)
164
- throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(request.headers)}`);
165
- return parseAsText ? await request.text() : await request.json();
154
+ if (response.status === 404)
155
+ throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(response.headers)}`);
156
+ return parseAsText ? await response.text() : await response.json();
166
157
  }
167
158
  /**
168
159
  * Search something raw on the node, please note only add tracks to players of that node
@@ -237,7 +228,7 @@ export class LavalinkNode {
237
228
  * @param query LavaSearchQuery Object
238
229
  * @param requestUser Request User for creating the player(s)
239
230
  * @param throwOnEmpty Wether to throw on an empty result or not
240
- * @returns LavaSearchresult
231
+ * @returns LavaSearchresult (SearchResult if link is provided)
241
232
  *
242
233
  * @example
243
234
  * ```ts
@@ -257,8 +248,8 @@ export class LavalinkNode {
257
248
  throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.id}`);
258
249
  if (!this.info.plugins.find(v => v.name === "lavasrc-plugin"))
259
250
  throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
260
- const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
261
- const res = (request.status === 204 ? {} : await request.json());
251
+ const { response } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
252
+ const res = (response.status === 204 ? {} : await response.json());
262
253
  if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
263
254
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
264
255
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.LavaSearchNothingFound, {
@@ -295,7 +286,6 @@ export class LavalinkNode {
295
286
  this.syncPlayerData(data);
296
287
  const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
297
288
  r.method = "PATCH";
298
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
299
289
  r.headers["Content-Type"] = "application/json";
300
290
  r.body = JSON.stringify(data.playerOptions);
301
291
  if (data.noReplace) {
@@ -311,7 +301,8 @@ export class LavalinkNode {
311
301
  functionLayer: "LavalinkNode > node > updatePlayer()",
312
302
  });
313
303
  }
314
- return this.syncPlayerData({}, res), res;
304
+ this.syncPlayerData({}, res);
305
+ return res;
315
306
  }
316
307
  /**
317
308
  * Destroys the Player on the Lavalink Server
@@ -380,6 +371,7 @@ export class LavalinkNode {
380
371
  if (this.pingTimeout)
381
372
  clearTimeout(this.pingTimeout);
382
373
  this.pingTimeout = setTimeout(() => {
374
+ this.pingTimeout = null;
383
375
  if (!this.socket) {
384
376
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
385
377
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.NoSocketOnDestroy, {
@@ -399,7 +391,7 @@ export class LavalinkNode {
399
391
  }
400
392
  this.isAlive = false;
401
393
  this.socket.terminate();
402
- }, 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
394
+ }, 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
403
395
  }
404
396
  /**
405
397
  * Get the id of the node
@@ -415,7 +407,7 @@ export class LavalinkNode {
415
407
  }
416
408
  /**
417
409
  * Destroys the Node-Connection (Websocket) and all player's of the node
418
- * @param destroyReason Destroyreason to use when destroying the players
410
+ * @param destroyReason Destroy Reason to use when destroying the players
419
411
  * @param deleteNode wether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
420
412
  * @returns void
421
413
  *
@@ -446,6 +438,28 @@ export class LavalinkNode {
446
438
  }
447
439
  return;
448
440
  }
441
+ /**
442
+ * Disconnects the Node-Connection (Websocket)
443
+ * @param disconnectReason Disconnect Reason to use when disconnecting Node
444
+ * @returns void
445
+ *
446
+ * Also the node will not get re-connected again.
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * player.node.destroy("custom Player Destroy Reason", true);
451
+ * ```
452
+ */
453
+ disconnect(disconnectReason) {
454
+ if (!this.connected)
455
+ return;
456
+ this.socket.close(1000, "Node-Disconnect");
457
+ this.socket.removeAllListeners();
458
+ this.socket = null;
459
+ this.reconnectAttempts = 1;
460
+ clearTimeout(this.reconnectTimeout);
461
+ this.NodeManager.emit("disconnect", this, { code: 1000, reason: disconnectReason });
462
+ }
449
463
  /**
450
464
  * Returns if connected to the Node.
451
465
  *
@@ -456,11 +470,8 @@ export class LavalinkNode {
456
470
  * ```
457
471
  */
458
472
  get connected() {
459
- if (!this.socket)
460
- return false;
461
- return this.socket.readyState === WebSocket.OPEN;
473
+ return this.socket && this.socket.readyState === WebSocket.OPEN;
462
474
  }
463
- isAlive = false;
464
475
  /**
465
476
  * Returns the current ConnectionStatus
466
477
  *
@@ -581,11 +592,108 @@ export class LavalinkNode {
581
592
  return await this.request(`/decodetracks`, r => {
582
593
  r.method = "POST";
583
594
  r.body = JSON.stringify(encodeds);
584
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
585
595
  r.headers["Content-Type"] = "application/json";
586
596
  }).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
587
597
  }
588
598
  };
599
+ lyrics = {
600
+ /**
601
+ * Get the lyrics of a track
602
+ * @param track the track to get the lyrics for
603
+ * @param skipTrackSource wether to skip the track source or not
604
+ * @returns the lyrics of the track
605
+ * @example
606
+ *
607
+ * ```ts
608
+ * const lyrics = await player.node.lyrics.get(track, true);
609
+ * // use it of player instead:
610
+ * // const lyrics = await player.getLyrics(track, true);
611
+ * ```
612
+ */
613
+ get: async (track, skipTrackSource = false) => {
614
+ if (!this.sessionId)
615
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
616
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
617
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
618
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
619
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
620
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
621
+ const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
622
+ return (await this.request(url));
623
+ },
624
+ /**
625
+ * Get the lyrics of the current playing track
626
+ *
627
+ * @param guildId the guild id of the player
628
+ * @param skipTrackSource wether to skip the track source or not
629
+ * @returns the lyrics of the current playing track
630
+ * @example
631
+ * ```ts
632
+ * const lyrics = await player.node.lyrics.getCurrent(guildId);
633
+ * // use it of player instead:
634
+ * // const lyrics = await player.getCurrentLyrics();
635
+ * ```
636
+ */
637
+ getCurrent: async (guildId, skipTrackSource = false) => {
638
+ if (!this.sessionId)
639
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
640
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
641
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
642
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
643
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
644
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
645
+ const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
646
+ return (await this.request(url));
647
+ },
648
+ /**
649
+ * subscribe to lyrics updates for a guild
650
+ * @param guildId the guild id of the player
651
+ * @returns request data of the request
652
+ *
653
+ * @example
654
+ * ```ts
655
+ * await player.node.lyrics.subscribe(guildId);
656
+ * // use it of player instead:
657
+ * // const lyrics = await player.subscribeLyrics();
658
+ * ```
659
+ */
660
+ subscribe: async (guildId) => {
661
+ if (!this.sessionId)
662
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
663
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
664
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
665
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
666
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
667
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
668
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
669
+ options.method = "POST";
670
+ });
671
+ },
672
+ /**
673
+ * unsubscribe from lyrics updates for a guild
674
+ * @param guildId the guild id of the player
675
+ * @returns request data of the request
676
+ *
677
+ * @example
678
+ * ```ts
679
+ * await player.node.lyrics.unsubscribe(guildId);
680
+ * // use it of player instead:
681
+ * // const lyrics = await player.unsubscribeLyrics();
682
+ * ```
683
+ */
684
+ unsubscribe: async (guildId) => {
685
+ if (!this.sessionId)
686
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
687
+ if (!this.info.plugins.find(v => v.name === "lavalyrics-plugin"))
688
+ throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
689
+ if (!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
690
+ !this.info.plugins.find(v => v.name === "java-lyrics-plugin"))
691
+ throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
692
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/unsubscribe`, (options) => {
693
+ options.method = "DELETE";
694
+ });
695
+ },
696
+ };
589
697
  /**
590
698
  * Request Lavalink statistics.
591
699
  * @returns the lavalink node stats
@@ -658,9 +766,8 @@ export class LavalinkNode {
658
766
  unmarkFailedAddress: async (address) => {
659
767
  if (!this.sessionId)
660
768
  throw new Error("the Lavalink-Node is either not ready, or not up to date!");
661
- await this.request(`/routeplanner/free/address`, r => {
769
+ return await this.request(`/routeplanner/free/address`, r => {
662
770
  r.method = "POST";
663
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
664
771
  r.headers["Content-Type"] = "application/json";
665
772
  r.body = JSON.stringify({ address });
666
773
  });
@@ -679,7 +786,6 @@ export class LavalinkNode {
679
786
  throw new Error("the Lavalink-Node is either not ready, or not up to date!");
680
787
  return await this.request(`/routeplanner/free/all`, r => {
681
788
  r.method = "POST";
682
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
683
789
  r.headers["Content-Type"] = "application/json";
684
790
  });
685
791
  }
@@ -692,6 +798,7 @@ export class LavalinkNode {
692
798
  throw new SyntaxError("LavalinkNode requires 'host'");
693
799
  if (!this.options.port)
694
800
  throw new SyntaxError("LavalinkNode requires 'port'");
801
+ // TODO add more validations
695
802
  }
696
803
  /**
697
804
  * Sync the data of the player you make an action to lavalink to
@@ -756,11 +863,13 @@ export class LavalinkNode {
756
863
  const player = this.NodeManager.LavalinkManager.getPlayer(data.guildId);
757
864
  if (!player)
758
865
  return;
759
- if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false)
760
- return player.destroy(DestroyReasons.LavalinkNoVoice);
866
+ if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false) {
867
+ player.destroy(DestroyReasons.LavalinkNoVoice);
868
+ return;
869
+ }
761
870
  player.ping.ws = res?.voice?.ping || player?.ping.ws;
762
871
  }
763
- return true;
872
+ return;
764
873
  }
765
874
  /**
766
875
  * Get the rest Adress for making requests
@@ -794,6 +903,7 @@ export class LavalinkNode {
794
903
  return;
795
904
  }
796
905
  this.reconnectTimeout = setTimeout(() => {
906
+ this.reconnectTimeout = null;
797
907
  if (this.reconnectAttempts >= this.options.retryAmount) {
798
908
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
799
909
  this.NodeManager.emit("error", this, error);
@@ -829,7 +939,7 @@ export class LavalinkNode {
829
939
  this.isAlive = false;
830
940
  this.heartBeatPingTimestamp = performance.now();
831
941
  this.socket.ping();
832
- }, this.options.heartBeatInterval || 30000);
942
+ }, this.options.heartBeatInterval || 30_000);
833
943
  }
834
944
  if (this.reconnectTimeout)
835
945
  clearTimeout(this.reconnectTimeout);
@@ -850,6 +960,8 @@ export class LavalinkNode {
850
960
  clearInterval(this.heartBeatInterval);
851
961
  if (code === 1006 && !reason)
852
962
  reason = "Socket got terminated due to no ping connection";
963
+ if (code === 1000 && reason === "Node-Disconnect")
964
+ return; // manually disconnected and already emitted the event.
853
965
  this.NodeManager.emit("disconnect", this, { code, reason });
854
966
  if (code !== 1000 || reason !== "Node-Destroy")
855
967
  this.reconnect();
@@ -874,7 +986,14 @@ export class LavalinkNode {
874
986
  d = Buffer.concat(d);
875
987
  else if (d instanceof ArrayBuffer)
876
988
  d = Buffer.from(d);
877
- const payload = JSON.parse(d.toString());
989
+ let payload;
990
+ try {
991
+ payload = JSON.parse(d.toString());
992
+ }
993
+ catch (e) {
994
+ this.NodeManager.emit("error", this, e);
995
+ return;
996
+ }
878
997
  if (!payload.op)
879
998
  return;
880
999
  this.NodeManager.emit("raw", this, payload);
@@ -905,7 +1024,7 @@ export class LavalinkNode {
905
1024
  player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
906
1025
  if (!player.createdTimeStamp && payload.state.time)
907
1026
  player.createdTimeStamp = payload.state.time;
908
- if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600000) || isAbsolute(player.queue.current?.info?.uri))) {
1027
+ if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600_000) || isAbsolute(player.queue.current?.info?.uri))) {
909
1028
  player.filterManager.filterUpdatedState = false;
910
1029
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
911
1030
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateFilterFixApply, {
@@ -982,13 +1101,22 @@ export class LavalinkNode {
982
1101
  case "ChapterStarted":
983
1102
  this.SponsorBlockChapterStarted(player, player.queue.current, payload);
984
1103
  break;
1104
+ case "LyricsLineEvent":
1105
+ this.LyricsLine(player, player.queue.current, payload);
1106
+ break;
1107
+ case "LyricsFoundEvent":
1108
+ this.LyricsFound(player, player.queue.current, payload);
1109
+ break;
1110
+ case "LyricsNotFoundEvent":
1111
+ this.LyricsNotFound(player, player.queue.current, payload);
1112
+ break;
985
1113
  default:
986
1114
  this.NodeManager.emit("error", this, new Error(`Node#event unknown event '${payload.type}'.`), payload);
987
1115
  break;
988
1116
  }
989
1117
  return;
990
1118
  }
991
- async getTrackOfPayload(payload) {
1119
+ getTrackOfPayload(payload) {
992
1120
  return "track" in payload
993
1121
  ? this.NodeManager.LavalinkManager.utils.buildTrack(payload.track, undefined)
994
1122
  : null;
@@ -1009,7 +1137,7 @@ export class LavalinkNode {
1009
1137
  return;
1010
1138
  }
1011
1139
  if (!player.queue.current) {
1012
- player.queue.current = await this.getTrackOfPayload(payload);
1140
+ player.queue.current = this.getTrackOfPayload(payload);
1013
1141
  if (player.queue.current) {
1014
1142
  await player.queue.utils.save();
1015
1143
  }
@@ -1023,11 +1151,12 @@ export class LavalinkNode {
1023
1151
  }
1024
1152
  }
1025
1153
  }
1026
- return this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
1154
+ this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
1155
+ return;
1027
1156
  }
1028
1157
  /** @private util function for handling trackEnd event */
1029
1158
  async trackEnd(player, track, payload) {
1030
- const trackToUse = track || await this.getTrackOfPayload(payload);
1159
+ const trackToUse = track || this.getTrackOfPayload(payload);
1031
1160
  // If a track was forcibly played
1032
1161
  if (payload.reason === "replaced") {
1033
1162
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
@@ -1037,7 +1166,8 @@ export class LavalinkNode {
1037
1166
  functionLayer: "LavalinkNode > trackEnd()",
1038
1167
  });
1039
1168
  }
1040
- return this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1169
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1170
+ return;
1041
1171
  }
1042
1172
  // If there are no songs in the queue
1043
1173
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
@@ -1051,7 +1181,10 @@ export class LavalinkNode {
1051
1181
  // fire event
1052
1182
  this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1053
1183
  // play track if autoSkip is true
1054
- return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
1184
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1185
+ player.play({ noReplace: true });
1186
+ }
1187
+ return;
1055
1188
  }
1056
1189
  // remove tracks from the queue
1057
1190
  if (player.repeatMode !== "track" || player.get("internal_skipped"))
@@ -1069,7 +1202,10 @@ export class LavalinkNode {
1069
1202
  // fire event
1070
1203
  this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
1071
1204
  // play track if autoSkip is true
1072
- return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
1205
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1206
+ player.play({ noReplace: true });
1207
+ }
1208
+ return;
1073
1209
  }
1074
1210
  /** @private util function for handling trackStuck event */
1075
1211
  async trackStuck(player, track, payload) {
@@ -1085,21 +1221,25 @@ export class LavalinkNode {
1085
1221
  functionLayer: "LavalinkNode > trackStuck()",
1086
1222
  });
1087
1223
  }
1088
- return player.destroy(DestroyReasons.TrackStuckMaxTracksErroredPerTime);
1224
+ player.destroy(DestroyReasons.TrackStuckMaxTracksErroredPerTime);
1225
+ return;
1089
1226
  }
1090
1227
  }
1091
- this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
1228
+ this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
1092
1229
  // If there are no songs in the queue
1093
1230
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1094
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1231
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1095
1232
  // remove the current track, and enqueue the next one
1096
1233
  await queueTrackEnd(player);
1097
1234
  // if no track available, end queue
1098
1235
  if (!player.queue.current) {
1099
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1236
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1100
1237
  }
1101
1238
  // play track if autoSkip is true
1102
- return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
1239
+ if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1240
+ player.play({ noReplace: true });
1241
+ }
1242
+ return;
1103
1243
  }
1104
1244
  /** @private util function for handling trackError event */
1105
1245
  async trackError(player, track, payload) {
@@ -1115,46 +1255,37 @@ export class LavalinkNode {
1115
1255
  functionLayer: "LavalinkNode > trackError()",
1116
1256
  });
1117
1257
  }
1118
- return player.destroy(DestroyReasons.TrackErrorMaxTracksErroredPerTime);
1258
+ player.destroy(DestroyReasons.TrackErrorMaxTracksErroredPerTime);
1259
+ return;
1119
1260
  }
1120
1261
  }
1121
- this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
1122
- return; // get's handled by trackEnd
1123
- // If there are no songs in the queue
1124
- if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1125
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1126
- // remove the current track, and enqueue the next one
1127
- await queueTrackEnd(player);
1128
- // if no track available, end queue
1129
- if (!player.queue.current)
1130
- return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
1131
- // play track if autoSkip is true
1132
- return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
1262
+ this.NodeManager.LavalinkManager.emit("trackError", player, track || this.getTrackOfPayload(payload), payload);
1263
+ return;
1133
1264
  }
1134
1265
  /** @private util function for handling socketClosed event */
1135
1266
  socketClosed(player, payload) {
1136
1267
  this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
1137
- // i don't think this is needed.
1138
- // this.socket = null;
1139
- // // causing a socket reconnect
1140
- // this.connect();
1141
1268
  return;
1142
1269
  }
1143
1270
  /** @private util function for handling SponsorBlock Segmentloaded event */
1144
1271
  async SponsorBlockSegmentLoaded(player, track, payload) {
1145
- return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || await this.getTrackOfPayload(payload), payload);
1272
+ this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || this.getTrackOfPayload(payload), payload);
1273
+ return;
1146
1274
  }
1147
1275
  /** @private util function for handling SponsorBlock SegmentSkipped event */
1148
1276
  async SponsorBlockSegmentSkipped(player, track, payload) {
1149
- return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || await this.getTrackOfPayload(payload), payload);
1277
+ this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || this.getTrackOfPayload(payload), payload);
1278
+ return;
1150
1279
  }
1151
1280
  /** @private util function for handling SponsorBlock Chaptersloaded event */
1152
1281
  async SponsorBlockChaptersLoaded(player, track, payload) {
1153
- return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || await this.getTrackOfPayload(payload), payload);
1282
+ this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || this.getTrackOfPayload(payload), payload);
1283
+ return;
1154
1284
  }
1155
1285
  /** @private util function for handling SponsorBlock Chaptersstarted event */
1156
1286
  async SponsorBlockChapterStarted(player, track, payload) {
1157
- return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || await this.getTrackOfPayload(payload), payload);
1287
+ this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || this.getTrackOfPayload(payload), payload);
1288
+ return;
1158
1289
  }
1159
1290
  /**
1160
1291
  * Get the current sponsorblocks for the sponsorblock plugin
@@ -1301,8 +1432,10 @@ export class LavalinkNode {
1301
1432
  await player.queue.utils.save();
1302
1433
  }
1303
1434
  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) {
1304
- if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
1305
- return player.destroy(DestroyReasons.QueueEmpty);
1435
+ if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0) {
1436
+ player.destroy(DestroyReasons.QueueEmpty);
1437
+ return;
1438
+ }
1306
1439
  else {
1307
1440
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1308
1441
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TriggerQueueEmptyInterval, {
@@ -1311,15 +1444,100 @@ export class LavalinkNode {
1311
1444
  functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs",
1312
1445
  });
1313
1446
  }
1447
+ this.NodeManager.LavalinkManager.emit("playerQueueEmptyStart", player, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs);
1314
1448
  if (player.get("internal_queueempty")) {
1315
1449
  clearTimeout(player.get("internal_queueempty"));
1316
1450
  player.set("internal_queueempty", undefined);
1317
1451
  }
1318
1452
  player.set("internal_queueempty", setTimeout(() => {
1453
+ player.set("internal_queueempty", undefined);
1454
+ if (player.queue.current) {
1455
+ return this.NodeManager.LavalinkManager.emit("playerQueueEmptyCancel", player);
1456
+ }
1457
+ this.NodeManager.LavalinkManager.emit("playerQueueEmptyEnd", player);
1319
1458
  player.destroy(DestroyReasons.QueueEmpty);
1320
1459
  }, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
1321
1460
  }
1322
1461
  }
1323
- return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
1462
+ this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
1463
+ return;
1464
+ }
1465
+ /**
1466
+ * Emitted whenever a line of lyrics gets emitted
1467
+ * @event
1468
+ * @param {Player} player The player that emitted the event
1469
+ * @param {Track} track The track that emitted the event
1470
+ * @param {LyricsLineEvent} payload The payload of the event
1471
+ */
1472
+ async LyricsLine(player, track, payload) {
1473
+ if (!player.queue.current) {
1474
+ player.queue.current = this.getTrackOfPayload(payload);
1475
+ if (player.queue.current) {
1476
+ await player.queue.utils.save();
1477
+ }
1478
+ else {
1479
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1480
+ this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStartNoTrack, {
1481
+ state: "warn",
1482
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1483
+ functionLayer: "LavalinkNode > trackStart()",
1484
+ });
1485
+ }
1486
+ }
1487
+ }
1488
+ this.NodeManager.LavalinkManager.emit("LyricsLine", player, track, payload);
1489
+ return;
1490
+ }
1491
+ /**
1492
+ * Emitted whenever the lyrics for a track got found
1493
+ * @event
1494
+ * @param {Player} player The player that emitted the event
1495
+ * @param {Track} track The track that emitted the event
1496
+ * @param {LyricsFoundEvent} payload The payload of the event
1497
+ */
1498
+ async LyricsFound(player, track, payload) {
1499
+ if (!player.queue.current) {
1500
+ player.queue.current = this.getTrackOfPayload(payload);
1501
+ if (player.queue.current) {
1502
+ await player.queue.utils.save();
1503
+ }
1504
+ else {
1505
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1506
+ this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStartNoTrack, {
1507
+ state: "warn",
1508
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1509
+ functionLayer: "LavalinkNode > trackStart()",
1510
+ });
1511
+ }
1512
+ }
1513
+ }
1514
+ this.NodeManager.LavalinkManager.emit("LyricsFound", player, track, payload);
1515
+ return;
1516
+ }
1517
+ /**
1518
+ * Emitted whenever the lyrics for a track got not found
1519
+ * @event
1520
+ * @param {Player} player The player that emitted the event
1521
+ * @param {Track} track The track that emitted the event
1522
+ * @param {LyricsNotFoundEvent} payload The payload of the event
1523
+ */
1524
+ async LyricsNotFound(player, track, payload) {
1525
+ if (!player.queue.current) {
1526
+ player.queue.current = this.getTrackOfPayload(payload);
1527
+ if (player.queue.current) {
1528
+ await player.queue.utils.save();
1529
+ }
1530
+ else {
1531
+ if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1532
+ this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStartNoTrack, {
1533
+ state: "warn",
1534
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
1535
+ functionLayer: "LavalinkNode > trackStart()",
1536
+ });
1537
+ }
1538
+ }
1539
+ }
1540
+ this.NodeManager.LavalinkManager.emit("LyricsNotFound", player, track, payload);
1541
+ return;
1324
1542
  }
1325
1543
  }
@@ -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