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.
Files changed (6) hide show
  1. package/README.md +726 -518
  2. package/dist/index.d.mts +2427 -1893
  3. package/dist/index.d.ts +2427 -1893
  4. package/dist/index.js +1776 -566
  5. package/dist/index.mjs +1773 -566
  6. 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 = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
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
- "youtubemusic": "ytmsearch",
392
- "ytmsearch": "ytmsearch",
393
- "ytm": "ytmsearch",
394
- "musicyoutube": "ytmsearch",
430
+ youtubemusic: "ytmsearch",
431
+ ytmsearch: "ytmsearch",
432
+ ytm: "ytmsearch",
433
+ musicyoutube: "ytmsearch",
395
434
  "music youtube": "ytmsearch",
396
435
  // youtube
397
- "youtube": "ytsearch",
398
- "yt": "ytsearch",
399
- "ytsearch": "ytsearch",
436
+ youtube: "ytsearch",
437
+ yt: "ytsearch",
438
+ ytsearch: "ytsearch",
400
439
  // soundcloud
401
- "soundcloud": "scsearch",
402
- "scsearch": "scsearch",
403
- "sc": "scsearch",
440
+ soundcloud: "scsearch",
441
+ scsearch: "scsearch",
442
+ sc: "scsearch",
404
443
  // apple music
405
444
  "apple music": "amsearch",
406
- "apple": "amsearch",
407
- "applemusic": "amsearch",
408
- "amsearch": "amsearch",
409
- "am": "amsearch",
410
- "musicapple": "amsearch",
445
+ apple: "amsearch",
446
+ applemusic: "amsearch",
447
+ amsearch: "amsearch",
448
+ am: "amsearch",
449
+ musicapple: "amsearch",
411
450
  "music apple": "amsearch",
412
451
  // spotify
413
- "spotify": "spsearch",
414
- "spsearch": "spsearch",
415
- "sp": "spsearch",
452
+ spotify: "spsearch",
453
+ spsearch: "spsearch",
454
+ sp: "spsearch",
416
455
  "spotify.com": "spsearch",
417
- "spotifycom": "spsearch",
418
- "sprec": "sprec",
419
- "spsuggestion": "sprec",
456
+ spotifycom: "spsearch",
457
+ sprec: "sprec",
458
+ spsuggestion: "sprec",
420
459
  // deezer
421
- "deezer": "dzsearch",
422
- "dz": "dzsearch",
423
- "dzsearch": "dzsearch",
424
- "dzisrc": "dzisrc",
425
- "dzrec": "dzrec",
460
+ deezer: "dzsearch",
461
+ dz: "dzsearch",
462
+ dzsearch: "dzsearch",
463
+ dzisrc: "dzisrc",
464
+ dzrec: "dzrec",
426
465
  // yandexmusic
427
466
  "yandex music": "ymsearch",
428
- "yandexmusic": "ymsearch",
429
- "yandex": "ymsearch",
430
- "ymsearch": "ymsearch",
431
- "ymrec": "ymrec",
467
+ yandexmusic: "ymsearch",
468
+ yandex: "ymsearch",
469
+ ymsearch: "ymsearch",
470
+ ymrec: "ymrec",
432
471
  // VK Music (lavasrc)
433
- "vksearch": "vksearch",
434
- "vkmusic": "vksearch",
472
+ vksearch: "vksearch",
473
+ vkmusic: "vksearch",
435
474
  "vk music": "vksearch",
436
- "vkrec": "vkrec",
437
- "vk": "vksearch",
475
+ vkrec: "vkrec",
476
+ vk: "vksearch",
438
477
  // Qobuz (lavasrc)
439
- "qbsearch": "qbsearch",
440
- "qobuz": "qbsearch",
441
- "qbisrc": "qbisrc",
442
- "qbrec": "qbrec",
478
+ qbsearch: "qbsearch",
479
+ qobuz: "qbsearch",
480
+ qbisrc: "qbisrc",
481
+ qbrec: "qbrec",
443
482
  // pandora (lavasrc)
444
- "pandora": "pdsearch",
445
- "pd": "pdsearch",
446
- "pdsearch": "pdsearch",
447
- "pdisrc": "pdisrc",
448
- "pdrec": "pdrec",
483
+ pandora: "pdsearch",
484
+ pd: "pdsearch",
485
+ pdsearch: "pdsearch",
486
+ pdisrc: "pdisrc",
487
+ pdrec: "pdrec",
449
488
  "pandora music": "pdsearch",
450
- "pandoramusic": "pdsearch",
489
+ pandoramusic: "pdsearch",
451
490
  // speak PLUGIN
452
- "speak": "speak",
453
- "tts": "tts",
454
- "ftts": "ftts",
455
- "flowery": "ftts",
491
+ speak: "speak",
492
+ tts: "tts",
493
+ ftts: "ftts",
494
+ flowery: "ftts",
456
495
  "flowery.tts": "ftts",
457
- "flowerytts": "ftts",
496
+ flowerytts: "ftts",
458
497
  // Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
459
- "bandcamp": "bcsearch",
460
- "bc": "bcsearch",
461
- "bcsearch": "bcsearch",
498
+ bandcamp: "bcsearch",
499
+ bc: "bcsearch",
500
+ bcsearch: "bcsearch",
462
501
  // other searches:
463
- "phsearch": "phsearch",
464
- "pornhub": "phsearch",
465
- "porn": "phsearch",
502
+ phsearch: "phsearch",
503
+ pornhub: "phsearch",
504
+ porn: "phsearch",
466
505
  // local files
467
- "local": "local",
506
+ local: "local",
468
507
  // http requests
469
- "http": "http",
470
- "https": "https",
471
- "link": "link",
472
- "uri": "uri",
508
+ http: "http",
509
+ https: "https",
510
+ link: "link",
511
+ uri: "uri",
473
512
  // tidal
474
- "tidal": "tdsearch",
475
- "td": "tdsearch",
513
+ tidal: "tdsearch",
514
+ td: "tdsearch",
476
515
  "tidal music": "tdsearch",
477
- "tdsearch": "tdsearch",
478
- "tdrec": "tdrec",
516
+ tdsearch: "tdsearch",
517
+ tdrec: "tdrec",
479
518
  // jiosaavn
480
- "jiosaavn": "jssearch",
481
- "js": "jssearch",
482
- "jssearch": "jssearch",
483
- "jsrec": "jsrec"
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\-]+(?:\/[\w\-]+)*\/(?<identifier>TR[A-Za-z0-9]+)(?:[?#].*)?$/,
535
- PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w\-]+(?:\/[\w\-]+)*\/(?<identifier>AL[A-Za-z0-9]+)(?:[?#].*)?$/,
536
- PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w\-]+\/(?<identifier>AR[A-Za-z0-9]+)(?:[?#].*)?$/,
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\-]+(?:\/[\w\-]+)*\/(?<identifier>(?:TR|AL|AR)[A-Za-z0-9]+))(?:[?#].*)?$/,
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://")) throw new Error(`ConnectionUrl (${connectionUrl}) must start with '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") throw new RangeError("Argument 'data.encoded' must be present.");
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, { configurable: true, value: true });
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 (!["connect", "destroy", "destroyPlayer", "fetchAllPlayers", "fetchInfo", "fetchPlayer", "fetchStats", "fetchVersion", "request", "updatePlayer", "updateSession"].every((v) => keys.includes(v))) return false;
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)) return false;
682
- if ("poolOptions" in data && typeof data.poolOptions !== "object" && data.poolOptions !== void 0) return false;
683
- if ("retryAmount" in data && (typeof data.retryAmount !== "number" || isNaN(data.retryAmount) || data.retryAmount <= 0 && data.retryAmount !== void 0)) return false;
684
- if ("retryDelay" in data && (typeof data.retryDelay !== "number" || isNaN(data.retryDelay) || data.retryDelay <= 0 && data.retryDelay !== void 0)) return false;
685
- if ("requestTimeout" in data && (typeof data.requestTimeout !== "number" || isNaN(data.requestTimeout) || data.requestTimeout <= 0 && data.requestTimeout !== void 0)) return false;
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 (this.LavalinkManager.options?.autoChecks?.sourcesValidations && !node.info.sourceManagers?.length) throw new Error("Lavalink Node, has no sourceManagers enabled");
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) throw new Error(`Query is speak, which is limited to 100 characters.`);
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((v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString))) {
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) throw new Error("Using links to make a request is not allowed.");
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((v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString))) {
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 (!this.LavalinkManager.options?.autoChecks?.sourcesValidations) return;
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("Query / Link Provided for this Source but Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
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("Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled");
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
- transformQuery(query) {
829
- const sourceOfQuery = typeof query === "string" ? void 0 : DefaultSources[query.source?.trim?.()?.toLowerCase?.() ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? query.source?.trim?.()?.toLowerCase?.();
830
- const Query = {
831
- query: typeof query === "string" ? query : query.query,
832
- extraQueryUrlParams: typeof query !== "string" ? query.extraQueryUrlParams : void 0,
833
- source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
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
- Query.source = DefaultSources[foundSource];
838
- Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
938
+ return foundSource;
839
939
  }
840
- return Query;
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 sourceOfQuery = typeof query === "string" ? void 0 : DefaultSources[query.source?.trim?.()?.toLowerCase?.() ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? query.source?.trim?.()?.toLowerCase?.();
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: typeof query === "string" ? query : query.query,
846
- types: query.types ? ["track", "playlist", "artist", "album", "text"].filter((v) => query.types?.find((x) => x.toLowerCase().startsWith(v))) : [
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: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
1017
+ source: validSourceExtracted ?? providedSource ?? typedDefault
854
1018
  };
855
- const foundSource = Object.keys(DefaultSources).find((source) => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
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) throw new Error(`Lavalink Node SearchQuerySource: '${sourceString}' is not available`);
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 (!this.LavalinkManager.options?.autoChecks?.sourcesValidations) return;
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("Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jsrec' to work");
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("Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jssearch' to work");
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" && this.LavalinkManager.options?.autoChecks?.pluginValidations && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
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" && this.LavalinkManager.options?.autoChecks?.pluginValidations && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
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) player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
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)) await nextSong.resolve(player);
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]) return queueTrackEnd(player);
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) 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)) if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key]) resTrack.info[key] = data.info[key];
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)) return player.LavalinkManager.utils.buildTrack(data, data.requester);
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) throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
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
- query,
1017
- source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform
1018
- }, data.requester).then((res) => {
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) trackToUse = res.tracks.find(
1021
- (track) => (
1022
- // find via author name (i ... case insensitve)
1023
- [data.info?.author || "", `${data.info?.author} - Topic`].some((name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)) || // find via title (i ... case insensitve)
1024
- new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title)
1025
- )
1026
- );
1027
- if (data.info?.isrc && !trackToUse) trackToUse = res.tracks.find((track) => track.info?.isrc === data.info?.isrc);
1028
- if (data.info?.duration && !trackToUse) trackToUse = res.tracks.find((track) => track.info?.duration >= data.info?.duration - 1500 && track?.info.duration <= data.info?.duration + 1500);
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(obj, (key, value) => {
1035
- if (typeof value === "function") return void 0;
1036
- if (typeof value === "symbol") return void 0;
1037
- if (typeof value === "bigint") return value.toString();
1038
- if (typeof value === "object" && value !== null) {
1039
- if (seen.has(value)) return "[Circular]";
1040
- seen.add(value);
1041
- }
1042
- return value;
1043
- }, padding);
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
- /** Whether the node resuming is enabled or not */
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 whether the plugin validations are enabled or not
1321
+ * Returns wether the plugin validations are enabled or not
1122
1322
  */
1123
1323
  get _checkForPlugins() {
1124
- return !!this._LManager.options?.autoChecks?.pluginValidations;
1324
+ if (this.nodeType === "NodeLink") return false;
1325
+ return !!this.options?.autoChecks?.pluginValidations;
1125
1326
  }
1126
1327
  /**
1127
- * Returns whether the source validations are enabled or not
1328
+ * Returns wether the source validations are enabled or not
1128
1329
  */
1129
1330
  get _checkForSources() {
1130
- return !!this._LManager.options?.autoChecks?.sourcesValidations;
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
- ...options
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) throw new SyntaxError("If secure is true, then the port must be 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
- "Authorization": this.options.authorization
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) throw new Error("The node is not connected to the Lavalink Server!, Please call node.connect() first!");
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) throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`);
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 Whether to throw on an empty result or not
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._LManager.options?.autoChecks?.sourcesValidations && !this.info.sourceManagers.includes("bandcamp")) {
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(resTracks[res.data?.info?.selectedTrack], requestUser) : null,
1299
- duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || cur?.info?.length || 0), 0) : 0
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 Whether to throw on an empty result or not
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)) return this.search({ query: Query.query, source: Query.source }, requestUser);
1321
- if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source)) throw new SyntaxError(`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`);
1322
- if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasearch-plugin")) throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.id}`);
1323
- if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin")) throw new RangeError(`there is no lavasrc-plugin available in the lavalink node: ${this.id}`);
1324
- const { response } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
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) => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser)) })) || [],
1337
- artists: res.artists?.map((v) => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser)) })) || [],
1338
- playlists: res.playlists?.map((v) => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser)) })) || [],
1339
- texts: res.texts?.map((v) => ({ text: v.text, pluginInfo: v?.plugin || v.pluginInfo })) || [],
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("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
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(`ws${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}/v4/websocket`, { headers });
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 whether to delete the nodte from the nodes list too, if false it will emit a disconnect. @default true
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) return void this.NodeManager.emit("disconnect", this, { code: 1e3, reason: destroyReason });
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(Array.from(players.values()).map(
1501
- (player) => player.destroy(destroyReason || "NodeDestroy" /* NodeDestroy */).catch((error) => {
1502
- this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
1503
- state: "error",
1504
- message: `Failed to destroy player ${player.guildId}: ${error.message}`,
1505
- error,
1506
- functionLayer: "Node > destroy() > movePlayers"
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((n) => n.connected && n.options.id !== this.id);
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(Array.from(players.values()).map(
1514
- (player) => player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((error) => {
1515
- this._emitDebugEvent("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
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 destroy player ${player.guildId}: ${error.message}`,
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
- return Promise.allSettled(Array.from(players.values()).map(
1525
- (player) => player.changeNode(nodeToMove.options.id).catch((error) => {
1526
- this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
1527
- state: "error",
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) return void this.NodeManager.emit("disconnect", this, { code: 1e3, reason: destroyReason });
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(await this.request(`/decodetrack?encodedTrack=${encodeURIComponent(encoded.replace(/\s/g, ""))}`), requester);
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)) throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
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 whether to skip the track source or not
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")) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
1690
- if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin")) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
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 whether to skip the track source or not
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")) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
1710
- if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavasrc-plugin") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "java-lyrics-plugin")) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);
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")) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
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")) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);
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) throw new Error("There is no Information about whether you are using NodeLink instead of Lavalink, so this function won't work");
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(`/version`, (r) => {
1789
- r.path = "/version";
1790
- }, true);
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(data.playerOptions.volume / this._LManager.options.playerOptions.volumeDecrementer);
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) player.filterManager.data.timescale = data.playerOptions.filters.timescale;
1898
- if (data.playerOptions.filters.distortion) player.filterManager.data.distortion = data.playerOptions.filters.distortion;
1899
- if (data.playerOptions.filters.pluginFilters) player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
1900
- if (data.playerOptions.filters.vibrato) player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
1901
- if (data.playerOptions.filters.volume) player.filterManager.data.volume = data.playerOptions.filters.volume;
1902
- if (data.playerOptions.filters.equalizer) player.filterManager.equalizerBands = data.playerOptions.filters.equalizer;
1903
- if (data.playerOptions.filters.karaoke) player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
1904
- if (data.playerOptions.filters.lowPass) player.filterManager.data.lowPass = data.playerOptions.filters.lowPass;
1905
- if (data.playerOptions.filters.rotation) player.filterManager.data.rotation = data.playerOptions.filters.rotation;
1906
- if (data.playerOptions.filters.tremolo) player.filterManager.data.tremolo = data.playerOptions.filters.tremolo;
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 Whether to instantly try to reconnect (force it)
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?.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length || 0;
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
- if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
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.options.enablePingOnStatsCheck) this.heartBeat();
2015
- if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
2016
- if (this.options.heartBeatInterval > 0) {
2017
- this.socket.on("pong", () => {
2018
- this.heartBeatPongTimestamp = performance.now();
2019
- this.isAlive = true;
2020
- });
2021
- this.heartBeatInterval = setInterval(() => {
2022
- if (!this.socket) return console.error("Node-Heartbeat-Interval - Socket not available - maybe reconnecting?");
2023
- if (!this.isAlive) return this.close(500, "Node-Heartbeat-Timeout");
2024
- this.isAlive = false;
2025
- this.heartBeatPingTimestamp = performance.now();
2026
- this.socket?.ping?.();
2027
- }, this.options.heartBeatInterval || 3e4);
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) return this._emitDebugEvent("PlayerUpdateNoPlayer" /* PlayerUpdateNoPlayer */, {
2108
- state: "error",
2109
- message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
2110
- functionLayer: "LavalinkNode > nodeEvent > playerUpdate"
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(NodeLinkEventType, player, player.queue.current, payload);
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("error", this, new Error(`Node#event unknown event '${payload.type}'.`), payload);
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"))) return this.queueEnd(player, track, payload);
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) player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
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((v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold);
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({ guildId: player.guildId, playerOptions: { track: { encoded: null } } });
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((v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold);
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")) throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
2389
- return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`);
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")) throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
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()))) throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map((v) => `'${v}'`).join(", ")}`);
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 = { Authorization: this.options.authorization, "Content-Type": "application/json" };
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("internal_sponsorBlockCategories", segments.map((v) => v.toLowerCase()));
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")) throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
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 this._emitDebugEvent("AutoplayNoSongsAdded" /* AutoplayNoSongsAdded */, {
2467
- state: "warn",
2468
- message: `Autoplay was triggered but no songs were added to the queue.`,
2469
- functionLayer: "LavalinkNode > queueEnd() > autoplayFunction"
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) player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
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("playerQueueEmptyStart", player, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs);
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("internal_queueempty", setTimeout(() => {
2506
- player.set("internal_queueempty", void 0);
2507
- if (player.queue.current) {
2508
- return this._LManager.emit("playerQueueEmptyCancel", player);
2509
- }
2510
- this._LManager.emit("playerQueueEmptyEnd", player);
2511
- player.destroy("QueueEmpty" /* QueueEmpty */);
2512
- }, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
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) this.LavalinkManager.options.nodes.forEach((node) => {
2651
- this.createNode(node);
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) throw new Error("There are no nodes to disconnect (all nodes disconnected)");
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) throw new Error("There are no nodes to connect (all nodes connected)");
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}`)) return this.nodes.get(options.id || `${options.host}:${options.port}`);
2712
- const newNode = new LavalinkNode(options, this);
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((a, b) => (a.stats?.cpu?.lavalinkLoad || 0) - (b.stats?.cpu?.lavalinkLoad || 0));
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((a, b) => (a.stats?.cpu?.systemLoad || 0) - (b.stats?.cpu?.systemLoad || 0));
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((a, b) => (a.stats?.playingPlayers || 0) - (b.stats?.playingPlayers || 0));
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) console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
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
- "Cookie": "$Version=1"
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?.((item) => player.LavalinkManager.utils.buildUnresolvedTrack({
2812
- uri: item.url || item.uri,
2813
- artworkUrl: item.img,
2814
- author: item.band_name,
2815
- title: item.name,
2816
- identifier: item.id ? `${item.id}` : item.url?.split("/")?.reverse()[0]
2817
- }, requestUser));
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
- "normalization": {
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
- "echo": {
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) delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.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) delete sendData.pluginFilters["lavalink-filter-plugin"];
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)) delete sendData[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")) throw new Error("Node#Info#filters does not include the 'channelMix' Filter (Node has it not enable)");
3144
- if (!type || !audioOutputsData[type]) throw "Invalid audio type added, must be 'mono' / 'stereo' / 'left' / 'right'";
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")) throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'rotation' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'vibrato' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'tremolo' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'lowPass' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3325
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("low-pass")) throw new Error("Node#Info#filters does not include the 'low-pass' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3351
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("high-pass")) throw new Error("Node#Info#filters does not include the 'high-pass' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3377
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("normalization")) throw new Error("Node#Info#filters does not include the 'normalization' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3403
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo")) throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
3434
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo")) throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)");
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")) throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
3465
- if (this._checkForSources && !this.player?.node?.info?.filters?.includes?.("reverb")) throw new Error("Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)");
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")) throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
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")) throw new Error("Node#Info#filters does not include the 'karaoke' Filter (Node has it not enable)");
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"]')) throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
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((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.previous.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) : [];
3774
- this.tracks = Array.isArray(data.tracks) && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.tracks.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) : [];
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) this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
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)) this.current = data.current;
3796
- if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track))) this.tracks.splice(override ? 0 : this.tracks.length, override ? this.tracks.length : 0, ...data.tracks.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)));
3797
- if (Array.isArray(data.previous) && data?.previous.length && data.previous.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track))) this.previous.splice(0, override ? this.tracks.length : 0, ...data.previous.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)));
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) this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
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((acc, cur) => acc + (cur.info.duration || 0), this.current?.info.duration || 0);
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") this.queueChanges.shuffled(this.guildId, oldStored, this.utils.toJSON());
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(index, 0, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
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(...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)));
3935
- if (typeof this.queueChanges?.tracksAdd === "function") try {
3936
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), this.tracks.length, oldStored, this.utils.toJSON());
3937
- } catch {
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") try {
3956
- this.queueChanges.tracksAdd(this.guildId, (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)), index, oldStored, this.utils.toJSON());
3957
- } catch {
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") try {
3965
- this.queueChanges.tracksRemoved(this.guildId, spliced, index, oldStored, this.utils.toJSON());
3966
- } catch {
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") try {
4013
- this.queueChanges.tracksRemoved(this.guildId, removed2, removeQueryTrack, oldStored, this.utils.toJSON());
4014
- } catch {
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") try {
4030
- this.queueChanges.tracksRemoved(this.guildId, removed3, removeQueryTrack, oldStored, this.utils.toJSON());
4031
- } catch {
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(({ v, i }) => removeQueryTrack.find(
4037
- (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)
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") try {
4048
- this.queueChanges.tracksRemoved(this.guildId, removed2, tracksToRemove.map((v) => v.i), oldStored, this.utils.toJSON());
4049
- } catch {
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") try {
4060
- this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
4061
- } catch {
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") for (const [key, value] of Object.entries(options.customData)) this.set(key, value);
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) throw new Error("No available Node was found, please add a LavalinkNode to the Manager via Manager.NodeManager#createNode");
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(Math.max(Math.min(Math.round(
4329
- this.LavalinkManager.options.playerOptions.volumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
4330
- ), 1e3), 0));
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(this.guildId, {}, new QueueSaver(this.LavalinkManager.options.queueOptions), this.LavalinkManager.options.queueOptions);
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]) return this.play(options);
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) options.clientTrack.userData = {
4401
- ...typeof options?.clientTrack?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.clientTrack?.requester || {}) } : {},
4402
- ...options?.clientTrack.userData,
4403
- ...options.track?.userData
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) vol *= 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(Object.entries({
4422
- encoded: options.track.encoded,
4423
- identifier: options.track.identifier,
4424
- userData: {
4425
- ...typeof options?.track?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {}) } : {},
4426
- ...options.track.userData
4427
- }
4428
- }).filter((v) => typeof v[1] !== "undefined"));
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(Object.entries({
4438
- track,
4439
- position: options.position ?? void 0,
4440
- paused: options.paused ?? void 0,
4441
- endTime: options?.endTime ?? void 0,
4442
- filters: options?.filters ?? void 0,
4443
- volume: options.volume ?? this.lavalinkVolume ?? void 0,
4444
- voice: options.voice ?? void 0
4445
- }).filter((v) => typeof v[1] !== "undefined"))
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) this.queue.current.userData = {
4458
- ...typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {},
4459
- ...this.queue.current?.userData,
4460
- ...options.track?.userData
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]) return this.play(options);
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) vol *= 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(Object.entries({
4486
- track: {
4487
- encoded: this.queue.current?.encoded || null,
4488
- // identifier: options.identifier,
4489
- userData: {
4490
- ...typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {},
4491
- ...options?.track?.userData,
4492
- ...this.queue.current?.userData
4493
- }
4494
- },
4495
- volume: this.lavalinkVolume,
4496
- position: options?.position ?? 0,
4497
- endTime: options?.endTime ?? void 0,
4498
- filters: options?.filters ?? void 0,
4499
- paused: options?.paused ?? void 0,
4500
- voice: options?.voice ?? void 0
4501
- }).filter((v) => typeof v[1] !== "undefined"));
4502
- 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) throw new Error("PlayerOption#position must be a positive number, less than track's duration");
4503
- if (typeof finalOptions.volume !== "undefined" && isNaN(finalOptions.volume) || typeof finalOptions.volume === "number" && finalOptions.volume < 0) throw new Error("PlayerOption#volume must be a positive number");
4504
- 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) throw new Error("PlayerOption#endTime must be a positive number, less than track's duration");
4505
- if (typeof finalOptions.position === "number" && typeof finalOptions.endTime === "number" && finalOptions.endTime < finalOptions.position) throw new Error("PlayerOption#endTime must be bigger than PlayerOption#position");
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(Math.max(Math.min(Math.round(
4525
- this.LavalinkManager.options.playerOptions.volumeDecrementer && !ignoreVolumeDecrementer ? this.volume * this.LavalinkManager.options.playerOptions.volumeDecrementer : this.volume
4526
- ), 1e3), 0));
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({ guildId: this.guildId, playerOptions: { filters: { volume: this.lavalinkVolume / 100 } } });
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({ guildId: this.guildId, playerOptions: { volume: this.lavalinkVolume } });
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) throw new RangeError("Current Track is not seekable / a stream");
4621
- if (position < 0 || position > this.queue.current.info.duration) position = Math.max(Math.min(position, this.queue.current.info.duration), 0);
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)) throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
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)) throw new RangeError("Can't skip more than the queue size");
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({ guildId: this.guildId, playerOptions: { track: { encoded: null }, paused: false } });
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({ guildId: this.guildId, playerOptions: { track: { encoded: null } } });
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) throw new RangeError("No Voice Channel id has been set. (player.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) throw new RangeError("New Channel can't be equal to the old Channel.");
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) throw new RangeError("No Voice Channel id has been set. (player.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) console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
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) console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
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) console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Player got destroyed successfully`);
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) throw new Error("Player is already changing the node please wait");
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(updateNode, this.LavalinkManager.options.playerOptions.defaultSearchPlatform);
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()) throw new RangeError(`defaultSearchPlatform "${this.LavalinkManager.options.playerOptions.defaultSearchPlatform}" is not supported by the newNode`);
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([this.queue.current, ...this.queue.tracks].map((track) => track.info.sourceName));
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 (this.LavalinkManager.options.autoChecks?.sourcesValidations && missingSources.length)
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.LavalinkManager.options?.autoChecks?.pluginValidations || this.node.info?.plugins?.find((v) => v.name === "sponsorblock-plugin");
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) node = Array.from(this.LavalinkManager.nodeManager.leastUsedNodes("playingPlayers")).find((n) => n.connected && n.options.id !== this.node.options.id).id;
4919
- if (!node || !this.LavalinkManager.nodeManager.nodes.get(node)) throw new RangeError("No nodes are available.");
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 */, { state: "log", message: `Player.moveNode() was executed, trying to move from "${this.node.id}" to "${node}"`, functionLayer: "Player > moveNode()" });
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") throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
5088
- if (options?.autoSkip && typeof options?.autoSkip !== "boolean") throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
5089
- if (options?.autoSkipOnResolveError && typeof options?.autoSkipOnResolveError !== "boolean") throw new SyntaxError("ManagerOption.autoSkipOnResolveError must be either false | true aka boolean");
5090
- if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean") throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
5091
- if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every((node) => this.utils.isNodeOptions(node))) throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
5092
- if (typeof options?.autoChecks?.sourcesValidations !== "boolean") throw new SyntaxError("ManagerOption.autoChecks.sourcesValidations must be either false | true aka boolean");
5093
- if (typeof options?.autoChecks?.pluginValidations !== "boolean") throw new SyntaxError("ManagerOption.autoChecks.pluginValidations must be either false | true aka boolean");
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")) throw new SyntaxError(`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`);
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")) throw new SyntaxError(`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`);
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) options.queueOptions.maxPreviousTracks = 25;
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) throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`);
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) throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
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 this._emitDebugEvent("FailedToConnectToNodes" /* FailedToConnectToNodes */, {
5317
- state: "error",
5318
- message: "Failed to connect to at least 1 Node",
5319
- functionLayer: "LavalinkManager > init()"
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
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) return void player.destroy("ChannelDeleted" /* ChannelDeleted */);
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states");
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use }, update, playerVoice: player.voice });
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use }, playerVoice: player.voice, update });
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(update.channel_id === player.voiceChannelId ? "playerVoiceJoin" : "playerVoiceLeave", player, update.user_id);
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) console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, voice update user is not equal to provided client id of the manageroptions#client#id", "user:", update.user_id, "manager client id:", this.options?.client.id);
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) this.emit("playerMove", player, 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) console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`);
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) this.emit("playerMuteChange", player, player.voiceState.selfMute, player.voiceState.serverMute);
5468
- if (selfDeafChanged || serverDeafChanged) this.emit("playerDeafChange", player, player.voiceState.selfDeaf, player.voiceState.serverDeaf);
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({ position: previousPosition, paused: previousPaused, clientTrack: player.queue.current });
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,