lavalink-client 2.1.7 → 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.
package/README.md CHANGED
@@ -189,6 +189,7 @@ client.lavalink = new LavalinkManager({
189
189
 
190
190
  # UpdateLog
191
191
 
192
+
192
193
  ## **Version 1.2.0**
193
194
  - Added `player.stopPlaying()`: When executed it **clears the Queue** and **stops playing**, **without destroying the Player**
194
195
  - Adjusted `Player.skip()`
@@ -298,3 +299,13 @@ Most features of this update got tested, but if you encounter any bugs feel free
298
299
  - Enforce link searches for users with following searchPlatform Options: "http" | "https" | "link" | "uri"
299
300
  - Additionally strongend the code behind that
300
301
  - Added searchPlatform for local tracks (aka files on the lavalink server...): "local"
302
+
303
+ ## **Version 2.2.0**
304
+ - Changed console.error to throw error on queue.utils.sync if no data was provided/found
305
+ - Changed undici.fetch to native fetch, but requires nodejs v18+ to support other runtimes, e.g. bun
306
+ - Added sourceNames for `bandcamp` (from native lavalink) if it's supported it will use lavalink'S search, else the client search on player.search({ source: "bandcamp" }) (you can also use bcsearch or bc)
307
+ - Added sourceName for `phsearch` from the dunktebot plugin, released in v.1.7.0
308
+ - Support for youtube still going via the youtube-source plugin (disable youtube for lavalink, and use the plugin instead)
309
+ - Exporting events
310
+ - Added new debugOption: logCustomSearches
311
+ - *(Next version update i will remove the internal interval for position update, to calculations)*
@@ -1,13 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bandCampSearch = void 0;
4
- const undici_1 = require("undici");
5
4
  const bandCampSearch = async (player, query, requestUser) => {
6
5
  let error = null;
7
6
  let tracks = [];
7
+ if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
8
+ console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
8
9
  player.LavalinkManager.utils.validateQueryString(player.node, query);
9
10
  try {
10
- const data = await (0, undici_1.fetch)(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
11
+ const data = await fetch(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
11
12
  headers: {
12
13
  'User-Agent': 'android-async-http/1.4.1 (http://loopj.com/android-async-http)',
13
14
  'Cookie': '$Version=1'
@@ -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 {};
@@ -59,6 +59,7 @@ class LavalinkManager extends events_1.EventEmitter {
59
59
  },
60
60
  advancedOptions: {
61
61
  debugOptions: {
62
+ logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
62
63
  noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
63
64
  playerDestroy: {
64
65
  dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
@@ -50,10 +50,14 @@ exports.DefaultSources = {
50
50
  "flowery": "ftts",
51
51
  "flowery.tts": "ftts",
52
52
  "flowerytts": "ftts",
53
- // Client sided search platforms
53
+ // Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
54
54
  "bandcamp": "bcsearch",
55
55
  "bc": "bcsearch",
56
56
  "bcsearch": "bcsearch",
57
+ // other searches:
58
+ "phsearch": "phsearch",
59
+ "pornhub": "phsearch",
60
+ "porn": "phsearch",
57
61
  // local files
58
62
  "local": "local",
59
63
  // 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;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LavalinkNode = exports.validSponsorBlocks = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path_1 = require("path");
6
- const undici_1 = require("undici");
7
6
  const ws_1 = tslib_1.__importDefault(require("ws"));
8
7
  const Player_1 = require("./Player");
9
8
  const Utils_1 = require("./Utils");
@@ -47,8 +46,6 @@ class LavalinkNode {
47
46
  reconnectAttempts = 1;
48
47
  /** The Socket of the Lavalink */
49
48
  socket = null;
50
- /** The Rest Server for this Lavalink */
51
- rest;
52
49
  /** Version of what the Lavalink Server should be */
53
50
  version = "v4";
54
51
  /**
@@ -61,14 +58,13 @@ class LavalinkNode {
61
58
  secure: false,
62
59
  retryAmount: 5,
63
60
  retryDelay: 30e3,
64
- requestTimeout: 10e3,
61
+ requestSignalTimeoutMS: 10000,
65
62
  ...options
66
63
  };
67
64
  this.NodeManager = manager;
68
65
  this.validate();
69
66
  if (this.options.secure && this.options.port !== 443)
70
67
  throw new SyntaxError("If secure is true, then the port must be 443");
71
- this.rest = new undici_1.Pool(this.poolAddress, this.options.poolOptions);
72
68
  this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
73
69
  Object.defineProperty(this, Utils_1.NodeSymbol, { configurable: true, value: true });
74
70
  }
@@ -83,15 +79,15 @@ class LavalinkNode {
83
79
  path: `/${this.version}/${endpoint.replace(/^\//gm, "")}`,
84
80
  method: "GET",
85
81
  headers: {
86
- Authorization: this.options.authorization
82
+ "Authorization": this.options.authorization
87
83
  },
88
- headersTimeout: this.options.requestTimeout,
84
+ signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : undefined,
89
85
  };
90
86
  modify?.(options);
91
- const url = new URL(`${this.poolAddress}${options.path}`);
87
+ const url = new URL(`${this.restAddress}${options.path}`);
92
88
  url.searchParams.append("trace", "true");
93
- options.path = url.pathname + url.search;
94
- const request = await this.rest.request(options);
89
+ delete options.path;
90
+ const request = await fetch(url.href, options);
95
91
  this.calls++;
96
92
  return { request, options };
97
93
  }
@@ -105,9 +101,9 @@ class LavalinkNode {
105
101
  const { request, options } = await this.rawRequest(endpoint, modify);
106
102
  if (["DELETE", "PUT"].includes(options.method))
107
103
  return;
108
- if (request.statusCode === 404)
104
+ if (request.status === 404)
109
105
  throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(request.headers)}`);
110
- return parseAsText ? await request.body.text() : await request.body.json();
106
+ return parseAsText ? await request.text() : await request.json();
111
107
  }
112
108
  /**
113
109
  * Search something raw on the node, please note only add tracks to players of that node
@@ -120,8 +116,8 @@ class LavalinkNode {
120
116
  this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query, Query.source);
121
117
  if (Query.source)
122
118
  this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
123
- if (["bcsearch", "bandcamp"].includes(Query.source)) {
124
- throw new Error("Bandcamp Search only works on the player!");
119
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.info.sourceManagers.includes("bandcamp")) {
120
+ throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
125
121
  }
126
122
  let uri = `/loadtracks?identifier=`;
127
123
  if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
@@ -169,7 +165,7 @@ class LavalinkNode {
169
165
  const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
170
166
  if (throwOnEmpty === true)
171
167
  throw new Error("Nothing found");
172
- const res = (request.statusCode === 204 ? {} : await request.body.json());
168
+ const res = (request.status === 204 ? {} : await request.json());
173
169
  return {
174
170
  tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
175
171
  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)) })) || [],
@@ -194,7 +190,7 @@ class LavalinkNode {
194
190
  r.headers["Content-Type"] = "application/json";
195
191
  r.body = JSON.stringify(data.playerOptions);
196
192
  if (data.noReplace) {
197
- const url = new URL(`${this.poolAddress}${r.path}`);
193
+ const url = new URL(`${this.restAddress}${r.path}`);
198
194
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
199
195
  r.path = url.pathname + url.search;
200
196
  }
@@ -475,7 +471,7 @@ class LavalinkNode {
475
471
  }
476
472
  return true;
477
473
  }
478
- get poolAddress() {
474
+ get restAddress() {
479
475
  return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
480
476
  }
481
477
  reconnect(instaReconnect = false) {
@@ -510,9 +506,9 @@ class LavalinkNode {
510
506
  clearTimeout(this.reconnectTimeout);
511
507
  // reset the reconnect attempts amount
512
508
  this.reconnectAttempts = 1;
513
- this.info = await this.fetchInfo().catch(() => null);
509
+ this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
514
510
  if (!this.info && ["v3", "v4"].includes(this.version)) {
515
- const errorString = `Lavalink Node (${this.poolAddress}) does not provide any /${this.version}/info`;
511
+ const errorString = `Lavalink Node (${this.restAddress}) does not provide any /${this.version}/info`;
516
512
  throw new Error(errorString);
517
513
  }
518
514
  this.NodeManager.emit("connect", this);
@@ -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
@@ -132,17 +132,17 @@ class Player {
132
132
  if (options?.clientTrack && (this.LavalinkManager.utils.isTrack(options?.clientTrack) || this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
133
133
  if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))
134
134
  await options.clientTrack.resolve(this);
135
- if (typeof options.track.userData === "object")
136
- options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track.userData || {}) };
135
+ if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
136
+ options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
137
137
  await this.queue.add(options?.clientTrack, 0);
138
138
  return await this.skip();
139
139
  }
140
140
  else if (options?.track?.encoded) {
141
141
  // handle play encoded options manually // TODO let it resolve by lavalink!
142
142
  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);
143
- if (typeof options.track.userData === "object")
144
- track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
145
143
  if (track) {
144
+ if (typeof options.track?.userData === "object")
145
+ track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
146
146
  replaced = true;
147
147
  this.queue.add(track, 0);
148
148
  await (0, Utils_1.queueTrackEnd)(this);
@@ -153,9 +153,9 @@ class Player {
153
153
  const res = await this.search({
154
154
  query: options?.track?.identifier
155
155
  }, options?.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
156
- if (typeof options.track.userData === "object")
157
- res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
158
156
  if (res.tracks[0]) {
157
+ if (typeof options.track?.userData === "object")
158
+ res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
159
159
  replaced = true;
160
160
  this.queue.add(res.tracks[0], 0);
161
161
  await (0, Utils_1.queueTrackEnd)(this);
@@ -167,8 +167,8 @@ class Player {
167
167
  try {
168
168
  // resolve the unresolved track
169
169
  await this.queue.current.resolve(this);
170
- if (typeof options.track.userData === "object")
171
- this.queue.current.userData = { ...(this.queue.current.userData || {}), ...(options.track.userData || {}) };
170
+ if (typeof options.track?.userData === "object" && this.queue.current)
171
+ this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
172
172
  }
173
173
  catch (error) {
174
174
  this.LavalinkManager.emit("trackError", this, this.queue.current, error);
@@ -264,7 +264,7 @@ class Player {
264
264
  */
265
265
  async search(query, requestUser) {
266
266
  const Query = this.LavalinkManager.utils.transformQuery(query);
267
- if (["bcsearch", "bandcamp"].includes(Query.source))
267
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
268
268
  return await (0, BandCampSearch_1.bandCampSearch)(this, Query.query, requestUser);
269
269
  return this.node.search(Query, requestUser);
270
270
  }
@@ -81,7 +81,7 @@ class Queue {
81
81
  sync: async (override = true, dontSyncCurrent = true) => {
82
82
  const data = await this.QueueSaver.get(this.guildId);
83
83
  if (!data)
84
- return console.log("No data found to sync for guildId: ", this.guildId);
84
+ throw new Error(`No data found to sync for guildId: ${this.guildId}`);
85
85
  if (!dontSyncCurrent && !this.current && (this.managerUtils.isTrack(data.current)))
86
86
  this.current = data.current;
87
87
  if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(track => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
@@ -14,10 +14,10 @@ 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;
@@ -50,7 +50,7 @@ class ManagerUtils {
50
50
  identifier: data.info.identifier,
51
51
  title: data.info.title,
52
52
  author: data.info.author,
53
- duration: data.info.length || data.info.duration,
53
+ duration: data.info.duration || data.info.length,
54
54
  artworkUrl: data.info.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
55
55
  uri: data.info.uri,
56
56
  sourceName: data.info.sourceName,
@@ -206,7 +206,7 @@ class ManagerUtils {
206
206
  throw new Error("Lavalink Node has not 'soundcloud' enabled");
207
207
  }
208
208
  if (LavalinkManagerStatics_1.SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
209
- throw new Error("Lavalink Node has not 'bandcamp' enabled");
209
+ throw new Error("Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
210
210
  }
211
211
  if (LavalinkManagerStatics_1.SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
212
212
  throw new Error("Lavalink Node has not 'twitch' enabled");
@@ -238,9 +238,10 @@ class ManagerUtils {
238
238
  return;
239
239
  }
240
240
  transformQuery(query) {
241
+ const sourceOfQuery = typeof query === "string" ? undefined : (LavalinkManagerStatics_1.DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
241
242
  const Query = {
242
243
  query: typeof query === "string" ? query : query.query,
243
- source: LavalinkManagerStatics_1.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?.()
244
+ source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
244
245
  };
245
246
  const foundSource = Object.keys(LavalinkManagerStatics_1.DefaultSources).find(source => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
246
247
  // ignore links...
@@ -252,10 +253,11 @@ class ManagerUtils {
252
253
  }
253
254
  transformLavaSearchQuery(query) {
254
255
  // transform the query object
256
+ const sourceOfQuery = typeof query === "string" ? undefined : (LavalinkManagerStatics_1.DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
255
257
  const Query = {
256
258
  query: typeof query === "string" ? query : query.query,
257
259
  types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(v => query.types?.find(x => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album", /*"text"*/],
258
- source: LavalinkManagerStatics_1.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?.()
260
+ source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
259
261
  };
260
262
  const foundSource = Object.keys(LavalinkManagerStatics_1.DefaultSources).find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
261
263
  if (foundSource && LavalinkManagerStatics_1.DefaultSources[foundSource]) {
@@ -1,7 +1,8 @@
1
- import { fetch } from "undici";
2
1
  export const bandCampSearch = async (player, query, requestUser) => {
3
2
  let error = null;
4
3
  let tracks = [];
4
+ if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
5
+ console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
5
6
  player.LavalinkManager.utils.validateQueryString(player.node, query);
6
7
  try {
7
8
  const data = await fetch(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
@@ -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 {};
@@ -56,6 +56,7 @@ export class LavalinkManager extends EventEmitter {
56
56
  },
57
57
  advancedOptions: {
58
58
  debugOptions: {
59
+ logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
59
60
  noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
60
61
  playerDestroy: {
61
62
  dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
@@ -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
@@ -165,7 +161,7 @@ export class LavalinkNode {
165
161
  const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
166
162
  if (throwOnEmpty === true)
167
163
  throw new Error("Nothing found");
168
- const res = (request.statusCode === 204 ? {} : await request.body.json());
164
+ const res = (request.status === 204 ? {} : await request.json());
169
165
  return {
170
166
  tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
171
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)) })) || [],
@@ -190,7 +186,7 @@ export class LavalinkNode {
190
186
  r.headers["Content-Type"] = "application/json";
191
187
  r.body = JSON.stringify(data.playerOptions);
192
188
  if (data.noReplace) {
193
- const url = new URL(`${this.poolAddress}${r.path}`);
189
+ const url = new URL(`${this.restAddress}${r.path}`);
194
190
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
195
191
  r.path = url.pathname + url.search;
196
192
  }
@@ -471,7 +467,7 @@ export class LavalinkNode {
471
467
  }
472
468
  return true;
473
469
  }
474
- get poolAddress() {
470
+ get restAddress() {
475
471
  return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
476
472
  }
477
473
  reconnect(instaReconnect = false) {
@@ -506,9 +502,9 @@ export class LavalinkNode {
506
502
  clearTimeout(this.reconnectTimeout);
507
503
  // reset the reconnect attempts amount
508
504
  this.reconnectAttempts = 1;
509
- this.info = await this.fetchInfo().catch(() => null);
505
+ this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
510
506
  if (!this.info && ["v3", "v4"].includes(this.version)) {
511
- 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`;
512
508
  throw new Error(errorString);
513
509
  }
514
510
  this.NodeManager.emit("connect", this);
@@ -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);
@@ -150,9 +150,9 @@ export class Player {
150
150
  const res = await this.search({
151
151
  query: options?.track?.identifier
152
152
  }, options?.track?.requester || 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 || {}) };
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)))
@@ -14,10 +14,10 @@ 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;
@@ -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
@@ -14,10 +14,10 @@ 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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.1.7",
3
+ "version": "2.2.0",
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",
@@ -59,8 +59,10 @@
59
59
  "typescript": "^5.1.6"
60
60
  },
61
61
  "dependencies": {
62
- "undici": "^5.23.0",
63
62
  "tslib": "^2.6.1",
64
63
  "ws": "^8.13.0"
64
+ },
65
+ "engines": {
66
+ "node": ">=18.0.0"
65
67
  }
66
68
  }