lavalink-client 2.1.6 → 2.2.0

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 (31) hide show
  1. package/README.md +11 -0
  2. package/dist/cjs/structures/CustomSearches/BandCampSearch.js +3 -2
  3. package/dist/cjs/structures/LavalinkManager.d.ts +5 -4
  4. package/dist/cjs/structures/LavalinkManager.js +1 -0
  5. package/dist/cjs/structures/LavalinkManagerStatics.js +5 -1
  6. package/dist/cjs/structures/Node.d.ts +10 -13
  7. package/dist/cjs/structures/Node.js +55 -56
  8. package/dist/cjs/structures/NodeManager.d.ts +1 -1
  9. package/dist/cjs/structures/Player.js +10 -10
  10. package/dist/cjs/structures/Queue.js +1 -1
  11. package/dist/cjs/structures/Track.d.ts +2 -0
  12. package/dist/cjs/structures/Utils.d.ts +12 -7
  13. package/dist/cjs/structures/Utils.js +6 -4
  14. package/dist/esm/structures/CustomSearches/BandCampSearch.js +2 -1
  15. package/dist/esm/structures/LavalinkManager.d.ts +5 -4
  16. package/dist/esm/structures/LavalinkManager.js +1 -0
  17. package/dist/esm/structures/LavalinkManagerStatics.js +5 -1
  18. package/dist/esm/structures/Node.d.ts +10 -13
  19. package/dist/esm/structures/Node.js +55 -56
  20. package/dist/esm/structures/NodeManager.d.ts +1 -1
  21. package/dist/esm/structures/Player.js +10 -10
  22. package/dist/esm/structures/Queue.js +1 -1
  23. package/dist/esm/structures/Track.d.ts +2 -0
  24. package/dist/esm/structures/Utils.d.ts +12 -7
  25. package/dist/esm/structures/Utils.js +6 -4
  26. package/dist/types/structures/LavalinkManager.d.ts +5 -4
  27. package/dist/types/structures/Node.d.ts +10 -13
  28. package/dist/types/structures/NodeManager.d.ts +1 -1
  29. package/dist/types/structures/Track.d.ts +2 -0
  30. package/dist/types/structures/Utils.d.ts +12 -7
  31. package/package.json +4 -2
@@ -47,10 +47,14 @@ export const DefaultSources = {
47
47
  "flowery": "ftts",
48
48
  "flowery.tts": "ftts",
49
49
  "flowerytts": "ftts",
50
- // Client sided search platforms
50
+ // Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
51
51
  "bandcamp": "bcsearch",
52
52
  "bc": "bcsearch",
53
53
  "bcsearch": "bcsearch",
54
+ // other searches:
55
+ "phsearch": "phsearch",
56
+ "pornhub": "phsearch",
57
+ "porn": "phsearch",
54
58
  // local files
55
59
  "local": "local",
56
60
  // http requests
@@ -1,12 +1,13 @@
1
1
  /// <reference types="node" />
2
2
  import internal from "stream";
3
- import { Dispatcher, Pool } from "undici";
4
3
  import { NodeManager } from "./NodeManager";
5
4
  import { DestroyReasonsType, Player } from "./Player";
6
5
  import { Track } from "./Track";
7
6
  import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
8
- /** Modifies any outgoing REST requests. */
9
- export type ModifyRequest = (options: Dispatcher.RequestOptions) => void;
7
+ /** Ability to manipulate fetch requests */
8
+ export type ModifyRequest = (options: RequestInit & {
9
+ path: string;
10
+ }) => void;
10
11
  export declare const validSponsorBlocks: string[];
11
12
  export type SponsorBlockSegment = "sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler";
12
13
  export interface LavalinkNodeOptions {
@@ -24,14 +25,12 @@ export interface LavalinkNodeOptions {
24
25
  id?: string;
25
26
  /** Voice Regions of this Node */
26
27
  regions?: string[];
27
- /** Options for the undici http pool used for http requests */
28
- poolOptions?: Pool.Options;
29
28
  /** The retryAmount for the node. */
30
29
  retryAmount?: number;
31
30
  /** The retryDelay for the node. */
32
31
  retryDelay?: number;
33
- /** Pool Undici Options - requestTimeout */
34
- requestTimeout?: number;
32
+ /** signal for cancelling requests - default: AbortSignal.timeout(options.requestSignalTimeoutMS || 10000) - put <= 0 to disable */
33
+ requestSignalTimeoutMS?: number;
35
34
  }
36
35
  export interface MemoryStats {
37
36
  /** The free memory of the allocated amount. */
@@ -145,8 +144,6 @@ export declare class LavalinkNode {
145
144
  private reconnectAttempts;
146
145
  /** The Socket of the Lavalink */
147
146
  private socket;
148
- /** The Rest Server for this Lavalink */
149
- private rest;
150
147
  /** Version of what the Lavalink Server should be */
151
148
  private version;
152
149
  /**
@@ -168,7 +165,7 @@ export declare class LavalinkNode {
168
165
  * @param modify Used to modify the request before being sent
169
166
  * @returns The returned data
170
167
  */
171
- request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<unknown>;
168
+ request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<any>;
172
169
  /**
173
170
  * Search something raw on the node, please note only add tracks to players of that node
174
171
  * @param query SearchQuery Object
@@ -188,7 +185,7 @@ export declare class LavalinkNode {
188
185
  * @param guildId
189
186
  * @returns
190
187
  */
191
- destroyPlayer(guildId: any): Promise<unknown>;
188
+ destroyPlayer(guildId: any): Promise<any>;
192
189
  /**
193
190
  * Connect to the Lavalink Node
194
191
  * @param sessionId Provide the Session Id of the previous connection, to resume the node and it's player(s)
@@ -266,12 +263,12 @@ export declare class LavalinkNode {
266
263
  /**
267
264
  * Release all blacklisted IP addresses into pool of IPs
268
265
  */
269
- unmarkAllFailedAddresses: () => Promise<unknown>;
266
+ unmarkAllFailedAddresses: () => Promise<any>;
270
267
  };
271
268
  /** Private Utils */
272
269
  private validate;
273
270
  private syncPlayerData;
274
- private get poolAddress();
271
+ private get restAddress();
275
272
  private reconnect;
276
273
  private open;
277
274
  private close;
@@ -1,5 +1,4 @@
1
1
  import { isAbsolute } from "path";
2
- import { Pool } from "undici";
3
2
  import WebSocket from "ws";
4
3
  import { DestroyReasons } from "./Player";
5
4
  import { NodeSymbol, queueTrackEnd } from "./Utils";
@@ -43,8 +42,6 @@ export class LavalinkNode {
43
42
  reconnectAttempts = 1;
44
43
  /** The Socket of the Lavalink */
45
44
  socket = null;
46
- /** The Rest Server for this Lavalink */
47
- rest;
48
45
  /** Version of what the Lavalink Server should be */
49
46
  version = "v4";
50
47
  /**
@@ -57,14 +54,13 @@ export class LavalinkNode {
57
54
  secure: false,
58
55
  retryAmount: 5,
59
56
  retryDelay: 30e3,
60
- requestTimeout: 10e3,
57
+ requestSignalTimeoutMS: 10000,
61
58
  ...options
62
59
  };
63
60
  this.NodeManager = manager;
64
61
  this.validate();
65
62
  if (this.options.secure && this.options.port !== 443)
66
63
  throw new SyntaxError("If secure is true, then the port must be 443");
67
- this.rest = new Pool(this.poolAddress, this.options.poolOptions);
68
64
  this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
69
65
  Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
70
66
  }
@@ -79,15 +75,15 @@ export class LavalinkNode {
79
75
  path: `/${this.version}/${endpoint.replace(/^\//gm, "")}`,
80
76
  method: "GET",
81
77
  headers: {
82
- Authorization: this.options.authorization
78
+ "Authorization": this.options.authorization
83
79
  },
84
- headersTimeout: this.options.requestTimeout,
80
+ signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : undefined,
85
81
  };
86
82
  modify?.(options);
87
- const url = new URL(`${this.poolAddress}${options.path}`);
83
+ const url = new URL(`${this.restAddress}${options.path}`);
88
84
  url.searchParams.append("trace", "true");
89
- options.path = url.pathname + url.search;
90
- const request = await this.rest.request(options);
85
+ delete options.path;
86
+ const request = await fetch(url.href, options);
91
87
  this.calls++;
92
88
  return { request, options };
93
89
  }
@@ -101,9 +97,9 @@ export class LavalinkNode {
101
97
  const { request, options } = await this.rawRequest(endpoint, modify);
102
98
  if (["DELETE", "PUT"].includes(options.method))
103
99
  return;
104
- if (request.statusCode === 404)
100
+ if (request.status === 404)
105
101
  throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(request.headers)}`);
106
- return parseAsText ? await request.body.text() : await request.body.json();
102
+ return parseAsText ? await request.text() : await request.json();
107
103
  }
108
104
  /**
109
105
  * Search something raw on the node, please note only add tracks to players of that node
@@ -116,8 +112,8 @@ export class LavalinkNode {
116
112
  this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query, Query.source);
117
113
  if (Query.source)
118
114
  this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
119
- if (["bcsearch", "bandcamp"].includes(Query.source)) {
120
- throw new Error("Bandcamp Search only works on the player!");
115
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.info.sourceManagers.includes("bandcamp")) {
116
+ throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
121
117
  }
122
118
  let uri = `/loadtracks?identifier=`;
123
119
  if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
@@ -139,12 +135,13 @@ export class LavalinkNode {
139
135
  exception: res.loadType === "error" ? res.data : null,
140
136
  pluginInfo: res.pluginInfo || {},
141
137
  playlist: res.loadType === "playlist" ? {
138
+ name: res.data.info?.name || res.data.pluginInfo?.name || null,
142
139
  title: res.data.info?.name || res.data.pluginInfo?.name || null,
143
140
  author: res.data.info?.author || res.data.pluginInfo?.author || null,
144
141
  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,
145
142
  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,
146
143
  selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this.NodeManager.LavalinkManager.utils.buildTrack(resTracks[res.data?.info?.selectedTrack], requestUser) : null,
147
- duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || 0), 0) : 0,
144
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.length || 0), 0) : 0,
148
145
  } : null,
149
146
  tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
150
147
  };
@@ -164,7 +161,7 @@ export class LavalinkNode {
164
161
  const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
165
162
  if (throwOnEmpty === true)
166
163
  throw new Error("Nothing found");
167
- const res = (request.statusCode === 204 ? {} : await request.body.json());
164
+ const res = (request.status === 204 ? {} : await request.json());
168
165
  return {
169
166
  tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
170
167
  albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
@@ -189,7 +186,7 @@ export class LavalinkNode {
189
186
  r.headers["Content-Type"] = "application/json";
190
187
  r.body = JSON.stringify(data.playerOptions);
191
188
  if (data.noReplace) {
192
- const url = new URL(`${this.poolAddress}${r.path}`);
189
+ const url = new URL(`${this.restAddress}${r.path}`);
193
190
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
194
191
  r.path = url.pathname + url.search;
195
192
  }
@@ -470,7 +467,7 @@ export class LavalinkNode {
470
467
  }
471
468
  return true;
472
469
  }
473
- get poolAddress() {
470
+ get restAddress() {
474
471
  return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
475
472
  }
476
473
  reconnect(instaReconnect = false) {
@@ -505,9 +502,9 @@ export class LavalinkNode {
505
502
  clearTimeout(this.reconnectTimeout);
506
503
  // reset the reconnect attempts amount
507
504
  this.reconnectAttempts = 1;
508
- this.info = await this.fetchInfo().catch(() => null);
505
+ this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
509
506
  if (!this.info && ["v3", "v4"].includes(this.version)) {
510
- const errorString = `Lavalink Node (${this.poolAddress}) does not provide any /${this.version}/info`;
507
+ const errorString = `Lavalink Node (${this.restAddress}) does not provide any /${this.version}/info`;
511
508
  throw new Error(errorString);
512
509
  }
513
510
  this.NodeManager.emit("connect", this);
@@ -537,48 +534,50 @@ export class LavalinkNode {
537
534
  this.stats = { ...payload };
538
535
  break;
539
536
  case "playerUpdate":
540
- const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
541
- if (!player)
542
- return;
543
- const oldPlayer = player?.toJSON();
544
- if (player.get("internal_updateInterval"))
545
- clearInterval(player.get("internal_updateInterval"));
546
- // override the position
547
- player.position = payload.state.position || 0;
548
- player.lastPosition = payload.state.position || 0;
549
- player.connected = payload.state.connected;
550
- player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
551
- if (!player.createdTimeStamp && payload.state.time)
552
- player.createdTimeStamp = payload.state.time;
553
- if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
554
- player.set("internal_updateInterval", setInterval(() => {
555
- player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
556
- if (player.filterManager.filterUpdatedState >= 1) {
557
- player.filterManager.filterUpdatedState++;
558
- const maxMins = 8;
559
- const currentDuration = player.queue.current?.info?.duration || 0;
560
- if (currentDuration <= maxMins * 6e4 || isAbsolute(player.queue.current?.info?.uri)) {
561
- if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
537
+ {
538
+ const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
539
+ if (!player)
540
+ return;
541
+ const oldPlayer = player?.toJSON();
542
+ if (player.get("internal_updateInterval"))
543
+ clearInterval(player.get("internal_updateInterval"));
544
+ // override the position
545
+ player.position = payload.state.position || 0;
546
+ player.lastPosition = payload.state.position || 0;
547
+ player.connected = payload.state.connected;
548
+ player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
549
+ if (!player.createdTimeStamp && payload.state.time)
550
+ player.createdTimeStamp = payload.state.time;
551
+ if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
552
+ player.set("internal_updateInterval", setInterval(() => {
553
+ player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
554
+ if (player.filterManager.filterUpdatedState >= 1) {
555
+ player.filterManager.filterUpdatedState++;
556
+ const maxMins = 8;
557
+ const currentDuration = player.queue.current?.info?.duration || 0;
558
+ if (currentDuration <= maxMins * 6e4 || isAbsolute(player.queue.current?.info?.uri)) {
559
+ if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
560
+ player.filterManager.filterUpdatedState = 0;
561
+ player.seek(player.position);
562
+ }
563
+ }
564
+ else {
562
565
  player.filterManager.filterUpdatedState = 0;
563
- player.seek(player.position);
564
566
  }
565
567
  }
566
- else {
567
- player.filterManager.filterUpdatedState = 0;
568
- }
568
+ }, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
569
+ }
570
+ else {
571
+ if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
572
+ const maxMins = 8;
573
+ const currentDuration = player.queue.current?.info?.duration || 0;
574
+ if (currentDuration <= maxMins * 6e4 || isAbsolute(player.queue.current?.info?.uri))
575
+ player.seek(player.position);
576
+ player.filterManager.filterUpdatedState = 0;
569
577
  }
570
- }, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
571
- }
572
- else {
573
- if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
574
- const maxMins = 8;
575
- const currentDuration = player.queue.current?.info?.duration || 0;
576
- if (currentDuration <= maxMins * 6e4 || isAbsolute(player.queue.current?.info?.uri))
577
- player.seek(player.position);
578
- player.filterManager.filterUpdatedState = 0;
579
578
  }
579
+ this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
580
580
  }
581
- this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
582
581
  break;
583
582
  case "event":
584
583
  this.handleEvent(payload);
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
5
5
  import { DestroyReasonsType } from "./Player";
6
6
  import { LavalinkPlayer, MiniMap } from "./Utils";
7
7
  type LavalinkNodeIdentifier = string;
8
- interface NodeManagerEvents {
8
+ export interface NodeManagerEvents {
9
9
  /**
10
10
  * Emitted when a Node is created.
11
11
  * @event Manager.nodeManager#create
@@ -129,17 +129,17 @@ export class Player {
129
129
  if (options?.clientTrack && (this.LavalinkManager.utils.isTrack(options?.clientTrack) || this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
130
130
  if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))
131
131
  await options.clientTrack.resolve(this);
132
- if (typeof options.track.userData === "object")
133
- options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track.userData || {}) };
132
+ if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
133
+ options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
134
134
  await this.queue.add(options?.clientTrack, 0);
135
135
  return await this.skip();
136
136
  }
137
137
  else if (options?.track?.encoded) {
138
138
  // handle play encoded options manually // TODO let it resolve by lavalink!
139
139
  const track = await this.node.decode.singleTrack(options.track?.encoded, options.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
140
- if (typeof options.track.userData === "object")
141
- track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
142
140
  if (track) {
141
+ if (typeof options.track?.userData === "object")
142
+ track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
143
143
  replaced = true;
144
144
  this.queue.add(track, 0);
145
145
  await queueTrackEnd(this);
@@ -149,10 +149,10 @@ export class Player {
149
149
  // handle play identifier options manually // TODO let it resolve by lavalink!
150
150
  const res = await this.search({
151
151
  query: options?.track?.identifier
152
- }, options?.track?.identifier || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
153
- if (typeof options.track.userData === "object")
154
- res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
152
+ }, options?.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
155
153
  if (res.tracks[0]) {
154
+ if (typeof options.track?.userData === "object")
155
+ res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
156
156
  replaced = true;
157
157
  this.queue.add(res.tracks[0], 0);
158
158
  await queueTrackEnd(this);
@@ -164,8 +164,8 @@ export class Player {
164
164
  try {
165
165
  // resolve the unresolved track
166
166
  await this.queue.current.resolve(this);
167
- if (typeof options.track.userData === "object")
168
- this.queue.current.userData = { ...(this.queue.current.userData || {}), ...(options.track.userData || {}) };
167
+ if (typeof options.track?.userData === "object" && this.queue.current)
168
+ this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
169
169
  }
170
170
  catch (error) {
171
171
  this.LavalinkManager.emit("trackError", this, this.queue.current, error);
@@ -261,7 +261,7 @@ export class Player {
261
261
  */
262
262
  async search(query, requestUser) {
263
263
  const Query = this.LavalinkManager.utils.transformQuery(query);
264
- if (["bcsearch", "bandcamp"].includes(Query.source))
264
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
265
265
  return await bandCampSearch(this, Query.query, requestUser);
266
266
  return this.node.search(Query, requestUser);
267
267
  }
@@ -76,7 +76,7 @@ export class Queue {
76
76
  sync: async (override = true, dontSyncCurrent = true) => {
77
77
  const data = await this.QueueSaver.get(this.guildId);
78
78
  if (!data)
79
- return console.log("No data found to sync for guildId: ", this.guildId);
79
+ throw new Error(`No data found to sync for guildId: ${this.guildId}`);
80
80
  if (!dontSyncCurrent && !this.current && (this.managerUtils.isTrack(data.current)))
81
81
  this.current = data.current;
82
82
  if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(track => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
@@ -55,6 +55,8 @@ export interface PluginInfo {
55
55
  type?: "album" | "playlist" | "artist" | "recommendations" | string;
56
56
  /** The Identifier provided by a plugin */
57
57
  albumName?: string;
58
+ /** The url of the album */
59
+ albumUrl?: string;
58
60
  /** The url of the album art */
59
61
  albumArtUrl?: string;
60
62
  /** The url of the artist */
@@ -14,23 +14,23 @@ export type IntegerNumber = Opaque<number, 'Int'>;
14
14
  export type FloatNumber = Opaque<number, 'Float'>;
15
15
  export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
16
16
  export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
17
- export type DuncteSearchPlatform = "speak" | "tts";
17
+ export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
18
18
  export type LavalinkClientSearchPlatform = "bcsearch";
19
19
  export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
20
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
20
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
21
21
  export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
22
22
  export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
23
23
  "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
24
24
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
25
25
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
26
26
  export interface PlaylistInfo {
27
- /** The playlist title. */
28
- title: string;
29
- /** The playlist name (if provided instead of title) */
27
+ /** The playlist name */
30
28
  name: string;
31
- /** The Playlist Author */
29
+ /** The playlist title (same as name) */
30
+ title: string;
31
+ /** The playlist Author */
32
32
  author?: string;
33
- /** The Playlist Thumbnail */
33
+ /** The playlist Thumbnail */
34
34
  thumbnail?: string;
35
35
  /** A Uri to the playlist */
36
36
  uri?: string;
@@ -168,8 +168,13 @@ export declare class MiniMap<K, V> extends Map<K, V> {
168
168
  export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;
169
169
  export type Severity = "COMMON" | "SUSPICIOUS" | "FAULT";
170
170
  export interface Exception {
171
+ /** Severity of the error */
171
172
  severity: Severity;
173
+ /** Nodejs Error */
174
+ error?: Error;
175
+ /** Message by lavalink */
172
176
  message: string;
177
+ /** Cause by lavalink */
173
178
  cause: string;
174
179
  }
175
180
  export interface PlayerEvent {
@@ -46,7 +46,7 @@ export class ManagerUtils {
46
46
  identifier: data.info.identifier,
47
47
  title: data.info.title,
48
48
  author: data.info.author,
49
- duration: data.info.length || data.info.duration,
49
+ duration: data.info.duration || data.info.length,
50
50
  artworkUrl: data.info.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
51
51
  uri: data.info.uri,
52
52
  sourceName: data.info.sourceName,
@@ -202,7 +202,7 @@ export class ManagerUtils {
202
202
  throw new Error("Lavalink Node has not 'soundcloud' enabled");
203
203
  }
204
204
  if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
205
- throw new Error("Lavalink Node has not 'bandcamp' enabled");
205
+ throw new Error("Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
206
206
  }
207
207
  if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
208
208
  throw new Error("Lavalink Node has not 'twitch' enabled");
@@ -234,9 +234,10 @@ export class ManagerUtils {
234
234
  return;
235
235
  }
236
236
  transformQuery(query) {
237
+ const sourceOfQuery = typeof query === "string" ? undefined : (DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
237
238
  const Query = {
238
239
  query: typeof query === "string" ? query : query.query,
239
- source: DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
240
+ source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
240
241
  };
241
242
  const foundSource = Object.keys(DefaultSources).find(source => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
242
243
  // ignore links...
@@ -248,10 +249,11 @@ export class ManagerUtils {
248
249
  }
249
250
  transformLavaSearchQuery(query) {
250
251
  // transform the query object
252
+ const sourceOfQuery = typeof query === "string" ? undefined : (DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
251
253
  const Query = {
252
254
  query: typeof query === "string" ? query : query.query,
253
255
  types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(v => query.types?.find(x => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album", /*"text"*/],
254
- source: DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
256
+ source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
255
257
  };
256
258
  const foundSource = Object.keys(DefaultSources).find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
257
259
  if (foundSource && DefaultSources[foundSource]) {
@@ -66,6 +66,8 @@ export interface ManagerOptions {
66
66
  advancedOptions?: {
67
67
  /** optional */
68
68
  debugOptions?: {
69
+ /** For logging custom searches */
70
+ logCustomSearches?: boolean;
69
71
  /** logs for debugging the "no-Audio" playing error */
70
72
  noAudio?: boolean;
71
73
  /** For Logging the Destroy function */
@@ -78,7 +80,7 @@ export interface ManagerOptions {
78
80
  };
79
81
  };
80
82
  }
81
- interface LavalinkManagerEvents {
83
+ export interface LavalinkManagerEvents {
82
84
  /**
83
85
  * Emitted when a Track started playing.
84
86
  * @event Manager#trackStart
@@ -93,12 +95,12 @@ interface LavalinkManagerEvents {
93
95
  * Emitted when a Track got stuck while playing.
94
96
  * @event Manager#trackStuck
95
97
  */
96
- "trackStuck": (player: Player, track: Track, payload: TrackStuckEvent) => void;
98
+ "trackStuck": (player: Player, track: Track | null, payload: TrackStuckEvent) => void;
97
99
  /**
98
100
  * Emitted when a Track errored.
99
101
  * @event Manager#trackError
100
102
  */
101
- "trackError": (player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent) => void;
103
+ "trackError": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackExceptionEvent) => void;
102
104
  /**
103
105
  * Emitted when the Playing finished and no more tracks in the queue.
104
106
  * @event Manager#queueEnd
@@ -347,4 +349,3 @@ export declare class LavalinkManager extends EventEmitter {
347
349
  */
348
350
  sendRawData(data: VoicePacket | VoiceServer | VoiceState | ChannelDeletePacket): Promise<void>;
349
351
  }
350
- export {};
@@ -1,12 +1,13 @@
1
1
  /// <reference types="node" />
2
2
  import internal from "stream";
3
- import { Dispatcher, Pool } from "undici";
4
3
  import { NodeManager } from "./NodeManager";
5
4
  import { DestroyReasonsType, Player } from "./Player";
6
5
  import { Track } from "./Track";
7
6
  import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
8
- /** Modifies any outgoing REST requests. */
9
- export type ModifyRequest = (options: Dispatcher.RequestOptions) => void;
7
+ /** Ability to manipulate fetch requests */
8
+ export type ModifyRequest = (options: RequestInit & {
9
+ path: string;
10
+ }) => void;
10
11
  export declare const validSponsorBlocks: string[];
11
12
  export type SponsorBlockSegment = "sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler";
12
13
  export interface LavalinkNodeOptions {
@@ -24,14 +25,12 @@ export interface LavalinkNodeOptions {
24
25
  id?: string;
25
26
  /** Voice Regions of this Node */
26
27
  regions?: string[];
27
- /** Options for the undici http pool used for http requests */
28
- poolOptions?: Pool.Options;
29
28
  /** The retryAmount for the node. */
30
29
  retryAmount?: number;
31
30
  /** The retryDelay for the node. */
32
31
  retryDelay?: number;
33
- /** Pool Undici Options - requestTimeout */
34
- requestTimeout?: number;
32
+ /** signal for cancelling requests - default: AbortSignal.timeout(options.requestSignalTimeoutMS || 10000) - put <= 0 to disable */
33
+ requestSignalTimeoutMS?: number;
35
34
  }
36
35
  export interface MemoryStats {
37
36
  /** The free memory of the allocated amount. */
@@ -145,8 +144,6 @@ export declare class LavalinkNode {
145
144
  private reconnectAttempts;
146
145
  /** The Socket of the Lavalink */
147
146
  private socket;
148
- /** The Rest Server for this Lavalink */
149
- private rest;
150
147
  /** Version of what the Lavalink Server should be */
151
148
  private version;
152
149
  /**
@@ -168,7 +165,7 @@ export declare class LavalinkNode {
168
165
  * @param modify Used to modify the request before being sent
169
166
  * @returns The returned data
170
167
  */
171
- request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<unknown>;
168
+ request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<any>;
172
169
  /**
173
170
  * Search something raw on the node, please note only add tracks to players of that node
174
171
  * @param query SearchQuery Object
@@ -188,7 +185,7 @@ export declare class LavalinkNode {
188
185
  * @param guildId
189
186
  * @returns
190
187
  */
191
- destroyPlayer(guildId: any): Promise<unknown>;
188
+ destroyPlayer(guildId: any): Promise<any>;
192
189
  /**
193
190
  * Connect to the Lavalink Node
194
191
  * @param sessionId Provide the Session Id of the previous connection, to resume the node and it's player(s)
@@ -266,12 +263,12 @@ export declare class LavalinkNode {
266
263
  /**
267
264
  * Release all blacklisted IP addresses into pool of IPs
268
265
  */
269
- unmarkAllFailedAddresses: () => Promise<unknown>;
266
+ unmarkAllFailedAddresses: () => Promise<any>;
270
267
  };
271
268
  /** Private Utils */
272
269
  private validate;
273
270
  private syncPlayerData;
274
- private get poolAddress();
271
+ private get restAddress();
275
272
  private reconnect;
276
273
  private open;
277
274
  private close;
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
5
5
  import { DestroyReasonsType } from "./Player";
6
6
  import { LavalinkPlayer, MiniMap } from "./Utils";
7
7
  type LavalinkNodeIdentifier = string;
8
- interface NodeManagerEvents {
8
+ export interface NodeManagerEvents {
9
9
  /**
10
10
  * Emitted when a Node is created.
11
11
  * @event Manager.nodeManager#create
@@ -55,6 +55,8 @@ export interface PluginInfo {
55
55
  type?: "album" | "playlist" | "artist" | "recommendations" | string;
56
56
  /** The Identifier provided by a plugin */
57
57
  albumName?: string;
58
+ /** The url of the album */
59
+ albumUrl?: string;
58
60
  /** The url of the album art */
59
61
  albumArtUrl?: string;
60
62
  /** The url of the artist */