lavalink-client 2.9.1 → 2.9.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/README.md CHANGED
@@ -44,7 +44,7 @@
44
44
 
45
45
  ## 📦 Installation
46
46
 
47
- **Latest Stable Version: `v2.5.x`**
47
+ **Latest Stable Version: `v2.9.x`**
48
48
 
49
49
  <details>
50
50
  <summary><strong>👉 via NPM</strong></summary>
@@ -359,6 +359,7 @@ This client powers various Discord bots:
359
359
  - [All Time Bot](https://top.gg/bot/1163027457671180418) (@PeterGamez)
360
360
  - [BeatDock](https://github.com/lazaroagomez/BeatDock) (@lazaroagomez)
361
361
  - [Nazha](https://top.gg/bot/1124681788070055967) (@Nazha-Team)
362
+ - [Arii Music](https://arimusic.me/) (@friston_ae)
362
363
 
363
364
  ---
364
365
 
package/dist/index.d.mts CHANGED
@@ -2699,6 +2699,14 @@ declare class Player {
2699
2699
  * @param options
2700
2700
  */
2701
2701
  play(options?: Partial<PlayOptions>): any;
2702
+ /**
2703
+ * The old JSON of the player, used for the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
2704
+ */
2705
+ oldJSON: PlayerJson;
2706
+ /**
2707
+ * Emits the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
2708
+ */
2709
+ triggerPlayerClientUpdate(): void;
2702
2710
  /**
2703
2711
  * Set the Volume for the Player
2704
2712
  * @param volume The Volume in percent
@@ -3793,6 +3801,20 @@ interface LavalinkManagerEvents<CustomPlayerT extends Player = Player> {
3793
3801
  * @event Manager#playerUpdate
3794
3802
  */
3795
3803
  playerUpdate: (oldPlayerJson: PlayerJson, newPlayer: CustomPlayerT) => void;
3804
+ /**
3805
+ * Always emits when the player (on client side) got updated via a function-call.
3806
+ * This is useful for example, if you want to save the player data on every update, or similar.
3807
+ * @event Manager#playerClientUpdate
3808
+ *
3809
+ * Emits only when you call one of those functions:
3810
+ * player.pause()
3811
+ * player.resume()
3812
+ * player.seek()
3813
+ * player.setRepeatMode()
3814
+ * player.setVolume()
3815
+ * and on every call of the filterManager.
3816
+ */
3817
+ playerClientUpdate: (oldPlayerJson: PlayerJson, newPlayer: CustomPlayerT) => void;
3796
3818
  /**
3797
3819
  * Emitted when the player's selfMuted or serverMuted state changed (true -> false | false -> true)
3798
3820
  * @event Manager#playerMuteChange
@@ -3944,6 +3966,14 @@ interface ManagerPlayerOptions<CustomPlayerT extends Player = Player> {
3944
3966
  destroyAfterMs?: number;
3945
3967
  };
3946
3968
  useUnresolvedData?: boolean;
3969
+ /**
3970
+ * If true (default), when changing nodes, `setSponsorBlock()` with default categories is called
3971
+ * even if the user never explicitly set categories. This is needed so the SponsorBlock plugin
3972
+ * registers its event listeners (ChapterStarted/ChapterLoaded) on the new node.
3973
+ * Set to false to disable this behavior if you don't use SponsorBlock events.
3974
+ * @default true
3975
+ */
3976
+ enforceSponsorBlockRequestForEventEnablement?: boolean;
3947
3977
  }
3948
3978
  type DeepRequired<T> = {
3949
3979
  [K in keyof T]-?: NonNullable<T[K]> extends object ? DeepRequired<NonNullable<T[K]>> : NonNullable<T[K]>;
package/dist/index.d.ts CHANGED
@@ -2699,6 +2699,14 @@ declare class Player {
2699
2699
  * @param options
2700
2700
  */
2701
2701
  play(options?: Partial<PlayOptions>): any;
2702
+ /**
2703
+ * The old JSON of the player, used for the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
2704
+ */
2705
+ oldJSON: PlayerJson;
2706
+ /**
2707
+ * Emits the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
2708
+ */
2709
+ triggerPlayerClientUpdate(): void;
2702
2710
  /**
2703
2711
  * Set the Volume for the Player
2704
2712
  * @param volume The Volume in percent
@@ -3793,6 +3801,20 @@ interface LavalinkManagerEvents<CustomPlayerT extends Player = Player> {
3793
3801
  * @event Manager#playerUpdate
3794
3802
  */
3795
3803
  playerUpdate: (oldPlayerJson: PlayerJson, newPlayer: CustomPlayerT) => void;
3804
+ /**
3805
+ * Always emits when the player (on client side) got updated via a function-call.
3806
+ * This is useful for example, if you want to save the player data on every update, or similar.
3807
+ * @event Manager#playerClientUpdate
3808
+ *
3809
+ * Emits only when you call one of those functions:
3810
+ * player.pause()
3811
+ * player.resume()
3812
+ * player.seek()
3813
+ * player.setRepeatMode()
3814
+ * player.setVolume()
3815
+ * and on every call of the filterManager.
3816
+ */
3817
+ playerClientUpdate: (oldPlayerJson: PlayerJson, newPlayer: CustomPlayerT) => void;
3796
3818
  /**
3797
3819
  * Emitted when the player's selfMuted or serverMuted state changed (true -> false | false -> true)
3798
3820
  * @event Manager#playerMuteChange
@@ -3944,6 +3966,14 @@ interface ManagerPlayerOptions<CustomPlayerT extends Player = Player> {
3944
3966
  destroyAfterMs?: number;
3945
3967
  };
3946
3968
  useUnresolvedData?: boolean;
3969
+ /**
3970
+ * If true (default), when changing nodes, `setSponsorBlock()` with default categories is called
3971
+ * even if the user never explicitly set categories. This is needed so the SponsorBlock plugin
3972
+ * registers its event listeners (ChapterStarted/ChapterLoaded) on the new node.
3973
+ * Set to false to disable this behavior if you don't use SponsorBlock events.
3974
+ * @default true
3975
+ */
3976
+ enforceSponsorBlockRequestForEventEnablement?: boolean;
3947
3977
  }
3948
3978
  type DeepRequired<T> = {
3949
3979
  [K in keyof T]-?: NonNullable<T[K]> extends object ? DeepRequired<NonNullable<T[K]>> : NonNullable<T[K]>;
package/dist/index.js CHANGED
@@ -2581,12 +2581,13 @@ var LavalinkNode = class _LavalinkNode {
2581
2581
  if (!error) return;
2582
2582
  this.NodeManager.emit("error", this, error);
2583
2583
  this.reconnectionState = "IDLE" /* IDLE */;
2584
- this.reconnect();
2585
2584
  if (this.options.closeOnError) {
2586
2585
  if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
2587
2586
  if (this.pingTimeout) clearTimeout(this.pingTimeout);
2588
2587
  this.socket?.close(500, "Node-Error - Force Reconnect");
2588
+ return;
2589
2589
  }
2590
+ this.reconnect();
2590
2591
  }
2591
2592
  /** @private util function for handling message events from websocket */
2592
2593
  async message(d) {
@@ -2810,7 +2811,7 @@ var LavalinkNode = class _LavalinkNode {
2810
2811
  (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2811
2812
  );
2812
2813
  player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2813
- if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2814
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2814
2815
  this._emitDebugEvent("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */, {
2815
2816
  state: "log",
2816
2817
  message: `trackStuck Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
@@ -2848,7 +2849,7 @@ var LavalinkNode = class _LavalinkNode {
2848
2849
  (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2849
2850
  );
2850
2851
  player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2851
- if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2852
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2852
2853
  this._emitDebugEvent("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */, {
2853
2854
  state: "log",
2854
2855
  message: `TrackError Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
@@ -3908,6 +3909,7 @@ var FilterManager = class {
3908
3909
  }
3909
3910
  const now = performance.now();
3910
3911
  if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
3912
+ this.player.triggerPlayerClientUpdate();
3911
3913
  await this.player.node.updatePlayer({
3912
3914
  guildId: this.player.guildId,
3913
3915
  playerOptions: {
@@ -4002,7 +4004,7 @@ var FilterManager = class {
4002
4004
  this.filters.tremolo = false;
4003
4005
  this.filters.vibrato = false;
4004
4006
  this.filters.karaoke = false;
4005
- this.filters.karaoke = false;
4007
+ this.filters.vaporwave = false;
4006
4008
  this.filters.volume = false;
4007
4009
  this.filters.nodeLinkEcho = false;
4008
4010
  this.filters.nodeLinkChorus = false;
@@ -4083,7 +4085,7 @@ var FilterManager = class {
4083
4085
  this.data = this.data ?? {};
4084
4086
  this.filters.nightcore = false;
4085
4087
  this.filters.vaporwave = false;
4086
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, speed };
4088
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, speed };
4087
4089
  this.isCustomFilterActive();
4088
4090
  await this.applyPlayerFilters();
4089
4091
  return this;
@@ -4105,7 +4107,7 @@ var FilterManager = class {
4105
4107
  this.data = this.data ?? {};
4106
4108
  this.filters.nightcore = false;
4107
4109
  this.filters.vaporwave = false;
4108
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, pitch };
4110
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, pitch };
4109
4111
  this.isCustomFilterActive();
4110
4112
  await this.applyPlayerFilters();
4111
4113
  return this;
@@ -4127,7 +4129,7 @@ var FilterManager = class {
4127
4129
  this.data = this.data ?? {};
4128
4130
  this.filters.nightcore = false;
4129
4131
  this.filters.vaporwave = false;
4130
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, rate };
4132
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, rate };
4131
4133
  this.isCustomFilterActive();
4132
4134
  await this.applyPlayerFilters();
4133
4135
  return this;
@@ -4556,6 +4558,7 @@ var FilterManager = class {
4556
4558
  if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
4557
4559
  const now = performance.now();
4558
4560
  if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
4561
+ this.player.triggerPlayerClientUpdate();
4559
4562
  await this.player.node.updatePlayer({
4560
4563
  guildId: this.player.guildId,
4561
4564
  playerOptions: {
@@ -4712,7 +4715,7 @@ var Queue = class {
4712
4715
  * @param queueOptions
4713
4716
  */
4714
4717
  constructor(guildId, data = {}, QueueSaver2, queueOptions) {
4715
- this.queueChanges = queueOptions.queueChangesWatcher || null;
4718
+ this.queueChanges = queueOptions?.queueChangesWatcher || null;
4716
4719
  this.guildId = guildId;
4717
4720
  this.QueueSaver = QueueSaver2;
4718
4721
  this.options.maxPreviousTracks = this.QueueSaver?.options?.maxPreviousTracks ?? this.options.maxPreviousTracks;
@@ -4763,7 +4766,7 @@ var Queue = class {
4763
4766
  ))
4764
4767
  this.previous.splice(
4765
4768
  0,
4766
- override ? this.tracks.length : 0,
4769
+ override ? this.previous.length : 0,
4767
4770
  ...data.previous.filter(
4768
4771
  (track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
4769
4772
  )
@@ -4778,8 +4781,8 @@ var Queue = class {
4778
4781
  * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
4779
4782
  */
4780
4783
  toJSON: () => {
4781
- if (this.previous.length > this.options.maxPreviousTracks)
4782
- this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
4784
+ if (this.previous?.length > this.options?.maxPreviousTracks)
4785
+ this.previous?.splice(this.options?.maxPreviousTracks, this.previous.length);
4783
4786
  return {
4784
4787
  current: this.current ? { ...this.current } : null,
4785
4788
  previous: this.previous ? [...this.previous] : [],
@@ -5595,6 +5598,17 @@ var Player = class {
5595
5598
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5596
5599
  return this;
5597
5600
  }
5601
+ /**
5602
+ * The old JSON of the player, used for the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
5603
+ */
5604
+ oldJSON = this.toJSON();
5605
+ /**
5606
+ * Emits the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
5607
+ */
5608
+ triggerPlayerClientUpdate() {
5609
+ this.LavalinkManager.emit("playerClientUpdate", this.oldJSON, this);
5610
+ this.oldJSON = this.toJSON();
5611
+ }
5598
5612
  /**
5599
5613
  * Set the Volume for the Player
5600
5614
  * @param volume The Volume in percent
@@ -5615,6 +5629,7 @@ var Player = class {
5615
5629
  0
5616
5630
  )
5617
5631
  );
5632
+ this.triggerPlayerClientUpdate();
5618
5633
  const now = performance.now();
5619
5634
  if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
5620
5635
  this._emitDebugEvent("PlayerVolumeAsFilter" /* PlayerVolumeAsFilter */, {
@@ -5689,6 +5704,7 @@ var Player = class {
5689
5704
  this.paused = true;
5690
5705
  this.lastPositionChange = null;
5691
5706
  const now = performance.now();
5707
+ this.triggerPlayerClientUpdate();
5692
5708
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
5693
5709
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5694
5710
  this.LavalinkManager.emit("playerPaused", this, this.queue.current);
@@ -5701,6 +5717,7 @@ var Player = class {
5701
5717
  if (!this.paused) throw new Error("Player isn't paused - not able to resume.");
5702
5718
  this.paused = false;
5703
5719
  const now = performance.now();
5720
+ this.triggerPlayerClientUpdate();
5704
5721
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
5705
5722
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5706
5723
  this.LavalinkManager.emit("playerResumed", this, this.queue.current);
@@ -5721,6 +5738,7 @@ var Player = class {
5721
5738
  this.lastPositionChange = Date.now();
5722
5739
  this.lastPosition = position;
5723
5740
  const now = performance.now();
5741
+ this.triggerPlayerClientUpdate();
5724
5742
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { position } });
5725
5743
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5726
5744
  return this;
@@ -5733,6 +5751,7 @@ var Player = class {
5733
5751
  if (!["off", "track", "queue"].includes(repeatMode))
5734
5752
  throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
5735
5753
  this.repeatMode = repeatMode;
5754
+ this.triggerPlayerClientUpdate();
5736
5755
  return this;
5737
5756
  }
5738
5757
  /**
@@ -5987,7 +6006,7 @@ var Player = class {
5987
6006
  functionLayer: "Player > changeNode()"
5988
6007
  });
5989
6008
  });
5990
- } else {
6009
+ } else if (this.LavalinkManager.options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement !== false) {
5991
6010
  await this.setSponsorBlock().catch((error) => {
5992
6011
  this._emitDebugEvent("PlayerChangeNode" /* PlayerChangeNode */, {
5993
6012
  state: "error",
@@ -6083,7 +6102,7 @@ var Player = class {
6083
6102
  nodeId: this.node?.id,
6084
6103
  nodeSessionId: this.node?.sessionId,
6085
6104
  ping: this.ping,
6086
- queue: this.queue.utils.toJSON()
6105
+ queue: this.queue?.utils?.toJSON?.()
6087
6106
  };
6088
6107
  }
6089
6108
  };
@@ -6183,7 +6202,8 @@ var LavalinkManager = class extends import_events2.EventEmitter {
6183
6202
  maxErrorsPerTime: {
6184
6203
  threshold: options?.playerOptions?.maxErrorsPerTime?.threshold ?? 35e3,
6185
6204
  maxAmount: options?.playerOptions?.maxErrorsPerTime?.maxAmount ?? 3
6186
- }
6205
+ },
6206
+ enforceSponsorBlockRequestForEventEnablement: options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement ?? true
6187
6207
  },
6188
6208
  linksWhitelist: options?.linksWhitelist ?? [],
6189
6209
  linksBlacklist: options?.linksBlacklist ?? [],
package/dist/index.mjs CHANGED
@@ -2517,12 +2517,13 @@ var LavalinkNode = class _LavalinkNode {
2517
2517
  if (!error) return;
2518
2518
  this.NodeManager.emit("error", this, error);
2519
2519
  this.reconnectionState = "IDLE" /* IDLE */;
2520
- this.reconnect();
2521
2520
  if (this.options.closeOnError) {
2522
2521
  if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
2523
2522
  if (this.pingTimeout) clearTimeout(this.pingTimeout);
2524
2523
  this.socket?.close(500, "Node-Error - Force Reconnect");
2524
+ return;
2525
2525
  }
2526
+ this.reconnect();
2526
2527
  }
2527
2528
  /** @private util function for handling message events from websocket */
2528
2529
  async message(d) {
@@ -2746,7 +2747,7 @@ var LavalinkNode = class _LavalinkNode {
2746
2747
  (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2747
2748
  );
2748
2749
  player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2749
- if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2750
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2750
2751
  this._emitDebugEvent("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */, {
2751
2752
  state: "log",
2752
2753
  message: `trackStuck Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
@@ -2784,7 +2785,7 @@ var LavalinkNode = class _LavalinkNode {
2784
2785
  (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2785
2786
  );
2786
2787
  player.set("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2787
- if (oldTimestamps.length > this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2788
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2788
2789
  this._emitDebugEvent("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */, {
2789
2790
  state: "log",
2790
2791
  message: `TrackError Event was triggered too often within a given threshold (LavalinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
@@ -3844,6 +3845,7 @@ var FilterManager = class {
3844
3845
  }
3845
3846
  const now = performance.now();
3846
3847
  if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
3848
+ this.player.triggerPlayerClientUpdate();
3847
3849
  await this.player.node.updatePlayer({
3848
3850
  guildId: this.player.guildId,
3849
3851
  playerOptions: {
@@ -3938,7 +3940,7 @@ var FilterManager = class {
3938
3940
  this.filters.tremolo = false;
3939
3941
  this.filters.vibrato = false;
3940
3942
  this.filters.karaoke = false;
3941
- this.filters.karaoke = false;
3943
+ this.filters.vaporwave = false;
3942
3944
  this.filters.volume = false;
3943
3945
  this.filters.nodeLinkEcho = false;
3944
3946
  this.filters.nodeLinkChorus = false;
@@ -4019,7 +4021,7 @@ var FilterManager = class {
4019
4021
  this.data = this.data ?? {};
4020
4022
  this.filters.nightcore = false;
4021
4023
  this.filters.vaporwave = false;
4022
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, speed };
4024
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, speed };
4023
4025
  this.isCustomFilterActive();
4024
4026
  await this.applyPlayerFilters();
4025
4027
  return this;
@@ -4041,7 +4043,7 @@ var FilterManager = class {
4041
4043
  this.data = this.data ?? {};
4042
4044
  this.filters.nightcore = false;
4043
4045
  this.filters.vaporwave = false;
4044
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, pitch };
4046
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, pitch };
4045
4047
  this.isCustomFilterActive();
4046
4048
  await this.applyPlayerFilters();
4047
4049
  return this;
@@ -4063,7 +4065,7 @@ var FilterManager = class {
4063
4065
  this.data = this.data ?? {};
4064
4066
  this.filters.nightcore = false;
4065
4067
  this.filters.vaporwave = false;
4066
- this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, rate };
4068
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, rate };
4067
4069
  this.isCustomFilterActive();
4068
4070
  await this.applyPlayerFilters();
4069
4071
  return this;
@@ -4492,6 +4494,7 @@ var FilterManager = class {
4492
4494
  if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
4493
4495
  const now = performance.now();
4494
4496
  if (this.player.options.instaUpdateFiltersFix === true) this.filterUpdatedState = true;
4497
+ this.player.triggerPlayerClientUpdate();
4495
4498
  await this.player.node.updatePlayer({
4496
4499
  guildId: this.player.guildId,
4497
4500
  playerOptions: {
@@ -4648,7 +4651,7 @@ var Queue = class {
4648
4651
  * @param queueOptions
4649
4652
  */
4650
4653
  constructor(guildId, data = {}, QueueSaver2, queueOptions) {
4651
- this.queueChanges = queueOptions.queueChangesWatcher || null;
4654
+ this.queueChanges = queueOptions?.queueChangesWatcher || null;
4652
4655
  this.guildId = guildId;
4653
4656
  this.QueueSaver = QueueSaver2;
4654
4657
  this.options.maxPreviousTracks = this.QueueSaver?.options?.maxPreviousTracks ?? this.options.maxPreviousTracks;
@@ -4699,7 +4702,7 @@ var Queue = class {
4699
4702
  ))
4700
4703
  this.previous.splice(
4701
4704
  0,
4702
- override ? this.tracks.length : 0,
4705
+ override ? this.previous.length : 0,
4703
4706
  ...data.previous.filter(
4704
4707
  (track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)
4705
4708
  )
@@ -4714,8 +4717,8 @@ var Queue = class {
4714
4717
  * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
4715
4718
  */
4716
4719
  toJSON: () => {
4717
- if (this.previous.length > this.options.maxPreviousTracks)
4718
- this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
4720
+ if (this.previous?.length > this.options?.maxPreviousTracks)
4721
+ this.previous?.splice(this.options?.maxPreviousTracks, this.previous.length);
4719
4722
  return {
4720
4723
  current: this.current ? { ...this.current } : null,
4721
4724
  previous: this.previous ? [...this.previous] : [],
@@ -5531,6 +5534,17 @@ var Player = class {
5531
5534
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5532
5535
  return this;
5533
5536
  }
5537
+ /**
5538
+ * The old JSON of the player, used for the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
5539
+ */
5540
+ oldJSON = this.toJSON();
5541
+ /**
5542
+ * Emits the "playerClientUpdate" event, which is emitted on every update of the player via a function call, so that you can compare the old data with the new data and do something with it if you want to.
5543
+ */
5544
+ triggerPlayerClientUpdate() {
5545
+ this.LavalinkManager.emit("playerClientUpdate", this.oldJSON, this);
5546
+ this.oldJSON = this.toJSON();
5547
+ }
5534
5548
  /**
5535
5549
  * Set the Volume for the Player
5536
5550
  * @param volume The Volume in percent
@@ -5551,6 +5565,7 @@ var Player = class {
5551
5565
  0
5552
5566
  )
5553
5567
  );
5568
+ this.triggerPlayerClientUpdate();
5554
5569
  const now = performance.now();
5555
5570
  if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
5556
5571
  this._emitDebugEvent("PlayerVolumeAsFilter" /* PlayerVolumeAsFilter */, {
@@ -5625,6 +5640,7 @@ var Player = class {
5625
5640
  this.paused = true;
5626
5641
  this.lastPositionChange = null;
5627
5642
  const now = performance.now();
5643
+ this.triggerPlayerClientUpdate();
5628
5644
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
5629
5645
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5630
5646
  this.LavalinkManager.emit("playerPaused", this, this.queue.current);
@@ -5637,6 +5653,7 @@ var Player = class {
5637
5653
  if (!this.paused) throw new Error("Player isn't paused - not able to resume.");
5638
5654
  this.paused = false;
5639
5655
  const now = performance.now();
5656
+ this.triggerPlayerClientUpdate();
5640
5657
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
5641
5658
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5642
5659
  this.LavalinkManager.emit("playerResumed", this, this.queue.current);
@@ -5657,6 +5674,7 @@ var Player = class {
5657
5674
  this.lastPositionChange = Date.now();
5658
5675
  this.lastPosition = position;
5659
5676
  const now = performance.now();
5677
+ this.triggerPlayerClientUpdate();
5660
5678
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { position } });
5661
5679
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
5662
5680
  return this;
@@ -5669,6 +5687,7 @@ var Player = class {
5669
5687
  if (!["off", "track", "queue"].includes(repeatMode))
5670
5688
  throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
5671
5689
  this.repeatMode = repeatMode;
5690
+ this.triggerPlayerClientUpdate();
5672
5691
  return this;
5673
5692
  }
5674
5693
  /**
@@ -5923,7 +5942,7 @@ var Player = class {
5923
5942
  functionLayer: "Player > changeNode()"
5924
5943
  });
5925
5944
  });
5926
- } else {
5945
+ } else if (this.LavalinkManager.options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement !== false) {
5927
5946
  await this.setSponsorBlock().catch((error) => {
5928
5947
  this._emitDebugEvent("PlayerChangeNode" /* PlayerChangeNode */, {
5929
5948
  state: "error",
@@ -6019,7 +6038,7 @@ var Player = class {
6019
6038
  nodeId: this.node?.id,
6020
6039
  nodeSessionId: this.node?.sessionId,
6021
6040
  ping: this.ping,
6022
- queue: this.queue.utils.toJSON()
6041
+ queue: this.queue?.utils?.toJSON?.()
6023
6042
  };
6024
6043
  }
6025
6044
  };
@@ -6119,7 +6138,8 @@ var LavalinkManager = class extends EventEmitter2 {
6119
6138
  maxErrorsPerTime: {
6120
6139
  threshold: options?.playerOptions?.maxErrorsPerTime?.threshold ?? 35e3,
6121
6140
  maxAmount: options?.playerOptions?.maxErrorsPerTime?.maxAmount ?? 3
6122
- }
6141
+ },
6142
+ enforceSponsorBlockRequestForEventEnablement: options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement ?? true
6123
6143
  },
6124
6144
  linksWhitelist: options?.linksWhitelist ?? [],
6125
6145
  linksBlacklist: options?.linksBlacklist ?? [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.9.1",
3
+ "version": "2.9.2",
4
4
  "description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients. - Supports NodeLink@v3 too.",
5
5
  "keywords": [
6
6
  "advanced",