lavalink-client 2.3.1 → 2.3.4

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 (33) hide show
  1. package/README.md +35 -22
  2. package/dist/cjs/package.json +3 -0
  3. package/dist/cjs/structures/Constants.d.ts +44 -1
  4. package/dist/cjs/structures/Constants.js +45 -1
  5. package/dist/cjs/structures/LavalinkManager.js +80 -5
  6. package/dist/cjs/structures/Node.js +202 -20
  7. package/dist/cjs/structures/NodeManager.d.ts +1 -1
  8. package/dist/cjs/structures/NodeManager.js +2 -2
  9. package/dist/cjs/structures/Player.d.ts +0 -1
  10. package/dist/cjs/structures/Player.js +98 -8
  11. package/dist/cjs/structures/Queue.js +29 -2
  12. package/dist/cjs/structures/Types/Manager.d.ts +25 -0
  13. package/dist/cjs/structures/Types/Queue.d.ts +1 -1
  14. package/dist/cjs/structures/Utils.js +89 -23
  15. package/dist/esm/package.json +3 -0
  16. package/dist/esm/structures/Constants.d.ts +44 -1
  17. package/dist/esm/structures/Constants.js +44 -0
  18. package/dist/esm/structures/LavalinkManager.js +81 -6
  19. package/dist/esm/structures/Node.js +203 -21
  20. package/dist/esm/structures/NodeManager.d.ts +1 -1
  21. package/dist/esm/structures/NodeManager.js +1 -1
  22. package/dist/esm/structures/Player.d.ts +0 -1
  23. package/dist/esm/structures/Player.js +98 -8
  24. package/dist/esm/structures/Queue.js +29 -2
  25. package/dist/esm/structures/Types/Manager.d.ts +25 -0
  26. package/dist/esm/structures/Types/Queue.d.ts +1 -1
  27. package/dist/esm/structures/Utils.js +89 -23
  28. package/dist/types/structures/Constants.d.ts +44 -1
  29. package/dist/types/structures/NodeManager.d.ts +1 -1
  30. package/dist/types/structures/Player.d.ts +0 -1
  31. package/dist/types/structures/Types/Manager.d.ts +25 -0
  32. package/dist/types/structures/Types/Queue.d.ts +1 -1
  33. package/package.json +13 -6
@@ -286,11 +286,18 @@ export class Queue {
286
286
  * ```
287
287
  */
288
288
  async remove(removeQueryTrack) {
289
+ const oldStored = typeof this.queueChanges?.tracksRemoved === "function" ? this.utils.toJSON() : null;
289
290
  if (typeof removeQueryTrack === "number") {
290
291
  const toRemove = this.tracks[removeQueryTrack];
291
292
  if (!toRemove)
292
293
  return null;
293
294
  const removed = this.tracks.splice(removeQueryTrack, 1);
295
+ // Log if available
296
+ if (typeof this.queueChanges?.tracksRemoved === "function")
297
+ try {
298
+ this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
299
+ }
300
+ catch (e) { /* */ }
294
301
  await this.utils.save();
295
302
  return { removed };
296
303
  }
@@ -298,11 +305,18 @@ export class Queue {
298
305
  if (removeQueryTrack.every(v => typeof v === "number")) {
299
306
  const removed = [];
300
307
  for (const i of removeQueryTrack) {
301
- if (this.tracks[i])
308
+ if (this.tracks[i]) {
302
309
  removed.push(...this.tracks.splice(i, 1));
310
+ }
303
311
  }
304
312
  if (!removed.length)
305
313
  return null;
314
+ // Log if available
315
+ if (typeof this.queueChanges?.tracksRemoved === "function")
316
+ try {
317
+ this.queueChanges.tracksRemoved(this.guildId, removed, removeQueryTrack, oldStored, this.utils.toJSON());
318
+ }
319
+ catch (e) { /* */ }
306
320
  await this.utils.save();
307
321
  return { removed };
308
322
  }
@@ -317,9 +331,16 @@ export class Queue {
317
331
  return null;
318
332
  const removed = [];
319
333
  for (const { i } of tracksToRemove) {
320
- if (this.tracks[i])
334
+ if (this.tracks[i]) {
321
335
  removed.push(...this.tracks.splice(i, 1));
336
+ }
322
337
  }
338
+ // Log if available
339
+ if (typeof this.queueChanges?.tracksRemoved === "function")
340
+ try {
341
+ this.queueChanges.tracksRemoved(this.guildId, removed, tracksToRemove.map(v => v.i), oldStored, this.utils.toJSON());
342
+ }
343
+ catch (e) { /* */ }
323
344
  await this.utils.save();
324
345
  return { removed };
325
346
  }
@@ -332,6 +353,12 @@ export class Queue {
332
353
  if (toRemove < 0)
333
354
  return null;
334
355
  const removed = this.tracks.splice(toRemove, 1);
356
+ // Log if available
357
+ if (typeof this.queueChanges?.tracksRemoved === "function")
358
+ try {
359
+ this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
360
+ }
361
+ catch (e) { /* */ }
335
362
  await this.utils.save();
336
363
  return { removed };
337
364
  }
@@ -1,3 +1,4 @@
1
+ import type { DebugEvents } from "../Constants";
1
2
  import type { Player } from "../Player";
2
3
  import type { LavalinkNodeOptions } from "./Node";
3
4
  import type { DestroyReasonsType, PlayerJson } from "./Player";
@@ -100,6 +101,19 @@ export interface LavalinkManagerEvents {
100
101
  * @event Manager#trackError
101
102
  */
102
103
  "ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
104
+ /**
105
+ * Lavalink-Client Debug Event
106
+ * Emitted for several erros, and logs within lavalink-client, if managerOptions.advancedOptions.enableDebugEvents is true
107
+ * Useful for debugging the lavalink-client
108
+ *
109
+ * @event Manager#debug
110
+ */
111
+ "debug": (eventKey: DebugEvents, eventData: {
112
+ message: string;
113
+ state: "log" | "warn" | "error";
114
+ error?: Error | string;
115
+ functionLayer: string;
116
+ }) => void;
103
117
  }
104
118
  /**
105
119
  * The Bot client Options needed for the manager
@@ -131,6 +145,15 @@ export interface ManagerPlayerOptions {
131
145
  /** Instantly destroy player (overrides autoReconnect) | Don't provide == disable feature*/
132
146
  destroyPlayer?: boolean;
133
147
  };
148
+ /** Minimum time to play the song before autoPlayFunction is executed (prevents error spamming) Set to 0 to disable it @default 10000 */
149
+ minAutoPlayMs?: number;
150
+ /** Allows you to declare how many tracks are allowed to error/stuck within a time-frame before player is destroyed @default "{threshold: 35000, maxAmount: 3 }" */
151
+ maxErrorsPerTime?: {
152
+ /** The threshold time to count errors (recommended is 35s) */
153
+ threshold: number;
154
+ /** The max amount of errors within the threshold time which are allowed before destroying the player (when errors > maxAmount -> player.destroy()) */
155
+ maxAmount: number;
156
+ };
134
157
  onEmptyQueue?: {
135
158
  /** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
136
159
  autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
@@ -166,6 +189,8 @@ export interface ManagerOptions {
166
189
  advancedOptions?: {
167
190
  /** Max duration for that the filter fix duration works (in ms) - default is 8mins */
168
191
  maxFilterFixDuration?: number;
192
+ /** Enable Debug event */
193
+ enableDebugEvents?: boolean;
169
194
  /** optional */
170
195
  debugOptions?: {
171
196
  /** For logging custom searches */
@@ -28,7 +28,7 @@ export interface QueueChangesWatcher {
28
28
  /** get a Value (MUST RETURN UNPARSED!) */
29
29
  tracksAdd: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
30
30
  /** Set a value inside a guildId (MUST BE UNPARSED) */
31
- tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
31
+ tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number | number[], oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
32
32
  /** Set a value inside a guildId (MUST BE UNPARSED) */
33
33
  shuffled: (guildId: string, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
34
34
  }
@@ -1,5 +1,6 @@
1
1
  import { URL } from "node:url";
2
2
  import { isRegExp } from "node:util/types";
3
+ import { DebugEvents } from "./Constants";
3
4
  import { DefaultSources, LavalinkPlugins, SourceLinksRegexes } from "./LavalinkManagerStatics";
4
5
  export const TrackSymbol = Symbol("LC-Track");
5
6
  export const UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
@@ -71,6 +72,14 @@ export class ManagerUtils {
71
72
  return r;
72
73
  }
73
74
  catch (error) {
75
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
76
+ this.LavalinkManager?.emit("debug", DebugEvents.BuildTrackError, {
77
+ error: error,
78
+ functionLayer: "ManagerUtils > buildTrack()",
79
+ message: "Error while building track",
80
+ state: "error",
81
+ });
82
+ }
74
83
  throw new RangeError(`Argument "data" is not a valid track: ${error.message}`);
75
84
  }
76
85
  }
@@ -129,7 +138,14 @@ export class ManagerUtils {
129
138
  : requester;
130
139
  }
131
140
  catch (e) {
132
- console.error("errored while transforming requester:", e);
141
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
142
+ this.LavalinkManager?.emit("debug", DebugEvents.TransformRequesterFunctionFailed, {
143
+ error: e,
144
+ functionLayer: "ManagerUtils > getTransformedRequester()",
145
+ message: "Your custom transformRequesterFunction failed to execute, please check your function for errors.",
146
+ state: "error",
147
+ });
148
+ }
133
149
  return requester;
134
150
  }
135
151
  }
@@ -195,62 +211,96 @@ export class ManagerUtils {
195
211
  return typeof data === "object" && !("info" in data) && typeof data.title === "string";
196
212
  }
197
213
  async getClosestTrack(data, player) {
198
- return getClosestTrack(data, player);
214
+ try {
215
+ return getClosestTrack(data, player);
216
+ }
217
+ catch (e) {
218
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
219
+ this.LavalinkManager?.emit("debug", DebugEvents.GetClosestTrackFailed, {
220
+ error: e,
221
+ functionLayer: "ManagerUtils > getClosestTrack()",
222
+ message: "Failed to resolve track because the getClosestTrack function failed.",
223
+ state: "error",
224
+ });
225
+ }
226
+ throw e;
227
+ }
199
228
  }
200
229
  validateQueryString(node, queryString, sourceString) {
201
230
  if (!node.info)
202
231
  throw new Error("No Lavalink Node was provided");
203
232
  if (!node.info.sourceManagers?.length)
204
233
  throw new Error("Lavalink Node, has no sourceManagers enabled");
234
+ if (!queryString.trim().length)
235
+ throw new Error(`Query string is empty, please provide a valid query string.`);
205
236
  if (sourceString === "speak" && queryString.length > 100)
206
- // checks for blacklisted links / domains / queries
207
- if (this.LavalinkManager.options?.linksBlacklist?.length > 0 && this.LavalinkManager.options?.linksBlacklist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || isRegExp(v) && v.test(queryString))) {
237
+ throw new Error(`Query is speak, which is limited to 100 characters.`);
238
+ // checks for blacklisted links / domains / queries
239
+ if (this.LavalinkManager.options?.linksBlacklist?.length > 0) {
240
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
241
+ this.LavalinkManager.emit("debug", DebugEvents.ValidatingBlacklistLinks, {
242
+ state: "log",
243
+ message: `Validating Query against LavalinkManager.options.linksBlacklist, query: "${queryString}"`,
244
+ functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()",
245
+ });
246
+ }
247
+ if (this.LavalinkManager.options?.linksBlacklist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || isRegExp(v) && v.test(queryString))) {
208
248
  throw new Error(`Query string contains a link / word which is blacklisted.`);
209
249
  }
250
+ }
210
251
  if (!/^https?:\/\//.test(queryString))
211
252
  return;
212
253
  else if (this.LavalinkManager.options?.linksAllowed === false)
213
254
  throw new Error("Using links to make a request is not allowed.");
214
255
  // checks for if the query is whitelisted (should only work for links, so it skips the check for no link queries)
215
- if (this.LavalinkManager.options?.linksWhitelist?.length > 0 && !this.LavalinkManager.options?.linksWhitelist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || isRegExp(v) && v.test(queryString))) {
216
- throw new Error(`Query string contains a link / word which isn't whitelisted.`);
256
+ if (this.LavalinkManager.options?.linksWhitelist?.length > 0) {
257
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
258
+ this.LavalinkManager.emit("debug", DebugEvents.ValidatingWhitelistLinks, {
259
+ state: "log",
260
+ message: `Link was provided to the Query, validating against LavalinkManager.options.linksWhitelist, query: "${queryString}"`,
261
+ functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()",
262
+ });
263
+ }
264
+ if (!this.LavalinkManager.options?.linksWhitelist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || isRegExp(v) && v.test(queryString))) {
265
+ throw new Error(`Query string contains a link / word which isn't whitelisted.`);
266
+ }
217
267
  }
218
268
  // missing links: beam.pro local getyarn.io clypit pornhub reddit ocreamix soundgasm
219
269
  if ((SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
220
- throw new Error("Lavalink Node has not 'youtube' enabled");
270
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'youtube' enabled");
221
271
  }
222
272
  if ((SourceLinksRegexes.SoundCloudMobileRegex.test(queryString) || SourceLinksRegexes.SoundCloudRegex.test(queryString)) && !node.info?.sourceManagers?.includes("soundcloud")) {
223
- throw new Error("Lavalink Node has not 'soundcloud' enabled");
273
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'soundcloud' enabled");
224
274
  }
225
275
  if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
226
- throw new Error("Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
276
+ 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)");
227
277
  }
228
278
  if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
229
- throw new Error("Lavalink Node has not 'twitch' enabled");
279
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'twitch' enabled");
230
280
  }
231
281
  if (SourceLinksRegexes.vimeo.test(queryString) && !node.info?.sourceManagers?.includes("vimeo")) {
232
- throw new Error("Lavalink Node has not 'vimeo' enabled");
282
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'vimeo' enabled");
233
283
  }
234
284
  if (SourceLinksRegexes.tiktok.test(queryString) && !node.info?.sourceManagers?.includes("tiktok")) {
235
- throw new Error("Lavalink Node has not 'tiktok' enabled");
285
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tiktok' enabled");
236
286
  }
237
287
  if (SourceLinksRegexes.mixcloud.test(queryString) && !node.info?.sourceManagers?.includes("mixcloud")) {
238
- throw new Error("Lavalink Node has not 'mixcloud' enabled");
288
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'mixcloud' enabled");
239
289
  }
240
290
  if (SourceLinksRegexes.AllSpotifyRegex.test(queryString) && !node.info?.sourceManagers?.includes("spotify")) {
241
- throw new Error("Lavalink Node has not 'spotify' enabled");
291
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'spotify' enabled");
242
292
  }
243
293
  if (SourceLinksRegexes.appleMusic.test(queryString) && !node.info?.sourceManagers?.includes("applemusic")) {
244
- throw new Error("Lavalink Node has not 'applemusic' enabled");
294
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'applemusic' enabled");
245
295
  }
246
296
  if (SourceLinksRegexes.AllDeezerRegex.test(queryString) && !node.info?.sourceManagers?.includes("deezer")) {
247
- throw new Error("Lavalink Node has not 'deezer' enabled");
297
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'deezer' enabled");
248
298
  }
249
299
  if (SourceLinksRegexes.AllDeezerRegex.test(queryString) && node.info?.sourceManagers?.includes("deezer") && !node.info?.sourceManagers?.includes("http")) {
250
- throw new Error("Lavalink Node has not 'http' enabled, which is required to have 'deezer' to work");
300
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'http' enabled, which is required to have 'deezer' to work");
251
301
  }
252
302
  if (SourceLinksRegexes.musicYandex.test(queryString) && !node.info?.sourceManagers?.includes("yandexmusic")) {
253
- throw new Error("Lavalink Node has not 'yandexmusic' enabled");
303
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'yandexmusic' enabled");
254
304
  }
255
305
  return;
256
306
  }
@@ -371,11 +421,27 @@ export async function queueTrackEnd(player) {
371
421
  player.queue.tracks.push(player.queue.current);
372
422
  // change the current Track to the next upcoming one
373
423
  const nextSong = player.queue.tracks.shift();
374
- if (player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
375
- await nextSong.resolve(player);
376
- player.queue.current = nextSong || null;
377
- // save it in the DB
378
- await player.queue.utils.save();
424
+ try {
425
+ if (player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
426
+ await nextSong.resolve(player);
427
+ player.queue.current = nextSong || null;
428
+ // save it in the DB
429
+ await player.queue.utils.save();
430
+ }
431
+ catch (error) {
432
+ if (player.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
433
+ player.LavalinkManager.emit("debug", DebugEvents.PlayerPlayUnresolvedTrackFailed, {
434
+ state: "error",
435
+ error: error,
436
+ message: `queueTrackEnd Util was called, tried to resolve the next track, but failed to find the closest matching song`,
437
+ functionLayer: "Player > play() > resolve currentTrack",
438
+ });
439
+ }
440
+ player.LavalinkManager.emit("trackError", player, player.queue.current, error);
441
+ // try to play the next track if possible
442
+ if (player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
443
+ return queueTrackEnd(player);
444
+ }
379
445
  // return the new current Track
380
446
  return player.queue.current;
381
447
  }
@@ -1,4 +1,45 @@
1
1
  import type { AudioOutputs, ChannelMixFilter, EQBand } from "./Types/Filters";
2
+ export declare enum DebugEvents {
3
+ SetSponsorBlock = "SetSponsorBlock",
4
+ DeleteSponsorBlock = "DeleteSponsorBlock",
5
+ TrackEndReplaced = "TrackEndReplaced",
6
+ AutoplayNoSongsAdded = "AutoplayNoSongsAdded",
7
+ AutoplayThresholdSpamLimiter = "AutoplayThresholdSpamLimiter",
8
+ TriggerQueueEmptyInterval = "TriggerQueueEmptyInterval",
9
+ QueueEnded = "QueueEnded",
10
+ TrackStartNewSongsOnly = "TrackStartNewSongsOnly",
11
+ TrackStartNoTrack = "TrackStartNoTrack",
12
+ ResumingFetchingError = "ResumingFetchingError",
13
+ PlayerUpdateNoPlayer = "PlayerUpdateNoPlayer",
14
+ PlayerUpdateFilterFixApply = "PlayerUpdateFilterFixApply",
15
+ PlayerUpdateSuccess = "PlayerUpdateSuccess",
16
+ HeartBeatTriggered = "HeartBeatTriggered",
17
+ NoSocketOnDestroy = "NoSocketOnDestroy",
18
+ SocketTerminateHeartBeatTimeout = "SocketTerminateHeartBeatTimeout",
19
+ TryingConnectWhileConnected = "TryingConnectWhileConnected",
20
+ LavaSearchNothingFound = "LavaSearchNothingFound",
21
+ SearchNothingFound = "SearchNothingFound",
22
+ ValidatingBlacklistLinks = "ValidatingBlacklistLinks",
23
+ ValidatingWhitelistLinks = "ValidatingWhitelistLinks",
24
+ TrackErrorMaxTracksErroredPerTime = "TrackErrorMaxTracksErroredPerTime",
25
+ TrackStuckMaxTracksErroredPerTime = "TrackStuckMaxTracksErroredPerTime",
26
+ PlayerDestroyingSomewhereElse = "PlayerDestroyingSomewhereElse",
27
+ PlayerCreateNodeNotFound = "PlayerCreateNodeNotFound",
28
+ PlayerPlayQueueEmptyTimeoutClear = "PlayerPlayQueueEmptyTimeoutClear",
29
+ PlayerPlayWithTrackReplace = "PlayerPlayWithTrackReplace",
30
+ PlayerPlayUnresolvedTrack = "PlayerPlayUnresolvedTrack",
31
+ PlayerPlayUnresolvedTrackFailed = "PlayerPlayUnresolvedTrackFailed",
32
+ PlayerVolumeAsFilter = "PlayerVolumeAsFilter",
33
+ BandcampSearchLokalEngine = "BandcampSearchLokalEngine",
34
+ PlayerChangeNode = "PlayerChangeNode",
35
+ BuildTrackError = "BuildTrackError",
36
+ TransformRequesterFunctionFailed = "TransformRequesterFunctionFailed",
37
+ GetClosestTrackFailed = "GetClosestTrackFailed",
38
+ PlayerDeleteInsteadOfDestroy = "PlayerDeleteInsteadOfDestroy",
39
+ FailedToConnectToNodes = "FailedToConnectToNodes",
40
+ NoAudioDebug = "NoAudioDebug",
41
+ PlayerAutoReconnect = "PlayerAutoReconnect"
42
+ }
2
43
  export declare enum DestroyReasons {
3
44
  QueueEmpty = "QueueEmpty",
4
45
  NodeDestroy = "NodeDestroy",
@@ -9,7 +50,9 @@ export declare enum DestroyReasons {
9
50
  PlayerReconnectFail = "PlayerReconnectFail",
10
51
  ChannelDeleted = "ChannelDeleted",
11
52
  DisconnectAllNodes = "DisconnectAllNodes",
12
- ReconnectAllNodes = "ReconnectAllNodes"
53
+ ReconnectAllNodes = "ReconnectAllNodes",
54
+ TrackErrorMaxTracksErroredPerTime = "TrackErrorMaxTracksErroredPerTime",
55
+ TrackStuckMaxTracksErroredPerTime = "TrackStuckMaxTracksErroredPerTime"
13
56
  }
14
57
  export declare const validSponsorBlocks: string[];
15
58
  /** The audio Outputs Data map declaration */
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { EventEmitter } from "stream";
2
+ import { EventEmitter } from "events";
3
3
  import { LavalinkNode } from "./Node";
4
4
  import { MiniMap } from "./Utils";
5
5
  import type { LavalinkNodeIdentifier, LavalinkNodeOptions, NodeManagerEvents } from "./Types/Node";
@@ -134,7 +134,6 @@ export declare class Player {
134
134
  * @param repeatMode
135
135
  */
136
136
  setRepeatMode(repeatMode: RepeatMode): Promise<this>;
137
- 1: any;
138
137
  /**
139
138
  * Skip the current song, or a specific amount of songs
140
139
  * @param amount provide the index of the next track to skip to
@@ -1,3 +1,4 @@
1
+ import type { DebugEvents } from "../Constants";
1
2
  import type { Player } from "../Player";
2
3
  import type { LavalinkNodeOptions } from "./Node";
3
4
  import type { DestroyReasonsType, PlayerJson } from "./Player";
@@ -100,6 +101,19 @@ export interface LavalinkManagerEvents {
100
101
  * @event Manager#trackError
101
102
  */
102
103
  "ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
104
+ /**
105
+ * Lavalink-Client Debug Event
106
+ * Emitted for several erros, and logs within lavalink-client, if managerOptions.advancedOptions.enableDebugEvents is true
107
+ * Useful for debugging the lavalink-client
108
+ *
109
+ * @event Manager#debug
110
+ */
111
+ "debug": (eventKey: DebugEvents, eventData: {
112
+ message: string;
113
+ state: "log" | "warn" | "error";
114
+ error?: Error | string;
115
+ functionLayer: string;
116
+ }) => void;
103
117
  }
104
118
  /**
105
119
  * The Bot client Options needed for the manager
@@ -131,6 +145,15 @@ export interface ManagerPlayerOptions {
131
145
  /** Instantly destroy player (overrides autoReconnect) | Don't provide == disable feature*/
132
146
  destroyPlayer?: boolean;
133
147
  };
148
+ /** Minimum time to play the song before autoPlayFunction is executed (prevents error spamming) Set to 0 to disable it @default 10000 */
149
+ minAutoPlayMs?: number;
150
+ /** Allows you to declare how many tracks are allowed to error/stuck within a time-frame before player is destroyed @default "{threshold: 35000, maxAmount: 3 }" */
151
+ maxErrorsPerTime?: {
152
+ /** The threshold time to count errors (recommended is 35s) */
153
+ threshold: number;
154
+ /** The max amount of errors within the threshold time which are allowed before destroying the player (when errors > maxAmount -> player.destroy()) */
155
+ maxAmount: number;
156
+ };
134
157
  onEmptyQueue?: {
135
158
  /** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
136
159
  autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
@@ -166,6 +189,8 @@ export interface ManagerOptions {
166
189
  advancedOptions?: {
167
190
  /** Max duration for that the filter fix duration works (in ms) - default is 8mins */
168
191
  maxFilterFixDuration?: number;
192
+ /** Enable Debug event */
193
+ enableDebugEvents?: boolean;
169
194
  /** optional */
170
195
  debugOptions?: {
171
196
  /** For logging custom searches */
@@ -28,7 +28,7 @@ export interface QueueChangesWatcher {
28
28
  /** get a Value (MUST RETURN UNPARSED!) */
29
29
  tracksAdd: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
30
30
  /** Set a value inside a guildId (MUST BE UNPARSED) */
31
- tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
31
+ tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number | number[], oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
32
32
  /** Set a value inside a guildId (MUST BE UNPARSED) */
33
33
  shuffled: (guildId: string, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
34
34
  }
package/package.json CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.3.1",
3
+ "version": "2.3.4",
4
4
  "description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/types/index.d.js",
8
8
  "scripts": {
9
9
  "build": "npm run build:cjs && npm run build:esm && npm run build:types",
10
- "build:cjs": "node tools/cleanup cjs && tsc -p config/tsconfig.cjs.json",
11
- "build:esm": "node tools/cleanup esm && tsc -p config/tsconfig.esm.json",
12
- "build:types": "node tools/cleanup types && tsc -p config/tsconfig.types.json",
10
+ "build:cjs": "node tools/cleanup cjs && tsc -p config/tsconfig.cjs.json && tsc-alias -p config/tsconfig.cjs.json && node tools/fixup cjs",
11
+ "build:esm": "node tools/cleanup esm && tsc -p config/tsconfig.esm.json && tsc-alias -p config/tsconfig.esm.json && node tools/fixup esm",
12
+ "build:types": "node tools/cleanup types && tsc -p config/tsconfig.types.json && tsc-alias -p config/tsconfig.types.json",
13
13
  "clean": "node tools/cleanup",
14
14
  "lint": "eslint .",
15
15
  "lint:fix": "npm run lint -- --fix",
16
16
  "test": "node -v",
17
17
  "docs": "npx typedoc"
18
18
  },
19
+ "exports": {
20
+ "require": "./dist/cjs/index.js",
21
+ "import": "./dist/esm/index.js",
22
+ "types": "./dist/types/index.d.js",
23
+ "default": "./dist/cjs/index.js"
24
+ },
19
25
  "publishConfig": {
20
26
  "access": "public"
21
27
  },
@@ -52,7 +58,7 @@
52
58
  "@typescript-eslint/eslint-plugin": "^6.4.0",
53
59
  "@typescript-eslint/parser": "^6.4.0",
54
60
  "eslint": "^8.47.0",
55
- "ts-loader": "^9.4.4",
61
+ "tsc-alias": "^1.8.10",
56
62
  "typedoc": "^0.25.4",
57
63
  "typedoc-theme-hierarchy": "^4.1.2",
58
64
  "typescript": "^5.1.6"
@@ -62,6 +68,7 @@
62
68
  "ws": "^8.13.0"
63
69
  },
64
70
  "engines": {
65
- "node": ">=18.0.0"
71
+ "node": ">=18.0.0",
72
+ "bun": ">=1.0.0"
66
73
  }
67
74
  }