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.js
CHANGED
|
@@ -41,12 +41,15 @@ __export(index_exports, {
|
|
|
41
41
|
LavalinkPlugins: () => LavalinkPlugins,
|
|
42
42
|
ManagerUtils: () => ManagerUtils,
|
|
43
43
|
MiniMap: () => MiniMap,
|
|
44
|
+
NodeLinkExclusiveEvents: () => NodeLinkExclusiveEvents,
|
|
45
|
+
NodeLinkNode: () => NodeLinkNode,
|
|
44
46
|
NodeManager: () => NodeManager,
|
|
45
47
|
NodeSymbol: () => NodeSymbol,
|
|
46
48
|
Player: () => Player,
|
|
47
49
|
Queue: () => Queue,
|
|
48
50
|
QueueSaver: () => QueueSaver,
|
|
49
51
|
QueueSymbol: () => QueueSymbol,
|
|
52
|
+
RecommendationsStrings: () => RecommendationsStrings,
|
|
50
53
|
ReconnectionState: () => ReconnectionState,
|
|
51
54
|
SourceLinksRegexes: () => SourceLinksRegexes,
|
|
52
55
|
TrackSymbol: () => TrackSymbol,
|
|
@@ -132,7 +135,16 @@ var DisconnectReasons = /* @__PURE__ */ ((DisconnectReasons2) => {
|
|
|
132
135
|
DisconnectReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
|
|
133
136
|
return DisconnectReasons2;
|
|
134
137
|
})(DisconnectReasons || {});
|
|
135
|
-
var validSponsorBlocks = [
|
|
138
|
+
var validSponsorBlocks = [
|
|
139
|
+
"sponsor",
|
|
140
|
+
"selfpromo",
|
|
141
|
+
"interaction",
|
|
142
|
+
"intro",
|
|
143
|
+
"outro",
|
|
144
|
+
"preview",
|
|
145
|
+
"music_offtopic",
|
|
146
|
+
"filler"
|
|
147
|
+
];
|
|
136
148
|
var audioOutputsData = {
|
|
137
149
|
mono: {
|
|
138
150
|
// totalLeft: 1, totalRight: 1
|
|
@@ -363,6 +375,33 @@ var EQList = {
|
|
|
363
375
|
{ band: 14, gain: -0.35 }
|
|
364
376
|
]
|
|
365
377
|
};
|
|
378
|
+
var RecommendationsStrings = {
|
|
379
|
+
highCPULoad: (cpuLoad) => `High CPU load (${(cpuLoad * 100).toFixed(1)}%). Consider reducing player count or upgrading CPU.`,
|
|
380
|
+
highSystemLoad: (systemLoad) => `High system load (${(systemLoad * 100).toFixed(1)}%). Check other processes on the server.`,
|
|
381
|
+
highMemoryUsage: (memoryUsagePercent) => `High memory usage (${memoryUsagePercent.toFixed(1)}%). Consider increasing allocated memory or reducing player count.`,
|
|
382
|
+
frameDeficit: (frameDeficit) => `Frame deficit detected (${frameDeficit}). Audio quality may be affected. Check network and CPU.`,
|
|
383
|
+
highLatency: (ping) => `High latency (${ping}ms). Check network connection to the node.`,
|
|
384
|
+
nodeRestart: "Node restart recommended to clear memory and reset connections.",
|
|
385
|
+
highPlayercount: (players) => `High player count (${players}). Consider load balancing across multiple nodes.`,
|
|
386
|
+
nodeOffline: "Node is offline or disconnected",
|
|
387
|
+
checkConnectivity: "Check node connectivity and restart if needed"
|
|
388
|
+
};
|
|
389
|
+
var NodeLinkExclusiveEvents = [
|
|
390
|
+
"PlayerCreatedEvent",
|
|
391
|
+
"PlayerDestroyedEvent",
|
|
392
|
+
"PlayerConnectedEvent",
|
|
393
|
+
"PlayerReconnectingEvent",
|
|
394
|
+
"VolumeChangedEvent",
|
|
395
|
+
"FiltersChangedEvent",
|
|
396
|
+
"SeekEvent",
|
|
397
|
+
"PauseEvent",
|
|
398
|
+
"ConnectionStatusEvent",
|
|
399
|
+
"MixStartedEvent",
|
|
400
|
+
"MixEndedEvent",
|
|
401
|
+
"LyricsFoundEvent",
|
|
402
|
+
"LyricsLineEvent",
|
|
403
|
+
"LyricsNotFoundEvent"
|
|
404
|
+
];
|
|
366
405
|
|
|
367
406
|
// src/structures/NodeManager.ts
|
|
368
407
|
var import_events = require("events");
|
|
@@ -388,99 +427,99 @@ var import_types = require("util/types");
|
|
|
388
427
|
var DefaultSources = {
|
|
389
428
|
// youtubemusic
|
|
390
429
|
"youtube music": "ytmsearch",
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
430
|
+
youtubemusic: "ytmsearch",
|
|
431
|
+
ytmsearch: "ytmsearch",
|
|
432
|
+
ytm: "ytmsearch",
|
|
433
|
+
musicyoutube: "ytmsearch",
|
|
395
434
|
"music youtube": "ytmsearch",
|
|
396
435
|
// youtube
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
436
|
+
youtube: "ytsearch",
|
|
437
|
+
yt: "ytsearch",
|
|
438
|
+
ytsearch: "ytsearch",
|
|
400
439
|
// soundcloud
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
440
|
+
soundcloud: "scsearch",
|
|
441
|
+
scsearch: "scsearch",
|
|
442
|
+
sc: "scsearch",
|
|
404
443
|
// apple music
|
|
405
444
|
"apple music": "amsearch",
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
445
|
+
apple: "amsearch",
|
|
446
|
+
applemusic: "amsearch",
|
|
447
|
+
amsearch: "amsearch",
|
|
448
|
+
am: "amsearch",
|
|
449
|
+
musicapple: "amsearch",
|
|
411
450
|
"music apple": "amsearch",
|
|
412
451
|
// spotify
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
452
|
+
spotify: "spsearch",
|
|
453
|
+
spsearch: "spsearch",
|
|
454
|
+
sp: "spsearch",
|
|
416
455
|
"spotify.com": "spsearch",
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
456
|
+
spotifycom: "spsearch",
|
|
457
|
+
sprec: "sprec",
|
|
458
|
+
spsuggestion: "sprec",
|
|
420
459
|
// deezer
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
460
|
+
deezer: "dzsearch",
|
|
461
|
+
dz: "dzsearch",
|
|
462
|
+
dzsearch: "dzsearch",
|
|
463
|
+
dzisrc: "dzisrc",
|
|
464
|
+
dzrec: "dzrec",
|
|
426
465
|
// yandexmusic
|
|
427
466
|
"yandex music": "ymsearch",
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
467
|
+
yandexmusic: "ymsearch",
|
|
468
|
+
yandex: "ymsearch",
|
|
469
|
+
ymsearch: "ymsearch",
|
|
470
|
+
ymrec: "ymrec",
|
|
432
471
|
// VK Music (lavasrc)
|
|
433
|
-
|
|
434
|
-
|
|
472
|
+
vksearch: "vksearch",
|
|
473
|
+
vkmusic: "vksearch",
|
|
435
474
|
"vk music": "vksearch",
|
|
436
|
-
|
|
437
|
-
|
|
475
|
+
vkrec: "vkrec",
|
|
476
|
+
vk: "vksearch",
|
|
438
477
|
// Qobuz (lavasrc)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
478
|
+
qbsearch: "qbsearch",
|
|
479
|
+
qobuz: "qbsearch",
|
|
480
|
+
qbisrc: "qbisrc",
|
|
481
|
+
qbrec: "qbrec",
|
|
443
482
|
// pandora (lavasrc)
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
483
|
+
pandora: "pdsearch",
|
|
484
|
+
pd: "pdsearch",
|
|
485
|
+
pdsearch: "pdsearch",
|
|
486
|
+
pdisrc: "pdisrc",
|
|
487
|
+
pdrec: "pdrec",
|
|
449
488
|
"pandora music": "pdsearch",
|
|
450
|
-
|
|
489
|
+
pandoramusic: "pdsearch",
|
|
451
490
|
// speak PLUGIN
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
491
|
+
speak: "speak",
|
|
492
|
+
tts: "tts",
|
|
493
|
+
ftts: "ftts",
|
|
494
|
+
flowery: "ftts",
|
|
456
495
|
"flowery.tts": "ftts",
|
|
457
|
-
|
|
496
|
+
flowerytts: "ftts",
|
|
458
497
|
// Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
498
|
+
bandcamp: "bcsearch",
|
|
499
|
+
bc: "bcsearch",
|
|
500
|
+
bcsearch: "bcsearch",
|
|
462
501
|
// other searches:
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
502
|
+
phsearch: "phsearch",
|
|
503
|
+
pornhub: "phsearch",
|
|
504
|
+
porn: "phsearch",
|
|
466
505
|
// local files
|
|
467
|
-
|
|
506
|
+
local: "local",
|
|
468
507
|
// http requests
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
508
|
+
http: "http",
|
|
509
|
+
https: "https",
|
|
510
|
+
link: "link",
|
|
511
|
+
uri: "uri",
|
|
473
512
|
// tidal
|
|
474
|
-
|
|
475
|
-
|
|
513
|
+
tidal: "tdsearch",
|
|
514
|
+
td: "tdsearch",
|
|
476
515
|
"tidal music": "tdsearch",
|
|
477
|
-
|
|
478
|
-
|
|
516
|
+
tdsearch: "tdsearch",
|
|
517
|
+
tdrec: "tdrec",
|
|
479
518
|
// jiosaavn
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
519
|
+
jiosaavn: "jssearch",
|
|
520
|
+
js: "jssearch",
|
|
521
|
+
jssearch: "jssearch",
|
|
522
|
+
jsrec: "jsrec"
|
|
484
523
|
};
|
|
485
524
|
var LavalinkPlugins = {
|
|
486
525
|
DuncteBot_Plugin: "DuncteBot-plugin",
|
|
@@ -531,11 +570,11 @@ var SourceLinksRegexes = {
|
|
|
531
570
|
/** From jiosaavn-plugin */
|
|
532
571
|
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
|
|
533
572
|
/** From pandora */
|
|
534
|
-
PandoraTrackRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
535
|
-
PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
536
|
-
PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w
|
|
573
|
+
PandoraTrackRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>TR[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
574
|
+
PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>AL[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
575
|
+
PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+\/(?<identifier>AR[A-Za-z0-9]+)(?:[?#].*)?$/,
|
|
537
576
|
PandoraPlaylistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/playlist\/(?<identifier>PL:[\d:]+)(?:[?#].*)?$/,
|
|
538
|
-
AllPandoraRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/(?:playlist\/(?<playlistId>PL:[\d:]+)|artist\/[\w
|
|
577
|
+
AllPandoraRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/(?:playlist\/(?<playlistId>PL:[\d:]+)|artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>(?:TR|AL|AR)[A-Za-z0-9]+))(?:[?#].*)?$/,
|
|
539
578
|
/** FROM DUNCTE BOT PLUGIN */
|
|
540
579
|
tiktok: /https:\/\/www\.tiktok\.com\//,
|
|
541
580
|
mixcloud: /https:\/\/www\.mixcloud\.com\//,
|
|
@@ -544,13 +583,14 @@ var SourceLinksRegexes = {
|
|
|
544
583
|
};
|
|
545
584
|
|
|
546
585
|
// src/structures/Utils.ts
|
|
547
|
-
var TrackSymbol = Symbol("LC-Track");
|
|
548
|
-
var UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
549
|
-
var QueueSymbol = Symbol("LC-Queue");
|
|
550
|
-
var NodeSymbol = Symbol("LC-Node");
|
|
586
|
+
var TrackSymbol = /* @__PURE__ */ Symbol("LC-Track");
|
|
587
|
+
var UnresolvedTrackSymbol = /* @__PURE__ */ Symbol("LC-Track-Unresolved");
|
|
588
|
+
var QueueSymbol = /* @__PURE__ */ Symbol("LC-Queue");
|
|
589
|
+
var NodeSymbol = /* @__PURE__ */ Symbol("LC-Node");
|
|
551
590
|
var escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
552
591
|
function parseLavalinkConnUrl(connectionUrl) {
|
|
553
|
-
if (!connectionUrl.startsWith("lavalink://"))
|
|
592
|
+
if (!connectionUrl.startsWith("lavalink://"))
|
|
593
|
+
throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'lavalink://'`);
|
|
554
594
|
const parsed = new import_node_url.URL(connectionUrl);
|
|
555
595
|
return {
|
|
556
596
|
authorization: parsed.password,
|
|
@@ -564,14 +604,27 @@ var ManagerUtils = class {
|
|
|
564
604
|
constructor(LavalinkManager2) {
|
|
565
605
|
this.LavalinkManager = LavalinkManager2;
|
|
566
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* 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.
|
|
609
|
+
* @param data
|
|
610
|
+
* @param clientData
|
|
611
|
+
* @returns
|
|
612
|
+
*/
|
|
567
613
|
buildPluginInfo(data, clientData = {}) {
|
|
568
614
|
return {
|
|
569
615
|
clientData,
|
|
570
616
|
...data.pluginInfo || data.plugin
|
|
571
617
|
};
|
|
572
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* 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.
|
|
621
|
+
* @param data
|
|
622
|
+
* @param requester
|
|
623
|
+
* @returns
|
|
624
|
+
*/
|
|
573
625
|
buildTrack(data, requester) {
|
|
574
|
-
if (!data?.encoded || typeof data.encoded !== "string")
|
|
626
|
+
if (!data?.encoded || typeof data.encoded !== "string")
|
|
627
|
+
throw new RangeError("Argument 'data.encoded' must be present.");
|
|
575
628
|
if (!data.info) throw new RangeError("Argument 'data.info' must be present.");
|
|
576
629
|
try {
|
|
577
630
|
let transformedRequester = typeof requester === "object" ? this.getTransformedRequester(requester) : void 0;
|
|
@@ -619,8 +672,7 @@ var ManagerUtils = class {
|
|
|
619
672
|
* @param requester
|
|
620
673
|
*/
|
|
621
674
|
buildUnresolvedTrack(query, requester) {
|
|
622
|
-
if (typeof query === "undefined")
|
|
623
|
-
throw new RangeError('Argument "query" must be present.');
|
|
675
|
+
if (typeof query === "undefined") throw new RangeError('Argument "query" must be present.');
|
|
624
676
|
const unresolvedTrack = {
|
|
625
677
|
encoded: query.encoded || void 0,
|
|
626
678
|
info: query.info ? query.info : query.title ? query : void 0,
|
|
@@ -636,7 +688,10 @@ var ManagerUtils = class {
|
|
|
636
688
|
}
|
|
637
689
|
};
|
|
638
690
|
if (!this.isUnresolvedTrack(unresolvedTrack)) throw SyntaxError("Could not build Unresolved Track");
|
|
639
|
-
Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, {
|
|
691
|
+
Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, {
|
|
692
|
+
configurable: true,
|
|
693
|
+
value: true
|
|
694
|
+
});
|
|
640
695
|
return unresolvedTrack;
|
|
641
696
|
}
|
|
642
697
|
/**
|
|
@@ -648,9 +703,27 @@ var ManagerUtils = class {
|
|
|
648
703
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(data));
|
|
649
704
|
if (!keys.includes("constructor")) return false;
|
|
650
705
|
if (!keys.length) return false;
|
|
651
|
-
if (![
|
|
706
|
+
if (![
|
|
707
|
+
"connect",
|
|
708
|
+
"destroy",
|
|
709
|
+
"destroyPlayer",
|
|
710
|
+
"fetchAllPlayers",
|
|
711
|
+
"fetchInfo",
|
|
712
|
+
"fetchPlayer",
|
|
713
|
+
"fetchStats",
|
|
714
|
+
"fetchVersion",
|
|
715
|
+
"request",
|
|
716
|
+
"updatePlayer",
|
|
717
|
+
"updateSession"
|
|
718
|
+
].every((v) => keys.includes(v)))
|
|
719
|
+
return false;
|
|
652
720
|
return true;
|
|
653
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* 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.
|
|
724
|
+
* @param requester
|
|
725
|
+
* @returns
|
|
726
|
+
*/
|
|
654
727
|
getTransformedRequester(requester) {
|
|
655
728
|
try {
|
|
656
729
|
return typeof this.LavalinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.LavalinkManager?.options?.playerOptions?.requesterTransformer(requester) : requester;
|
|
@@ -678,11 +751,16 @@ var ManagerUtils = class {
|
|
|
678
751
|
if ("secure" in data && typeof data.secure !== "boolean" && data.secure !== void 0) return false;
|
|
679
752
|
if ("sessionId" in data && typeof data.sessionId !== "string" && data.sessionId !== void 0) return false;
|
|
680
753
|
if ("id" in data && typeof data.id !== "string" && data.id !== void 0) return false;
|
|
681
|
-
if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0))
|
|
682
|
-
|
|
683
|
-
if ("
|
|
684
|
-
|
|
685
|
-
if ("
|
|
754
|
+
if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0))
|
|
755
|
+
return false;
|
|
756
|
+
if ("poolOptions" in data && typeof data.poolOptions !== "object" && data.poolOptions !== void 0)
|
|
757
|
+
return false;
|
|
758
|
+
if ("retryAmount" in data && (typeof data.retryAmount !== "number" || isNaN(data.retryAmount) || data.retryAmount <= 0 && data.retryAmount !== void 0))
|
|
759
|
+
return false;
|
|
760
|
+
if ("retryDelay" in data && (typeof data.retryDelay !== "number" || isNaN(data.retryDelay) || data.retryDelay <= 0 && data.retryDelay !== void 0))
|
|
761
|
+
return false;
|
|
762
|
+
if ("requestTimeout" in data && (typeof data.requestTimeout !== "number" || isNaN(data.requestTimeout) || data.requestTimeout <= 0 && data.requestTimeout !== void 0))
|
|
763
|
+
return false;
|
|
686
764
|
return true;
|
|
687
765
|
}
|
|
688
766
|
/**
|
|
@@ -734,6 +812,12 @@ var ManagerUtils = class {
|
|
|
734
812
|
isUnresolvedTrackQuery(data) {
|
|
735
813
|
return typeof data === "object" && !("info" in data) && typeof data.title === "string";
|
|
736
814
|
}
|
|
815
|
+
/**
|
|
816
|
+
* 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.
|
|
817
|
+
* @param data
|
|
818
|
+
* @param player
|
|
819
|
+
* @returns
|
|
820
|
+
*/
|
|
737
821
|
async getClosestTrack(data, player) {
|
|
738
822
|
try {
|
|
739
823
|
return getClosestTrack(data, player);
|
|
@@ -749,11 +833,20 @@ var ManagerUtils = class {
|
|
|
749
833
|
throw e;
|
|
750
834
|
}
|
|
751
835
|
}
|
|
836
|
+
/**
|
|
837
|
+
* 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.
|
|
838
|
+
* @param node
|
|
839
|
+
* @param queryString
|
|
840
|
+
* @param sourceString
|
|
841
|
+
* @returns
|
|
842
|
+
*/
|
|
752
843
|
validateQueryString(node, queryString, sourceString) {
|
|
753
844
|
if (!node.info) throw new Error("No Lavalink Node was provided");
|
|
754
|
-
if (
|
|
845
|
+
if (node._checkForSources && !node.info.sourceManagers?.length)
|
|
846
|
+
throw new Error("Lavalink Node, has no sourceManagers enabled");
|
|
755
847
|
if (!queryString.trim().length) throw new Error(`Query string is empty, please provide a valid query string.`);
|
|
756
|
-
if (sourceString === "speak" && queryString.length > 100)
|
|
848
|
+
if (sourceString === "speak" && queryString.length > 100)
|
|
849
|
+
throw new Error(`Query is speak, which is limited to 100 characters.`);
|
|
757
850
|
if (this.LavalinkManager.options?.linksBlacklist?.length > 0) {
|
|
758
851
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
759
852
|
this.LavalinkManager.emit("debug", "ValidatingBlacklistLinks" /* ValidatingBlacklistLinks */, {
|
|
@@ -762,12 +855,15 @@ var ManagerUtils = class {
|
|
|
762
855
|
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
|
|
763
856
|
});
|
|
764
857
|
}
|
|
765
|
-
if (this.LavalinkManager.options?.linksBlacklist.some(
|
|
858
|
+
if (this.LavalinkManager.options?.linksBlacklist.some(
|
|
859
|
+
(v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString)
|
|
860
|
+
)) {
|
|
766
861
|
throw new Error(`Query string contains a link / word which is blacklisted.`);
|
|
767
862
|
}
|
|
768
863
|
}
|
|
769
864
|
if (!/^https?:\/\//.test(queryString)) return;
|
|
770
|
-
else if (this.LavalinkManager.options?.linksAllowed === false)
|
|
865
|
+
else if (this.LavalinkManager.options?.linksAllowed === false)
|
|
866
|
+
throw new Error("Using links to make a request is not allowed.");
|
|
771
867
|
if (this.LavalinkManager.options?.linksWhitelist?.length > 0) {
|
|
772
868
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
773
869
|
this.LavalinkManager.emit("debug", "ValidatingWhitelistLinks" /* ValidatingWhitelistLinks */, {
|
|
@@ -776,11 +872,13 @@ var ManagerUtils = class {
|
|
|
776
872
|
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
|
|
777
873
|
});
|
|
778
874
|
}
|
|
779
|
-
if (!this.LavalinkManager.options?.linksWhitelist.some(
|
|
875
|
+
if (!this.LavalinkManager.options?.linksWhitelist.some(
|
|
876
|
+
(v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString)
|
|
877
|
+
)) {
|
|
780
878
|
throw new Error(`Query string contains a link / word which isn't whitelisted.`);
|
|
781
879
|
}
|
|
782
880
|
}
|
|
783
|
-
if (!
|
|
881
|
+
if (!node._checkForSources) return;
|
|
784
882
|
if ((SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
|
|
785
883
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'youtube' enabled");
|
|
786
884
|
}
|
|
@@ -788,7 +886,9 @@ var ManagerUtils = class {
|
|
|
788
886
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'soundcloud' enabled");
|
|
789
887
|
}
|
|
790
888
|
if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
|
|
791
|
-
throw new Error(
|
|
889
|
+
throw new Error(
|
|
890
|
+
"Query / Link Provided for this Source but Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)"
|
|
891
|
+
);
|
|
792
892
|
}
|
|
793
893
|
if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
|
|
794
894
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'twitch' enabled");
|
|
@@ -815,7 +915,9 @@ var ManagerUtils = class {
|
|
|
815
915
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'yandexmusic' enabled");
|
|
816
916
|
}
|
|
817
917
|
if (SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
818
|
-
throw new Error(
|
|
918
|
+
throw new Error(
|
|
919
|
+
"Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled"
|
|
920
|
+
);
|
|
819
921
|
}
|
|
820
922
|
if (SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
|
|
821
923
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
|
|
@@ -825,46 +927,112 @@ var ManagerUtils = class {
|
|
|
825
927
|
}
|
|
826
928
|
return;
|
|
827
929
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
};
|
|
835
|
-
const foundSource = Object.keys(DefaultSources).find((source) => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
930
|
+
/**
|
|
931
|
+
* 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).
|
|
932
|
+
* @param queryString
|
|
933
|
+
* @returns
|
|
934
|
+
*/
|
|
935
|
+
findSourceOfQuery(queryString) {
|
|
936
|
+
const foundSource = Object.keys(DefaultSources).find((source) => queryString?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
836
937
|
if (foundSource && !["https", "http"].includes(foundSource) && DefaultSources[foundSource]) {
|
|
837
|
-
|
|
838
|
-
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
|
|
938
|
+
return foundSource;
|
|
839
939
|
}
|
|
840
|
-
return
|
|
940
|
+
return null;
|
|
841
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Extracts the source from the query if it starts with a valid source prefix (e.g., "ytsearch:") and updates the searchQuery object accordingly.
|
|
944
|
+
* @param searchQuery
|
|
945
|
+
* @returns The updated searchQuery object with the extracted source and modified query string.
|
|
946
|
+
*/
|
|
947
|
+
extractSourceOfQuery(searchQuery) {
|
|
948
|
+
const foundSource = this.findSourceOfQuery(searchQuery.query);
|
|
949
|
+
if (foundSource) {
|
|
950
|
+
searchQuery.source = DefaultSources[foundSource];
|
|
951
|
+
searchQuery.query = searchQuery.query.slice(`${foundSource}:`.length, searchQuery.query.length);
|
|
952
|
+
}
|
|
953
|
+
return searchQuery;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* 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.
|
|
957
|
+
* @param input
|
|
958
|
+
* @returns
|
|
959
|
+
*/
|
|
960
|
+
typedLowerCase(input) {
|
|
961
|
+
if (!input) return input;
|
|
962
|
+
if (typeof input === "string") return input.toLowerCase();
|
|
963
|
+
return input;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* 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.
|
|
967
|
+
* @param query
|
|
968
|
+
* @returns
|
|
969
|
+
*/
|
|
970
|
+
transformQuery(query) {
|
|
971
|
+
const typedDefault = this.typedLowerCase(this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform);
|
|
972
|
+
if (typeof query === "string") {
|
|
973
|
+
const Query = {
|
|
974
|
+
query,
|
|
975
|
+
extraQueryUrlParams: void 0,
|
|
976
|
+
source: typedDefault
|
|
977
|
+
};
|
|
978
|
+
return this.extractSourceOfQuery(Query);
|
|
979
|
+
}
|
|
980
|
+
const providedSource = query?.source?.trim?.()?.toLowerCase?.();
|
|
981
|
+
const validSourceExtracted = DefaultSources[providedSource ?? typedDefault];
|
|
982
|
+
return this.extractSourceOfQuery({
|
|
983
|
+
query: query.query,
|
|
984
|
+
extraQueryUrlParams: query.extraQueryUrlParams,
|
|
985
|
+
source: validSourceExtracted ?? providedSource ?? typedDefault
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* 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.
|
|
990
|
+
* @param query
|
|
991
|
+
* @returns
|
|
992
|
+
*/
|
|
842
993
|
transformLavaSearchQuery(query) {
|
|
843
|
-
const
|
|
994
|
+
const typedDefault = this.typedLowerCase(this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform);
|
|
995
|
+
if (typeof query === "string") {
|
|
996
|
+
const Query2 = {
|
|
997
|
+
query,
|
|
998
|
+
types: [],
|
|
999
|
+
extraQueryUrlParams: void 0,
|
|
1000
|
+
source: typedDefault
|
|
1001
|
+
};
|
|
1002
|
+
return this.extractSourceOfQuery(Query2);
|
|
1003
|
+
}
|
|
1004
|
+
const providedSource = query?.source?.trim?.()?.toLowerCase?.();
|
|
1005
|
+
const validSourceExtracted = DefaultSources[providedSource ?? typedDefault];
|
|
844
1006
|
const Query = {
|
|
845
|
-
query:
|
|
846
|
-
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(
|
|
1007
|
+
query: query.query,
|
|
1008
|
+
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(
|
|
1009
|
+
(v) => query.types?.find((x) => x.toLowerCase().startsWith(v))
|
|
1010
|
+
) : [
|
|
847
1011
|
"track",
|
|
848
1012
|
"playlist",
|
|
849
1013
|
"artist",
|
|
850
1014
|
"album"
|
|
851
1015
|
/*"text"*/
|
|
852
1016
|
],
|
|
853
|
-
source:
|
|
1017
|
+
source: validSourceExtracted ?? providedSource ?? typedDefault
|
|
854
1018
|
};
|
|
855
|
-
|
|
856
|
-
if (foundSource && DefaultSources[foundSource]) {
|
|
857
|
-
Query.source = DefaultSources[foundSource];
|
|
858
|
-
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
|
|
859
|
-
}
|
|
860
|
-
return Query;
|
|
1019
|
+
return this.extractSourceOfQuery(Query);
|
|
861
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* 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.
|
|
1023
|
+
* @param node
|
|
1024
|
+
* @param sourceString
|
|
1025
|
+
* @returns
|
|
1026
|
+
*/
|
|
862
1027
|
validateSourceString(node, sourceString) {
|
|
863
1028
|
if (!sourceString) throw new Error(`No SourceString was provided`);
|
|
864
1029
|
const source = DefaultSources[sourceString.toLowerCase().trim()];
|
|
865
|
-
if (!source
|
|
1030
|
+
if (!source && !!this.LavalinkManager.options.playerOptions.allowCustomSources)
|
|
1031
|
+
throw new Error(
|
|
1032
|
+
`Lavalink-Client does not support SearchQuerySource: '${sourceString}'. You can disable this check by setting 'ManagerOptions.PlayerOptions.allowCustomSources' to true`
|
|
1033
|
+
);
|
|
866
1034
|
if (!node.info) throw new Error("Lavalink Node does not have any info cached yet, not ready yet!");
|
|
867
|
-
if (!
|
|
1035
|
+
if (!node._checkForSources) return;
|
|
868
1036
|
if (source === "amsearch" && !node.info?.sourceManagers?.includes("applemusic")) {
|
|
869
1037
|
throw new Error("Lavalink Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
|
|
870
1038
|
}
|
|
@@ -878,15 +1046,21 @@ var ManagerUtils = class {
|
|
|
878
1046
|
throw new Error("Lavalink Node has not 'http' enabled, which is required to have 'dzisrc' to work");
|
|
879
1047
|
}
|
|
880
1048
|
if (source === "jsrec" && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
881
|
-
throw new Error(
|
|
1049
|
+
throw new Error(
|
|
1050
|
+
"Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jsrec' to work"
|
|
1051
|
+
);
|
|
882
1052
|
}
|
|
883
1053
|
if (source === "jssearch" && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
884
|
-
throw new Error(
|
|
1054
|
+
throw new Error(
|
|
1055
|
+
"Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jssearch' to work"
|
|
1056
|
+
);
|
|
885
1057
|
}
|
|
886
1058
|
if (source === "scsearch" && !node.info?.sourceManagers?.includes("soundcloud")) {
|
|
887
1059
|
throw new Error("Lavalink Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
|
|
888
1060
|
}
|
|
889
|
-
if (source === "speak" &&
|
|
1061
|
+
if (source === "speak" && node._checkForPlugins && !node.info?.plugins?.find(
|
|
1062
|
+
(c) => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase())
|
|
1063
|
+
)) {
|
|
890
1064
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
891
1065
|
}
|
|
892
1066
|
if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
@@ -895,7 +1069,9 @@ var ManagerUtils = class {
|
|
|
895
1069
|
if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
896
1070
|
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
|
|
897
1071
|
}
|
|
898
|
-
if (source === "tts" &&
|
|
1072
|
+
if (source === "tts" && node._checkForPlugins && !node.info?.plugins?.find(
|
|
1073
|
+
(c) => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase())
|
|
1074
|
+
)) {
|
|
899
1075
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
900
1076
|
}
|
|
901
1077
|
if (source === "ftts" && !(node.info?.sourceManagers?.includes("ftts") || node.info?.sourceManagers?.includes("flowery-tts") || node.info?.sourceManagers?.includes("flowerytts"))) {
|
|
@@ -958,13 +1134,15 @@ var MiniMap = class extends Map {
|
|
|
958
1134
|
async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
959
1135
|
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) {
|
|
960
1136
|
player.queue.previous.unshift(player.queue.current);
|
|
961
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1137
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1138
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
962
1139
|
await player.queue.utils.save();
|
|
963
1140
|
}
|
|
964
1141
|
if (player.repeatMode === "queue" && player.queue.current) player.queue.tracks.push(player.queue.current);
|
|
965
1142
|
const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
|
|
966
1143
|
try {
|
|
967
|
-
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
1144
|
+
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
1145
|
+
await nextSong.resolve(player);
|
|
968
1146
|
player.queue.current = nextSong || null;
|
|
969
1147
|
await player.queue.utils.save();
|
|
970
1148
|
} catch (error) {
|
|
@@ -977,7 +1155,8 @@ async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
|
977
1155
|
});
|
|
978
1156
|
}
|
|
979
1157
|
player.LavalinkManager.emit("trackError", player, player.queue.current, error);
|
|
980
|
-
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
1158
|
+
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
1159
|
+
return queueTrackEnd(player);
|
|
981
1160
|
}
|
|
982
1161
|
return player.queue.current;
|
|
983
1162
|
}
|
|
@@ -989,18 +1168,23 @@ async function applyUnresolvedData(resTrack, data, utils) {
|
|
|
989
1168
|
if (data.info.title?.length) resTrack.info.title = data.info.title;
|
|
990
1169
|
if (data.info.author?.length) resTrack.info.author = data.info.author;
|
|
991
1170
|
} else {
|
|
992
|
-
if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
1171
|
+
if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
1172
|
+
resTrack.info.title = data.info.title;
|
|
993
1173
|
if (resTrack.info.author !== data.info.author) resTrack.info.author = data.info.author;
|
|
994
1174
|
if (resTrack.info.artworkUrl !== data.info.artworkUrl) resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
995
1175
|
}
|
|
996
|
-
for (const key of Object.keys(data.info))
|
|
1176
|
+
for (const key of Object.keys(data.info))
|
|
1177
|
+
if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key])
|
|
1178
|
+
resTrack.info[key] = data.info[key];
|
|
997
1179
|
return resTrack;
|
|
998
1180
|
}
|
|
999
1181
|
async function getClosestTrack(data, player) {
|
|
1000
1182
|
if (!player || !player.node) throw new RangeError("No player with a lavalink node was provided");
|
|
1001
|
-
if (player.LavalinkManager.utils.isTrack(data))
|
|
1183
|
+
if (player.LavalinkManager.utils.isTrack(data))
|
|
1184
|
+
return player.LavalinkManager.utils.buildTrack(data, data.requester);
|
|
1002
1185
|
if (!player.LavalinkManager.utils.isUnresolvedTrack(data)) throw new RangeError("Track is not an unresolved Track");
|
|
1003
|
-
if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
|
|
1186
|
+
if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
|
|
1187
|
+
throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
|
|
1004
1188
|
if (!data.requester) throw new SyntaxError("The requester is required");
|
|
1005
1189
|
if (typeof data.encoded === "string") {
|
|
1006
1190
|
const r = await player.node.decode.singleTrack(data.encoded, data.requester);
|
|
@@ -1012,44 +1196,60 @@ async function getClosestTrack(data, player) {
|
|
|
1012
1196
|
}
|
|
1013
1197
|
const query = [data.info?.title, data.info?.author].filter((str) => !!str).join(" by ");
|
|
1014
1198
|
const sourceName = data.info?.sourceName;
|
|
1015
|
-
return await player.search(
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1199
|
+
return await player.search(
|
|
1200
|
+
{
|
|
1201
|
+
query,
|
|
1202
|
+
source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform
|
|
1203
|
+
},
|
|
1204
|
+
data.requester
|
|
1205
|
+
).then((res) => {
|
|
1019
1206
|
let trackToUse = null;
|
|
1020
|
-
if ((data.info?.title || data.info?.author) && !trackToUse)
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1207
|
+
if ((data.info?.title || data.info?.author) && !trackToUse)
|
|
1208
|
+
trackToUse = res.tracks.find(
|
|
1209
|
+
(track) => (
|
|
1210
|
+
// find via author name (i ... case insensitve)
|
|
1211
|
+
[data.info?.author || "", `${data.info?.author} - Topic`].some(
|
|
1212
|
+
(name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)
|
|
1213
|
+
) || // find via title (i ... case insensitve)
|
|
1214
|
+
new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title)
|
|
1215
|
+
)
|
|
1216
|
+
);
|
|
1217
|
+
if (data.info?.isrc && !trackToUse)
|
|
1218
|
+
trackToUse = res.tracks.find((track) => track.info?.isrc === data.info?.isrc);
|
|
1219
|
+
if (data.info?.duration && !trackToUse)
|
|
1220
|
+
trackToUse = res.tracks.find(
|
|
1221
|
+
(track) => track.info?.duration >= data.info?.duration - 1500 && track?.info.duration <= data.info?.duration + 1500
|
|
1222
|
+
);
|
|
1029
1223
|
return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
|
|
1030
1224
|
});
|
|
1031
1225
|
}
|
|
1032
1226
|
function safeStringify(obj, padding = 0) {
|
|
1033
1227
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1034
|
-
return JSON.stringify(
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1228
|
+
return JSON.stringify(
|
|
1229
|
+
obj,
|
|
1230
|
+
(key, value) => {
|
|
1231
|
+
if (typeof value === "function") return void 0;
|
|
1232
|
+
if (typeof value === "symbol") return void 0;
|
|
1233
|
+
if (typeof value === "bigint") return value.toString();
|
|
1234
|
+
if (typeof value === "object" && value !== null) {
|
|
1235
|
+
if (seen.has(value)) return "[Circular]";
|
|
1236
|
+
seen.add(value);
|
|
1237
|
+
}
|
|
1238
|
+
return value;
|
|
1239
|
+
},
|
|
1240
|
+
padding
|
|
1241
|
+
);
|
|
1044
1242
|
}
|
|
1045
1243
|
|
|
1046
1244
|
// src/structures/Node.ts
|
|
1047
|
-
var LavalinkNode = class {
|
|
1245
|
+
var LavalinkNode = class _LavalinkNode {
|
|
1048
1246
|
heartBeatPingTimestamp = 0;
|
|
1049
1247
|
heartBeatPongTimestamp = 0;
|
|
1050
1248
|
heartBeatInterval;
|
|
1051
1249
|
pingTimeout;
|
|
1250
|
+
nodeType = "Lavalink";
|
|
1052
1251
|
isAlive = false;
|
|
1252
|
+
static _NodeLinkClass = null;
|
|
1053
1253
|
/** The provided Options of the Node */
|
|
1054
1254
|
options;
|
|
1055
1255
|
/** The amount of rest calls the node has made. */
|
|
@@ -1089,7 +1289,7 @@ var LavalinkNode = class {
|
|
|
1089
1289
|
};
|
|
1090
1290
|
/** The current sessionId, only present when connected */
|
|
1091
1291
|
sessionId = null;
|
|
1092
|
-
/**
|
|
1292
|
+
/** Wether the node resuming is enabled or not */
|
|
1093
1293
|
resuming = { enabled: true, timeout: null };
|
|
1094
1294
|
/** Actual Lavalink Information of the Node */
|
|
1095
1295
|
info = null;
|
|
@@ -1118,16 +1318,17 @@ var LavalinkNode = class {
|
|
|
1118
1318
|
return this.heartBeatPongTimestamp - this.heartBeatPingTimestamp;
|
|
1119
1319
|
}
|
|
1120
1320
|
/**
|
|
1121
|
-
* Returns
|
|
1321
|
+
* Returns wether the plugin validations are enabled or not
|
|
1122
1322
|
*/
|
|
1123
1323
|
get _checkForPlugins() {
|
|
1124
|
-
|
|
1324
|
+
if (this.nodeType === "NodeLink") return false;
|
|
1325
|
+
return !!this.options?.autoChecks?.pluginValidations;
|
|
1125
1326
|
}
|
|
1126
1327
|
/**
|
|
1127
|
-
* Returns
|
|
1328
|
+
* Returns wether the source validations are enabled or not
|
|
1128
1329
|
*/
|
|
1129
1330
|
get _checkForSources() {
|
|
1130
|
-
return !!this.
|
|
1331
|
+
return !!this.options?.autoChecks?.sourcesValidations;
|
|
1131
1332
|
}
|
|
1132
1333
|
/**
|
|
1133
1334
|
* Emits a debug event to the LavalinkManager
|
|
@@ -1188,13 +1389,22 @@ var LavalinkNode = class {
|
|
|
1188
1389
|
retryTimespan: -1,
|
|
1189
1390
|
requestSignalTimeoutMS: 1e4,
|
|
1190
1391
|
heartBeatInterval: 3e4,
|
|
1191
|
-
closeOnError: true,
|
|
1192
1392
|
enablePingOnStatsCheck: true,
|
|
1193
|
-
|
|
1393
|
+
closeOnError: true,
|
|
1394
|
+
...options,
|
|
1395
|
+
autoChecks: {
|
|
1396
|
+
sourcesValidations: options?.autoChecks?.sourcesValidations ?? true,
|
|
1397
|
+
pluginValidations: options?.autoChecks?.pluginValidations ?? true
|
|
1398
|
+
}
|
|
1194
1399
|
};
|
|
1400
|
+
if (this.options.nodeType === "NodeLink" && this.constructor.name === "LavalinkNode" && _LavalinkNode._NodeLinkClass) {
|
|
1401
|
+
return new _LavalinkNode._NodeLinkClass(options, manager);
|
|
1402
|
+
}
|
|
1403
|
+
this.nodeType = this.options.nodeType || "Lavalink";
|
|
1195
1404
|
this.NodeManager = manager;
|
|
1196
1405
|
this.validate();
|
|
1197
|
-
if (this.options.secure && this.options.port !== 443)
|
|
1406
|
+
if (this.options.secure && this.options.port !== 443)
|
|
1407
|
+
throw new SyntaxError("If secure is true, then the port must be 443");
|
|
1198
1408
|
this.options.regions = (this.options.regions || []).map((a) => a.toLowerCase());
|
|
1199
1409
|
Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
|
|
1200
1410
|
}
|
|
@@ -1215,7 +1425,7 @@ var LavalinkNode = class {
|
|
|
1215
1425
|
path: `/${this.version}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`,
|
|
1216
1426
|
method: "GET",
|
|
1217
1427
|
headers: {
|
|
1218
|
-
|
|
1428
|
+
Authorization: this.options.authorization
|
|
1219
1429
|
},
|
|
1220
1430
|
signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : void 0
|
|
1221
1431
|
};
|
|
@@ -1234,18 +1444,22 @@ var LavalinkNode = class {
|
|
|
1234
1444
|
return { response, options };
|
|
1235
1445
|
}
|
|
1236
1446
|
async request(endpoint, modify, parseAsText) {
|
|
1237
|
-
if (!this.connected)
|
|
1447
|
+
if (!this.connected)
|
|
1448
|
+
throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
|
|
1238
1449
|
const { response, options } = await this.rawRequest(endpoint, modify);
|
|
1239
1450
|
if (["DELETE", "PUT"].includes(options.method)) return;
|
|
1240
1451
|
if (response.status === 204) return;
|
|
1241
|
-
if (response.status === 404)
|
|
1452
|
+
if (response.status === 404)
|
|
1453
|
+
throw new Error(
|
|
1454
|
+
`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`
|
|
1455
|
+
);
|
|
1242
1456
|
return parseAsText ? await response.text() : await response.json();
|
|
1243
1457
|
}
|
|
1244
1458
|
/**
|
|
1245
1459
|
* Search something raw on the node, please note only add tracks to players of that node
|
|
1246
1460
|
* @param query SearchQuery Object
|
|
1247
1461
|
* @param requestUser Request User for creating the player(s)
|
|
1248
|
-
* @param throwOnEmpty
|
|
1462
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
1249
1463
|
* @returns Searchresult
|
|
1250
1464
|
*
|
|
1251
1465
|
* @example
|
|
@@ -1259,7 +1473,7 @@ var LavalinkNode = class {
|
|
|
1259
1473
|
const Query = this._LManager.utils.transformQuery(query);
|
|
1260
1474
|
this._LManager.utils.validateQueryString(this, Query.query, Query.source);
|
|
1261
1475
|
if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
|
|
1262
|
-
if (["bcsearch", "bandcamp"].includes(Query.source) && this.
|
|
1476
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && this._checkForSources && !this.info.sourceManagers.includes("bandcamp")) {
|
|
1263
1477
|
throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
|
|
1264
1478
|
}
|
|
1265
1479
|
const requestUrl = new URL(`${this.restAddress}/loadtracks`);
|
|
@@ -1295,8 +1509,14 @@ var LavalinkNode = class {
|
|
|
1295
1509
|
author: res.data.info?.author || res.data.pluginInfo?.author || null,
|
|
1296
1510
|
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,
|
|
1297
1511
|
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,
|
|
1298
|
-
selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this._LManager.utils.buildTrack(
|
|
1299
|
-
|
|
1512
|
+
selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this._LManager.utils.buildTrack(
|
|
1513
|
+
resTracks[res.data?.info?.selectedTrack],
|
|
1514
|
+
requestUser
|
|
1515
|
+
) : null,
|
|
1516
|
+
duration: resTracks.length ? resTracks.reduce(
|
|
1517
|
+
(acc, cur) => acc + (cur?.info?.duration || cur?.info?.length || 0),
|
|
1518
|
+
0
|
|
1519
|
+
) : 0
|
|
1300
1520
|
} : null,
|
|
1301
1521
|
tracks: resTracks.length ? resTracks.map((t) => this._LManager.utils.buildTrack(t, requestUser)) : []
|
|
1302
1522
|
};
|
|
@@ -1305,7 +1525,7 @@ var LavalinkNode = class {
|
|
|
1305
1525
|
* Search something using the lavaSearchPlugin (filtered searches by types)
|
|
1306
1526
|
* @param query LavaSearchQuery Object
|
|
1307
1527
|
* @param requestUser Request User for creating the player(s)
|
|
1308
|
-
* @param throwOnEmpty
|
|
1528
|
+
* @param throwOnEmpty Wether to throw on an empty result or not
|
|
1309
1529
|
* @returns LavaSearchresult (SearchResult if link is provided)
|
|
1310
1530
|
*
|
|
1311
1531
|
* @example
|
|
@@ -1317,11 +1537,19 @@ var LavalinkNode = class {
|
|
|
1317
1537
|
async lavaSearch(query, requestUser, throwOnEmpty = false) {
|
|
1318
1538
|
const Query = this._LManager.utils.transformLavaSearchQuery(query);
|
|
1319
1539
|
if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
|
|
1320
|
-
if (/^https?:\/\//.test(Query.query))
|
|
1321
|
-
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
|
|
1540
|
+
if (/^https?:\/\//.test(Query.query))
|
|
1541
|
+
return this.search({ query: Query.query, source: Query.source }, requestUser);
|
|
1542
|
+
if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
|
|
1543
|
+
throw new SyntaxError(
|
|
1544
|
+
`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`
|
|
1545
|
+
);
|
|
1546
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasearch-plugin"))
|
|
1547
|
+
throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.id}`);
|
|
1548
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin"))
|
|
1549
|
+
throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
|
|
1550
|
+
const { response } = await this.rawRequest(
|
|
1551
|
+
`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`
|
|
1552
|
+
);
|
|
1325
1553
|
const res = response.status === 204 ? {} : await response.json();
|
|
1326
1554
|
if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
|
|
1327
1555
|
this._emitDebugEvent("LavaSearchNothingFound" /* LavaSearchNothingFound */, {
|
|
@@ -1333,10 +1561,25 @@ var LavalinkNode = class {
|
|
|
1333
1561
|
}
|
|
1334
1562
|
return {
|
|
1335
1563
|
tracks: res.tracks?.map((v) => this._LManager.utils.buildTrack(v, requestUser)) || [],
|
|
1336
|
-
albums: res.albums?.map((v) => ({
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1564
|
+
albums: res.albums?.map((v) => ({
|
|
1565
|
+
info: v.info,
|
|
1566
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1567
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1568
|
+
})) || [],
|
|
1569
|
+
artists: res.artists?.map((v) => ({
|
|
1570
|
+
info: v.info,
|
|
1571
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1572
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1573
|
+
})) || [],
|
|
1574
|
+
playlists: res.playlists?.map((v) => ({
|
|
1575
|
+
info: v.info,
|
|
1576
|
+
pluginInfo: v?.plugin || v.pluginInfo,
|
|
1577
|
+
tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
|
|
1578
|
+
})) || [],
|
|
1579
|
+
texts: res.texts?.map((v) => ({
|
|
1580
|
+
text: v.text,
|
|
1581
|
+
pluginInfo: v?.plugin || v.pluginInfo
|
|
1582
|
+
})) || [],
|
|
1340
1583
|
pluginInfo: res.pluginInfo || res?.plugin
|
|
1341
1584
|
};
|
|
1342
1585
|
}
|
|
@@ -1360,7 +1603,10 @@ var LavalinkNode = class {
|
|
|
1360
1603
|
r.body = safeStringify(data.playerOptions);
|
|
1361
1604
|
if (data.noReplace) {
|
|
1362
1605
|
const url = new URL(`${this.restAddress}${r.path}`);
|
|
1363
|
-
url.searchParams.append(
|
|
1606
|
+
url.searchParams.append(
|
|
1607
|
+
"noReplace",
|
|
1608
|
+
data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false"
|
|
1609
|
+
);
|
|
1364
1610
|
r.path = url.pathname + url.search;
|
|
1365
1611
|
}
|
|
1366
1612
|
});
|
|
@@ -1414,19 +1660,26 @@ var LavalinkNode = class {
|
|
|
1414
1660
|
const headers = {
|
|
1415
1661
|
Authorization: this.options.authorization,
|
|
1416
1662
|
"User-Id": this._LManager.options.client.id,
|
|
1417
|
-
"Client-Name": this._LManager.options.client.username || "Lavalink-Client"
|
|
1663
|
+
"Client-Name": String(this._LManager.options.client.username || "Lavalink-Client").replace(
|
|
1664
|
+
/[^\x20-\x7E]/g,
|
|
1665
|
+
""
|
|
1666
|
+
)
|
|
1418
1667
|
};
|
|
1419
1668
|
if (typeof this.options.sessionId === "string" || typeof sessionId === "string") {
|
|
1420
1669
|
headers["Session-Id"] = this.options.sessionId || sessionId;
|
|
1421
1670
|
this.sessionId = this.options.sessionId || sessionId;
|
|
1422
1671
|
}
|
|
1423
|
-
this.socket = new import_ws.default(
|
|
1672
|
+
this.socket = new import_ws.default(
|
|
1673
|
+
`ws${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}/v4/websocket`,
|
|
1674
|
+
{ headers }
|
|
1675
|
+
);
|
|
1424
1676
|
this.socket.on("open", this.open.bind(this));
|
|
1425
1677
|
this.socket.on("close", (code, reason) => this.close(code, reason?.toString()));
|
|
1426
1678
|
this.socket.on("message", this.message.bind(this));
|
|
1427
1679
|
this.socket.on("error", this.error.bind(this));
|
|
1428
1680
|
}
|
|
1429
1681
|
heartBeat() {
|
|
1682
|
+
if (this.nodeType !== "Lavalink") return;
|
|
1430
1683
|
this._emitDebugEvent("HeartBeatTriggered" /* HeartBeatTriggered */, {
|
|
1431
1684
|
state: "log",
|
|
1432
1685
|
message: `Node Socket Heartbeat triggered, resetting old Timeout to 65000ms (should happen every 60s due to /stats event)`,
|
|
@@ -1466,7 +1719,7 @@ var LavalinkNode = class {
|
|
|
1466
1719
|
/**
|
|
1467
1720
|
* Destroys the Node-Connection (Websocket) and all player's of the node
|
|
1468
1721
|
* @param destroyReason Destroy Reason to use when destroying the players
|
|
1469
|
-
* @param deleteNode
|
|
1722
|
+
* @param deleteNode wether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
|
|
1470
1723
|
* @param movePlayers whether to movePlayers to different eligible connected node. If false players won't be moved @default false
|
|
1471
1724
|
* @returns void
|
|
1472
1725
|
*
|
|
@@ -1488,8 +1741,11 @@ var LavalinkNode = class {
|
|
|
1488
1741
|
this.socket?.removeAllListeners();
|
|
1489
1742
|
this.socket = null;
|
|
1490
1743
|
this.resetReconnectionAttempts();
|
|
1491
|
-
if (!deleteNode)
|
|
1492
|
-
|
|
1744
|
+
if (!deleteNode)
|
|
1745
|
+
return void this.NodeManager.emit("disconnect", this, {
|
|
1746
|
+
code: 1e3,
|
|
1747
|
+
reason: destroyReason
|
|
1748
|
+
});
|
|
1493
1749
|
this.NodeManager.emit("destroy", this, destroyReason);
|
|
1494
1750
|
this.NodeManager.nodes.delete(this.id);
|
|
1495
1751
|
this.resetAckTimeouts(true, true);
|
|
@@ -1497,55 +1753,67 @@ var LavalinkNode = class {
|
|
|
1497
1753
|
}
|
|
1498
1754
|
const handlePlayerOperations = () => {
|
|
1499
1755
|
if (!movePlayers) {
|
|
1500
|
-
return Promise.allSettled(
|
|
1501
|
-
(
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1756
|
+
return Promise.allSettled(
|
|
1757
|
+
Array.from(players.values()).map(
|
|
1758
|
+
(player) => player.destroy(destroyReason || "NodeDestroy" /* NodeDestroy */).catch((error) => {
|
|
1759
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1760
|
+
state: "error",
|
|
1761
|
+
message: `Failed to destroy player ${player.guildId}: ${error.message}`,
|
|
1762
|
+
error,
|
|
1763
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1764
|
+
});
|
|
1765
|
+
})
|
|
1766
|
+
)
|
|
1767
|
+
);
|
|
1510
1768
|
}
|
|
1511
|
-
const nodeToMove = Array.from(this.NodeManager.leastUsedNodes("playingPlayers")).find(
|
|
1769
|
+
const nodeToMove = Array.from(this.NodeManager.leastUsedNodes("playingPlayers")).find(
|
|
1770
|
+
(n) => n.connected && n.options.id !== this.id
|
|
1771
|
+
);
|
|
1512
1772
|
if (!nodeToMove) {
|
|
1513
|
-
return Promise.allSettled(
|
|
1514
|
-
(
|
|
1515
|
-
|
|
1773
|
+
return Promise.allSettled(
|
|
1774
|
+
Array.from(players.values()).map(
|
|
1775
|
+
(player) => player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((error) => {
|
|
1776
|
+
this._emitDebugEvent("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
|
|
1777
|
+
state: "error",
|
|
1778
|
+
message: `Failed to destroy player ${player.guildId}: ${error.message}`,
|
|
1779
|
+
error,
|
|
1780
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1781
|
+
});
|
|
1782
|
+
})
|
|
1783
|
+
)
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
return Promise.allSettled(
|
|
1787
|
+
Array.from(players.values()).map(
|
|
1788
|
+
(player) => player.changeNode(nodeToMove.options.id).catch((error) => {
|
|
1789
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1516
1790
|
state: "error",
|
|
1517
|
-
message: `Failed to
|
|
1791
|
+
message: `Failed to move player ${player.guildId}: ${error.message}`,
|
|
1518
1792
|
error,
|
|
1519
1793
|
functionLayer: "Node > destroy() > movePlayers"
|
|
1520
1794
|
});
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
message: `Failed to move player ${player.guildId}: ${error.message}`,
|
|
1529
|
-
error,
|
|
1530
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1531
|
-
});
|
|
1532
|
-
return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1533
|
-
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1534
|
-
state: "error",
|
|
1535
|
-
message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
|
|
1536
|
-
error: destroyError,
|
|
1537
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1795
|
+
return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1796
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1797
|
+
state: "error",
|
|
1798
|
+
message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
|
|
1799
|
+
error: destroyError,
|
|
1800
|
+
functionLayer: "Node > destroy() > movePlayers"
|
|
1801
|
+
});
|
|
1538
1802
|
});
|
|
1539
|
-
})
|
|
1540
|
-
|
|
1541
|
-
)
|
|
1803
|
+
})
|
|
1804
|
+
)
|
|
1805
|
+
);
|
|
1542
1806
|
};
|
|
1543
1807
|
return void handlePlayerOperations().finally(() => {
|
|
1544
1808
|
this.socket?.close(1e3, "Node-Destroy");
|
|
1545
1809
|
this.socket?.removeAllListeners();
|
|
1546
1810
|
this.socket = null;
|
|
1547
1811
|
this.resetReconnectionAttempts();
|
|
1548
|
-
if (!deleteNode)
|
|
1812
|
+
if (!deleteNode)
|
|
1813
|
+
return void this.NodeManager.emit("disconnect", this, {
|
|
1814
|
+
code: 1e3,
|
|
1815
|
+
reason: destroyReason
|
|
1816
|
+
});
|
|
1549
1817
|
this.NodeManager.emit("destroy", this, destroyReason);
|
|
1550
1818
|
this.NodeManager.nodes.delete(this.id);
|
|
1551
1819
|
this.resetAckTimeouts(true, true);
|
|
@@ -1646,7 +1914,12 @@ var LavalinkNode = class {
|
|
|
1646
1914
|
*/
|
|
1647
1915
|
singleTrack: async (encoded, requester) => {
|
|
1648
1916
|
if (!encoded) throw new SyntaxError("No encoded (Base64 string) was provided");
|
|
1649
|
-
return this._LManager.utils?.buildTrack(
|
|
1917
|
+
return this._LManager.utils?.buildTrack(
|
|
1918
|
+
await this.request(
|
|
1919
|
+
`/decodetrack?encodedTrack=${encodeURIComponent(encoded.replace(/\s/g, ""))}`
|
|
1920
|
+
),
|
|
1921
|
+
requester
|
|
1922
|
+
);
|
|
1650
1923
|
},
|
|
1651
1924
|
/**
|
|
1652
1925
|
* Decodes multiple tracks into their info
|
|
@@ -1662,7 +1935,8 @@ var LavalinkNode = class {
|
|
|
1662
1935
|
* ```
|
|
1663
1936
|
*/
|
|
1664
1937
|
multipleTracks: async (encodeds, requester) => {
|
|
1665
|
-
if (!Array.isArray(encodeds) || !encodeds.every((v) => typeof v === "string" && v.length > 1))
|
|
1938
|
+
if (!Array.isArray(encodeds) || !encodeds.every((v) => typeof v === "string" && v.length > 1))
|
|
1939
|
+
throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
|
|
1666
1940
|
return await this.request(`/decodetracks`, (r) => {
|
|
1667
1941
|
r.method = "POST";
|
|
1668
1942
|
r.body = safeStringify(encodeds);
|
|
@@ -1674,7 +1948,7 @@ var LavalinkNode = class {
|
|
|
1674
1948
|
/**
|
|
1675
1949
|
* Get the lyrics of a track
|
|
1676
1950
|
* @param track the track to get the lyrics for
|
|
1677
|
-
* @param skipTrackSource
|
|
1951
|
+
* @param skipTrackSource wether to skip the track source or not
|
|
1678
1952
|
* @returns the lyrics of the track
|
|
1679
1953
|
* @example
|
|
1680
1954
|
*
|
|
@@ -1686,8 +1960,14 @@ var LavalinkNode = class {
|
|
|
1686
1960
|
*/
|
|
1687
1961
|
get: async (track, skipTrackSource = false) => {
|
|
1688
1962
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1689
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1690
|
-
|
|
1963
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1964
|
+
throw new RangeError(
|
|
1965
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1966
|
+
);
|
|
1967
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin"))
|
|
1968
|
+
throw new RangeError(
|
|
1969
|
+
`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`
|
|
1970
|
+
);
|
|
1691
1971
|
const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
|
|
1692
1972
|
return await this.request(url);
|
|
1693
1973
|
},
|
|
@@ -1695,7 +1975,7 @@ var LavalinkNode = class {
|
|
|
1695
1975
|
* Get the lyrics of the current playing track
|
|
1696
1976
|
*
|
|
1697
1977
|
* @param guildId the guild id of the player
|
|
1698
|
-
* @param skipTrackSource
|
|
1978
|
+
* @param skipTrackSource wether to skip the track source or not
|
|
1699
1979
|
* @returns the lyrics of the current playing track
|
|
1700
1980
|
* @example
|
|
1701
1981
|
* ```ts
|
|
@@ -1706,8 +1986,14 @@ var LavalinkNode = class {
|
|
|
1706
1986
|
*/
|
|
1707
1987
|
getCurrent: async (guildId, skipTrackSource = false) => {
|
|
1708
1988
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1709
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1710
|
-
|
|
1989
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
1990
|
+
throw new RangeError(
|
|
1991
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
1992
|
+
);
|
|
1993
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin"))
|
|
1994
|
+
throw new RangeError(
|
|
1995
|
+
`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`
|
|
1996
|
+
);
|
|
1711
1997
|
const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
|
|
1712
1998
|
return await this.request(url);
|
|
1713
1999
|
},
|
|
@@ -1725,7 +2011,10 @@ var LavalinkNode = class {
|
|
|
1725
2011
|
*/
|
|
1726
2012
|
subscribe: async (guildId) => {
|
|
1727
2013
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1728
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
2014
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
2015
|
+
throw new RangeError(
|
|
2016
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
2017
|
+
);
|
|
1729
2018
|
return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
|
|
1730
2019
|
options.method = "POST";
|
|
1731
2020
|
});
|
|
@@ -1744,7 +2033,10 @@ var LavalinkNode = class {
|
|
|
1744
2033
|
*/
|
|
1745
2034
|
unsubscribe: async (guildId) => {
|
|
1746
2035
|
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");
|
|
1747
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
2036
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
|
|
2037
|
+
throw new RangeError(
|
|
2038
|
+
`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`
|
|
2039
|
+
);
|
|
1748
2040
|
return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
|
|
1749
2041
|
options.method = "DELETE";
|
|
1750
2042
|
});
|
|
@@ -1772,7 +2064,10 @@ var LavalinkNode = class {
|
|
|
1772
2064
|
* ```
|
|
1773
2065
|
*/
|
|
1774
2066
|
async fetchConnectionMetrics() {
|
|
1775
|
-
if (this.info && !this.info.isNodelink)
|
|
2067
|
+
if (this.info && !this.info.isNodelink)
|
|
2068
|
+
throw new Error(
|
|
2069
|
+
"There is no Information about wether you are using NodeLink instead of Lavalink, so this function won't work"
|
|
2070
|
+
);
|
|
1776
2071
|
return await this.request(`/connection`);
|
|
1777
2072
|
}
|
|
1778
2073
|
/**
|
|
@@ -1785,9 +2080,13 @@ var LavalinkNode = class {
|
|
|
1785
2080
|
* ```
|
|
1786
2081
|
*/
|
|
1787
2082
|
async fetchVersion() {
|
|
1788
|
-
return await this.request(
|
|
1789
|
-
|
|
1790
|
-
|
|
2083
|
+
return await this.request(
|
|
2084
|
+
`/version`,
|
|
2085
|
+
(r) => {
|
|
2086
|
+
r.path = "/version";
|
|
2087
|
+
},
|
|
2088
|
+
true
|
|
2089
|
+
);
|
|
1791
2090
|
}
|
|
1792
2091
|
/**
|
|
1793
2092
|
* Request Lavalink information.
|
|
@@ -1803,6 +2102,160 @@ var LavalinkNode = class {
|
|
|
1803
2102
|
async fetchInfo() {
|
|
1804
2103
|
return await this.request(`/info`);
|
|
1805
2104
|
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Returns the metric summary of the node
|
|
2107
|
+
* @returns the metric summary of the node
|
|
2108
|
+
*/
|
|
2109
|
+
nodeMetricSummary() {
|
|
2110
|
+
if (!this.connected || !this.isAlive)
|
|
2111
|
+
return {
|
|
2112
|
+
systemLoad: 0,
|
|
2113
|
+
cpuLoad: 0,
|
|
2114
|
+
memoryUsage: 0,
|
|
2115
|
+
players: 0,
|
|
2116
|
+
playingPlayers: 0,
|
|
2117
|
+
uptime: 0,
|
|
2118
|
+
ping: 0,
|
|
2119
|
+
frameDeficit: 0
|
|
2120
|
+
};
|
|
2121
|
+
const _memoryUsed = this.stats.memory.used;
|
|
2122
|
+
const _memoryAllocated = this.stats.memory.allocated;
|
|
2123
|
+
return {
|
|
2124
|
+
systemLoad: this.stats.cpu.systemLoad,
|
|
2125
|
+
cpuLoad: this.stats.cpu.lavalinkLoad,
|
|
2126
|
+
memoryUsage: _memoryAllocated > 0 ? _memoryUsed / _memoryAllocated * 100 : 0,
|
|
2127
|
+
players: this.stats.players,
|
|
2128
|
+
playingPlayers: this.stats.playingPlayers,
|
|
2129
|
+
uptime: this.stats.uptime,
|
|
2130
|
+
ping: this.heartBeatPing,
|
|
2131
|
+
frameDeficit: this.stats.frameStats?.deficit || 0
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* Get the node's health status with performance assessment.
|
|
2136
|
+
* @returns Object containing health status, performance rating, load balancing info, and recommendations
|
|
2137
|
+
*
|
|
2138
|
+
* @example
|
|
2139
|
+
* ```ts
|
|
2140
|
+
* const health = node.getHealthStatus();
|
|
2141
|
+
* console.log(`Node Status: ${health.status}`); // "healthy" | "degraded" | "critical" | "offline"
|
|
2142
|
+
* console.log(`Performance: ${health.performance}`); // "excellent" | "good" | "fair" | "poor"
|
|
2143
|
+
* console.log(`Penalty Score: ${health.penaltyScore}`); // Lower is better for load balancing
|
|
2144
|
+
* console.log(`Estimated Capacity: ${health.estimatedRemainingCapacity} more players`);
|
|
2145
|
+
* console.log(`Overloaded: ${health.isOverloaded}`);
|
|
2146
|
+
* console.log(`Needs Restart: ${health.needsRestart}`);
|
|
2147
|
+
* if (health.recommendations.length) {
|
|
2148
|
+
* console.log("Recommendations:", health.recommendations);
|
|
2149
|
+
* }
|
|
2150
|
+
* ```
|
|
2151
|
+
*/
|
|
2152
|
+
getHealthStatus(thresholds) {
|
|
2153
|
+
const cpuThresholds = {
|
|
2154
|
+
excellent: 0.3,
|
|
2155
|
+
good: 0.5,
|
|
2156
|
+
fair: 0.7,
|
|
2157
|
+
poor: 0.85,
|
|
2158
|
+
...thresholds?.cpu
|
|
2159
|
+
};
|
|
2160
|
+
const memoryThresholds = {
|
|
2161
|
+
excellent: 60,
|
|
2162
|
+
good: 75,
|
|
2163
|
+
fair: 85,
|
|
2164
|
+
poor: 95,
|
|
2165
|
+
...thresholds?.memory
|
|
2166
|
+
};
|
|
2167
|
+
const pingThresholds = {
|
|
2168
|
+
excellent: 50,
|
|
2169
|
+
good: 100,
|
|
2170
|
+
fair: 200,
|
|
2171
|
+
poor: 300,
|
|
2172
|
+
...thresholds?.ping
|
|
2173
|
+
};
|
|
2174
|
+
const recommendations = [];
|
|
2175
|
+
const metrics = this.nodeMetricSummary();
|
|
2176
|
+
if (!this.connected || !this.isAlive) {
|
|
2177
|
+
return {
|
|
2178
|
+
status: "offline",
|
|
2179
|
+
performance: "poor",
|
|
2180
|
+
isOverloaded: false,
|
|
2181
|
+
needsRestart: true,
|
|
2182
|
+
penaltyScore: 999999,
|
|
2183
|
+
// Maximum penalty for offline nodes
|
|
2184
|
+
estimatedRemainingCapacity: 0,
|
|
2185
|
+
recommendations: [RecommendationsStrings.nodeOffline, RecommendationsStrings.checkConnectivity],
|
|
2186
|
+
metrics
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
let cpuScore = 0;
|
|
2190
|
+
if (metrics.cpuLoad < cpuThresholds.excellent) cpuScore = 4;
|
|
2191
|
+
else if (metrics.cpuLoad < cpuThresholds.good) cpuScore = 3;
|
|
2192
|
+
else if (metrics.cpuLoad < cpuThresholds.fair) cpuScore = 2;
|
|
2193
|
+
else if (metrics.cpuLoad < cpuThresholds.poor) cpuScore = 1;
|
|
2194
|
+
let memoryScore = 0;
|
|
2195
|
+
if (metrics.memoryUsage < memoryThresholds.excellent) memoryScore = 4;
|
|
2196
|
+
else if (metrics.memoryUsage < memoryThresholds.good) memoryScore = 3;
|
|
2197
|
+
else if (metrics.memoryUsage < memoryThresholds.fair) memoryScore = 2;
|
|
2198
|
+
else if (metrics.memoryUsage < memoryThresholds.poor) memoryScore = 1;
|
|
2199
|
+
let pingScore = 0;
|
|
2200
|
+
if (metrics.ping < pingThresholds.excellent) pingScore = 4;
|
|
2201
|
+
else if (metrics.ping < pingThresholds.good) pingScore = 3;
|
|
2202
|
+
else if (metrics.ping < pingThresholds.fair) pingScore = 2;
|
|
2203
|
+
else if (metrics.ping < pingThresholds.poor) pingScore = 1;
|
|
2204
|
+
const avgScore = (cpuScore + memoryScore + pingScore) / 3;
|
|
2205
|
+
let performance2 = "poor";
|
|
2206
|
+
if (avgScore >= 3.5) performance2 = "excellent";
|
|
2207
|
+
else if (avgScore >= 2.5) performance2 = "good";
|
|
2208
|
+
else if (avgScore >= 1.5) performance2 = "fair";
|
|
2209
|
+
const isOverloaded = metrics.cpuLoad > cpuThresholds.fair || metrics.memoryUsage > memoryThresholds.fair || metrics.frameDeficit > 100;
|
|
2210
|
+
const isCritical = metrics.cpuLoad > cpuThresholds.poor || metrics.memoryUsage > memoryThresholds.poor || metrics.frameDeficit > 500;
|
|
2211
|
+
const status = isCritical ? "critical" : isOverloaded ? "degraded" : "healthy";
|
|
2212
|
+
const needsRestart = status === "critical" || isOverloaded && metrics.memoryUsage > 90 || metrics.frameDeficit > 1e3 || this.reconnectionAttemptCount > 0 && this.reconnectionAttemptCount >= this.options.retryAmount / 2;
|
|
2213
|
+
if (metrics.cpuLoad > cpuThresholds.fair)
|
|
2214
|
+
recommendations.push(RecommendationsStrings.highCPULoad(metrics.cpuLoad));
|
|
2215
|
+
if (metrics.systemLoad > 0.8) recommendations.push(RecommendationsStrings.highSystemLoad(metrics.systemLoad));
|
|
2216
|
+
if (metrics.memoryUsage > memoryThresholds.fair)
|
|
2217
|
+
recommendations.push(RecommendationsStrings.highMemoryUsage(metrics.memoryUsage));
|
|
2218
|
+
if (metrics.frameDeficit > 100) recommendations.push(RecommendationsStrings.frameDeficit(metrics.frameDeficit));
|
|
2219
|
+
if (metrics.ping > pingThresholds.fair) recommendations.push(RecommendationsStrings.highLatency(metrics.ping));
|
|
2220
|
+
if (needsRestart) recommendations.push(RecommendationsStrings.nodeRestart);
|
|
2221
|
+
if (metrics.players > 500) recommendations.push(RecommendationsStrings.highPlayercount(metrics.players));
|
|
2222
|
+
const nullFrames = this.stats.frameStats?.nulled || 0;
|
|
2223
|
+
let penaltyScore = metrics.players + // Player count penalty (each player adds base penalty)
|
|
2224
|
+
Math.pow(metrics.cpuLoad * 100, 2) + // CPU penalty (exponential - heavily penalize high CPU)
|
|
2225
|
+
Math.pow(metrics.memoryUsage, 1.5) + // Memory penalty (exponential - heavily penalize high memory)
|
|
2226
|
+
metrics.ping * 2 + // Latency penalty
|
|
2227
|
+
metrics.frameDeficit * 10 + // Frame deficit penalty (critical for audio quality)
|
|
2228
|
+
nullFrames * 5;
|
|
2229
|
+
if (status === "critical") penaltyScore += 1e4;
|
|
2230
|
+
else if (status === "degraded") penaltyScore += 5e3;
|
|
2231
|
+
if (this.reconnectionAttemptCount > 0) penaltyScore += this.reconnectionAttemptCount * 1e3;
|
|
2232
|
+
penaltyScore = Math.round(penaltyScore);
|
|
2233
|
+
let estimatedRemainingCapacity = 0;
|
|
2234
|
+
if (status !== "critical") {
|
|
2235
|
+
const cpuCapacity = metrics.players === 0 ? 200 : metrics.cpuLoad > 0 ? Math.max(
|
|
2236
|
+
0,
|
|
2237
|
+
Math.floor((cpuThresholds.fair - metrics.cpuLoad) / metrics.cpuLoad * metrics.players)
|
|
2238
|
+
) : 200;
|
|
2239
|
+
const memoryCapacity = metrics.players === 0 ? 200 : metrics.memoryUsage > 0 ? Math.max(
|
|
2240
|
+
0,
|
|
2241
|
+
Math.floor(
|
|
2242
|
+
(memoryThresholds.fair - metrics.memoryUsage) / metrics.memoryUsage * metrics.players
|
|
2243
|
+
)
|
|
2244
|
+
) : 200;
|
|
2245
|
+
estimatedRemainingCapacity = Math.min(Math.min(cpuCapacity, memoryCapacity), 500);
|
|
2246
|
+
if (isOverloaded) estimatedRemainingCapacity = 0;
|
|
2247
|
+
}
|
|
2248
|
+
return {
|
|
2249
|
+
status,
|
|
2250
|
+
performance: performance2,
|
|
2251
|
+
isOverloaded,
|
|
2252
|
+
needsRestart,
|
|
2253
|
+
penaltyScore,
|
|
2254
|
+
estimatedRemainingCapacity,
|
|
2255
|
+
recommendations,
|
|
2256
|
+
metrics
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
1806
2259
|
/**
|
|
1807
2260
|
* Lavalink's Route Planner Api
|
|
1808
2261
|
*/
|
|
@@ -1862,6 +2315,44 @@ var LavalinkNode = class {
|
|
|
1862
2315
|
if (!this.options.authorization) throw new SyntaxError("LavalinkNode requires 'authorization'");
|
|
1863
2316
|
if (!this.options.host) throw new SyntaxError("LavalinkNode requires 'host'");
|
|
1864
2317
|
if (!this.options.port) throw new SyntaxError("LavalinkNode requires 'port'");
|
|
2318
|
+
if (typeof this.options.port !== "number" || this.options.port < 1 || this.options.port > 65535)
|
|
2319
|
+
throw new SyntaxError("LavalinkNode.port must be a number within 1 and 65535");
|
|
2320
|
+
if (this.options.closeOnError !== void 0 && typeof this.options.closeOnError !== "boolean")
|
|
2321
|
+
throw new SyntaxError("LavalinkNode.closeOnError must be either false | true aka boolean");
|
|
2322
|
+
if (this.options.retryDelay !== void 0 && typeof this.options.retryDelay !== "number")
|
|
2323
|
+
throw new SyntaxError("LavalinkNodeOptions.retryDelay must be a number");
|
|
2324
|
+
if (this.options.retryAmount !== void 0 && typeof this.options.retryAmount !== "number")
|
|
2325
|
+
throw new SyntaxError("LavalinkNodeOptions.retryAmount must be a number");
|
|
2326
|
+
if (this.options.retryTimespan !== void 0 && typeof this.options.retryTimespan !== "number")
|
|
2327
|
+
throw new SyntaxError("LavalinkNodeOptions.retryTimespan must be a number");
|
|
2328
|
+
if (this.options.requestSignalTimeoutMS !== void 0 && typeof this.options.requestSignalTimeoutMS !== "number")
|
|
2329
|
+
throw new SyntaxError("LavalinkNodeOptions.requestSignalTimeoutMS must be a number");
|
|
2330
|
+
if (this.options.heartBeatInterval !== void 0 && typeof this.options.heartBeatInterval !== "number")
|
|
2331
|
+
throw new SyntaxError("LavalinkNodeOptions.heartBeatInterval must be a number");
|
|
2332
|
+
if (this.options.enablePingOnStatsCheck !== void 0 && typeof this.options.enablePingOnStatsCheck !== "boolean")
|
|
2333
|
+
throw new SyntaxError("LavalinkNodeOptions.enablePingOnStatsCheck must be either false | true aka boolean");
|
|
2334
|
+
if (this.options.autoChecks !== void 0 && typeof this.options.autoChecks !== "object")
|
|
2335
|
+
throw new SyntaxError("LavalinkNode.autoChecks must be an object");
|
|
2336
|
+
if (this.options?.autoChecks?.sourcesValidations !== void 0 && typeof this.options?.autoChecks?.sourcesValidations !== "boolean")
|
|
2337
|
+
throw new SyntaxError("LavalinkNode.autoChecks.sourcesValidations must be either false | true aka boolean");
|
|
2338
|
+
if (this.options?.autoChecks?.pluginValidations !== void 0 && typeof this.options?.autoChecks?.pluginValidations !== "boolean")
|
|
2339
|
+
throw new SyntaxError("LavalinkNode.autoChecks.pluginValidations must be either false | true aka boolean");
|
|
2340
|
+
if (this.options.regions !== void 0 && (!Array.isArray(this.options.regions) || !this.options.regions.every((r) => typeof r === "string")))
|
|
2341
|
+
throw new SyntaxError("LavalinkNode.regions must be an Array of strings");
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Checks if the node is a NodeLink node
|
|
2345
|
+
* @returns true if the node is a NodeLink node
|
|
2346
|
+
*/
|
|
2347
|
+
isNodeLink() {
|
|
2348
|
+
return this.nodeType === "NodeLink";
|
|
2349
|
+
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Checks if the node is a Lavalink node
|
|
2352
|
+
* @returns true if the node is a Lavalink node
|
|
2353
|
+
*/
|
|
2354
|
+
isLavalinkNode() {
|
|
2355
|
+
return this.nodeType === "Lavalink";
|
|
1865
2356
|
}
|
|
1866
2357
|
/**
|
|
1867
2358
|
* Sync the data of the player you make an action to lavalink to
|
|
@@ -1884,7 +2375,9 @@ var LavalinkNode = class {
|
|
|
1884
2375
|
if (typeof data.playerOptions.voice !== "undefined") player.voice = data.playerOptions.voice;
|
|
1885
2376
|
if (typeof data.playerOptions.volume !== "undefined") {
|
|
1886
2377
|
if (this._LManager.options.playerOptions.volumeDecrementer) {
|
|
1887
|
-
player.volume = Math.round(
|
|
2378
|
+
player.volume = Math.round(
|
|
2379
|
+
data.playerOptions.volume / this._LManager.options.playerOptions.volumeDecrementer
|
|
2380
|
+
);
|
|
1888
2381
|
player.lavalinkVolume = Math.round(data.playerOptions.volume);
|
|
1889
2382
|
} else {
|
|
1890
2383
|
player.volume = Math.round(data.playerOptions.volume);
|
|
@@ -1894,16 +2387,26 @@ var LavalinkNode = class {
|
|
|
1894
2387
|
if (typeof data.playerOptions.filters !== "undefined") {
|
|
1895
2388
|
const oldFilterTimescale = { ...player.filterManager.data.timescale };
|
|
1896
2389
|
Object.freeze(oldFilterTimescale);
|
|
1897
|
-
if (data.playerOptions.filters.timescale)
|
|
1898
|
-
|
|
1899
|
-
if (data.playerOptions.filters.
|
|
1900
|
-
|
|
1901
|
-
if (data.playerOptions.filters.
|
|
1902
|
-
|
|
1903
|
-
if (data.playerOptions.filters.
|
|
1904
|
-
|
|
1905
|
-
if (data.playerOptions.filters.
|
|
1906
|
-
|
|
2390
|
+
if (data.playerOptions.filters.timescale)
|
|
2391
|
+
player.filterManager.data.timescale = data.playerOptions.filters.timescale;
|
|
2392
|
+
if (data.playerOptions.filters.distortion)
|
|
2393
|
+
player.filterManager.data.distortion = data.playerOptions.filters.distortion;
|
|
2394
|
+
if (data.playerOptions.filters.pluginFilters)
|
|
2395
|
+
player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
|
|
2396
|
+
if (data.playerOptions.filters.vibrato)
|
|
2397
|
+
player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
|
|
2398
|
+
if (data.playerOptions.filters.volume)
|
|
2399
|
+
player.filterManager.data.volume = data.playerOptions.filters.volume;
|
|
2400
|
+
if (data.playerOptions.filters.equalizer)
|
|
2401
|
+
player.filterManager.equalizerBands = data.playerOptions.filters.equalizer;
|
|
2402
|
+
if (data.playerOptions.filters.karaoke)
|
|
2403
|
+
player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
|
|
2404
|
+
if (data.playerOptions.filters.lowPass)
|
|
2405
|
+
player.filterManager.data.lowPass = data.playerOptions.filters.lowPass;
|
|
2406
|
+
if (data.playerOptions.filters.rotation)
|
|
2407
|
+
player.filterManager.data.rotation = data.playerOptions.filters.rotation;
|
|
2408
|
+
if (data.playerOptions.filters.tremolo)
|
|
2409
|
+
player.filterManager.data.tremolo = data.playerOptions.filters.tremolo;
|
|
1907
2410
|
player.filterManager.checkFiltersState(oldFilterTimescale);
|
|
1908
2411
|
}
|
|
1909
2412
|
}
|
|
@@ -1932,7 +2435,7 @@ var LavalinkNode = class {
|
|
|
1932
2435
|
}
|
|
1933
2436
|
/**
|
|
1934
2437
|
* Reconnect to the lavalink node
|
|
1935
|
-
* @param force @default false
|
|
2438
|
+
* @param force @default false Wether to instantly try to reconnect (force it)
|
|
1936
2439
|
* @returns void
|
|
1937
2440
|
*
|
|
1938
2441
|
* @example
|
|
@@ -1957,14 +2460,13 @@ var LavalinkNode = class {
|
|
|
1957
2460
|
}, this.options.retryDelay || 1e3);
|
|
1958
2461
|
}
|
|
1959
2462
|
get reconnectionAttemptCount() {
|
|
1960
|
-
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1961
2463
|
const maxAllowedTimestan = this.options.retryTimespan || -1;
|
|
1962
2464
|
if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
|
|
1963
|
-
return this.reconnectAttempts
|
|
2465
|
+
return this.reconnectAttempts.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length;
|
|
1964
2466
|
}
|
|
1965
2467
|
/**
|
|
1966
2468
|
* Private Utility function to execute the reconnection
|
|
1967
|
-
|
|
2469
|
+
*/
|
|
1968
2470
|
executeReconnect() {
|
|
1969
2471
|
if (this.reconnectionAttemptCount >= this.options.retryAmount) {
|
|
1970
2472
|
const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
|
|
@@ -1973,8 +2475,11 @@ var LavalinkNode = class {
|
|
|
1973
2475
|
this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
|
|
1974
2476
|
return;
|
|
1975
2477
|
}
|
|
1976
|
-
|
|
2478
|
+
const MAX_RECONNECT_ATTEMPTS = 1e3;
|
|
1977
2479
|
this.reconnectAttempts.push(Date.now());
|
|
2480
|
+
if (this.reconnectAttempts.length > MAX_RECONNECT_ATTEMPTS) {
|
|
2481
|
+
this.reconnectAttempts = this.reconnectAttempts.slice(-MAX_RECONNECT_ATTEMPTS);
|
|
2482
|
+
}
|
|
1978
2483
|
this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
|
|
1979
2484
|
this.NodeManager.emit("reconnecting", this);
|
|
1980
2485
|
this.connect();
|
|
@@ -2011,20 +2516,23 @@ var LavalinkNode = class {
|
|
|
2011
2516
|
async open() {
|
|
2012
2517
|
this.isAlive = true;
|
|
2013
2518
|
this.resetReconnectionAttempts();
|
|
2014
|
-
if (this.
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
this.
|
|
2018
|
-
this.
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2519
|
+
if (this.nodeType === "Lavalink") {
|
|
2520
|
+
if (this.options.enablePingOnStatsCheck) this.heartBeat();
|
|
2521
|
+
if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
|
|
2522
|
+
if (this.options.heartBeatInterval > 0) {
|
|
2523
|
+
this.socket.on("pong", () => {
|
|
2524
|
+
this.heartBeatPongTimestamp = performance.now();
|
|
2525
|
+
this.isAlive = true;
|
|
2526
|
+
});
|
|
2527
|
+
this.heartBeatInterval = setInterval(() => {
|
|
2528
|
+
if (!this.socket)
|
|
2529
|
+
return console.error("Node-Heartbeat-Interval - Socket not available - maybe reconnecting?");
|
|
2530
|
+
if (!this.isAlive) return this.close(500, "Node-Heartbeat-Timeout");
|
|
2531
|
+
this.isAlive = false;
|
|
2532
|
+
this.heartBeatPingTimestamp = performance.now();
|
|
2533
|
+
this.socket?.ping?.();
|
|
2534
|
+
}, this.options.heartBeatInterval || 3e4);
|
|
2535
|
+
}
|
|
2028
2536
|
}
|
|
2029
2537
|
this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
|
|
2030
2538
|
if (!this.info && ["v3", "v4"].includes(this.version)) {
|
|
@@ -2063,8 +2571,7 @@ var LavalinkNode = class {
|
|
|
2063
2571
|
this._LManager.players.filter((p) => p?.node?.options?.id === this?.options?.id).forEach((p) => {
|
|
2064
2572
|
if (!this._LManager.options.autoMove) return p.playing = false;
|
|
2065
2573
|
if (this._LManager.options.autoMove) {
|
|
2066
|
-
if (this.NodeManager.nodes.filter((n) => n.connected).size === 0)
|
|
2067
|
-
return p.playing = false;
|
|
2574
|
+
if (this.NodeManager.nodes.filter((n) => n.connected).size === 0) return p.playing = false;
|
|
2068
2575
|
p.moveNode();
|
|
2069
2576
|
}
|
|
2070
2577
|
});
|
|
@@ -2080,7 +2587,6 @@ var LavalinkNode = class {
|
|
|
2080
2587
|
if (this.pingTimeout) clearTimeout(this.pingTimeout);
|
|
2081
2588
|
this.socket?.close(500, "Node-Error - Force Reconnect");
|
|
2082
2589
|
}
|
|
2083
|
-
;
|
|
2084
2590
|
}
|
|
2085
2591
|
/** @private util function for handling message events from websocket */
|
|
2086
2592
|
async message(d) {
|
|
@@ -2104,11 +2610,12 @@ var LavalinkNode = class {
|
|
|
2104
2610
|
case "playerUpdate":
|
|
2105
2611
|
{
|
|
2106
2612
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2107
|
-
if (!player)
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2613
|
+
if (!player)
|
|
2614
|
+
return this._emitDebugEvent("PlayerUpdateNoPlayer" /* PlayerUpdateNoPlayer */, {
|
|
2615
|
+
state: "error",
|
|
2616
|
+
message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
|
|
2617
|
+
functionLayer: "LavalinkNode > nodeEvent > playerUpdate"
|
|
2618
|
+
});
|
|
2112
2619
|
const oldPlayer = player?.toJSON();
|
|
2113
2620
|
player.lastPositionChange = Date.now();
|
|
2114
2621
|
player.lastPosition = payload.state.position || 0;
|
|
@@ -2159,21 +2666,13 @@ var LavalinkNode = class {
|
|
|
2159
2666
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2160
2667
|
if (!player) return;
|
|
2161
2668
|
const NodeLinkEventType = payload.type;
|
|
2162
|
-
const NodeLinkExclusiveEvents = [
|
|
2163
|
-
"PlayerCreatedEvent",
|
|
2164
|
-
"PlayerDestroyedEvent",
|
|
2165
|
-
"PlayerConnectedEvent",
|
|
2166
|
-
"PlayerReconnectingEvent",
|
|
2167
|
-
"VolumeChangedEvent",
|
|
2168
|
-
"FiltersChangedEvent",
|
|
2169
|
-
"SeekEvent",
|
|
2170
|
-
"PauseEvent",
|
|
2171
|
-
"ConnectionStatusEvent",
|
|
2172
|
-
"MixStartedEvent",
|
|
2173
|
-
"MixEndedEvent"
|
|
2174
|
-
];
|
|
2175
2669
|
if (NodeLinkExclusiveEvents.includes(NodeLinkEventType) && (!this.info || this.info.isNodelink)) {
|
|
2176
|
-
return this.nodeLinkEventHandler(
|
|
2670
|
+
return this.nodeLinkEventHandler(
|
|
2671
|
+
NodeLinkEventType,
|
|
2672
|
+
player,
|
|
2673
|
+
player.queue.current,
|
|
2674
|
+
payload
|
|
2675
|
+
);
|
|
2177
2676
|
}
|
|
2178
2677
|
switch (payload.type) {
|
|
2179
2678
|
case "TrackStartEvent":
|
|
@@ -2213,7 +2712,12 @@ var LavalinkNode = class {
|
|
|
2213
2712
|
this.LyricsNotFound(player, player.queue.current, payload);
|
|
2214
2713
|
break;
|
|
2215
2714
|
default:
|
|
2216
|
-
this.NodeManager.emit(
|
|
2715
|
+
this.NodeManager.emit(
|
|
2716
|
+
"error",
|
|
2717
|
+
this,
|
|
2718
|
+
new Error(`Node#event unknown event '${payload.type}'.`),
|
|
2719
|
+
payload
|
|
2720
|
+
);
|
|
2217
2721
|
break;
|
|
2218
2722
|
}
|
|
2219
2723
|
return;
|
|
@@ -2272,7 +2776,8 @@ var LavalinkNode = class {
|
|
|
2272
2776
|
this._LManager.emit("trackEnd", player, trackToUse, payload);
|
|
2273
2777
|
return;
|
|
2274
2778
|
}
|
|
2275
|
-
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
2779
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
2780
|
+
return this.queueEnd(player, track, payload);
|
|
2276
2781
|
if (["loadFailed", "cleanup"].includes(payload.reason)) {
|
|
2277
2782
|
if (player.get("internal_destroystatus") === true) return;
|
|
2278
2783
|
await queueTrackEnd(player);
|
|
@@ -2286,7 +2791,8 @@ var LavalinkNode = class {
|
|
|
2286
2791
|
if (player.repeatMode !== "track" || player.get("internal_skipped")) await queueTrackEnd(player);
|
|
2287
2792
|
else if (trackToUse && !trackToUse?.pluginInfo?.clientData?.previousTrack) {
|
|
2288
2793
|
player.queue.previous.unshift(trackToUse);
|
|
2289
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2794
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
2795
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
2290
2796
|
await player.queue.utils.save();
|
|
2291
2797
|
}
|
|
2292
2798
|
if (!player.queue.current) return this.queueEnd(player, trackToUse, payload);
|
|
@@ -2300,7 +2806,9 @@ var LavalinkNode = class {
|
|
|
2300
2806
|
/** @private util function for handling trackStuck event */
|
|
2301
2807
|
async trackStuck(player, track, payload) {
|
|
2302
2808
|
if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
2303
|
-
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2809
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2810
|
+
(v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
|
|
2811
|
+
);
|
|
2304
2812
|
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
2305
2813
|
if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
2306
2814
|
this._emitDebugEvent("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */, {
|
|
@@ -2315,7 +2823,10 @@ var LavalinkNode = class {
|
|
|
2315
2823
|
this._LManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
|
|
2316
2824
|
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
|
|
2317
2825
|
try {
|
|
2318
|
-
await player.node.updatePlayer({
|
|
2826
|
+
await player.node.updatePlayer({
|
|
2827
|
+
guildId: player.guildId,
|
|
2828
|
+
playerOptions: { track: { encoded: null } }
|
|
2829
|
+
});
|
|
2319
2830
|
return;
|
|
2320
2831
|
} catch {
|
|
2321
2832
|
return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
|
|
@@ -2333,7 +2844,9 @@ var LavalinkNode = class {
|
|
|
2333
2844
|
/** @private util function for handling trackError event */
|
|
2334
2845
|
async trackError(player, track, payload) {
|
|
2335
2846
|
if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
|
|
2336
|
-
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2847
|
+
const oldTimestamps = (player.get("internal_erroredTracksTimestamps") || []).filter(
|
|
2848
|
+
(v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
|
|
2849
|
+
);
|
|
2337
2850
|
player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
|
|
2338
2851
|
if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
|
|
2339
2852
|
this._emitDebugEvent("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */, {
|
|
@@ -2385,8 +2898,11 @@ var LavalinkNode = class {
|
|
|
2385
2898
|
* ```
|
|
2386
2899
|
*/
|
|
2387
2900
|
async getSponsorBlock(player) {
|
|
2388
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2389
|
-
|
|
2901
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2902
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2903
|
+
return await this.request(
|
|
2904
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`
|
|
2905
|
+
);
|
|
2390
2906
|
}
|
|
2391
2907
|
/**
|
|
2392
2908
|
* Set the current sponsorblocks for the sponsorblock plugin
|
|
@@ -2400,15 +2916,25 @@ var LavalinkNode = class {
|
|
|
2400
2916
|
* ```
|
|
2401
2917
|
*/
|
|
2402
2918
|
async setSponsorBlock(player, segments = ["sponsor", "selfpromo"]) {
|
|
2403
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2919
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2920
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2404
2921
|
if (!segments.length) throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
|
|
2405
|
-
if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
|
|
2922
|
+
if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
|
|
2923
|
+
throw new SyntaxError(
|
|
2924
|
+
`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map((v) => `'${v}'`).join(", ")}`
|
|
2925
|
+
);
|
|
2406
2926
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
2407
2927
|
r.method = "PUT";
|
|
2408
|
-
r.headers = {
|
|
2928
|
+
r.headers = {
|
|
2929
|
+
Authorization: this.options.authorization,
|
|
2930
|
+
"Content-Type": "application/json"
|
|
2931
|
+
};
|
|
2409
2932
|
r.body = safeStringify(segments.map((v) => v.toLowerCase()));
|
|
2410
2933
|
});
|
|
2411
|
-
player.set(
|
|
2934
|
+
player.set(
|
|
2935
|
+
"internal_sponsorBlockCategories",
|
|
2936
|
+
segments.map((v) => v.toLowerCase())
|
|
2937
|
+
);
|
|
2412
2938
|
this._emitDebugEvent("SetSponsorBlock" /* SetSponsorBlock */, {
|
|
2413
2939
|
state: "log",
|
|
2414
2940
|
message: `SponsorBlock was set for Player: ${player.guildId} to: ${segments.map((v) => `'${v.toLowerCase()}'`).join(", ")}`,
|
|
@@ -2428,7 +2954,8 @@ var LavalinkNode = class {
|
|
|
2428
2954
|
* ```
|
|
2429
2955
|
*/
|
|
2430
2956
|
async deleteSponsorBlock(player) {
|
|
2431
|
-
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2957
|
+
if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
|
|
2958
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
2432
2959
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
2433
2960
|
r.method = "DELETE";
|
|
2434
2961
|
});
|
|
@@ -2463,11 +2990,12 @@ var LavalinkNode = class {
|
|
|
2463
2990
|
await this._LManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
2464
2991
|
player.set("internal_previousautoplay", Date.now());
|
|
2465
2992
|
if (player.queue.tracks.length > 0) await queueTrackEnd(player);
|
|
2466
|
-
else
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2993
|
+
else
|
|
2994
|
+
this._emitDebugEvent("AutoplayNoSongsAdded" /* AutoplayNoSongsAdded */, {
|
|
2995
|
+
state: "warn",
|
|
2996
|
+
message: `Autoplay was triggered but no songs were added to the queue.`,
|
|
2997
|
+
functionLayer: "LavalinkNode > queueEnd() > autoplayFunction"
|
|
2998
|
+
});
|
|
2471
2999
|
}
|
|
2472
3000
|
if (player.queue.current) {
|
|
2473
3001
|
if (payload.type === "TrackEndEvent") this._LManager.emit("trackEnd", player, track, payload);
|
|
@@ -2484,7 +3012,8 @@ var LavalinkNode = class {
|
|
|
2484
3012
|
player.set("internal_autoplayStopPlaying", void 0);
|
|
2485
3013
|
if (track && !track?.pluginInfo?.clientData?.previousTrack) {
|
|
2486
3014
|
player.queue.previous.unshift(track);
|
|
2487
|
-
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
3015
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
3016
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
2488
3017
|
await player.queue.utils.save();
|
|
2489
3018
|
}
|
|
2490
3019
|
if (payload?.reason !== "stopped") {
|
|
@@ -2500,16 +3029,23 @@ var LavalinkNode = class {
|
|
|
2500
3029
|
message: `Trigger Queue Empty Interval was Triggered because playerOptions.onEmptyQueue.destroyAfterMs is set to ${this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs}ms`,
|
|
2501
3030
|
functionLayer: "LavalinkNode > queueEnd() > destroyAfterMs"
|
|
2502
3031
|
});
|
|
2503
|
-
this._LManager.emit(
|
|
3032
|
+
this._LManager.emit(
|
|
3033
|
+
"playerQueueEmptyStart",
|
|
3034
|
+
player,
|
|
3035
|
+
this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs
|
|
3036
|
+
);
|
|
2504
3037
|
if (player.get("internal_queueempty")) clearTimeout(player.get("internal_queueempty"));
|
|
2505
|
-
player.set(
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
3038
|
+
player.set(
|
|
3039
|
+
"internal_queueempty",
|
|
3040
|
+
setTimeout(() => {
|
|
3041
|
+
player.set("internal_queueempty", void 0);
|
|
3042
|
+
if (player.queue.current) {
|
|
3043
|
+
return this._LManager.emit("playerQueueEmptyCancel", player);
|
|
3044
|
+
}
|
|
3045
|
+
this._LManager.emit("playerQueueEmptyEnd", player);
|
|
3046
|
+
player.destroy("QueueEmpty" /* QueueEmpty */);
|
|
3047
|
+
}, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs)
|
|
3048
|
+
);
|
|
2513
3049
|
}
|
|
2514
3050
|
}
|
|
2515
3051
|
this._LManager.emit("queueEnd", player, track, payload);
|
|
@@ -2586,6 +3122,310 @@ var LavalinkNode = class {
|
|
|
2586
3122
|
}
|
|
2587
3123
|
};
|
|
2588
3124
|
|
|
3125
|
+
// src/structures/NodeLink.ts
|
|
3126
|
+
var NodeLinkNode = class extends LavalinkNode {
|
|
3127
|
+
nodeType = "NodeLink";
|
|
3128
|
+
constructor(options, manager) {
|
|
3129
|
+
super(options, manager);
|
|
3130
|
+
if (this.options.nodeType === "Lavalink" && this.constructor.name === "NodeLink") {
|
|
3131
|
+
return new LavalinkNode(options, manager);
|
|
3132
|
+
}
|
|
3133
|
+
this.nodeType = "NodeLink";
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* Adds a new audio track to be mixed over the current playback.
|
|
3137
|
+
* @param player The player to add the mixer layer to.
|
|
3138
|
+
* @param trackToAdd The track to add to the mixer layer.
|
|
3139
|
+
* @param volume The volume of the track to add to the mixer layer. (0 - 100)
|
|
3140
|
+
* @link {https://nodelink.js.org/docs/api/rest#add-mix-layer} documentiation
|
|
3141
|
+
*/
|
|
3142
|
+
async addMixerLayer(player, trackToAdd, volume) {
|
|
3143
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3144
|
+
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
|
|
3145
|
+
m.method = "POST";
|
|
3146
|
+
m.body = safeStringify({
|
|
3147
|
+
track: {
|
|
3148
|
+
encoded: trackToAdd.encoded,
|
|
3149
|
+
//identifier: trackToAdd.info?.identifier, // atm not supported
|
|
3150
|
+
userData: trackToAdd.userData
|
|
3151
|
+
},
|
|
3152
|
+
volume: (volume / 100).toFixed(2)
|
|
3153
|
+
});
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Retrieves a list of currently active mix layers.
|
|
3158
|
+
* @param player The player to list the mixer layers for.
|
|
3159
|
+
* @link {https://nodelink.js.org/docs/api/rest#get-active-mixes} documentiation
|
|
3160
|
+
*/
|
|
3161
|
+
async listMixerLayers(player) {
|
|
3162
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3163
|
+
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
|
|
3164
|
+
m.method = "GET";
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Updates the volume of a specific mix layer.
|
|
3169
|
+
* @param player The player to update the mixer layer volume for.
|
|
3170
|
+
* @param mixId The ID of the mix layer to update.
|
|
3171
|
+
* @param volume The volume of the mix layer to update. (0 - 100)
|
|
3172
|
+
* @link {https://nodelink.js.org/docs/api/rest#update-mix-volume} documentiation
|
|
3173
|
+
*/
|
|
3174
|
+
async updateMixerLayerVolume(player, mixId, volume) {
|
|
3175
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3176
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
|
|
3177
|
+
m.method = "PATCH";
|
|
3178
|
+
m.body = safeStringify({
|
|
3179
|
+
volume: (volume / 100).toFixed(2)
|
|
3180
|
+
});
|
|
3181
|
+
});
|
|
3182
|
+
return true;
|
|
3183
|
+
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Removes a specific mix layer.
|
|
3186
|
+
* @param player The player to remove the mix layer from.
|
|
3187
|
+
* @param mixId The ID of the mix layer to remove.
|
|
3188
|
+
* @link {https://nodelink.js.org/docs/api/rest#remove-mix-layer} documentiation
|
|
3189
|
+
*/
|
|
3190
|
+
async removeMixerLayer(player, mixId) {
|
|
3191
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3192
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
|
|
3193
|
+
m.method = "DELETE";
|
|
3194
|
+
});
|
|
3195
|
+
return true;
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* @description
|
|
3199
|
+
* NodeLink has a lot of filters SPECIFICALLY for NodeLink, check the documentation for more information.
|
|
3200
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#additional-filters} documentiation
|
|
3201
|
+
*/
|
|
3202
|
+
specificFilters = {
|
|
3203
|
+
/**
|
|
3204
|
+
* Creates delay-based echo with feedback control
|
|
3205
|
+
* @param player The player to apply the filter to
|
|
3206
|
+
* @param options The echo filter options
|
|
3207
|
+
*/
|
|
3208
|
+
echo: async (player, options, disableFilter = false) => {
|
|
3209
|
+
if (disableFilter) delete player.filterManager.data.echo;
|
|
3210
|
+
else player.filterManager.data.echo = options;
|
|
3211
|
+
await player.filterManager.applyPlayerFilters();
|
|
3212
|
+
return player.filterManager.filters.nodeLinkEcho;
|
|
3213
|
+
},
|
|
3214
|
+
/**
|
|
3215
|
+
* Simulates multiple voices playing together with modulated delays
|
|
3216
|
+
* @param player The player to apply the filter to
|
|
3217
|
+
* @param options The chorus filter options
|
|
3218
|
+
*/
|
|
3219
|
+
chorus: async (player, options, disableFilter = false) => {
|
|
3220
|
+
if (disableFilter) delete player.filterManager.data.chorus;
|
|
3221
|
+
else player.filterManager.data.chorus = options;
|
|
3222
|
+
await player.filterManager.applyPlayerFilters();
|
|
3223
|
+
return player.filterManager.filters.nodeLinkChorus;
|
|
3224
|
+
},
|
|
3225
|
+
/**
|
|
3226
|
+
* Dynamic range compression for balanced audio levels
|
|
3227
|
+
* @param player The player to apply the filter to
|
|
3228
|
+
* @param options The compressor filter options
|
|
3229
|
+
*/
|
|
3230
|
+
compressor: async (player, options, disableFilter = false) => {
|
|
3231
|
+
if (disableFilter) delete player.filterManager.data.compressor;
|
|
3232
|
+
else player.filterManager.data.compressor = options;
|
|
3233
|
+
await player.filterManager.applyPlayerFilters();
|
|
3234
|
+
return player.filterManager.filters.nodeLinkCompressor;
|
|
3235
|
+
},
|
|
3236
|
+
/**
|
|
3237
|
+
* Filters out low frequencies, letting high frequencies pass through
|
|
3238
|
+
* @param player The player to apply the filter to
|
|
3239
|
+
* @param options The highpass filter options
|
|
3240
|
+
*/
|
|
3241
|
+
highPass: async (player, options, disableFilter = false) => {
|
|
3242
|
+
if (disableFilter) delete player.filterManager.data.highPass;
|
|
3243
|
+
else player.filterManager.data.highPass = options;
|
|
3244
|
+
await player.filterManager.applyPlayerFilters();
|
|
3245
|
+
return player.filterManager.filters.nodeLinkHighPass;
|
|
3246
|
+
},
|
|
3247
|
+
/**
|
|
3248
|
+
* Sweeps all-pass filters across the frequency spectrum for a swooshing effect
|
|
3249
|
+
* @param player The player to apply the filter to
|
|
3250
|
+
* @param options The phaser filter options
|
|
3251
|
+
*/
|
|
3252
|
+
phaser: async (player, options, disableFilter = false) => {
|
|
3253
|
+
if (disableFilter) delete player.filterManager.data.phaser;
|
|
3254
|
+
else player.filterManager.data.phaser = options;
|
|
3255
|
+
await player.filterManager.applyPlayerFilters();
|
|
3256
|
+
return player.filterManager.filters.nodeLinkPhaser;
|
|
3257
|
+
},
|
|
3258
|
+
/**
|
|
3259
|
+
* Creates spatial audio using cross-channel delays and modulation
|
|
3260
|
+
* @param player The player to apply the filter to
|
|
3261
|
+
* @param options The spatial filter options
|
|
3262
|
+
*/
|
|
3263
|
+
spatial: async (player, options, disableFilter = false) => {
|
|
3264
|
+
if (disableFilter) delete player.filterManager.data.spatial;
|
|
3265
|
+
else player.filterManager.data.spatial = options;
|
|
3266
|
+
await player.filterManager.applyPlayerFilters();
|
|
3267
|
+
return player.filterManager.filters.nodeLinkSpatial;
|
|
3268
|
+
},
|
|
3269
|
+
/**
|
|
3270
|
+
* Resets all NodeLink filters
|
|
3271
|
+
* @param player The player to reset the filters for
|
|
3272
|
+
*/
|
|
3273
|
+
resetNodeLinkFilters: async (player) => {
|
|
3274
|
+
delete player.filterManager.data.spatial;
|
|
3275
|
+
delete player.filterManager.data.echo;
|
|
3276
|
+
delete player.filterManager.data.chorus;
|
|
3277
|
+
delete player.filterManager.data.compressor;
|
|
3278
|
+
delete player.filterManager.data.highPass;
|
|
3279
|
+
delete player.filterManager.data.phaser;
|
|
3280
|
+
player.filterManager.checkFiltersState();
|
|
3281
|
+
await player.filterManager.applyPlayerFilters();
|
|
3282
|
+
return true;
|
|
3283
|
+
}
|
|
3284
|
+
};
|
|
3285
|
+
/**
|
|
3286
|
+
* Retrieve Lyrics of Youtube Videos.
|
|
3287
|
+
* @param player The Player you use with that node.
|
|
3288
|
+
* @param track if not provided, it will use the current track
|
|
3289
|
+
* @param language if not provided, it will use the default language (en)
|
|
3290
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#lyrics--chapters}
|
|
3291
|
+
* @returns NodeLinkLyrics either synced/unsynced or NodeLinkNoLyrics
|
|
3292
|
+
*/
|
|
3293
|
+
async nodeLinkLyrics(player, track, language = "en") {
|
|
3294
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3295
|
+
const encodedTrack = track?.encoded || player.queue.current?.encoded;
|
|
3296
|
+
if (!encodedTrack) throw new Error("No track provided");
|
|
3297
|
+
return await this.request(
|
|
3298
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/lyrics?encodedTrack=${encodedTrack}&lang=${language}`,
|
|
3299
|
+
(m) => {
|
|
3300
|
+
m.method = "GET";
|
|
3301
|
+
}
|
|
3302
|
+
);
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Retrieve Chapters of Youtube Videos.
|
|
3306
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#loadchapters}
|
|
3307
|
+
* @param player The Player you use with that node.
|
|
3308
|
+
* @param track if not provided, it will use the current track
|
|
3309
|
+
* @returns Array of NodeLinkChapter objects (if empty than there are no chapters available)
|
|
3310
|
+
*/
|
|
3311
|
+
async getChapters(player, track) {
|
|
3312
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3313
|
+
const encodedTrack = track?.encoded || player.queue.current?.encoded;
|
|
3314
|
+
if (!encodedTrack) throw new Error("No track provided");
|
|
3315
|
+
return await this.request(
|
|
3316
|
+
`/sessions/${this.sessionId}/players/${player.guildId}/chapters?encodedTrack=${encodedTrack}`,
|
|
3317
|
+
(m) => {
|
|
3318
|
+
m.method = "GET";
|
|
3319
|
+
}
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
3322
|
+
/**
|
|
3323
|
+
* @link {https://nodelink.js.org/docs/api/rest#node-information}
|
|
3324
|
+
* @returns
|
|
3325
|
+
*/
|
|
3326
|
+
async getConnectionMetrics() {
|
|
3327
|
+
return await this.request(`/connection`, (m) => {
|
|
3328
|
+
m.method = "GET";
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
/**
|
|
3332
|
+
* Stream audio directly from NodeLink without Discord voice connection. | Note this must be enabled by NodeLink...
|
|
3333
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#direct-streaming}
|
|
3334
|
+
*/
|
|
3335
|
+
async getDirectStream(track) {
|
|
3336
|
+
return await this.request(`/trackstream?encodedTrack=${track.encoded}`, (m) => {
|
|
3337
|
+
m.method = "GET";
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* Stream raw PCM audio for custom processing or recording.
|
|
3342
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#loadstream}
|
|
3343
|
+
* @param track The track to stream
|
|
3344
|
+
* @param volume The volume to stream at
|
|
3345
|
+
* @param position The position to stream from
|
|
3346
|
+
* @param filters The filters to apply to the stream
|
|
3347
|
+
* @returns Returns a raw PCM stream with Content-Type: audio/l16;rate=48000;channels=2.
|
|
3348
|
+
*/
|
|
3349
|
+
async loadDirectStream(track, volume, position, filters) {
|
|
3350
|
+
let requestPath = `/loadstream?encodedTrack=${track.encoded}`;
|
|
3351
|
+
if (volume && volume > 0 && volume <= 100) requestPath += `&volume=${(volume / 100).toFixed(2)}`;
|
|
3352
|
+
if (position && position > 0) requestPath += `&position=${position}`;
|
|
3353
|
+
if (filters) requestPath += `&filters=${typeof filters === "object" ? safeStringify(filters) : filters}`;
|
|
3354
|
+
const res = await this.rawRequest(requestPath, (m) => {
|
|
3355
|
+
m.method = "GET";
|
|
3356
|
+
});
|
|
3357
|
+
return res.response;
|
|
3358
|
+
}
|
|
3359
|
+
/**
|
|
3360
|
+
* NodeLink supports selecting specific audio tracks for videos that contain multiple audio streams (e.g., Netflixstyle dubs, multi-language YouTube videos).
|
|
3361
|
+
* This function changes the current language of the audio, in place at the same position of the current track.
|
|
3362
|
+
* You can always do it manually by providing extra field in the track object "audioTrackId"
|
|
3363
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#additional-filters}
|
|
3364
|
+
* @param player The player to apply the filter to
|
|
3365
|
+
* @param language_audioTrackId The language of the audio track to select, see it in the pluginInfo.audioTracks
|
|
3366
|
+
*/
|
|
3367
|
+
async changeAudioTrackLanguage(player, language_audioTrackId) {
|
|
3368
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3369
|
+
const res = await this.request(`/sessions/${this.sessionId}/players/${player.guildId}`, (r) => {
|
|
3370
|
+
r.method = "PATCH";
|
|
3371
|
+
r.headers["Content-Type"] = "application/json";
|
|
3372
|
+
r.body = safeStringify({
|
|
3373
|
+
track: {
|
|
3374
|
+
encoded: player.queue.current?.encoded,
|
|
3375
|
+
position: player.position,
|
|
3376
|
+
audioTrackId: language_audioTrackId
|
|
3377
|
+
}
|
|
3378
|
+
});
|
|
3379
|
+
});
|
|
3380
|
+
return res;
|
|
3381
|
+
}
|
|
3382
|
+
/**
|
|
3383
|
+
* Updates the YouTube configuration (RefreshToken or VisitorData) in real-time.
|
|
3384
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#update-config}
|
|
3385
|
+
*/
|
|
3386
|
+
async updateYoutubeConfig(refreshToken, visitorData) {
|
|
3387
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3388
|
+
const res = await this.request(`/youtube/config`, (r) => {
|
|
3389
|
+
r.method = "PATCH";
|
|
3390
|
+
r.headers["Content-Type"] = "application/json";
|
|
3391
|
+
r.body = safeStringify({
|
|
3392
|
+
refreshToken,
|
|
3393
|
+
visitorData
|
|
3394
|
+
});
|
|
3395
|
+
});
|
|
3396
|
+
return res;
|
|
3397
|
+
}
|
|
3398
|
+
async getYoutubeConfig(validate = false) {
|
|
3399
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3400
|
+
const res = await this.request(`/youtube/config${validate ? "?validate=true" : ""}`, (r) => {
|
|
3401
|
+
r.method = "GET";
|
|
3402
|
+
});
|
|
3403
|
+
return res;
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#oauth}
|
|
3407
|
+
*/
|
|
3408
|
+
async getYoutubeOAUTH(refreshToken) {
|
|
3409
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3410
|
+
return await this.request(`/youtube/oauth?refreshToken=${refreshToken}`, (m) => {
|
|
3411
|
+
m.method = "GET";
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* @link {https://nodelink.js.org/docs/api/nodelink-features#oauth}
|
|
3416
|
+
*/
|
|
3417
|
+
async updateYoutubeOAUTH(refreshToken) {
|
|
3418
|
+
if (!this.sessionId) throw new Error("The Lavalink Node is either not ready, or not up to date!");
|
|
3419
|
+
return await this.request(`/youtube/oauth`, (m) => {
|
|
3420
|
+
m.method = "POST";
|
|
3421
|
+
m.body = safeStringify({
|
|
3422
|
+
refreshToken
|
|
3423
|
+
});
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3426
|
+
};
|
|
3427
|
+
LavalinkNode._NodeLinkClass = NodeLinkNode;
|
|
3428
|
+
|
|
2589
3429
|
// src/structures/NodeManager.ts
|
|
2590
3430
|
var NodeManager = class extends import_events.EventEmitter {
|
|
2591
3431
|
/**
|
|
@@ -2647,9 +3487,10 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2647
3487
|
constructor(LavalinkManager2) {
|
|
2648
3488
|
super();
|
|
2649
3489
|
this.LavalinkManager = LavalinkManager2;
|
|
2650
|
-
if (this.LavalinkManager.options.nodes)
|
|
2651
|
-
this.
|
|
2652
|
-
|
|
3490
|
+
if (this.LavalinkManager.options.nodes)
|
|
3491
|
+
this.LavalinkManager.options.nodes.forEach((node) => {
|
|
3492
|
+
this.createNode(node);
|
|
3493
|
+
});
|
|
2653
3494
|
}
|
|
2654
3495
|
/**
|
|
2655
3496
|
* Disconnects all Nodes from lavalink ws sockets
|
|
@@ -2659,7 +3500,8 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2659
3500
|
*/
|
|
2660
3501
|
async disconnectAll(deleteAllNodes = false, destroyPlayers = true) {
|
|
2661
3502
|
if (!this.nodes.size) throw new Error("There are no nodes to disconnect (no nodes in the nodemanager)");
|
|
2662
|
-
if (!this.nodes.filter((v) => v.connected).size)
|
|
3503
|
+
if (!this.nodes.filter((v) => v.connected).size)
|
|
3504
|
+
throw new Error("There are no nodes to disconnect (all nodes disconnected)");
|
|
2663
3505
|
let counter = 0;
|
|
2664
3506
|
for (const node of this.nodes.values()) {
|
|
2665
3507
|
if (!node.connected) continue;
|
|
@@ -2678,7 +3520,8 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2678
3520
|
*/
|
|
2679
3521
|
async connectAll() {
|
|
2680
3522
|
if (!this.nodes.size) throw new Error("There are no nodes to connect (no nodes in the nodemanager)");
|
|
2681
|
-
if (!this.nodes.filter((v) => !v.connected).size)
|
|
3523
|
+
if (!this.nodes.filter((v) => !v.connected).size)
|
|
3524
|
+
throw new Error("There are no nodes to connect (all nodes connected)");
|
|
2682
3525
|
let counter = 0;
|
|
2683
3526
|
for (const node of this.nodes.values()) {
|
|
2684
3527
|
if (node.connected) continue;
|
|
@@ -2708,8 +3551,9 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2708
3551
|
* @returns The node that was created
|
|
2709
3552
|
*/
|
|
2710
3553
|
createNode(options) {
|
|
2711
|
-
if (this.nodes.has(options.id || `${options.host}:${options.port}`))
|
|
2712
|
-
|
|
3554
|
+
if (this.nodes.has(options.id || `${options.host}:${options.port}`))
|
|
3555
|
+
return this.nodes.get(options.id || `${options.host}:${options.port}`);
|
|
3556
|
+
const newNode = options.nodeType === "NodeLink" ? new NodeLinkNode(options, this) : new LavalinkNode(options, this);
|
|
2713
3557
|
this.nodes.set(newNode.id, newNode);
|
|
2714
3558
|
return newNode;
|
|
2715
3559
|
}
|
|
@@ -2728,12 +3572,16 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2728
3572
|
break;
|
|
2729
3573
|
case "cpuLavalink":
|
|
2730
3574
|
{
|
|
2731
|
-
return connectedNodes.sort(
|
|
3575
|
+
return connectedNodes.sort(
|
|
3576
|
+
(a, b) => (a.stats?.cpu?.lavalinkLoad || 0) - (b.stats?.cpu?.lavalinkLoad || 0)
|
|
3577
|
+
);
|
|
2732
3578
|
}
|
|
2733
3579
|
break;
|
|
2734
3580
|
case "cpuSystem":
|
|
2735
3581
|
{
|
|
2736
|
-
return connectedNodes.sort(
|
|
3582
|
+
return connectedNodes.sort(
|
|
3583
|
+
(a, b) => (a.stats?.cpu?.systemLoad || 0) - (b.stats?.cpu?.systemLoad || 0)
|
|
3584
|
+
);
|
|
2737
3585
|
}
|
|
2738
3586
|
break;
|
|
2739
3587
|
case "calls":
|
|
@@ -2743,7 +3591,9 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2743
3591
|
break;
|
|
2744
3592
|
case "playingPlayers":
|
|
2745
3593
|
{
|
|
2746
|
-
return connectedNodes.sort(
|
|
3594
|
+
return connectedNodes.sort(
|
|
3595
|
+
(a, b) => (a.stats?.playingPlayers || 0) - (b.stats?.playingPlayers || 0)
|
|
3596
|
+
);
|
|
2747
3597
|
}
|
|
2748
3598
|
break;
|
|
2749
3599
|
case "players":
|
|
@@ -2763,7 +3613,7 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2763
3613
|
* @param node The node to delete
|
|
2764
3614
|
* @param movePlayers whether to movePlayers to different connected node before deletion. @default false
|
|
2765
3615
|
* @returns
|
|
2766
|
-
*
|
|
3616
|
+
*
|
|
2767
3617
|
* @example
|
|
2768
3618
|
* Deletes the node
|
|
2769
3619
|
* ```ts
|
|
@@ -2784,13 +3634,25 @@ var NodeManager = class extends import_events.EventEmitter {
|
|
|
2784
3634
|
this.nodes.delete(decodeNode.id);
|
|
2785
3635
|
return;
|
|
2786
3636
|
}
|
|
3637
|
+
/**
|
|
3638
|
+
* Get a node from the nodeManager
|
|
3639
|
+
* @param node The node to get
|
|
3640
|
+
* @returns The node that was retrieved
|
|
3641
|
+
*/
|
|
3642
|
+
getNode(node) {
|
|
3643
|
+
const decodeNode = typeof node === "string" ? this.nodes.get(node) : node;
|
|
3644
|
+
if (!decodeNode) return void 0;
|
|
3645
|
+
if (decodeNode.nodeType === "NodeLink") return decodeNode;
|
|
3646
|
+
return decodeNode;
|
|
3647
|
+
}
|
|
2787
3648
|
};
|
|
2788
3649
|
|
|
2789
3650
|
// src/structures/CustomSearches/BandCampSearch.ts
|
|
2790
3651
|
var bandCampSearch = async (player, query, requestUser) => {
|
|
2791
3652
|
let error = null;
|
|
2792
3653
|
let tracks = [];
|
|
2793
|
-
if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
|
|
3654
|
+
if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
|
|
3655
|
+
console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
|
|
2794
3656
|
player.LavalinkManager.utils.validateQueryString(player.node, query);
|
|
2795
3657
|
try {
|
|
2796
3658
|
const requestUrl = new URL("https://bandcamp.com/api/nusearch/2/autocomplete");
|
|
@@ -2798,7 +3660,7 @@ var bandCampSearch = async (player, query, requestUser) => {
|
|
|
2798
3660
|
const data = await fetch(requestUrl.toString(), {
|
|
2799
3661
|
headers: {
|
|
2800
3662
|
"User-Agent": "android-async-http/1.4.1 (http://loopj.com/android-async-http)",
|
|
2801
|
-
|
|
3663
|
+
Cookie: "$Version=1"
|
|
2802
3664
|
}
|
|
2803
3665
|
});
|
|
2804
3666
|
if (!data.ok) throw new Error(`Bandcamp Error: ${data.statusText}`);
|
|
@@ -2808,13 +3670,18 @@ var bandCampSearch = async (player, query, requestUser) => {
|
|
|
2808
3670
|
} catch {
|
|
2809
3671
|
throw new Error("Invalid JSON response from Bandcamp");
|
|
2810
3672
|
}
|
|
2811
|
-
tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
3673
|
+
tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(
|
|
3674
|
+
(item) => player.LavalinkManager.utils.buildUnresolvedTrack(
|
|
3675
|
+
{
|
|
3676
|
+
uri: item.url || item.uri,
|
|
3677
|
+
artworkUrl: item.img,
|
|
3678
|
+
author: item.band_name,
|
|
3679
|
+
title: item.name,
|
|
3680
|
+
identifier: item.id ? `${item.id}` : item.url?.split("/")?.reverse()[0]
|
|
3681
|
+
},
|
|
3682
|
+
requestUser
|
|
3683
|
+
)
|
|
3684
|
+
);
|
|
2818
3685
|
} catch (e) {
|
|
2819
3686
|
error = e;
|
|
2820
3687
|
}
|
|
@@ -2863,6 +3730,43 @@ var DEFAULT_FILTER_DATAS = {
|
|
|
2863
3730
|
// 0 < x <= 1
|
|
2864
3731
|
},
|
|
2865
3732
|
channelMix: audioOutputsData.stereo,
|
|
3733
|
+
// NODELINK SPECIFIC
|
|
3734
|
+
echo: {
|
|
3735
|
+
delay: 0,
|
|
3736
|
+
feedback: 0,
|
|
3737
|
+
mix: 0
|
|
3738
|
+
},
|
|
3739
|
+
chorus: {
|
|
3740
|
+
rate: 0,
|
|
3741
|
+
depth: 0,
|
|
3742
|
+
delay: 0,
|
|
3743
|
+
mix: 0,
|
|
3744
|
+
feedback: 0
|
|
3745
|
+
},
|
|
3746
|
+
compressor: {
|
|
3747
|
+
threshold: 0,
|
|
3748
|
+
ratio: 1,
|
|
3749
|
+
attack: 0,
|
|
3750
|
+
release: 0,
|
|
3751
|
+
gain: 0
|
|
3752
|
+
},
|
|
3753
|
+
highPass: {
|
|
3754
|
+
smoothing: 0
|
|
3755
|
+
},
|
|
3756
|
+
phaser: {
|
|
3757
|
+
stages: 0,
|
|
3758
|
+
rate: 0,
|
|
3759
|
+
depth: 0,
|
|
3760
|
+
feedback: 0,
|
|
3761
|
+
mix: 0,
|
|
3762
|
+
minFrequency: 0,
|
|
3763
|
+
maxFrequency: 0
|
|
3764
|
+
},
|
|
3765
|
+
spatial: {
|
|
3766
|
+
depth: 0,
|
|
3767
|
+
rate: 0
|
|
3768
|
+
},
|
|
3769
|
+
// LAVALINK-FILTER-PLUGIN SPECIFIC
|
|
2866
3770
|
pluginFilters: {
|
|
2867
3771
|
"lavalink-filter-plugin": {
|
|
2868
3772
|
echo: {
|
|
@@ -2888,12 +3792,12 @@ var DEFAULT_FILTER_DATAS = {
|
|
|
2888
3792
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
2889
3793
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
2890
3794
|
},
|
|
2891
|
-
|
|
3795
|
+
normalization: {
|
|
2892
3796
|
// Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
2893
3797
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
2894
3798
|
// "adaptive": true // false
|
|
2895
3799
|
},
|
|
2896
|
-
|
|
3800
|
+
echo: {
|
|
2897
3801
|
// Self-explanatory; provides an echo effect.
|
|
2898
3802
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
2899
3803
|
// "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
|
|
@@ -2927,6 +3831,12 @@ var FilterManager = class {
|
|
|
2927
3831
|
tremolo: false,
|
|
2928
3832
|
vibrato: false,
|
|
2929
3833
|
lowPass: false,
|
|
3834
|
+
nodeLinkEcho: false,
|
|
3835
|
+
nodeLinkChorus: false,
|
|
3836
|
+
nodeLinkCompressor: false,
|
|
3837
|
+
nodeLinkHighPass: false,
|
|
3838
|
+
nodeLinkPhaser: false,
|
|
3839
|
+
nodeLinkSpatial: false,
|
|
2930
3840
|
lavalinkFilterPlugin: {
|
|
2931
3841
|
echo: false,
|
|
2932
3842
|
reverb: false
|
|
@@ -2943,21 +3853,6 @@ var FilterManager = class {
|
|
|
2943
3853
|
data = structuredClone(DEFAULT_FILTER_DATAS);
|
|
2944
3854
|
/** The Player assigned to this Filter Manager */
|
|
2945
3855
|
player;
|
|
2946
|
-
get _LManager() {
|
|
2947
|
-
return this.player.LavalinkManager;
|
|
2948
|
-
}
|
|
2949
|
-
/**
|
|
2950
|
-
* Returns whether the plugin validations are enabled or not
|
|
2951
|
-
*/
|
|
2952
|
-
get _checkForPlugins() {
|
|
2953
|
-
return !!this._LManager.options?.autoChecks?.pluginValidations;
|
|
2954
|
-
}
|
|
2955
|
-
/**
|
|
2956
|
-
* Returns whether the source validations are enabled or not
|
|
2957
|
-
*/
|
|
2958
|
-
get _checkForSources() {
|
|
2959
|
-
return !!this._LManager.options?.autoChecks?.sourcesValidations;
|
|
2960
|
-
}
|
|
2961
3856
|
/** The Constructor for the FilterManager */
|
|
2962
3857
|
constructor(player) {
|
|
2963
3858
|
this.player = player;
|
|
@@ -2983,24 +3878,33 @@ var FilterManager = class {
|
|
|
2983
3878
|
if (!this.filters.tremolo) delete sendData.tremolo;
|
|
2984
3879
|
if (!this.filters.vibrato) delete sendData.vibrato;
|
|
2985
3880
|
if (!this.filters.lavalinkFilterPlugin.echo) delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.echo;
|
|
2986
|
-
if (!this.filters.lavalinkFilterPlugin.reverb)
|
|
3881
|
+
if (!this.filters.lavalinkFilterPlugin.reverb)
|
|
3882
|
+
delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
|
|
2987
3883
|
if (!this.filters.lavalinkLavaDspxPlugin.echo) delete sendData.pluginFilters?.echo;
|
|
2988
3884
|
if (!this.filters.lavalinkLavaDspxPlugin.normalization) delete sendData.pluginFilters?.normalization;
|
|
2989
3885
|
if (!this.filters.lavalinkLavaDspxPlugin.highPass) delete sendData.pluginFilters?.["high-pass"];
|
|
2990
3886
|
if (!this.filters.lavalinkLavaDspxPlugin.lowPass) delete sendData.pluginFilters?.["low-pass"];
|
|
2991
|
-
if (sendData.pluginFilters?.["lavalink-filter-plugin"] && Object.values(sendData.pluginFilters?.["lavalink-filter-plugin"]).length === 0)
|
|
3887
|
+
if (sendData.pluginFilters?.["lavalink-filter-plugin"] && Object.values(sendData.pluginFilters?.["lavalink-filter-plugin"]).length === 0)
|
|
3888
|
+
delete sendData.pluginFilters["lavalink-filter-plugin"];
|
|
2992
3889
|
if (sendData.pluginFilters && Object.values(sendData.pluginFilters).length === 0) delete sendData.pluginFilters;
|
|
2993
3890
|
if (!this.filters.lowPass) delete sendData.lowPass;
|
|
2994
3891
|
if (!this.filters.karaoke) delete sendData.karaoke;
|
|
2995
3892
|
if (!this.filters.rotation) delete sendData.rotation;
|
|
2996
3893
|
if (this.filters.audioOutput === "stereo") delete sendData.channelMix;
|
|
3894
|
+
if (!this.filters.nodeLinkEcho) delete sendData.echo;
|
|
3895
|
+
if (!this.filters.nodeLinkChorus) delete sendData.chorus;
|
|
3896
|
+
if (!this.filters.nodeLinkCompressor) delete sendData.compressor;
|
|
3897
|
+
if (!this.filters.nodeLinkHighPass) delete sendData.highPass;
|
|
3898
|
+
if (!this.filters.nodeLinkPhaser) delete sendData.phaser;
|
|
3899
|
+
if (!this.filters.nodeLinkSpatial) delete sendData.spatial;
|
|
2997
3900
|
if (Object.values(this.data.timescale ?? {}).every((v) => v === 1)) delete sendData.timescale;
|
|
2998
3901
|
if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
|
|
2999
3902
|
sendData.equalizer = [...this.equalizerBands];
|
|
3000
3903
|
if (sendData.equalizer.length === 0) delete sendData.equalizer;
|
|
3001
3904
|
for (const key of Object.keys(sendData)) {
|
|
3002
3905
|
if (key === "pluginFilters") {
|
|
3003
|
-
} else if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.(key))
|
|
3906
|
+
} else if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.(key))
|
|
3907
|
+
delete sendData[key];
|
|
3004
3908
|
}
|
|
3005
3909
|
const now = performance.now();
|
|
3006
3910
|
if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
|
|
@@ -3059,6 +3963,12 @@ var FilterManager = class {
|
|
|
3059
3963
|
echo: Object.values(this.data.pluginFilters?.echo || {})?.length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined"
|
|
3060
3964
|
};
|
|
3061
3965
|
this.filters.lowPass = this.privateNot0(this.data.lowPass?.smoothing);
|
|
3966
|
+
this.filters.nodeLinkEcho = this.privateNot0(this.data.echo?.delay) || this.privateNot0(this.data.echo?.feedback) || this.privateNot0(this.data.echo?.mix);
|
|
3967
|
+
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);
|
|
3968
|
+
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);
|
|
3969
|
+
this.filters.nodeLinkHighPass = this.privateNot0(this.data.highPass?.smoothing);
|
|
3970
|
+
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);
|
|
3971
|
+
this.filters.nodeLinkSpatial = this.privateNot0(this.data.spatial?.depth) || this.privateNot0(this.data.spatial?.rate);
|
|
3062
3972
|
this.filters.karaoke = Object.values(this.data.karaoke ?? {}).some((v) => v !== 0);
|
|
3063
3973
|
if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
|
|
3064
3974
|
if (oldFilterTimescale.pitch !== this.data.timescale?.pitch || oldFilterTimescale.rate !== this.data.timescale?.rate || oldFilterTimescale.speed !== this.data.timescale?.speed) {
|
|
@@ -3094,6 +4004,12 @@ var FilterManager = class {
|
|
|
3094
4004
|
this.filters.karaoke = false;
|
|
3095
4005
|
this.filters.karaoke = false;
|
|
3096
4006
|
this.filters.volume = false;
|
|
4007
|
+
this.filters.nodeLinkEcho = false;
|
|
4008
|
+
this.filters.nodeLinkChorus = false;
|
|
4009
|
+
this.filters.nodeLinkCompressor = false;
|
|
4010
|
+
this.filters.nodeLinkHighPass = false;
|
|
4011
|
+
this.filters.nodeLinkPhaser = false;
|
|
4012
|
+
this.filters.nodeLinkSpatial = false;
|
|
3097
4013
|
this.filters.audioOutput = "stereo";
|
|
3098
4014
|
this.data = structuredClone(DEFAULT_FILTER_DATAS);
|
|
3099
4015
|
await this.applyPlayerFilters();
|
|
@@ -3140,8 +4056,10 @@ var FilterManager = class {
|
|
|
3140
4056
|
* ```
|
|
3141
4057
|
*/
|
|
3142
4058
|
async setAudioOutput(type) {
|
|
3143
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("channelMix"))
|
|
3144
|
-
|
|
4059
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("channelMix"))
|
|
4060
|
+
throw new Error("Node#Info#filters does not include the 'channelMix' Filter (Node has it not enable)");
|
|
4061
|
+
if (!type || !audioOutputsData[type])
|
|
4062
|
+
throw "Invalid audio type added, must be 'mono' / 'stereo' / 'left' / 'right'";
|
|
3145
4063
|
this.data = this.data ?? {};
|
|
3146
4064
|
this.data.channelMix = audioOutputsData[type];
|
|
3147
4065
|
this.filters.audioOutput = type;
|
|
@@ -3160,7 +4078,8 @@ var FilterManager = class {
|
|
|
3160
4078
|
* ```
|
|
3161
4079
|
*/
|
|
3162
4080
|
async setSpeed(speed = 1) {
|
|
3163
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4081
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4082
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3164
4083
|
this.data = this.data ?? {};
|
|
3165
4084
|
this.filters.nightcore = false;
|
|
3166
4085
|
this.filters.vaporwave = false;
|
|
@@ -3181,7 +4100,8 @@ var FilterManager = class {
|
|
|
3181
4100
|
* ```
|
|
3182
4101
|
*/
|
|
3183
4102
|
async setPitch(pitch = 1) {
|
|
3184
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4103
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4104
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3185
4105
|
this.data = this.data ?? {};
|
|
3186
4106
|
this.filters.nightcore = false;
|
|
3187
4107
|
this.filters.vaporwave = false;
|
|
@@ -3202,7 +4122,8 @@ var FilterManager = class {
|
|
|
3202
4122
|
* ```
|
|
3203
4123
|
*/
|
|
3204
4124
|
async setRate(rate = 1) {
|
|
3205
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4125
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4126
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3206
4127
|
this.data = this.data ?? {};
|
|
3207
4128
|
this.filters.nightcore = false;
|
|
3208
4129
|
this.filters.vaporwave = false;
|
|
@@ -3226,7 +4147,8 @@ var FilterManager = class {
|
|
|
3226
4147
|
* ```
|
|
3227
4148
|
*/
|
|
3228
4149
|
async toggleRotation(rotationHz = 0.2) {
|
|
3229
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("rotation"))
|
|
4150
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("rotation"))
|
|
4151
|
+
throw new Error("Node#Info#filters does not include the 'rotation' Filter (Node has it not enable)");
|
|
3230
4152
|
this.data = this.data ?? {};
|
|
3231
4153
|
this.data.rotation = this.filters.rotation ? DEFAULT_FILTER_DATAS.rotation : { rotationHz };
|
|
3232
4154
|
this.filters.rotation = !this.filters.rotation;
|
|
@@ -3249,7 +4171,8 @@ var FilterManager = class {
|
|
|
3249
4171
|
* ```
|
|
3250
4172
|
*/
|
|
3251
4173
|
async toggleVibrato(frequency = 10, depth = 1) {
|
|
3252
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("vibrato"))
|
|
4174
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("vibrato"))
|
|
4175
|
+
throw new Error("Node#Info#filters does not include the 'vibrato' Filter (Node has it not enable)");
|
|
3253
4176
|
this.data = this.data ?? {};
|
|
3254
4177
|
this.data.vibrato = this.filters.vibrato ? DEFAULT_FILTER_DATAS.vibrato : { depth, frequency };
|
|
3255
4178
|
this.filters.vibrato = !this.filters.vibrato;
|
|
@@ -3272,7 +4195,8 @@ var FilterManager = class {
|
|
|
3272
4195
|
* ```
|
|
3273
4196
|
*/
|
|
3274
4197
|
async toggleTremolo(frequency = 4, depth = 0.8) {
|
|
3275
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("tremolo"))
|
|
4198
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("tremolo"))
|
|
4199
|
+
throw new Error("Node#Info#filters does not include the 'tremolo' Filter (Node has it not enable)");
|
|
3276
4200
|
this.data = this.data ?? {};
|
|
3277
4201
|
this.data.tremolo = this.filters.tremolo ? DEFAULT_FILTER_DATAS.tremolo : { depth, frequency };
|
|
3278
4202
|
this.filters.tremolo = !this.filters.tremolo;
|
|
@@ -3294,7 +4218,8 @@ var FilterManager = class {
|
|
|
3294
4218
|
* ```
|
|
3295
4219
|
*/
|
|
3296
4220
|
async toggleLowPass(smoothing = 20) {
|
|
3297
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("lowPass"))
|
|
4221
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("lowPass"))
|
|
4222
|
+
throw new Error("Node#Info#filters does not include the 'lowPass' Filter (Node has it not enable)");
|
|
3298
4223
|
this.data = this.data ?? {};
|
|
3299
4224
|
this.data.lowPass = this.filters.lowPass ? DEFAULT_FILTER_DATAS.lowPass : { smoothing };
|
|
3300
4225
|
this.filters.lowPass = !this.filters.lowPass;
|
|
@@ -3321,8 +4246,10 @@ var FilterManager = class {
|
|
|
3321
4246
|
* ```
|
|
3322
4247
|
*/
|
|
3323
4248
|
toggleLowPass: async (boostFactor = 1, cutoffFrequency = 80) => {
|
|
3324
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3325
|
-
|
|
4249
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4250
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4251
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("low-pass"))
|
|
4252
|
+
throw new Error("Node#Info#filters does not include the 'low-pass' Filter (Node has it not enable)");
|
|
3326
4253
|
this.data = this.data ?? {};
|
|
3327
4254
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3328
4255
|
if (this.filters.lavalinkLavaDspxPlugin.lowPass) delete this.data.pluginFilters["low-pass"];
|
|
@@ -3347,8 +4274,10 @@ var FilterManager = class {
|
|
|
3347
4274
|
* ```
|
|
3348
4275
|
*/
|
|
3349
4276
|
toggleHighPass: async (boostFactor = 1, cutoffFrequency = 80) => {
|
|
3350
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3351
|
-
|
|
4277
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4278
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4279
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("high-pass"))
|
|
4280
|
+
throw new Error("Node#Info#filters does not include the 'high-pass' Filter (Node has it not enable)");
|
|
3352
4281
|
this.data = this.data ?? {};
|
|
3353
4282
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3354
4283
|
if (this.filters.lavalinkLavaDspxPlugin.highPass) delete this.data.pluginFilters["high-pass"];
|
|
@@ -3373,8 +4302,12 @@ var FilterManager = class {
|
|
|
3373
4302
|
* ```
|
|
3374
4303
|
*/
|
|
3375
4304
|
toggleNormalization: async (maxAmplitude = 0.75, adaptive = true) => {
|
|
3376
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3377
|
-
|
|
4305
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4306
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4307
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("normalization"))
|
|
4308
|
+
throw new Error(
|
|
4309
|
+
"Node#Info#filters does not include the 'normalization' Filter (Node has it not enable)"
|
|
4310
|
+
);
|
|
3378
4311
|
this.data = this.data ?? {};
|
|
3379
4312
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3380
4313
|
if (this.filters.lavalinkLavaDspxPlugin.normalization) delete this.data.pluginFilters.normalization;
|
|
@@ -3399,8 +4332,10 @@ var FilterManager = class {
|
|
|
3399
4332
|
* ```
|
|
3400
4333
|
*/
|
|
3401
4334
|
toggleEcho: async (decay = 0.5, echoLength = 0.5) => {
|
|
3402
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
3403
|
-
|
|
4335
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
|
|
4336
|
+
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
|
|
4337
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
|
|
4338
|
+
throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable)");
|
|
3404
4339
|
this.data = this.data ?? {};
|
|
3405
4340
|
this.data.pluginFilters = this.data.pluginFilters ?? {};
|
|
3406
4341
|
if (this.filters.lavalinkLavaDspxPlugin.echo) delete this.data.pluginFilters.echo;
|
|
@@ -3430,8 +4365,12 @@ var FilterManager = class {
|
|
|
3430
4365
|
* ```
|
|
3431
4366
|
*/
|
|
3432
4367
|
toggleEcho: async (delay = 4, decay = 0.8) => {
|
|
3433
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
3434
|
-
|
|
4368
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
4369
|
+
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
|
|
4370
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
|
|
4371
|
+
throw new Error(
|
|
4372
|
+
"Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)"
|
|
4373
|
+
);
|
|
3435
4374
|
this.data = this.data ?? {};
|
|
3436
4375
|
const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["lavalink-filter-plugin"];
|
|
3437
4376
|
this.data.pluginFilters = {
|
|
@@ -3461,8 +4400,12 @@ var FilterManager = class {
|
|
|
3461
4400
|
* ```
|
|
3462
4401
|
*/
|
|
3463
4402
|
toggleReverb: async (delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) => {
|
|
3464
|
-
if (this._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
3465
|
-
|
|
4403
|
+
if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavalink-filter-plugin"))
|
|
4404
|
+
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
|
|
4405
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("reverb"))
|
|
4406
|
+
throw new Error(
|
|
4407
|
+
"Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)"
|
|
4408
|
+
);
|
|
3466
4409
|
this.data = this.data ?? {};
|
|
3467
4410
|
const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["lavalink-filter-plugin"];
|
|
3468
4411
|
this.data.pluginFilters = {
|
|
@@ -3494,7 +4437,8 @@ var FilterManager = class {
|
|
|
3494
4437
|
* ```
|
|
3495
4438
|
*/
|
|
3496
4439
|
async toggleNightcore(speed = 1.289999523162842, pitch = 1.289999523162842, rate = 0.9365999523162842) {
|
|
3497
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4440
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4441
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3498
4442
|
this.data = this.data ?? {};
|
|
3499
4443
|
this.data.timescale = this.filters.nightcore ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
|
|
3500
4444
|
this.filters.nightcore = !this.filters.nightcore;
|
|
@@ -3520,7 +4464,8 @@ var FilterManager = class {
|
|
|
3520
4464
|
* ```
|
|
3521
4465
|
*/
|
|
3522
4466
|
async toggleVaporwave(speed = 0.8500000238418579, pitch = 0.800000011920929, rate = 1) {
|
|
3523
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4467
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
|
|
4468
|
+
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
|
|
3524
4469
|
this.data = this.data ?? {};
|
|
3525
4470
|
this.data.timescale = this.filters.vaporwave ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
|
|
3526
4471
|
this.filters.vaporwave = !this.filters.vaporwave;
|
|
@@ -3547,7 +4492,8 @@ var FilterManager = class {
|
|
|
3547
4492
|
* ```
|
|
3548
4493
|
*/
|
|
3549
4494
|
async toggleKaraoke(level = 1, monoLevel = 1, filterBand = 220, filterWidth = 100) {
|
|
3550
|
-
if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("karaoke"))
|
|
4495
|
+
if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("karaoke"))
|
|
4496
|
+
throw new Error("Node#Info#filters does not include the 'karaoke' Filter (Node has it not enable)");
|
|
3551
4497
|
this.data = this.data ?? {};
|
|
3552
4498
|
this.data.karaoke = this.filters.karaoke ? DEFAULT_FILTER_DATAS.karaoke : { level, monoLevel, filterBand, filterWidth };
|
|
3553
4499
|
this.filters.karaoke = !this.filters.karaoke;
|
|
@@ -3604,7 +4550,8 @@ var FilterManager = class {
|
|
|
3604
4550
|
*/
|
|
3605
4551
|
async setEQ(bands) {
|
|
3606
4552
|
if (!Array.isArray(bands)) bands = [bands];
|
|
3607
|
-
if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
|
|
4553
|
+
if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
|
|
4554
|
+
throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
|
|
3608
4555
|
for (const { band, gain } of bands) this.equalizerBands[band] = { band, gain };
|
|
3609
4556
|
if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
|
|
3610
4557
|
const now = performance.now();
|
|
@@ -3770,8 +4717,14 @@ var Queue = class {
|
|
|
3770
4717
|
this.QueueSaver = QueueSaver2;
|
|
3771
4718
|
this.options.maxPreviousTracks = this.QueueSaver?.options?.maxPreviousTracks ?? this.options.maxPreviousTracks;
|
|
3772
4719
|
this.current = this.managerUtils.isTrack(data.current) ? data.current : null;
|
|
3773
|
-
this.previous = Array.isArray(data.previous) && data.previous.some(
|
|
3774
|
-
|
|
4720
|
+
this.previous = Array.isArray(data.previous) && data.previous.some(
|
|
4721
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4722
|
+
) ? data.previous.filter(
|
|
4723
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4724
|
+
) : [];
|
|
4725
|
+
this.tracks = Array.isArray(data.tracks) && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.tracks.filter(
|
|
4726
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4727
|
+
) : [];
|
|
3775
4728
|
Object.defineProperty(this, QueueSymbol, { configurable: true, value: true });
|
|
3776
4729
|
}
|
|
3777
4730
|
/**
|
|
@@ -3782,7 +4735,8 @@ var Queue = class {
|
|
|
3782
4735
|
* Save the current cached Queue on the database/server (overides the server)
|
|
3783
4736
|
*/
|
|
3784
4737
|
save: async () => {
|
|
3785
|
-
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4738
|
+
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4739
|
+
this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
|
|
3786
4740
|
return await this.QueueSaver.set(this.guildId, this.utils.toJSON());
|
|
3787
4741
|
},
|
|
3788
4742
|
/**
|
|
@@ -3792,9 +4746,28 @@ var Queue = class {
|
|
|
3792
4746
|
sync: async (override = true, dontSyncCurrent = true) => {
|
|
3793
4747
|
const data = await this.QueueSaver.get(this.guildId);
|
|
3794
4748
|
if (!data) throw new Error(`No data found to sync for guildId: ${this.guildId}`);
|
|
3795
|
-
if (!dontSyncCurrent && !this.current && this.managerUtils.isTrack(data.current))
|
|
3796
|
-
|
|
3797
|
-
if (Array.isArray(data.
|
|
4749
|
+
if (!dontSyncCurrent && !this.current && this.managerUtils.isTrack(data.current))
|
|
4750
|
+
this.current = data.current;
|
|
4751
|
+
if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(
|
|
4752
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4753
|
+
))
|
|
4754
|
+
this.tracks.splice(
|
|
4755
|
+
override ? 0 : this.tracks.length,
|
|
4756
|
+
override ? this.tracks.length : 0,
|
|
4757
|
+
...data.tracks.filter(
|
|
4758
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4759
|
+
)
|
|
4760
|
+
);
|
|
4761
|
+
if (Array.isArray(data.previous) && data?.previous.length && data.previous.some(
|
|
4762
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4763
|
+
))
|
|
4764
|
+
this.previous.splice(
|
|
4765
|
+
0,
|
|
4766
|
+
override ? this.tracks.length : 0,
|
|
4767
|
+
...data.previous.filter(
|
|
4768
|
+
(track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
|
|
4769
|
+
)
|
|
4770
|
+
);
|
|
3798
4771
|
await this.utils.save();
|
|
3799
4772
|
return;
|
|
3800
4773
|
},
|
|
@@ -3805,7 +4778,8 @@ var Queue = class {
|
|
|
3805
4778
|
* @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
|
|
3806
4779
|
*/
|
|
3807
4780
|
toJSON: () => {
|
|
3808
|
-
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4781
|
+
if (this.previous.length > this.options.maxPreviousTracks)
|
|
4782
|
+
this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
|
|
3809
4783
|
return {
|
|
3810
4784
|
current: this.current ? { ...this.current } : null,
|
|
3811
4785
|
previous: this.previous ? [...this.previous] : [],
|
|
@@ -3817,25 +4791,28 @@ var Queue = class {
|
|
|
3817
4791
|
* @returns {number}
|
|
3818
4792
|
*/
|
|
3819
4793
|
totalDuration: () => {
|
|
3820
|
-
return this.tracks.reduce(
|
|
4794
|
+
return this.tracks.reduce(
|
|
4795
|
+
(acc, cur) => acc + (cur.info.duration || 0),
|
|
4796
|
+
this.current?.info.duration || 0
|
|
4797
|
+
);
|
|
3821
4798
|
},
|
|
3822
4799
|
/**
|
|
3823
4800
|
* Find tracks in the queue matching specific criteria.
|
|
3824
4801
|
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
3825
4802
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
3826
4803
|
* @returns Array of matching tracks with their indexes
|
|
3827
|
-
*
|
|
4804
|
+
*
|
|
3828
4805
|
* @example
|
|
3829
4806
|
* ```ts
|
|
3830
4807
|
* // Find by author
|
|
3831
4808
|
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
3832
|
-
*
|
|
4809
|
+
*
|
|
3833
4810
|
* // Find by duration range (5-10 minutes)
|
|
3834
4811
|
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
3835
|
-
*
|
|
4812
|
+
*
|
|
3836
4813
|
* // Find by title (partial match)
|
|
3837
4814
|
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
3838
|
-
*
|
|
4815
|
+
*
|
|
3839
4816
|
* // Custom predicate
|
|
3840
4817
|
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
3841
4818
|
* ```
|
|
@@ -3883,7 +4860,7 @@ var Queue = class {
|
|
|
3883
4860
|
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
3884
4861
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
3885
4862
|
* @returns First matching track with its index, or null if not found
|
|
3886
|
-
*
|
|
4863
|
+
*
|
|
3887
4864
|
* @example
|
|
3888
4865
|
* ```ts
|
|
3889
4866
|
* // Find first track by author
|
|
@@ -3891,7 +4868,7 @@ var Queue = class {
|
|
|
3891
4868
|
* if (track) {
|
|
3892
4869
|
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
3893
4870
|
* }
|
|
3894
|
-
*
|
|
4871
|
+
*
|
|
3895
4872
|
* // Find with custom predicate
|
|
3896
4873
|
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
3897
4874
|
* ```
|
|
@@ -3916,7 +4893,8 @@ var Queue = class {
|
|
|
3916
4893
|
[this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
|
|
3917
4894
|
}
|
|
3918
4895
|
}
|
|
3919
|
-
if (typeof this.queueChanges?.shuffled === "function")
|
|
4896
|
+
if (typeof this.queueChanges?.shuffled === "function")
|
|
4897
|
+
this.queueChanges.shuffled(this.guildId, oldStored, this.utils.toJSON());
|
|
3920
4898
|
await this.utils.save();
|
|
3921
4899
|
return this.tracks.length;
|
|
3922
4900
|
}
|
|
@@ -3928,14 +4906,27 @@ var Queue = class {
|
|
|
3928
4906
|
*/
|
|
3929
4907
|
async add(TrackOrTracks, index) {
|
|
3930
4908
|
if (typeof index === "number" && index >= 0 && index < this.tracks.length) {
|
|
3931
|
-
return await this.splice(
|
|
4909
|
+
return await this.splice(
|
|
4910
|
+
index,
|
|
4911
|
+
0,
|
|
4912
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
4913
|
+
);
|
|
3932
4914
|
}
|
|
3933
4915
|
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
3934
|
-
this.tracks.push(
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4916
|
+
this.tracks.push(
|
|
4917
|
+
...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
4918
|
+
);
|
|
4919
|
+
if (typeof this.queueChanges?.tracksAdd === "function")
|
|
4920
|
+
try {
|
|
4921
|
+
this.queueChanges.tracksAdd(
|
|
4922
|
+
this.guildId,
|
|
4923
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
|
|
4924
|
+
this.tracks.length,
|
|
4925
|
+
oldStored,
|
|
4926
|
+
this.utils.toJSON()
|
|
4927
|
+
);
|
|
4928
|
+
} catch {
|
|
4929
|
+
}
|
|
3939
4930
|
await this.utils.save();
|
|
3940
4931
|
return this.tracks.length;
|
|
3941
4932
|
}
|
|
@@ -3952,19 +4943,27 @@ var Queue = class {
|
|
|
3952
4943
|
if (TrackOrTracks) return await this.add(TrackOrTracks);
|
|
3953
4944
|
return null;
|
|
3954
4945
|
}
|
|
3955
|
-
if (TrackOrTracks && typeof this.queueChanges?.tracksAdd === "function")
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
4946
|
+
if (TrackOrTracks && typeof this.queueChanges?.tracksAdd === "function")
|
|
4947
|
+
try {
|
|
4948
|
+
this.queueChanges.tracksAdd(
|
|
4949
|
+
this.guildId,
|
|
4950
|
+
(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
|
|
4951
|
+
index,
|
|
4952
|
+
oldStored,
|
|
4953
|
+
this.utils.toJSON()
|
|
4954
|
+
);
|
|
4955
|
+
} catch {
|
|
4956
|
+
}
|
|
3959
4957
|
const spliced = TrackOrTracks ? this.tracks.splice(
|
|
3960
4958
|
index,
|
|
3961
4959
|
amount,
|
|
3962
4960
|
...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
|
|
3963
4961
|
) : this.tracks.splice(index, amount);
|
|
3964
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
4962
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4963
|
+
try {
|
|
4964
|
+
this.queueChanges.tracksRemoved(this.guildId, spliced, index, oldStored, this.utils.toJSON());
|
|
4965
|
+
} catch {
|
|
4966
|
+
}
|
|
3968
4967
|
await this.utils.save();
|
|
3969
4968
|
return spliced.length === 1 ? spliced[0] : spliced;
|
|
3970
4969
|
}
|
|
@@ -4009,10 +5008,17 @@ var Queue = class {
|
|
|
4009
5008
|
const toRemove2 = this.tracks[removeQueryTrack];
|
|
4010
5009
|
if (!toRemove2) return null;
|
|
4011
5010
|
const removed2 = this.tracks.splice(removeQueryTrack, 1);
|
|
4012
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
5011
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
5012
|
+
try {
|
|
5013
|
+
this.queueChanges.tracksRemoved(
|
|
5014
|
+
this.guildId,
|
|
5015
|
+
removed2,
|
|
5016
|
+
removeQueryTrack,
|
|
5017
|
+
oldStored,
|
|
5018
|
+
this.utils.toJSON()
|
|
5019
|
+
);
|
|
5020
|
+
} catch {
|
|
5021
|
+
}
|
|
4016
5022
|
await this.utils.save();
|
|
4017
5023
|
return { removed: removed2 };
|
|
4018
5024
|
}
|
|
@@ -4026,16 +5032,25 @@ var Queue = class {
|
|
|
4026
5032
|
}
|
|
4027
5033
|
}
|
|
4028
5034
|
if (!removed3.length) return null;
|
|
4029
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
5035
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
5036
|
+
try {
|
|
5037
|
+
this.queueChanges.tracksRemoved(
|
|
5038
|
+
this.guildId,
|
|
5039
|
+
removed3,
|
|
5040
|
+
removeQueryTrack,
|
|
5041
|
+
oldStored,
|
|
5042
|
+
this.utils.toJSON()
|
|
5043
|
+
);
|
|
5044
|
+
} catch {
|
|
5045
|
+
}
|
|
4033
5046
|
await this.utils.save();
|
|
4034
5047
|
return { removed: removed3 };
|
|
4035
5048
|
}
|
|
4036
|
-
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(
|
|
4037
|
-
(
|
|
4038
|
-
|
|
5049
|
+
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(
|
|
5050
|
+
({ v, i }) => removeQueryTrack.find(
|
|
5051
|
+
(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)
|
|
5052
|
+
)
|
|
5053
|
+
);
|
|
4039
5054
|
if (!tracksToRemove.length) return null;
|
|
4040
5055
|
const removed2 = [];
|
|
4041
5056
|
tracksToRemove.sort((a, b) => b.i - a.i);
|
|
@@ -4044,10 +5059,17 @@ var Queue = class {
|
|
|
4044
5059
|
removed2.unshift(...this.tracks.splice(i, 1));
|
|
4045
5060
|
}
|
|
4046
5061
|
}
|
|
4047
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
5062
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
5063
|
+
try {
|
|
5064
|
+
this.queueChanges.tracksRemoved(
|
|
5065
|
+
this.guildId,
|
|
5066
|
+
removed2,
|
|
5067
|
+
tracksToRemove.map((v) => v.i),
|
|
5068
|
+
oldStored,
|
|
5069
|
+
this.utils.toJSON()
|
|
5070
|
+
);
|
|
5071
|
+
} catch {
|
|
5072
|
+
}
|
|
4051
5073
|
await this.utils.save();
|
|
4052
5074
|
return { removed: removed2 };
|
|
4053
5075
|
}
|
|
@@ -4056,10 +5078,11 @@ var Queue = class {
|
|
|
4056
5078
|
);
|
|
4057
5079
|
if (toRemove < 0) return null;
|
|
4058
5080
|
const removed = this.tracks.splice(toRemove, 1);
|
|
4059
|
-
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
5081
|
+
if (typeof this.queueChanges?.tracksRemoved === "function")
|
|
5082
|
+
try {
|
|
5083
|
+
this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
|
|
5084
|
+
} catch {
|
|
5085
|
+
}
|
|
4063
5086
|
await this.utils.save();
|
|
4064
5087
|
return { removed };
|
|
4065
5088
|
}
|
|
@@ -4086,7 +5109,7 @@ var Queue = class {
|
|
|
4086
5109
|
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
4087
5110
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
4088
5111
|
* @returns Array of matching tracks with their indexes
|
|
4089
|
-
*
|
|
5112
|
+
*
|
|
4090
5113
|
* @example
|
|
4091
5114
|
* ```ts
|
|
4092
5115
|
* // Use the new method instead:
|
|
@@ -4102,7 +5125,7 @@ var Queue = class {
|
|
|
4102
5125
|
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
4103
5126
|
* @param predicate Function to test each track, or an object with criteria to match
|
|
4104
5127
|
* @returns First matching track with its index, or null if not found
|
|
4105
|
-
*
|
|
5128
|
+
*
|
|
4106
5129
|
* @example
|
|
4107
5130
|
* ```ts
|
|
4108
5131
|
* // Use the new method instead:
|
|
@@ -4118,15 +5141,15 @@ var Queue = class {
|
|
|
4118
5141
|
* @param sortBy Property to sort by or custom comparator function
|
|
4119
5142
|
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4120
5143
|
* @returns The queue instance for chaining
|
|
4121
|
-
*
|
|
5144
|
+
*
|
|
4122
5145
|
* @example
|
|
4123
5146
|
* ```ts
|
|
4124
5147
|
* // Sort by duration (shortest first)
|
|
4125
5148
|
* await player.queue.sortBy("duration", "asc");
|
|
4126
|
-
*
|
|
5149
|
+
*
|
|
4127
5150
|
* // Sort by title alphabetically (Z-A)
|
|
4128
5151
|
* await player.queue.sortBy("title", "desc");
|
|
4129
|
-
*
|
|
5152
|
+
*
|
|
4130
5153
|
* // Custom sorting
|
|
4131
5154
|
* await player.queue.sortBy((a, b) => {
|
|
4132
5155
|
* return a.info.title.localeCompare(b.info.title);
|
|
@@ -4134,7 +5157,6 @@ var Queue = class {
|
|
|
4134
5157
|
* ```
|
|
4135
5158
|
*/
|
|
4136
5159
|
async sortBy(sortBy, order = "asc") {
|
|
4137
|
-
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
4138
5160
|
if (typeof sortBy === "function") {
|
|
4139
5161
|
this.tracks.sort(sortBy);
|
|
4140
5162
|
} else {
|
|
@@ -4165,16 +5187,16 @@ var Queue = class {
|
|
|
4165
5187
|
* @param sortBy Property to sort by or custom comparator function
|
|
4166
5188
|
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4167
5189
|
* @returns A new sorted array of tracks (does not modify the queue)
|
|
4168
|
-
*
|
|
5190
|
+
*
|
|
4169
5191
|
* @example
|
|
4170
5192
|
* ```ts
|
|
4171
5193
|
* // Get sorted copy by duration (shortest first)
|
|
4172
5194
|
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
4173
5195
|
* // Original queue remains unchanged
|
|
4174
|
-
*
|
|
5196
|
+
*
|
|
4175
5197
|
* // Get sorted copy by title alphabetically (Z-A)
|
|
4176
5198
|
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
4177
|
-
*
|
|
5199
|
+
*
|
|
4178
5200
|
* // Custom sorting
|
|
4179
5201
|
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
4180
5202
|
* return a.info.title.localeCompare(b.info.title);
|
|
@@ -4210,12 +5232,12 @@ var Queue = class {
|
|
|
4210
5232
|
* @param start Start index (inclusive)
|
|
4211
5233
|
* @param end End index (exclusive)
|
|
4212
5234
|
* @returns Array of tracks in the specified range
|
|
4213
|
-
*
|
|
5235
|
+
*
|
|
4214
5236
|
* @example
|
|
4215
5237
|
* ```ts
|
|
4216
5238
|
* // Get tracks 5-15
|
|
4217
5239
|
* const tracks = player.queue.getTracks(5, 15);
|
|
4218
|
-
*
|
|
5240
|
+
*
|
|
4219
5241
|
* // Get first 10 tracks
|
|
4220
5242
|
* const firstTen = player.queue.getTracks(0, 10);
|
|
4221
5243
|
* ```
|
|
@@ -4303,7 +5325,8 @@ var Player = class {
|
|
|
4303
5325
|
* @param LavalinkManager
|
|
4304
5326
|
*/
|
|
4305
5327
|
constructor(options, LavalinkManager2, dontEmitPlayerCreateEvent) {
|
|
4306
|
-
if (typeof options?.customData === "object")
|
|
5328
|
+
if (typeof options?.customData === "object")
|
|
5329
|
+
for (const [key, value] of Object.entries(options.customData)) this.set(key, value);
|
|
4307
5330
|
this.options = options;
|
|
4308
5331
|
this.filterManager = new FilterManager(this);
|
|
4309
5332
|
this.LavalinkManager = LavalinkManager2;
|
|
@@ -4322,14 +5345,30 @@ var Player = class {
|
|
|
4322
5345
|
const least = this.LavalinkManager.nodeManager.leastUsedNodes();
|
|
4323
5346
|
this.node = least.filter((v) => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || least[0] || null;
|
|
4324
5347
|
}
|
|
4325
|
-
if (!this.node)
|
|
5348
|
+
if (!this.node)
|
|
5349
|
+
throw new Error(
|
|
5350
|
+
"No available Node was found, please add a LavalinkNode to the Manager via Manager.NodeManager#createNode"
|
|
5351
|
+
);
|
|
4326
5352
|
if (typeof options.volume === "number" && !isNaN(options.volume)) this.volume = Number(options.volume);
|
|
4327
5353
|
this.volume = Math.round(Math.max(Math.min(this.volume, 1e3), 0));
|
|
4328
|
-
this.lavalinkVolume = Math.round(
|
|
4329
|
-
|
|
4330
|
-
|
|
5354
|
+
this.lavalinkVolume = Math.round(
|
|
5355
|
+
Math.max(
|
|
5356
|
+
Math.min(
|
|
5357
|
+
Math.round(
|
|
5358
|
+
this.LavalinkManager.options.playerOptions.volumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
|
|
5359
|
+
),
|
|
5360
|
+
1e3
|
|
5361
|
+
),
|
|
5362
|
+
0
|
|
5363
|
+
)
|
|
5364
|
+
);
|
|
4331
5365
|
if (!dontEmitPlayerCreateEvent) this.LavalinkManager.emit("playerCreate", this);
|
|
4332
|
-
this.queue = new Queue(
|
|
5366
|
+
this.queue = new Queue(
|
|
5367
|
+
this.guildId,
|
|
5368
|
+
{},
|
|
5369
|
+
new QueueSaver(this.LavalinkManager.options.queueOptions),
|
|
5370
|
+
this.LavalinkManager.options.queueOptions
|
|
5371
|
+
);
|
|
4333
5372
|
}
|
|
4334
5373
|
/**
|
|
4335
5374
|
* Set custom data.
|
|
@@ -4393,20 +5432,30 @@ var Player = class {
|
|
|
4393
5432
|
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
4394
5433
|
if (options && "clientTrack" in options) delete options.clientTrack;
|
|
4395
5434
|
if (options && "track" in options) delete options.track;
|
|
4396
|
-
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5435
|
+
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5436
|
+
return this.play(options);
|
|
4397
5437
|
return this;
|
|
4398
5438
|
}
|
|
4399
5439
|
}
|
|
4400
|
-
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
5440
|
+
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
5441
|
+
options.clientTrack.userData = {
|
|
5442
|
+
...typeof options?.clientTrack?.requester === "object" ? {
|
|
5443
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5444
|
+
options?.clientTrack?.requester || {}
|
|
5445
|
+
)
|
|
5446
|
+
} : {},
|
|
5447
|
+
...options?.clientTrack.userData,
|
|
5448
|
+
...options.track?.userData
|
|
5449
|
+
};
|
|
4405
5450
|
options.track = {
|
|
4406
5451
|
encoded: options.clientTrack?.encoded,
|
|
4407
5452
|
requester: options.clientTrack?.requester,
|
|
4408
|
-
userData: options.clientTrack?.userData
|
|
5453
|
+
userData: options.clientTrack?.userData,
|
|
5454
|
+
audioTrackId: options.track?.audioTrackId ?? options.clientTrack?.audioTrackId
|
|
4409
5455
|
};
|
|
5456
|
+
if (options.track.audioTrackId && !this.node.isNodeLink()) {
|
|
5457
|
+
delete options.track.audioTrackId;
|
|
5458
|
+
}
|
|
4410
5459
|
}
|
|
4411
5460
|
if (options?.track?.encoded || options?.track?.identifier) {
|
|
4412
5461
|
this.queue.current = options.clientTrack || null;
|
|
@@ -4414,35 +5463,48 @@ var Player = class {
|
|
|
4414
5463
|
if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
|
|
4415
5464
|
this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
|
|
4416
5465
|
let vol = Number(this.volume);
|
|
4417
|
-
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5466
|
+
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5467
|
+
vol *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
|
|
4418
5468
|
this.lavalinkVolume = Math.round(vol);
|
|
4419
5469
|
options.volume = this.lavalinkVolume;
|
|
4420
5470
|
}
|
|
4421
|
-
const track = Object.fromEntries(
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
5471
|
+
const track = Object.fromEntries(
|
|
5472
|
+
Object.entries({
|
|
5473
|
+
encoded: options.track.encoded,
|
|
5474
|
+
identifier: options.track.identifier,
|
|
5475
|
+
userData: {
|
|
5476
|
+
...typeof options?.track?.requester === "object" ? {
|
|
5477
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5478
|
+
options?.track?.requester || {}
|
|
5479
|
+
)
|
|
5480
|
+
} : {},
|
|
5481
|
+
...options.track.userData
|
|
5482
|
+
},
|
|
5483
|
+
audioTrackId: options.track.audioTrackId
|
|
5484
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5485
|
+
);
|
|
4429
5486
|
this._emitDebugEvent("PlayerPlayWithTrackReplace" /* PlayerPlayWithTrackReplace */, {
|
|
4430
5487
|
state: "log",
|
|
4431
5488
|
message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
|
|
4432
5489
|
functionLayer: "Player > play()"
|
|
4433
5490
|
});
|
|
5491
|
+
if (track.audioTrackId && !this.node.isNodeLink()) {
|
|
5492
|
+
delete track.audioTrackId;
|
|
5493
|
+
}
|
|
4434
5494
|
return this.node.updatePlayer({
|
|
4435
5495
|
guildId: this.guildId,
|
|
4436
5496
|
noReplace: false,
|
|
4437
|
-
playerOptions: Object.fromEntries(
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
5497
|
+
playerOptions: Object.fromEntries(
|
|
5498
|
+
Object.entries({
|
|
5499
|
+
track,
|
|
5500
|
+
position: options.position ?? void 0,
|
|
5501
|
+
paused: options.paused ?? void 0,
|
|
5502
|
+
endTime: options?.endTime ?? void 0,
|
|
5503
|
+
filters: options?.filters ?? void 0,
|
|
5504
|
+
volume: options.volume ?? this.lavalinkVolume ?? void 0,
|
|
5505
|
+
voice: options.voice ?? void 0
|
|
5506
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5507
|
+
)
|
|
4446
5508
|
});
|
|
4447
5509
|
}
|
|
4448
5510
|
if (!this.queue.current && this.queue.tracks.length) await queueTrackEnd(this);
|
|
@@ -4454,11 +5516,16 @@ var Player = class {
|
|
|
4454
5516
|
});
|
|
4455
5517
|
try {
|
|
4456
5518
|
await this.queue.current.resolve(this);
|
|
4457
|
-
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
5519
|
+
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
5520
|
+
this.queue.current.userData = {
|
|
5521
|
+
...typeof this.queue.current?.requester === "object" ? {
|
|
5522
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5523
|
+
this.queue.current?.requester || {}
|
|
5524
|
+
)
|
|
5525
|
+
} : {},
|
|
5526
|
+
...this.queue.current?.userData,
|
|
5527
|
+
...options.track?.userData
|
|
5528
|
+
};
|
|
4462
5529
|
} catch (error) {
|
|
4463
5530
|
this._emitDebugEvent("PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
|
|
4464
5531
|
state: "error",
|
|
@@ -4470,7 +5537,8 @@ var Player = class {
|
|
|
4470
5537
|
if (options && "clientTrack" in options) delete options.clientTrack;
|
|
4471
5538
|
if (options && "track" in options) delete options.track;
|
|
4472
5539
|
await queueTrackEnd(this, true);
|
|
4473
|
-
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5540
|
+
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
5541
|
+
return this.play(options);
|
|
4474
5542
|
return this;
|
|
4475
5543
|
}
|
|
4476
5544
|
}
|
|
@@ -4478,31 +5546,46 @@ var Player = class {
|
|
|
4478
5546
|
if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
|
|
4479
5547
|
this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
|
|
4480
5548
|
let vol = Number(this.volume);
|
|
4481
|
-
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5549
|
+
if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
|
|
5550
|
+
vol *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
|
|
4482
5551
|
this.lavalinkVolume = Math.round(vol);
|
|
4483
5552
|
options.volume = this.lavalinkVolume;
|
|
4484
5553
|
}
|
|
4485
|
-
const finalOptions = Object.fromEntries(
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
5554
|
+
const finalOptions = Object.fromEntries(
|
|
5555
|
+
Object.entries({
|
|
5556
|
+
track: {
|
|
5557
|
+
encoded: this.queue.current?.encoded || null,
|
|
5558
|
+
// identifier: options.identifier,
|
|
5559
|
+
userData: {
|
|
5560
|
+
...typeof this.queue.current?.requester === "object" ? {
|
|
5561
|
+
requester: this.LavalinkManager.utils.getTransformedRequester(
|
|
5562
|
+
this.queue.current?.requester || {}
|
|
5563
|
+
)
|
|
5564
|
+
} : {},
|
|
5565
|
+
...options?.track?.userData,
|
|
5566
|
+
...this.queue.current?.userData
|
|
5567
|
+
},
|
|
5568
|
+
audioTrackId: options?.track?.audioTrackId
|
|
5569
|
+
},
|
|
5570
|
+
volume: this.lavalinkVolume,
|
|
5571
|
+
position: options?.position ?? 0,
|
|
5572
|
+
endTime: options?.endTime ?? void 0,
|
|
5573
|
+
filters: options?.filters ?? void 0,
|
|
5574
|
+
paused: options?.paused ?? void 0,
|
|
5575
|
+
voice: options?.voice ?? void 0
|
|
5576
|
+
}).filter((v) => typeof v[1] !== "undefined")
|
|
5577
|
+
);
|
|
5578
|
+
if (finalOptions.track.audioTrackId && !this.node.isNodeLink()) {
|
|
5579
|
+
delete finalOptions.track.audioTrackId;
|
|
5580
|
+
}
|
|
5581
|
+
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)
|
|
5582
|
+
throw new Error("PlayerOption#position must be a positive number, less than track's duration");
|
|
5583
|
+
if (typeof finalOptions.volume !== "undefined" && isNaN(finalOptions.volume) || typeof finalOptions.volume === "number" && finalOptions.volume < 0)
|
|
5584
|
+
throw new Error("PlayerOption#volume must be a positive number");
|
|
5585
|
+
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)
|
|
5586
|
+
throw new Error("PlayerOption#endTime must be a positive number, less than track's duration");
|
|
5587
|
+
if (typeof finalOptions.position === "number" && typeof finalOptions.endTime === "number" && finalOptions.endTime < finalOptions.position)
|
|
5588
|
+
throw new Error("PlayerOption#endTime must be bigger than PlayerOption#position");
|
|
4506
5589
|
const now = performance.now();
|
|
4507
5590
|
await this.node.updatePlayer({
|
|
4508
5591
|
guildId: this.guildId,
|
|
@@ -4521,9 +5604,17 @@ var Player = class {
|
|
|
4521
5604
|
volume = Number(volume);
|
|
4522
5605
|
if (isNaN(volume)) throw new TypeError("Volume must be a number.");
|
|
4523
5606
|
this.volume = Math.round(Math.max(Math.min(volume, 1e3), 0));
|
|
4524
|
-
this.lavalinkVolume = Math.round(
|
|
4525
|
-
|
|
4526
|
-
|
|
5607
|
+
this.lavalinkVolume = Math.round(
|
|
5608
|
+
Math.max(
|
|
5609
|
+
Math.min(
|
|
5610
|
+
Math.round(
|
|
5611
|
+
this.LavalinkManager.options.playerOptions.volumeDecrementer && !ignoreVolumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
|
|
5612
|
+
),
|
|
5613
|
+
1e3
|
|
5614
|
+
),
|
|
5615
|
+
0
|
|
5616
|
+
)
|
|
5617
|
+
);
|
|
4527
5618
|
const now = performance.now();
|
|
4528
5619
|
if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
|
|
4529
5620
|
this._emitDebugEvent("PlayerVolumeAsFilter" /* PlayerVolumeAsFilter */, {
|
|
@@ -4531,9 +5622,15 @@ var Player = class {
|
|
|
4531
5622
|
message: `Player Volume was set as a Filter, because LavalinkManager option "playerOptions.applyVolumeAsFilter" is true`,
|
|
4532
5623
|
functionLayer: "Player > setVolume()"
|
|
4533
5624
|
});
|
|
4534
|
-
await this.node.updatePlayer({
|
|
5625
|
+
await this.node.updatePlayer({
|
|
5626
|
+
guildId: this.guildId,
|
|
5627
|
+
playerOptions: { filters: { volume: this.lavalinkVolume / 100 } }
|
|
5628
|
+
});
|
|
4535
5629
|
} else {
|
|
4536
|
-
await this.node.updatePlayer({
|
|
5630
|
+
await this.node.updatePlayer({
|
|
5631
|
+
guildId: this.guildId,
|
|
5632
|
+
playerOptions: { volume: this.lavalinkVolume }
|
|
5633
|
+
});
|
|
4537
5634
|
}
|
|
4538
5635
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4539
5636
|
return this;
|
|
@@ -4617,8 +5714,10 @@ var Player = class {
|
|
|
4617
5714
|
if (!this.queue.current) return void 0;
|
|
4618
5715
|
position = Number(position);
|
|
4619
5716
|
if (isNaN(position)) throw new RangeError("Position must be a number.");
|
|
4620
|
-
if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream)
|
|
4621
|
-
|
|
5717
|
+
if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream)
|
|
5718
|
+
throw new RangeError("Current Track is not seekable / a stream");
|
|
5719
|
+
if (position < 0 || position > this.queue.current.info.duration)
|
|
5720
|
+
position = Math.max(Math.min(position, this.queue.current.info.duration), 0);
|
|
4622
5721
|
this.lastPositionChange = Date.now();
|
|
4623
5722
|
this.lastPosition = position;
|
|
4624
5723
|
const now = performance.now();
|
|
@@ -4631,7 +5730,8 @@ var Player = class {
|
|
|
4631
5730
|
* @param repeatMode
|
|
4632
5731
|
*/
|
|
4633
5732
|
async setRepeatMode(repeatMode) {
|
|
4634
|
-
if (!["off", "track", "queue"].includes(repeatMode))
|
|
5733
|
+
if (!["off", "track", "queue"].includes(repeatMode))
|
|
5734
|
+
throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
|
|
4635
5735
|
this.repeatMode = repeatMode;
|
|
4636
5736
|
return this;
|
|
4637
5737
|
}
|
|
@@ -4640,7 +5740,8 @@ var Player = class {
|
|
|
4640
5740
|
* @param amount provide the index of the next track to skip to
|
|
4641
5741
|
*/
|
|
4642
5742
|
async skip(skipTo = 0, throwError = true) {
|
|
4643
|
-
if (!this.queue.tracks.length && (throwError || typeof skipTo === "boolean" && skipTo === true))
|
|
5743
|
+
if (!this.queue.tracks.length && (throwError || typeof skipTo === "boolean" && skipTo === true))
|
|
5744
|
+
throw new RangeError("Can't skip more than the queue size");
|
|
4644
5745
|
if (typeof skipTo === "number" && skipTo > 1) {
|
|
4645
5746
|
if (skipTo > this.queue.tracks.length) throw new RangeError("Can't skip more than the queue size");
|
|
4646
5747
|
await this.queue.splice(0, skipTo - 1);
|
|
@@ -4648,7 +5749,10 @@ var Player = class {
|
|
|
4648
5749
|
if (!this.playing && !this.queue.current) return this.play(), this;
|
|
4649
5750
|
const now = performance.now();
|
|
4650
5751
|
this.set("internal_skipped", true);
|
|
4651
|
-
await this.node.updatePlayer({
|
|
5752
|
+
await this.node.updatePlayer({
|
|
5753
|
+
guildId: this.guildId,
|
|
5754
|
+
playerOptions: { track: { encoded: null }, paused: false }
|
|
5755
|
+
});
|
|
4652
5756
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4653
5757
|
return this;
|
|
4654
5758
|
}
|
|
@@ -4662,7 +5766,10 @@ var Player = class {
|
|
|
4662
5766
|
if (executeAutoplay === false) this.set("internal_autoplayStopPlaying", true);
|
|
4663
5767
|
else this.set("internal_autoplayStopPlaying", void 0);
|
|
4664
5768
|
const now = performance.now();
|
|
4665
|
-
await this.node.updatePlayer({
|
|
5769
|
+
await this.node.updatePlayer({
|
|
5770
|
+
guildId: this.guildId,
|
|
5771
|
+
playerOptions: { track: { encoded: null } }
|
|
5772
|
+
});
|
|
4666
5773
|
this.paused = false;
|
|
4667
5774
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
4668
5775
|
return this;
|
|
@@ -4672,7 +5779,8 @@ var Player = class {
|
|
|
4672
5779
|
* @returns
|
|
4673
5780
|
*/
|
|
4674
5781
|
async connect() {
|
|
4675
|
-
if (!this.options.voiceChannelId)
|
|
5782
|
+
if (!this.options.voiceChannelId)
|
|
5783
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
4676
5784
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4677
5785
|
op: 4,
|
|
4678
5786
|
d: {
|
|
@@ -4686,7 +5794,8 @@ var Player = class {
|
|
|
4686
5794
|
return this;
|
|
4687
5795
|
}
|
|
4688
5796
|
async changeVoiceState(data) {
|
|
4689
|
-
if (this.options.voiceChannelId === data.voiceChannelId)
|
|
5797
|
+
if (this.options.voiceChannelId === data.voiceChannelId)
|
|
5798
|
+
throw new RangeError("New Channel can't be equal to the old Channel.");
|
|
4690
5799
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4691
5800
|
op: 4,
|
|
4692
5801
|
d: {
|
|
@@ -4708,7 +5817,8 @@ var Player = class {
|
|
|
4708
5817
|
* @returns
|
|
4709
5818
|
*/
|
|
4710
5819
|
async disconnect(force = false) {
|
|
4711
|
-
if (!force && !this.options.voiceChannelId)
|
|
5820
|
+
if (!force && !this.options.voiceChannelId)
|
|
5821
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
4712
5822
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
4713
5823
|
op: 4,
|
|
4714
5824
|
d: {
|
|
@@ -4725,7 +5835,10 @@ var Player = class {
|
|
|
4725
5835
|
* Destroy the player and disconnect from the voice channel
|
|
4726
5836
|
*/
|
|
4727
5837
|
async destroy(reason, disconnect = true) {
|
|
4728
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5838
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5839
|
+
console.log(
|
|
5840
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`
|
|
5841
|
+
);
|
|
4729
5842
|
if (this.get("internal_queueempty")) {
|
|
4730
5843
|
clearTimeout(this.get("internal_queueempty"));
|
|
4731
5844
|
this.set("internal_queueempty", void 0);
|
|
@@ -4736,7 +5849,10 @@ var Player = class {
|
|
|
4736
5849
|
message: `Player is already destroying somewhere else..`,
|
|
4737
5850
|
functionLayer: "Player > destroy()"
|
|
4738
5851
|
});
|
|
4739
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5852
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5853
|
+
console.log(
|
|
5854
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`
|
|
5855
|
+
);
|
|
4740
5856
|
return;
|
|
4741
5857
|
}
|
|
4742
5858
|
this.set("internal_destroystatus", true);
|
|
@@ -4745,7 +5861,10 @@ var Player = class {
|
|
|
4745
5861
|
await this.queue.utils.destroy();
|
|
4746
5862
|
this.LavalinkManager.deletePlayer(this.guildId);
|
|
4747
5863
|
await this.node.destroyPlayer(this.guildId);
|
|
4748
|
-
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5864
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
5865
|
+
console.log(
|
|
5866
|
+
`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Player got destroyed successfully`
|
|
5867
|
+
);
|
|
4749
5868
|
this.LavalinkManager.emit("playerDestroy", this, reason);
|
|
4750
5869
|
return this;
|
|
4751
5870
|
}
|
|
@@ -4812,23 +5931,32 @@ var Player = class {
|
|
|
4812
5931
|
if (!updateNode) throw new Error("Could not find the new Node");
|
|
4813
5932
|
if (!updateNode.connected) throw new Error("The provided Node is not active or disconnected");
|
|
4814
5933
|
if (this.node.id === updateNode.id) throw new Error("Player is already on the provided Node");
|
|
4815
|
-
if (this.get("internal_nodeChanging") === true)
|
|
5934
|
+
if (this.get("internal_nodeChanging") === true)
|
|
5935
|
+
throw new Error("Player is already changing the node please wait");
|
|
4816
5936
|
if (checkSources) {
|
|
4817
5937
|
const isDefaultSource = () => {
|
|
4818
5938
|
try {
|
|
4819
|
-
this.LavalinkManager.utils.validateSourceString(
|
|
5939
|
+
this.LavalinkManager.utils.validateSourceString(
|
|
5940
|
+
updateNode,
|
|
5941
|
+
this.LavalinkManager.options.playerOptions.defaultSearchPlatform
|
|
5942
|
+
);
|
|
4820
5943
|
return true;
|
|
4821
5944
|
} catch {
|
|
4822
5945
|
return false;
|
|
4823
5946
|
}
|
|
4824
5947
|
};
|
|
4825
|
-
if (!isDefaultSource())
|
|
5948
|
+
if (!isDefaultSource())
|
|
5949
|
+
throw new RangeError(
|
|
5950
|
+
`defaultSearchPlatform "${this.LavalinkManager.options.playerOptions.defaultSearchPlatform}" is not supported by the newNode`
|
|
5951
|
+
);
|
|
4826
5952
|
if (this.queue.current || this.queue.tracks.length) {
|
|
4827
|
-
const trackSources = new Set(
|
|
5953
|
+
const trackSources = new Set(
|
|
5954
|
+
[this.queue.current, ...this.queue.tracks].map((track) => track.info.sourceName)
|
|
5955
|
+
);
|
|
4828
5956
|
const missingSources = [...trackSources].filter(
|
|
4829
5957
|
(source) => !updateNode.info?.sourceManagers.includes(source)
|
|
4830
5958
|
);
|
|
4831
|
-
if (
|
|
5959
|
+
if (updateNode._checkForSources && missingSources.length)
|
|
4832
5960
|
throw new RangeError(`Sources missing for Node ${updateNode.id}: ${missingSources.join(", ")}`);
|
|
4833
5961
|
}
|
|
4834
5962
|
}
|
|
@@ -4847,7 +5975,7 @@ var Player = class {
|
|
|
4847
5975
|
const now = performance.now();
|
|
4848
5976
|
try {
|
|
4849
5977
|
await this.connect();
|
|
4850
|
-
const hasSponsorBlock = !this.
|
|
5978
|
+
const hasSponsorBlock = !this.node._checkForPlugins || this.node.info?.plugins?.find((v) => v.name === "sponsorblock-plugin");
|
|
4851
5979
|
if (hasSponsorBlock) {
|
|
4852
5980
|
const sponsorBlockCategories = this.get("internal_sponsorBlockCategories");
|
|
4853
5981
|
if (Array.isArray(sponsorBlockCategories) && sponsorBlockCategories.length) {
|
|
@@ -4915,10 +6043,18 @@ var Player = class {
|
|
|
4915
6043
|
*/
|
|
4916
6044
|
async moveNode(node) {
|
|
4917
6045
|
try {
|
|
4918
|
-
if (!node)
|
|
4919
|
-
|
|
6046
|
+
if (!node)
|
|
6047
|
+
node = Array.from(this.LavalinkManager.nodeManager.leastUsedNodes("playingPlayers")).find(
|
|
6048
|
+
(n) => n.connected && n.options.id !== this.node.options.id
|
|
6049
|
+
).id;
|
|
6050
|
+
if (!node || !this.LavalinkManager.nodeManager.nodes.get(node))
|
|
6051
|
+
throw new RangeError("No nodes are available.");
|
|
4920
6052
|
if (this.node.options.id === node) return this;
|
|
4921
|
-
this.LavalinkManager.emit("debug", "PlayerChangeNode" /* PlayerChangeNode */, {
|
|
6053
|
+
this.LavalinkManager.emit("debug", "PlayerChangeNode" /* PlayerChangeNode */, {
|
|
6054
|
+
state: "log",
|
|
6055
|
+
message: `Player.moveNode() was executed, trying to move from "${this.node.id}" to "${node}"`,
|
|
6056
|
+
functionLayer: "Player > moveNode()"
|
|
6057
|
+
});
|
|
4922
6058
|
const updateNode = this.LavalinkManager.nodeManager.nodes.get(node);
|
|
4923
6059
|
if (!updateNode) throw new RangeError("No nodes are available.");
|
|
4924
6060
|
return await this.changeNode(updateNode);
|
|
@@ -5022,10 +6158,6 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5022
6158
|
id: options?.client?.id,
|
|
5023
6159
|
username: options?.client?.username ?? "lavalink-client"
|
|
5024
6160
|
},
|
|
5025
|
-
autoChecks: {
|
|
5026
|
-
sourcesValidations: options?.autoChecks?.sourcesValidations ?? true,
|
|
5027
|
-
pluginValidations: options?.autoChecks?.pluginValidations ?? true
|
|
5028
|
-
},
|
|
5029
6161
|
sendToShard: options?.sendToShard,
|
|
5030
6162
|
autoMove: options?.autoMove ?? false,
|
|
5031
6163
|
nodes: options?.nodes,
|
|
@@ -5034,6 +6166,7 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5034
6166
|
applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
|
|
5035
6167
|
clientBasedPositionUpdateInterval: options?.playerOptions?.clientBasedPositionUpdateInterval ?? 100,
|
|
5036
6168
|
defaultSearchPlatform: options?.playerOptions?.defaultSearchPlatform ?? "ytsearch",
|
|
6169
|
+
allowCustomSources: options?.playerOptions?.allowCustomSources ?? false,
|
|
5037
6170
|
onDisconnect: {
|
|
5038
6171
|
destroyPlayer: options?.playerOptions?.onDisconnect?.destroyPlayer ?? true,
|
|
5039
6172
|
autoReconnect: options?.playerOptions?.onDisconnect?.autoReconnect ?? false,
|
|
@@ -5084,24 +6217,36 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5084
6217
|
* @param options
|
|
5085
6218
|
*/
|
|
5086
6219
|
validateOptions(options) {
|
|
5087
|
-
if (typeof options?.sendToShard !== "function")
|
|
5088
|
-
|
|
5089
|
-
if (options?.
|
|
5090
|
-
|
|
5091
|
-
if (
|
|
5092
|
-
|
|
5093
|
-
if (typeof options?.
|
|
6220
|
+
if (typeof options?.sendToShard !== "function")
|
|
6221
|
+
throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
|
|
6222
|
+
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
6223
|
+
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
6224
|
+
if (options?.autoSkipOnResolveError && typeof options?.autoSkipOnResolveError !== "boolean")
|
|
6225
|
+
throw new SyntaxError("ManagerOption.autoSkipOnResolveError must be either false | true aka boolean");
|
|
6226
|
+
if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean")
|
|
6227
|
+
throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
|
|
6228
|
+
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every((node) => this.utils.isNodeOptions(node)))
|
|
6229
|
+
throw new SyntaxError(
|
|
6230
|
+
"ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node"
|
|
6231
|
+
);
|
|
5094
6232
|
if (options?.queueOptions?.queueStore) {
|
|
5095
6233
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueStore));
|
|
5096
6234
|
const requiredKeys = ["get", "set", "stringify", "parse", "delete"];
|
|
5097
|
-
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
6235
|
+
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
6236
|
+
throw new SyntaxError(
|
|
6237
|
+
`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`
|
|
6238
|
+
);
|
|
5098
6239
|
}
|
|
5099
6240
|
if (options?.queueOptions?.queueChangesWatcher) {
|
|
5100
6241
|
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueChangesWatcher));
|
|
5101
6242
|
const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
|
|
5102
|
-
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
6243
|
+
if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
6244
|
+
throw new SyntaxError(
|
|
6245
|
+
`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`
|
|
6246
|
+
);
|
|
5103
6247
|
}
|
|
5104
|
-
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
6248
|
+
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
6249
|
+
options.queueOptions.maxPreviousTracks = 25;
|
|
5105
6250
|
}
|
|
5106
6251
|
/**
|
|
5107
6252
|
* Emits a debug event to the LavalinkManager
|
|
@@ -5138,6 +6283,7 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5138
6283
|
* applyVolumeAsFilter: false,
|
|
5139
6284
|
* clientBasedPositionUpdateInterval: 150,
|
|
5140
6285
|
* defaultSearchPlatform: "ytmsearch",
|
|
6286
|
+
* allowCustomSources: false,
|
|
5141
6287
|
* volumeDecrementer: 0.75,
|
|
5142
6288
|
* //requesterTransformer: YourRequesterTransformerFunction,
|
|
5143
6289
|
* onDisconnect: {
|
|
@@ -5260,7 +6406,10 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5260
6406
|
const oldPlayer = this.getPlayer(guildId);
|
|
5261
6407
|
if (!oldPlayer) return;
|
|
5262
6408
|
if (typeof oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
5263
|
-
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
6409
|
+
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
6410
|
+
throw new Error(
|
|
6411
|
+
`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`
|
|
6412
|
+
);
|
|
5264
6413
|
this._emitDebugEvent("PlayerDeleteInsteadOfDestroy" /* PlayerDeleteInsteadOfDestroy */, {
|
|
5265
6414
|
state: "warn",
|
|
5266
6415
|
message: "Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player",
|
|
@@ -5300,7 +6449,8 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5300
6449
|
if (this.initiated) return this;
|
|
5301
6450
|
clientData = clientData ?? {};
|
|
5302
6451
|
this.options.client = { ...this.options?.client, ...clientData };
|
|
5303
|
-
if (!this.options?.client.id)
|
|
6452
|
+
if (!this.options?.client.id)
|
|
6453
|
+
throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
|
|
5304
6454
|
if (typeof this.options?.client.id !== "string") throw new Error('"client.id" set is not type of "string"');
|
|
5305
6455
|
let success = 0;
|
|
5306
6456
|
for (const node of this.nodeManager.nodes.values()) {
|
|
@@ -5313,11 +6463,12 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5313
6463
|
}
|
|
5314
6464
|
}
|
|
5315
6465
|
if (success > 0) this.initiated = true;
|
|
5316
|
-
else
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
6466
|
+
else
|
|
6467
|
+
this._emitDebugEvent("FailedToConnectToNodes" /* FailedToConnectToNodes */, {
|
|
6468
|
+
state: "error",
|
|
6469
|
+
message: "Failed to connect to at least 1 Node",
|
|
6470
|
+
functionLayer: "LavalinkManager > init()"
|
|
6471
|
+
});
|
|
5321
6472
|
return this;
|
|
5322
6473
|
}
|
|
5323
6474
|
/**
|
|
@@ -5342,7 +6493,10 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5342
6493
|
message: "Manager is not initated yet",
|
|
5343
6494
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5344
6495
|
});
|
|
5345
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6496
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6497
|
+
console.debug(
|
|
6498
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet"
|
|
6499
|
+
);
|
|
5346
6500
|
return;
|
|
5347
6501
|
}
|
|
5348
6502
|
if (!("t" in data)) {
|
|
@@ -5351,14 +6505,19 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5351
6505
|
message: "No 't' in payload-data of the raw event:",
|
|
5352
6506
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5353
6507
|
});
|
|
5354
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6508
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6509
|
+
console.debug(
|
|
6510
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:",
|
|
6511
|
+
data
|
|
6512
|
+
);
|
|
5355
6513
|
return;
|
|
5356
6514
|
}
|
|
5357
6515
|
if ("CHANNEL_DELETE" === data.t) {
|
|
5358
6516
|
const update = "d" in data ? data.d : data;
|
|
5359
6517
|
if (!update.guild_id) return;
|
|
5360
6518
|
const player = this.getPlayer(update.guild_id);
|
|
5361
|
-
if (player && player.voiceChannelId === update.id)
|
|
6519
|
+
if (player && player.voiceChannelId === update.id)
|
|
6520
|
+
return void player.destroy("ChannelDeleted" /* ChannelDeleted */);
|
|
5362
6521
|
}
|
|
5363
6522
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
5364
6523
|
const update = "d" in data ? data.d : data;
|
|
@@ -5368,7 +6527,11 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5368
6527
|
message: `No Update data found in payload :: ${safeStringify(data, 2)}`,
|
|
5369
6528
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5370
6529
|
});
|
|
5371
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6530
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6531
|
+
console.debug(
|
|
6532
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:",
|
|
6533
|
+
data
|
|
6534
|
+
);
|
|
5372
6535
|
return;
|
|
5373
6536
|
}
|
|
5374
6537
|
if (!("token" in update) && !("session_id" in update)) {
|
|
@@ -5377,7 +6540,11 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5377
6540
|
message: `No 'token' nor 'session_id' found in payload :: ${safeStringify(data, 2)}`,
|
|
5378
6541
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5379
6542
|
});
|
|
5380
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6543
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6544
|
+
console.debug(
|
|
6545
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:",
|
|
6546
|
+
data
|
|
6547
|
+
);
|
|
5381
6548
|
return;
|
|
5382
6549
|
}
|
|
5383
6550
|
const player = this.getPlayer(update.guild_id);
|
|
@@ -5387,7 +6554,11 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5387
6554
|
message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${safeStringify(update, 2)}`,
|
|
5388
6555
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5389
6556
|
});
|
|
5390
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6557
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6558
|
+
console.debug(
|
|
6559
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:",
|
|
6560
|
+
update
|
|
6561
|
+
);
|
|
5391
6562
|
return;
|
|
5392
6563
|
}
|
|
5393
6564
|
if (player.get("internal_destroystatus") === true) {
|
|
@@ -5396,7 +6567,10 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5396
6567
|
message: `Player is in a destroying state. can't signal the voice states`,
|
|
5397
6568
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5398
6569
|
});
|
|
5399
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6570
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6571
|
+
console.debug(
|
|
6572
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states"
|
|
6573
|
+
);
|
|
5400
6574
|
return;
|
|
5401
6575
|
}
|
|
5402
6576
|
if ("token" in update) {
|
|
@@ -5408,7 +6582,15 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5408
6582
|
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)}`,
|
|
5409
6583
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5410
6584
|
});
|
|
5411
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6585
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6586
|
+
console.debug(
|
|
6587
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId",
|
|
6588
|
+
{
|
|
6589
|
+
voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use },
|
|
6590
|
+
update,
|
|
6591
|
+
playerVoice: player.voice
|
|
6592
|
+
}
|
|
6593
|
+
);
|
|
5412
6594
|
} else {
|
|
5413
6595
|
await player.node.updatePlayer({
|
|
5414
6596
|
guildId: player.guildId,
|
|
@@ -5425,24 +6607,44 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5425
6607
|
message: `Sent updatePlayer for voice token session :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use }, update, playerVoice: player.voice }, 2)}`,
|
|
5426
6608
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5427
6609
|
});
|
|
5428
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6610
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6611
|
+
console.debug(
|
|
6612
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session",
|
|
6613
|
+
{
|
|
6614
|
+
voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use },
|
|
6615
|
+
playerVoice: player.voice,
|
|
6616
|
+
update
|
|
6617
|
+
}
|
|
6618
|
+
);
|
|
5429
6619
|
}
|
|
5430
6620
|
return;
|
|
5431
6621
|
}
|
|
5432
6622
|
if (update.user_id !== this.options?.client.id) {
|
|
5433
6623
|
if (update.user_id && player.voiceChannelId) {
|
|
5434
|
-
this.emit(
|
|
6624
|
+
this.emit(
|
|
6625
|
+
update.channel_id === player.voiceChannelId ? "playerVoiceJoin" : "playerVoiceLeave",
|
|
6626
|
+
player,
|
|
6627
|
+
update.user_id
|
|
6628
|
+
);
|
|
5435
6629
|
}
|
|
5436
6630
|
this._emitDebugEvent("NoAudioDebug" /* NoAudioDebug */, {
|
|
5437
6631
|
state: "warn",
|
|
5438
6632
|
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}"`,
|
|
5439
6633
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5440
6634
|
});
|
|
5441
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6635
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6636
|
+
console.debug(
|
|
6637
|
+
"Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, voice update user is not equal to provided client id of the manageroptions#client#id",
|
|
6638
|
+
"user:",
|
|
6639
|
+
update.user_id,
|
|
6640
|
+
"manager client id:",
|
|
6641
|
+
this.options?.client.id
|
|
6642
|
+
);
|
|
5442
6643
|
return;
|
|
5443
6644
|
}
|
|
5444
6645
|
if (update.channel_id) {
|
|
5445
|
-
if (player.voiceChannelId !== update.channel_id)
|
|
6646
|
+
if (player.voiceChannelId !== update.channel_id)
|
|
6647
|
+
this.emit("playerMove", player, player.voiceChannelId, update.channel_id);
|
|
5446
6648
|
player.voice.sessionId = update.session_id || player.voice.sessionId;
|
|
5447
6649
|
if (!player.voice.sessionId) {
|
|
5448
6650
|
this._emitDebugEvent("NoAudioDebug" /* NoAudioDebug */, {
|
|
@@ -5450,7 +6652,10 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5450
6652
|
message: `Function to assing sessionId provided, but no found in Payload: ${safeStringify({ update, playerVoice: player.voice }, 2)}`,
|
|
5451
6653
|
functionLayer: "LavalinkManager > sendRawData()"
|
|
5452
6654
|
});
|
|
5453
|
-
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6655
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
6656
|
+
console.debug(
|
|
6657
|
+
`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`
|
|
6658
|
+
);
|
|
5454
6659
|
}
|
|
5455
6660
|
player.voiceChannelId = update.channel_id;
|
|
5456
6661
|
player.options.voiceChannelId = update.channel_id;
|
|
@@ -5464,15 +6669,13 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5464
6669
|
player.voiceState.serverDeaf = update.deaf ?? player.voiceState?.serverDeaf;
|
|
5465
6670
|
player.voiceState.serverMute = update.mute ?? player.voiceState?.serverMute;
|
|
5466
6671
|
player.voiceState.suppress = update.suppress ?? player.voiceState?.suppress;
|
|
5467
|
-
if (selfMuteChanged || serverMuteChanged)
|
|
5468
|
-
|
|
6672
|
+
if (selfMuteChanged || serverMuteChanged)
|
|
6673
|
+
this.emit("playerMuteChange", player, player.voiceState.selfMute, player.voiceState.serverMute);
|
|
6674
|
+
if (selfDeafChanged || serverDeafChanged)
|
|
6675
|
+
this.emit("playerDeafChange", player, player.voiceState.selfDeaf, player.voiceState.serverDeaf);
|
|
5469
6676
|
if (suppressChange) this.emit("playerSuppressChange", player, player.voiceState.suppress);
|
|
5470
6677
|
} else {
|
|
5471
|
-
const {
|
|
5472
|
-
autoReconnectOnlyWithTracks,
|
|
5473
|
-
destroyPlayer,
|
|
5474
|
-
autoReconnect
|
|
5475
|
-
} = this.options?.playerOptions?.onDisconnect ?? {};
|
|
6678
|
+
const { autoReconnectOnlyWithTracks, destroyPlayer, autoReconnect } = this.options?.playerOptions?.onDisconnect ?? {};
|
|
5476
6679
|
if (destroyPlayer === true) {
|
|
5477
6680
|
return void await player.destroy("Disconnected" /* Disconnected */);
|
|
5478
6681
|
}
|
|
@@ -5490,7 +6693,11 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5490
6693
|
this.emit("playerReconnect", player, player.voiceChannelId);
|
|
5491
6694
|
}
|
|
5492
6695
|
if (player.queue.current) {
|
|
5493
|
-
return void await player.play({
|
|
6696
|
+
return void await player.play({
|
|
6697
|
+
position: previousPosition,
|
|
6698
|
+
paused: previousPaused,
|
|
6699
|
+
clientTrack: player.queue.current
|
|
6700
|
+
});
|
|
5494
6701
|
}
|
|
5495
6702
|
if (player.queue.tracks.length) {
|
|
5496
6703
|
return void await player.play({ paused: previousPaused });
|
|
@@ -5528,12 +6735,15 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5528
6735
|
LavalinkPlugins,
|
|
5529
6736
|
ManagerUtils,
|
|
5530
6737
|
MiniMap,
|
|
6738
|
+
NodeLinkExclusiveEvents,
|
|
6739
|
+
NodeLinkNode,
|
|
5531
6740
|
NodeManager,
|
|
5532
6741
|
NodeSymbol,
|
|
5533
6742
|
Player,
|
|
5534
6743
|
Queue,
|
|
5535
6744
|
QueueSaver,
|
|
5536
6745
|
QueueSymbol,
|
|
6746
|
+
RecommendationsStrings,
|
|
5537
6747
|
ReconnectionState,
|
|
5538
6748
|
SourceLinksRegexes,
|
|
5539
6749
|
TrackSymbol,
|