lavalink-client 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -307,6 +307,15 @@ import { EventEmitter } from "events";
307
307
  import { isAbsolute } from "path";
308
308
  import WebSocket from "ws";
309
309
 
310
+ // src/structures/Types/Node.ts
311
+ var ReconnectionState = /* @__PURE__ */ ((ReconnectionState2) => {
312
+ ReconnectionState2["IDLE"] = "IDLE";
313
+ ReconnectionState2["RECONNECTING"] = "RECONNECTING";
314
+ ReconnectionState2["PENDING"] = "PENDING";
315
+ ReconnectionState2["DESTROYING"] = "DESTROYING";
316
+ return ReconnectionState2;
317
+ })(ReconnectionState || {});
318
+
310
319
  // src/structures/Utils.ts
311
320
  import { URL as URL2 } from "url";
312
321
  import { isRegExp } from "util/types";
@@ -1010,12 +1019,14 @@ var LavalinkNode = class {
1010
1019
  resuming = { enabled: true, timeout: null };
1011
1020
  /** Actual Lavalink Information of the Node */
1012
1021
  info = null;
1022
+ /** current state of the Reconnections */
1023
+ reconnectionState = "IDLE" /* IDLE */;
1013
1024
  /** The Node Manager of this Node */
1014
1025
  NodeManager = null;
1015
1026
  /** The Reconnection Timeout */
1016
1027
  reconnectTimeout = void 0;
1017
- /** The Reconnection Attempt counter */
1018
- reconnectAttempts = 1;
1028
+ /** The Reconnection Attempt counter (array of datetimes when it tried it.) */
1029
+ reconnectAttempts = [];
1019
1030
  /** The Socket of the Lavalink */
1020
1031
  socket = null;
1021
1032
  /** Version of what the Lavalink Server should be */
@@ -1038,6 +1049,7 @@ var LavalinkNode = class {
1038
1049
  secure: false,
1039
1050
  retryAmount: 5,
1040
1051
  retryDelay: 1e4,
1052
+ retryTimespan: -1,
1041
1053
  requestSignalTimeoutMS: 1e4,
1042
1054
  heartBeatInterval: 3e4,
1043
1055
  closeOnError: true,
@@ -1294,6 +1306,7 @@ var LavalinkNode = class {
1294
1306
  functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat()"
1295
1307
  });
1296
1308
  }
1309
+ this.resetAckTimeouts(false, true);
1297
1310
  if (this.pingTimeout) clearTimeout(this.pingTimeout);
1298
1311
  this.pingTimeout = setTimeout(() => {
1299
1312
  this.pingTimeout = null;
@@ -1390,13 +1403,11 @@ var LavalinkNode = class {
1390
1403
  this.socket?.close(1e3, "Node-Destroy");
1391
1404
  this.socket?.removeAllListeners();
1392
1405
  this.socket = null;
1393
- this.reconnectAttempts = 1;
1394
- clearTimeout(this.reconnectTimeout);
1406
+ this.resetReconnectionAttempts();
1395
1407
  if (deleteNode) {
1396
1408
  this.NodeManager.emit("destroy", this, destroyReason);
1397
1409
  this.NodeManager.nodes.delete(this.id);
1398
- clearInterval(this.heartBeatInterval);
1399
- clearTimeout(this.pingTimeout);
1410
+ this.resetAckTimeouts(true, true);
1400
1411
  } else {
1401
1412
  this.NodeManager.emit("disconnect", this, { code: 1e3, reason: destroyReason });
1402
1413
  }
@@ -1405,13 +1416,11 @@ var LavalinkNode = class {
1405
1416
  this.socket?.close(1e3, "Node-Destroy");
1406
1417
  this.socket?.removeAllListeners();
1407
1418
  this.socket = null;
1408
- this.reconnectAttempts = 1;
1409
- clearTimeout(this.reconnectTimeout);
1419
+ this.resetReconnectionAttempts();
1410
1420
  if (deleteNode) {
1411
1421
  this.NodeManager.emit("destroy", this, destroyReason);
1412
1422
  this.NodeManager.nodes.delete(this.id);
1413
- clearInterval(this.heartBeatInterval);
1414
- clearTimeout(this.pingTimeout);
1423
+ this.resetAckTimeouts(true, true);
1415
1424
  } else {
1416
1425
  this.NodeManager.emit("disconnect", this, { code: 1e3, reason: destroyReason });
1417
1426
  }
@@ -1435,8 +1444,7 @@ var LavalinkNode = class {
1435
1444
  this.socket?.close(1e3, "Node-Disconnect");
1436
1445
  this.socket?.removeAllListeners();
1437
1446
  this.socket = null;
1438
- this.reconnectAttempts = 1;
1439
- clearTimeout(this.reconnectTimeout);
1447
+ this.resetReconnectionAttempts();
1440
1448
  this.NodeManager.emit("disconnect", this, { code: 1e3, reason: disconnectReason });
1441
1449
  }
1442
1450
  /**
@@ -1806,46 +1814,91 @@ var LavalinkNode = class {
1806
1814
  get restAddress() {
1807
1815
  return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
1808
1816
  }
1817
+ /**
1818
+ * If already trying to reconnect or pending, return
1819
+ */
1820
+ get isNodeReconnecting() {
1821
+ return this.reconnectionState !== "IDLE" /* IDLE */;
1822
+ }
1809
1823
  /**
1810
1824
  * Reconnect to the lavalink node
1811
- * @param instaReconnect @default false wether to instantly try to reconnect
1825
+ * @param force @default false Wether to instantly try to reconnect (force it)
1812
1826
  * @returns void
1813
1827
  *
1814
1828
  * @example
1815
1829
  * ```ts
1816
- * await player.node.reconnect();
1830
+ * await player.node.reconnect(true); //true forcefully trys the reconnect
1817
1831
  * ```
1818
1832
  */
1819
- reconnect(instaReconnect = false) {
1833
+ reconnect(force = false) {
1834
+ if (this.isNodeReconnecting) {
1835
+ return;
1836
+ }
1837
+ this.reconnectionState = "PENDING" /* PENDING */;
1820
1838
  this.NodeManager.emit("reconnectinprogress", this);
1821
- if (instaReconnect) {
1822
- if (this.reconnectAttempts >= this.options.retryAmount) {
1823
- const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
1824
- this.NodeManager.emit("error", this, error);
1825
- return this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
1826
- }
1827
- this.NodeManager.emit("reconnecting", this);
1828
- this.connect();
1829
- this.reconnectAttempts++;
1839
+ if (force) {
1840
+ this.executeReconnect();
1830
1841
  return;
1831
1842
  }
1843
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
1832
1844
  this.reconnectTimeout = setTimeout(() => {
1833
1845
  this.reconnectTimeout = null;
1834
- if (this.reconnectAttempts >= this.options.retryAmount) {
1835
- const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
1836
- this.NodeManager.emit("error", this, error);
1837
- return this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
1838
- }
1839
- this.NodeManager.emit("reconnecting", this);
1840
- this.connect();
1841
- this.reconnectAttempts++;
1846
+ this.executeReconnect();
1842
1847
  }, this.options.retryDelay || 1e3);
1843
1848
  }
1849
+ get reconnectionAttemptCount() {
1850
+ const maxAllowedTimestan = this.options.retryTimespan || -1;
1851
+ if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
1852
+ return this.reconnectAttempts.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length;
1853
+ }
1854
+ /**
1855
+ * Private Utility function to execute the reconnection
1856
+ */
1857
+ executeReconnect() {
1858
+ if (this.reconnectionAttemptCount >= this.options.retryAmount) {
1859
+ const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
1860
+ this.reconnectionState = "DESTROYING" /* DESTROYING */;
1861
+ this.NodeManager.emit("error", this, error);
1862
+ this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
1863
+ return;
1864
+ }
1865
+ this.reconnectAttempts.push(Date.now());
1866
+ this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
1867
+ this.NodeManager.emit("reconnecting", this);
1868
+ this.connect();
1869
+ }
1870
+ /**
1871
+ * Private function to reset the reconnection attempts
1872
+ * @returns
1873
+ */
1874
+ resetReconnectionAttempts() {
1875
+ this.reconnectionState = "IDLE" /* IDLE */;
1876
+ this.reconnectAttempts = [];
1877
+ clearTimeout(this.reconnectTimeout);
1878
+ this.reconnectTimeout = null;
1879
+ return;
1880
+ }
1881
+ /**
1882
+ * Private function to reset timeouts/intervals for heartbeating/pinging
1883
+ * @param heartbeat
1884
+ * @param ping
1885
+ * @returns
1886
+ */
1887
+ resetAckTimeouts(heartbeat = true, ping = true) {
1888
+ if (ping) {
1889
+ if (this.pingTimeout) clearTimeout(this.pingTimeout);
1890
+ this.pingTimeout = null;
1891
+ }
1892
+ if (heartbeat) {
1893
+ if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1894
+ this.heartBeatInterval = null;
1895
+ }
1896
+ return;
1897
+ }
1844
1898
  /** @private util function for handling opening events from websocket */
1845
1899
  async open() {
1846
1900
  this.isAlive = true;
1847
- this.reconnectAttempts = 1;
1848
- if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
1901
+ this.resetReconnectionAttempts();
1849
1902
  if (this.options.enablePingOnStatsCheck) this.heartBeat();
1850
1903
  if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1851
1904
  if (this.options.heartBeatInterval > 0) {
@@ -1870,7 +1923,7 @@ var LavalinkNode = class {
1870
1923
  }
1871
1924
  /** @private util function for handling closing events from websocket */
1872
1925
  close(code, reason) {
1873
- if (this.pingTimeout) clearTimeout(this.pingTimeout);
1926
+ this.resetAckTimeouts(true, true);
1874
1927
  try {
1875
1928
  if (this.socket) {
1876
1929
  this.socket.removeAllListeners();
@@ -1886,7 +1939,6 @@ var LavalinkNode = class {
1886
1939
  }
1887
1940
  }
1888
1941
  this.isAlive = false;
1889
- if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1890
1942
  if (code === 1006 && !reason) reason = "Socket got terminated due to no ping connection";
1891
1943
  if (code === 1e3 && reason === "Node-Disconnect") return;
1892
1944
  this.NodeManager.emit("disconnect", this, { code, reason });
@@ -1908,6 +1960,7 @@ var LavalinkNode = class {
1908
1960
  error(error) {
1909
1961
  if (!error) return;
1910
1962
  this.NodeManager.emit("error", this, error);
1963
+ this.reconnect();
1911
1964
  if (this.options.closeOnError) {
1912
1965
  if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1913
1966
  if (this.pingTimeout) clearTimeout(this.pingTimeout);
@@ -1971,6 +2024,7 @@ var LavalinkNode = class {
1971
2024
  this.handleEvent(payload);
1972
2025
  break;
1973
2026
  case "ready":
2027
+ this.resetReconnectionAttempts();
1974
2028
  this.sessionId = payload.sessionId;
1975
2029
  this.resuming.enabled = payload.resumed;
1976
2030
  if (payload.resumed === true) {
@@ -2340,10 +2394,7 @@ var LavalinkNode = class {
2340
2394
  });
2341
2395
  }
2342
2396
  this.NodeManager.LavalinkManager.emit("playerQueueEmptyStart", player, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs);
2343
- if (player.get("internal_queueempty")) {
2344
- clearTimeout(player.get("internal_queueempty"));
2345
- player.set("internal_queueempty", void 0);
2346
- }
2397
+ if (player.get("internal_queueempty")) clearTimeout(player.get("internal_queueempty"));
2347
2398
  player.set("internal_queueempty", setTimeout(() => {
2348
2399
  player.set("internal_queueempty", void 0);
2349
2400
  if (player.queue.current) {
@@ -2649,7 +2700,13 @@ var bandCampSearch = async (player, query, requestUser) => {
2649
2700
  "Cookie": "$Version=1"
2650
2701
  }
2651
2702
  });
2652
- const json = await data.json();
2703
+ if (!data.ok) throw new Error(`Bandcamp Error: ${data.statusText}`);
2704
+ let json = null;
2705
+ try {
2706
+ json = await data.json();
2707
+ } catch {
2708
+ throw new Error("Invalid JSON response from Bandcamp");
2709
+ }
2653
2710
  tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.((item) => player.LavalinkManager.utils.buildUnresolvedTrack({
2654
2711
  uri: item.url || item.uri,
2655
2712
  artworkUrl: item.img,
@@ -2864,15 +2921,15 @@ var FilterManager = class {
2864
2921
  const lavalinkFilterData = this.data.pluginFilters?.["lavalink-filter-plugin"] || { echo: { decay: this.data.pluginFilters?.echo?.decay && !this.data.pluginFilters?.echo?.echoLength ? this.data.pluginFilters.echo.decay : 0, delay: this.data.pluginFilters?.echo?.delay || 0 }, reverb: { gains: [], delays: [], ...this.data.pluginFilters.reverb } };
2865
2922
  this.filters.lavalinkFilterPlugin.echo = lavalinkFilterData.echo.decay !== 0 || lavalinkFilterData.echo.delay !== 0;
2866
2923
  this.filters.lavalinkFilterPlugin.reverb = lavalinkFilterData.reverb?.delays?.length !== 0 || lavalinkFilterData.reverb?.gains?.length !== 0;
2867
- this.filters.lavalinkLavaDspxPlugin.highPass = Object.values(this.data.pluginFilters["high-pass"] || {}).length > 0;
2868
- this.filters.lavalinkLavaDspxPlugin.lowPass = Object.values(this.data.pluginFilters["low-pass"] || {}).length > 0;
2869
- this.filters.lavalinkLavaDspxPlugin.normalization = Object.values(this.data.pluginFilters.normalization || {}).length > 0;
2870
- this.filters.lavalinkLavaDspxPlugin.echo = Object.values(this.data.pluginFilters.echo || {}).length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined";
2871
- this.filters.lowPass = this.data.lowPass.smoothing !== 0;
2872
- this.filters.karaoke = Object.values(this.data.karaoke).some((v) => v !== 0);
2924
+ this.filters.lavalinkLavaDspxPlugin.highPass = Object.values(this.data.pluginFilters?.["high-pass"] || {}).length > 0;
2925
+ this.filters.lavalinkLavaDspxPlugin.lowPass = Object.values(this.data.pluginFilters?.["low-pass"] || {}).length > 0;
2926
+ this.filters.lavalinkLavaDspxPlugin.normalization = Object.values(this.data.pluginFilters?.normalization || {}).length > 0;
2927
+ this.filters.lavalinkLavaDspxPlugin.echo = Object.values(this.data.pluginFilters?.echo || {}).length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined";
2928
+ this.filters.lowPass = this.privateNot0(this.data.lowPass?.smoothing);
2929
+ this.filters.karaoke = Object.values(this.data.karaoke ?? {}).some((v) => v !== 0);
2873
2930
  if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
2874
- if (oldFilterTimescale.pitch !== this.data.timescale.pitch || oldFilterTimescale.rate !== this.data.timescale.rate || oldFilterTimescale.speed !== this.data.timescale.speed) {
2875
- this.filters.custom = Object.values(this.data.timescale).some((v) => v !== 1);
2931
+ if (oldFilterTimescale.pitch !== this.data.timescale?.pitch || oldFilterTimescale.rate !== this.data.timescale?.rate || oldFilterTimescale.speed !== this.data.timescale?.speed) {
2932
+ this.filters.custom = Object.values(this.data.timescale || {}).some((v) => v !== 1);
2876
2933
  this.filters.nightcore = false;
2877
2934
  this.filters.vaporwave = false;
2878
2935
  }
@@ -3685,8 +3742,11 @@ var Queue = class {
3685
3742
  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());
3686
3743
  } catch {
3687
3744
  }
3688
- let spliced = TrackOrTracks ? this.tracks.splice(index, amount, ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))) : this.tracks.splice(index, amount);
3689
- spliced = Array.isArray(spliced) ? spliced : [spliced];
3745
+ const spliced = TrackOrTracks ? this.tracks.splice(
3746
+ index,
3747
+ amount,
3748
+ ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
3749
+ ) : this.tracks.splice(index, amount);
3690
3750
  if (typeof this.queueChanges?.tracksRemoved === "function") try {
3691
3751
  this.queueChanges.tracksRemoved(this.guildId, spliced, index, oldStored, this.utils.toJSON());
3692
3752
  } catch {
@@ -4251,6 +4311,7 @@ var Player = class {
4251
4311
  else this.set("internal_autoplayStopPlaying", void 0);
4252
4312
  const now = performance.now();
4253
4313
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { track: { encoded: null } } });
4314
+ this.paused = false;
4254
4315
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
4255
4316
  return this;
4256
4317
  }
@@ -5053,6 +5114,7 @@ var LavalinkManager = class extends EventEmitter2 {
5053
5114
  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)}`);
5054
5115
  }
5055
5116
  player.voiceChannelId = update.channel_id;
5117
+ player.options.voiceChannelId = update.channel_id;
5056
5118
  const selfMuteChanged = typeof update.self_mute === "boolean" && player.voiceState.selfMute !== update.self_mute;
5057
5119
  const serverMuteChanged = typeof update.mute === "boolean" && player.voiceState.serverMute !== update.mute;
5058
5120
  const selfDeafChanged = typeof update.self_deaf === "boolean" && player.voiceState.selfDeaf !== update.self_deaf;
@@ -5095,11 +5157,13 @@ var LavalinkManager = class extends EventEmitter2 {
5095
5157
  if (player.queue.tracks.length) {
5096
5158
  return void await player.play({ paused: previousPaused });
5097
5159
  }
5098
- this.emit("debug", "PlayerAutoReconnect" /* PlayerAutoReconnect */, {
5099
- state: "log",
5100
- message: `Auto reconnected, but nothing to play`,
5101
- functionLayer: "LavalinkManager > sendRawData()"
5102
- });
5160
+ if (this.options?.advancedOptions?.enableDebugEvents) {
5161
+ this.emit("debug", "PlayerAutoReconnect" /* PlayerAutoReconnect */, {
5162
+ state: "log",
5163
+ message: `Auto reconnected, but nothing to play`,
5164
+ functionLayer: "LavalinkManager > sendRawData()"
5165
+ });
5166
+ }
5103
5167
  return;
5104
5168
  } catch (e) {
5105
5169
  console.error(e);
@@ -5133,6 +5197,7 @@ export {
5133
5197
  Queue,
5134
5198
  QueueSaver,
5135
5199
  QueueSymbol,
5200
+ ReconnectionState,
5136
5201
  SourceLinksRegexes,
5137
5202
  TrackSymbol,
5138
5203
  UnresolvedTrackSymbol,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -55,12 +55,12 @@
55
55
  },
56
56
  "homepage": "https://tomato6966.github.io/lavalink-client/",
57
57
  "devDependencies": {
58
- "@eslint/eslintrc": "^3.3.1",
58
+ "@eslint/eslintrc": "^3.3.3",
59
59
  "@eslint/js": "^9.39.1",
60
60
  "@types/node": "^24.10.1",
61
61
  "@types/ws": "^8.18.1",
62
- "@typescript-eslint/eslint-plugin": "^8.46.4",
63
- "@typescript-eslint/parser": "^8.46.4",
62
+ "@typescript-eslint/eslint-plugin": "^8.48.0",
63
+ "@typescript-eslint/parser": "^8.48.0",
64
64
  "eslint": "^9.39.1",
65
65
  "tsup": "^8.5.1",
66
66
  "typescript": "^5.9.3"