lavalink-client 2.8.0 → 2.9.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 +726 -518
- package/dist/index.d.mts +2427 -1893
- package/dist/index.d.ts +2427 -1893
- package/dist/index.js +1776 -566
- package/dist/index.mjs +1773 -566
- package/package.json +79 -81
package/dist/index.mjs
CHANGED
|
@@ -71,7 +71,16 @@ var DisconnectReasons = /* @__PURE__ */ ((DisconnectReasons2) => {
|
|
|
71
71
|
DisconnectReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
|
|
72
72
|
return DisconnectReasons2;
|
|
73
73
|
})(DisconnectReasons || {});
|
|
74
|
-
var validSponsorBlocks = [
|
|
74
|
+
var validSponsorBlocks = [
|
|
75
|
+
"sponsor",
|
|
76
|
+
"selfpromo",
|
|
77
|
+
"interaction",
|
|
78
|
+
"intro",
|
|
79
|
+
"outro",
|
|
80
|
+
"preview",
|
|
81
|
+
"music_offtopic",
|
|
82
|
+
"filler"
|
|
83
|
+
];
|
|
75
84
|
var audioOutputsData = {
|
|
76
85
|
mono: {
|
|
77
86
|
// totalLeft: 1, totalRight: 1
|
|
@@ -302,6 +311,33 @@ var EQList = {
|
|
|
302
311
|
{ band: 14, gain: -0.35 }
|
|
303
312
|
]
|
|
304
313
|
};
|
|
314
|
+
var RecommendationsStrings = {
|
|
315
|
+
highCPULoad: (cpuLoad) => `High CPU load (${(cpuLoad * 100).toFixed(1)}%). Consider reducing player count or upgrading CPU.`,
|
|
316
|
+
highSystemLoad: (systemLoad) => `High system load (${(systemLoad * 100).toFixed(1)}%). Check other processes on the server.`,
|
|
317
|
+
highMemoryUsage: (memoryUsagePercent) => `High memory usage (${memoryUsagePercent.toFixed(1)}%). Consider increasing allocated memory or reducing player count.`,
|
|
318
|
+
frameDeficit: (frameDeficit) => `Frame deficit detected (${frameDeficit}). Audio quality may be affected. Check network and CPU.`,
|
|
319
|
+
highLatency: (ping) => `High latency (${ping}ms). Check network connection to the node.`,
|
|
320
|
+
nodeRestart: "Node restart recommended to clear memory and reset connections.",
|
|
321
|
+
highPlayercount: (players) => `High player count (${players}). Consider load balancing across multiple nodes.`,
|
|
322
|
+
nodeOffline: "Node is offline or disconnected",
|
|
323
|
+
checkConnectivity: "Check node connectivity and restart if needed"
|
|
324
|
+
};
|
|
325
|
+
var NodeLinkExclusiveEvents = [
|
|
326
|
+
"PlayerCreatedEvent",
|
|
327
|
+
"PlayerDestroyedEvent",
|
|
328
|
+
"PlayerConnectedEvent",
|
|
329
|
+
"PlayerReconnectingEvent",
|
|
330
|
+
"VolumeChangedEvent",
|
|
331
|
+
"FiltersChangedEvent",
|
|
332
|
+
"SeekEvent",
|
|
333
|
+
"PauseEvent",
|
|
334
|
+
"ConnectionStatusEvent",
|
|
335
|
+
"MixStartedEvent",
|
|
336
|
+
"MixEndedEvent",
|
|
337
|
+
"LyricsFoundEvent",
|
|
338
|
+
"LyricsLineEvent",
|
|
339
|
+
"LyricsNotFoundEvent"
|
|
340
|
+
];
|
|
305
341
|
|
|
306
342
|
// src/structures/NodeManager.ts
|
|
307
343
|
import { EventEmitter } from "events";
|
|
@@ -327,99 +363,99 @@ import { isRegExp } from "util/types";
|
|
|
327
363
|
var DefaultSources = {
|
|
328
364
|
// youtubemusic
|
|
329
365
|
"youtube music": "ytmsearch",
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
366
|
+
youtubemusic: "ytmsearch",
|
|
367
|
+
ytmsearch: "ytmsearch",
|
|
368
|
+
ytm: "ytmsearch",
|
|
369
|
+
musicyoutube: "ytmsearch",
|
|
334
370
|
"music youtube": "ytmsearch",
|
|
335
371
|
// youtube
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
372
|
+
youtube: "ytsearch",
|
|
373
|
+
yt: "ytsearch",
|
|
374
|
+
ytsearch: "ytsearch",
|
|
339
375
|
// soundcloud
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
376
|
+
soundcloud: "scsearch",
|
|
377
|
+
scsearch: "scsearch",
|
|
378
|
+
sc: "scsearch",
|
|
343
379
|
// apple music
|
|
344
380
|
"apple music": "amsearch",
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
381
|
+
apple: "amsearch",
|
|
382
|
+
applemusic: "amsearch",
|
|
383
|
+
amsearch: "amsearch",
|
|
384
|
+
am: "amsearch",
|
|
385
|
+
musicapple: "amsearch",
|
|
350
386
|
"music apple": "amsearch",
|
|
351
387
|
// spotify
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
388
|
+
spotify: "spsearch",
|
|
389
|
+
spsearch: "spsearch",
|
|
390
|
+
sp: "spsearch",
|
|
355
391
|
"spotify.com": "spsearch",
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
392
|
+
spotifycom: "spsearch",
|
|
393
|
+
sprec: "sprec",
|
|
394
|
+
spsuggestion: "sprec",
|
|
359
395
|
// deezer
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
396
|
+
deezer: "dzsearch",
|
|
397
|
+
dz: "dzsearch",
|
|
398
|
+
dzsearch: "dzsearch",
|
|
399
|
+
dzisrc: "dzisrc",
|
|
400
|
+
dzrec: "dzrec",
|
|
365
401
|
// yandexmusic
|
|
366
402
|
"yandex music": "ymsearch",
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
403
|
+
yandexmusic: "ymsearch",
|
|
404
|
+
yandex: "ymsearch",
|
|
405
|
+
ymsearch: "ymsearch",
|
|
406
|
+
ymrec: "ymrec",
|
|
371
407
|
// VK Music (lavasrc)
|
|
372
|
-
|
|
373
|
-
|
|
408
|
+
vksearch: "vksearch",
|
|
409
|
+
vkmusic: "vksearch",
|
|
374
410
|
"vk music": "vksearch",
|
|
375
|
-
|
|
376
|
-
|
|
411
|
+
vkrec: "vkrec",
|
|
412
|
+
vk: "vksearch",
|
|
377
413
|
// Qobuz (lavasrc)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
414
|
+
qbsearch: "qbsearch",
|
|
415
|
+
qobuz: "qbsearch",
|
|
416
|
+
qbisrc: "qbisrc",
|
|
417
|
+
qbrec: "qbrec",
|
|
382
418
|
// pandora (lavasrc)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
419
|
+
pandora: "pdsearch",
|
|
420
|
+
pd: "pdsearch",
|
|
421
|
+
pdsearch: "pdsearch",
|
|
422
|
+
pdisrc: "pdisrc",
|
|
423
|
+
pdrec: "pdrec",
|
|
388
424
|
"pandora music": "pdsearch",
|
|
389
|
-
|
|
425
|
+
pandoramusic: "pdsearch",
|
|
390
426
|
// speak PLUGIN
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
427
|
+
speak: "speak",
|
|
428
|
+
tts: "tts",
|
|
429
|
+
ftts: "ftts",
|
|
430
|
+
flowery: "ftts",
|
|
395
431
|
"flowery.tts": "ftts",
|
|
396
|
-
|
|
432
|
+
flowerytts: "ftts",
|
|
397
433
|
// Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
434
|
+
bandcamp: "bcsearch",
|
|
435
|
+
bc: "bcsearch",
|
|
436
|
+
bcsearch: "bcsearch",
|
|
401
437
|
// other searches:
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
438
|
+
phsearch: "phsearch",
|
|
439
|
+
pornhub: "phsearch",
|
|
440
|
+
porn: "phsearch",
|
|
405
441
|
// local files
|
|
406
|
-
|
|
442
|
+
local: "local",
|
|
407
443
|
// http requests
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
444
|
+
http: "http",
|
|
445
|
+
https: "https",
|
|
446
|
+
link: "link",
|
|
447
|
+
uri: "uri",
|
|
412
448
|
// tidal
|
|
413
|
-
|
|
414
|
-
|
|
449
|
+
tidal: "tdsearch",
|
|
450
|
+
td: "tdsearch",
|
|
415
451
|
"tidal music": "tdsearch",
|
|
416
|
-
|
|
417
|
-
|
|
452
|
+
tdsearch: "tdsearch",
|
|
453
|
+
tdrec: "tdrec",
|
|
418
454
|
// jiosaavn
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
455
|
+
jiosaavn: "jssearch",
|
|
456
|
+
js: "jssearch",
|
|
457
|
+
jssearch: "jssearch",
|
|
458
|
+
jsrec: "jsrec"
|
|
423
459
|
};
|
|
424
460
|
var LavalinkPlugins = {
|
|
425
461
|
DuncteBot_Plugin: "DuncteBot-plugin",
|
|
@@ -470,11 +506,11 @@ var SourceLinksRegexes = {
|
|
|
470
506
|
/** From jiosaavn-plugin */
|
|
471
507
|
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
|
|
472
508
|
/** From pandora */
|
|
473
|
-
PandoraTrackRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
474
|
-
PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
475
|
-
PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
509
|
+
PandoraTrackRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>TR[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
510
|
+
PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>AL[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
511
|
+
PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+\/(?<identifier>AR[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
476
512
|
PandoraPlaylistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/playlist\/(?<identifier>PL:[\d:]+)(?:[?#].*)?$/,
|
|
477
|
-
AllPandoraRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/(?:playlist\/(?<playlistId>PL:[\d:]+)|artist\/[\w
|
|
513
|
+
AllPandoraRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/(?:playlist\/(?<playlistId>PL:[\d:]+)|artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>(?:TR|AL|AR)[A-Za-z0-9]+))(?:[?#].*)?$/,
|
|
478
514
|
/** FROM DUNCTE BOT PLUGIN */
|
|
479
515
|
tiktok: /https:\/\/www\.tiktok\.com\//,
|
|
480
516
|
mixcloud: /https:\/\/www\.mixcloud\.com\//,
|
|
@@ -483,13 +519,14 @@ var SourceLinksRegexes = {
|
|
|
483
519
|
};
|
|
484
520
|
|
|
485
521
|
// src/structures/Utils.ts
|
|
486
|
-
var TrackSymbol = Symbol("LC-Track");
|
|
487
|
-
var UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
488
|
-
var QueueSymbol = Symbol("LC-Queue");
|
|
489
|
-
var NodeSymbol = Symbol("LC-Node");
|
|
522
|
+
var TrackSymbol = /* @__PURE__ */ Symbol("LC-Track");
|
|
523
|
+
var UnresolvedTrackSymbol = /* @__PURE__ */ Symbol("LC-Track-Unresolved");
|
|
524
|
+
var QueueSymbol = /* @__PURE__ */ Symbol("LC-Queue");
|
|
525
|
+
var NodeSymbol = /* @__PURE__ */ Symbol("LC-Node");
|
|
490
526
|
var escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
491
527
|
function parseLavalinkConnUrl(connectionUrl) {
|
|
492
|
-
if (!connectionUrl.startsWith("lavalink://"))
|
|
528
|
+
if (!connectionUrl.startsWith("lavalink://"))
|
|
529
|
+
throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'lavalink://'`);
|
|
493
530
|
const parsed = new URL2(connectionUrl);
|
|
494
531
|
return {
|
|
495
532
|
authorization: parsed.password,
|
|
@@ -503,14 +540,27 @@ var ManagerUtils = class {
|
|
|
503
540
|
constructor(LavalinkManager2) {
|
|
504
541
|
this.LavalinkManager = LavalinkManager2;
|
|
505
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Builds a pluginInfo object based on the provided data, extracting relevant information from the data and clientData parameters. This function is used to construct the pluginInfo property for tracks, allowing for consistent handling of plugin-related information across different track sources and formats.
|
|
545
|
+
* @param data
|
|
546
|
+
* @param clientData
|
|
547
|
+
* @returns
|
|
548
|
+
*/
|
|
506
549
|
buildPluginInfo(data, clientData = {}) {
|
|
507
550
|
return {
|
|
508
551
|
clientData,
|
|
509
552
|
...data.pluginInfo || data.plugin
|
|
510
553
|
};
|
|
511
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* Builds a Track object from the provided data and requester information. It validates the presence of required properties in the data, transforms the requester using a custom transformer function if provided, and constructs a Track object with the appropriate properties and plugin information. The function also includes error handling to ensure that the input data is valid and provides debug information if track building fails.
|
|
557
|
+
* @param data
|
|
558
|
+
* @param requester
|
|
559
|
+
* @returns
|
|
560
|
+
*/
|
|
512
561
|
buildTrack(data, requester) {
|
|
513
|
-
if (!data?.encoded || typeof data.encoded !== "string")
|
|
562
|
+
if (!data?.encoded || typeof data.encoded !== "string")
|
|
563
|
+
throw new RangeError("Argument 'data.encoded' must be present.");
|
|
514
564
|
if (!data.info) throw new RangeError("Argument 'data.info' must be present.");
|
|
515
565
|
try {
|
|
516
566
|
let transformedRequester = typeof requester === "object" ? this.getTransformedRequester(requester) : void 0;
|
|
@@ -558,8 +608,7 @@ var ManagerUtils = class {
|
|
|
558
608
|
* @param requester
|
|
559
609
|
*/
|
|
560
610
|
buildUnresolvedTrack(query, requester) {
|
|
561
|
-
if (typeof query === "undefined")
|
|
562
|
-
throw new RangeError('Argument "query" must be present.');
|
|
611
|
+
if (typeof query === "undefined") throw new RangeError('Argument "query" must be present.');
|
|
563
612
|
const unresolvedTrack = {
|
|
564
613
|
encoded: query.encoded || void 0,
|
|
565
614
|
info: query.info ? query.info : query.title ? query : void 0,
|
|
@@ -575,7 +624,10 @@ var ManagerUtils = class {
|
|
|
575
624
|
}
|
|
576
625
|
};
|
|
577
626
|
if (!this.isUnresolvedTrack(unresolvedTrack)) throw SyntaxError("Could not build Unresolved Track");
|
|
578
|
-
Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, {
|
|
627
|
+
Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, {
|
|
628
|
+
configurable: true,
|
|
629
|
+
value: true
|
|
630
|
+
});
|
|
579
631
|
return unresolvedTrack;
|
|
580
632
|
}
|
|
581
633
|
/**
|
|
@@ -587,9 +639,27 @@ var ManagerUtils = class {
|
|
|
587
639
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(data));
|
|
588
640
|
if (!keys.includes("constructor")) return false;
|
|
589
641
|
if (!keys.length) return false;
|
|
590
|
-
if (![
|
|
642
|
+
if (![
|
|
643
|
+
"connect",
|
|
644
|
+
"destroy",
|
|
645
|
+
"destroyPlayer",
|
|
646
|
+
"fetchAllPlayers",
|
|
647
|
+
"fetchInfo",
|
|
648
|
+
"fetchPlayer",
|
|
649
|
+
"fetchStats",
|
|
650
|
+
"fetchVersion",
|
|
651
|
+
"request",
|
|
652
|
+
"updatePlayer",
|
|
653
|
+
"updateSession"
|
|
654
|
+
].every((v) => keys.includes(v)))
|
|
655
|
+
return false;
|
|
591
656
|
return true;
|
|
592
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* Gets the transformed requester based on the LavalinkManager options. If a custom requester transformer function is provided in the player options, it applies that function to the requester; otherwise, it returns the requester as is. The function also includes error handling to catch any exceptions that may occur during the transformation process and emits a debug event if the transformation fails.
|
|
660
|
+
* @param requester
|
|
661
|
+
* @returns
|
|
662
|
+
*/
|
|
593
663
|
getTransformedRequester(requester) {
|
|
594
664
|
try {
|
|
595
665
|
return typeof this.LavalinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.LavalinkManager?.options?.playerOptions?.requesterTransformer(requester) : requester;
|
|
@@ -617,11 +687,16 @@ var ManagerUtils = class {
|
|
|
617
687
|
if ("secure" in data && typeof data.secure !== "boolean" && data.secure !== void 0) return false;
|
|
618
688
|
if ("sessionId" in data && typeof data.sessionId !== "string" && data.sessionId !== void 0) return false;
|
|
619
689
|
if ("id" in data && typeof data.id !== "string" && data.id !== void 0) return false;
|
|
620
|
-
if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0))
|
|
621
|
-
|
|
622
|
-
if ("
|
|
623
|
-
|
|
624
|
-
if ("
|
|
690
|
+
if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0))
|
|
691
|
+
return false;
|
|
692
|
+
if ("poolOptions" in data && typeof data.poolOptions !== "object" && data.poolOptions !== void 0)
|
|
693
|
+
return false;
|
|
694
|
+
if ("retryAmount" in data && (typeof data.retryAmount !== "number" || isNaN(data.retryAmount) || data.retryAmount <= 0 && data.retryAmount !== void 0))
|
|
695
|
+
return false;
|
|
696
|
+
if ("retryDelay" in data && (typeof data.retryDelay !== "number" || isNaN(data.retryDelay) || data.retryDelay <= 0 && data.retryDelay !== void 0))
|
|
697
|
+
return false;
|
|
698
|
+
if ("requestTimeout" in data && (typeof data.requestTimeout !== "number" || isNaN(data.requestTimeout) || data.requestTimeout <= 0 && data.requestTimeout !== void 0))
|
|
699
|
+
return false;
|
|
625
700
|
return true;
|
|
626
701
|
}
|
|
627
702
|
/**
|
|
@@ -673,6 +748,12 @@ var ManagerUtils = class {
|
|
|
673
748
|
isUnresolvedTrackQuery(data) {
|
|
674
749
|
return typeof data === "object" && !("info" in data) && typeof data.title === "string";
|
|
675
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Gets the closest track by resolving the provided UnresolvedTrack using the getClosestTrack function. It includes error handling to catch any exceptions that may occur during the resolution process and emits a debug event if the resolution fails. The function returns a Promise that resolves to a Track object if successful, or undefined if no closest track is found.
|
|
753
|
+
* @param data
|
|
754
|
+
* @param player
|
|
755
|
+
* @returns
|
|
756
|
+
*/
|
|
676
757
|
async getClosestTrack(data, player) {
|
|
677
758
|
try {
|
|
678
759
|
return getClosestTrack(data, player);
|
|
@@ -688,11 +769,20 @@ var ManagerUtils = class {
|
|
|
688
769
|
throw e;
|
|
689
770
|
}
|
|
690
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Validates the query string against various criteria, including checking for empty strings, length limits for specific sources, blacklisted links or words, and ensuring that the Lavalink node has the necessary source managers enabled for the provided query. The function also includes debug event emissions to assist with troubleshooting and understanding the validation process.
|
|
774
|
+
* @param node
|
|
775
|
+
* @param queryString
|
|
776
|
+
* @param sourceString
|
|
777
|
+
* @returns
|
|
778
|
+
*/
|
|
691
779
|
validateQueryString(node, queryString, sourceString) {
|
|
692
780
|
if (!node.info) throw new Error("No Lavalink Node was provided");
|
|
693
|
-
if (
|
|
781
|
+
if (node._checkForSources && !node.info.sourceManagers?.length)
|
|
782
|
+
throw new Error("Lavalink Node, has no sourceManagers enabled");
|
|
694
783
|
if (!queryString.trim().length) throw new Error(`Query string is empty, please provide a valid query string.`);
|
|
695
|
-
if (sourceString === "speak" && queryString.length > 100)
|
|
784
|
+
if (sourceString === "speak" && queryString.length > 100)
|
|
785
|
+
throw new Error(`Query is speak, which is limited to 100 characters.`);
|
|
696
786
|
if (this.LavalinkManager.options?.linksBlacklist?.length > 0) {
|
|
697
787
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
698
788
|
this.LavalinkManager.emit("debug", "ValidatingBlacklistLinks" /* ValidatingBlacklistLinks */, {
|
|
@@ -701,12 +791,15 @@ var ManagerUtils = class {
|
|
|
701
791
|
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
|
|
702
792
|
});
|
|
703
793
|
}
|
|
704
|
-
if (this.LavalinkManager.options?.linksBlacklist.some(
|
|
794
|
+
if (this.LavalinkManager.options?.linksBlacklist.some(
|
|
795
|
+
(v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || isRegExp(v) && v.test(queryString)
|
|
796
|
+
)) {
|
|
705
797
|
throw new Error(`Query string contains a link / word which is blacklisted.`);
|
|
706
798
|
}
|
|
707
799
|
}
|
|
708
800
|
if (!/^https?:\/\//.test(queryString)) return;
|
|
709
|
-
else if (this.LavalinkManager.options?.linksAllowed === false)
|
|
801
|
+
else if (this.LavalinkManager.options?.linksAllowed === false)
|
|
802
|
+
throw new Error("Using links to make a request is not allowed.");
|
|
710
803
|
if (this.LavalinkManager.options?.linksWhitelist?.length > 0) {
|
|
711
804
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
712
805
|
this.LavalinkManager.emit("debug", "ValidatingWhitelistLinks" /* ValidatingWhitelistLinks */, {
|
|
@@ -715,11 +808,13 @@ var ManagerUtils = class {
|
|
|
715
808
|
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
|
|
716
809
|
});
|
|
717
810
|
}
|
|
718
|
-
if (!this.LavalinkManager.options?.linksWhitelist.some(
|
|
811
|
+
if (!this.LavalinkManager.options?.linksWhitelist.some(
|
|
812
|
+
(v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || isRegExp(v) && v.test(queryString)
|
|
813
|
+
)) {
|
|
719
814
|
throw new Error(`Query string contains a link / word which isn't whitelisted.`);
|
|
720
815
|
}
|
|
721
816
|
}
|
|
722
|
-
if (!
|
|
817
|
+
if (!node._checkForSources) return;
|
|
723
818
|
if ((SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
|
|
724
819
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'youtube' enabled");
|
|
725
820
|
}
|
|
@@ -727,7 +822,9 @@ var ManagerUtils = class {
|
|
|
727
822
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'soundcloud' enabled");
|
|
728
823
|
}
|
|
729
824
|
if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
|
|
730
|
-
throw new Error(
|
|
825
|
+
throw new Error(
|
|
826
|
+
"Query / Link Provided for this Source but Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)"
|
|
827
|
+
);
|
|
731
828
|
}
|
|
732
829
|
if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
|
|
733
830
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'twitch' enabled");
|
|
@@ -754,7 +851,9 @@ var ManagerUtils = class {
|
|
|
754
851
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'yandexmusic' enabled");
|
|
755
852
|
}
|
|
756
853
|
if (SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
757
|
-
throw new Error(
|
|
854
|
+
throw new Error(
|
|
855
|
+
"Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled"
|
|
856
|
+
);
|
|
758
857
|
}
|
|
759
858
|
if (SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
|
|
760
859
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
|
|
@@ -764,46 +863,112 @@ var ManagerUtils = class {
|
|
|
764
863
|
}
|
|
765
864
|
return;
|
|
766
865
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
};
|
|
774
|
-
const foundSource = Object.keys(DefaultSources).find((source) => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
866
|
+
/**
|
|
867
|
+
* Finds the source of a query string by checking if it starts with a valid source prefix defined in the DefaultSources object. If a valid source prefix is found, it returns the corresponding SearchPlatform; otherwise, it returns null. This function is useful for determining the intended search platform for a given query string, allowing for more accurate search results when the user specifies a source (e.g., "ytsearch:Never Gonna Give You Up" would indicate that the search should be performed on YouTube).
|
|
868
|
+
* @param queryString
|
|
869
|
+
* @returns
|
|
870
|
+
*/
|
|
871
|
+
findSourceOfQuery(queryString) {
|
|
872
|
+
const foundSource = Object.keys(DefaultSources).find((source) => queryString?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
775
873
|
if (foundSource && !["https", "http"].includes(foundSource) && DefaultSources[foundSource]) {
|
|
776
|
-
|
|
777
|
-
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
|
|
874
|
+
return foundSource;
|
|
778
875
|
}
|
|
779
|
-
return
|
|
876
|
+
return null;
|
|
780
877
|
}
|
|
878
|
+
/**
|
|
879
|
+
* Extracts the source from the query if it starts with a valid source prefix (e.g., "ytsearch:") and updates the searchQuery object accordingly.
|
|
880
|
+
* @param searchQuery
|
|
881
|
+
* @returns The updated searchQuery object with the extracted source and modified query string.
|
|
882
|
+
*/
|
|
883
|
+
extractSourceOfQuery(searchQuery) {
|
|
884
|
+
const foundSource = this.findSourceOfQuery(searchQuery.query);
|
|
885
|
+
if (foundSource) {
|
|
886
|
+
searchQuery.source = DefaultSources[foundSource];
|
|
887
|
+
searchQuery.query = searchQuery.query.slice(`${foundSource}:`.length, searchQuery.query.length);
|
|
888
|
+
}
|
|
889
|
+
return searchQuery;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Converts a string to lowercase if the input is a string, otherwise returns the input as is. This is useful for ensuring that search platform identifiers are case-insensitive while allowing other types of input to pass through unchanged.
|
|
893
|
+
* @param input
|
|
894
|
+
* @returns
|
|
895
|
+
*/
|
|
896
|
+
typedLowerCase(input) {
|
|
897
|
+
if (!input) return input;
|
|
898
|
+
if (typeof input === "string") return input.toLowerCase();
|
|
899
|
+
return input;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Transforms a search query by determining the appropriate search platform based on the query string and the default search platform specified in the LavalinkManager options. It checks if the query string starts with a valid source prefix and extracts it if present. The function returns an object containing the modified query string, any extra URL parameters, and the determined search platform to be used for the search operation.
|
|
903
|
+
* @param query
|
|
904
|
+
* @returns
|
|
905
|
+
*/
|
|
906
|
+
transformQuery(query) {
|
|
907
|
+
const typedDefault = this.typedLowerCase(this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform);
|
|
908
|
+
if (typeof query === "string") {
|
|
909
|
+
const Query = {
|
|
910
|
+
query,
|
|
911
|
+
extraQueryUrlParams: void 0,
|
|
912
|
+
source: typedDefault
|
|
913
|
+
};
|
|
914
|
+
return this.extractSourceOfQuery(Query);
|
|
915
|
+
}
|
|
916
|
+
const providedSource = query?.source?.trim?.()?.toLowerCase?.();
|
|
917
|
+
const validSourceExtracted = DefaultSources[providedSource ?? typedDefault];
|
|
918
|
+
return this.extractSourceOfQuery({
|
|
919
|
+
query: query.query,
|
|
920
|
+
extraQueryUrlParams: query.extraQueryUrlParams,
|
|
921
|
+
source: validSourceExtracted ?? providedSource ?? typedDefault
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Transforms a LavaSearchQuery by determining the appropriate search platform based on the query string and the default search platform specified in the LavalinkManager options. It checks if the query string starts with a valid source prefix and extracts it if present. The function returns an object containing the modified query string, any extra URL parameters, the determined search platform to be used for the search operation, and the types of search (track, playlist, artist, album, text) to be performed.
|
|
926
|
+
* @param query
|
|
927
|
+
* @returns
|
|
928
|
+
*/
|
|
781
929
|
transformLavaSearchQuery(query) {
|
|
782
|
-
const
|
|
930
|
+
const typedDefault = this.typedLowerCase(this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform);
|
|
931
|
+
if (typeof query === "string") {
|
|
932
|
+
const Query2 = {
|
|
933
|
+
query,
|
|
934
|
+
types: [],
|
|
935
|
+
extraQueryUrlParams: void 0,
|
|
936
|
+
source: typedDefault
|
|
937
|
+
};
|
|
938
|
+
return this.extractSourceOfQuery(Query2);
|
|
939
|
+
}
|
|
940
|
+
const providedSource = query?.source?.trim?.()?.toLowerCase?.();
|
|
941
|
+
const validSourceExtracted = DefaultSources[providedSource ?? typedDefault];
|
|
783
942
|
const Query = {
|
|
784
|
-
query:
|
|
785
|
-
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(
|
|
943
|
+
query: query.query,
|
|
944
|
+
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(
|
|
945
|
+
(v) => query.types?.find((x) => x.toLowerCase().startsWith(v))
|
|
946
|
+
) : [
|
|
786
947
|
"track",
|
|
787
948
|
"playlist",
|
|
788
949
|
"artist",
|
|
789
950
|
"album"
|
|
790
951
|
/*"text"*/
|
|
791
952
|
],
|
|
792
|
-
source:
|
|
953
|
+
source: validSourceExtracted ?? providedSource ?? typedDefault
|
|
793
954
|
};
|
|
794
|
-
|
|
795
|
-
if (foundSource && DefaultSources[foundSource]) {
|
|
796
|
-
Query.source = DefaultSources[foundSource];
|
|
797
|
-
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
|
|
798
|
-
}
|
|
799
|
-
return Query;
|
|
955
|
+
return this.extractSourceOfQuery(Query);
|
|
800
956
|
}
|
|
957
|
+
/**
|
|
958
|
+
* Validates the provided source string against the capabilities of the Lavalink node. It checks if the source string is supported by the node's enabled source managers and plugins, throwing errors if any required sources or plugins are missing for the specified search platform. This ensures that search queries are only executed with compatible sources based on the node's configuration.
|
|
959
|
+
* @param node
|
|
960
|
+
* @param sourceString
|
|
961
|
+
* @returns
|
|
962
|
+
*/
|
|
801
963
|
validateSourceString(node, sourceString) {
|
|
802
964
|
if (!sourceString) throw new Error(`No SourceString was provided`);
|
|
803
965
|
const source = DefaultSources[sourceString.toLowerCase().trim()];
|
|
804
|
-
if (!source
|
|
966
|
+
if (!source && !!this.LavalinkManager.options.playerOptions.allowCustomSources)
|
|
967
|
+
throw new Error(
|
|
968
|
+
`Lavalink-Client does not support SearchQuerySource: '${sourceString}'. You can disable this check by setting 'ManagerOptions.PlayerOptions.allowCustomSources' to true`
|
|
969
|
+
);
|
|
805
970
|
if (!node.info) throw new Error("Lavalink Node does not have any info cached yet, not ready yet!");
|
|
806
|
-
if (!
|
|
971
|
+
if (!node._checkForSources) return;
|
|
807
972
|
if (source === "amsearch" && !node.info?.sourceManagers?.includes("applemusic")) {
|
|
808
973
|
throw new Error("Lavalink Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
|
|
809
974
|
}
|
|
@@ -817,15 +982,21 @@ var ManagerUtils = class {
|
|
|
817
982
|
throw new Error("Lavalink Node has not 'http' enabled, which is required to have 'dzisrc' to work");
|
|
818
983
|
}
|
|
819
984
|
if (source === "jsrec" && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
820
|
-
throw new Error(
|
|
985
|
+
throw new Error(
|
|
986
|
+
"Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jsrec' to work"
|
|
987
|
+
);
|
|
821
988
|
}
|
|
822
989
|
if (source === "jssearch" && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
823
|
-
throw new Error(
|
|
990
|
+
throw new Error(
|
|
991
|
+
"Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jssearch' to work"
|
|
992
|
+
);
|
|
824
993
|
}
|
|
825
994
|
if (source === "scsearch" && !node.info?.sourceManagers?.includes("soundcloud")) {
|
|
826
995
|
throw new Error("Lavalink Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
|
|
827
996
|
}
|
|
828
|
-
if (source === "speak" &&
|
|
997
|
+
if (source === "speak" && node._checkForPlugins && !node.info?.plugins?.find(
|
|
998
|
+
(c) => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase())
|
|
999
|
+
)) {
|
|
829
1000
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
830
1001
|
}
|
|
831
1002
|
if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
@@ -834,7 +1005,9 @@ var ManagerUtils = class {
|
|
|
834
1005
|
if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
835
1006
|
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
|
|
836
1007
|
}
|
|
837
|
-
if (source === "tts" &&
|
|
1008
|
+
if (source === "tts" && node._checkForPlugins && !node.info?.plugins?.find(
|
|
1009
|
+
(c) => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase())
|
|
1010
|
+
)) {
|
|
838
1011
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
839
1012
|
}
|
|
840
1013
|
if (source === "ftts" && !(node.info?.sourceManagers?.includes("ftts") || node.info?.sourceManagers?.includes("flowery-tts") || node.info?.sourceManagers?.includes("flowerytts"))) {
|
|
@@ -897,13 +1070,15 @@ var MiniMap = class extends Map {
|
|
|
897
1070
|
async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
898
1071
|
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) {
|
|
899
1072
|
player.queue.previous.unshift(player.queue.current);
|
|
900
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1073
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1074
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
901
1075
|
await player.queue.utils.save();
|
|
902
1076
|
}
|
|
903
1077
|
if (player.repeatMode === "queue" && player.queue.current) player.queue.tracks.push(player.queue.current);
|
|
904
1078
|
const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
|
|
905
1079
|
try {
|
|
906
|
-
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
1080
|
+
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
1081
|
+
await nextSong.resolve(player);
|
|
907
1082
|
player.queue.current = nextSong || null;
|
|
908
1083
|
await player.queue.utils.save();
|
|
909
1084
|
} catch (error) {
|
|
@@ -916,7 +1091,8 @@ async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
|
916
1091
|
});
|
|
917
1092
|
}
|
|
918
1093
|
player.LavalinkManager.emit("trackError", player, player.queue.current, error);
|
|
919
|
-
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
1094
|
+
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
1095
|
+
return queueTrackEnd(player);
|
|
920
1096
|
}
|
|
921
1097
|
return player.queue.current;
|
|
922
1098
|
}
|
|
@@ -928,18 +1104,23 @@ async function applyUnresolvedData(resTrack, data, utils) {
|
|
|
928
1104
|
if (data.info.title?.length) resTrack.info.title = data.info.title;
|
|
929
1105
|
if (data.info.author?.length) resTrack.info.author = data.info.author;
|
|
930
1106
|
} else {
|
|
931
|
-
if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
1107
|
+
if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
1108
|
+
resTrack.info.title = data.info.title;
|
|
932
1109
|
if (resTrack.info.author !== data.info.author) resTrack.info.author = data.info.author;
|
|
933
1110
|
if (resTrack.info.artworkUrl !== data.info.artworkUrl) resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
934
1111
|
}
|
|
935
|
-
for (const key of Object.keys(data.info))
|
|
1112
|
+
for (const key of Object.keys(data.info))
|
|
1113
|
+
if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key])
|
|
1114
|
+
resTrack.info[key] = data.info[key];
|
|
936
1115
|
return resTrack;
|
|
937
1116
|
}
|
|
938
1117
|
async function getClosestTrack(data, player) {
|
|
939
1118
|
if (!player || !player.node) throw new RangeError("No player with a lavalink node was provided");
|
|
940
|
-
if (player.LavalinkManager.utils.isTrack(data))
|
|
1119
|
+
if (player.LavalinkManager.utils.isTrack(data))
|
|
1120
|
+
return player.LavalinkManager.utils.buildTrack(data, data.requester);
|
|
941
1121
|
if (!player.LavalinkManager.utils.isUnresolvedTrack(data)) throw new RangeError("Track is not an unresolved Track");
|
|
942
|
-
if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
|
|
1122
|
+
if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
|
|
1123
|
+
throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
|
|
943
1124
|
if (!data.requester) throw new SyntaxError("The requester is required");
|
|
944
1125
|
if (typeof data.encoded === "string") {
|
|
945
1126
|
const r = await player.node.decode.singleTrack(data.encoded, data.requester);
|
|
@@ -951,44 +1132,60 @@ async function getClosestTrack(data, player) {
|
|
|
951
1132
|
}
|
|
952
1133
|
const query = [data.info?.title, data.info?.author].filter((str) => !!str).join(" by ");
|
|
953
1134
|
const sourceName = data.info?.sourceName;
|
|
954
|
-
return await player.search(
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1135
|
+
return await player.search(
|
|
1136
|
+
{
|
|
1137
|
+
query,
|
|
1138
|
+
source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform
|
|
1139
|
+
},
|
|
1140
|
+
data.requester
|
|
1141
|
+
).then((res) => {
|
|
958
1142
|
let trackToUse = null;
|
|
959
|
-
if ((data.info?.title || data.info?.author) && !trackToUse)
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1143
|
+
if ((data.info?.title || data.info?.author) && !trackToUse)
|
|
1144
|
+
trackToUse = res.tracks.find(
|
|
1145
|
+
(track) => (
|
|
1146
|
+
// find via author name (i ... case insensitve)
|
|
1147
|
+
[data.info?.author || "", `${data.info?.author} - Topic`].some(
|
|
1148
|
+
(name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)
|
|
1149
|
+
) || // find via title (i ... case insensitve)
|
|
1150
|
+
new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title)
|
|
1151
|
+
)
|
|
1152
|
+
);
|
|
1153
|
+
if (data.info?.isrc && !trackToUse)
|
|
1154
|
+
trackToUse = res.tracks.find((track) => track.info?.isrc === data.info?.isrc);
|
|
1155
|
+
if (data.info?.duration && !trackToUse)
|
|
1156
|
+
trackToUse = res.tracks.find(
|
|
1157
|
+
(track) => track.info?.duration >= data.info?.duration - 1500 && track?.info.duration <= data.info?.duration + 1500
|
|
1158
|
+
);
|
|
968
1159
|
return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
|
|
969
1160
|
});
|
|
970
1161
|
}
|
|
971
1162
|
function safeStringify(obj, padding = 0) {
|
|
972
1163
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
973
|
-
return JSON.stringify(
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1164
|
+
return JSON.stringify(
|
|
1165
|
+
obj,
|
|
1166
|
+
(key, value) => {
|
|
1167
|
+
if (typeof value === "function") return void 0;
|
|
1168
|
+
if (typeof value === "symbol") return void 0;
|
|
1169
|
+
if (typeof value === "bigint") return value.toString();
|
|
1170
|
+
if (typeof value === "object" && value !== null) {
|
|
1171
|
+
if (seen.has(value)) return "[Circular]";
|
|
1172
|
+
seen.add(value);
|
|
1173
|
+
}
|
|
1174
|
+
return value;
|
|
1175
|
+
},
|
|
1176
|
+
padding
|
|
1177
|
+
);
|
|
983
1178
|
}
|
|
984
1179
|
|
|
985
1180
|
// src/structures/Node.ts
|
|
986
|
-
var LavalinkNode = class {
|
|
1181
|
+
var LavalinkNode = class _LavalinkNode {
|
|
987
1182
|
heartBeatPingTimestamp = 0;
|
|
988
1183
|
heartBeatPongTimestamp = 0;
|
|
989
1184
|
heartBeatInterval;
|
|
990
1185
|
pingTimeout;
|
|
1186
|
+
nodeType = "Lavalink";
|
|
991
1187
|
isAlive = false;
|
|
1188
|
+
static _NodeLinkClass = null;
|
|
992
1189
|
/** The provided Options of the Node */
|
|
993
1190
|
options;
|
|
994
1191
|
/** The amount of rest calls the node has made. */
|
|
@@ -1028,7 +1225,7 @@ var LavalinkNode = class {
|
|
|
1028
1225
|
};
|
|
1029
1226
|
/** The current sessionId, only present when connected */
|
|
1030
1227
|
sessionId = null;
|
|
1031
|
-
/**
|
|
1228
|
+
/** Wether the node resuming is enabled or not */
|
|
1032
1229
|
resuming = { enabled: true, timeout: null };
|
|
1033
1230
|
/** Actual Lavalink Information of the Node */
|
|
1034
1231
|
info = null;
|
|
@@ -1057,16 +1254,17 @@ var LavalinkNode = class {
|
|
|
1057
1254
|
return this.heartBeatPongTimestamp - this.heartBeatPingTimestamp;
|
|
1058
1255
|
}
|
|
1059
1256
|
/**
|
|
1060
|
-
* Returns
|
|
1257
|
+
* Returns wether the plugin validations are enabled or not
|
|
1061
1258
|
*/
|
|
1062
1259
|
get _checkForPlugins() {
|
|
1063
|
-
|
|
1260
|
+
if (this.nodeType === "NodeLink") return false;
|
|
1261
|
+
return !!this.options?.autoChecks?.pluginValidations;
|
|
1064
1262
|
}
|
|
1065
1263
|
/**
|
|
1066
|
-
* Returns
|
|
1264
|
+
* Returns wether the source validations are enabled or not
|
|
1067
1265
|
*/
|
|
1068
1266
|
get _checkForSources() {
|
|
1069
|
-
return !!this.
|
|
1267
|
+
return !!this.options?.autoChecks?.sourcesValidations;
|
|
1070
1268
|
}
|
|
1071
1269
|
/**
|
|
1072
1270
|
* Emits a debug event to the LavalinkManager
|
|
@@ -1127,13 +1325,22 @@ var LavalinkNode = class {
|
|
|
1127
1325
|
retryTimespan: -1,
|
|
1128
1326
|
requestSignalTimeoutMS: 1e4,
|
|
1129
1327
|
heartBeatInterval: 3e4,
|
|
1130
|
-
closeOnError: true,
|
|
1131
1328
|
enablePingOnStatsCheck: true,
|
|
1132
|
-
|
|
1329
|
+
closeOnError: true,
|
|
1330
|
+
...options,
|
|
1331
|
+
autoChecks: {
|
|
1332
|
+
sourcesValidations: options?.autoChecks?.sourcesValidations ?? true,
|
|
1333
|
+
pluginValidations: options?.autoChecks?.pluginValidations ?? true
|
|
1334
|
+
}
|
|
1133
1335
|
};
|
|
1336
|
+
if (this.options.nodeType === "NodeLink" && this.constructor.name === "LavalinkNode" && _LavalinkNode._NodeLinkClass) {
|
|
1337
|
+
return new _LavalinkNode._NodeLinkClass(options, manager);
|
|
1338
|
+
}
|
|
1339
|
+
this.nodeType = this.options.nodeType || "Lavalink";
|
|
1134
1340
|
this.NodeManager = manager;
|
|
1135
1341
|
this.validate();
|
|
1136
|
-
if (this.options.secure && this.options.port !== 443)
|
|
1342
|
+
if (this.options.secure && this.options.port !== 443)
|
|
1343
|
+
throw new SyntaxError("If secure is true, then the port must be 443");
|
|
1137
1344
|
this.options.regions = (this.options.regions || []).map((a) => a.toLowerCase());
|
|
1138
1345
|
Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
|
|
1139
1346
|
}
|
|
@@ -1154,7 +1361,7 @@ var LavalinkNode = class {
|
|
|
1154
1361
|
path: `/${this.version}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`,
|
|
1155
1362
|
method: "GET",
|
|
1156
1363
|
headers: {
|
|
1157
|
-
|
|
1364
|
+
Authorization: this.options.authorization
|
|
1158
1365
|
},
|
|
1159
1366
|
signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : void 0
|
|
1160
1367
|
};
|
|
@@ -1173,18 +1380,22 @@ var LavalinkNode = class {
|
|
|
1173
1380
|
return { response, options };
|
|
1174
1381
|
}
|
|
1175
1382
|
async request(endpoint, modify, parseAsText) {
|
|
1176
|
-
if (!this.connected)
|
|
1383
|
+
if (!this.connected)
|
|
1384
|
+
throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
|
|
1177
1385
|
const { response, options } = await this.rawRequest(endpoint, modify);
|
|
1178
1386
|
if (["DELETE", "PUT"].includes(options.method)) return;
|
|
1179
1387
|
if (response.status === 204) return;
|
|
1180
|
-
if (response.status === 404)
|
|
1388
|
+
if (response.status === 404)
|
|
1389
|
+
throw new Error(
|
|
1390
|
+
`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`
|
|
1391
|
+
);
|
|
1181
1392
|
return parseAsText ? await response.text() : await response.json();
|
|
1182
1393
|
}
|
|
1183
1394
|
/**
|
|
1184
1395
|
* Search something raw on the node, please note only add tracks to players of that node
|
|
1185
1396
|
* @param query SearchQuery Object
|
|
1186
1397
|
* @param requestUser Request User for creating the player(s)
|
|
1187
|
-
* @param throwOnEmpty
|
|
1398
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
1188
1399
|
* @returns Searchresult
|
|
1189
1400
|
*
|
|
1190
1401
|
* @example
|
|
@@ -1198,7 +1409,7 @@ var LavalinkNode = class {
|
|
|
1198
1409
|
const Query = this._LManager.utils.transformQuery(query);
|
|
1199
1410
|
this._LManager.utils.validateQueryString(this, Query.query, Query.source);
|
|
1200
1411
|
if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
|
|
1201
|
-
if (["bcsearch", "bandcamp"].includes(Query.source) && this.
|
|
1412
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && this._checkForSources && !this.info.sourceManagers.includes("bandcamp")) {
|
|
1202
1413
|
throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
|
|
1203
1414
|
}
|
|
1204
1415
|
const requestUrl = new URL(`${this.restAddress}/loadtracks`);
|
|
@@ -1234,8 +1445,14 @@ var LavalinkNode = class {
|
|
|
1234
1445
|
author: res.data.info?.author || res.data.pluginInfo?.author || null,
|
|
1235
1446
|
thumbnail: res.data.info?.artworkUrl || res.data.pluginInfo?.artworkUrl || (typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? resTracks[res.data?.info?.selectedTrack]?.info?.artworkUrl || resTracks[res.data?.info?.selectedTrack]?.info?.pluginInfo?.artworkUrl : null) || null,
|
|
1236
1447
|
uri: res.data.info?.url || res.data.info?.uri || res.data.info?.link || res.data.pluginInfo?.url || res.data.pluginInfo?.uri || res.data.pluginInfo?.link || null,
|
|
1237
|
-
selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this._LManager.utils.buildTrack(
|
|
1238
|
-
|
|
1448
|
+
selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this._LManager.utils.buildTrack(
|
|
1449
|
+
resTracks[res.data?.info?.selectedTrack],
|
|
1450
|
+
requestUser
|
|
1451
|
+
) : null,
|
|
1452
|
+
duration: resTracks.length ? resTracks.reduce(
|
|
1453
|
+
(acc, cur) => acc + (cur?.info?.duration || cur?.info?.length || 0),
|
|
1454
|
+
0
|
|
1455
|
+
) : 0
|
|
1239
1456
|
} : null,
|
|
1240
1457
|
tracks: resTracks.length ? resTracks.map((t) => this._LManager.utils.buildTrack(t, requestUser)) : []
|
|
1241
1458
|
};
|
|
@@ -1244,7 +1461,7 @@ var LavalinkNode = class {
|
|
|
1244
1461
|
* Search something using the lavaSearchPlugin (filtered searches by types)
|
|
1245
1462
|
* @param query LavaSearchQuery Object
|
|
1246
1463
|
* @param requestUser Request User for creating the player(s)
|
|
1247
|
-
* @param throwOnEmpty
|
|
1464
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
1248
1465
|
* @returns LavaSearchresult (SearchResult if link is provided)
|
|
1249
1466
|
*
|
|
1250
1467
|
* @example
|
|
@@ -1256,11 +1473,19 @@ var LavalinkNode = class {
|
|
|
1256
1473
|
async lavaSearch(query, requestUser, throwOnEmpty = false) {
|
|
1257
1474
|
const Query = this._LManager.utils.transformLavaSearchQuery(query);
|
|
1258
1475
|
if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
|
|
1259
|
-
if (/^https?:\/\//.test(Query.query))
|
|
1260
|
-
|
|
1261
|
-
if (
|
|
1262
|
-
|
|
1263
|
-
|
|
1476
|
+
if (/^https?:\/\//.test(Query.query))
|
|
1477
|
+
return this.search({ query: Query.query, source: Query.source }, requestUser);
|
|
1478
|
+
if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
|
|
1479
|
+
throw new SyntaxError(
|
|
1480
|
+
`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`
|
|
1481
|
+
);
|
|
1482
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasearch-plugin"))
|
|
1483
|
+
throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.id}`);
|
|
1484
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin"))
|
|
1485
|
+
throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
|
|
1486
|
+
const { response } = await this.rawRequest(
|
|
1487
|
+
`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`
|
|
1488
|
+
);
|
|
1264
1489
|
const res = response.status === 204 ? {} : await response.json();
|
|
1265
1490
|
if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
|
|
1266
1491
|
this._emitDebugEvent("LavaSearchNothingFound" /* LavaSearchNothingFound */, {
|
|
@@ -1272,10 +1497,25 @@ var LavalinkNode = class {
|
|
|
1272
1497
|
}
|
|
1273
1498
|
return {
|
|
1274
1499
|
tracks: res.tracks?.map((v) => this._LManager.utils.buildTrack(v, requestUser)) || [],
|
|
1275
|
-
albums: res.albums?.map((v) => ({
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1500
|
+
albums: res.albums?.map((v) => ({
|
|
1501
|
+
info: v.info,
|
|
1502
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1503
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1504
|
+
})) || [],
|
|
1505
|
+
artists: res.artists?.map((v) => ({
|
|
1506
|
+
info: v.info,
|
|
1507
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1508
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1509
|
+
})) || [],
|
|
1510
|
+
playlists: res.playlists?.map((v) => ({
|
|
1511
|
+
info: v.info,
|
|
1512
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1513
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1514
|
+
})) || [],
|
|
1515
|
+
texts: res.texts?.map((v) => ({
|
|
1516
|
+
text: v.text,
|
|
1517
|
+
pluginInfo: v?.plugin || v.pluginInfo
|
|
1518
|
+
})) || [],
|
|
1279
1519
|
pluginInfo: res.pluginInfo || res?.plugin
|
|
1280
1520
|
};
|
|
1281
1521
|
}
|
|
@@ -1299,7 +1539,10 @@ var LavalinkNode = class {
|
|
|
1299
1539
|
r.body = safeStringify(data.playerOptions);
|
|
1300
1540
|
if (data.noReplace) {
|
|
1301
1541
|
const url = new URL(`${this.restAddress}${r.path}`);
|
|
1302
|
-
url.searchParams.append(
|
|
1542
|
+
url.searchParams.append(
|
|
1543
|
+
"noReplace",
|
|
1544
|
+
data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false"
|
|
1545
|
+
);
|
|
1303
1546
|
r.path = url.pathname + url.search;
|
|
1304
1547
|
}
|
|
1305
1548
|
});
|
|
@@ -1353,19 +1596,26 @@ var LavalinkNode = class {
|
|
|
1353
1596
|
const headers = {
|
|
1354
1597
|
Authorization: this.options.authorization,
|
|
1355
1598
|
"User-Id": this._LManager.options.client.id,
|
|
1356
|
-
"Client-Name": this._LManager.options.client.username || "Lavalink-Client"
|
|
1599
|
+
"Client-Name": String(this._LManager.options.client.username || "Lavalink-Client").replace(
|
|
1600
|
+
/[^\x20-\x7E]/g,
|
|
1601
|
+
""
|
|
1602
|
+
)
|
|
1357
1603
|
};
|
|
1358
1604
|
if (typeof this.options.sessionId === "string" || typeof sessionId === "string") {
|
|
1359
1605
|
headers["Session-Id"] = this.options.sessionId || sessionId;
|
|
1360
1606
|
this.sessionId = this.options.sessionId || sessionId;
|
|
1361
1607
|
}
|
|
1362
|
-
this.socket = new WebSocket(
|
|
1608
|
+
this.socket = new WebSocket(
|
|
1609
|
+
`ws${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}/v4/websocket`,
|
|
1610
|
+
{ headers }
|
|
1611
|
+
);
|
|
1363
1612
|
this.socket.on("open", this.open.bind(this));
|
|
1364
1613
|
this.socket.on("close", (code, reason) => this.close(code, reason?.toString()));
|
|
1365
1614
|
this.socket.on("message", this.message.bind(this));
|
|
1366
1615
|
this.socket.on("error", this.error.bind(this));
|
|
1367
1616
|
}
|
|
1368
1617
|
heartBeat() {
|
|
1618
|
+
if (this.nodeType !== "Lavalink") return;
|
|
1369
1619
|
this._emitDebugEvent("HeartBeatTriggered" /* HeartBeatTriggered */, {
|
|
1370
1620
|
state: "log",
|
|
1371
1621
|
message: `Node Socket Heartbeat triggered, resetting old Timeout to 65000ms (should happen every 60s due to /stats event)`,
|
|
@@ -1405,7 +1655,7 @@ var LavalinkNode = class {
|
|
|
1405
1655
|
/**
|
|
1406
1656
|
* Destroys the Node-Connection (Websocket) and all player's of the node
|
|
1407
1657
|
* @param destroyReason Destroy Reason to use when destroying the players
|
|
1408
|
-
* @param deleteNode
|
|
1658
|
+
* @param deleteNode wether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
|
|
1409
1659
|
* @param movePlayers whether to movePlayers to different eligible connected node. If false players won't be moved @default false
|
|
1410
1660
|
* @returns void
|
|
1411
1661
|
*
|
|
@@ -1427,8 +1677,11 @@ var LavalinkNode = class {
|
|
|
1427
1677
|
this.socket?.removeAllListeners();
|
|
1428
1678
|
this.socket = null;
|
|
1429
1679
|
this.resetReconnectionAttempts();
|
|
1430
|
-
if (!deleteNode)
|
|
1431
|
-
|
|
1680
|
+
if (!deleteNode)
|
|
1681
|
+
return void this.NodeManager.emit("disconnect", this, {
|
|
1682
|
+
code: 1e3,
|
|
1683
|
+
reason: destroyReason
|
|
1684
|
+
});
|
|
1432
1685
|
this.NodeManager.emit("destroy", this, destroyReason);
|
|
1433
1686
|
this.NodeManager.nodes.delete(this.id);
|
|
1434
1687
|
this.resetAckTimeouts(true, true);
|
|
@@ -1436,55 +1689,67 @@ var LavalinkNode = class {
|
|
|
1436
1689
|
}
|
|
1437
1690
|
const handlePlayerOperations = () => {
|
|
1438
1691
|
if (!movePlayers) {
|
|
1439
|
-
return Promise.allSettled(
|
|
1440
|
-
(
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1692
|
+
return Promise.allSettled(
|
|
1693
|
+
Array.from(players.values()).map(
|
|
1694
|
+
(player) => player.destroy(destroyReason || "NodeDestroy" /* NodeDestroy */).catch((error) => {
|
|
1695
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1696
|
+
state: "error",
|
|
1697
|
+
message: `Failed to destroy player ${player.guildId}: ${error.message}`,
|
|
1698
|
+
error,
|
|
1699
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1700
|
+
});
|
|
1701
|
+
})
|
|
1702
|
+
)
|
|
1703
|
+
);
|
|
1449
1704
|
}
|
|
1450
|
-
const nodeToMove = Array.from(this.NodeManager.leastUsedNodes("playingPlayers")).find(
|
|
1705
|
+
const nodeToMove = Array.from(this.NodeManager.leastUsedNodes("playingPlayers")).find(
|
|
1706
|
+
(n) => n.connected && n.options.id !== this.id
|
|
1707
|
+
);
|
|
1451
1708
|
if (!nodeToMove) {
|
|
1452
|
-
return Promise.allSettled(
|
|
1453
|
-
(
|
|
1454
|
-
|
|
1709
|
+
return Promise.allSettled(
|
|
1710
|
+
Array.from(players.values()).map(
|
|
1711
|
+
(player) => player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((error) => {
|
|
1712
|
+
this._emitDebugEvent("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
|
|
1713
|
+
state: "error",
|
|
1714
|
+
message: `Failed to destroy player ${player.guildId}: ${error.message}`,
|
|
1715
|
+
error,
|
|
1716
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1717
|
+
});
|
|
1718
|
+
})
|
|
1719
|
+
)
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
return Promise.allSettled(
|
|
1723
|
+
Array.from(players.values()).map(
|
|
1724
|
+
(player) => player.changeNode(nodeToMove.options.id).catch((error) => {
|
|
1725
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1455
1726
|
state: "error",
|
|
1456
|
-
message: `Failed to
|
|
1727
|
+
message: `Failed to move player ${player.guildId}: ${error.message}`,
|
|
1457
1728
|
error,
|
|
1458
1729
|
functionLayer: "Node > destroy() > movePlayers"
|
|
1459
1730
|
});
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
message: `Failed to move player ${player.guildId}: ${error.message}`,
|
|
1468
|
-
error,
|
|
1469
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1470
|
-
});
|
|
1471
|
-
return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1472
|
-
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1473
|
-
state: "error",
|
|
1474
|
-
message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
|
|
1475
|
-
error: destroyError,
|
|
1476
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1731
|
+
return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1732
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1733
|
+
state: "error",
|
|
1734
|
+
message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
|
|
1735
|
+
error: destroyError,
|
|
1736
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1737
|
+
});
|
|
1477
1738
|
});
|
|
1478
|
-
})
|
|
1479
|
-
|
|
1480
|
-
)
|
|
1739
|
+
})
|
|
1740
|
+
)
|
|
1741
|
+
);
|
|
1481
1742
|
};
|
|
1482
1743
|
return void handlePlayerOperations().finally(() => {
|
|
1483
1744
|
this.socket?.close(1e3, "Node-Destroy");
|
|
1484
1745
|
this.socket?.removeAllListeners();
|
|
1485
1746
|
this.socket = null;
|
|
1486
1747
|
this.resetReconnectionAttempts();
|
|
1487
|
-
if (!deleteNode)
|
|
1748
|
+
if (!deleteNode)
|
|
1749
|
+
return void this.NodeManager.emit("disconnect", this, {
|
|
1750
|
+
code: 1e3,
|
|
1751
|
+
reason: destroyReason
|
|
1752
|
+
});
|
|
1488
1753
|
this.NodeManager.emit("destroy", this, destroyReason);
|
|
1489
1754
|
this.NodeManager.nodes.delete(this.id);
|
|
1490
1755
|
this.resetAckTimeouts(true, true);
|
|
@@ -1585,7 +1850,12 @@ var LavalinkNode = class {
|
|
|
1585
1850
|
*/
|
|
1586
1851
|
singleTrack: async (encoded, requester) => {
|
|
1587
1852
|
if (!encoded) throw new SyntaxError("No encoded (Base64 string) was provided");
|
|
1588
|
-
return this._LManager.utils?.buildTrack(
|
|
1853
|
+
return this._LManager.utils?.buildTrack(
|
|
1854
|
+
await this.request(
|
|
1855
|
+
`/decodetrack?encodedTrack=${encodeURIComponent(encoded.replace(/\s/g, ""))}`
|
|
1856
|
+
),
|
|
1857
|
+
requester
|
|
1858
|
+
);
|
|
1589
1859
|
},
|
|
1590
1860
|
/**
|
|
1591
1861
|
* Decodes multiple tracks into their info
|
|
@@ -1601,7 +1871,8 @@ var LavalinkNode = class {
|
|
|
1601
1871
|
* ```
|
|
1602
1872
|
*/
|
|
1603
1873
|
multipleTracks: async (encodeds, requester) => {
|
|
1604
|
-
if (!Array.isArray(encodeds) || !encodeds.every((v) => typeof v === "string" && v.length > 1))
|
|
1874
|
+
if (!Array.isArray(encodeds) || !encodeds.every((v) => typeof v === "string" && v.length > 1))
|
|
1875
|
+
throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
|
|
1605
1876
|
return await this.request(`/decodetracks`, (r) => {
|
|
1606
1877
|
r.method = "POST";
|
|
1607
1878
|
r.body = safeStringify(encodeds);
|
|
@@ -1613,7 +1884,7 @@ var LavalinkNode = class {
|
|
|
1613
1884
|
/**
|
|
1614
1885
|
* Get the lyrics of a track
|
|
1615
1886
|
* @param track the track to get the lyrics for
|
|
1616
|
-
* @param skipTrackSource
|
|
1887
|
+
* @param skipTrackSource wether to skip the track source or not
|
|
1617
1888
|
* @returns the lyrics of the track
|
|
1618
1889
|
* @example
|
|
1619
1890
|
*
|
|
@@ -1625,8 +1896,14 @@ var LavalinkNode = class {
|
|
|
1625
1896
|
*/
|
|
1626
1897
|
get: async (track, skipTrackSource = false) => {
|
|
1627
1898
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1628
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1629
|
-
|
|
1899
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1900
|
+
throw new RangeError(
|
|
1901
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1902
|
+
);
|
|
1903
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin"))
|
|
1904
|
+
throw new RangeError(
|
|
1905
|
+
`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`
|
|
1906
|
+
);
|
|
1630
1907
|
const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
|
|
1631
1908
|
return await this.request(url);
|
|
1632
1909
|
},
|
|
@@ -1634,7 +1911,7 @@ var LavalinkNode = class {
|
|
|
1634
1911
|
* Get the lyrics of the current playing track
|
|
1635
1912
|
*
|
|
1636
1913
|
* @param guildId the guild id of the player
|
|
1637
|
-
* @param skipTrackSource
|
|
1914
|
+
* @param skipTrackSource wether to skip the track source or not
|
|
1638
1915
|
* @returns the lyrics of the current playing track
|
|
1639
1916
|
* @example
|
|
1640
1917
|
* ```ts
|
|
@@ -1645,8 +1922,14 @@ var LavalinkNode = class {
|
|
|
1645
1922
|
*/
|
|
1646
1923
|
getCurrent: async (guildId, skipTrackSource = false) => {
|
|
1647
1924
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1648
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1649
|
-
|
|
1925
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1926
|
+
throw new RangeError(
|
|
1927
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1928
|
+
);
|
|
1929
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin"))
|
|
1930
|
+
throw new RangeError(
|
|
1931
|
+
`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`
|
|
1932
|
+
);
|
|
1650
1933
|
const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
|
|
1651
1934
|
return await this.request(url);
|
|
1652
1935
|
},
|
|
@@ -1664,7 +1947,10 @@ var LavalinkNode = class {
|
|
|
1664
1947
|
*/
|
|
1665
1948
|
subscribe: async (guildId) => {
|
|
1666
1949
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1667
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1950
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1951
|
+
throw new RangeError(
|
|
1952
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1953
|
+
);
|
|
1668
1954
|
return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
|
|
1669
1955
|
options.method = "POST";
|
|
1670
1956
|
});
|
|
@@ -1683,7 +1969,10 @@ var LavalinkNode = class {
|
|
|
1683
1969
|
*/
|
|
1684
1970
|
unsubscribe: async (guildId) => {
|
|
1685
1971
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1686
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1972
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1973
|
+
throw new RangeError(
|
|
1974
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1975
|
+
);
|
|
1687
1976
|
return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
|
|
1688
1977
|
options.method = "DELETE";
|
|
1689
1978
|
});
|
|
@@ -1711,7 +2000,10 @@ var LavalinkNode = class {
|
|
|
1711
2000
|
* ```
|
|
1712
2001
|
*/
|
|
1713
2002
|
async fetchConnectionMetrics() {
|
|
1714
|
-
if (this.info && !this.info.isNodelink)
|
|
2003
|
+
if (this.info && !this.info.isNodelink)
|
|
2004
|
+
throw new Error(
|
|
2005
|
+
"There is no Information about wether you are using NodeLink instead of Lavalink, so this function won't work"
|
|
2006
|
+
);
|
|
1715
2007
|
return await this.request(`/connection`);
|
|
1716
2008
|
}
|
|
1717
2009
|
/**
|
|
@@ -1724,9 +2016,13 @@ var LavalinkNode = class {
|
|
|
1724
2016
|
* ```
|
|
1725
2017
|
*/
|
|
1726
2018
|
async fetchVersion() {
|
|
1727
|
-
return await this.request(
|
|
1728
|
-
|
|
1729
|
-
|
|
2019
|
+
return await this.request(
|
|
2020
|
+
`/version`,
|
|
2021
|
+
(r) => {
|
|
2022
|
+
r.path = "/version";
|
|
2023
|
+
},
|
|
2024
|
+
true
|
|
2025
|
+
);
|
|
1730
2026
|
}
|
|
1731
2027
|
/**
|
|
1732
2028
|
* Request Lavalink information.
|
|
@@ -1742,6 +2038,160 @@ var LavalinkNode = class {
|
|
|
1742
2038
|
async fetchInfo() {
|
|
1743
2039
|
return await this.request(`/info`);
|
|
1744
2040
|
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Returns the metric summary of the node
|
|
2043
|
+
* @returns the metric summary of the node
|
|
2044
|
+
*/
|
|
2045
|
+
nodeMetricSummary() {
|
|
2046
|
+
if (!this.connected || !this.isAlive)
|
|
2047
|
+
return {
|
|
2048
|
+
systemLoad: 0,
|
|
2049
|
+
cpuLoad: 0,
|
|
2050
|
+
memoryUsage: 0,
|
|
2051
|
+
players: 0,
|
|
2052
|
+
playingPlayers: 0,
|
|
2053
|
+
uptime: 0,
|
|
2054
|
+
ping: 0,
|
|
2055
|
+
frameDeficit: 0
|
|
2056
|
+
};
|
|
2057
|
+
const _memoryUsed = this.stats.memory.used;
|
|
2058
|
+
const _memoryAllocated = this.stats.memory.allocated;
|
|
2059
|
+
return {
|
|
2060
|
+
systemLoad: this.stats.cpu.systemLoad,
|
|
2061
|
+
cpuLoad: this.stats.cpu.lavalinkLoad,
|
|
2062
|
+
memoryUsage: _memoryAllocated > 0 ? _memoryUsed / _memoryAllocated * 100 : 0,
|
|
2063
|
+
players: this.stats.players,
|
|
2064
|
+
playingPlayers: this.stats.playingPlayers,
|
|
2065
|
+
uptime: this.stats.uptime,
|
|
2066
|
+
ping: this.heartBeatPing,
|
|
2067
|
+
frameDeficit: this.stats.frameStats?.deficit || 0
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Get the node's health status with performance assessment.
|
|
2072
|
+
* @returns Object containing health status, performance rating, load balancing info, and recommendations
|
|
2073
|
+
*
|
|
2074
|
+
* @example
|
|
2075
|
+
* ```ts
|
|
2076
|
+
* const health = node.getHealthStatus();
|
|
2077
|
+
* console.log(`Node Status: ${health.status}`); // "healthy" | "degraded" | "critical" | "offline"
|
|
2078
|
+
* console.log(`Performance: ${health.performance}`); // "excellent" | "good" | "fair" | "poor"
|
|
2079
|
+
* console.log(`Penalty Score: ${health.penaltyScore}`); // Lower is better for load balancing
|
|
2080
|
+
* console.log(`Estimated Capacity: ${health.estimatedRemainingCapacity} more players`);
|
|
2081
|
+
* console.log(`Overloaded: ${health.isOverloaded}`);
|
|
2082
|
+
* console.log(`Needs Restart: ${health.needsRestart}`);
|
|
2083
|
+
* if (health.recommendations.length) {
|
|
2084
|
+
* console.log("Recommendations:", health.recommendations);
|
|
2085
|
+
* }
|
|
2086
|
+
* ```
|
|
2087
|
+
*/
|
|
2088
|
+
getHealthStatus(thresholds) {
|
|
2089
|
+
const cpuThresholds = {
|
|
2090
|
+
excellent: 0.3,
|
|
2091
|
+
good: 0.5,
|
|
2092
|
+
fair: 0.7,
|
|
2093
|
+
poor: 0.85,
|
|
2094
|
+
...thresholds?.cpu
|
|
2095
|
+
};
|
|
2096
|
+
const memoryThresholds = {
|
|
2097
|
+
excellent: 60,
|
|
2098
|
+
good: 75,
|
|
2099
|
+
fair: 85,
|
|
2100
|
+
poor: 95,
|
|
2101
|
+
...thresholds?.memory
|
|
2102
|
+
};
|
|
2103
|
+
const pingThresholds = {
|
|
2104
|
+
excellent: 50,
|
|
2105
|
+
good: 100,
|
|
2106
|
+
fair: 200,
|
|
2107
|
+
poor: 300,
|
|
2108
|
+
...thresholds?.ping
|
|
2109
|
+
};
|
|
2110
|
+
const recommendations = [];
|
|
2111
|
+
const metrics = this.nodeMetricSummary();
|
|
2112
|
+
if (!this.connected || !this.isAlive) {
|
|
2113
|
+
return {
|
|
2114
|
+
status: "offline",
|
|
2115
|
+
performance: "poor",
|
|
2116
|
+
isOverloaded: false,
|
|
2117
|
+
needsRestart: true,
|
|
2118
|
+
penaltyScore: 999999,
|
|
2119
|
+
// Maximum penalty for offline nodes
|
|
2120
|
+
estimatedRemainingCapacity: 0,
|
|
2121
|
+
recommendations: [RecommendationsStrings.nodeOffline, RecommendationsStrings.checkConnectivity],
|
|
2122
|
+
metrics
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
let cpuScore = 0;
|
|
2126
|
+
if (metrics.cpuLoad < cpuThresholds.excellent) cpuScore = 4;
|
|
2127
|
+
else if (metrics.cpuLoad < cpuThresholds.good) cpuScore = 3;
|
|
2128
|
+
else if (metrics.cpuLoad < cpuThresholds.fair) cpuScore = 2;
|
|
2129
|
+
else if (metrics.cpuLoad < cpuThresholds.poor) cpuScore = 1;
|
|
2130
|
+
let memoryScore = 0;
|
|
2131
|
+
if (metrics.memoryUsage < memoryThresholds.excellent) memoryScore = 4;
|
|
2132
|
+
else if (metrics.memoryUsage < memoryThresholds.good) memoryScore = 3;
|
|
2133
|
+
else if (metrics.memoryUsage < memoryThresholds.fair) memoryScore = 2;
|
|
2134
|
+
else if (metrics.memoryUsage < memoryThresholds.poor) memoryScore = 1;
|
|
2135
|
+
let pingScore = 0;
|
|
2136
|
+
if (metrics.ping < pingThresholds.excellent) pingScore = 4;
|
|
2137
|
+
else if (metrics.ping < pingThresholds.good) pingScore = 3;
|
|
2138
|
+
else if (metrics.ping < pingThresholds.fair) pingScore = 2;
|
|
2139
|
+
else if (metrics.ping < pingThresholds.poor) pingScore = 1;
|
|
2140
|
+
const avgScore = (cpuScore + memoryScore + pingScore) / 3;
|
|
2141
|
+
let performance2 = "poor";
|
|
2142
|
+
if (avgScore >= 3.5) performance2 = "excellent";
|
|
2143
|
+
else if (avgScore >= 2.5) performance2 = "good";
|
|
2144
|
+
else if (avgScore >= 1.5) performance2 = "fair";
|
|
2145
|
+
const isOverloaded = metrics.cpuLoad > cpuThresholds.fair || metrics.memoryUsage > memoryThresholds.fair || metrics.frameDeficit > 100;
|
|
2146
|
+
const isCritical = metrics.cpuLoad > cpuThresholds.poor || metrics.memoryUsage > memoryThresholds.poor || metrics.frameDeficit > 500;
|
|
2147
|
+
const status = isCritical ? "critical" : isOverloaded ? "degraded" : "healthy";
|
|
2148
|
+
const needsRestart = status === "critical" || isOverloaded && metrics.memoryUsage > 90 || metrics.frameDeficit > 1e3 || this.reconnectionAttemptCount > 0 && this.reconnectionAttemptCount >= this.options.retryAmount / 2;
|
|
2149
|
+
if (metrics.cpuLoad > cpuThresholds.fair)
|
|
2150
|
+
recommendations.push(RecommendationsStrings.highCPULoad(metrics.cpuLoad));
|
|
2151
|
+
if (metrics.systemLoad > 0.8) recommendations.push(RecommendationsStrings.highSystemLoad(metrics.systemLoad));
|
|
2152
|
+
if (metrics.memoryUsage > memoryThresholds.fair)
|
|
2153
|
+
recommendations.push(RecommendationsStrings.highMemoryUsage(metrics.memoryUsage));
|
|
2154
|
+
if (metrics.frameDeficit > 100) recommendations.push(RecommendationsStrings.frameDeficit(metrics.frameDeficit));
|
|
2155
|
+
if (metrics.ping > pingThresholds.fair) recommendations.push(RecommendationsStrings.highLatency(metrics.ping));
|
|
2156
|
+
if (needsRestart) recommendations.push(RecommendationsStrings.nodeRestart);
|
|
2157
|
+
if (metrics.players > 500) recommendations.push(RecommendationsStrings.highPlayercount(metrics.players));
|
|
2158
|
+
const nullFrames = this.stats.frameStats?.nulled || 0;
|
|
2159
|
+
let penaltyScore = metrics.players + // Player count penalty (each player adds base penalty)
|
|
2160
|
+
Math.pow(metrics.cpuLoad * 100, 2) + // CPU penalty (exponential - heavily penalize high CPU)
|
|
2161
|
+
Math.pow(metrics.memoryUsage, 1.5) + // Memory penalty (exponential - heavily penalize high memory)
|
|
2162
|
+
metrics.ping * 2 + // Latency penalty
|
|
2163
|
+
metrics.frameDeficit * 10 + // Frame deficit penalty (critical for audio quality)
|
|
2164
|
+
nullFrames * 5;
|
|
2165
|
+
if (status === "critical") penaltyScore += 1e4;
|
|
2166
|
+
else if (status === "degraded") penaltyScore += 5e3;
|
|
2167
|
+
if (this.reconnectionAttemptCount > 0) penaltyScore += this.reconnectionAttemptCount * 1e3;
|
|
2168
|
+
penaltyScore = Math.round(penaltyScore);
|
|
2169
|
+
let estimatedRemainingCapacity = 0;
|
|
2170
|
+
if (status !== "critical") {
|
|
2171
|
+
const cpuCapacity = metrics.players === 0 ? 200 : metrics.cpuLoad > 0 ? Math.max(
|
|
2172
|
+
0,
|
|
2173
|
+
Math.floor((cpuThresholds.fair - metrics.cpuLoad) / metrics.cpuLoad * metrics.players)
|
|
2174
|
+
) : 200;
|
|
2175
|
+
const memoryCapacity = metrics.players === 0 ? 200 : metrics.memoryUsage > 0 ? Math.max(
|
|
2176
|
+
0,
|
|
2177
|
+
Math.floor(
|
|
2178
|
+
(memoryThresholds.fair - metrics.memoryUsage) / metrics.memoryUsage * metrics.players
|
|
2179
|
+
)
|
|
2180
|
+
) : 200;
|
|
2181
|
+
estimatedRemainingCapacity = Math.min(Math.min(cpuCapacity, memoryCapacity), 500);
|
|
2182
|
+
if (isOverloaded) estimatedRemainingCapacity = 0;
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
status,
|
|
2186
|
+
performance: performance2,
|
|
2187
|
+
isOverloaded,
|
|
2188
|
+
needsRestart,
|
|
2189
|
+
penaltyScore,
|
|
2190
|
+
estimatedRemainingCapacity,
|
|
2191
|
+
recommendations,
|
|
2192
|
+
metrics
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
1745
2195
|
/**
|
|
1746
2196
|
* Lavalink's Route Planner Api
|
|
1747
2197
|
*/
|
|
@@ -1801,6 +2251,44 @@ var LavalinkNode = class {
|
|
|
1801
2251
|
if (!this.options.authorization) throw new SyntaxError("LavalinkNode requires 'authorization'");
|
|
1802
2252
|
if (!this.options.host) throw new SyntaxError("LavalinkNode requires 'host'");
|
|
1803
2253
|
if (!this.options.port) throw new SyntaxError("LavalinkNode requires 'port'");
|
|
2254
|
+
if (typeof this.options.port !== "number" || this.options.port < 1 || this.options.port > 65535)
|
|
2255
|
+
throw new SyntaxError("LavalinkNode.port must be a number within 1 and 65535");
|
|
2256
|
+
if (this.options.closeOnError !== void 0 && typeof this.options.closeOnError !== "boolean")
|
|
2257
|
+
throw new SyntaxError("LavalinkNode.closeOnError must be either false | true aka boolean");
|
|
2258
|
+
if (this.options.retryDelay !== void 0 && typeof this.options.retryDelay !== "number")
|
|
2259
|
+
throw new SyntaxError("LavalinkNodeOptions.retryDelay must be a number");
|
|
2260
|
+
if (this.options.retryAmount !== void 0 && typeof this.options.retryAmount !== "number")
|
|
2261
|
+
throw new SyntaxError("LavalinkNodeOptions.retryAmount must be a number");
|
|
2262
|
+
if (this.options.retryTimespan !== void 0 && typeof this.options.retryTimespan !== "number")
|
|
2263
|
+
throw new SyntaxError("LavalinkNodeOptions.retryTimespan must be a number");
|
|
2264
|
+
if (this.options.requestSignalTimeoutMS !== void 0 && typeof this.options.requestSignalTimeoutMS !== "number")
|
|
2265
|
+
throw new SyntaxError("LavalinkNodeOptions.requestSignalTimeoutMS must be a number");
|
|
2266
|
+
if (this.options.heartBeatInterval !== void 0 && typeof this.options.heartBeatInterval !== "number")
|
|
2267
|
+
throw new SyntaxError("LavalinkNodeOptions.heartBeatInterval must be a number");
|
|
2268
|
+
if (this.options.enablePingOnStatsCheck !== void 0 && typeof this.options.enablePingOnStatsCheck !== "boolean")
|
|
2269
|
+
throw new SyntaxError("LavalinkNodeOptions.enablePingOnStatsCheck must be either false | true aka boolean");
|
|
2270
|
+
if (this.options.autoChecks !== void 0 && typeof this.options.autoChecks !== "object")
|
|
2271
|
+
throw new SyntaxError("LavalinkNode.autoChecks must be an object");
|
|
2272
|
+
if (this.options?.autoChecks?.sourcesValidations !== void 0 && typeof this.options?.autoChecks?.sourcesValidations !== "boolean")
|
|
2273
|
+
throw new SyntaxError("LavalinkNode.autoChecks.sourcesValidations must be either false | true aka boolean");
|
|
2274
|
+
if (this.options?.autoChecks?.pluginValidations !== void 0 && typeof this.options?.autoChecks?.pluginValidations !== "boolean")
|
|
2275
|
+
throw new SyntaxError("LavalinkNode.autoChecks.pluginValidations must be either false | true aka boolean");
|
|
2276
|
+
if (this.options.regions !== void 0 && (!Array.isArray(this.options.regions) || !this.options.regions.every((r) => typeof r === "string")))
|
|
2277
|
+
throw new SyntaxError("LavalinkNode.regions must be an Array of strings");
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* Checks if the node is a NodeLink node
|
|
2281
|
+
* @returns true if the node is a NodeLink node
|
|
2282
|
+
*/
|
|
2283
|
+
isNodeLink() {
|
|
2284
|
+
return this.nodeType === "NodeLink";
|
|
2285
|
+
}
|
|
2286
|
+
/**
|
|
2287
|
+
* Checks if the node is a Lavalink node
|
|
2288
|
+
* @returns true if the node is a Lavalink node
|
|
2289
|
+
*/
|
|
2290
|
+
isLavalinkNode() {
|
|
2291
|
+
return this.nodeType === "Lavalink";
|
|
1804
2292
|
}
|
|
1805
2293
|
/**
|
|
1806
2294
|
* Sync the data of the player you make an action to lavalink to
|
|
@@ -1823,7 +2311,9 @@ var LavalinkNode = class {
|
|
|
1823
2311
|
if (typeof data.playerOptions.voice !== "undefined") player.voice = data.playerOptions.voice;
|
|
1824
2312
|
if (typeof data.playerOptions.volume !== "undefined") {
|
|
1825
2313
|
if (this._LManager.options.playerOptions.volumeDecrementer) {
|
|
1826
|
-
player.volume = Math.round(
|
|
2314
|
+
player.volume = Math.round(
|
|
2315
|
+
data.playerOptions.volume / this._LManager.options.playerOptions.volumeDecrementer
|
|
2316
|
+
);
|
|
1827
2317
|
player.lavalinkVolume = Math.round(data.playerOptions.volume);
|
|
1828
2318
|
} else {
|
|
1829
2319
|
player.volume = Math.round(data.playerOptions.volume);
|
|
@@ -1833,16 +2323,26 @@ var LavalinkNode = class {
|
|
|
1833
2323
|
if (typeof data.playerOptions.filters !== "undefined") {
|
|
1834
2324
|
const oldFilterTimescale = { ...player.filterManager.data.timescale };
|
|
1835
2325
|
Object.freeze(oldFilterTimescale);
|
|
1836
|
-
if (data.playerOptions.filters.timescale)
|
|
1837
|
-
|
|
1838
|
-
if (data.playerOptions.filters.
|
|
1839
|
-
|
|
1840
|
-
if (data.playerOptions.filters.
|
|
1841
|
-
|
|
1842
|
-
if (data.playerOptions.filters.
|
|
1843
|
-
|
|
1844
|
-
if (data.playerOptions.filters.
|
|
1845
|
-
|
|
2326
|
+
if (data.playerOptions.filters.timescale)
|
|
2327
|
+
player.filterManager.data.timescale = data.playerOptions.filters.timescale;
|
|
2328
|
+
if (data.playerOptions.filters.distortion)
|
|
2329
|
+
player.filterManager.data.distortion = data.playerOptions.filters.distortion;
|
|
2330
|
+
if (data.playerOptions.filters.pluginFilters)
|
|
2331
|
+
player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
|
|
2332
|
+
if (data.playerOptions.filters.vibrato)
|
|
2333
|
+
player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
|
|
2334
|
+
if (data.playerOptions.filters.volume)
|
|
2335
|
+
player.filterManager.data.volume = data.playerOptions.filters.volume;
|
|
2336
|
+
if (data.playerOptions.filters.equalizer)
|
|
2337
|
+
player.filterManager.equalizerBands = data.playerOptions.filters.equalizer;
|
|
2338
|
+
if (data.playerOptions.filters.karaoke)
|
|
2339
|
+
player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
|
|
2340
|
+
if (data.playerOptions.filters.lowPass)
|
|
2341
|
+
player.filterManager.data.lowPass = data.playerOptions.filters.lowPass;
|
|
2342
|
+
if (data.playerOptions.filters.rotation)
|
|
2343
|
+
player.filterManager.data.rotation = data.playerOptions.filters.rotation;
|
|
2344
|
+
if (data.playerOptions.filters.tremolo)
|
|
2345
|
+
player.filterManager.data.tremolo = data.playerOptions.filters.tremolo;
|
|
1846
2346
|
player.filterManager.checkFiltersState(oldFilterTimescale);
|
|
1847
2347
|
}
|
|
1848
2348
|
}
|
|
@@ -1871,7 +2371,7 @@ var LavalinkNode = class {
|
|
|
1871
2371
|
}
|
|
1872
2372
|
/**
|
|
1873
2373
|
* Reconnect to the lavalink node
|
|
1874
|
-
* @param force @default false
|
|
2374
|
+
* @param force @default false Wether to instantly try to reconnect (force it)
|
|
1875
2375
|
* @returns void
|
|
1876
2376
|
*
|
|
1877
2377
|
* @example
|
|
@@ -1896,14 +2396,13 @@ var LavalinkNode = class {
|
|
|
1896
2396
|
}, this.options.retryDelay || 1e3);
|
|
1897
2397
|
}
|
|
1898
2398
|
get reconnectionAttemptCount() {
|
|
1899
|
-
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1900
2399
|
const maxAllowedTimestan = this.options.retryTimespan || -1;
|
|
1901
2400
|
if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
|
|
1902
|
-
return this.reconnectAttempts
|
|
2401
|
+
return this.reconnectAttempts.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length;
|
|
1903
2402
|
}
|
|
1904
2403
|
/**
|
|
1905
2404
|
* Private Utility function to execute the reconnection
|
|
1906
|
-
|
|
2405
|
+
*/
|
|
1907
2406
|
executeReconnect() {
|
|
1908
2407
|
if (this.reconnectionAttemptCount >= this.options.retryAmount) {
|
|
1909
2408
|
const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
|
|
@@ -1912,8 +2411,11 @@ var LavalinkNode = class {
|
|
|
1912
2411
|
this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
|
|
1913
2412
|
return;
|
|
1914
2413
|
}
|
|
1915
|
-
|
|
2414
|
+
const MAX_RECONNECT_ATTEMPTS = 1e3;
|
|
1916
2415
|
this.reconnectAttempts.push(Date.now());
|
|
2416
|
+
if (this.reconnectAttempts.length > MAX_RECONNECT_ATTEMPTS) {
|
|
2417
|
+
this.reconnectAttempts = this.reconnectAttempts.slice(-MAX_RECONNECT_ATTEMPTS);
|
|
2418
|
+
}
|
|
1917
2419
|
this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
|
|
1918
2420
|
this.NodeManager.emit("reconnecting", this);
|
|
1919
2421
|
this.connect();
|
|
@@ -1950,20 +2452,23 @@ var LavalinkNode = class {
|
|
|
1950
2452
|
async open() {
|
|
1951
2453
|
this.isAlive = true;
|
|
1952
2454
|
this.resetReconnectionAttempts();
|
|
1953
|
-
if (this.
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
this.
|
|
1957
|
-
this.
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2455
|
+
if (this.nodeType === "Lavalink") {
|
|
2456
|
+
if (this.options.enablePingOnStatsCheck) this.heartBeat();
|
|
2457
|
+
if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
|
|
2458
|
+
if (this.options.heartBeatInterval > 0) {
|
|
2459
|
+
this.socket.on("pong", () => {
|
|
2460
|
+
this.heartBeatPongTimestamp = performance.now();
|
|
2461
|
+
this.isAlive = true;
|
|
2462
|
+
});
|
|
2463
|
+
this.heartBeatInterval = setInterval(() => {
|
|
2464
|
+
if (!this.socket)
|
|
2465
|
+
return console.error("Node-Heartbeat-Interval - Socket not available - maybe reconnecting?");
|
|
2466
|
+
if (!this.isAlive) return this.close(500, "Node-Heartbeat-Timeout");
|
|
2467
|
+
this.isAlive = false;
|
|
2468
|
+
this.heartBeatPingTimestamp = performance.now();
|
|
2469
|
+
this.socket?.ping?.();
|
|
2470
|
+
}, this.options.heartBeatInterval || 3e4);
|
|
2471
|
+
}
|
|
1967
2472
|
}
|
|
1968
2473
|
this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
|
|
1969
2474
|
if (!this.info && ["v3", "v4"].includes(this.version)) {
|
|
@@ -2002,8 +2507,7 @@ var LavalinkNode = class {
|
|
|
2002
2507
|
this._LManager.players.filter((p) => p?.node?.options?.id === this?.options?.id).forEach((p) => {
|
|
2003
2508
|
if (!this._LManager.options.autoMove) return p.playing = false;
|
|
2004
2509
|
if (this._LManager.options.autoMove) {
|
|
2005
|
-
if (this.NodeManager.nodes.filter((n) => n.connected).size === 0)
|
|
2006
|
-
return p.playing = false;
|
|
2510
|
+
if (this.NodeManager.nodes.filter((n) => n.connected).size === 0) return p.playing = false;
|
|
2007
2511
|
p.moveNode();
|
|
2008
2512
|
}
|
|
2009
2513
|
});
|
|
@@ -2019,7 +2523,6 @@ var LavalinkNode = class {
|
|
|
2019
2523
|
if (this.pingTimeout) clearTimeout(this.pingTimeout);
|
|
2020
2524
|
this.socket?.close(500, "Node-Error - Force Reconnect");
|
|
2021
2525
|
}
|
|
2022
|
-
;
|
|
2023
2526
|
}
|
|
2024
2527
|
/** @private util function for handling message events from websocket */
|
|
2025
2528
|
async message(d) {
|
|
@@ -2043,11 +2546,12 @@ var LavalinkNode = class {
|
|
|
2043
2546
|
case "playerUpdate":
|
|
2044
2547
|
{
|
|
2045
2548
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2046
|
-
if (!player)
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2549
|
+
if (!player)
|
|
2550
|
+
return this._emitDebugEvent("PlayerUpdateNoPlayer" /* PlayerUpdateNoPlayer */, {
|
|
2551
|
+
state: "error",
|
|
2552
|
+
message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
|
|
2553
|
+
functionLayer: "LavalinkNode > nodeEvent > playerUpdate"
|
|
2554
|
+
});
|
|
2051
2555
|
const oldPlayer = player?.toJSON();
|
|
2052
2556
|
player.lastPositionChange = Date.now();
|
|
2053
2557
|
player.lastPosition = payload.state.position || 0;
|
|
@@ -2098,21 +2602,13 @@ var LavalinkNode = class {
|
|
|
2098
2602
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2099
2603
|
if (!player) return;
|
|
2100
2604
|
const NodeLinkEventType = payload.type;
|
|
2101
|
-
const NodeLinkExclusiveEvents = [
|
|
2102
|
-
"PlayerCreatedEvent",
|
|
2103
|
-
"PlayerDestroyedEvent",
|
|
2104
|
-
"PlayerConnectedEvent",
|
|
2105
|
-
"PlayerReconnectingEvent",
|
|
2106
|
-
"VolumeChangedEvent",
|
|
2107
|
-
"FiltersChangedEvent",
|
|
2108
|
-
"SeekEvent",
|
|
2109
|
-
"PauseEvent",
|
|
2110
|
-
"ConnectionStatusEvent",
|
|
2111
|
-
"MixStartedEvent",
|
|
2112
|
-
"MixEndedEvent"
|
|
2113
|
-
];
|
|
2114
2605
|
if (NodeLinkExclusiveEvents.includes(NodeLinkEventType) && (!this.info || this.info.isNodelink)) {
|
|
2115
|
-
return this.nodeLinkEventHandler(
|
|
2606
|
+
return this.nodeLinkEventHandler(
|
|
2607
|
+
NodeLinkEventType,
|
|
2608
|
+
player,
|
|
2609
|
+
player.queue.current,
|
|
2610
|
+
payload
|
|
2611
|
+
);
|
|
2116
2612
|
}
|
|
2117
2613
|
switch (payload.type) {
|
|
2118
2614
|
case "TrackStartEvent":
|
|
@@ -2152,7 +2648,12 @@ var LavalinkNode = class {
|
|
|
2152
2648
|
this.LyricsNotFound(player, player.queue.current, payload);
|
|
2153
2649
|
break;
|
|
2154
2650
|
default:
|
|
2155
|
-
this.NodeManager.emit(
|
|
2651
|
+
this.NodeManager.emit(
|
|
2652
|
+
"error",
|
|
2653
|
+
this,
|
|
2654
|
+
new Error(`Node#event unknown event '${payload.type}'.`),
|
|
2655
|
+
payload
|
|
2656
|
+
);
|
|
2156
2657
|
break;
|
|
2157
2658
|
}
|
|
2158
2659
|
return;
|
|
@@ -2211,7 +2712,8 @@ var LavalinkNode = class {
|
|
|
2211
2712
|
this._LManager.emit("trackEnd", player, trackToUse, payload);
|
|
2212
2713
|
return;
|
|
2213
2714
|
}
|
|
2214
|
-
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
2715
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
2716
|
+
return this.queueEnd(player, track, payload);
|
|
2215
2717
|
if (["loadFailed", "cleanup"].includes(payload.reason)) {
|
|
2216
2718
|
if (player.get("internal_destroystatus") === true) return;
|
|
2217
2719
|
await queueTrackEnd(player);
|
|
@@ -2225,7 +2727,8 @@ var LavalinkNode = class {
|
|
|
2225
2727
|
if (player.repeatMode !== "track" || player.get("internal_skipped")) await queueTrackEnd(player);
|
|
2226
2728
|
else if (trackToUse && !trackToUse?.pluginInfo?.clientData?.previousTrack) {
|
|
2227
2729
|
player.queue.previous.unshift(trackToUse);
|
|
2228
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2730
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2731
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
2229
2732
|
await player.queue.utils.save();
|
|
2230
2733
|
}
|
|
2231
2734
|
if (!player.queue.current) return this.queueEnd(player, trackToUse, payload);
|
|
@@ -2239,7 +2742,9 @@ var LavalinkNode = class {
|
|
|
2239
2742
|
/** @private util function for handling trackStuck event */
|
|
2240
2743
|
async trackStuck(player, track, payload) {
|
|
2241
2744
|
if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
2242
|
-
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2745
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2746
|
+
(v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
|
|
2747
|
+
);
|
|
2243
2748
|
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
2244
2749
|
if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
2245
2750
|
this._emitDebugEvent("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */, {
|
|
@@ -2254,7 +2759,10 @@ var LavalinkNode = class {
|
|
|
2254
2759
|
this._LManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
|
|
2255
2760
|
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
|
|
2256
2761
|
try {
|
|
2257
|
-
await player.node.updatePlayer({
|
|
2762
|
+
await player.node.updatePlayer({
|
|
2763
|
+
guildId: player.guildId,
|
|
2764
|
+
playerOptions: { track: { encoded: null } }
|
|
2765
|
+
});
|
|
2258
2766
|
return;
|
|
2259
2767
|
} catch {
|
|
2260
2768
|
return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
|
|
@@ -2272,7 +2780,9 @@ var LavalinkNode = class {
|
|
|
2272
2780
|
/** @private util function for handling trackError event */
|
|
2273
2781
|
async trackError(player, track, payload) {
|
|
2274
2782
|
if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
2275
|
-
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2783
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2784
|
+
(v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
|
|
2785
|
+
);
|
|
2276
2786
|
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
2277
2787
|
if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
2278
2788
|
this._emitDebugEvent("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */, {
|
|
@@ -2324,8 +2834,11 @@ var LavalinkNode = class {
|
|
|
2324
2834
|
* ```
|
|
2325
2835
|
*/
|
|
2326
2836
|
async getSponsorBlock(player) {
|
|
2327
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2328
|
-
|
|
2837
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2838
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2839
|
+
return await this.request(
|
|
2840
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`
|
|
2841
|
+
);
|
|
2329
2842
|
}
|
|
2330
2843
|
/**
|
|
2331
2844
|
* Set the current sponsorblocks for the sponsorblock plugin
|
|
@@ -2339,15 +2852,25 @@ var LavalinkNode = class {
|
|
|
2339
2852
|
* ```
|
|
2340
2853
|
*/
|
|
2341
2854
|
async setSponsorBlock(player, segments = ["sponsor", "selfpromo"]) {
|
|
2342
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2855
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2856
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2343
2857
|
if (!segments.length) throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
|
|
2344
|
-
if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
|
|
2858
|
+
if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
|
|
2859
|
+
throw new SyntaxError(
|
|
2860
|
+
`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map((v) => `'${v}'`).join(", ")}`
|
|
2861
|
+
);
|
|
2345
2862
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
2346
2863
|
r.method = "PUT";
|
|
2347
|
-
r.headers = {
|
|
2864
|
+
r.headers = {
|
|
2865
|
+
Authorization: this.options.authorization,
|
|
2866
|
+
"Content-Type": "application/json"
|
|
2867
|
+
};
|
|
2348
2868
|
r.body = safeStringify(segments.map((v) => v.toLowerCase()));
|
|
2349
2869
|
});
|
|
2350
|
-
player.set(
|
|
2870
|
+
player.set(
|
|
2871
|
+
"internal_sponsorBlockCategories",
|
|
2872
|
+
segments.map((v) => v.toLowerCase())
|
|
2873
|
+
);
|
|
2351
2874
|
this._emitDebugEvent("SetSponsorBlock" /* SetSponsorBlock */, {
|
|
2352
2875
|
state: "log",
|
|
2353
2876
|
message: `SponsorBlock was set for Player: ${player.guildId} to: ${segments.map((v) => `'${v.toLowerCase()}'`).join(", ")}`,
|
|
@@ -2367,7 +2890,8 @@ var LavalinkNode = class {
|
|
|
2367
2890
|
* ```
|
|
2368
2891
|
*/
|
|
2369
2892
|
async deleteSponsorBlock(player) {
|
|
2370
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2893
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2894
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2371
2895
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
2372
2896
|
r.method = "DELETE";
|
|
2373
2897
|
});
|
|
@@ -2402,11 +2926,12 @@ var LavalinkNode = class {
|
|
|
2402
2926
|
await this._LManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
2403
2927
|
player.set("internal_previousautoplay", Date.now());
|
|
2404
2928
|
if (player.queue.tracks.length > 0) await queueTrackEnd(player);
|
|
2405
|
-
else
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2929
|
+
else
|
|
2930
|
+
this._emitDebugEvent("AutoplayNoSongsAdded" /* AutoplayNoSongsAdded */, {
|
|
2931
|
+
state: "warn",
|
|
2932
|
+
message: `Autoplay was triggered but no songs were added to the queue.`,
|
|
2933
|
+
functionLayer: "LavalinkNode > queueEnd() > autoplayFunction"
|
|
2934
|
+
});
|
|
2410
2935
|
}
|
|
2411
2936
|
if (player.queue.current) {
|
|
2412
2937
|
if (payload.type === "TrackEndEvent") this._LManager.emit("trackEnd", player, track, payload);
|
|
@@ -2423,7 +2948,8 @@ var LavalinkNode = class {
|
|
|
2423
2948
|
player.set("internal_autoplayStopPlaying", void 0);
|
|
2424
2949
|
if (track && !track?.pluginInfo?.clientData?.previousTrack) {
|
|
2425
2950
|
player.queue.previous.unshift(track);
|
|
2426
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2951
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2952
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
2427
2953
|
await player.queue.utils.save();
|
|
2428
2954
|
}
|
|
2429
2955
|
if (payload?.reason !== "stopped") {
|
|
@@ -2439,16 +2965,23 @@ var LavalinkNode = class {
|
|
|
2439
2965
|
message: `Trigger Queue Empty Interval was Triggered because playerOptions.onEmptyQueue.destroyAfterMs is set to ${this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs}ms`,
|
|
2440
2966
|
functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs"
|
|
2441
2967
|
});
|
|
2442
|
-
this._LManager.emit(
|
|
2968
|
+
this._LManager.emit(
|
|
2969
|
+
"playerQueueEmptyStart",
|
|
2970
|
+
player,
|
|
2971
|
+
this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs
|
|
2972
|
+
);
|
|
2443
2973
|
if (player.get("internal_queueempty")) clearTimeout(player.get("internal_queueempty"));
|
|
2444
|
-
player.set(
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2974
|
+
player.set(
|
|
2975
|
+
"internal_queueempty",
|
|
2976
|
+
setTimeout(() => {
|
|
2977
|
+
player.set("internal_queueempty", void 0);
|
|
2978
|
+
if (player.queue.current) {
|
|
2979
|
+
return this._LManager.emit("playerQueueEmptyCancel", player);
|
|
2980
|
+
}
|
|
2981
|
+
this._LManager.emit("playerQueueEmptyEnd", player);
|
|
2982
|
+
player.destroy("QueueEmpty" /* QueueEmpty */);
|
|
2983
|
+
}, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs)
|
|
2984
|
+
);
|
|
2452
2985
|
}
|
|
2453
2986
|
}
|
|
2454
2987
|
this._LManager.emit("queueEnd", player, track, payload);
|
|
@@ -2525,6 +3058,310 @@ var LavalinkNode = class {
|
|
|
2525
3058
|
}
|
|
2526
3059
|
};
|
|
2527
3060
|
|
|
3061
|
+
// src/structures/NodeLink.ts
|
|
3062
|
+
var NodeLinkNode = class extends LavalinkNode {
|
|
3063
|
+
nodeType = "NodeLink";
|
|
3064
|
+
constructor(options, manager) {
|
|
3065
|
+
super(options, manager);
|
|
3066
|
+
if (this.options.nodeType === "Lavalink" && this.constructor.name === "NodeLink") {
|
|
3067
|
+
return new LavalinkNode(options, manager);
|
|
3068
|
+
}
|
|
3069
|
+
this.nodeType = "NodeLink";
|
|
3070
|
+
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Adds a new audio track to be mixed over the current playback.
|
|
3073
|
+
* @param player The player to add the mixer layer to.
|
|
3074
|
+
* @param trackToAdd The track to add to the mixer layer.
|
|
3075
|
+
* @param volume The volume of the track to add to the mixer layer. (0 - 100)
|
|
3076
|
+
* @link {https://nodelink.js.org/docs/api/rest#add-mix-layer} documentiation
|
|
3077
|
+
*/
|
|
3078
|
+
async addMixerLayer(player, trackToAdd, volume) {
|
|
3079
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3080
|
+
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
|
|
3081
|
+
m.method = "POST";
|
|
3082
|
+
m.body = safeStringify({
|
|
3083
|
+
track: {
|
|
3084
|
+
encoded: trackToAdd.encoded,
|
|
3085
|
+
//identifier: trackToAdd.info?.identifier, // atm not supported
|
|
3086
|
+
userData: trackToAdd.userData
|
|
3087
|
+
},
|
|
3088
|
+
volume: (volume / 100).toFixed(2)
|
|
3089
|
+
});
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Retrieves a list of currently active mix layers.
|
|
3094
|
+
* @param player The player to list the mixer layers for.
|
|
3095
|
+
* @link {https://nodelink.js.org/docs/api/rest#get-active-mixes} documentiation
|
|
3096
|
+
*/
|
|
3097
|
+
async listMixerLayers(player) {
|
|
3098
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3099
|
+
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
|
|
3100
|
+
m.method = "GET";
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
3104
|
+
* Updates the volume of a specific mix layer.
|
|
3105
|
+
* @param player The player to update the mixer layer volume for.
|
|
3106
|
+
* @param mixId The ID of the mix layer to update.
|
|
3107
|
+
* @param volume The volume of the mix layer to update. (0 - 100)
|
|
3108
|
+
* @link {https://nodelink.js.org/docs/api/rest#update-mix-volume} documentiation
|
|
3109
|
+
*/
|
|
3110
|
+
async updateMixerLayerVolume(player, mixId, volume) {
|
|
3111
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3112
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
|
|
3113
|
+
m.method = "PATCH";
|
|
3114
|
+
m.body = safeStringify({
|
|
3115
|
+
volume: (volume / 100).toFixed(2)
|
|
3116
|
+
});
|
|
3117
|
+
});
|
|
3118
|
+
return true;
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Removes a specific mix layer.
|
|
3122
|
+
* @param player The player to remove the mix layer from.
|
|
3123
|
+
* @param mixId The ID of the mix layer to remove.
|
|
3124
|
+
* @link {https://nodelink.js.org/docs/api/rest#remove-mix-layer} documentiation
|
|
3125
|
+
*/
|
|
3126
|
+
async removeMixerLayer(player, mixId) {
|
|
3127
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3128
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
|
|
3129
|
+
m.method = "DELETE";
|
|
3130
|
+
});
|
|
3131
|
+
return true;
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* @description
|
|
3135
|
+
* NodeLink has a lot of filters SPECIFICALLY for NodeLink, check the documentation for more information.
|
|
3136
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#additional-filters} documentiation
|
|
3137
|
+
*/
|
|
3138
|
+
specificFilters = {
|
|
3139
|
+
/**
|
|
3140
|
+
* Creates delay-based echo with feedback control
|
|
3141
|
+
* @param player The player to apply the filter to
|
|
3142
|
+
* @param options The echo filter options
|
|
3143
|
+
*/
|
|
3144
|
+
echo: async (player, options, disableFilter = false) => {
|
|
3145
|
+
if (disableFilter) delete player.filterManager.data.echo;
|
|
3146
|
+
else player.filterManager.data.echo = options;
|
|
3147
|
+
await player.filterManager.applyPlayerFilters();
|
|
3148
|
+
return player.filterManager.filters.nodeLinkEcho;
|
|
3149
|
+
},
|
|
3150
|
+
/**
|
|
3151
|
+
* Simulates multiple voices playing together with modulated delays
|
|
3152
|
+
* @param player The player to apply the filter to
|
|
3153
|
+
* @param options The chorus filter options
|
|
3154
|
+
*/
|
|
3155
|
+
chorus: async (player, options, disableFilter = false) => {
|
|
3156
|
+
if (disableFilter) delete player.filterManager.data.chorus;
|
|
3157
|
+
else player.filterManager.data.chorus = options;
|
|
3158
|
+
await player.filterManager.applyPlayerFilters();
|
|
3159
|
+
return player.filterManager.filters.nodeLinkChorus;
|
|
3160
|
+
},
|
|
3161
|
+
/**
|
|
3162
|
+
* Dynamic range compression for balanced audio levels
|
|
3163
|
+
* @param player The player to apply the filter to
|
|
3164
|
+
* @param options The compressor filter options
|
|
3165
|
+
*/
|
|
3166
|
+
compressor: async (player, options, disableFilter = false) => {
|
|
3167
|
+
if (disableFilter) delete player.filterManager.data.compressor;
|
|
3168
|
+
else player.filterManager.data.compressor = options;
|
|
3169
|
+
await player.filterManager.applyPlayerFilters();
|
|
3170
|
+
return player.filterManager.filters.nodeLinkCompressor;
|
|
3171
|
+
},
|
|
3172
|
+
/**
|
|
3173
|
+
* Filters out low frequencies, letting high frequencies pass through
|
|
3174
|
+
* @param player The player to apply the filter to
|
|
3175
|
+
* @param options The highpass filter options
|
|
3176
|
+
*/
|
|
3177
|
+
highPass: async (player, options, disableFilter = false) => {
|
|
3178
|
+
if (disableFilter) delete player.filterManager.data.highPass;
|
|
3179
|
+
else player.filterManager.data.highPass = options;
|
|
3180
|
+
await player.filterManager.applyPlayerFilters();
|
|
3181
|
+
return player.filterManager.filters.nodeLinkHighPass;
|
|
3182
|
+
},
|
|
3183
|
+
/**
|
|
3184
|
+
* Sweeps all-pass filters across the frequency spectrum for a swooshing effect
|
|
3185
|
+
* @param player The player to apply the filter to
|
|
3186
|
+
* @param options The phaser filter options
|
|
3187
|
+
*/
|
|
3188
|
+
phaser: async (player, options, disableFilter = false) => {
|
|
3189
|
+
if (disableFilter) delete player.filterManager.data.phaser;
|
|
3190
|
+
else player.filterManager.data.phaser = options;
|
|
3191
|
+
await player.filterManager.applyPlayerFilters();
|
|
3192
|
+
return player.filterManager.filters.nodeLinkPhaser;
|
|
3193
|
+
},
|
|
3194
|
+
/**
|
|
3195
|
+
* Creates spatial audio using cross-channel delays and modulation
|
|
3196
|
+
* @param player The player to apply the filter to
|
|
3197
|
+
* @param options The spatial filter options
|
|
3198
|
+
*/
|
|
3199
|
+
spatial: async (player, options, disableFilter = false) => {
|
|
3200
|
+
if (disableFilter) delete player.filterManager.data.spatial;
|
|
3201
|
+
else player.filterManager.data.spatial = options;
|
|
3202
|
+
await player.filterManager.applyPlayerFilters();
|
|
3203
|
+
return player.filterManager.filters.nodeLinkSpatial;
|
|
3204
|
+
},
|
|
3205
|
+
/**
|
|
3206
|
+
* Resets all NodeLink filters
|
|
3207
|
+
* @param player The player to reset the filters for
|
|
3208
|
+
*/
|
|
3209
|
+
resetNodeLinkFilters: async (player) => {
|
|
3210
|
+
delete player.filterManager.data.spatial;
|
|
3211
|
+
delete player.filterManager.data.echo;
|
|
3212
|
+
delete player.filterManager.data.chorus;
|
|
3213
|
+
delete player.filterManager.data.compressor;
|
|
3214
|
+
delete player.filterManager.data.highPass;
|
|
3215
|
+
delete player.filterManager.data.phaser;
|
|
3216
|
+
player.filterManager.checkFiltersState();
|
|
3217
|
+
await player.filterManager.applyPlayerFilters();
|
|
3218
|
+
return true;
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3221
|
+
/**
|
|
3222
|
+
* Retrieve Lyrics of Youtube Videos.
|
|
3223
|
+
* @param player The Player you use with that node.
|
|
3224
|
+
* @param track if not provided, it will use the current track
|
|
3225
|
+
* @param language if not provided, it will use the default language (en)
|
|
3226
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#lyrics--chapters}
|
|
3227
|
+
* @returns NodeLinkLyrics either synced/unsynced or NodeLinkNoLyrics
|
|
3228
|
+
*/
|
|
3229
|
+
async nodeLinkLyrics(player, track, language = "en") {
|
|
3230
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3231
|
+
const encodedTrack = track?.encoded || player.queue.current?.encoded;
|
|
3232
|
+
if (!encodedTrack) throw new Error("No track provided");
|
|
3233
|
+
return await this.request(
|
|
3234
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/lyrics?encodedTrack=${encodedTrack}&lang=${language}`,
|
|
3235
|
+
(m) => {
|
|
3236
|
+
m.method = "GET";
|
|
3237
|
+
}
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* Retrieve Chapters of Youtube Videos.
|
|
3242
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#loadchapters}
|
|
3243
|
+
* @param player The Player you use with that node.
|
|
3244
|
+
* @param track if not provided, it will use the current track
|
|
3245
|
+
* @returns Array of NodeLinkChapter objects (if empty than there are no chapters available)
|
|
3246
|
+
*/
|
|
3247
|
+
async getChapters(player, track) {
|
|
3248
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3249
|
+
const encodedTrack = track?.encoded || player.queue.current?.encoded;
|
|
3250
|
+
if (!encodedTrack) throw new Error("No track provided");
|
|
3251
|
+
return await this.request(
|
|
3252
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/chapters?encodedTrack=${encodedTrack}`,
|
|
3253
|
+
(m) => {
|
|
3254
|
+
m.method = "GET";
|
|
3255
|
+
}
|
|
3256
|
+
);
|
|
3257
|
+
}
|
|
3258
|
+
/**
|
|
3259
|
+
* @link {https://nodelink.js.org/docs/api/rest#node-information}
|
|
3260
|
+
* @returns
|
|
3261
|
+
*/
|
|
3262
|
+
async getConnectionMetrics() {
|
|
3263
|
+
return await this.request(`/connection`, (m) => {
|
|
3264
|
+
m.method = "GET";
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
3267
|
+
/**
|
|
3268
|
+
* Stream audio directly from NodeLink without Discord voice connection. | Note this must be enabled by NodeLink...
|
|
3269
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#direct-streaming}
|
|
3270
|
+
*/
|
|
3271
|
+
async getDirectStream(track) {
|
|
3272
|
+
return await this.request(`/trackstream?encodedTrack=${track.encoded}`, (m) => {
|
|
3273
|
+
m.method = "GET";
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Stream raw PCM audio for custom processing or recording.
|
|
3278
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#loadstream}
|
|
3279
|
+
* @param track The track to stream
|
|
3280
|
+
* @param volume The volume to stream at
|
|
3281
|
+
* @param position The position to stream from
|
|
3282
|
+
* @param filters The filters to apply to the stream
|
|
3283
|
+
* @returns Returns a raw PCM stream with Content-Type: audio/l16;rate=48000;channels=2.
|
|
3284
|
+
*/
|
|
3285
|
+
async loadDirectStream(track, volume, position, filters) {
|
|
3286
|
+
let requestPath = `/loadstream?encodedTrack=${track.encoded}`;
|
|
3287
|
+
if (volume && volume > 0 && volume <= 100) requestPath += `&volume=${(volume / 100).toFixed(2)}`;
|
|
3288
|
+
if (position && position > 0) requestPath += `&position=${position}`;
|
|
3289
|
+
if (filters) requestPath += `&filters=${typeof filters === "object" ? safeStringify(filters) : filters}`;
|
|
3290
|
+
const res = await this.rawRequest(requestPath, (m) => {
|
|
3291
|
+
m.method = "GET";
|
|
3292
|
+
});
|
|
3293
|
+
return res.response;
|
|
3294
|
+
}
|
|
3295
|
+
/**
|
|
3296
|
+
* NodeLink supports selecting specific audio tracks for videos that contain multiple audio streams (e.g., Netflixstyle dubs, multi-language YouTube videos).
|
|
3297
|
+
* This function changes the current language of the audio, in place at the same position of the current track.
|
|
3298
|
+
* You can always do it manually by providing extra field in the track object "audioTrackId"
|
|
3299
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#additional-filters}
|
|
3300
|
+
* @param player The player to apply the filter to
|
|
3301
|
+
* @param language_audioTrackId The language of the audio track to select, see it in the pluginInfo.audioTracks
|
|
3302
|
+
*/
|
|
3303
|
+
async changeAudioTrackLanguage(player, language_audioTrackId) {
|
|
3304
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3305
|
+
const res = await this.request(`/sessions/${this.sessionId}/players/${player.guildId}`, (r) => {
|
|
3306
|
+
r.method = "PATCH";
|
|
3307
|
+
r.headers["Content-Type"] = "application/json";
|
|
3308
|
+
r.body = safeStringify({
|
|
3309
|
+
track: {
|
|
3310
|
+
encoded: player.queue.current?.encoded,
|
|
3311
|
+
position: player.position,
|
|
3312
|
+
audioTrackId: language_audioTrackId
|
|
3313
|
+
}
|
|
3314
|
+
});
|
|
3315
|
+
});
|
|
3316
|
+
return res;
|
|
3317
|
+
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Updates the YouTube configuration (RefreshToken or VisitorData) in real-time.
|
|
3320
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#update-config}
|
|
3321
|
+
*/
|
|
3322
|
+
async updateYoutubeConfig(refreshToken, visitorData) {
|
|
3323
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3324
|
+
const res = await this.request(`/youtube/config`, (r) => {
|
|
3325
|
+
r.method = "PATCH";
|
|
3326
|
+
r.headers["Content-Type"] = "application/json";
|
|
3327
|
+
r.body = safeStringify({
|
|
3328
|
+
refreshToken,
|
|
3329
|
+
visitorData
|
|
3330
|
+
});
|
|
3331
|
+
});
|
|
3332
|
+
return res;
|
|
3333
|
+
}
|
|
3334
|
+
async getYoutubeConfig(validate = false) {
|
|
3335
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3336
|
+
const res = await this.request(`/youtube/config${validate ? "?validate=true" : ""}`, (r) => {
|
|
3337
|
+
r.method = "GET";
|
|
3338
|
+
});
|
|
3339
|
+
return res;
|
|
3340
|
+
}
|
|
3341
|
+
/**
|
|
3342
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#oauth}
|
|
3343
|
+
*/
|
|
3344
|
+
async getYoutubeOAUTH(refreshToken) {
|
|
3345
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3346
|
+
return await this.request(`/youtube/oauth?refreshToken=${refreshToken}`, (m) => {
|
|
3347
|
+
m.method = "GET";
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
/**
|
|
3351
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#oauth}
|
|
3352
|
+
*/
|
|
3353
|
+
async updateYoutubeOAUTH(refreshToken) {
|
|
3354
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3355
|
+
return await this.request(`/youtube/oauth`, (m) => {
|
|
3356
|
+
m.method = "POST";
|
|
3357
|
+
m.body = safeStringify({
|
|
3358
|
+
refreshToken
|
|
3359
|
+
});
|
|
3360
|
+
});
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
LavalinkNode._NodeLinkClass = NodeLinkNode;
|
|
3364
|
+
|
|
2528
3365
|
// src/structures/NodeManager.ts
|
|
2529
3366
|
var NodeManager = class extends EventEmitter {
|
|
2530
3367
|
/**
|
|
@@ -2586,9 +3423,10 @@ var NodeManager = class extends EventEmitter {
|
|
|
2586
3423
|
constructor(LavalinkManager2) {
|
|
2587
3424
|
super();
|
|
2588
3425
|
this.LavalinkManager = LavalinkManager2;
|
|
2589
|
-
if (this.LavalinkManager.options.nodes)
|
|
2590
|
-
this.
|
|
2591
|
-
|
|
3426
|
+
if (this.LavalinkManager.options.nodes)
|
|
3427
|
+
this.LavalinkManager.options.nodes.forEach((node) => {
|
|
3428
|
+
this.createNode(node);
|
|
3429
|
+
});
|
|
2592
3430
|
}
|
|
2593
3431
|
/**
|
|
2594
3432
|
* Disconnects all Nodes from lavalink ws sockets
|
|
@@ -2598,7 +3436,8 @@ var NodeManager = class extends EventEmitter {
|
|
|
2598
3436
|
*/
|
|
2599
3437
|
async disconnectAll(deleteAllNodes = false, destroyPlayers = true) {
|
|
2600
3438
|
if (!this.nodes.size) throw new Error("There are no nodes to disconnect (no nodes in the nodemanager)");
|
|
2601
|
-
if (!this.nodes.filter((v) => v.connected).size)
|
|
3439
|
+
if (!this.nodes.filter((v) => v.connected).size)
|
|
3440
|
+
throw new Error("There are no nodes to disconnect (all nodes disconnected)");
|
|
2602
3441
|
let counter = 0;
|
|
2603
3442
|
for (const node of this.nodes.values()) {
|
|
2604
3443
|
if (!node.connected) continue;
|
|
@@ -2617,7 +3456,8 @@ var NodeManager = class extends EventEmitter {
|
|
|
2617
3456
|
*/
|
|
2618
3457
|
async connectAll() {
|
|
2619
3458
|
if (!this.nodes.size) throw new Error("There are no nodes to connect (no nodes in the nodemanager)");
|
|
2620
|
-
if (!this.nodes.filter((v) => !v.connected).size)
|
|
3459
|
+
if (!this.nodes.filter((v) => !v.connected).size)
|
|
3460
|
+
throw new Error("There are no nodes to connect (all nodes connected)");
|
|
2621
3461
|
let counter = 0;
|
|
2622
3462
|
for (const node of this.nodes.values()) {
|
|
2623
3463
|
if (node.connected) continue;
|
|
@@ -2647,8 +3487,9 @@ var NodeManager = class extends EventEmitter {
|
|
|
2647
3487
|
* @returns The node that was created
|
|
2648
3488
|
*/
|
|
2649
3489
|
createNode(options) {
|
|
2650
|
-
if (this.nodes.has(options.id || `${options.host}:${options.port}`))
|
|
2651
|
-
|
|
3490
|
+
if (this.nodes.has(options.id || `${options.host}:${options.port}`))
|
|
3491
|
+
return this.nodes.get(options.id || `${options.host}:${options.port}`);
|
|
3492
|
+
const newNode = options.nodeType === "NodeLink" ? new NodeLinkNode(options, this) : new LavalinkNode(options, this);
|
|
2652
3493
|
this.nodes.set(newNode.id, newNode);
|
|
2653
3494
|
return newNode;
|
|
2654
3495
|
}
|
|
@@ -2667,12 +3508,16 @@ var NodeManager = class extends EventEmitter {
|
|
|
2667
3508
|
break;
|
|
2668
3509
|
case "cpuLavalink":
|
|
2669
3510
|
{
|
|
2670
|
-
return connectedNodes.sort(
|
|
3511
|
+
return connectedNodes.sort(
|
|
3512
|
+
(a, b) => (a.stats?.cpu?.lavalinkLoad || 0) - (b.stats?.cpu?.lavalinkLoad || 0)
|
|
3513
|
+
);
|
|
2671
3514
|
}
|
|
2672
3515
|
break;
|
|
2673
3516
|
case "cpuSystem":
|
|
2674
3517
|
{
|
|
2675
|
-
return connectedNodes.sort(
|
|
3518
|
+
return connectedNodes.sort(
|
|
3519
|
+
(a, b) => (a.stats?.cpu?.systemLoad || 0) - (b.stats?.cpu?.systemLoad || 0)
|
|
3520
|
+
);
|
|
2676
3521
|
}
|
|
2677
3522
|
break;
|
|
2678
3523
|
case "calls":
|
|
@@ -2682,7 +3527,9 @@ var NodeManager = class extends EventEmitter {
|
|
|
2682
3527
|
break;
|
|
2683
3528
|
case "playingPlayers":
|
|
2684
3529
|
{
|
|
2685
|
-
return connectedNodes.sort(
|
|
3530
|
+
return connectedNodes.sort(
|
|
3531
|
+
(a, b) => (a.stats?.playingPlayers || 0) - (b.stats?.playingPlayers || 0)
|
|
3532
|
+
);
|
|
2686
3533
|
}
|
|
2687
3534
|
break;
|
|
2688
3535
|
case "players":
|
|
@@ -2702,7 +3549,7 @@ var NodeManager = class extends EventEmitter {
|
|
|
2702
3549
|
* @param node The node to delete
|
|
2703
3550
|
* @param movePlayers whether to movePlayers to different connected node before deletion. @default false
|
|
2704
3551
|
* @returns
|
|
2705
|
-
*
|
|
3552
|
+
*
|
|
2706
3553
|
* @example
|
|
2707
3554
|
* Deletes the node
|
|
2708
3555
|
* ```ts
|
|
@@ -2723,13 +3570,25 @@ var NodeManager = class extends EventEmitter {
|
|
|
2723
3570
|
this.nodes.delete(decodeNode.id);
|
|
2724
3571
|
return;
|
|
2725
3572
|
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Get a node from the nodeManager
|
|
3575
|
+
* @param node The node to get
|
|
3576
|
+
* @returns The node that was retrieved
|
|
3577
|
+
*/
|
|
3578
|
+
getNode(node) {
|
|
3579
|
+
const decodeNode = typeof node === "string" ? this.nodes.get(node) : node;
|
|
3580
|
+
if (!decodeNode) return void 0;
|
|
3581
|
+
if (decodeNode.nodeType === "NodeLink") return decodeNode;
|
|
3582
|
+
return decodeNode;
|
|
3583
|
+
}
|
|
2726
3584
|
};
|
|
2727
3585
|
|
|
2728
3586
|
// src/structures/CustomSearches/BandCampSearch.ts
|
|
2729
3587
|
var bandCampSearch = async (player, query, requestUser) => {
|
|
2730
3588
|
let error = null;
|
|
2731
3589
|
let tracks = [];
|
|
2732
|
-
if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
|
|
3590
|
+
if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
|
|
3591
|
+
console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
|
|
2733
3592
|
player.LavalinkManager.utils.validateQueryString(player.node, query);
|
|
2734
3593
|
try {
|
|
2735
3594
|
const requestUrl = new URL("https://bandcamp.com/api/nusearch/2/autocomplete");
|
|
@@ -2737,7 +3596,7 @@ var bandCampSearch = async (player, query, requestUser) => {
|
|
|
2737
3596
|
const data = await fetch(requestUrl.toString(), {
|
|
2738
3597
|
headers: {
|
|
2739
3598
|
"User-Agent": "android-async-http/1.4.1 (http://loopj.com/android-async-http)",
|
|
2740
|
-
|
|
3599
|
+
Cookie: "$Version=1"
|
|
2741
3600
|
}
|
|
2742
3601
|
});
|
|
2743
3602
|
if (!data.ok) throw new Error(`Bandcamp Error: ${data.statusText}`);
|
|
@@ -2747,13 +3606,18 @@ var bandCampSearch = async (player, query, requestUser) => {
|
|
|
2747
3606
|
} catch {
|
|
2748
3607
|
throw new Error("Invalid JSON response from Bandcamp");
|
|
2749
3608
|
}
|
|
2750
|
-
tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3609
|
+
tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(
|
|
3610
|
+
(item) => player.LavalinkManager.utils.buildUnresolvedTrack(
|
|
3611
|
+
{
|
|
3612
|
+
uri: item.url || item.uri,
|
|
3613
|
+
artworkUrl: item.img,
|
|
3614
|
+
author: item.band_name,
|
|
3615
|
+
title: item.name,
|
|
3616
|
+
identifier: item.id ? `${item.id}` : item.url?.split("/")?.reverse()[0]
|
|
3617
|
+
},
|
|
3618
|
+
requestUser
|
|
3619
|
+
)
|
|
3620
|
+
);
|
|
2757
3621
|
} catch (e) {
|
|
2758
3622
|
error = e;
|
|
2759
3623
|
}
|
|
@@ -2802,6 +3666,43 @@ var DEFAULT_FILTER_DATAS = {
|
|
|
2802
3666
|
// 0 < x <= 1
|
|
2803
3667
|
},
|
|
2804
3668
|
channelMix: audioOutputsData.stereo,
|
|
3669
|
+
// NODELINK SPECIFIC
|
|
3670
|
+
echo: {
|
|
3671
|
+
delay: 0,
|
|
3672
|
+
feedback: 0,
|
|
3673
|
+
mix: 0
|
|
3674
|
+
},
|
|
3675
|
+
chorus: {
|
|
3676
|
+
rate: 0,
|
|
3677
|
+
depth: 0,
|
|
3678
|
+
delay: 0,
|
|
3679
|
+
mix: 0,
|
|
3680
|
+
feedback: 0
|
|
3681
|
+
},
|
|
3682
|
+
compressor: {
|
|
3683
|
+
threshold: 0,
|
|
3684
|
+
ratio: 1,
|
|
3685
|
+
attack: 0,
|
|
3686
|
+
release: 0,
|
|
3687
|
+
gain: 0
|
|
3688
|
+
},
|
|
3689
|
+
highPass: {
|
|
3690
|
+
smoothing: 0
|
|
3691
|
+
},
|
|
3692
|
+
phaser: {
|
|
3693
|
+
stages: 0,
|
|
3694
|
+
rate: 0,
|
|
3695
|
+
depth: 0,
|
|
3696
|
+
feedback: 0,
|
|
3697
|
+
mix: 0,
|
|
3698
|
+
minFrequency: 0,
|
|
3699
|
+
maxFrequency: 0
|
|
3700
|
+
},
|
|
3701
|
+
spatial: {
|
|
3702
|
+
depth: 0,
|
|
3703
|
+
rate: 0
|
|
3704
|
+
},
|
|
3705
|
+
// LAVALINK-FILTER-PLUGIN SPECIFIC
|
|
2805
3706
|
pluginFilters: {
|
|
2806
3707
|
"lavalink-filter-plugin": {
|
|
2807
3708
|
echo: {
|
|
@@ -2827,12 +3728,12 @@ var DEFAULT_FILTER_DATAS = {
|
|
|
2827
3728
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
2828
3729
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
2829
3730
|
},
|
|
2830
|
-
|
|
3731
|
+
normalization: {
|
|
2831
3732
|
// Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
2832
3733
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
2833
3734
|
// "adaptive": true // false
|
|
2834
3735
|
},
|
|
2835
|
-
|
|
3736
|
+
echo: {
|
|
2836
3737
|
// Self-explanatory; provides an echo effect.
|
|
2837
3738
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
2838
3739
|
// "decay": 0.4649 // Float, within the range of 0.0 - 1.0. A value of 1.0 means no decay, and a value of 0.0 means
|
|
@@ -2866,6 +3767,12 @@ var FilterManager = class {
|
|
|
2866
3767
|
tremolo: false,
|
|
2867
3768
|
vibrato: false,
|
|
2868
3769
|
lowPass: false,
|
|
3770
|
+
nodeLinkEcho: false,
|
|
3771
|
+
nodeLinkChorus: false,
|
|
3772
|
+
nodeLinkCompressor: false,
|
|
3773
|
+
nodeLinkHighPass: false,
|
|
3774
|
+
nodeLinkPhaser: false,
|
|
3775
|
+
nodeLinkSpatial: false,
|
|
2869
3776
|
lavalinkFilterPlugin: {
|
|
2870
3777
|
echo: false,
|
|
2871
3778
|
reverb: false
|
|
@@ -2882,21 +3789,6 @@ var FilterManager = class {
|
|
|
2882
3789
|
data = structuredClone(DEFAULT_FILTER_DATAS);
|
|
2883
3790
|
/** The Player assigned to this Filter Manager */
|
|
2884
3791
|
player;
|
|
2885
|
-
get _LManager() {
|
|
2886
|
-
return this.player.LavalinkManager;
|
|
2887
|
-
}
|
|
2888
|
-
/**
|
|
2889
|
-
* Returns whether the plugin validations are enabled or not
|
|
2890
|
-
*/
|
|
2891
|
-
get _checkForPlugins() {
|
|
2892
|
-
return !!this._LManager.options?.autoChecks?.pluginValidations;
|
|
2893
|
-
}
|
|
2894
|
-
/**
|
|
2895
|
-
* Returns whether the source validations are enabled or not
|
|
2896
|
-
*/
|
|
2897
|
-
get _checkForSources() {
|
|
2898
|
-
return !!this._LManager.options?.autoChecks?.sourcesValidations;
|
|
2899
|
-
}
|
|
2900
3792
|
/** The Constructor for the FilterManager */
|
|
2901
3793
|
constructor(player) {
|
|
2902
3794
|
this.player = player;
|
|
@@ -2922,24 +3814,33 @@ var FilterManager = class {
|
|
|
2922
3814
|
if (!this.filters.tremolo) delete sendData.tremolo;
|
|
2923
3815
|
if (!this.filters.vibrato) delete sendData.vibrato;
|
|
2924
3816
|
if (!this.filters.lavalinkFilterPlugin.echo) delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.echo;
|
|
2925
|
-
if (!this.filters.lavalinkFilterPlugin.reverb)
|
|
3817
|
+
if (!this.filters.lavalinkFilterPlugin.reverb)
|
|
3818
|
+
delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
|
|
2926
3819
|
if (!this.filters.lavalinkLavaDspxPlugin.echo) delete sendData.pluginFilters?.echo;
|
|
2927
3820
|
if (!this.filters.lavalinkLavaDspxPlugin.normalization) delete sendData.pluginFilters?.normalization;
|
|
2928
3821
|
if (!this.filters.lavalinkLavaDspxPlugin.highPass) delete sendData.pluginFilters?.["high-pass"];
|
|
2929
3822
|
if (!this.filters.lavalinkLavaDspxPlugin.lowPass) delete sendData.pluginFilters?.["low-pass"];
|
|
2930
|
-
if (sendData.pluginFilters?.["lavalink-filter-plugin"] && Object.values(sendData.pluginFilters?.["lavalink-filter-plugin"]).length === 0)
|
|
3823
|
+
if (sendData.pluginFilters?.["lavalink-filter-plugin"] && Object.values(sendData.pluginFilters?.["lavalink-filter-plugin"]).length === 0)
|
|
3824
|
+
delete sendData.pluginFilters["lavalink-filter-plugin"];
|
|
2931
3825
|
if (sendData.pluginFilters && Object.values(sendData.pluginFilters).length === 0) delete sendData.pluginFilters;
|
|
2932
3826
|
if (!this.filters.lowPass) delete sendData.lowPass;
|
|
2933
3827
|
if (!this.filters.karaoke) delete sendData.karaoke;
|
|
2934
3828
|
if (!this.filters.rotation) delete sendData.rotation;
|
|
2935
3829
|
if (this.filters.audioOutput === "stereo") delete sendData.channelMix;
|
|
3830
|
+
if (!this.filters.nodeLinkEcho) delete sendData.echo;
|
|
3831
|
+
if (!this.filters.nodeLinkChorus) delete sendData.chorus;
|
|
3832
|
+
if (!this.filters.nodeLinkCompressor) delete sendData.compressor;
|
|
3833
|
+
if (!this.filters.nodeLinkHighPass) delete sendData.highPass;
|
|
3834
|
+
if (!this.filters.nodeLinkPhaser) delete sendData.phaser;
|
|
3835
|
+
if (!this.filters.nodeLinkSpatial) delete sendData.spatial;
|
|
2936
3836
|
if (Object.values(this.data.timescale ?? {}).every((v) => v === 1)) delete sendData.timescale;
|
|
2937
3837
|
if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
|
|
2938
3838
|
sendData.equalizer = [...this.equalizerBands];
|
|
2939
3839
|
if (sendData.equalizer.length === 0) delete sendData.equalizer;
|
|
2940
3840
|
for (const key of Object.keys(sendData)) {
|
|
2941
3841
|
if (key === "pluginFilters") {
|
|
2942
|
-
} else if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.(key))
|
|
3842
|
+
} else if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.(key))
|
|
3843
|
+
delete sendData[key];
|
|
2943
3844
|
}
|
|
2944
3845
|
const now = performance.now();
|
|
2945
3846
|
if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
|
|
@@ -2998,6 +3899,12 @@ var FilterManager = class {
|
|
|
2998
3899
|
echo: Object.values(this.data.pluginFilters?.echo || {})?.length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined"
|
|
2999
3900
|
};
|
|
3000
3901
|
this.filters.lowPass = this.privateNot0(this.data.lowPass?.smoothing);
|
|
3902
|
+
this.filters.nodeLinkEcho = this.privateNot0(this.data.echo?.delay) || this.privateNot0(this.data.echo?.feedback) || this.privateNot0(this.data.echo?.mix);
|
|
3903
|
+
this.filters.nodeLinkChorus = this.privateNot0(this.data.chorus?.rate) || this.privateNot0(this.data.chorus?.depth) || this.privateNot0(this.data.chorus?.delay) || this.privateNot0(this.data.chorus?.mix) || this.privateNot0(this.data.chorus?.feedback);
|
|
3904
|
+
this.filters.nodeLinkCompressor = this.privateNot0(this.data.compressor?.threshold) || this.privateNot0(this.data.compressor?.ratio) || this.privateNot0(this.data.compressor?.attack) || this.privateNot0(this.data.compressor?.release) || this.privateNot0(this.data.compressor?.gain);
|
|
3905
|
+
this.filters.nodeLinkHighPass = this.privateNot0(this.data.highPass?.smoothing);
|
|
3906
|
+
this.filters.nodeLinkPhaser = this.privateNot0(this.data.phaser?.stages) || this.privateNot0(this.data.phaser?.rate) || this.privateNot0(this.data.phaser?.depth) || this.privateNot0(this.data.phaser?.feedback) || this.privateNot0(this.data.phaser?.mix) || this.privateNot0(this.data.phaser?.minFrequency) || this.privateNot0(this.data.phaser?.maxFrequency);
|
|
3907
|
+
this.filters.nodeLinkSpatial = this.privateNot0(this.data.spatial?.depth) || this.privateNot0(this.data.spatial?.rate);
|
|
3001
3908
|
this.filters.karaoke = Object.values(this.data.karaoke ?? {}).some((v) => v !== 0);
|
|
3002
3909
|
if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
|
|
3003
3910
|
if (oldFilterTimescale.pitch !== this.data.timescale?.pitch || oldFilterTimescale.rate !== this.data.timescale?.rate || oldFilterTimescale.speed !== this.data.timescale?.speed) {
|
|
@@ -3033,6 +3940,12 @@ var FilterManager = class {
|
|
|
3033
3940
|
this.filters.karaoke = false;
|
|
3034
3941
|
this.filters.karaoke = false;
|
|
3035
3942
|
this.filters.volume = false;
|
|
3943
|
+
this.filters.nodeLinkEcho = false;
|
|
3944
|
+
this.filters.nodeLinkChorus = false;
|
|
3945
|
+
this.filters.nodeLinkCompressor = false;
|
|
3946
|
+
this.filters.nodeLinkHighPass = false;
|
|
3947
|
+
this.filters.nodeLinkPhaser = false;
|
|
3948
|
+
this.filters.nodeLinkSpatial = false;
|
|
3036
3949
|
this.filters.audioOutput = "stereo";
|
|
3037
3950
|
this.data = structuredClone(DEFAULT_FILTER_DATAS);
|
|
3038
3951
|
await this.applyPlayerFilters();
|
|
@@ -3079,8 +3992,10 @@ var FilterManager = class {
|
|
|
3079
3992
|
* ```
|
|
3080
3993
|
*/
|
|
3081
3994
|
async setAudioOutput(type) {
|
|
3082
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("channelMix"))
|
|
3083
|
-
|
|
3995
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("channelMix"))
|
|
3996
|
+
throw new Error("Node#Info#filters does not include the 'channelMix' Filter (Node has it not enable)");
|
|
3997
|
+
if (!type || !audioOutputsData[type])
|
|
3998
|
+
throw "Invalid audio type added, must be 'mono' / 'stereo' / 'left' / 'right'";
|
|
3084
3999
|
this.data = this.data ?? {};
|
|
3085
4000
|
this.data.channelMix = audioOutputsData[type];
|
|
3086
4001
|
this.filters.audioOutput = type;
|
|
@@ -3099,7 +4014,8 @@ var FilterManager = class {
|
|
|
3099
4014
|
* ```
|
|
3100
4015
|
*/
|
|
3101
4016
|
async setSpeed(speed = 1) {
|
|
3102
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4017
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4018
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3103
4019
|
this.data = this.data ?? {};
|
|
3104
4020
|
this.filters.nightcore = false;
|
|
3105
4021
|
this.filters.vaporwave = false;
|
|
@@ -3120,7 +4036,8 @@ var FilterManager = class {
|
|
|
3120
4036
|
* ```
|
|
3121
4037
|
*/
|
|
3122
4038
|
async setPitch(pitch = 1) {
|
|
3123
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4039
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4040
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3124
4041
|
this.data = this.data ?? {};
|
|
3125
4042
|
this.filters.nightcore = false;
|
|
3126
4043
|
this.filters.vaporwave = false;
|
|
@@ -3141,7 +4058,8 @@ var FilterManager = class {
|
|
|
3141
4058
|
* ```
|
|
3142
4059
|
*/
|
|
3143
4060
|
async setRate(rate = 1) {
|
|
3144
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4061
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4062
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3145
4063
|
this.data = this.data ?? {};
|
|
3146
4064
|
this.filters.nightcore = false;
|
|
3147
4065
|
this.filters.vaporwave = false;
|
|
@@ -3165,7 +4083,8 @@ var FilterManager = class {
|
|
|
3165
4083
|
* ```
|
|
3166
4084
|
*/
|
|
3167
4085
|
async toggleRotation(rotationHz = 0.2) {
|
|
3168
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("rotation"))
|
|
4086
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("rotation"))
|
|
4087
|
+
throw new Error("Node#Info#filters does not include the 'rotation' Filter (Node has it not enable)");
|
|
3169
4088
|
this.data = this.data ?? {};
|
|
3170
4089
|
this.data.rotation = this.filters.rotation ? DEFAULT_FILTER_DATAS.rotation : { rotationHz };
|
|
3171
4090
|
this.filters.rotation = !this.filters.rotation;
|
|
@@ -3188,7 +4107,8 @@ var FilterManager = class {
|
|
|
3188
4107
|
* ```
|
|
3189
4108
|
*/
|
|
3190
4109
|
async toggleVibrato(frequency = 10, depth = 1) {
|
|
3191
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("vibrato"))
|
|
4110
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("vibrato"))
|
|
4111
|
+
throw new Error("Node#Info#filters does not include the 'vibrato' Filter (Node has it not enable)");
|
|
3192
4112
|
this.data = this.data ?? {};
|
|
3193
4113
|
this.data.vibrato = this.filters.vibrato ? DEFAULT_FILTER_DATAS.vibrato : { depth, frequency };
|
|
3194
4114
|
this.filters.vibrato = !this.filters.vibrato;
|
|
@@ -3211,7 +4131,8 @@ var FilterManager = class {
|
|
|
3211
4131
|
* ```
|
|
3212
4132
|
*/
|
|
3213
4133
|
async toggleTremolo(frequency = 4, depth = 0.8) {
|
|
3214
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("tremolo"))
|
|
4134
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("tremolo"))
|
|
4135
|
+
throw new Error("Node#Info#filters does not include the 'tremolo' Filter (Node has it not enable)");
|
|
3215
4136
|
this.data = this.data ?? {};
|
|
3216
4137
|
this.data.tremolo = this.filters.tremolo ? DEFAULT_FILTER_DATAS.tremolo : { depth, frequency };
|
|
3217
4138
|
this.filters.tremolo = !this.filters.tremolo;
|
|
@@ -3233,7 +4154,8 @@ var FilterManager = class {
|
|
|
3233
4154
|
* ```
|
|
3234
4155
|
*/
|
|
3235
4156
|
async toggleLowPass(smoothing = 20) {
|
|
3236
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("lowPass"))
|
|
4157
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("lowPass"))
|
|
4158
|
+
throw new Error("Node#Info#filters does not include the 'lowPass' Filter (Node has it not enable)");
|
|
3237
4159
|
this.data = this.data ?? {};
|
|
3238
4160
|
this.data.lowPass = this.filters.lowPass ? DEFAULT_FILTER_DATAS.lowPass : { smoothing };
|
|
3239
4161
|
this.filters.lowPass = !this.filters.lowPass;
|
|
@@ -3260,8 +4182,10 @@ var FilterManager = class {
|
|
|
3260
4182
|
* ```
|
|
3261
4183
|
*/
|
|
3262
4184
|
toggleLowPass: async (boostFactor = 1, cutoffFrequency = 80) => {
|
|
3263
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3264
|
-
|
|
4185
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4186
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4187
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("low-pass"))
|
|
4188
|
+
throw new Error("Node#Info#filters does not include the 'low-pass' Filter (Node has it not enable)");
|
|
3265
4189
|
this.data = this.data ?? {};
|
|
3266
4190
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3267
4191
|
if (this.filters.lavalinkLavaDspxPlugin.lowPass) delete this.data.pluginFilters["low-pass"];
|
|
@@ -3286,8 +4210,10 @@ var FilterManager = class {
|
|
|
3286
4210
|
* ```
|
|
3287
4211
|
*/
|
|
3288
4212
|
toggleHighPass: async (boostFactor = 1, cutoffFrequency = 80) => {
|
|
3289
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3290
|
-
|
|
4213
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4214
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4215
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("high-pass"))
|
|
4216
|
+
throw new Error("Node#Info#filters does not include the 'high-pass' Filter (Node has it not enable)");
|
|
3291
4217
|
this.data = this.data ?? {};
|
|
3292
4218
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3293
4219
|
if (this.filters.lavalinkLavaDspxPlugin.highPass) delete this.data.pluginFilters["high-pass"];
|
|
@@ -3312,8 +4238,12 @@ var FilterManager = class {
|
|
|
3312
4238
|
* ```
|
|
3313
4239
|
*/
|
|
3314
4240
|
toggleNormalization: async (maxAmplitude = 0.75, adaptive = true) => {
|
|
3315
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3316
|
-
|
|
4241
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4242
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4243
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("normalization"))
|
|
4244
|
+
throw new Error(
|
|
4245
|
+
"Node#Info#filters does not include the 'normalization' Filter (Node has it not enable)"
|
|
4246
|
+
);
|
|
3317
4247
|
this.data = this.data ?? {};
|
|
3318
4248
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3319
4249
|
if (this.filters.lavalinkLavaDspxPlugin.normalization) delete this.data.pluginFilters.normalization;
|
|
@@ -3338,8 +4268,10 @@ var FilterManager = class {
|
|
|
3338
4268
|
* ```
|
|
3339
4269
|
*/
|
|
3340
4270
|
toggleEcho: async (decay = 0.5, echoLength = 0.5) => {
|
|
3341
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3342
|
-
|
|
4271
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4272
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4273
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
|
|
4274
|
+
throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable)");
|
|
3343
4275
|
this.data = this.data ?? {};
|
|
3344
4276
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3345
4277
|
if (this.filters.lavalinkLavaDspxPlugin.echo) delete this.data.pluginFilters.echo;
|
|
@@ -3369,8 +4301,12 @@ var FilterManager = class {
|
|
|
3369
4301
|
* ```
|
|
3370
4302
|
*/
|
|
3371
4303
|
toggleEcho: async (delay = 4, decay = 0.8) => {
|
|
3372
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
3373
|
-
|
|
4304
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
4305
|
+
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
|
|
4306
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
|
|
4307
|
+
throw new Error(
|
|
4308
|
+
"Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)"
|
|
4309
|
+
);
|
|
3374
4310
|
this.data = this.data ?? {};
|
|
3375
4311
|
const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["lavalink-filter-plugin"];
|
|
3376
4312
|
this.data.pluginFilters = {
|
|
@@ -3400,8 +4336,12 @@ var FilterManager = class {
|
|
|
3400
4336
|
* ```
|
|
3401
4337
|
*/
|
|
3402
4338
|
toggleReverb: async (delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) => {
|
|
3403
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
3404
|
-
|
|
4339
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
4340
|
+
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
|
|
4341
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("reverb"))
|
|
4342
|
+
throw new Error(
|
|
4343
|
+
"Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)"
|
|
4344
|
+
);
|
|
3405
4345
|
this.data = this.data ?? {};
|
|
3406
4346
|
const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["lavalink-filter-plugin"];
|
|
3407
4347
|
this.data.pluginFilters = {
|
|
@@ -3433,7 +4373,8 @@ var FilterManager = class {
|
|
|
3433
4373
|
* ```
|
|
3434
4374
|
*/
|
|
3435
4375
|
async toggleNightcore(speed = 1.289999523162842, pitch = 1.289999523162842, rate = 0.9365999523162842) {
|
|
3436
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4376
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4377
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3437
4378
|
this.data = this.data ?? {};
|
|
3438
4379
|
this.data.timescale = this.filters.nightcore ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
|
|
3439
4380
|
this.filters.nightcore = !this.filters.nightcore;
|
|
@@ -3459,7 +4400,8 @@ var FilterManager = class {
|
|
|
3459
4400
|
* ```
|
|
3460
4401
|
*/
|
|
3461
4402
|
async toggleVaporwave(speed = 0.8500000238418579, pitch = 0.800000011920929, rate = 1) {
|
|
3462
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4403
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4404
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3463
4405
|
this.data = this.data ?? {};
|
|
3464
4406
|
this.data.timescale = this.filters.vaporwave ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
|
|
3465
4407
|
this.filters.vaporwave = !this.filters.vaporwave;
|
|
@@ -3486,7 +4428,8 @@ var FilterManager = class {
|
|
|
3486
4428
|
* ```
|
|
3487
4429
|
*/
|
|
3488
4430
|
async toggleKaraoke(level = 1, monoLevel = 1, filterBand = 220, filterWidth = 100) {
|
|
3489
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("karaoke"))
|
|
4431
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("karaoke"))
|
|
4432
|
+
throw new Error("Node#Info#filters does not include the 'karaoke' Filter (Node has it not enable)");
|
|
3490
4433
|
this.data = this.data ?? {};
|
|
3491
4434
|
this.data.karaoke = this.filters.karaoke ? DEFAULT_FILTER_DATAS.karaoke : { level, monoLevel, filterBand, filterWidth };
|
|
3492
4435
|
this.filters.karaoke = !this.filters.karaoke;
|
|
@@ -3543,7 +4486,8 @@ var FilterManager = class {
|
|
|
3543
4486
|
*/
|
|
3544
4487
|
async setEQ(bands) {
|
|
3545
4488
|
if (!Array.isArray(bands)) bands = [bands];
|
|
3546
|
-
if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
|
|
4489
|
+
if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
|
|
4490
|
+
throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
|
|
3547
4491
|
for (const { band, gain } of bands) this.equalizerBands[band] = { band, gain };
|
|
3548
4492
|
if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
|
|
3549
4493
|
const now = performance.now();
|
|
@@ -3709,8 +4653,14 @@ var Queue = class {
|
|
|
3709
4653
|
this.QueueSaver = QueueSaver2;
|
|
3710
4654
|
this.options.maxPreviousTracks = this.QueueSaver?.options?.maxPreviousTracks ?? this.options.maxPreviousTracks;
|
|
3711
4655
|
this.current = this.managerUtils.isTrack(data.current) ? data.current : null;
|
|
3712
|
-
this.previous = Array.isArray(data.previous) && data.previous.some(
|
|
3713
|
-
|
|
4656
|
+
this.previous = Array.isArray(data.previous) && data.previous.some(
|
|
4657
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4658
|
+
) ? data.previous.filter(
|
|
4659
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4660
|
+
) : [];
|
|
4661
|
+
this.tracks = Array.isArray(data.tracks) && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.tracks.filter(
|
|
4662
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4663
|
+
) : [];
|
|
3714
4664
|
Object.defineProperty(this, QueueSymbol, { configurable: true, value: true });
|
|
3715
4665
|
}
|
|
3716
4666
|
/**
|
|
@@ -3721,7 +4671,8 @@ var Queue = class {
|
|
|
3721
4671
|
* Save the current cached Queue on the database/server (overides the server)
|
|
3722
4672
|
*/
|
|
3723
4673
|
save: async () => {
|
|
3724
|
-
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4674
|
+
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4675
|
+
this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
|
|
3725
4676
|
return await this.QueueSaver.set(this.guildId, this.utils.toJSON());
|
|
3726
4677
|
},
|
|
3727
4678
|
/**
|
|
@@ -3731,9 +4682,28 @@ var Queue = class {
|
|
|
3731
4682
|
sync: async (override = true, dontSyncCurrent = true) => {
|
|
3732
4683
|
const data = await this.QueueSaver.get(this.guildId);
|
|
3733
4684
|
if (!data) throw new Error(`No data found to sync for guildId: ${this.guildId}`);
|
|
3734
|
-
if (!dontSyncCurrent && !this.current && this.managerUtils.isTrack(data.current))
|
|
3735
|
-
|
|
3736
|
-
if (Array.isArray(data.
|
|
4685
|
+
if (!dontSyncCurrent && !this.current && this.managerUtils.isTrack(data.current))
|
|
4686
|
+
this.current = data.current;
|
|
4687
|
+
if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(
|
|
4688
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4689
|
+
))
|
|
4690
|
+
this.tracks.splice(
|
|
4691
|
+
override ? 0 : this.tracks.length,
|
|
4692
|
+
override ? this.tracks.length : 0,
|
|
4693
|
+
...data.tracks.filter(
|
|
4694
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4695
|
+
)
|
|
4696
|
+
);
|
|
4697
|
+
if (Array.isArray(data.previous) && data?.previous.length && data.previous.some(
|
|
4698
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4699
|
+
))
|
|
4700
|
+
this.previous.splice(
|
|
4701
|
+
0,
|
|
4702
|
+
override ? this.tracks.length : 0,
|
|
4703
|
+
...data.previous.filter(
|
|
4704
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4705
|
+
)
|
|
4706
|
+
);
|
|
3737
4707
|
await this.utils.save();
|
|
3738
4708
|
return;
|
|
3739
4709
|
},
|
|
@@ -3744,7 +4714,8 @@ var Queue = class {
|
|
|
3744
4714
|
* @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
|
|
3745
4715
|
*/
|
|
3746
4716
|
toJSON: () => {
|
|
3747
|
-
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4717
|
+
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4718
|
+
this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
|
|
3748
4719
|
return {
|
|
3749
4720
|
current: this.current ? { ...this.current } : null,
|
|
3750
4721
|
previous: this.previous ? [...this.previous] : [],
|
|
@@ -3756,25 +4727,28 @@ var Queue = class {
|
|
|
3756
4727
|
* @returns {number}
|
|
3757
4728
|
*/
|
|
3758
4729
|
totalDuration: () => {
|
|
3759
|
-
return this.tracks.reduce(
|
|
4730
|
+
return this.tracks.reduce(
|
|
4731
|
+
(acc, cur) => acc + (cur.info.duration || 0),
|
|
4732
|
+
this.current?.info.duration || 0
|
|
4733
|
+
);
|
|
3760
4734
|
},
|
|
3761
4735
|
/**
|
|
3762
4736
|
* Find tracks in the queue matching specific criteria.
|
|
3763
4737
|
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
3764
4738
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
3765
4739
|
* @returns Array of matching tracks with their indexes
|
|
3766
|
-
*
|
|
4740
|
+
*
|
|
3767
4741
|
* @example
|
|
3768
4742
|
* ```ts
|
|
3769
4743
|
* // Find by author
|
|
3770
4744
|
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
3771
|
-
*
|
|
4745
|
+
*
|
|
3772
4746
|
* // Find by duration range (5-10 minutes)
|
|
3773
4747
|
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
3774
|
-
*
|
|
4748
|
+
*
|
|
3775
4749
|
* // Find by title (partial match)
|
|
3776
4750
|
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
3777
|
-
*
|
|
4751
|
+
*
|
|
3778
4752
|
* // Custom predicate
|
|
3779
4753
|
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
3780
4754
|
* ```
|
|
@@ -3822,7 +4796,7 @@ var Queue = class {
|
|
|
3822
4796
|
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
3823
4797
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
3824
4798
|
* @returns First matching track with its index, or null if not found
|
|
3825
|
-
*
|
|
4799
|
+
*
|
|
3826
4800
|
* @example
|
|
3827
4801
|
* ```ts
|
|
3828
4802
|
* // Find first track by author
|
|
@@ -3830,7 +4804,7 @@ var Queue = class {
|
|
|
3830
4804
|
* if (track) {
|
|
3831
4805
|
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
3832
4806
|
* }
|
|
3833
|
-
*
|
|
4807
|
+
*
|
|
3834
4808
|
* // Find with custom predicate
|
|
3835
4809
|
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
3836
4810
|
* ```
|
|
@@ -3855,7 +4829,8 @@ var Queue = class {
|
|
|
3855
4829
|
[this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
|
|
3856
4830
|
}
|
|
3857
4831
|
}
|
|
3858
|
-
if (typeof this.queueChanges?.shuffled === "function")
|
|
4832
|
+
if (typeof this.queueChanges?.shuffled === "function")
|
|
4833
|
+
this.queueChanges.shuffled(this.guildId, oldStored, this.utils.toJSON());
|
|
3859
4834
|
await this.utils.save();
|
|
3860
4835
|
return this.tracks.length;
|
|
3861
4836
|
}
|
|
@@ -3867,14 +4842,27 @@ var Queue = class {
|
|
|
3867
4842
|
*/
|
|
3868
4843
|
async add(TrackOrTracks, index) {
|
|
3869
4844
|
if (typeof index === "number" && index >= 0 && index < this.tracks.length) {
|
|
3870
|
-
return await this.splice(
|
|
4845
|
+
return await this.splice(
|
|
4846
|
+
index,
|
|
4847
|
+
0,
|
|
4848
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
4849
|
+
);
|
|
3871
4850
|
}
|
|
3872
4851
|
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
3873
|
-
this.tracks.push(
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
4852
|
+
this.tracks.push(
|
|
4853
|
+
...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
4854
|
+
);
|
|
4855
|
+
if (typeof this.queueChanges?.tracksAdd === "function")
|
|
4856
|
+
try {
|
|
4857
|
+
this.queueChanges.tracksAdd(
|
|
4858
|
+
this.guildId,
|
|
4859
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
|
|
4860
|
+
this.tracks.length,
|
|
4861
|
+
oldStored,
|
|
4862
|
+
this.utils.toJSON()
|
|
4863
|
+
);
|
|
4864
|
+
} catch {
|
|
4865
|
+
}
|
|
3878
4866
|
await this.utils.save();
|
|
3879
4867
|
return this.tracks.length;
|
|
3880
4868
|
}
|
|
@@ -3891,19 +4879,27 @@ var Queue = class {
|
|
|
3891
4879
|
if (TrackOrTracks) return await this.add(TrackOrTracks);
|
|
3892
4880
|
return null;
|
|
3893
4881
|
}
|
|
3894
|
-
if (TrackOrTracks && typeof this.queueChanges?.tracksAdd === "function")
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
4882
|
+
if (TrackOrTracks && typeof this.queueChanges?.tracksAdd === "function")
|
|
4883
|
+
try {
|
|
4884
|
+
this.queueChanges.tracksAdd(
|
|
4885
|
+
this.guildId,
|
|
4886
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
|
|
4887
|
+
index,
|
|
4888
|
+
oldStored,
|
|
4889
|
+
this.utils.toJSON()
|
|
4890
|
+
);
|
|
4891
|
+
} catch {
|
|
4892
|
+
}
|
|
3898
4893
|
const spliced = TrackOrTracks ? this.tracks.splice(
|
|
3899
4894
|
index,
|
|
3900
4895
|
amount,
|
|
3901
4896
|
...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
3902
4897
|
) : this.tracks.splice(index, amount);
|
|
3903
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
4898
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4899
|
+
try {
|
|
4900
|
+
this.queueChanges.tracksRemoved(this.guildId, spliced, index, oldStored, this.utils.toJSON());
|
|
4901
|
+
} catch {
|
|
4902
|
+
}
|
|
3907
4903
|
await this.utils.save();
|
|
3908
4904
|
return spliced.length === 1 ? spliced[0] : spliced;
|
|
3909
4905
|
}
|
|
@@ -3948,10 +4944,17 @@ var Queue = class {
|
|
|
3948
4944
|
const toRemove2 = this.tracks[removeQueryTrack];
|
|
3949
4945
|
if (!toRemove2) return null;
|
|
3950
4946
|
const removed2 = this.tracks.splice(removeQueryTrack, 1);
|
|
3951
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
4947
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4948
|
+
try {
|
|
4949
|
+
this.queueChanges.tracksRemoved(
|
|
4950
|
+
this.guildId,
|
|
4951
|
+
removed2,
|
|
4952
|
+
removeQueryTrack,
|
|
4953
|
+
oldStored,
|
|
4954
|
+
this.utils.toJSON()
|
|
4955
|
+
);
|
|
4956
|
+
} catch {
|
|
4957
|
+
}
|
|
3955
4958
|
await this.utils.save();
|
|
3956
4959
|
return { removed: removed2 };
|
|
3957
4960
|
}
|
|
@@ -3965,16 +4968,25 @@ var Queue = class {
|
|
|
3965
4968
|
}
|
|
3966
4969
|
}
|
|
3967
4970
|
if (!removed3.length) return null;
|
|
3968
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4971
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4972
|
+
try {
|
|
4973
|
+
this.queueChanges.tracksRemoved(
|
|
4974
|
+
this.guildId,
|
|
4975
|
+
removed3,
|
|
4976
|
+
removeQueryTrack,
|
|
4977
|
+
oldStored,
|
|
4978
|
+
this.utils.toJSON()
|
|
4979
|
+
);
|
|
4980
|
+
} catch {
|
|
4981
|
+
}
|
|
3972
4982
|
await this.utils.save();
|
|
3973
4983
|
return { removed: removed3 };
|
|
3974
4984
|
}
|
|
3975
|
-
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(
|
|
3976
|
-
(
|
|
3977
|
-
|
|
4985
|
+
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(
|
|
4986
|
+
({ v, i }) => removeQueryTrack.find(
|
|
4987
|
+
(t) => typeof t === "number" && t === i || typeof t === "object" && (t.encoded && t.encoded === v.encoded || t.info?.identifier && t.info.identifier === v.info?.identifier || t.info?.uri && t.info.uri === v.info?.uri || t.info?.title && t.info.title === v.info?.title || t.info?.isrc && t.info.isrc === v.info?.isrc || t.info?.artworkUrl && t.info.artworkUrl === v.info?.artworkUrl)
|
|
4988
|
+
)
|
|
4989
|
+
);
|
|
3978
4990
|
if (!tracksToRemove.length) return null;
|
|
3979
4991
|
const removed2 = [];
|
|
3980
4992
|
tracksToRemove.sort((a, b) => b.i - a.i);
|
|
@@ -3983,10 +4995,17 @@ var Queue = class {
|
|
|
3983
4995
|
removed2.unshift(...this.tracks.splice(i, 1));
|
|
3984
4996
|
}
|
|
3985
4997
|
}
|
|
3986
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4998
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4999
|
+
try {
|
|
5000
|
+
this.queueChanges.tracksRemoved(
|
|
5001
|
+
this.guildId,
|
|
5002
|
+
removed2,
|
|
5003
|
+
tracksToRemove.map((v) => v.i),
|
|
5004
|
+
oldStored,
|
|
5005
|
+
this.utils.toJSON()
|
|
5006
|
+
);
|
|
5007
|
+
} catch {
|
|
5008
|
+
}
|
|
3990
5009
|
await this.utils.save();
|
|
3991
5010
|
return { removed: removed2 };
|
|
3992
5011
|
}
|
|
@@ -3995,10 +5014,11 @@ var Queue = class {
|
|
|
3995
5014
|
);
|
|
3996
5015
|
if (toRemove < 0) return null;
|
|
3997
5016
|
const removed = this.tracks.splice(toRemove, 1);
|
|
3998
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
5017
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
5018
|
+
try {
|
|
5019
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
|
|
5020
|
+
} catch {
|
|
5021
|
+
}
|
|
4002
5022
|
await this.utils.save();
|
|
4003
5023
|
return { removed };
|
|
4004
5024
|
}
|
|
@@ -4025,7 +5045,7 @@ var Queue = class {
|
|
|
4025
5045
|
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
4026
5046
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
4027
5047
|
* @returns Array of matching tracks with their indexes
|
|
4028
|
-
*
|
|
5048
|
+
*
|
|
4029
5049
|
* @example
|
|
4030
5050
|
* ```ts
|
|
4031
5051
|
* // Use the new method instead:
|
|
@@ -4041,7 +5061,7 @@ var Queue = class {
|
|
|
4041
5061
|
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
4042
5062
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
4043
5063
|
* @returns First matching track with its index, or null if not found
|
|
4044
|
-
*
|
|
5064
|
+
*
|
|
4045
5065
|
* @example
|
|
4046
5066
|
* ```ts
|
|
4047
5067
|
* // Use the new method instead:
|
|
@@ -4057,15 +5077,15 @@ var Queue = class {
|
|
|
4057
5077
|
* @param sortBy Property to sort by or custom comparator function
|
|
4058
5078
|
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4059
5079
|
* @returns The queue instance for chaining
|
|
4060
|
-
*
|
|
5080
|
+
*
|
|
4061
5081
|
* @example
|
|
4062
5082
|
* ```ts
|
|
4063
5083
|
* // Sort by duration (shortest first)
|
|
4064
5084
|
* await player.queue.sortBy("duration", "asc");
|
|
4065
|
-
*
|
|
5085
|
+
*
|
|
4066
5086
|
* // Sort by title alphabetically (Z-A)
|
|
4067
5087
|
* await player.queue.sortBy("title", "desc");
|
|
4068
|
-
*
|
|
5088
|
+
*
|
|
4069
5089
|
* // Custom sorting
|
|
4070
5090
|
* await player.queue.sortBy((a, b) => {
|
|
4071
5091
|
* return a.info.title.localeCompare(b.info.title);
|
|
@@ -4073,7 +5093,6 @@ var Queue = class {
|
|
|
4073
5093
|
* ```
|
|
4074
5094
|
*/
|
|
4075
5095
|
async sortBy(sortBy, order = "asc") {
|
|
4076
|
-
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
4077
5096
|
if (typeof sortBy === "function") {
|
|
4078
5097
|
this.tracks.sort(sortBy);
|
|
4079
5098
|
} else {
|
|
@@ -4104,16 +5123,16 @@ var Queue = class {
|
|
|
4104
5123
|
* @param sortBy Property to sort by or custom comparator function
|
|
4105
5124
|
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4106
5125
|
* @returns A new sorted array of tracks (does not modify the queue)
|
|
4107
|
-
*
|
|
5126
|
+
*
|
|
4108
5127
|
* @example
|
|
4109
5128
|
* ```ts
|
|
4110
5129
|
* // Get sorted copy by duration (shortest first)
|
|
4111
5130
|
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
4112
5131
|
* // Original queue remains unchanged
|
|
4113
|
-
*
|
|
5132
|
+
*
|
|
4114
5133
|
* // Get sorted copy by title alphabetically (Z-A)
|
|
4115
5134
|
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
4116
|
-
*
|
|
5135
|
+
*
|
|
4117
5136
|
* // Custom sorting
|
|
4118
5137
|
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
4119
5138
|
* return a.info.title.localeCompare(b.info.title);
|
|
@@ -4149,12 +5168,12 @@ var Queue = class {
|
|
|
4149
5168
|
* @param start Start index (inclusive)
|
|
4150
5169
|
* @param end End index (exclusive)
|
|
4151
5170
|
* @returns Array of tracks in the specified range
|
|
4152
|
-
*
|
|
5171
|
+
*
|
|
4153
5172
|
* @example
|
|
4154
5173
|
* ```ts
|
|
4155
5174
|
* // Get tracks 5-15
|
|
4156
5175
|
* const tracks = player.queue.getTracks(5, 15);
|
|
4157
|
-
*
|
|
5176
|
+
*
|
|
4158
5177
|
* // Get first 10 tracks
|
|
4159
5178
|
* const firstTen = player.queue.getTracks(0, 10);
|
|
4160
5179
|
* ```
|
|
@@ -4242,7 +5261,8 @@ var Player = class {
|
|
|
4242
5261
|
* @param LavalinkManager
|
|
4243
5262
|
*/
|
|
4244
5263
|
constructor(options, LavalinkManager2, dontEmitPlayerCreateEvent) {
|
|
4245
|
-
if (typeof options?.customData === "object")
|
|
5264
|
+
if (typeof options?.customData === "object")
|
|
5265
|
+
for (const [key, value] of Object.entries(options.customData)) this.set(key, value);
|
|
4246
5266
|
this.options = options;
|
|
4247
5267
|
this.filterManager = new FilterManager(this);
|
|
4248
5268
|
this.LavalinkManager = LavalinkManager2;
|
|
@@ -4261,14 +5281,30 @@ var Player = class {
|
|
|
4261
5281
|
const least = this.LavalinkManager.nodeManager.leastUsedNodes();
|
|
4262
5282
|
this.node = least.filter((v) => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || least[0] || null;
|
|
4263
5283
|
}
|
|
4264
|
-
if (!this.node)
|
|
5284
|
+
if (!this.node)
|
|
5285
|
+
throw new Error(
|
|
5286
|
+
"No available Node was found, please add a LavalinkNode to the Manager via Manager.NodeManager#createNode"
|
|
5287
|
+
);
|
|
4265
5288
|
if (typeof options.volume === "number" && !isNaN(options.volume)) this.volume = Number(options.volume);
|
|
4266
5289
|
this.volume = Math.round(Math.max(Math.min(this.volume, 1e3), 0));
|
|
4267
|
-
this.lavalinkVolume = Math.round(
|
|
4268
|
-
|
|
4269
|
-
|
|
5290
|
+
this.lavalinkVolume = Math.round(
|
|
5291
|
+
Math.max(
|
|
5292
|
+
Math.min(
|
|
5293
|
+
Math.round(
|
|
5294
|
+
this.LavalinkManager.options.playerOptions.volumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
|
|
5295
|
+
),
|
|
5296
|
+
1e3
|
|
5297
|
+
),
|
|
5298
|
+
0
|
|
5299
|
+
)
|
|
5300
|
+
);
|
|
4270
5301
|
if (!dontEmitPlayerCreateEvent) this.LavalinkManager.emit("playerCreate", this);
|
|
4271
|
-
this.queue = new Queue(
|
|
5302
|
+
this.queue = new Queue(
|
|
5303
|
+
this.guildId,
|
|
5304
|
+
{},
|
|
5305
|
+
new QueueSaver(this.LavalinkManager.options.queueOptions),
|
|
5306
|
+
this.LavalinkManager.options.queueOptions
|
|
5307
|
+
);
|
|
4272
5308
|
}
|
|
4273
5309
|
/**
|
|
4274
5310
|
* Set custom data.
|
|
@@ -4332,20 +5368,30 @@ var Player = class {
|
|
|
4332
5368
|
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
4333
5369
|
if (options && "clientTrack" in options) delete options.clientTrack;
|
|
4334
5370
|
if (options && "track" in options) delete options.track;
|
|
4335
|
-
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5371
|
+
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5372
|
+
return this.play(options);
|
|
4336
5373
|
return this;
|
|
4337
5374
|
}
|
|
4338
5375
|
}
|
|
4339
|
-
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
5376
|
+
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
5377
|
+
options.clientTrack.userData = {
|
|
5378
|
+
...typeof options?.clientTrack?.requester === "object" ? {
|
|
5379
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5380
|
+
options?.clientTrack?.requester || {}
|
|
5381
|
+
)
|
|
5382
|
+
} : {},
|
|
5383
|
+
...options?.clientTrack.userData,
|
|
5384
|
+
...options.track?.userData
|
|
5385
|
+
};
|
|
4344
5386
|
options.track = {
|
|
4345
5387
|
encoded: options.clientTrack?.encoded,
|
|
4346
5388
|
requester: options.clientTrack?.requester,
|
|
4347
|
-
userData: options.clientTrack?.userData
|
|
5389
|
+
userData: options.clientTrack?.userData,
|
|
5390
|
+
audioTrackId: options.track?.audioTrackId ?? options.clientTrack?.audioTrackId
|
|
4348
5391
|
};
|
|
5392
|
+
if (options.track.audioTrackId && !this.node.isNodeLink()) {
|
|
5393
|
+
delete options.track.audioTrackId;
|
|
5394
|
+
}
|
|
4349
5395
|
}
|
|
4350
5396
|
if (options?.track?.encoded || options?.track?.identifier) {
|
|
4351
5397
|
this.queue.current = options.clientTrack || null;
|
|
@@ -4353,35 +5399,48 @@ var Player = class {
|
|
|
4353
5399
|
if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
|
|
4354
5400
|
this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
|
|
4355
5401
|
let vol = Number(this.volume);
|
|
4356
|
-
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5402
|
+
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5403
|
+
vol *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
|
|
4357
5404
|
this.lavalinkVolume = Math.round(vol);
|
|
4358
5405
|
options.volume = this.lavalinkVolume;
|
|
4359
5406
|
}
|
|
4360
|
-
const track = Object.fromEntries(
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
5407
|
+
const track = Object.fromEntries(
|
|
5408
|
+
Object.entries({
|
|
5409
|
+
encoded: options.track.encoded,
|
|
5410
|
+
identifier: options.track.identifier,
|
|
5411
|
+
userData: {
|
|
5412
|
+
...typeof options?.track?.requester === "object" ? {
|
|
5413
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5414
|
+
options?.track?.requester || {}
|
|
5415
|
+
)
|
|
5416
|
+
} : {},
|
|
5417
|
+
...options.track.userData
|
|
5418
|
+
},
|
|
5419
|
+
audioTrackId: options.track.audioTrackId
|
|
5420
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5421
|
+
);
|
|
4368
5422
|
this._emitDebugEvent("PlayerPlayWithTrackReplace" /* PlayerPlayWithTrackReplace */, {
|
|
4369
5423
|
state: "log",
|
|
4370
5424
|
message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
|
|
4371
5425
|
functionLayer: "Player > play()"
|
|
4372
5426
|
});
|
|
5427
|
+
if (track.audioTrackId && !this.node.isNodeLink()) {
|
|
5428
|
+
delete track.audioTrackId;
|
|
5429
|
+
}
|
|
4373
5430
|
return this.node.updatePlayer({
|
|
4374
5431
|
guildId: this.guildId,
|
|
4375
5432
|
noReplace: false,
|
|
4376
|
-
playerOptions: Object.fromEntries(
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
5433
|
+
playerOptions: Object.fromEntries(
|
|
5434
|
+
Object.entries({
|
|
5435
|
+
track,
|
|
5436
|
+
position: options.position ?? void 0,
|
|
5437
|
+
paused: options.paused ?? void 0,
|
|
5438
|
+
endTime: options?.endTime ?? void 0,
|
|
5439
|
+
filters: options?.filters ?? void 0,
|
|
5440
|
+
volume: options.volume ?? this.lavalinkVolume ?? void 0,
|
|
5441
|
+
voice: options.voice ?? void 0
|
|
5442
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5443
|
+
)
|
|
4385
5444
|
});
|
|
4386
5445
|
}
|
|
4387
5446
|
if (!this.queue.current && this.queue.tracks.length) await queueTrackEnd(this);
|
|
@@ -4393,11 +5452,16 @@ var Player = class {
|
|
|
4393
5452
|
});
|
|
4394
5453
|
try {
|
|
4395
5454
|
await this.queue.current.resolve(this);
|
|
4396
|
-
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
5455
|
+
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
5456
|
+
this.queue.current.userData = {
|
|
5457
|
+
...typeof this.queue.current?.requester === "object" ? {
|
|
5458
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5459
|
+
this.queue.current?.requester || {}
|
|
5460
|
+
)
|
|
5461
|
+
} : {},
|
|
5462
|
+
...this.queue.current?.userData,
|
|
5463
|
+
...options.track?.userData
|
|
5464
|
+
};
|
|
4401
5465
|
} catch (error) {
|
|
4402
5466
|
this._emitDebugEvent("PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
|
|
4403
5467
|
state: "error",
|
|
@@ -4409,7 +5473,8 @@ var Player = class {
|
|
|
4409
5473
|
if (options && "clientTrack" in options) delete options.clientTrack;
|
|
4410
5474
|
if (options && "track" in options) delete options.track;
|
|
4411
5475
|
await queueTrackEnd(this, true);
|
|
4412
|
-
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5476
|
+
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5477
|
+
return this.play(options);
|
|
4413
5478
|
return this;
|
|
4414
5479
|
}
|
|
4415
5480
|
}
|
|
@@ -4417,31 +5482,46 @@ var Player = class {
|
|
|
4417
5482
|
if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
|
|
4418
5483
|
this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
|
|
4419
5484
|
let vol = Number(this.volume);
|
|
4420
|
-
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5485
|
+
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5486
|
+
vol *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
|
|
4421
5487
|
this.lavalinkVolume = Math.round(vol);
|
|
4422
5488
|
options.volume = this.lavalinkVolume;
|
|
4423
5489
|
}
|
|
4424
|
-
const finalOptions = Object.fromEntries(
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
5490
|
+
const finalOptions = Object.fromEntries(
|
|
5491
|
+
Object.entries({
|
|
5492
|
+
track: {
|
|
5493
|
+
encoded: this.queue.current?.encoded || null,
|
|
5494
|
+
// identifier: options.identifier,
|
|
5495
|
+
userData: {
|
|
5496
|
+
...typeof this.queue.current?.requester === "object" ? {
|
|
5497
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5498
|
+
this.queue.current?.requester || {}
|
|
5499
|
+
)
|
|
5500
|
+
} : {},
|
|
5501
|
+
...options?.track?.userData,
|
|
5502
|
+
...this.queue.current?.userData
|
|
5503
|
+
},
|
|
5504
|
+
audioTrackId: options?.track?.audioTrackId
|
|
5505
|
+
},
|
|
5506
|
+
volume: this.lavalinkVolume,
|
|
5507
|
+
position: options?.position ?? 0,
|
|
5508
|
+
endTime: options?.endTime ?? void 0,
|
|
5509
|
+
filters: options?.filters ?? void 0,
|
|
5510
|
+
paused: options?.paused ?? void 0,
|
|
5511
|
+
voice: options?.voice ?? void 0
|
|
5512
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5513
|
+
);
|
|
5514
|
+
if (finalOptions.track.audioTrackId && !this.node.isNodeLink()) {
|
|
5515
|
+
delete finalOptions.track.audioTrackId;
|
|
5516
|
+
}
|
|
5517
|
+
if (typeof finalOptions.position !== "undefined" && isNaN(finalOptions.position) || typeof finalOptions.position === "number" && finalOptions.position < 0 || typeof finalOptions.position === "number" && this.queue.current.info.duration > 0 && finalOptions.position >= this.queue.current.info.duration)
|
|
5518
|
+
throw new Error("PlayerOption#position must be a positive number, less than track's duration");
|
|
5519
|
+
if (typeof finalOptions.volume !== "undefined" && isNaN(finalOptions.volume) || typeof finalOptions.volume === "number" && finalOptions.volume < 0)
|
|
5520
|
+
throw new Error("PlayerOption#volume must be a positive number");
|
|
5521
|
+
if (typeof finalOptions.endTime !== "undefined" && isNaN(finalOptions.endTime) || typeof finalOptions.endTime === "number" && finalOptions.endTime < 0 || typeof finalOptions.endTime === "number" && this.queue.current.info.duration > 0 && finalOptions.endTime >= this.queue.current.info.duration)
|
|
5522
|
+
throw new Error("PlayerOption#endTime must be a positive number, less than track's duration");
|
|
5523
|
+
if (typeof finalOptions.position === "number" && typeof finalOptions.endTime === "number" && finalOptions.endTime < finalOptions.position)
|
|
5524
|
+
throw new Error("PlayerOption#endTime must be bigger than PlayerOption#position");
|
|
4445
5525
|
const now = performance.now();
|
|
4446
5526
|
await this.node.updatePlayer({
|
|
4447
5527
|
guildId: this.guildId,
|
|
@@ -4460,9 +5540,17 @@ var Player = class {
|
|
|
4460
5540
|
volume = Number(volume);
|
|
4461
5541
|
if (isNaN(volume)) throw new TypeError("Volume must be a number.");
|
|
4462
5542
|
this.volume = Math.round(Math.max(Math.min(volume, 1e3), 0));
|
|
4463
|
-
this.lavalinkVolume = Math.round(
|
|
4464
|
-
|
|
4465
|
-
|
|
5543
|
+
this.lavalinkVolume = Math.round(
|
|
5544
|
+
Math.max(
|
|
5545
|
+
Math.min(
|
|
5546
|
+
Math.round(
|
|
5547
|
+
this.LavalinkManager.options.playerOptions.volumeDecrementer && !ignoreVolumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
|
|
5548
|
+
),
|
|
5549
|
+
1e3
|
|
5550
|
+
),
|
|
5551
|
+
0
|
|
5552
|
+
)
|
|
5553
|
+
);
|
|
4466
5554
|
const now = performance.now();
|
|
4467
5555
|
if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
|
|
4468
5556
|
this._emitDebugEvent("PlayerVolumeAsFilter" /* PlayerVolumeAsFilter */, {
|
|
@@ -4470,9 +5558,15 @@ var Player = class {
|
|
|
4470
5558
|
message: `Player Volume was set as a Filter, because LavalinkManager option "playerOptions.applyVolumeAsFilter" is true`,
|
|
4471
5559
|
functionLayer: "Player > setVolume()"
|
|
4472
5560
|
});
|
|
4473
|
-
await this.node.updatePlayer({
|
|
5561
|
+
await this.node.updatePlayer({
|
|
5562
|
+
guildId: this.guildId,
|
|
5563
|
+
playerOptions: { filters: { volume: this.lavalinkVolume / 100 } }
|
|
5564
|
+
});
|
|
4474
5565
|
} else {
|
|
4475
|
-
await this.node.updatePlayer({
|
|
5566
|
+
await this.node.updatePlayer({
|
|
5567
|
+
guildId: this.guildId,
|
|
5568
|
+
playerOptions: { volume: this.lavalinkVolume }
|
|
5569
|
+
});
|
|
4476
5570
|
}
|
|
4477
5571
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4478
5572
|
return this;
|
|
@@ -4556,8 +5650,10 @@ var Player = class {
|
|
|
4556
5650
|
if (!this.queue.current) return void 0;
|
|
4557
5651
|
position = Number(position);
|
|
4558
5652
|
if (isNaN(position)) throw new RangeError("Position must be a number.");
|
|
4559
|
-
if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream)
|
|
4560
|
-
|
|
5653
|
+
if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream)
|
|
5654
|
+
throw new RangeError("Current Track is not seekable / a stream");
|
|
5655
|
+
if (position < 0 || position > this.queue.current.info.duration)
|
|
5656
|
+
position = Math.max(Math.min(position, this.queue.current.info.duration), 0);
|
|
4561
5657
|
this.lastPositionChange = Date.now();
|
|
4562
5658
|
this.lastPosition = position;
|
|
4563
5659
|
const now = performance.now();
|
|
@@ -4570,7 +5666,8 @@ var Player = class {
|
|
|
4570
5666
|
* @param repeatMode
|
|
4571
5667
|
*/
|
|
4572
5668
|
async setRepeatMode(repeatMode) {
|
|
4573
|
-
if (!["off", "track", "queue"].includes(repeatMode))
|
|
5669
|
+
if (!["off", "track", "queue"].includes(repeatMode))
|
|
5670
|
+
throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
|
|
4574
5671
|
this.repeatMode = repeatMode;
|
|
4575
5672
|
return this;
|
|
4576
5673
|
}
|
|
@@ -4579,7 +5676,8 @@ var Player = class {
|
|
|
4579
5676
|
* @param amount provide the index of the next track to skip to
|
|
4580
5677
|
*/
|
|
4581
5678
|
async skip(skipTo = 0, throwError = true) {
|
|
4582
|
-
if (!this.queue.tracks.length && (throwError || typeof skipTo === "boolean" && skipTo === true))
|
|
5679
|
+
if (!this.queue.tracks.length && (throwError || typeof skipTo === "boolean" && skipTo === true))
|
|
5680
|
+
throw new RangeError("Can't skip more than the queue size");
|
|
4583
5681
|
if (typeof skipTo === "number" && skipTo > 1) {
|
|
4584
5682
|
if (skipTo > this.queue.tracks.length) throw new RangeError("Can't skip more than the queue size");
|
|
4585
5683
|
await this.queue.splice(0, skipTo - 1);
|
|
@@ -4587,7 +5685,10 @@ var Player = class {
|
|
|
4587
5685
|
if (!this.playing && !this.queue.current) return this.play(), this;
|
|
4588
5686
|
const now = performance.now();
|
|
4589
5687
|
this.set("internal_skipped", true);
|
|
4590
|
-
await this.node.updatePlayer({
|
|
5688
|
+
await this.node.updatePlayer({
|
|
5689
|
+
guildId: this.guildId,
|
|
5690
|
+
playerOptions: { track: { encoded: null }, paused: false }
|
|
5691
|
+
});
|
|
4591
5692
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4592
5693
|
return this;
|
|
4593
5694
|
}
|
|
@@ -4601,7 +5702,10 @@ var Player = class {
|
|
|
4601
5702
|
if (executeAutoplay === false) this.set("internal_autoplayStopPlaying", true);
|
|
4602
5703
|
else this.set("internal_autoplayStopPlaying", void 0);
|
|
4603
5704
|
const now = performance.now();
|
|
4604
|
-
await this.node.updatePlayer({
|
|
5705
|
+
await this.node.updatePlayer({
|
|
5706
|
+
guildId: this.guildId,
|
|
5707
|
+
playerOptions: { track: { encoded: null } }
|
|
5708
|
+
});
|
|
4605
5709
|
this.paused = false;
|
|
4606
5710
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4607
5711
|
return this;
|
|
@@ -4611,7 +5715,8 @@ var Player = class {
|
|
|
4611
5715
|
* @returns
|
|
4612
5716
|
*/
|
|
4613
5717
|
async connect() {
|
|
4614
|
-
if (!this.options.voiceChannelId)
|
|
5718
|
+
if (!this.options.voiceChannelId)
|
|
5719
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
4615
5720
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4616
5721
|
op: 4,
|
|
4617
5722
|
d: {
|
|
@@ -4625,7 +5730,8 @@ var Player = class {
|
|
|
4625
5730
|
return this;
|
|
4626
5731
|
}
|
|
4627
5732
|
async changeVoiceState(data) {
|
|
4628
|
-
if (this.options.voiceChannelId === data.voiceChannelId)
|
|
5733
|
+
if (this.options.voiceChannelId === data.voiceChannelId)
|
|
5734
|
+
throw new RangeError("New Channel can't be equal to the old Channel.");
|
|
4629
5735
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4630
5736
|
op: 4,
|
|
4631
5737
|
d: {
|
|
@@ -4647,7 +5753,8 @@ var Player = class {
|
|
|
4647
5753
|
* @returns
|
|
4648
5754
|
*/
|
|
4649
5755
|
async disconnect(force = false) {
|
|
4650
|
-
if (!force && !this.options.voiceChannelId)
|
|
5756
|
+
if (!force && !this.options.voiceChannelId)
|
|
5757
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
4651
5758
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4652
5759
|
op: 4,
|
|
4653
5760
|
d: {
|
|
@@ -4664,7 +5771,10 @@ var Player = class {
|
|
|
4664
5771
|
* Destroy the player and disconnect from the voice channel
|
|
4665
5772
|
*/
|
|
4666
5773
|
async destroy(reason, disconnect = true) {
|
|
4667
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5774
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5775
|
+
console.log(
|
|
5776
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`
|
|
5777
|
+
);
|
|
4668
5778
|
if (this.get("internal_queueempty")) {
|
|
4669
5779
|
clearTimeout(this.get("internal_queueempty"));
|
|
4670
5780
|
this.set("internal_queueempty", void 0);
|
|
@@ -4675,7 +5785,10 @@ var Player = class {
|
|
|
4675
5785
|
message: `Player is already destroying somewhere else..`,
|
|
4676
5786
|
functionLayer: "Player > destroy()"
|
|
4677
5787
|
});
|
|
4678
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5788
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5789
|
+
console.log(
|
|
5790
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`
|
|
5791
|
+
);
|
|
4679
5792
|
return;
|
|
4680
5793
|
}
|
|
4681
5794
|
this.set("internal_destroystatus", true);
|
|
@@ -4684,7 +5797,10 @@ var Player = class {
|
|
|
4684
5797
|
await this.queue.utils.destroy();
|
|
4685
5798
|
this.LavalinkManager.deletePlayer(this.guildId);
|
|
4686
5799
|
await this.node.destroyPlayer(this.guildId);
|
|
4687
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5800
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5801
|
+
console.log(
|
|
5802
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Player got destroyed successfully`
|
|
5803
|
+
);
|
|
4688
5804
|
this.LavalinkManager.emit("playerDestroy", this, reason);
|
|
4689
5805
|
return this;
|
|
4690
5806
|
}
|
|
@@ -4751,23 +5867,32 @@ var Player = class {
|
|
|
4751
5867
|
if (!updateNode) throw new Error("Could not find the new Node");
|
|
4752
5868
|
if (!updateNode.connected) throw new Error("The provided Node is not active or disconnected");
|
|
4753
5869
|
if (this.node.id === updateNode.id) throw new Error("Player is already on the provided Node");
|
|
4754
|
-
if (this.get("internal_nodeChanging") === true)
|
|
5870
|
+
if (this.get("internal_nodeChanging") === true)
|
|
5871
|
+
throw new Error("Player is already changing the node please wait");
|
|
4755
5872
|
if (checkSources) {
|
|
4756
5873
|
const isDefaultSource = () => {
|
|
4757
5874
|
try {
|
|
4758
|
-
this.LavalinkManager.utils.validateSourceString(
|
|
5875
|
+
this.LavalinkManager.utils.validateSourceString(
|
|
5876
|
+
updateNode,
|
|
5877
|
+
this.LavalinkManager.options.playerOptions.defaultSearchPlatform
|
|
5878
|
+
);
|
|
4759
5879
|
return true;
|
|
4760
5880
|
} catch {
|
|
4761
5881
|
return false;
|
|
4762
5882
|
}
|
|
4763
5883
|
};
|
|
4764
|
-
if (!isDefaultSource())
|
|
5884
|
+
if (!isDefaultSource())
|
|
5885
|
+
throw new RangeError(
|
|
5886
|
+
`defaultSearchPlatform "${this.LavalinkManager.options.playerOptions.defaultSearchPlatform}" is not supported by the newNode`
|
|
5887
|
+
);
|
|
4765
5888
|
if (this.queue.current || this.queue.tracks.length) {
|
|
4766
|
-
const trackSources = new Set(
|
|
5889
|
+
const trackSources = new Set(
|
|
5890
|
+
[this.queue.current, ...this.queue.tracks].map((track) => track.info.sourceName)
|
|
5891
|
+
);
|
|
4767
5892
|
const missingSources = [...trackSources].filter(
|
|
4768
5893
|
(source) => !updateNode.info?.sourceManagers.includes(source)
|
|
4769
5894
|
);
|
|
4770
|
-
if (
|
|
5895
|
+
if (updateNode._checkForSources && missingSources.length)
|
|
4771
5896
|
throw new RangeError(`Sources missing for Node ${updateNode.id}: ${missingSources.join(", ")}`);
|
|
4772
5897
|
}
|
|
4773
5898
|
}
|
|
@@ -4786,7 +5911,7 @@ var Player = class {
|
|
|
4786
5911
|
const now = performance.now();
|
|
4787
5912
|
try {
|
|
4788
5913
|
await this.connect();
|
|
4789
|
-
const hasSponsorBlock = !this.
|
|
5914
|
+
const hasSponsorBlock = !this.node._checkForPlugins || this.node.info?.plugins?.find((v) => v.name === "sponsorblock-plugin");
|
|
4790
5915
|
if (hasSponsorBlock) {
|
|
4791
5916
|
const sponsorBlockCategories = this.get("internal_sponsorBlockCategories");
|
|
4792
5917
|
if (Array.isArray(sponsorBlockCategories) && sponsorBlockCategories.length) {
|
|
@@ -4854,10 +5979,18 @@ var Player = class {
|
|
|
4854
5979
|
*/
|
|
4855
5980
|
async moveNode(node) {
|
|
4856
5981
|
try {
|
|
4857
|
-
if (!node)
|
|
4858
|
-
|
|
5982
|
+
if (!node)
|
|
5983
|
+
node = Array.from(this.LavalinkManager.nodeManager.leastUsedNodes("playingPlayers")).find(
|
|
5984
|
+
(n) => n.connected && n.options.id !== this.node.options.id
|
|
5985
|
+
).id;
|
|
5986
|
+
if (!node || !this.LavalinkManager.nodeManager.nodes.get(node))
|
|
5987
|
+
throw new RangeError("No nodes are available.");
|
|
4859
5988
|
if (this.node.options.id === node) return this;
|
|
4860
|
-
this.LavalinkManager.emit("debug", "PlayerChangeNode" /* PlayerChangeNode */, {
|
|
5989
|
+
this.LavalinkManager.emit("debug", "PlayerChangeNode" /* PlayerChangeNode */, {
|
|
5990
|
+
state: "log",
|
|
5991
|
+
message: `Player.moveNode() was executed, trying to move from "${this.node.id}" to "${node}"`,
|
|
5992
|
+
functionLayer: "Player > moveNode()"
|
|
5993
|
+
});
|
|
4861
5994
|
const updateNode = this.LavalinkManager.nodeManager.nodes.get(node);
|
|
4862
5995
|
if (!updateNode) throw new RangeError("No nodes are available.");
|
|
4863
5996
|
return await this.changeNode(updateNode);
|
|
@@ -4961,10 +6094,6 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
4961
6094
|
id: options?.client?.id,
|
|
4962
6095
|
username: options?.client?.username ?? "lavalink-client"
|
|
4963
6096
|
},
|
|
4964
|
-
autoChecks: {
|
|
4965
|
-
sourcesValidations: options?.autoChecks?.sourcesValidations ?? true,
|
|
4966
|
-
pluginValidations: options?.autoChecks?.pluginValidations ?? true
|
|
4967
|
-
},
|
|
4968
6097
|
sendToShard: options?.sendToShard,
|
|
4969
6098
|
autoMove: options?.autoMove ?? false,
|
|
4970
6099
|
nodes: options?.nodes,
|
|
@@ -4973,6 +6102,7 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
4973
6102
|
applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
|
|
4974
6103
|
clientBasedPositionUpdateInterval: options?.playerOptions?.clientBasedPositionUpdateInterval ?? 100,
|
|
4975
6104
|
defaultSearchPlatform: options?.playerOptions?.defaultSearchPlatform ?? "ytsearch",
|
|
6105
|
+
allowCustomSources: options?.playerOptions?.allowCustomSources ?? false,
|
|
4976
6106
|
onDisconnect: {
|
|
4977
6107
|
destroyPlayer: options?.playerOptions?.onDisconnect?.destroyPlayer ?? true,
|
|
4978
6108
|
autoReconnect: options?.playerOptions?.onDisconnect?.autoReconnect ?? false,
|
|
@@ -5023,24 +6153,36 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5023
6153
|
* @param options
|
|
5024
6154
|
*/
|
|
5025
6155
|
validateOptions(options) {
|
|
5026
|
-
if (typeof options?.sendToShard !== "function")
|
|
5027
|
-
|
|
5028
|
-
if (options?.
|
|
5029
|
-
|
|
5030
|
-
if (
|
|
5031
|
-
|
|
5032
|
-
if (typeof options?.
|
|
6156
|
+
if (typeof options?.sendToShard !== "function")
|
|
6157
|
+
throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
|
|
6158
|
+
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
6159
|
+
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
6160
|
+
if (options?.autoSkipOnResolveError && typeof options?.autoSkipOnResolveError !== "boolean")
|
|
6161
|
+
throw new SyntaxError("ManagerOption.autoSkipOnResolveError must be either false | true aka boolean");
|
|
6162
|
+
if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean")
|
|
6163
|
+
throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
|
|
6164
|
+
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every((node) => this.utils.isNodeOptions(node)))
|
|
6165
|
+
throw new SyntaxError(
|
|
6166
|
+
"ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node"
|
|
6167
|
+
);
|
|
5033
6168
|
if (options?.queueOptions?.queueStore) {
|
|
5034
6169
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueStore));
|
|
5035
6170
|
const requiredKeys = ["get", "set", "stringify", "parse", "delete"];
|
|
5036
|
-
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
6171
|
+
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
6172
|
+
throw new SyntaxError(
|
|
6173
|
+
`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`
|
|
6174
|
+
);
|
|
5037
6175
|
}
|
|
5038
6176
|
if (options?.queueOptions?.queueChangesWatcher) {
|
|
5039
6177
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueChangesWatcher));
|
|
5040
6178
|
const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
|
|
5041
|
-
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
6179
|
+
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
6180
|
+
throw new SyntaxError(
|
|
6181
|
+
`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`
|
|
6182
|
+
);
|
|
5042
6183
|
}
|
|
5043
|
-
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
6184
|
+
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
6185
|
+
options.queueOptions.maxPreviousTracks = 25;
|
|
5044
6186
|
}
|
|
5045
6187
|
/**
|
|
5046
6188
|
* Emits a debug event to the LavalinkManager
|
|
@@ -5077,6 +6219,7 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5077
6219
|
* applyVolumeAsFilter: false,
|
|
5078
6220
|
* clientBasedPositionUpdateInterval: 150,
|
|
5079
6221
|
* defaultSearchPlatform: "ytmsearch",
|
|
6222
|
+
* allowCustomSources: false,
|
|
5080
6223
|
* volumeDecrementer: 0.75,
|
|
5081
6224
|
* //requesterTransformer: YourRequesterTransformerFunction,
|
|
5082
6225
|
* onDisconnect: {
|
|
@@ -5199,7 +6342,10 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5199
6342
|
const oldPlayer = this.getPlayer(guildId);
|
|
5200
6343
|
if (!oldPlayer) return;
|
|
5201
6344
|
if (typeof oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
5202
|
-
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
6345
|
+
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
6346
|
+
throw new Error(
|
|
6347
|
+
`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`
|
|
6348
|
+
);
|
|
5203
6349
|
this._emitDebugEvent("PlayerDeleteInsteadOfDestroy" /* PlayerDeleteInsteadOfDestroy */, {
|
|
5204
6350
|
state: "warn",
|
|
5205
6351
|
message: "Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player",
|
|
@@ -5239,7 +6385,8 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5239
6385
|
if (this.initiated) return this;
|
|
5240
6386
|
clientData = clientData ?? {};
|
|
5241
6387
|
this.options.client = { ...this.options?.client, ...clientData };
|
|
5242
|
-
if (!this.options?.client.id)
|
|
6388
|
+
if (!this.options?.client.id)
|
|
6389
|
+
throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
|
|
5243
6390
|
if (typeof this.options?.client.id !== "string") throw new Error('"client.id" set is not type of "string"');
|
|
5244
6391
|
let success = 0;
|
|
5245
6392
|
for (const node of this.nodeManager.nodes.values()) {
|
|
@@ -5252,11 +6399,12 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5252
6399
|
}
|
|
5253
6400
|
}
|
|
5254
6401
|
if (success > 0) this.initiated = true;
|
|
5255
|
-
else
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
6402
|
+
else
|
|
6403
|
+
this._emitDebugEvent("FailedToConnectToNodes" /* FailedToConnectToNodes */, {
|
|
6404
|
+
state: "error",
|
|
6405
|
+
message: "Failed to connect to at least 1 Node",
|
|
6406
|
+
functionLayer: "LavalinkManager > init()"
|
|
6407
|
+
});
|
|
5260
6408
|
return this;
|
|
5261
6409
|
}
|
|
5262
6410
|
/**
|
|
@@ -5281,7 +6429,10 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5281
6429
|
message: "Manager is not initated yet",
|
|
5282
6430
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5283
6431
|
});
|
|
5284
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6432
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6433
|
+
console.debug(
|
|
6434
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet"
|
|
6435
|
+
);
|
|
5285
6436
|
return;
|
|
5286
6437
|
}
|
|
5287
6438
|
if (!("t" in data)) {
|
|
@@ -5290,14 +6441,19 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5290
6441
|
message: "No 't' in payload-data of the raw event:",
|
|
5291
6442
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5292
6443
|
});
|
|
5293
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6444
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6445
|
+
console.debug(
|
|
6446
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:",
|
|
6447
|
+
data
|
|
6448
|
+
);
|
|
5294
6449
|
return;
|
|
5295
6450
|
}
|
|
5296
6451
|
if ("CHANNEL_DELETE" === data.t) {
|
|
5297
6452
|
const update = "d" in data ? data.d : data;
|
|
5298
6453
|
if (!update.guild_id) return;
|
|
5299
6454
|
const player = this.getPlayer(update.guild_id);
|
|
5300
|
-
if (player && player.voiceChannelId === update.id)
|
|
6455
|
+
if (player && player.voiceChannelId === update.id)
|
|
6456
|
+
return void player.destroy("ChannelDeleted" /* ChannelDeleted */);
|
|
5301
6457
|
}
|
|
5302
6458
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
5303
6459
|
const update = "d" in data ? data.d : data;
|
|
@@ -5307,7 +6463,11 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5307
6463
|
message: `No Update data found in payload :: ${safeStringify(data, 2)}`,
|
|
5308
6464
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5309
6465
|
});
|
|
5310
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6466
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6467
|
+
console.debug(
|
|
6468
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:",
|
|
6469
|
+
data
|
|
6470
|
+
);
|
|
5311
6471
|
return;
|
|
5312
6472
|
}
|
|
5313
6473
|
if (!("token" in update) && !("session_id" in update)) {
|
|
@@ -5316,7 +6476,11 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5316
6476
|
message: `No 'token' nor 'session_id' found in payload :: ${safeStringify(data, 2)}`,
|
|
5317
6477
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5318
6478
|
});
|
|
5319
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6479
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6480
|
+
console.debug(
|
|
6481
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:",
|
|
6482
|
+
data
|
|
6483
|
+
);
|
|
5320
6484
|
return;
|
|
5321
6485
|
}
|
|
5322
6486
|
const player = this.getPlayer(update.guild_id);
|
|
@@ -5326,7 +6490,11 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5326
6490
|
message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${safeStringify(update, 2)}`,
|
|
5327
6491
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5328
6492
|
});
|
|
5329
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6493
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6494
|
+
console.debug(
|
|
6495
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:",
|
|
6496
|
+
update
|
|
6497
|
+
);
|
|
5330
6498
|
return;
|
|
5331
6499
|
}
|
|
5332
6500
|
if (player.get("internal_destroystatus") === true) {
|
|
@@ -5335,7 +6503,10 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5335
6503
|
message: `Player is in a destroying state. can't signal the voice states`,
|
|
5336
6504
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5337
6505
|
});
|
|
5338
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6506
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6507
|
+
console.debug(
|
|
6508
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states"
|
|
6509
|
+
);
|
|
5339
6510
|
return;
|
|
5340
6511
|
}
|
|
5341
6512
|
if ("token" in update) {
|
|
@@ -5347,7 +6518,15 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5347
6518
|
message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use }, update, playerVoice: player.voice }, 2)}`,
|
|
5348
6519
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5349
6520
|
});
|
|
5350
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6521
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6522
|
+
console.debug(
|
|
6523
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId",
|
|
6524
|
+
{
|
|
6525
|
+
voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use },
|
|
6526
|
+
update,
|
|
6527
|
+
playerVoice: player.voice
|
|
6528
|
+
}
|
|
6529
|
+
);
|
|
5351
6530
|
} else {
|
|
5352
6531
|
await player.node.updatePlayer({
|
|
5353
6532
|
guildId: player.guildId,
|
|
@@ -5364,24 +6543,44 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5364
6543
|
message: `Sent updatePlayer for voice token session :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use }, update, playerVoice: player.voice }, 2)}`,
|
|
5365
6544
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5366
6545
|
});
|
|
5367
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6546
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6547
|
+
console.debug(
|
|
6548
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session",
|
|
6549
|
+
{
|
|
6550
|
+
voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use },
|
|
6551
|
+
playerVoice: player.voice,
|
|
6552
|
+
update
|
|
6553
|
+
}
|
|
6554
|
+
);
|
|
5368
6555
|
}
|
|
5369
6556
|
return;
|
|
5370
6557
|
}
|
|
5371
6558
|
if (update.user_id !== this.options?.client.id) {
|
|
5372
6559
|
if (update.user_id && player.voiceChannelId) {
|
|
5373
|
-
this.emit(
|
|
6560
|
+
this.emit(
|
|
6561
|
+
update.channel_id === player.voiceChannelId ? "playerVoiceJoin" : "playerVoiceLeave",
|
|
6562
|
+
player,
|
|
6563
|
+
update.user_id
|
|
6564
|
+
);
|
|
5374
6565
|
}
|
|
5375
6566
|
this._emitDebugEvent("NoAudioDebug" /* NoAudioDebug */, {
|
|
5376
6567
|
state: "warn",
|
|
5377
6568
|
message: `voice update user is not equal to provided client id of the LavalinkManager.options.client.id :: user: "${update.user_id}" manager client id: "${this.options?.client.id}"`,
|
|
5378
6569
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5379
6570
|
});
|
|
5380
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6571
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6572
|
+
console.debug(
|
|
6573
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, voice update user is not equal to provided client id of the manageroptions#client#id",
|
|
6574
|
+
"user:",
|
|
6575
|
+
update.user_id,
|
|
6576
|
+
"manager client id:",
|
|
6577
|
+
this.options?.client.id
|
|
6578
|
+
);
|
|
5381
6579
|
return;
|
|
5382
6580
|
}
|
|
5383
6581
|
if (update.channel_id) {
|
|
5384
|
-
if (player.voiceChannelId !== update.channel_id)
|
|
6582
|
+
if (player.voiceChannelId !== update.channel_id)
|
|
6583
|
+
this.emit("playerMove", player, player.voiceChannelId, update.channel_id);
|
|
5385
6584
|
player.voice.sessionId = update.session_id || player.voice.sessionId;
|
|
5386
6585
|
if (!player.voice.sessionId) {
|
|
5387
6586
|
this._emitDebugEvent("NoAudioDebug" /* NoAudioDebug */, {
|
|
@@ -5389,7 +6588,10 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5389
6588
|
message: `Function to assing sessionId provided, but no found in Payload: ${safeStringify({ update, playerVoice: player.voice }, 2)}`,
|
|
5390
6589
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5391
6590
|
});
|
|
5392
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6591
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6592
|
+
console.debug(
|
|
6593
|
+
`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`
|
|
6594
|
+
);
|
|
5393
6595
|
}
|
|
5394
6596
|
player.voiceChannelId = update.channel_id;
|
|
5395
6597
|
player.options.voiceChannelId = update.channel_id;
|
|
@@ -5403,15 +6605,13 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5403
6605
|
player.voiceState.serverDeaf = update.deaf ?? player.voiceState?.serverDeaf;
|
|
5404
6606
|
player.voiceState.serverMute = update.mute ?? player.voiceState?.serverMute;
|
|
5405
6607
|
player.voiceState.suppress = update.suppress ?? player.voiceState?.suppress;
|
|
5406
|
-
if (selfMuteChanged || serverMuteChanged)
|
|
5407
|
-
|
|
6608
|
+
if (selfMuteChanged || serverMuteChanged)
|
|
6609
|
+
this.emit("playerMuteChange", player, player.voiceState.selfMute, player.voiceState.serverMute);
|
|
6610
|
+
if (selfDeafChanged || serverDeafChanged)
|
|
6611
|
+
this.emit("playerDeafChange", player, player.voiceState.selfDeaf, player.voiceState.serverDeaf);
|
|
5408
6612
|
if (suppressChange) this.emit("playerSuppressChange", player, player.voiceState.suppress);
|
|
5409
6613
|
} else {
|
|
5410
|
-
const {
|
|
5411
|
-
autoReconnectOnlyWithTracks,
|
|
5412
|
-
destroyPlayer,
|
|
5413
|
-
autoReconnect
|
|
5414
|
-
} = this.options?.playerOptions?.onDisconnect ?? {};
|
|
6614
|
+
const { autoReconnectOnlyWithTracks, destroyPlayer, autoReconnect } = this.options?.playerOptions?.onDisconnect ?? {};
|
|
5415
6615
|
if (destroyPlayer === true) {
|
|
5416
6616
|
return void await player.destroy("Disconnected" /* Disconnected */);
|
|
5417
6617
|
}
|
|
@@ -5429,7 +6629,11 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5429
6629
|
this.emit("playerReconnect", player, player.voiceChannelId);
|
|
5430
6630
|
}
|
|
5431
6631
|
if (player.queue.current) {
|
|
5432
|
-
return void await player.play({
|
|
6632
|
+
return void await player.play({
|
|
6633
|
+
position: previousPosition,
|
|
6634
|
+
paused: previousPaused,
|
|
6635
|
+
clientTrack: player.queue.current
|
|
6636
|
+
});
|
|
5433
6637
|
}
|
|
5434
6638
|
if (player.queue.tracks.length) {
|
|
5435
6639
|
return void await player.play({ paused: previousPaused });
|
|
@@ -5466,12 +6670,15 @@ export {
|
|
|
5466
6670
|
LavalinkPlugins,
|
|
5467
6671
|
ManagerUtils,
|
|
5468
6672
|
MiniMap,
|
|
6673
|
+
NodeLinkExclusiveEvents,
|
|
6674
|
+
NodeLinkNode,
|
|
5469
6675
|
NodeManager,
|
|
5470
6676
|
NodeSymbol,
|
|
5471
6677
|
Player,
|
|
5472
6678
|
Queue,
|
|
5473
6679
|
QueueSaver,
|
|
5474
6680
|
QueueSymbol,
|
|
6681
|
+
RecommendationsStrings,
|
|
5475
6682
|
ReconnectionState,
|
|
5476
6683
|
SourceLinksRegexes,
|
|
5477
6684
|
TrackSymbol,
|