lavalink-client 2.2.2 → 2.3.1

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 (111) hide show
  1. package/README.md +95 -1
  2. package/dist/cjs/index.d.ts +8 -1
  3. package/dist/cjs/index.js +8 -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 +8 -232
  9. package/dist/cjs/structures/LavalinkManager.d.ts +31 -166
  10. package/dist/cjs/structures/LavalinkManager.js +59 -7
  11. package/dist/cjs/structures/LavalinkManagerStatics.d.ts +1 -1
  12. package/dist/cjs/structures/Node.d.ts +15 -156
  13. package/dist/cjs/structures/Node.js +131 -49
  14. package/dist/cjs/structures/NodeManager.d.ts +54 -52
  15. package/dist/cjs/structures/NodeManager.js +74 -4
  16. package/dist/cjs/structures/Player.d.ts +31 -124
  17. package/dist/cjs/structures/Player.js +77 -43
  18. package/dist/cjs/structures/Queue.d.ts +66 -42
  19. package/dist/cjs/structures/Queue.js +69 -11
  20. package/dist/cjs/structures/Types/Filters.d.ts +190 -0
  21. package/dist/cjs/structures/Types/Manager.d.ts +184 -0
  22. package/dist/cjs/structures/Types/Manager.js +2 -0
  23. package/dist/cjs/structures/Types/Node.d.ts +216 -0
  24. package/dist/cjs/structures/Types/Node.js +2 -0
  25. package/dist/cjs/structures/Types/Player.d.ts +108 -0
  26. package/dist/cjs/structures/Types/Player.js +2 -0
  27. package/dist/cjs/structures/Types/Queue.d.ts +34 -0
  28. package/dist/cjs/structures/Types/Queue.js +2 -0
  29. package/dist/cjs/structures/{Track.d.ts → Types/Track.d.ts} +3 -2
  30. package/dist/{structures → cjs/structures/Types}/Track.js +2 -2
  31. package/dist/cjs/structures/Types/Utils.d.ts +367 -0
  32. package/dist/cjs/structures/Types/Utils.js +2 -0
  33. package/dist/cjs/structures/Utils.d.ts +13 -369
  34. package/dist/cjs/structures/Utils.js +35 -14
  35. package/dist/esm/index.d.ts +8 -1
  36. package/dist/esm/index.js +8 -1
  37. package/dist/esm/structures/Constants.d.ts +40 -0
  38. package/dist/esm/structures/Constants.js +241 -0
  39. package/dist/esm/structures/CustomSearches/BandCampSearch.d.ts +2 -2
  40. package/dist/esm/structures/Filters.d.ts +2 -217
  41. package/dist/esm/structures/Filters.js +3 -227
  42. package/dist/esm/structures/LavalinkManager.d.ts +31 -166
  43. package/dist/esm/structures/LavalinkManager.js +57 -5
  44. package/dist/esm/structures/LavalinkManagerStatics.d.ts +1 -1
  45. package/dist/esm/structures/Node.d.ts +15 -156
  46. package/dist/esm/structures/Node.js +122 -40
  47. package/dist/esm/structures/NodeManager.d.ts +54 -52
  48. package/dist/esm/structures/NodeManager.js +71 -1
  49. package/dist/esm/structures/Player.d.ts +31 -124
  50. package/dist/esm/structures/Player.js +76 -42
  51. package/dist/esm/structures/Queue.d.ts +66 -42
  52. package/dist/esm/structures/Queue.js +69 -11
  53. package/dist/esm/structures/Types/Filters.d.ts +190 -0
  54. package/dist/esm/structures/Types/Manager.d.ts +184 -0
  55. package/dist/esm/structures/Types/Manager.js +1 -0
  56. package/dist/esm/structures/Types/Node.d.ts +216 -0
  57. package/dist/esm/structures/Types/Node.js +1 -0
  58. package/dist/esm/structures/Types/Player.d.ts +108 -0
  59. package/dist/esm/structures/Types/Player.js +1 -0
  60. package/dist/esm/structures/Types/Queue.d.ts +34 -0
  61. package/dist/esm/structures/Types/Queue.js +1 -0
  62. package/dist/esm/structures/{Track.d.ts → Types/Track.d.ts} +3 -2
  63. package/dist/esm/structures/Types/Track.js +1 -0
  64. package/dist/esm/structures/Types/Utils.d.ts +367 -0
  65. package/dist/esm/structures/Types/Utils.js +1 -0
  66. package/dist/esm/structures/Utils.d.ts +13 -369
  67. package/dist/esm/structures/Utils.js +35 -14
  68. package/dist/types/index.d.ts +8 -1
  69. package/dist/types/structures/Constants.d.ts +40 -0
  70. package/dist/types/structures/CustomSearches/BandCampSearch.d.ts +2 -2
  71. package/dist/types/structures/Filters.d.ts +2 -217
  72. package/dist/types/structures/LavalinkManager.d.ts +31 -166
  73. package/dist/types/structures/LavalinkManagerStatics.d.ts +1 -1
  74. package/dist/types/structures/Node.d.ts +15 -156
  75. package/dist/types/structures/NodeManager.d.ts +54 -52
  76. package/dist/types/structures/Player.d.ts +31 -124
  77. package/dist/types/structures/Queue.d.ts +66 -42
  78. package/dist/types/structures/Types/Filters.d.ts +190 -0
  79. package/dist/types/structures/Types/Manager.d.ts +184 -0
  80. package/dist/types/structures/Types/Node.d.ts +216 -0
  81. package/dist/types/structures/Types/Player.d.ts +108 -0
  82. package/dist/types/structures/Types/Queue.d.ts +34 -0
  83. package/dist/types/structures/{Track.d.ts → Types/Track.d.ts} +3 -2
  84. package/dist/types/structures/Types/Utils.d.ts +367 -0
  85. package/dist/types/structures/Utils.d.ts +13 -369
  86. package/package.json +1 -1
  87. package/dist/index.d.ts +0 -10
  88. package/dist/index.js +0 -13
  89. package/dist/structures/Filters.d.ts +0 -230
  90. package/dist/structures/Filters.js +0 -472
  91. package/dist/structures/LavalinkManager.d.ts +0 -47
  92. package/dist/structures/LavalinkManager.js +0 -36
  93. package/dist/structures/LavalinkManagerStatics.d.ts +0 -3
  94. package/dist/structures/LavalinkManagerStatics.js +0 -76
  95. package/dist/structures/Node.d.ts +0 -171
  96. package/dist/structures/Node.js +0 -462
  97. package/dist/structures/NodeManager.d.ts +0 -58
  98. package/dist/structures/NodeManager.js +0 -25
  99. package/dist/structures/Player.d.ts +0 -101
  100. package/dist/structures/Player.js +0 -232
  101. package/dist/structures/PlayerManager.d.ts +0 -62
  102. package/dist/structures/PlayerManager.js +0 -26
  103. package/dist/structures/Queue.d.ts +0 -93
  104. package/dist/structures/Queue.js +0 -160
  105. package/dist/structures/QueueManager.d.ts +0 -77
  106. package/dist/structures/QueueManager.js +0 -74
  107. package/dist/structures/Track.d.ts +0 -27
  108. package/dist/structures/Utils.d.ts +0 -183
  109. package/dist/structures/Utils.js +0 -43
  110. /package/dist/cjs/structures/{Track.js → Types/Filters.js} +0 -0
  111. /package/dist/esm/structures/{Track.js → Types/Filters.js} +0 -0
@@ -1,162 +1,18 @@
1
- /// <reference types="node" />
2
- import internal from "stream";
3
- import { NodeManager } from "./NodeManager";
4
- import { DestroyReasonsType, Player } from "./Player";
5
- import { Track } from "./Track";
6
- import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
7
- /** Ability to manipulate fetch requests */
8
- export type ModifyRequest = (options: RequestInit & {
9
- path: string;
10
- extraQueryUrlParams?: URLSearchParams;
11
- }) => void;
12
- export declare const validSponsorBlocks: string[];
13
- export type SponsorBlockSegment = "sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler";
14
- /**
15
- * Node Options for creating a lavalink node
16
- */
17
- export interface LavalinkNodeOptions {
18
- /** The Lavalink Server-Ip / Domain-URL */
19
- host: string;
20
- /** The Lavalink Connection Port */
21
- port: number;
22
- /** The Lavalink Password / Authorization-Key */
23
- authorization: string;
24
- /** Does the Server use ssl (https) */
25
- secure?: boolean;
26
- /** RESUME THE PLAYER? by providing a sessionid on the node-creation */
27
- sessionId?: string;
28
- /** Add a Custom ID to the node, for later use */
29
- id?: string;
30
- /** Voice Regions of this Node */
31
- regions?: string[];
32
- /** The retryAmount for the node. */
33
- retryAmount?: number;
34
- /** The retryDelay for the node. */
35
- retryDelay?: number;
36
- /** signal for cancelling requests - default: AbortSignal.timeout(options.requestSignalTimeoutMS || 10000) - put <= 0 to disable */
37
- requestSignalTimeoutMS?: number;
38
- }
39
- /**
40
- * Memory Stats object from lavalink
41
- */
42
- export interface MemoryStats {
43
- /** The free memory of the allocated amount. */
44
- free: number;
45
- /** The used memory of the allocated amount. */
46
- used: number;
47
- /** The total allocated memory. */
48
- allocated: number;
49
- /** The reservable memory. */
50
- reservable: number;
51
- }
52
- /**
53
- * CPU Stats object from lavalink
54
- */
55
- export interface CPUStats {
56
- /** The core amount the host machine has. */
57
- cores: number;
58
- /** The system load. */
59
- systemLoad: number;
60
- /** The lavalink load. */
61
- lavalinkLoad: number;
62
- }
63
- /**
64
- * FrameStats Object from lavalink
65
- */
66
- export interface FrameStats {
67
- /** The amount of sent frames. */
68
- sent?: number;
69
- /** The amount of nulled frames. */
70
- nulled?: number;
71
- /** The amount of deficit frames. */
72
- deficit?: number;
73
- }
74
- /**
75
- * BaseNodeStats object from Lavalink
76
- */
77
- export interface BaseNodeStats {
78
- /** The amount of players on the node. */
79
- players: number;
80
- /** The amount of playing players on the node. */
81
- playingPlayers: number;
82
- /** The uptime for the node. */
83
- uptime: number;
84
- /** The memory stats for the node. */
85
- memory: MemoryStats;
86
- /** The cpu stats for the node. */
87
- cpu: CPUStats;
88
- /** The frame stats for the node. */
89
- frameStats: FrameStats;
90
- }
91
- /**
92
- * Interface for nodeStats from lavalink
93
- */
94
- export interface NodeStats extends BaseNodeStats {
95
- /** The frame stats for the node. */
96
- frameStats: FrameStats;
97
- }
98
- /**
99
- * Entire lavalink information object from lavalink
100
- */
101
- export interface LavalinkInfo {
102
- /** The version of this Lavalink server */
103
- version: VersionObject;
104
- /** The millisecond unix timestamp when this Lavalink jar was built */
105
- buildTime: number;
106
- /** The git information of this Lavalink server */
107
- git: GitObject;
108
- /** The JVM version this Lavalink server runs on */
109
- jvm: string;
110
- /** The Lavaplayer version being used by this server */
111
- lavaplayer: string;
112
- /** The enabled source managers for this server */
113
- sourceManagers: string[];
114
- /** The enabled filters for this server */
115
- filters: string[];
116
- /** The enabled plugins for this server */
117
- plugins: PluginObject[];
118
- }
119
- /**
120
- * Lavalink's version object from lavalink
121
- */
122
- export interface VersionObject {
123
- /** The full version string of this Lavalink server */
124
- semver: string;
125
- /** The major version of this Lavalink server */
126
- major: number;
127
- /** The minor version of this Lavalink server */
128
- minor: number;
129
- /** The patch version of this Lavalink server */
130
- patch: internal;
131
- /** The pre-release version according to semver as a . separated list of identifiers */
132
- preRelease?: string;
133
- /** The build metadata according to semver as a . separated list of identifiers */
134
- build?: string;
135
- }
136
- /**
137
- * Git information object from lavalink
138
- */
139
- export interface GitObject {
140
- /** The branch this Lavalink server was built on */
141
- branch: string;
142
- /** The commit this Lavalink server was built on */
143
- commit: string;
144
- /** The millisecond unix timestamp for when the commit was created */
145
- commitTime: string;
146
- }
147
- /**
148
- * Lavalink's plugins object from lavalink's plugin
149
- */
150
- export interface PluginObject {
151
- /** The name of the plugin */
152
- name: string;
153
- /** The version of the plugin */
154
- version: string;
155
- }
1
+ import type { Player } from "./Player";
2
+ import type { DestroyReasonsType } from "./Types/Player";
3
+ import type { Track } from "./Types/Track";
4
+ import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils";
5
+ import type { NodeManager } from "./NodeManager";
6
+ import type { BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, ModifyRequest, NodeStats, SponsorBlockSegment } from "./Types/Node";
156
7
  /**
157
8
  * Lavalink Node creator class
158
9
  */
159
10
  export declare class LavalinkNode {
11
+ private heartBeatPingTimestamp;
12
+ private heartBeatPongTimestamp;
13
+ get heartBeatPing(): number;
14
+ private heartBeatInterval?;
15
+ private pingTimeout?;
160
16
  /** The provided Options of the Node */
161
17
  options: LavalinkNodeOptions;
162
18
  /** The amount of rest calls the node has made. */
@@ -300,6 +156,7 @@ export declare class LavalinkNode {
300
156
  * ```
301
157
  */
302
158
  connect(sessionId?: string): void;
159
+ private heartBeat;
303
160
  /**
304
161
  * Get the id of the node
305
162
  *
@@ -332,6 +189,7 @@ export declare class LavalinkNode {
332
189
  * ```
333
190
  */
334
191
  get connected(): boolean;
192
+ isAlive: boolean;
335
193
  /**
336
194
  * Returns the current ConnectionStatus
337
195
  *
@@ -356,7 +214,7 @@ export declare class LavalinkNode {
356
214
  * const playersOfLavalink = await node?.fetchAllPlayers();
357
215
  * ```
358
216
  */
359
- fetchAllPlayers(): Promise<LavalinkPlayer[]>;
217
+ fetchAllPlayers(): Promise<LavalinkPlayer[] | InvalidLavalinkRestRequest>;
360
218
  /**
361
219
  * Gets specific Player Information
362
220
  * @returns lavalink player object if player exists on lavalink
@@ -517,6 +375,7 @@ export declare class LavalinkNode {
517
375
  private message;
518
376
  /** @private middleware util function for handling all kind of events from websocket */
519
377
  private handleEvent;
378
+ private getTrackOfPayload;
520
379
  /** @private util function for handling trackStart event */
521
380
  private trackStart;
522
381
  /** @private util function for handling trackEnd event */
@@ -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
@@ -358,7 +380,7 @@ class LavalinkNode {
358
380
  const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id === this.id);
359
381
  if (players)
360
382
  players.forEach(p => {
361
- p.destroy(destroyReason || Player_1.DestroyReasons.NodeDestroy);
383
+ p.destroy(destroyReason || Constants_1.DestroyReasons.NodeDestroy);
362
384
  });
363
385
  this.socket.close(1000, "Node-Destroy");
364
386
  this.socket.removeAllListeners();
@@ -388,6 +410,7 @@ class LavalinkNode {
388
410
  return false;
389
411
  return this.socket.readyState === ws_1.default.OPEN;
390
412
  }
413
+ isAlive = false;
391
414
  /**
392
415
  * Returns the current ConnectionStatus
393
416
  *
@@ -419,11 +442,7 @@ class LavalinkNode {
419
442
  async fetchAllPlayers() {
420
443
  if (!this.sessionId)
421
444
  throw new Error("The Lavalink-Node is either not ready, or not up to date!");
422
- const players = await this.request(`/sessions/${this.sessionId}/players`);
423
- if (!Array.isArray(players))
424
- return [];
425
- else
426
- return players;
445
+ return this.request(`/sessions/${this.sessionId}/players`) || [];
427
446
  }
428
447
  /**
429
448
  * Gets specific Player Information
@@ -438,7 +457,7 @@ class LavalinkNode {
438
457
  async fetchPlayer(guildId) {
439
458
  if (!this.sessionId)
440
459
  throw new Error("The Lavalink-Node is either not ready, or not up to date!");
441
- return await this.request(`/sessions/${this.sessionId}/players/${guildId}`);
460
+ return this.request(`/sessions/${this.sessionId}/players/${guildId}`);
442
461
  }
443
462
  /**
444
463
  * Updates the session with and enables/disables resuming and timeout
@@ -464,7 +483,7 @@ class LavalinkNode {
464
483
  enabled: typeof resuming === "boolean" ? resuming : false,
465
484
  timeout: typeof resuming === "boolean" && resuming === true ? timeout : null,
466
485
  };
467
- return await this.request(`/sessions/${this.sessionId}`, r => {
486
+ return this.request(`/sessions/${this.sessionId}`, r => {
468
487
  r.method = "PATCH";
469
488
  r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
470
489
  r.body = JSON.stringify(data);
@@ -688,7 +707,7 @@ class LavalinkNode {
688
707
  if (!player)
689
708
  return;
690
709
  if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false)
691
- return player.destroy(Player_1.DestroyReasons.LavalinkNoVoice);
710
+ return player.destroy(Constants_1.DestroyReasons.LavalinkNoVoice);
692
711
  player.ping.ws = res?.voice?.ping || player?.ping.ws;
693
712
  }
694
713
  return true;
@@ -710,11 +729,12 @@ class LavalinkNode {
710
729
  * ```
711
730
  */
712
731
  reconnect(instaReconnect = false) {
732
+ this.NodeManager.emit("reconnectinprogress", this);
713
733
  if (instaReconnect) {
714
734
  if (this.reconnectAttempts >= this.options.retryAmount) {
715
735
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
716
736
  this.NodeManager.emit("error", this, error);
717
- return this.destroy(Player_1.DestroyReasons.NodeReconnectFail);
737
+ return this.destroy(Constants_1.DestroyReasons.NodeReconnectFail);
718
738
  }
719
739
  this.socket.removeAllListeners();
720
740
  this.socket = null;
@@ -727,7 +747,7 @@ class LavalinkNode {
727
747
  if (this.reconnectAttempts >= this.options.retryAmount) {
728
748
  const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
729
749
  this.NodeManager.emit("error", this, error);
730
- return this.destroy(Player_1.DestroyReasons.NodeReconnectFail);
750
+ return this.destroy(Constants_1.DestroyReasons.NodeReconnectFail);
731
751
  }
732
752
  this.socket.removeAllListeners();
733
753
  this.socket = null;
@@ -738,6 +758,29 @@ class LavalinkNode {
738
758
  }
739
759
  /** @private util function for handling opening events from websocket */
740
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
+ }
741
784
  if (this.reconnectTimeout)
742
785
  clearTimeout(this.reconnectTimeout);
743
786
  // reset the reconnect attempts amount
@@ -751,6 +794,12 @@ class LavalinkNode {
751
794
  }
752
795
  /** @private util function for handling closing events from websocket */
753
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";
754
803
  this.NodeManager.emit("disconnect", this, { code, reason });
755
804
  if (code !== 1000 || reason !== "Node-Destroy")
756
805
  this.reconnect();
@@ -760,6 +809,14 @@ class LavalinkNode {
760
809
  if (!error)
761
810
  return;
762
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
+ ;
763
820
  }
764
821
  /** @private util function for handling message events from websocket */
765
822
  async message(d) {
@@ -773,6 +830,8 @@ class LavalinkNode {
773
830
  this.NodeManager.emit("raw", this, payload);
774
831
  switch (payload.op) {
775
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
776
835
  delete payload.op;
777
836
  this.stats = { ...payload };
778
837
  break;
@@ -802,7 +861,13 @@ class LavalinkNode {
802
861
  this.sessionId = payload.sessionId;
803
862
  this.resuming.enabled = payload.resumed;
804
863
  if (payload.resumed === true) {
805
- 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
+ }
806
871
  }
807
872
  break;
808
873
  default:
@@ -812,7 +877,7 @@ class LavalinkNode {
812
877
  }
813
878
  /** @private middleware util function for handling all kind of events from websocket */
814
879
  async handleEvent(payload) {
815
- if (!payload.guildId)
880
+ if (!payload?.guildId)
816
881
  return;
817
882
  const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
818
883
  if (!player)
@@ -851,39 +916,51 @@ class LavalinkNode {
851
916
  }
852
917
  return;
853
918
  }
919
+ async getTrackOfPayload(payload) {
920
+ return "track" in payload
921
+ ? this.NodeManager.LavalinkManager.utils.buildTrack(payload.track, undefined)
922
+ : null;
923
+ }
854
924
  /** @private util function for handling trackStart event */
855
- trackStart(player, track, payload) {
925
+ async trackStart(player, track, payload) {
856
926
  player.playing = true;
857
927
  player.paused = false;
858
928
  // don't emit the event if previous track == new track aka track loop
859
929
  if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier)
860
930
  return;
861
- 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);
862
937
  }
863
938
  /** @private util function for handling trackEnd event */
864
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
+ }
865
945
  // If there are no songs in the queue
866
946
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
867
947
  return this.queueEnd(player, track, payload);
868
- // If a track was forcibly played
869
- if (payload.reason === "replaced")
870
- return this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
871
948
  // If a track had an error while starting
872
949
  if (["loadFailed", "cleanup"].includes(payload.reason)) {
873
950
  await (0, Utils_1.queueTrackEnd)(player);
874
951
  // if no track available, end queue
875
952
  if (!player.queue.current)
876
- return this.queueEnd(player, track, payload);
953
+ return this.queueEnd(player, trackToUse, payload);
877
954
  // fire event
878
- this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
955
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
879
956
  // play track if autoSkip is true
880
957
  return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
881
958
  }
882
959
  // remove tracks from the queue
883
960
  if (player.repeatMode !== "track" || player.get("internal_skipped"))
884
961
  await (0, Utils_1.queueTrackEnd)(player);
885
- else if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
886
- 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);
887
964
  if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
888
965
  player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
889
966
  await player.queue.utils.save();
@@ -891,60 +968,65 @@ class LavalinkNode {
891
968
  player.set("internal_skipped", false);
892
969
  // if no track available, end queue
893
970
  if (!player.queue.current)
894
- return this.queueEnd(player, track, payload);
971
+ return this.queueEnd(player, trackToUse, payload);
895
972
  // fire event
896
- this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
973
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
897
974
  // play track if autoSkip is true
898
975
  return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
899
976
  }
900
977
  /** @private util function for handling trackStuck event */
901
978
  async trackStuck(player, track, payload) {
902
- this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
979
+ this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
903
980
  // If there are no songs in the queue
904
981
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
905
- return this.queueEnd(player, track, payload);
982
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
906
983
  // remove the current track, and enqueue the next one
907
984
  await (0, Utils_1.queueTrackEnd)(player);
908
985
  // if no track available, end queue
909
986
  if (!player.queue.current)
910
- return this.queueEnd(player, track, payload);
987
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
911
988
  // play track if autoSkip is true
912
989
  return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
913
990
  }
914
991
  /** @private util function for handling trackError event */
915
992
  async trackError(player, track, payload) {
916
- this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
993
+ this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
917
994
  return; // get's handled by trackEnd
918
995
  // If there are no songs in the queue
919
996
  if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
920
- return this.queueEnd(player, track, payload);
997
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
921
998
  // remove the current track, and enqueue the next one
922
999
  await (0, Utils_1.queueTrackEnd)(player);
923
1000
  // if no track available, end queue
924
1001
  if (!player.queue.current)
925
- return this.queueEnd(player, track, payload);
1002
+ return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
926
1003
  // play track if autoSkip is true
927
1004
  return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
928
1005
  }
929
1006
  /** @private util function for handling socketClosed event */
930
1007
  socketClosed(player, payload) {
931
- 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;
932
1014
  }
933
1015
  /** @private util function for handling SponsorBlock Segmentloaded event */
934
- SponsorBlockSegmentLoaded(player, track, payload) {
935
- 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);
936
1018
  }
937
1019
  /** @private util function for handling SponsorBlock SegmentSkipped event */
938
- SponsorBlockSegmentSkipped(player, track, payload) {
939
- 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);
940
1022
  }
941
1023
  /** @private util function for handling SponsorBlock Chaptersloaded event */
942
- SponsorBlockChaptersLoaded(player, track, payload) {
943
- 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);
944
1026
  }
945
1027
  /** @private util function for handling SponsorBlock Chaptersstarted event */
946
- SponsorBlockChapterStarted(player, track, payload) {
947
- 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);
948
1030
  }
949
1031
  /**
950
1032
  * Get the current sponsorblocks for the sponsorblock plugin
@@ -983,8 +1065,8 @@ class LavalinkNode {
983
1065
  if (!segments.length)
984
1066
  throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
985
1067
  // a not valid segment
986
- if (segments.some(v => !exports.validSponsorBlocks.includes(v.toLowerCase())))
987
- 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(", ")}`);
988
1070
  // do the request
989
1071
  await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
990
1072
  r.method = "PUT";
@@ -1042,14 +1124,14 @@ class LavalinkNode {
1042
1124
  }
1043
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) {
1044
1126
  if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
1045
- return player.destroy(Player_1.DestroyReasons.QueueEmpty);
1127
+ return player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1046
1128
  else {
1047
1129
  if (player.get("internal_queueempty")) {
1048
1130
  clearTimeout(player.get("internal_queueempty"));
1049
1131
  player.set("internal_queueempty", undefined);
1050
1132
  }
1051
1133
  player.set("internal_queueempty", setTimeout(() => {
1052
- player.destroy(Player_1.DestroyReasons.QueueEmpty);
1134
+ player.destroy(Constants_1.DestroyReasons.QueueEmpty);
1053
1135
  }, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
1054
1136
  }
1055
1137
  }