lavalink-client 2.3.1 → 2.3.4
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 +35 -22
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/structures/Constants.d.ts +44 -1
- package/dist/cjs/structures/Constants.js +45 -1
- package/dist/cjs/structures/LavalinkManager.js +80 -5
- package/dist/cjs/structures/Node.js +202 -20
- package/dist/cjs/structures/NodeManager.d.ts +1 -1
- package/dist/cjs/structures/NodeManager.js +2 -2
- package/dist/cjs/structures/Player.d.ts +0 -1
- package/dist/cjs/structures/Player.js +98 -8
- package/dist/cjs/structures/Queue.js +29 -2
- package/dist/cjs/structures/Types/Manager.d.ts +25 -0
- package/dist/cjs/structures/Types/Queue.d.ts +1 -1
- package/dist/cjs/structures/Utils.js +89 -23
- package/dist/esm/package.json +3 -0
- package/dist/esm/structures/Constants.d.ts +44 -1
- package/dist/esm/structures/Constants.js +44 -0
- package/dist/esm/structures/LavalinkManager.js +81 -6
- package/dist/esm/structures/Node.js +203 -21
- package/dist/esm/structures/NodeManager.d.ts +1 -1
- package/dist/esm/structures/NodeManager.js +1 -1
- package/dist/esm/structures/Player.d.ts +0 -1
- package/dist/esm/structures/Player.js +98 -8
- package/dist/esm/structures/Queue.js +29 -2
- package/dist/esm/structures/Types/Manager.d.ts +25 -0
- package/dist/esm/structures/Types/Queue.d.ts +1 -1
- package/dist/esm/structures/Utils.js +89 -23
- package/dist/types/structures/Constants.d.ts +44 -1
- package/dist/types/structures/NodeManager.d.ts +1 -1
- package/dist/types/structures/Player.d.ts +0 -1
- package/dist/types/structures/Types/Manager.d.ts +25 -0
- package/dist/types/structures/Types/Queue.d.ts +1 -1
- package/package.json +13 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAbsolute } from "path";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
-
import { DestroyReasons, validSponsorBlocks } from "./Constants";
|
|
3
|
+
import { DebugEvents, DestroyReasons, validSponsorBlocks } from "./Constants";
|
|
4
4
|
import { NodeSymbol, queueTrackEnd } from "./Utils";
|
|
5
5
|
/**
|
|
6
6
|
* Lavalink Node creator class
|
|
@@ -188,7 +188,8 @@ export class LavalinkNode {
|
|
|
188
188
|
}
|
|
189
189
|
let uri = `/loadtracks?identifier=`;
|
|
190
190
|
if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
|
|
191
|
-
|
|
191
|
+
const url = encodeURIComponent(Query.query);
|
|
192
|
+
uri += url;
|
|
192
193
|
}
|
|
193
194
|
else { // if not make a query out of it
|
|
194
195
|
if (Query.source !== "local")
|
|
@@ -205,8 +206,16 @@ export class LavalinkNode {
|
|
|
205
206
|
});
|
|
206
207
|
// transform the data which can be Error, Track or Track[] to enfore [Track]
|
|
207
208
|
const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
|
|
208
|
-
if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length))
|
|
209
|
+
if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length)) {
|
|
210
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
211
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.SearchNothingFound, {
|
|
212
|
+
state: "warn",
|
|
213
|
+
message: `Search found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
|
|
214
|
+
functionLayer: "(LavalinkNode > node | player) > search()",
|
|
215
|
+
});
|
|
216
|
+
}
|
|
209
217
|
throw new Error("Nothing found");
|
|
218
|
+
}
|
|
210
219
|
return {
|
|
211
220
|
loadType: res.loadType,
|
|
212
221
|
exception: res.loadType === "error" ? res.data : null,
|
|
@@ -250,8 +259,16 @@ export class LavalinkNode {
|
|
|
250
259
|
throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
|
|
251
260
|
const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
|
|
252
261
|
const res = (request.status === 204 ? {} : await request.json());
|
|
253
|
-
if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length)
|
|
262
|
+
if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
|
|
263
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
264
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.LavaSearchNothingFound, {
|
|
265
|
+
state: "warn",
|
|
266
|
+
message: `LavaSearch found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
|
|
267
|
+
functionLayer: "(LavalinkNode > node | player) > lavaSearch()",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
254
270
|
throw new Error("Nothing found");
|
|
271
|
+
}
|
|
255
272
|
return {
|
|
256
273
|
tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
|
|
257
274
|
albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
|
|
@@ -287,6 +304,13 @@ export class LavalinkNode {
|
|
|
287
304
|
r.path = url.pathname + url.search;
|
|
288
305
|
}
|
|
289
306
|
});
|
|
307
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
308
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateSuccess, {
|
|
309
|
+
state: "log",
|
|
310
|
+
message: `Player get's updated with following payload :: ${JSON.stringify(data.playerOptions, null, 3)}`,
|
|
311
|
+
functionLayer: "LavalinkNode > node > updatePlayer()",
|
|
312
|
+
});
|
|
313
|
+
}
|
|
290
314
|
return this.syncPlayerData({}, res), res;
|
|
291
315
|
}
|
|
292
316
|
/**
|
|
@@ -319,8 +343,16 @@ export class LavalinkNode {
|
|
|
319
343
|
* ```
|
|
320
344
|
*/
|
|
321
345
|
connect(sessionId) {
|
|
322
|
-
if (this.connected)
|
|
346
|
+
if (this.connected) {
|
|
347
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
348
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TryingConnectWhileConnected, {
|
|
349
|
+
state: "warn",
|
|
350
|
+
message: `Tryed to connect to node, but it's already connected!`,
|
|
351
|
+
functionLayer: "LavalinkNode > node > connect()",
|
|
352
|
+
});
|
|
353
|
+
}
|
|
323
354
|
return;
|
|
355
|
+
}
|
|
324
356
|
const headers = {
|
|
325
357
|
Authorization: this.options.authorization,
|
|
326
358
|
"User-Id": this.NodeManager.LavalinkManager.options.client.id,
|
|
@@ -338,11 +370,33 @@ export class LavalinkNode {
|
|
|
338
370
|
// this.socket.on("ping", () => this.heartBeat("ping")); // lavalink doesn'T send ping periodically, therefore we use the stats message
|
|
339
371
|
}
|
|
340
372
|
heartBeat() {
|
|
373
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
374
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.HeartBeatTriggered, {
|
|
375
|
+
state: "log",
|
|
376
|
+
message: `Node Socket Heartbeat triggered, resetting old Timeout to 65000ms (should happen every 60s due to /stats event)`,
|
|
377
|
+
functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat()",
|
|
378
|
+
});
|
|
379
|
+
}
|
|
341
380
|
if (this.pingTimeout)
|
|
342
381
|
clearTimeout(this.pingTimeout);
|
|
343
382
|
this.pingTimeout = setTimeout(() => {
|
|
344
|
-
if (!this.socket)
|
|
345
|
-
|
|
383
|
+
if (!this.socket) {
|
|
384
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
385
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.NoSocketOnDestroy, {
|
|
386
|
+
state: "error",
|
|
387
|
+
message: `Heartbeat registered a disconnect, but socket didn't exist therefore can't terminate`,
|
|
388
|
+
functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat() > timeoutHit",
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
394
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.SocketTerminateHeartBeatTimeout, {
|
|
395
|
+
state: "warn",
|
|
396
|
+
message: `Heartbeat registered a disconnect, because timeout wasn't resetted in time. Terminating Web-Socket`,
|
|
397
|
+
functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat() > timeoutHit",
|
|
398
|
+
});
|
|
399
|
+
}
|
|
346
400
|
this.isAlive = false;
|
|
347
401
|
this.socket.terminate();
|
|
348
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
|
|
@@ -834,8 +888,16 @@ export class LavalinkNode {
|
|
|
834
888
|
case "playerUpdate":
|
|
835
889
|
{
|
|
836
890
|
const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
|
|
837
|
-
if (!player)
|
|
891
|
+
if (!player) {
|
|
892
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
893
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateNoPlayer, {
|
|
894
|
+
state: "error",
|
|
895
|
+
message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
|
|
896
|
+
functionLayer: "LavalinkNode > nodeEvent > playerUpdate",
|
|
897
|
+
});
|
|
898
|
+
}
|
|
838
899
|
return;
|
|
900
|
+
}
|
|
839
901
|
const oldPlayer = player?.toJSON();
|
|
840
902
|
player.lastPositionChange = Date.now();
|
|
841
903
|
player.lastPosition = payload.state.position || 0;
|
|
@@ -845,6 +907,13 @@ export class LavalinkNode {
|
|
|
845
907
|
player.createdTimeStamp = payload.state.time;
|
|
846
908
|
if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600000) || isAbsolute(player.queue.current?.info?.uri))) {
|
|
847
909
|
player.filterManager.filterUpdatedState = false;
|
|
910
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
911
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateFilterFixApply, {
|
|
912
|
+
state: "log",
|
|
913
|
+
message: `Fixing FilterState on "${player.guildId}" because player.options.instaUpdateFiltersFix === true`,
|
|
914
|
+
functionLayer: "LavalinkNode > nodeEvent > playerUpdate",
|
|
915
|
+
});
|
|
916
|
+
}
|
|
848
917
|
await player.seek(player.position);
|
|
849
918
|
}
|
|
850
919
|
this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
|
|
@@ -861,7 +930,14 @@ export class LavalinkNode {
|
|
|
861
930
|
this.NodeManager.emit("resumed", this, payload, await this.fetchAllPlayers());
|
|
862
931
|
}
|
|
863
932
|
catch (e) {
|
|
864
|
-
|
|
933
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
934
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.ResumingFetchingError, {
|
|
935
|
+
state: "error",
|
|
936
|
+
message: `Failed to fetch players for resumed event, falling back without players array`,
|
|
937
|
+
error: e,
|
|
938
|
+
functionLayer: "LavalinkNode > nodeEvent > resumed",
|
|
939
|
+
});
|
|
940
|
+
}
|
|
865
941
|
this.NodeManager.emit("resumed", this, payload, []);
|
|
866
942
|
}
|
|
867
943
|
}
|
|
@@ -922,12 +998,30 @@ export class LavalinkNode {
|
|
|
922
998
|
player.playing = true;
|
|
923
999
|
player.paused = false;
|
|
924
1000
|
// don't emit the event if previous track == new track aka track loop
|
|
925
|
-
if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier)
|
|
1001
|
+
if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier) {
|
|
1002
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1003
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStartNewSongsOnly, {
|
|
1004
|
+
state: "log",
|
|
1005
|
+
message: `TrackStart not Emitting, because playing the previous song again.`,
|
|
1006
|
+
functionLayer: "LavalinkNode > trackStart()",
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
926
1009
|
return;
|
|
1010
|
+
}
|
|
927
1011
|
if (!player.queue.current) {
|
|
928
1012
|
player.queue.current = await this.getTrackOfPayload(payload);
|
|
929
|
-
if (player.queue.current)
|
|
1013
|
+
if (player.queue.current) {
|
|
930
1014
|
await player.queue.utils.save();
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1018
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStartNoTrack, {
|
|
1019
|
+
state: "warn",
|
|
1020
|
+
message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
|
|
1021
|
+
functionLayer: "LavalinkNode > trackStart()",
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
931
1025
|
}
|
|
932
1026
|
return this.NodeManager.LavalinkManager.emit("trackStart", player, player.queue.current, payload);
|
|
933
1027
|
}
|
|
@@ -936,6 +1030,13 @@ export class LavalinkNode {
|
|
|
936
1030
|
const trackToUse = track || await this.getTrackOfPayload(payload);
|
|
937
1031
|
// If a track was forcibly played
|
|
938
1032
|
if (payload.reason === "replaced") {
|
|
1033
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1034
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackEndReplaced, {
|
|
1035
|
+
state: "warn",
|
|
1036
|
+
message: `TrackEnd Event does not handle any playback, because the track was replaced.`,
|
|
1037
|
+
functionLayer: "LavalinkNode > trackEnd()",
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
939
1040
|
return this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
|
|
940
1041
|
}
|
|
941
1042
|
// If there are no songs in the queue
|
|
@@ -961,10 +1062,10 @@ export class LavalinkNode {
|
|
|
961
1062
|
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
962
1063
|
await player.queue.utils.save();
|
|
963
1064
|
}
|
|
964
|
-
player.set("internal_skipped", false);
|
|
965
1065
|
// if no track available, end queue
|
|
966
1066
|
if (!player.queue.current)
|
|
967
1067
|
return this.queueEnd(player, trackToUse, payload);
|
|
1068
|
+
player.set("internal_skipped", false);
|
|
968
1069
|
// fire event
|
|
969
1070
|
this.NodeManager.LavalinkManager.emit("trackEnd", player, trackToUse, payload);
|
|
970
1071
|
// play track if autoSkip is true
|
|
@@ -972,6 +1073,21 @@ export class LavalinkNode {
|
|
|
972
1073
|
}
|
|
973
1074
|
/** @private util function for handling trackStuck event */
|
|
974
1075
|
async trackStuck(player, track, payload) {
|
|
1076
|
+
if (this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
1077
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || [])
|
|
1078
|
+
.filter(v => Date.now() - v < this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold);
|
|
1079
|
+
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
1080
|
+
if (oldTimestamps.length > this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
1081
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1082
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackStuckMaxTracksErroredPerTime, {
|
|
1083
|
+
state: "log",
|
|
1084
|
+
message: `trackStuck Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
|
|
1085
|
+
functionLayer: "LavalinkNode > trackStuck()",
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return player.destroy(DestroyReasons.TrackStuckMaxTracksErroredPerTime);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
975
1091
|
this.NodeManager.LavalinkManager.emit("trackStuck", player, track || await this.getTrackOfPayload(payload), payload);
|
|
976
1092
|
// If there are no songs in the queue
|
|
977
1093
|
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
@@ -979,13 +1095,29 @@ export class LavalinkNode {
|
|
|
979
1095
|
// remove the current track, and enqueue the next one
|
|
980
1096
|
await queueTrackEnd(player);
|
|
981
1097
|
// if no track available, end queue
|
|
982
|
-
if (!player.queue.current)
|
|
1098
|
+
if (!player.queue.current) {
|
|
983
1099
|
return this.queueEnd(player, track || await this.getTrackOfPayload(payload), payload);
|
|
1100
|
+
}
|
|
984
1101
|
// play track if autoSkip is true
|
|
985
1102
|
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
986
1103
|
}
|
|
987
1104
|
/** @private util function for handling trackError event */
|
|
988
1105
|
async trackError(player, track, payload) {
|
|
1106
|
+
if (this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
1107
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || [])
|
|
1108
|
+
.filter(v => Date.now() - v < this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold);
|
|
1109
|
+
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
1110
|
+
if (oldTimestamps.length > this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
1111
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1112
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TrackErrorMaxTracksErroredPerTime, {
|
|
1113
|
+
state: "log",
|
|
1114
|
+
message: `TrackError Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
|
|
1115
|
+
functionLayer: "LavalinkNode > trackError()",
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
return player.destroy(DestroyReasons.TrackErrorMaxTracksErroredPerTime);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
989
1121
|
this.NodeManager.LavalinkManager.emit("trackError", player, track || await this.getTrackOfPayload(payload), payload);
|
|
990
1122
|
return; // get's handled by trackEnd
|
|
991
1123
|
// If there are no songs in the queue
|
|
@@ -1069,6 +1201,13 @@ export class LavalinkNode {
|
|
|
1069
1201
|
r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
|
|
1070
1202
|
r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
|
|
1071
1203
|
});
|
|
1204
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1205
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.SetSponsorBlock, {
|
|
1206
|
+
state: "log",
|
|
1207
|
+
message: `SponsorBlock was set for Player: ${player.guildId} to: ${segments.map(v => `'${v.toLowerCase()}'`).join(", ")}`,
|
|
1208
|
+
functionLayer: "LavalinkNode > setSponsorBlock()",
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1072
1211
|
return;
|
|
1073
1212
|
}
|
|
1074
1213
|
/**
|
|
@@ -1090,6 +1229,13 @@ export class LavalinkNode {
|
|
|
1090
1229
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
1091
1230
|
r.method = "DELETE";
|
|
1092
1231
|
});
|
|
1232
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1233
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.DeleteSponsorBlock, {
|
|
1234
|
+
state: "log",
|
|
1235
|
+
message: `SponsorBlock was deleted for Player: ${player.guildId}`,
|
|
1236
|
+
functionLayer: "LavalinkNode > deleteSponsorBlock()",
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1093
1239
|
return;
|
|
1094
1240
|
}
|
|
1095
1241
|
/** private util function for handling the queue end event */
|
|
@@ -1098,17 +1244,46 @@ export class LavalinkNode {
|
|
|
1098
1244
|
player.queue.current = null;
|
|
1099
1245
|
player.playing = false;
|
|
1100
1246
|
player.set("internal_stopPlaying", undefined);
|
|
1247
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1248
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.QueueEnded, {
|
|
1249
|
+
state: "log",
|
|
1250
|
+
message: `Queue Ended because no more Tracks were in the Queue, due to EventName: "${payload.type}"`,
|
|
1251
|
+
functionLayer: "LavalinkNode > queueEnd()",
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1101
1254
|
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.get("internal_autoplayStopPlaying") === "undefined") {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1255
|
+
const previousAutoplayTime = player.get("internal_previousautoplay");
|
|
1256
|
+
const duration = previousAutoplayTime ? Date.now() - previousAutoplayTime : 0;
|
|
1257
|
+
if ((duration && duration > this.NodeManager.LavalinkManager.options.playerOptions.minAutoPlayMs) || !!player.get("internal_skipped")) {
|
|
1258
|
+
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
1259
|
+
player.set("internal_previousautoplay", Date.now());
|
|
1260
|
+
if (player.queue.tracks.length > 0)
|
|
1261
|
+
await queueTrackEnd(player);
|
|
1262
|
+
else if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1263
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.AutoplayNoSongsAdded, {
|
|
1264
|
+
state: "warn",
|
|
1265
|
+
message: `Autoplay was triggered but no songs were added to the queue.`,
|
|
1266
|
+
functionLayer: "LavalinkNode > queueEnd() > autoplayFunction",
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
if (player.queue.current) {
|
|
1270
|
+
if (payload.type === "TrackEndEvent")
|
|
1271
|
+
this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
|
|
1272
|
+
return player.play({ noReplace: true, paused: false });
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1277
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.AutoplayThresholdSpamLimiter, {
|
|
1278
|
+
state: "warn",
|
|
1279
|
+
message: `Autoplay was triggered after the previousautoplay too early. Threshold is: ${this.NodeManager.LavalinkManager.options.playerOptions.minAutoPlayMs}ms and the Duration was ${duration}ms`,
|
|
1280
|
+
functionLayer: "LavalinkNode > queueEnd() > autoplayFunction",
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1109
1283
|
}
|
|
1110
1284
|
}
|
|
1111
|
-
player.set("
|
|
1285
|
+
player.set("internal_skipped", false);
|
|
1286
|
+
player.set("internal_autoplayStopPlaying", Date.now());
|
|
1112
1287
|
if (track && !track?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
1113
1288
|
player.queue.previous.unshift(track);
|
|
1114
1289
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
@@ -1122,6 +1297,13 @@ export class LavalinkNode {
|
|
|
1122
1297
|
if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
|
|
1123
1298
|
return player.destroy(DestroyReasons.QueueEmpty);
|
|
1124
1299
|
else {
|
|
1300
|
+
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1301
|
+
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.TriggerQueueEmptyInterval, {
|
|
1302
|
+
state: "log",
|
|
1303
|
+
message: `Trigger Queue Empty Interval was Triggered because playerOptions.onEmptyQueue.destroyAfterMs is set to ${this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs}ms`,
|
|
1304
|
+
functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs",
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1125
1307
|
if (player.get("internal_queueempty")) {
|
|
1126
1308
|
clearTimeout(player.get("internal_queueempty"));
|
|
1127
1309
|
player.set("internal_queueempty", undefined);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { EventEmitter } from "
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
3
|
import { LavalinkNode } from "./Node";
|
|
4
4
|
import { MiniMap } from "./Utils";
|
|
5
5
|
import type { LavalinkNodeIdentifier, LavalinkNodeOptions, NodeManagerEvents } from "./Types/Node";
|
|
@@ -134,7 +134,6 @@ export declare class Player {
|
|
|
134
134
|
* @param repeatMode
|
|
135
135
|
*/
|
|
136
136
|
setRepeatMode(repeatMode: RepeatMode): Promise<this>;
|
|
137
|
-
1: any;
|
|
138
137
|
/**
|
|
139
138
|
* Skip the current song, or a specific amount of songs
|
|
140
139
|
* @param amount provide the index of the next track to skip to
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DebugEvents } from "./Constants";
|
|
1
2
|
import { bandCampSearch } from "./CustomSearches/BandCampSearch";
|
|
2
3
|
import { FilterManager } from "./Filters";
|
|
3
4
|
import { Queue, QueueSaver } from "./Queue";
|
|
@@ -69,8 +70,17 @@ export class Player {
|
|
|
69
70
|
this.guildId = this.options.guildId;
|
|
70
71
|
this.voiceChannelId = this.options.voiceChannelId;
|
|
71
72
|
this.textChannelId = this.options.textChannelId || null;
|
|
72
|
-
this.node = typeof this.options.node === "string"
|
|
73
|
+
this.node = typeof this.options.node === "string"
|
|
74
|
+
? this.LavalinkManager.nodeManager.nodes.get(this.options.node)
|
|
75
|
+
: this.options.node;
|
|
73
76
|
if (!this.node || typeof this.node.request !== "function") {
|
|
77
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
78
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerCreateNodeNotFound, {
|
|
79
|
+
state: "warn",
|
|
80
|
+
message: `Player was created with provided node Id: ${this.options.node}, but no node with that Id was found.`,
|
|
81
|
+
functionLayer: "Player > constructor()",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
74
84
|
const least = this.LavalinkManager.nodeManager.leastUsedNodes();
|
|
75
85
|
this.node = least.filter(v => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || least[0] || null;
|
|
76
86
|
}
|
|
@@ -125,13 +135,43 @@ export class Player {
|
|
|
125
135
|
*/
|
|
126
136
|
async play(options = {}) {
|
|
127
137
|
if (this.get("internal_queueempty")) {
|
|
138
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
139
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayQueueEmptyTimeoutClear, {
|
|
140
|
+
state: "log",
|
|
141
|
+
message: `Player was called to play something, while there was a queueEmpty Timeout set, clearing the timeout.`,
|
|
142
|
+
functionLayer: "Player > play()",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
128
145
|
clearTimeout(this.get("internal_queueempty"));
|
|
129
146
|
this.set("internal_queueempty", undefined);
|
|
130
147
|
}
|
|
131
148
|
// if clientTrack provided, override options.track object
|
|
132
149
|
if (options?.clientTrack && (this.LavalinkManager.utils.isTrack(options?.clientTrack) || this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
|
|
133
|
-
if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))
|
|
134
|
-
|
|
150
|
+
if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack)) {
|
|
151
|
+
try {
|
|
152
|
+
// resolve the unresolved track
|
|
153
|
+
await options.clientTrack.resolve(this);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
157
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrackFailed, {
|
|
158
|
+
state: "error",
|
|
159
|
+
error: error,
|
|
160
|
+
message: `Player Play was called with clientTrack, Song is unresolved, but couldn't resolve it`,
|
|
161
|
+
functionLayer: "Player > play() > resolve currentTrack",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
165
|
+
if (options && "clientTrack" in options)
|
|
166
|
+
delete options.clientTrack;
|
|
167
|
+
if (options && "track" in options)
|
|
168
|
+
delete options.track;
|
|
169
|
+
// try to play the next track if possible
|
|
170
|
+
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
171
|
+
return this.play(options);
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
135
175
|
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
136
176
|
options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
|
|
137
177
|
options.track = {
|
|
@@ -165,6 +205,13 @@ export class Player {
|
|
|
165
205
|
...(track.userData || {}),
|
|
166
206
|
requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
|
|
167
207
|
};
|
|
208
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
209
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayWithTrackReplace, {
|
|
210
|
+
state: "log",
|
|
211
|
+
message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
|
|
212
|
+
functionLayer: "Player > play()",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
168
215
|
return this.node.updatePlayer({
|
|
169
216
|
guildId: this.guildId,
|
|
170
217
|
noReplace: false,
|
|
@@ -182,6 +229,13 @@ export class Player {
|
|
|
182
229
|
if (!this.queue.current && this.queue.tracks.length)
|
|
183
230
|
await queueTrackEnd(this);
|
|
184
231
|
if (this.queue.current && this.LavalinkManager.utils.isUnresolvedTrack(this.queue.current)) {
|
|
232
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
233
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrack, {
|
|
234
|
+
state: "log",
|
|
235
|
+
message: `Player Play was called, current Queue Song is unresolved, resolving the track.`,
|
|
236
|
+
functionLayer: "Player > play()",
|
|
237
|
+
});
|
|
238
|
+
}
|
|
185
239
|
try {
|
|
186
240
|
// resolve the unresolved track
|
|
187
241
|
await this.queue.current.resolve(this);
|
|
@@ -189,6 +243,14 @@ export class Player {
|
|
|
189
243
|
this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
|
|
190
244
|
}
|
|
191
245
|
catch (error) {
|
|
246
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
247
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrackFailed, {
|
|
248
|
+
state: "error",
|
|
249
|
+
error: error,
|
|
250
|
+
message: `Player Play was called, current Queue Song is unresolved, but couldn't resolve it`,
|
|
251
|
+
functionLayer: "Player > play() > resolve currentTrack",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
192
254
|
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
193
255
|
if (options && "clientTrack" in options)
|
|
194
256
|
delete options.clientTrack;
|
|
@@ -255,6 +317,13 @@ export class Player {
|
|
|
255
317
|
: this.volume), 1000), 0));
|
|
256
318
|
const now = performance.now();
|
|
257
319
|
if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
|
|
320
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
321
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerVolumeAsFilter, {
|
|
322
|
+
state: "log",
|
|
323
|
+
message: `Player Volume was set as a Filter, because LavalinkManager option "playerOptions.applyVolumeAsFilter" is true`,
|
|
324
|
+
functionLayer: "Player > setVolume()",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
258
327
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { filters: { volume: this.lavalinkVolume / 100 } } });
|
|
259
328
|
}
|
|
260
329
|
else {
|
|
@@ -299,8 +368,16 @@ export class Player {
|
|
|
299
368
|
*/
|
|
300
369
|
async search(query, requestUser, throwOnEmpty = false) {
|
|
301
370
|
const Query = this.LavalinkManager.utils.transformQuery(query);
|
|
302
|
-
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
|
|
371
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp")) {
|
|
372
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
373
|
+
this.LavalinkManager.emit("debug", DebugEvents.BandcampSearchLokalEngine, {
|
|
374
|
+
state: "log",
|
|
375
|
+
message: `Player.search was called with a Bandcamp Query, but no bandcamp search was enabled on lavalink, searching with the custom Search Engine.`,
|
|
376
|
+
functionLayer: "Player > search()",
|
|
377
|
+
});
|
|
378
|
+
}
|
|
303
379
|
return await bandCampSearch(this, Query.query, requestUser);
|
|
380
|
+
}
|
|
304
381
|
return this.node.search(Query, requestUser, throwOnEmpty);
|
|
305
382
|
}
|
|
306
383
|
/**
|
|
@@ -359,7 +436,6 @@ export class Player {
|
|
|
359
436
|
this.repeatMode = repeatMode;
|
|
360
437
|
return this;
|
|
361
438
|
}
|
|
362
|
-
1;
|
|
363
439
|
/**
|
|
364
440
|
* Skip the current song, or a specific amount of songs
|
|
365
441
|
* @param amount provide the index of the next track to skip to
|
|
@@ -465,6 +541,13 @@ export class Player {
|
|
|
465
541
|
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
466
542
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
|
|
467
543
|
if (this.get("internal_destroystatus") === true) {
|
|
544
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
545
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerDestroyingSomewhereElse, {
|
|
546
|
+
state: "warn",
|
|
547
|
+
message: `Player is already destroying somewhere else..`,
|
|
548
|
+
functionLayer: "Player > destroy()",
|
|
549
|
+
});
|
|
550
|
+
}
|
|
468
551
|
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
469
552
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
|
|
470
553
|
return;
|
|
@@ -496,10 +579,19 @@ export class Player {
|
|
|
496
579
|
const updateNode = typeof newNode === "string" ? this.LavalinkManager.nodeManager.nodes.get(newNode) : newNode;
|
|
497
580
|
if (!updateNode)
|
|
498
581
|
throw new Error("Could not find the new Node");
|
|
582
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
583
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
|
|
584
|
+
state: "log",
|
|
585
|
+
message: `Player.changeNode() was executed, trying to change from "${this.node.id}" to "${updateNode.id}"`,
|
|
586
|
+
functionLayer: "Player > changeNode()",
|
|
587
|
+
});
|
|
588
|
+
}
|
|
499
589
|
const data = this.toJSON();
|
|
590
|
+
const currentTrack = this.queue.current;
|
|
500
591
|
await this.node.destroyPlayer(this.guildId);
|
|
501
592
|
this.node = updateNode;
|
|
502
593
|
const now = performance.now();
|
|
594
|
+
await this.connect();
|
|
503
595
|
await this.node.updatePlayer({
|
|
504
596
|
guildId: this.guildId,
|
|
505
597
|
noReplace: false,
|
|
@@ -508,9 +600,7 @@ export class Player {
|
|
|
508
600
|
volume: Math.round(Math.max(Math.min(data.volume, 1000), 0)),
|
|
509
601
|
paused: data.paused,
|
|
510
602
|
filters: { ...data.filters, equalizer: data.equalizer },
|
|
511
|
-
|
|
512
|
-
track: this.queue.current ?? undefined
|
|
513
|
-
// track: this.queue.current,
|
|
603
|
+
track: currentTrack ?? undefined
|
|
514
604
|
},
|
|
515
605
|
});
|
|
516
606
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|