lavalink-client 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -1
- package/dist/cjs/structures/Filters.d.ts +1 -1
- package/dist/cjs/structures/Filters.js +5 -5
- package/dist/cjs/structures/LavalinkManager.d.ts +18 -1
- package/dist/cjs/structures/LavalinkManager.js +14 -1
- package/dist/cjs/structures/LavalinkManagerStatics.d.ts +3 -0
- package/dist/cjs/structures/LavalinkManagerStatics.js +3 -0
- package/dist/cjs/structures/Node.d.ts +307 -22
- package/dist/cjs/structures/Node.js +317 -68
- package/dist/cjs/structures/Player.d.ts +44 -8
- package/dist/cjs/structures/Player.js +26 -18
- package/dist/cjs/structures/Utils.d.ts +3 -0
- package/dist/cjs/structures/Utils.js +1 -0
- package/dist/esm/structures/Filters.d.ts +1 -1
- package/dist/esm/structures/Filters.js +5 -5
- package/dist/esm/structures/LavalinkManager.d.ts +18 -1
- package/dist/esm/structures/LavalinkManager.js +14 -1
- package/dist/esm/structures/LavalinkManagerStatics.d.ts +3 -0
- package/dist/esm/structures/LavalinkManagerStatics.js +3 -0
- package/dist/esm/structures/Node.d.ts +307 -22
- package/dist/esm/structures/Node.js +317 -68
- package/dist/esm/structures/Player.d.ts +44 -8
- package/dist/esm/structures/Player.js +26 -18
- package/dist/esm/structures/Utils.d.ts +3 -0
- package/dist/esm/structures/Utils.js +1 -0
- package/dist/types/structures/Filters.d.ts +1 -1
- package/dist/types/structures/LavalinkManager.d.ts +18 -1
- package/dist/types/structures/LavalinkManagerStatics.d.ts +3 -0
- package/dist/types/structures/Node.d.ts +307 -22
- package/dist/types/structures/Player.d.ts +44 -8
- package/dist/types/structures/Utils.d.ts +3 -0
- package/package.json +2 -3
|
@@ -7,11 +7,15 @@ const ws_1 = tslib_1.__importDefault(require("ws"));
|
|
|
7
7
|
const Player_1 = require("./Player");
|
|
8
8
|
const Utils_1 = require("./Utils");
|
|
9
9
|
exports.validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
|
|
10
|
+
/**
|
|
11
|
+
* Lavalink Node creator class
|
|
12
|
+
*/
|
|
10
13
|
class LavalinkNode {
|
|
11
14
|
/** The provided Options of the Node */
|
|
12
15
|
options;
|
|
13
16
|
/** The amount of rest calls the node has made. */
|
|
14
17
|
calls = 0;
|
|
18
|
+
/** Stats from lavalink, will be updated via an interval by lavalink. */
|
|
15
19
|
stats = {
|
|
16
20
|
players: 0,
|
|
17
21
|
playingPlayers: 0,
|
|
@@ -33,6 +37,7 @@ class LavalinkNode {
|
|
|
33
37
|
sent: 0,
|
|
34
38
|
}
|
|
35
39
|
};
|
|
40
|
+
/** The current sessionId, only present when connected */
|
|
36
41
|
sessionId = null;
|
|
37
42
|
/** Wether the node resuming is enabled or not */
|
|
38
43
|
resuming = { enabled: true, timeout: null };
|
|
@@ -52,6 +57,14 @@ class LavalinkNode {
|
|
|
52
57
|
* Create a new Node
|
|
53
58
|
* @param options Lavalink Node Options
|
|
54
59
|
* @param manager Node Manager
|
|
60
|
+
*
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* // don't create a node manually, instead use:
|
|
65
|
+
*
|
|
66
|
+
* client.lavalink.nodeManager.createNode(options)
|
|
67
|
+
* ```
|
|
55
68
|
*/
|
|
56
69
|
constructor(options, manager) {
|
|
57
70
|
this.options = {
|
|
@@ -68,11 +81,43 @@ class LavalinkNode {
|
|
|
68
81
|
this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
|
|
69
82
|
Object.defineProperty(this, Utils_1.NodeSymbol, { configurable: true, value: true });
|
|
70
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse url params correctly for lavalink requests, including support for urls and uris.
|
|
86
|
+
* @param url input url object
|
|
87
|
+
* @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
|
|
88
|
+
* @returns the url as a valid string
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* player.node.getRequestingUrl(new URL(`http://localhost:2333/v4/loadtracks?identifier=Never gonna give you up`));
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
getRequestingUrl(url, extraQueryUrlParams) {
|
|
96
|
+
if (!url.searchParams.size)
|
|
97
|
+
return `${url.origin}${url.pathname}`;
|
|
98
|
+
const keysToAdd = [];
|
|
99
|
+
for (const [paramKey, paramValue] of url.searchParams.entries()) {
|
|
100
|
+
const decoded = decodeURIComponent(paramValue).trim(); // double decoding, once internally, a second time if decoded by provided user.
|
|
101
|
+
if (decoded.includes("://") && !/^https?:\/\//.test(decoded)) { // uri, but not url.
|
|
102
|
+
const [key, ...values] = decoded.split("://");
|
|
103
|
+
keysToAdd.push(`${paramKey}=${encodeURI(`${key}://${encodeURIComponent(values.join("://"))}${extraQueryUrlParams && extraQueryUrlParams?.size > 0 ? `?${extraQueryUrlParams.toString()}` : ""}`)}`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
keysToAdd.push(`${paramKey}=${encodeURIComponent(decoded)}`);
|
|
107
|
+
}
|
|
108
|
+
return `${url.origin}${url.pathname}?${keysToAdd.join("&")}`;
|
|
109
|
+
}
|
|
71
110
|
/**
|
|
72
111
|
* Raw Request util function
|
|
73
112
|
* @param endpoint endpoint string
|
|
74
113
|
* @param modify modify the request
|
|
75
|
-
* @
|
|
114
|
+
* @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
|
|
115
|
+
* @returns object containing request and option information
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* player.node.rawRequest(`/loadtracks?identifier=Never gonna give you up`, (options) => options.method = "GET");
|
|
120
|
+
* ```
|
|
76
121
|
*/
|
|
77
122
|
async rawRequest(endpoint, modify) {
|
|
78
123
|
const options = {
|
|
@@ -86,16 +131,23 @@ class LavalinkNode {
|
|
|
86
131
|
modify?.(options);
|
|
87
132
|
const url = new URL(`${this.restAddress}${options.path}`);
|
|
88
133
|
url.searchParams.append("trace", "true");
|
|
134
|
+
const urlToUse = this.getRequestingUrl(url, options?.extraQueryUrlParams);
|
|
89
135
|
delete options.path;
|
|
90
|
-
|
|
136
|
+
delete options.extraQueryUrlParams;
|
|
137
|
+
const request = await fetch(urlToUse, options);
|
|
91
138
|
this.calls++;
|
|
92
139
|
return { request, options };
|
|
93
140
|
}
|
|
94
141
|
/**
|
|
95
|
-
* Makes an API call to the Node
|
|
142
|
+
* Makes an API call to the Node. Should only be used for manual parsing like for not supported plugins
|
|
96
143
|
* @param endpoint The endpoint that we will make the call to
|
|
97
144
|
* @param modify Used to modify the request before being sent
|
|
98
145
|
* @returns The returned data
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* player.node.request(`/loadtracks?identifier=Never gonna give you up`, (options) => options.method = "GET", false);
|
|
150
|
+
* ```
|
|
99
151
|
*/
|
|
100
152
|
async request(endpoint, modify, parseAsText = false) {
|
|
101
153
|
const { request, options } = await this.rawRequest(endpoint, modify);
|
|
@@ -109,9 +161,17 @@ class LavalinkNode {
|
|
|
109
161
|
* Search something raw on the node, please note only add tracks to players of that node
|
|
110
162
|
* @param query SearchQuery Object
|
|
111
163
|
* @param requestUser Request User for creating the player(s)
|
|
164
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
112
165
|
* @returns Searchresult
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* // use player.search() instead
|
|
170
|
+
* player.node.search({ query: "Never gonna give you up by Rick Astley", source: "soundcloud" }, interaction.user);
|
|
171
|
+
* player.node.search({ query: "https://deezer.com/track/123456789" }, interaction.user);
|
|
172
|
+
* ```
|
|
113
173
|
*/
|
|
114
|
-
async search(query, requestUser) {
|
|
174
|
+
async search(query, requestUser, throwOnEmpty = false) {
|
|
115
175
|
const Query = this.NodeManager.LavalinkManager.utils.transformQuery(query);
|
|
116
176
|
this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query, Query.source);
|
|
117
177
|
if (Query.source)
|
|
@@ -121,19 +181,25 @@ class LavalinkNode {
|
|
|
121
181
|
}
|
|
122
182
|
let uri = `/loadtracks?identifier=`;
|
|
123
183
|
if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
|
|
124
|
-
uri += encodeURIComponent(
|
|
184
|
+
uri += encodeURIComponent(Query.query);
|
|
125
185
|
}
|
|
126
186
|
else { // if not make a query out of it
|
|
127
187
|
if (Query.source !== "local")
|
|
128
188
|
uri += `${Query.source}:`; // only add the query source string if it's not a local track
|
|
129
189
|
if (Query.source === "ftts")
|
|
130
|
-
uri += `//${encodeURIComponent(
|
|
190
|
+
uri += `//${encodeURIComponent(Query.query)}`;
|
|
131
191
|
else
|
|
132
|
-
uri += encodeURIComponent(
|
|
192
|
+
uri += encodeURIComponent(Query.query);
|
|
133
193
|
}
|
|
134
|
-
const res = await this.request(uri)
|
|
194
|
+
const res = await this.request(uri, (options) => {
|
|
195
|
+
if (typeof query === "object" && typeof query.extraQueryUrlParams?.size === "number" && query.extraQueryUrlParams?.size > 0) {
|
|
196
|
+
options.extraQueryUrlParams = query.extraQueryUrlParams;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
135
199
|
// transform the data which can be Error, Track or Track[] to enfore [Track]
|
|
136
200
|
const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
|
|
201
|
+
if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length))
|
|
202
|
+
throw new Error("Nothing found");
|
|
137
203
|
return {
|
|
138
204
|
loadType: res.loadType,
|
|
139
205
|
exception: res.loadType === "error" ? res.data : null,
|
|
@@ -150,6 +216,19 @@ class LavalinkNode {
|
|
|
150
216
|
tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
|
|
151
217
|
};
|
|
152
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Search something using the lavaSearchPlugin (filtered searches by types)
|
|
221
|
+
* @param query LavaSearchQuery Object
|
|
222
|
+
* @param requestUser Request User for creating the player(s)
|
|
223
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
224
|
+
* @returns LavaSearchresult
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* // use player.search() instead
|
|
229
|
+
* player.node.lavaSearch({ types: ["playlist", "album"], query: "Rick Astley", source: "spotify" }, interaction.user);
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
153
232
|
async lavaSearch(query, requestUser, throwOnEmpty = false) {
|
|
154
233
|
const Query = this.NodeManager.LavalinkManager.utils.transformLavaSearchQuery(query);
|
|
155
234
|
if (Query.source)
|
|
@@ -163,9 +242,9 @@ class LavalinkNode {
|
|
|
163
242
|
if (!this.info.plugins.find(v => v.name === "lavasrc-plugin"))
|
|
164
243
|
throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
|
|
165
244
|
const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
|
|
166
|
-
if (throwOnEmpty === true)
|
|
167
|
-
throw new Error("Nothing found");
|
|
168
245
|
const res = (request.status === 204 ? {} : await request.json());
|
|
246
|
+
if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length)
|
|
247
|
+
throw new Error("Nothing found");
|
|
169
248
|
return {
|
|
170
249
|
tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
|
|
171
250
|
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)) })) || [],
|
|
@@ -177,8 +256,14 @@ class LavalinkNode {
|
|
|
177
256
|
}
|
|
178
257
|
/**
|
|
179
258
|
* Update the Player State on the Lavalink Server
|
|
180
|
-
* @param data
|
|
181
|
-
* @returns
|
|
259
|
+
* @param data data to send to lavalink and sync locally
|
|
260
|
+
* @returns result from lavalink
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* // use player.search() instead
|
|
265
|
+
* player.node.updatePlayer({ guildId: player.guildId, playerOptions: { paused: true } }); // example to pause it
|
|
266
|
+
* ```
|
|
182
267
|
*/
|
|
183
268
|
async updatePlayer(data) {
|
|
184
269
|
if (!this.sessionId)
|
|
@@ -200,7 +285,13 @@ class LavalinkNode {
|
|
|
200
285
|
/**
|
|
201
286
|
* Destroys the Player on the Lavalink Server
|
|
202
287
|
* @param guildId
|
|
203
|
-
* @returns
|
|
288
|
+
* @returns request result
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* // use player.destroy() instead
|
|
293
|
+
* player.node.destroyPlayer(player.guildId);
|
|
294
|
+
* ```
|
|
204
295
|
*/
|
|
205
296
|
async destroyPlayer(guildId) {
|
|
206
297
|
if (!this.sessionId)
|
|
@@ -210,7 +301,15 @@ class LavalinkNode {
|
|
|
210
301
|
/**
|
|
211
302
|
* Connect to the Lavalink Node
|
|
212
303
|
* @param sessionId Provide the Session Id of the previous connection, to resume the node and it's player(s)
|
|
213
|
-
* @returns
|
|
304
|
+
* @returns void
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```ts
|
|
308
|
+
* player.node.connect(); // if provided on bootup in managerOptions#nodes, this will be called automatically when doing lavalink.init()
|
|
309
|
+
*
|
|
310
|
+
* // or connect from a resuming session:
|
|
311
|
+
* player.node.connect("sessionId");
|
|
312
|
+
* ```
|
|
214
313
|
*/
|
|
215
314
|
connect(sessionId) {
|
|
216
315
|
if (this.connected)
|
|
@@ -230,13 +329,28 @@ class LavalinkNode {
|
|
|
230
329
|
this.socket.on("message", this.message.bind(this));
|
|
231
330
|
this.socket.on("error", this.error.bind(this));
|
|
232
331
|
}
|
|
233
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Get the id of the node
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* const nodeId = player.node.id;
|
|
338
|
+
* console.log("node id is: ", nodeId)
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
234
341
|
get id() {
|
|
235
342
|
return this.options.id || `${this.options.host}:${this.options.port}`;
|
|
236
343
|
}
|
|
237
344
|
/**
|
|
238
345
|
* Destroys the Node-Connection (Websocket) and all player's of the node
|
|
239
|
-
* @
|
|
346
|
+
* @param destroyReason Destroyreason to use when destroying the players
|
|
347
|
+
* @param deleteNode wether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
|
|
348
|
+
* @returns void
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```ts
|
|
352
|
+
* player.node.destroy("custom Player Destroy Reason", true);
|
|
353
|
+
* ```
|
|
240
354
|
*/
|
|
241
355
|
destroy(destroyReason, deleteNode = true) {
|
|
242
356
|
if (!this.connected)
|
|
@@ -258,14 +372,47 @@ class LavalinkNode {
|
|
|
258
372
|
}
|
|
259
373
|
return;
|
|
260
374
|
}
|
|
261
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* Returns if connected to the Node.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* const isConnected = player.node.connected;
|
|
381
|
+
* console.log("node is connected: ", isConnected ? "yes" : "no")
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
262
384
|
get connected() {
|
|
263
385
|
if (!this.socket)
|
|
264
386
|
return false;
|
|
265
387
|
return this.socket.readyState === ws_1.default.OPEN;
|
|
266
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Returns the current ConnectionStatus
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* try {
|
|
395
|
+
* const statusOfConnection = player.node.connectionStatus;
|
|
396
|
+
* console.log("node's connection status is:", statusOfConnection)
|
|
397
|
+
* } catch (error) {
|
|
398
|
+
* console.error("no socket available?", error)
|
|
399
|
+
* }
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
get connectionStatus() {
|
|
403
|
+
if (!this.socket)
|
|
404
|
+
throw new Error("no websocket was initialized yet");
|
|
405
|
+
return ["CONNECTING", "OPEN", "CLOSING", "CLOSED"][this.socket.readyState] || "UNKNOWN";
|
|
406
|
+
}
|
|
267
407
|
/**
|
|
268
408
|
* Gets all Players of a Node
|
|
409
|
+
* @returns array of players inside of lavalink
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```ts
|
|
413
|
+
* const node = lavalink.nodes.get("NODEID");
|
|
414
|
+
* const playersOfLavalink = await node?.fetchAllPlayers();
|
|
415
|
+
* ```
|
|
269
416
|
*/
|
|
270
417
|
async fetchAllPlayers() {
|
|
271
418
|
if (!this.sessionId)
|
|
@@ -278,6 +425,13 @@ class LavalinkNode {
|
|
|
278
425
|
}
|
|
279
426
|
/**
|
|
280
427
|
* Gets specific Player Information
|
|
428
|
+
* @returns lavalink player object if player exists on lavalink
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* const node = lavalink.nodes.get("NODEID");
|
|
433
|
+
* const playerInformation = await node?.fetchPlayer("guildId");
|
|
434
|
+
* ```
|
|
281
435
|
*/
|
|
282
436
|
async fetchPlayer(guildId) {
|
|
283
437
|
if (!this.sessionId)
|
|
@@ -288,6 +442,13 @@ class LavalinkNode {
|
|
|
288
442
|
* Updates the session with and enables/disables resuming and timeout
|
|
289
443
|
* @param resuming Whether resuming is enabled for this session or not
|
|
290
444
|
* @param timeout The timeout in seconds (default is 60s)
|
|
445
|
+
* @returns the result of the request
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* const node = player.node || lavalink.nodes.get("NODEID");
|
|
450
|
+
* await node?.updateSession(true, 180e3); // will enable resuming for 180seconds
|
|
451
|
+
* ```
|
|
291
452
|
*/
|
|
292
453
|
async updateSession(resuming, timeout) {
|
|
293
454
|
if (!this.sessionId)
|
|
@@ -312,20 +473,35 @@ class LavalinkNode {
|
|
|
312
473
|
*/
|
|
313
474
|
decode = {
|
|
314
475
|
/**
|
|
315
|
-
* Decode a single track into its info
|
|
316
|
-
* @param encoded
|
|
317
|
-
* @
|
|
476
|
+
* Decode a single track into its info
|
|
477
|
+
* @param encoded valid encoded base64 string from a track
|
|
478
|
+
* @param requester the requesteruser for building the track
|
|
479
|
+
* @returns decoded track from lavalink
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```ts
|
|
483
|
+
* const encodedBase64 = 'QAACDgMACk5vIERpZ2dpdHkAC0JsYWNrc3RyZWV0AAAAAAAEo4AABjkxNjQ5NgABAB9odHRwczovL2RlZXplci5jb20vdHJhY2svOTE2NDk2AQBpaHR0cHM6Ly9lLWNkbnMtaW1hZ2VzLmR6Y2RuLm5ldC9pbWFnZXMvY292ZXIvZGFlN2EyNjViNzlmYjcxMjc4Y2RlMjUwNDg0OWQ2ZjcvMTAwMHgxMDAwLTAwMDAwMC04MC0wLTAuanBnAQAMVVNJUjE5NjAwOTc4AAZkZWV6ZXIBAChObyBEaWdnaXR5OiBUaGUgVmVyeSBCZXN0IE9mIEJsYWNrc3RyZWV0AQAjaHR0cHM6Ly93d3cuZGVlemVyLmNvbS9hbGJ1bS8xMDMyNTQBACJodHRwczovL3d3dy5kZWV6ZXIuY29tL2FydGlzdC8xODYxAQBqaHR0cHM6Ly9lLWNkbnMtaW1hZ2VzLmR6Y2RuLm5ldC9pbWFnZXMvYXJ0aXN0L2YxNmNhYzM2ZmVjMzkxZjczN2I3ZDQ4MmY1YWM3M2UzLzEwMDB4MTAwMC0wMDAwMDAtODAtMC0wLmpwZwEAT2h0dHBzOi8vY2RuLXByZXZpZXctYS5kemNkbi5uZXQvc3RyZWFtL2MtYTE1Yjg1NzFhYTYyMDBjMDQ0YmY1OWM3NmVkOTEyN2MtNi5tcDMAAAAAAAAAAAA=';
|
|
484
|
+
* const track = await player.node.decode.singleTrack(encodedBase64, interaction.user);
|
|
485
|
+
* ```
|
|
318
486
|
*/
|
|
319
487
|
singleTrack: async (encoded, requester) => {
|
|
320
488
|
if (!encoded)
|
|
321
489
|
throw new SyntaxError("No encoded (Base64 string) was provided");
|
|
322
490
|
// return the decoded + builded track
|
|
323
|
-
return this.NodeManager.LavalinkManager.utils
|
|
491
|
+
return this.NodeManager.LavalinkManager.utils?.buildTrack(await this.request(`/decodetrack?encodedTrack=${encodeURIComponent(encoded.replace(/\s/g, ""))}`), requester);
|
|
324
492
|
},
|
|
325
493
|
/**
|
|
494
|
+
* Decodes multiple tracks into their info
|
|
495
|
+
* @param encodeds valid encoded base64 string array from all tracks
|
|
496
|
+
* @param requester the requesteruser for building the tracks
|
|
497
|
+
* @returns array of all tracks you decoded
|
|
326
498
|
*
|
|
327
|
-
* @
|
|
328
|
-
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* const encodedBase64_1 = 'QAACDgMACk5vIERpZ2dpdHkAC0JsYWNrc3RyZWV0AAAAAAAEo4AABjkxNjQ5NgABAB9odHRwczovL2RlZXplci5jb20vdHJhY2svOTE2NDk2AQBpaHR0cHM6Ly9lLWNkbnMtaW1hZ2VzLmR6Y2RuLm5ldC9pbWFnZXMvY292ZXIvZGFlN2EyNjViNzlmYjcxMjc4Y2RlMjUwNDg0OWQ2ZjcvMTAwMHgxMDAwLTAwMDAwMC04MC0wLTAuanBnAQAMVVNJUjE5NjAwOTc4AAZkZWV6ZXIBAChObyBEaWdnaXR5OiBUaGUgVmVyeSBCZXN0IE9mIEJsYWNrc3RyZWV0AQAjaHR0cHM6Ly93d3cuZGVlemVyLmNvbS9hbGJ1bS8xMDMyNTQBACJodHRwczovL3d3dy5kZWV6ZXIuY29tL2FydGlzdC8xODYxAQBqaHR0cHM6Ly9lLWNkbnMtaW1hZ2VzLmR6Y2RuLm5ldC9pbWFnZXMvYXJ0aXN0L2YxNmNhYzM2ZmVjMzkxZjczN2I3ZDQ4MmY1YWM3M2UzLzEwMDB4MTAwMC0wMDAwMDAtODAtMC0wLmpwZwEAT2h0dHBzOi8vY2RuLXByZXZpZXctYS5kemNkbi5uZXQvc3RyZWFtL2MtYTE1Yjg1NzFhYTYyMDBjMDQ0YmY1OWM3NmVkOTEyN2MtNi5tcDMAAAAAAAAAAAA=';
|
|
502
|
+
* const encodedBase64_2 = 'QAABJAMAClRhbGsgYSBMb3QACjQwNHZpbmNlbnQAAAAAAAHr1gBxTzpodHRwczovL2FwaS12Mi5zb3VuZGNsb3VkLmNvbS9tZWRpYS9zb3VuZGNsb3VkOnRyYWNrczo4NTE0MjEwNzYvMzUyYTRiOTAtNzYxOS00M2E5LWJiOGItMjIxMzE0YzFjNjNhL3N0cmVhbS9obHMAAQAsaHR0cHM6Ly9zb3VuZGNsb3VkLmNvbS80MDR2aW5jZW50L3RhbGstYS1sb3QBADpodHRwczovL2kxLnNuZGNkbi5jb20vYXJ0d29ya3MtRTN1ek5Gc0Y4QzBXLTAtb3JpZ2luYWwuanBnAQAMUVpITkExOTg1Nzg0AApzb3VuZGNsb3VkAAAAAAAAAAA=';
|
|
503
|
+
* const tracks = await player.node.decode.multipleTracks([encodedBase64_1, encodedBase64_2], interaction.user);
|
|
504
|
+
* ```
|
|
329
505
|
*/
|
|
330
506
|
multipleTracks: async (encodeds, requester) => {
|
|
331
507
|
if (!Array.isArray(encodeds) || !encodeds.every(v => typeof v === "string" && v.length > 1))
|
|
@@ -341,14 +517,24 @@ class LavalinkNode {
|
|
|
341
517
|
};
|
|
342
518
|
/**
|
|
343
519
|
* Request Lavalink statistics.
|
|
344
|
-
* @returns
|
|
520
|
+
* @returns the lavalink node stats
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```ts
|
|
524
|
+
* const lavalinkStats = await player.node.fetchStats();
|
|
525
|
+
* ```
|
|
345
526
|
*/
|
|
346
527
|
async fetchStats() {
|
|
347
528
|
return await this.request(`/stats`);
|
|
348
529
|
}
|
|
349
530
|
/**
|
|
350
531
|
* Request Lavalink version.
|
|
351
|
-
* @returns
|
|
532
|
+
* @returns the current used lavalink version
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* const lavalinkVersion = await player.node.fetchVersion();
|
|
537
|
+
* ```
|
|
352
538
|
*/
|
|
353
539
|
async fetchVersion() {
|
|
354
540
|
// need to adjust path for no-prefix version info
|
|
@@ -356,7 +542,14 @@ class LavalinkNode {
|
|
|
356
542
|
}
|
|
357
543
|
/**
|
|
358
544
|
* Request Lavalink information.
|
|
359
|
-
* @returns
|
|
545
|
+
* @returns lavalink info object
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* const lavalinkInfo = await player.node.fetchInfo();
|
|
550
|
+
* const availablePlugins:string[] = lavalinkInfo.plugins.map(plugin => plugin.name);
|
|
551
|
+
* const availableSources:string[] = lavalinkInfo.sourceManagers;
|
|
552
|
+
* ```
|
|
360
553
|
*/
|
|
361
554
|
async fetchInfo() {
|
|
362
555
|
return await this.request(`/info`);
|
|
@@ -366,7 +559,15 @@ class LavalinkNode {
|
|
|
366
559
|
*/
|
|
367
560
|
routePlannerApi = {
|
|
368
561
|
/**
|
|
369
|
-
* Get routplanner Info from Lavalink
|
|
562
|
+
* Get routplanner Info from Lavalink for ip rotation
|
|
563
|
+
* @returns the status of the routeplanner
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
* ```ts
|
|
567
|
+
* const routePlannerStatus = await player.node.routePlannerApi.getStatus();
|
|
568
|
+
* const usedBlock = routePlannerStatus.details?.ipBlock;
|
|
569
|
+
* const currentIp = routePlannerStatus.currentAddress;
|
|
570
|
+
* ```
|
|
370
571
|
*/
|
|
371
572
|
getStatus: async () => {
|
|
372
573
|
if (!this.sessionId)
|
|
@@ -374,8 +575,14 @@ class LavalinkNode {
|
|
|
374
575
|
return await this.request(`/routeplanner/status`);
|
|
375
576
|
},
|
|
376
577
|
/**
|
|
377
|
-
* Release blacklisted IP address into pool of IPs
|
|
578
|
+
* Release blacklisted IP address into pool of IPs for ip rotation
|
|
378
579
|
* @param address IP address
|
|
580
|
+
* @returns request data of the request
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* ```ts
|
|
584
|
+
* await player.node.routePlannerApi.unmarkFailedAddress("ipv6address");
|
|
585
|
+
* ```
|
|
379
586
|
*/
|
|
380
587
|
unmarkFailedAddress: async (address) => {
|
|
381
588
|
if (!this.sessionId)
|
|
@@ -389,6 +596,12 @@ class LavalinkNode {
|
|
|
389
596
|
},
|
|
390
597
|
/**
|
|
391
598
|
* Release all blacklisted IP addresses into pool of IPs
|
|
599
|
+
* @returns request data of the request
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* await player.node.routePlannerApi.unmarkAllFailedAddresses();
|
|
604
|
+
* ```
|
|
392
605
|
*/
|
|
393
606
|
unmarkAllFailedAddresses: async () => {
|
|
394
607
|
if (!this.sessionId)
|
|
@@ -400,7 +613,7 @@ class LavalinkNode {
|
|
|
400
613
|
});
|
|
401
614
|
}
|
|
402
615
|
};
|
|
403
|
-
/**
|
|
616
|
+
/** @private Utils for validating the */
|
|
404
617
|
validate() {
|
|
405
618
|
if (!this.options.authorization)
|
|
406
619
|
throw new SyntaxError("LavalinkNode requires 'authorization'");
|
|
@@ -409,6 +622,12 @@ class LavalinkNode {
|
|
|
409
622
|
if (!this.options.port)
|
|
410
623
|
throw new SyntaxError("LavalinkNode requires 'port'");
|
|
411
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Sync the data of the player you make an action to lavalink to
|
|
627
|
+
* @param data data to use to update the player
|
|
628
|
+
* @param res result data from lavalink, to override, if available
|
|
629
|
+
* @returns boolean
|
|
630
|
+
*/
|
|
412
631
|
syncPlayerData(data, res) {
|
|
413
632
|
if (typeof data === "object" && typeof data?.guildId === "string" && typeof data.playerOptions === "object" && Object.keys(data.playerOptions).length > 1) {
|
|
414
633
|
const player = this.NodeManager.LavalinkManager.getPlayer(data.guildId);
|
|
@@ -419,8 +638,9 @@ class LavalinkNode {
|
|
|
419
638
|
player.playing = !data.playerOptions.paused;
|
|
420
639
|
}
|
|
421
640
|
if (typeof data.playerOptions.position === "number") {
|
|
422
|
-
player.position = data.playerOptions.position;
|
|
641
|
+
// player.position = data.playerOptions.position;
|
|
423
642
|
player.lastPosition = data.playerOptions.position;
|
|
643
|
+
player.lastPositionChange = Date.now();
|
|
424
644
|
}
|
|
425
645
|
if (typeof data.playerOptions.voice !== "undefined")
|
|
426
646
|
player.voice = data.playerOptions.voice;
|
|
@@ -471,9 +691,22 @@ class LavalinkNode {
|
|
|
471
691
|
}
|
|
472
692
|
return true;
|
|
473
693
|
}
|
|
694
|
+
/**
|
|
695
|
+
* Get the rest Adress for making requests
|
|
696
|
+
*/
|
|
474
697
|
get restAddress() {
|
|
475
698
|
return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
|
|
476
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Reconnect to the lavalink node
|
|
702
|
+
* @param instaReconnect @default false wether to instantly try to reconnect
|
|
703
|
+
* @returns void
|
|
704
|
+
*
|
|
705
|
+
* @example
|
|
706
|
+
* ```ts
|
|
707
|
+
* await player.node.reconnect();
|
|
708
|
+
* ```
|
|
709
|
+
*/
|
|
477
710
|
reconnect(instaReconnect = false) {
|
|
478
711
|
if (instaReconnect) {
|
|
479
712
|
if (this.reconnectAttempts >= this.options.retryAmount) {
|
|
@@ -501,6 +734,7 @@ class LavalinkNode {
|
|
|
501
734
|
this.reconnectAttempts++;
|
|
502
735
|
}, this.options.retryDelay || 1000);
|
|
503
736
|
}
|
|
737
|
+
/** @private util function for handling opening events from websocket */
|
|
504
738
|
async open() {
|
|
505
739
|
if (this.reconnectTimeout)
|
|
506
740
|
clearTimeout(this.reconnectTimeout);
|
|
@@ -513,16 +747,19 @@ class LavalinkNode {
|
|
|
513
747
|
}
|
|
514
748
|
this.NodeManager.emit("connect", this);
|
|
515
749
|
}
|
|
750
|
+
/** @private util function for handling closing events from websocket */
|
|
516
751
|
close(code, reason) {
|
|
517
752
|
this.NodeManager.emit("disconnect", this, { code, reason });
|
|
518
753
|
if (code !== 1000 || reason !== "Node-Destroy")
|
|
519
754
|
this.reconnect();
|
|
520
755
|
}
|
|
756
|
+
/** @private util function for handling error events from websocket */
|
|
521
757
|
error(error) {
|
|
522
758
|
if (!error)
|
|
523
759
|
return;
|
|
524
760
|
this.NodeManager.emit("error", this, error);
|
|
525
761
|
}
|
|
762
|
+
/** @private util function for handling message events from websocket */
|
|
526
763
|
async message(d) {
|
|
527
764
|
if (Array.isArray(d))
|
|
528
765
|
d = Buffer.concat(d);
|
|
@@ -543,42 +780,15 @@ class LavalinkNode {
|
|
|
543
780
|
if (!player)
|
|
544
781
|
return;
|
|
545
782
|
const oldPlayer = player?.toJSON();
|
|
546
|
-
|
|
547
|
-
clearInterval(player.get("internal_updateInterval"));
|
|
548
|
-
// override the position
|
|
549
|
-
player.position = payload.state.position || 0;
|
|
783
|
+
player.lastPositionChange = Date.now();
|
|
550
784
|
player.lastPosition = payload.state.position || 0;
|
|
551
785
|
player.connected = payload.state.connected;
|
|
552
786
|
player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
|
|
553
787
|
if (!player.createdTimeStamp && payload.state.time)
|
|
554
788
|
player.createdTimeStamp = payload.state.time;
|
|
555
|
-
if (
|
|
556
|
-
player.
|
|
557
|
-
|
|
558
|
-
if (player.filterManager.filterUpdatedState >= 1) {
|
|
559
|
-
player.filterManager.filterUpdatedState++;
|
|
560
|
-
const maxMins = 8;
|
|
561
|
-
const currentDuration = player.queue.current?.info?.duration || 0;
|
|
562
|
-
if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri)) {
|
|
563
|
-
if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
|
|
564
|
-
player.filterManager.filterUpdatedState = 0;
|
|
565
|
-
player.seek(player.position);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
else {
|
|
569
|
-
player.filterManager.filterUpdatedState = 0;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
|
|
576
|
-
const maxMins = 8;
|
|
577
|
-
const currentDuration = player.queue.current?.info?.duration || 0;
|
|
578
|
-
if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))
|
|
579
|
-
player.seek(player.position);
|
|
580
|
-
player.filterManager.filterUpdatedState = 0;
|
|
581
|
-
}
|
|
789
|
+
if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600000) || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))) {
|
|
790
|
+
player.filterManager.filterUpdatedState = false;
|
|
791
|
+
await player.seek(player.position);
|
|
582
792
|
}
|
|
583
793
|
this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
|
|
584
794
|
}
|
|
@@ -598,7 +808,7 @@ class LavalinkNode {
|
|
|
598
808
|
return;
|
|
599
809
|
}
|
|
600
810
|
}
|
|
601
|
-
|
|
811
|
+
/** @private middleware util function for handling all kind of events from websocket */
|
|
602
812
|
async handleEvent(payload) {
|
|
603
813
|
if (!payload.guildId)
|
|
604
814
|
return;
|
|
@@ -625,7 +835,7 @@ class LavalinkNode {
|
|
|
625
835
|
this.SponsorBlockSegmentLoaded(player, player.queue.current, payload);
|
|
626
836
|
break;
|
|
627
837
|
case "SegmentSkipped":
|
|
628
|
-
this.
|
|
838
|
+
this.SponsorBlockSegmentSkipped(player, player.queue.current, payload);
|
|
629
839
|
break;
|
|
630
840
|
case "ChaptersLoaded":
|
|
631
841
|
this.SponsorBlockChaptersLoaded(player, player.queue.current, payload);
|
|
@@ -639,7 +849,7 @@ class LavalinkNode {
|
|
|
639
849
|
}
|
|
640
850
|
return;
|
|
641
851
|
}
|
|
642
|
-
|
|
852
|
+
/** @private util function for handling trackStart event */
|
|
643
853
|
trackStart(player, track, payload) {
|
|
644
854
|
player.playing = true;
|
|
645
855
|
player.paused = false;
|
|
@@ -648,6 +858,7 @@ class LavalinkNode {
|
|
|
648
858
|
return;
|
|
649
859
|
return this.NodeManager.LavalinkManager.emit("trackStart", player, track, payload);
|
|
650
860
|
}
|
|
861
|
+
/** @private util function for handling trackEnd event */
|
|
651
862
|
async trackEnd(player, track, payload) {
|
|
652
863
|
// If there are no songs in the queue
|
|
653
864
|
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
@@ -684,6 +895,7 @@ class LavalinkNode {
|
|
|
684
895
|
// play track if autoSkip is true
|
|
685
896
|
return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
|
|
686
897
|
}
|
|
898
|
+
/** @private util function for handling trackStuck event */
|
|
687
899
|
async trackStuck(player, track, payload) {
|
|
688
900
|
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
689
901
|
// If there are no songs in the queue
|
|
@@ -697,6 +909,7 @@ class LavalinkNode {
|
|
|
697
909
|
// play track if autoSkip is true
|
|
698
910
|
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
699
911
|
}
|
|
912
|
+
/** @private util function for handling trackError event */
|
|
700
913
|
async trackError(player, track, payload) {
|
|
701
914
|
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
702
915
|
return; // get's handled by trackEnd
|
|
@@ -711,23 +924,37 @@ class LavalinkNode {
|
|
|
711
924
|
// play track if autoSkip is true
|
|
712
925
|
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
713
926
|
}
|
|
927
|
+
/** @private util function for handling socketClosed event */
|
|
714
928
|
socketClosed(player, payload) {
|
|
715
929
|
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
716
930
|
}
|
|
717
|
-
|
|
931
|
+
/** @private util function for handling SponsorBlock Segmentloaded event */
|
|
718
932
|
SponsorBlockSegmentLoaded(player, track, payload) {
|
|
719
933
|
return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
|
|
720
934
|
}
|
|
721
|
-
|
|
935
|
+
/** @private util function for handling SponsorBlock SegmentSkipped event */
|
|
936
|
+
SponsorBlockSegmentSkipped(player, track, payload) {
|
|
722
937
|
return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
|
|
723
938
|
}
|
|
939
|
+
/** @private util function for handling SponsorBlock Chaptersloaded event */
|
|
724
940
|
SponsorBlockChaptersLoaded(player, track, payload) {
|
|
725
941
|
return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
|
|
726
942
|
}
|
|
943
|
+
/** @private util function for handling SponsorBlock Chaptersstarted event */
|
|
727
944
|
SponsorBlockChapterStarted(player, track, payload) {
|
|
728
945
|
return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
|
|
729
946
|
}
|
|
730
|
-
|
|
947
|
+
/**
|
|
948
|
+
* Get the current sponsorblocks for the sponsorblock plugin
|
|
949
|
+
* @param player passthrough the player
|
|
950
|
+
* @returns sponsorblock seggment from lavalink
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```ts
|
|
954
|
+
* // use it on the player via player.getSponsorBlock();
|
|
955
|
+
* const sponsorBlockSegments = await player.node.getSponsorBlock(player);
|
|
956
|
+
* ```
|
|
957
|
+
*/
|
|
731
958
|
async getSponsorBlock(player) {
|
|
732
959
|
// no plugin enabled
|
|
733
960
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
@@ -735,6 +962,17 @@ class LavalinkNode {
|
|
|
735
962
|
// do the request
|
|
736
963
|
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`);
|
|
737
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* Set the current sponsorblocks for the sponsorblock plugin
|
|
967
|
+
* @param player passthrough the player
|
|
968
|
+
* @returns void
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* ```ts
|
|
972
|
+
* // use it on the player via player.setSponsorBlock();
|
|
973
|
+
* const sponsorBlockSegments = await player.node.setSponsorBlock(player, ["sponsor", "selfpromo"]);
|
|
974
|
+
* ```
|
|
975
|
+
*/
|
|
738
976
|
async setSponsorBlock(player, segments = ["sponsor", "selfpromo"]) {
|
|
739
977
|
// no plugin enabled
|
|
740
978
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
@@ -753,6 +991,17 @@ class LavalinkNode {
|
|
|
753
991
|
});
|
|
754
992
|
return;
|
|
755
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* Delete the sponsorblock plugins
|
|
996
|
+
* @param player passthrough the player
|
|
997
|
+
* @returns void
|
|
998
|
+
*
|
|
999
|
+
* @example
|
|
1000
|
+
* ```ts
|
|
1001
|
+
* // use it on the player via player.deleteSponsorBlock();
|
|
1002
|
+
* const sponsorBlockSegments = await player.node.deleteSponsorBlock(player);
|
|
1003
|
+
* ```
|
|
1004
|
+
*/
|
|
756
1005
|
async deleteSponsorBlock(player) {
|
|
757
1006
|
// no plugin enabled
|
|
758
1007
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
@@ -763,7 +1012,7 @@ class LavalinkNode {
|
|
|
763
1012
|
});
|
|
764
1013
|
return;
|
|
765
1014
|
}
|
|
766
|
-
|
|
1015
|
+
/** private util function for handling the queue end event */
|
|
767
1016
|
async queueEnd(player, track, payload) {
|
|
768
1017
|
// add previous track to the queue!
|
|
769
1018
|
player.queue.current = null;
|