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.
- package/README.md +73 -6
- package/dist/cjs/structures/Constants.d.ts +4 -0
- package/dist/cjs/structures/Constants.js +7 -2
- package/dist/cjs/structures/Filters.d.ts +24 -0
- package/dist/cjs/structures/Filters.js +34 -10
- package/dist/cjs/structures/LavalinkManager.d.ts +4 -5
- package/dist/cjs/structures/LavalinkManager.js +36 -14
- package/dist/cjs/structures/LavalinkManagerStatics.d.ts +2 -0
- package/dist/cjs/structures/LavalinkManagerStatics.js +11 -2
- package/dist/cjs/structures/Node.d.ts +107 -13
- package/dist/cjs/structures/Node.js +294 -76
- package/dist/cjs/structures/NodeManager.d.ts +2 -2
- package/dist/cjs/structures/NodeManager.js +19 -19
- package/dist/cjs/structures/Player.d.ts +51 -1
- package/dist/cjs/structures/Player.js +62 -0
- package/dist/cjs/structures/Queue.d.ts +9 -10
- package/dist/cjs/structures/Queue.js +3 -3
- package/dist/cjs/structures/Types/Manager.d.ts +59 -1
- package/dist/cjs/structures/Types/Node.d.ts +23 -1
- package/dist/cjs/structures/Types/Player.d.ts +5 -1
- package/dist/cjs/structures/Types/Queue.d.ts +6 -6
- package/dist/cjs/structures/Types/Track.d.ts +3 -1
- package/dist/cjs/structures/Types/Utils.d.ts +81 -8
- package/dist/cjs/structures/Utils.js +11 -9
- package/dist/esm/structures/Constants.d.ts +4 -0
- package/dist/esm/structures/Constants.js +6 -1
- package/dist/esm/structures/Filters.d.ts +24 -0
- package/dist/esm/structures/Filters.js +34 -10
- package/dist/esm/structures/LavalinkManager.d.ts +4 -5
- package/dist/esm/structures/LavalinkManager.js +36 -14
- package/dist/esm/structures/LavalinkManagerStatics.d.ts +2 -0
- package/dist/esm/structures/LavalinkManagerStatics.js +11 -2
- package/dist/esm/structures/Node.d.ts +107 -13
- package/dist/esm/structures/Node.js +294 -76
- package/dist/esm/structures/NodeManager.d.ts +2 -2
- package/dist/esm/structures/NodeManager.js +20 -20
- package/dist/esm/structures/Player.d.ts +51 -1
- package/dist/esm/structures/Player.js +62 -0
- package/dist/esm/structures/Queue.d.ts +9 -10
- package/dist/esm/structures/Queue.js +3 -3
- package/dist/esm/structures/Types/Manager.d.ts +59 -1
- package/dist/esm/structures/Types/Node.d.ts +23 -1
- package/dist/esm/structures/Types/Player.d.ts +5 -1
- package/dist/esm/structures/Types/Queue.d.ts +6 -6
- package/dist/esm/structures/Types/Track.d.ts +3 -1
- package/dist/esm/structures/Types/Utils.d.ts +81 -8
- package/dist/esm/structures/Utils.js +8 -6
- package/dist/types/structures/Constants.d.ts +4 -0
- package/dist/types/structures/Filters.d.ts +24 -0
- package/dist/types/structures/LavalinkManager.d.ts +4 -5
- package/dist/types/structures/LavalinkManagerStatics.d.ts +2 -0
- package/dist/types/structures/Node.d.ts +107 -13
- package/dist/types/structures/NodeManager.d.ts +2 -2
- package/dist/types/structures/Player.d.ts +51 -1
- package/dist/types/structures/Queue.d.ts +9 -10
- package/dist/types/structures/Types/Manager.d.ts +59 -1
- package/dist/types/structures/Types/Node.d.ts +23 -1
- package/dist/types/structures/Types/Player.d.ts +5 -1
- package/dist/types/structures/Types/Queue.d.ts +6 -6
- package/dist/types/structures/Types/Track.d.ts +3 -1
- package/dist/types/structures/Types/Utils.d.ts +81 -8
- 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:
|
|
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
|
|
144
|
+
const response = await fetch(urlToUse, options);
|
|
143
145
|
this.calls++;
|
|
144
|
-
return {
|
|
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 {
|
|
151
|
+
const { response, options } = await this.rawRequest(endpoint, modify);
|
|
161
152
|
if (["DELETE", "PUT"].includes(options.method))
|
|
162
153
|
return;
|
|
163
|
-
if (
|
|
164
|
-
throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(
|
|
165
|
-
return parseAsText ? await
|
|
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 {
|
|
261
|
-
const res = (
|
|
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
|
-
|
|
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
|
-
},
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ||
|
|
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
|
-
|
|
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 ||
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1224
|
+
player.destroy(DestroyReasons.TrackStuckMaxTracksErroredPerTime);
|
|
1225
|
+
return;
|
|
1089
1226
|
}
|
|
1090
1227
|
}
|
|
1091
|
-
this.NodeManager.LavalinkManager.emit("trackStuck", player, track ||
|
|
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 ||
|
|
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 ||
|
|
1236
|
+
return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
|
|
1100
1237
|
}
|
|
1101
1238
|
// play track if autoSkip is true
|
|
1102
|
-
|
|
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
|
-
|
|
1258
|
+
player.destroy(DestroyReasons.TrackErrorMaxTracksErroredPerTime);
|
|
1259
|
+
return;
|
|
1119
1260
|
}
|
|
1120
1261
|
}
|
|
1121
|
-
this.NodeManager.LavalinkManager.emit("trackError", player, track ||
|
|
1122
|
-
return;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|