lavalink-client 2.3.0 → 2.3.3
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 +31 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -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/Player.d.ts +0 -1
- package/dist/cjs/structures/Player.js +73 -6
- 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 +68 -18
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -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/Player.d.ts +0 -1
- package/dist/esm/structures/Player.js +73 -6
- 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 +68 -18
- package/dist/types/index.d.ts +1 -0
- package/dist/types/structures/Constants.d.ts +44 -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 +1 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -13
- package/dist/structures/Filters.d.ts +0 -230
- package/dist/structures/Filters.js +0 -472
- package/dist/structures/LavalinkManager.d.ts +0 -47
- package/dist/structures/LavalinkManager.js +0 -36
- package/dist/structures/LavalinkManagerStatics.d.ts +0 -3
- package/dist/structures/LavalinkManagerStatics.js +0 -76
- package/dist/structures/Node.d.ts +0 -171
- package/dist/structures/Node.js +0 -462
- package/dist/structures/NodeManager.d.ts +0 -58
- package/dist/structures/NodeManager.js +0 -25
- package/dist/structures/Player.d.ts +0 -101
- package/dist/structures/Player.js +0 -232
- package/dist/structures/PlayerManager.d.ts +0 -62
- package/dist/structures/PlayerManager.js +0 -26
- package/dist/structures/Queue.d.ts +0 -93
- package/dist/structures/Queue.js +0 -160
- package/dist/structures/QueueManager.d.ts +0 -77
- package/dist/structures/QueueManager.js +0 -74
- package/dist/structures/Track.d.ts +0 -27
- package/dist/structures/Track.js +0 -2
- package/dist/structures/Utils.d.ts +0 -183
- package/dist/structures/Utils.js +0 -43
|
@@ -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);
|
|
@@ -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,6 +135,13 @@ export class Player {
|
|
|
125
135
|
*/
|
|
126
136
|
async play(options = {}) {
|
|
127
137
|
if (this.get("internal_queueempty")) {
|
|
138
|
+
if (typeof this.options.node === "string" && 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
|
}
|
|
@@ -165,6 +182,13 @@ export class Player {
|
|
|
165
182
|
...(track.userData || {}),
|
|
166
183
|
requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
|
|
167
184
|
};
|
|
185
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
186
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayWithTrackReplace, {
|
|
187
|
+
state: "log",
|
|
188
|
+
message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
|
|
189
|
+
functionLayer: "Player > play()",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
168
192
|
return this.node.updatePlayer({
|
|
169
193
|
guildId: this.guildId,
|
|
170
194
|
noReplace: false,
|
|
@@ -182,6 +206,13 @@ export class Player {
|
|
|
182
206
|
if (!this.queue.current && this.queue.tracks.length)
|
|
183
207
|
await queueTrackEnd(this);
|
|
184
208
|
if (this.queue.current && this.LavalinkManager.utils.isUnresolvedTrack(this.queue.current)) {
|
|
209
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
210
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrack, {
|
|
211
|
+
state: "log",
|
|
212
|
+
message: `Player Play was called, current Queue Song is unresolved, resolving the track.`,
|
|
213
|
+
functionLayer: "Player > play()",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
185
216
|
try {
|
|
186
217
|
// resolve the unresolved track
|
|
187
218
|
await this.queue.current.resolve(this);
|
|
@@ -189,6 +220,14 @@ export class Player {
|
|
|
189
220
|
this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
|
|
190
221
|
}
|
|
191
222
|
catch (error) {
|
|
223
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
224
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrackFailed, {
|
|
225
|
+
state: "error",
|
|
226
|
+
error: error,
|
|
227
|
+
message: `Player Play was called, current Queue Song is unresolved, but couldn't resolve it`,
|
|
228
|
+
functionLayer: "Player > play() > resolve currentTrack",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
192
231
|
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
193
232
|
if (options && "clientTrack" in options)
|
|
194
233
|
delete options.clientTrack;
|
|
@@ -255,6 +294,13 @@ export class Player {
|
|
|
255
294
|
: this.volume), 1000), 0));
|
|
256
295
|
const now = performance.now();
|
|
257
296
|
if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
|
|
297
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
298
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerVolumeAsFilter, {
|
|
299
|
+
state: "log",
|
|
300
|
+
message: `Player Volume was set as a Filter, because LavalinkManager option "playerOptions.applyVolumeAsFilter" is true`,
|
|
301
|
+
functionLayer: "Player > setVolume()",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
258
304
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { filters: { volume: this.lavalinkVolume / 100 } } });
|
|
259
305
|
}
|
|
260
306
|
else {
|
|
@@ -299,8 +345,16 @@ export class Player {
|
|
|
299
345
|
*/
|
|
300
346
|
async search(query, requestUser, throwOnEmpty = false) {
|
|
301
347
|
const Query = this.LavalinkManager.utils.transformQuery(query);
|
|
302
|
-
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
|
|
348
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp")) {
|
|
349
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
350
|
+
this.LavalinkManager.emit("debug", DebugEvents.BandcampSearchLokalEngine, {
|
|
351
|
+
state: "log",
|
|
352
|
+
message: `Player.search was called with a Bandcamp Query, but no bandcamp search was enabled on lavalink, searching with the custom Search Engine.`,
|
|
353
|
+
functionLayer: "Player > search()",
|
|
354
|
+
});
|
|
355
|
+
}
|
|
303
356
|
return await bandCampSearch(this, Query.query, requestUser);
|
|
357
|
+
}
|
|
304
358
|
return this.node.search(Query, requestUser, throwOnEmpty);
|
|
305
359
|
}
|
|
306
360
|
/**
|
|
@@ -359,7 +413,6 @@ export class Player {
|
|
|
359
413
|
this.repeatMode = repeatMode;
|
|
360
414
|
return this;
|
|
361
415
|
}
|
|
362
|
-
1;
|
|
363
416
|
/**
|
|
364
417
|
* Skip the current song, or a specific amount of songs
|
|
365
418
|
* @param amount provide the index of the next track to skip to
|
|
@@ -465,6 +518,13 @@ export class Player {
|
|
|
465
518
|
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
466
519
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
|
|
467
520
|
if (this.get("internal_destroystatus") === true) {
|
|
521
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
522
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerDestroyingSomewhereElse, {
|
|
523
|
+
state: "warn",
|
|
524
|
+
message: `Player is already destroying somewhere else..`,
|
|
525
|
+
functionLayer: "Player > destroy()",
|
|
526
|
+
});
|
|
527
|
+
}
|
|
468
528
|
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
469
529
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
|
|
470
530
|
return;
|
|
@@ -496,10 +556,19 @@ export class Player {
|
|
|
496
556
|
const updateNode = typeof newNode === "string" ? this.LavalinkManager.nodeManager.nodes.get(newNode) : newNode;
|
|
497
557
|
if (!updateNode)
|
|
498
558
|
throw new Error("Could not find the new Node");
|
|
559
|
+
if (typeof this.options.node === "string" && this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
560
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
|
|
561
|
+
state: "log",
|
|
562
|
+
message: `Player.changeNode() was executed, trying to change from "${this.node.id}" to "${updateNode.id}"`,
|
|
563
|
+
functionLayer: "Player > changeNode()",
|
|
564
|
+
});
|
|
565
|
+
}
|
|
499
566
|
const data = this.toJSON();
|
|
567
|
+
const currentTrack = this.queue.current;
|
|
500
568
|
await this.node.destroyPlayer(this.guildId);
|
|
501
569
|
this.node = updateNode;
|
|
502
570
|
const now = performance.now();
|
|
571
|
+
await this.connect();
|
|
503
572
|
await this.node.updatePlayer({
|
|
504
573
|
guildId: this.guildId,
|
|
505
574
|
noReplace: false,
|
|
@@ -508,9 +577,7 @@ export class Player {
|
|
|
508
577
|
volume: Math.round(Math.max(Math.min(data.volume, 1000), 0)),
|
|
509
578
|
paused: data.paused,
|
|
510
579
|
filters: { ...data.filters, equalizer: data.equalizer },
|
|
511
|
-
|
|
512
|
-
track: this.queue.current ?? undefined
|
|
513
|
-
// track: this.queue.current,
|
|
580
|
+
track: currentTrack ?? undefined
|
|
514
581
|
},
|
|
515
582
|
});
|
|
516
583
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
@@ -286,11 +286,18 @@ export class Queue {
|
|
|
286
286
|
* ```
|
|
287
287
|
*/
|
|
288
288
|
async remove(removeQueryTrack) {
|
|
289
|
+
const oldStored = typeof this.queueChanges?.tracksRemoved === "function" ? this.utils.toJSON() : null;
|
|
289
290
|
if (typeof removeQueryTrack === "number") {
|
|
290
291
|
const toRemove = this.tracks[removeQueryTrack];
|
|
291
292
|
if (!toRemove)
|
|
292
293
|
return null;
|
|
293
294
|
const removed = this.tracks.splice(removeQueryTrack, 1);
|
|
295
|
+
// Log if available
|
|
296
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
297
|
+
try {
|
|
298
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
|
|
299
|
+
}
|
|
300
|
+
catch (e) { /* */ }
|
|
294
301
|
await this.utils.save();
|
|
295
302
|
return { removed };
|
|
296
303
|
}
|
|
@@ -298,11 +305,18 @@ export class Queue {
|
|
|
298
305
|
if (removeQueryTrack.every(v => typeof v === "number")) {
|
|
299
306
|
const removed = [];
|
|
300
307
|
for (const i of removeQueryTrack) {
|
|
301
|
-
if (this.tracks[i])
|
|
308
|
+
if (this.tracks[i]) {
|
|
302
309
|
removed.push(...this.tracks.splice(i, 1));
|
|
310
|
+
}
|
|
303
311
|
}
|
|
304
312
|
if (!removed.length)
|
|
305
313
|
return null;
|
|
314
|
+
// Log if available
|
|
315
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
316
|
+
try {
|
|
317
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
|
|
318
|
+
}
|
|
319
|
+
catch (e) { /* */ }
|
|
306
320
|
await this.utils.save();
|
|
307
321
|
return { removed };
|
|
308
322
|
}
|
|
@@ -317,9 +331,16 @@ export class Queue {
|
|
|
317
331
|
return null;
|
|
318
332
|
const removed = [];
|
|
319
333
|
for (const { i } of tracksToRemove) {
|
|
320
|
-
if (this.tracks[i])
|
|
334
|
+
if (this.tracks[i]) {
|
|
321
335
|
removed.push(...this.tracks.splice(i, 1));
|
|
336
|
+
}
|
|
322
337
|
}
|
|
338
|
+
// Log if available
|
|
339
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
340
|
+
try {
|
|
341
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, tracksToRemove.map(v => v.i), oldStored, this.utils.toJSON());
|
|
342
|
+
}
|
|
343
|
+
catch (e) { /* */ }
|
|
323
344
|
await this.utils.save();
|
|
324
345
|
return { removed };
|
|
325
346
|
}
|
|
@@ -332,6 +353,12 @@ export class Queue {
|
|
|
332
353
|
if (toRemove < 0)
|
|
333
354
|
return null;
|
|
334
355
|
const removed = this.tracks.splice(toRemove, 1);
|
|
356
|
+
// Log if available
|
|
357
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
358
|
+
try {
|
|
359
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
|
|
360
|
+
}
|
|
361
|
+
catch (e) { /* */ }
|
|
335
362
|
await this.utils.save();
|
|
336
363
|
return { removed };
|
|
337
364
|
}
|