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