lavalink-client 1.1.10 → 1.1.11

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.
@@ -0,0 +1,3 @@
1
+ import { Player } from "../Player";
2
+ import { UnresolvedSearchResult } from "../Utils";
3
+ export declare const bandCampSearch: (player: Player, query: string, requestUser: unknown) => Promise<UnresolvedSearchResult>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bandCampSearch = void 0;
4
+ const undici_1 = require("undici");
5
+ const http_1 = require("http");
6
+ const bandCampSearch = async (player, query, requestUser) => {
7
+ let error = null;
8
+ let tracks = [];
9
+ try {
10
+ const data = await (0, undici_1.fetch)(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
11
+ headers: {
12
+ 'User-Agent': 'android-async-http/1.4.1 (http://loopj.com/android-async-http)',
13
+ 'Cookie': '$Version=1'
14
+ }
15
+ });
16
+ const json = await data.json();
17
+ tracks = json?.results?.filter(x => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(item => player.LavalinkManager.utils.buildUnresolvedTrack({
18
+ uri: item.url || item.uri,
19
+ artworkUrl: item.img,
20
+ author: item.band_name,
21
+ title: item.name,
22
+ identifier: item.id ? `${item.id}` : item.url?.split("/").reverse()[0],
23
+ }, http_1.request));
24
+ }
25
+ catch (e) {
26
+ error = e;
27
+ }
28
+ return {
29
+ loadType: "search",
30
+ exception: error,
31
+ pluginInfo: {},
32
+ playlist: null,
33
+ tracks: tracks
34
+ };
35
+ };
36
+ exports.bandCampSearch = bandCampSearch;
@@ -95,11 +95,11 @@ export declare class FilterManager {
95
95
  toggleEcho(delay?: number, decay?: number): Promise<boolean>;
96
96
  /**
97
97
  * Enabels / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
98
- * @param delay
99
- * @param decay
98
+ * @param delays
99
+ * @param gains
100
100
  * @returns
101
101
  */
102
- toggleReverb(delay?: number, decay?: number): Promise<boolean>;
102
+ toggleReverb(delays?: number[], gains?: number[]): Promise<boolean>;
103
103
  /**
104
104
  * Enables / Disabels a Nightcore-like filter Effect. Disables/Overwrides both: custom and Vaporwave Filter
105
105
  * @param speed
@@ -276,8 +276,8 @@ export interface EchoFilter {
276
276
  * A Plugin Filter
277
277
  */
278
278
  export interface ReverbFilter {
279
- delay: number;
280
- decay: number;
279
+ delays: number[];
280
+ gains: number[];
281
281
  }
282
282
  /**
283
283
  * Filter Data stored in the Client and partially sent to Lavalink
@@ -292,6 +292,13 @@ export interface FilterData {
292
292
  distortion?: DistortionFilter;
293
293
  channelMix?: ChannelMixFilter;
294
294
  lowPass?: LowPassFilter;
295
+ pluginFilters?: Record<PluginFiltersKey, PluginFiltersValues>;
296
+ }
297
+ export type PluginFiltersKey = "lavalink-filter-plugin" | string;
298
+ export interface PluginFiltersValues extends LavalinkFiltersPlugin {
299
+ [key: string]: any;
300
+ }
301
+ export interface LavalinkFiltersPlugin {
295
302
  echo: EchoFilter;
296
303
  reverb: ReverbFilter;
297
304
  }
@@ -40,14 +40,6 @@ class FilterManager {
40
40
  pitch: 1,
41
41
  rate: 1 // 0 = x
42
42
  },
43
- echo: {
44
- delay: 0,
45
- decay: 0
46
- },
47
- reverb: {
48
- delay: 0,
49
- decay: 0
50
- },
51
43
  rotation: {
52
44
  rotationHz: 0
53
45
  },
@@ -59,6 +51,18 @@ class FilterManager {
59
51
  frequency: 0,
60
52
  depth: 0 // 0 < x = 1
61
53
  },
54
+ pluginFilters: {
55
+ /*"lavalink-filter-plugin": {
56
+ echo: {
57
+ delay: 0,
58
+ decay: 0
59
+ },
60
+ reverb: {
61
+ delays: [0.037, 0.042, 0.048, 0.053],
62
+ gains: [0.84, 0.83, 0.82, 0.81]
63
+ }
64
+ }*/
65
+ },
62
66
  channelMix: exports.audioOutputsData.stereo,
63
67
  /*distortion: {
64
68
  sinOffset: 0,
@@ -83,24 +87,27 @@ class FilterManager {
83
87
  */
84
88
  async applyPlayerFilters() {
85
89
  const sendData = { ...this.data };
90
+ this.checkFiltersState();
86
91
  if (!this.filters.volume)
87
92
  delete sendData.volume;
88
93
  if (!this.filters.tremolo)
89
94
  delete sendData.tremolo;
90
95
  if (!this.filters.vibrato)
91
96
  delete sendData.vibrato;
92
- //if(!this.filters.karaoke) delete sendData.karaoke;
93
97
  if (!this.filters.echo)
94
- delete sendData.echo;
98
+ delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.echo;
95
99
  if (!this.filters.reverb)
96
- delete sendData.reverb;
100
+ delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
97
101
  if (!this.filters.lowPass)
98
102
  delete sendData.lowPass;
99
103
  if (!this.filters.karaoke)
100
104
  delete sendData.karaoke;
101
- //if(!this.filters.rotating) delete sendData.rotating;
105
+ if (!this.filters.rotation)
106
+ delete sendData.rotation;
102
107
  if (this.filters.audioOutput === "stereo")
103
108
  delete sendData.channelMix;
109
+ if (Object.values(this.data.timescale).every(v => v === 1))
110
+ delete sendData.timescale;
104
111
  if (!this.player.node.sessionId)
105
112
  throw new Error("The Lavalink-Node is either not ready or not up to date");
106
113
  sendData.equalizer = [...this.equalizerBands];
@@ -130,8 +137,9 @@ class FilterManager {
130
137
  this.filters.rotation = this.data.rotation.rotationHz !== 0;
131
138
  this.filters.vibrato = this.data.vibrato.frequency !== 0 || this.data.vibrato.depth !== 0;
132
139
  this.filters.tremolo = this.data.tremolo.frequency !== 0 || this.data.tremolo.depth !== 0;
133
- this.filters.echo = this.data.echo.decay !== 0 || this.data.echo.delay !== 0;
134
- this.filters.reverb = this.data.reverb.decay !== 0 || this.data.reverb.delay !== 0;
140
+ const lavalinkFilterData = (this.data.pluginFilters?.["lavalink-filter-plugin"] || { echo: { decay: 0, delay: 0 }, reverb: { gains: [], delays: [] } });
141
+ this.filters.echo = lavalinkFilterData.echo.decay !== 0 || lavalinkFilterData.echo.delay !== 0;
142
+ this.filters.reverb = lavalinkFilterData.reverb?.delays?.length !== 0 || lavalinkFilterData.reverb?.gains?.length !== 0;
135
143
  this.filters.lowPass = this.data.lowPass.smoothing !== 0;
136
144
  this.filters.karaoke = Object.values(this.data.karaoke).some(v => v !== 0);
137
145
  if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
@@ -180,8 +188,8 @@ class FilterManager {
180
188
  decay: 0
181
189
  },
182
190
  reverb: {
183
- delay: 0,
184
- decay: 0
191
+ delays: [],
192
+ gains: []
185
193
  },
186
194
  rotation: {
187
195
  rotationHz: 0
@@ -358,23 +366,35 @@ class FilterManager {
358
366
  async toggleEcho(delay = 1, decay = 0.5) {
359
367
  if (this.player.node.info && !this.player.node.info?.filters?.includes("echo"))
360
368
  throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)");
361
- this.data.echo.delay = this.filters.echo ? 0 : delay;
362
- this.data.echo.decay = this.filters.echo ? 0 : decay;
369
+ if (!this.data)
370
+ this.data = {};
371
+ if (!this.data.pluginFilters)
372
+ this.data.pluginFilters = {};
373
+ if (!this.data.pluginFilters["lavalink-filter-plugin"])
374
+ this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
375
+ this.data.pluginFilters["lavalink-filter-plugin"].echo.delay = this.filters.echo ? 0 : delay;
376
+ this.data.pluginFilters["lavalink-filter-plugin"].echo.decay = this.filters.echo ? 0 : decay;
363
377
  this.filters.echo = !this.filters.echo;
364
378
  await this.applyPlayerFilters();
365
379
  return this.filters.echo;
366
380
  }
367
381
  /**
368
382
  * Enabels / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
369
- * @param delay
370
- * @param decay
383
+ * @param delays
384
+ * @param gains
371
385
  * @returns
372
386
  */
373
- async toggleReverb(delay = 1, decay = 0.5) {
387
+ async toggleReverb(delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) {
374
388
  if (this.player.node.info && !this.player.node.info?.filters?.includes("reverb"))
375
389
  throw new Error("Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)");
376
- this.data.reverb.delay = this.filters.reverb ? 0 : delay;
377
- this.data.reverb.decay = this.filters.reverb ? 0 : decay;
390
+ if (!this.data)
391
+ this.data = {};
392
+ if (!this.data.pluginFilters)
393
+ this.data.pluginFilters = {};
394
+ if (!this.data.pluginFilters["lavalink-filter-plugin"])
395
+ this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
396
+ this.data.pluginFilters["lavalink-filter-plugin"].reverb.delays = this.filters.reverb ? [] : delays;
397
+ this.data.pluginFilters["lavalink-filter-plugin"].reverb.gains = this.filters.reverb ? [] : gains;
378
398
  this.filters.reverb = !this.filters.reverb;
379
399
  await this.applyPlayerFilters();
380
400
  return this.filters.reverb;
@@ -40,7 +40,10 @@ exports.DefaultSources = {
40
40
  // speak PLUGIN
41
41
  "speak": "speak",
42
42
  "tts": "tts",
43
- "ftts": "ftts"
43
+ "ftts": "ftts",
44
+ // Client sided search platforms
45
+ "bandcamp": "bcsearch",
46
+ "bcsearch": "bcsearch",
44
47
  };
45
48
  exports.LavalinkPlugins = {
46
49
  DuncteBot_Plugin: "DuncteBot-plugin",
@@ -2,7 +2,7 @@
2
2
  import { Dispatcher, Pool } from "undici";
3
3
  import { NodeManager } from "./NodeManager";
4
4
  import internal from "stream";
5
- import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64 } from "./Utils";
5
+ import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64, SearchResult } from "./Utils";
6
6
  import { DestroyReasonsType } from "./Player";
7
7
  import { Track } from "./Track";
8
8
  /** Modifies any outgoing REST requests. */
@@ -135,6 +135,7 @@ export declare class LavalinkNode {
135
135
  * @returns The returned data
136
136
  */
137
137
  request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<unknown>;
138
+ search(querySourceString: string, requestUser: unknown): Promise<SearchResult>;
138
139
  /**
139
140
  * Update the Player State on the Lavalink Server
140
141
  * @param data
@@ -96,6 +96,25 @@ class LavalinkNode {
96
96
  throw new Error(`Node Request resulted into an error, request-URL: ${url} | headers: ${JSON.stringify(request.headers)}`);
97
97
  return parseAsText ? await request.body.text() : await request.body.json();
98
98
  }
99
+ async search(querySourceString, requestUser) {
100
+ const res = await this.request(`/loadsearch?query=${encodeURIComponent(decodeURIComponent(querySourceString))}`);
101
+ // transform the data which can be Error, Track or Track[] to enfore [Track]
102
+ const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
103
+ return {
104
+ loadType: res.loadType,
105
+ exception: res.loadType === "error" ? res.data : null,
106
+ pluginInfo: res.pluginInfo || {},
107
+ playlist: res.loadType === "playlist" ? {
108
+ title: res.data.info?.name || res.data.pluginInfo?.name || null,
109
+ author: res.data.info?.author || res.data.pluginInfo?.author || null,
110
+ 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,
111
+ 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,
112
+ 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,
113
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || 0), 0) : 0,
114
+ } : null,
115
+ tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
116
+ };
117
+ }
99
118
  /**
100
119
  * Update the Player State on the Lavalink Server
101
120
  * @param data
@@ -354,8 +373,8 @@ class LavalinkNode {
354
373
  player.filterManager.data.timescale = data.playerOptions.filters.timescale;
355
374
  if (data.playerOptions.filters.distortion)
356
375
  player.filterManager.data.distortion = data.playerOptions.filters.distortion;
357
- if (data.playerOptions.filters.echo)
358
- player.filterManager.data.echo = data.playerOptions.filters.echo;
376
+ if (data.playerOptions.filters.pluginFilters)
377
+ player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
359
378
  if (data.playerOptions.filters.vibrato)
360
379
  player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
361
380
  if (data.playerOptions.filters.volume)
@@ -142,7 +142,7 @@ export declare class Player {
142
142
  query: string;
143
143
  source: LavaSrcSearchPlatformBase;
144
144
  types?: LavaSearchType[];
145
- }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
145
+ }, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult | LavaSearchResponse>;
146
146
  /**
147
147
  *
148
148
  * @param query Query for your data
@@ -151,7 +151,7 @@ export declare class Player {
151
151
  search(query: {
152
152
  query: string;
153
153
  source?: SearchPlatform;
154
- } | string, requestUser: unknown): Promise<SearchResult>;
154
+ } | string, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult>;
155
155
  /**
156
156
  * Pause the player
157
157
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Player = exports.DestroyReasons = void 0;
4
+ const BandCampSearch_1 = require("./CustomSearches/BandCampSearch");
4
5
  const Filters_1 = require("./Filters");
5
6
  const LavalinkManagerStatics_1 = require("./LavalinkManagerStatics");
6
7
  const Queue_1 = require("./Queue");
@@ -261,6 +262,9 @@ class Player {
261
262
  this.LavalinkManager.utils.validateQueryString(this.node, Query.source);
262
263
  else if (Query.source)
263
264
  this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
265
+ if (["bcsearch", "bandcamp"].includes(Query.source)) {
266
+ return await (0, BandCampSearch_1.bandCampSearch)(this, Query.query, requestUser);
267
+ }
264
268
  // ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
265
269
  // request the data
266
270
  const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
@@ -414,7 +418,6 @@ class Player {
414
418
  const data = this.toJSON();
415
419
  await this.node.destroyPlayer(this.guildId);
416
420
  this.node = updateNode;
417
- await this.connect();
418
421
  const now = performance.now();
419
422
  await this.node.updatePlayer({
420
423
  guildId: this.guildId,
@@ -424,6 +427,8 @@ class Player {
424
427
  volume: Math.round(Math.max(Math.min(data.volume, 1000), 0)),
425
428
  paused: data.paused,
426
429
  filters: { ...data.filters, equalizer: data.equalizer },
430
+ voice: this.voice,
431
+ // track: this.queue.current,
427
432
  },
428
433
  });
429
434
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
@@ -15,8 +15,10 @@ 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
17
  export type DuncteSearchPlatform = "speak" | "tts";
18
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
19
- export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
18
+ export type LavalinkClientSearchPlatform = "bcsearch";
19
+ export type LavalinkClientSearchPlatformResolve = "bandcamp";
20
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
21
+ export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
20
22
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
21
23
  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";
22
24
  export interface PlaylistInfo {
@@ -42,6 +44,13 @@ export interface SearchResult {
42
44
  playlist: PlaylistInfo | null;
43
45
  tracks: Track[];
44
46
  }
47
+ export interface UnresolvedSearchResult {
48
+ loadType: LoadTypes;
49
+ exception: Exception | null;
50
+ pluginInfo: PluginInfo;
51
+ playlist: PlaylistInfo | null;
52
+ tracks: UnresolvedTrack[];
53
+ }
45
54
  export declare class ManagerUtils {
46
55
  LavalinkManager: LavalinkManager | null;
47
56
  constructor(LavalinkManager?: LavalinkManager);
@@ -56,7 +56,7 @@ class ManagerUtils {
56
56
  info: query.info ? query.info : query.title ? query : undefined,
57
57
  requester: typeof this.LavalinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.LavalinkManager?.options?.playerOptions?.requesterTransformer((query?.requester || requester)) : requester,
58
58
  async resolve(player) {
59
- const closest = await getClosestTrack(this, player, player.LavalinkManager.utils);
59
+ const closest = await getClosestTrack(this, player);
60
60
  if (!closest)
61
61
  throw new SyntaxError("No closest Track found");
62
62
  Object.getOwnPropertyNames(this).forEach(prop => delete this[prop]);
@@ -148,7 +148,7 @@ class ManagerUtils {
148
148
  return typeof data === "object" && !("info" in data) && typeof data.title === "string";
149
149
  }
150
150
  async getClosestTrack(data, player) {
151
- return getClosestTrack(data, player, this);
151
+ return getClosestTrack(data, player);
152
152
  }
153
153
  validateQueryString(node, queryString) {
154
154
  if (!node.info)
@@ -315,12 +315,12 @@ async function applyUnresolvedData(resTrack, data, utils) {
315
315
  resTrack.info[key] = data.info[key]; // add non-existing values
316
316
  return resTrack;
317
317
  }
318
- async function getClosestTrack(data, player, utils) {
318
+ async function getClosestTrack(data, player) {
319
319
  if (!player || !player.node)
320
320
  throw new RangeError("No player with a lavalink node was provided");
321
- if (utils.isTrack(data))
322
- return utils.buildTrack(data, data.requester);
323
- if (!utils.isUnresolvedTrack(data))
321
+ if (player.LavalinkManager.utils.isTrack(data))
322
+ return player.LavalinkManager.utils.buildTrack(data, data.requester);
323
+ if (!player.LavalinkManager.utils.isUnresolvedTrack(data))
324
324
  throw new RangeError("Track is not an unresolved Track");
325
325
  if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
326
326
  throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
@@ -330,31 +330,31 @@ async function getClosestTrack(data, player, utils) {
330
330
  if (typeof data.encoded === "string") {
331
331
  const r = await player.node.decode.singleTrack(data.encoded, data.requester);
332
332
  if (r)
333
- return applyUnresolvedData(r, data, utils);
333
+ return applyUnresolvedData(r, data, player.LavalinkManager.utils);
334
334
  }
335
335
  // try to fetch the track via a uri if possible
336
336
  if (typeof data.info.uri === "string") {
337
- const r = await player.search({ query: data?.info?.uri }, data.requester).then(v => v.tracks[0]);
337
+ const r = await player.search({ query: data?.info?.uri }, data.requester).then(v => v.tracks?.[0]);
338
338
  if (r)
339
- return applyUnresolvedData(r, data, utils);
339
+ return applyUnresolvedData(r, data, player.LavalinkManager.utils);
340
340
  }
341
341
  // search the track as closely as possible
342
342
  const query = [data.info?.title, data.info?.author].filter(str => !!str).join(" by ");
343
343
  const sourceName = data.info?.sourceName;
344
344
  return await player.search({
345
- query, source: sourceName !== "bandcamp" && sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform,
346
- }, data.requester).then(res => {
345
+ query, source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform,
346
+ }, data.requester).then((res) => {
347
347
  let trackToUse = null;
348
348
  // try to find via author name
349
349
  if (data.info.author && !trackToUse)
350
- trackToUse = res.tracks.find(track => [data.info.author, `${data.info.author} - Topic`].some(name => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author)) || new RegExp(`^${escapeRegExp(data.info.title)}$`, "i").test(track.info.title));
350
+ trackToUse = res.tracks.find(track => [data.info?.author || "", `${data.info?.author} - Topic`].some(name => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)) || new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title));
351
351
  // try to find via duration
352
352
  if (data.info.duration && !trackToUse)
353
- trackToUse = res.tracks.find(track => (track.info.duration >= (data.info.duration - 1500)) && (track.info.duration <= (data.info.duration + 1500)));
353
+ trackToUse = res.tracks.find(track => (track.info?.duration >= (data.info?.duration - 1500)) && (track?.info.duration <= (data.info?.duration + 1500)));
354
354
  // try to find via isrc
355
355
  if (data.info.isrc && !trackToUse)
356
- trackToUse = res.tracks.find(track => track.info.isrc === data.info.isrc);
356
+ trackToUse = res.tracks.find(track => track.info?.isrc === data.info?.isrc);
357
357
  // apply unresolved data and return the track
358
- return applyUnresolvedData(trackToUse || res.tracks[0], data, utils);
358
+ return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
359
359
  });
360
360
  }
@@ -0,0 +1,3 @@
1
+ import { Player } from "../Player";
2
+ import { UnresolvedSearchResult } from "../Utils";
3
+ export declare const bandCampSearch: (player: Player, query: string, requestUser: unknown) => Promise<UnresolvedSearchResult>;
@@ -0,0 +1,32 @@
1
+ import { fetch } from "undici";
2
+ import { request } from "http";
3
+ export const bandCampSearch = async (player, query, requestUser) => {
4
+ let error = null;
5
+ let tracks = [];
6
+ try {
7
+ const data = await fetch(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
8
+ headers: {
9
+ 'User-Agent': 'android-async-http/1.4.1 (http://loopj.com/android-async-http)',
10
+ 'Cookie': '$Version=1'
11
+ }
12
+ });
13
+ const json = await data.json();
14
+ tracks = json?.results?.filter(x => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(item => player.LavalinkManager.utils.buildUnresolvedTrack({
15
+ uri: item.url || item.uri,
16
+ artworkUrl: item.img,
17
+ author: item.band_name,
18
+ title: item.name,
19
+ identifier: item.id ? `${item.id}` : item.url?.split("/").reverse()[0],
20
+ }, request));
21
+ }
22
+ catch (e) {
23
+ error = e;
24
+ }
25
+ return {
26
+ loadType: "search",
27
+ exception: error,
28
+ pluginInfo: {},
29
+ playlist: null,
30
+ tracks: tracks
31
+ };
32
+ };
@@ -95,11 +95,11 @@ export declare class FilterManager {
95
95
  toggleEcho(delay?: number, decay?: number): Promise<boolean>;
96
96
  /**
97
97
  * Enabels / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
98
- * @param delay
99
- * @param decay
98
+ * @param delays
99
+ * @param gains
100
100
  * @returns
101
101
  */
102
- toggleReverb(delay?: number, decay?: number): Promise<boolean>;
102
+ toggleReverb(delays?: number[], gains?: number[]): Promise<boolean>;
103
103
  /**
104
104
  * Enables / Disabels a Nightcore-like filter Effect. Disables/Overwrides both: custom and Vaporwave Filter
105
105
  * @param speed
@@ -276,8 +276,8 @@ export interface EchoFilter {
276
276
  * A Plugin Filter
277
277
  */
278
278
  export interface ReverbFilter {
279
- delay: number;
280
- decay: number;
279
+ delays: number[];
280
+ gains: number[];
281
281
  }
282
282
  /**
283
283
  * Filter Data stored in the Client and partially sent to Lavalink
@@ -292,6 +292,13 @@ export interface FilterData {
292
292
  distortion?: DistortionFilter;
293
293
  channelMix?: ChannelMixFilter;
294
294
  lowPass?: LowPassFilter;
295
+ pluginFilters?: Record<PluginFiltersKey, PluginFiltersValues>;
296
+ }
297
+ export type PluginFiltersKey = "lavalink-filter-plugin" | string;
298
+ export interface PluginFiltersValues extends LavalinkFiltersPlugin {
299
+ [key: string]: any;
300
+ }
301
+ export interface LavalinkFiltersPlugin {
295
302
  echo: EchoFilter;
296
303
  reverb: ReverbFilter;
297
304
  }
@@ -37,14 +37,6 @@ export class FilterManager {
37
37
  pitch: 1,
38
38
  rate: 1 // 0 = x
39
39
  },
40
- echo: {
41
- delay: 0,
42
- decay: 0
43
- },
44
- reverb: {
45
- delay: 0,
46
- decay: 0
47
- },
48
40
  rotation: {
49
41
  rotationHz: 0
50
42
  },
@@ -56,6 +48,18 @@ export class FilterManager {
56
48
  frequency: 0,
57
49
  depth: 0 // 0 < x = 1
58
50
  },
51
+ pluginFilters: {
52
+ /*"lavalink-filter-plugin": {
53
+ echo: {
54
+ delay: 0,
55
+ decay: 0
56
+ },
57
+ reverb: {
58
+ delays: [0.037, 0.042, 0.048, 0.053],
59
+ gains: [0.84, 0.83, 0.82, 0.81]
60
+ }
61
+ }*/
62
+ },
59
63
  channelMix: audioOutputsData.stereo,
60
64
  /*distortion: {
61
65
  sinOffset: 0,
@@ -80,24 +84,27 @@ export class FilterManager {
80
84
  */
81
85
  async applyPlayerFilters() {
82
86
  const sendData = { ...this.data };
87
+ this.checkFiltersState();
83
88
  if (!this.filters.volume)
84
89
  delete sendData.volume;
85
90
  if (!this.filters.tremolo)
86
91
  delete sendData.tremolo;
87
92
  if (!this.filters.vibrato)
88
93
  delete sendData.vibrato;
89
- //if(!this.filters.karaoke) delete sendData.karaoke;
90
94
  if (!this.filters.echo)
91
- delete sendData.echo;
95
+ delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.echo;
92
96
  if (!this.filters.reverb)
93
- delete sendData.reverb;
97
+ delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
94
98
  if (!this.filters.lowPass)
95
99
  delete sendData.lowPass;
96
100
  if (!this.filters.karaoke)
97
101
  delete sendData.karaoke;
98
- //if(!this.filters.rotating) delete sendData.rotating;
102
+ if (!this.filters.rotation)
103
+ delete sendData.rotation;
99
104
  if (this.filters.audioOutput === "stereo")
100
105
  delete sendData.channelMix;
106
+ if (Object.values(this.data.timescale).every(v => v === 1))
107
+ delete sendData.timescale;
101
108
  if (!this.player.node.sessionId)
102
109
  throw new Error("The Lavalink-Node is either not ready or not up to date");
103
110
  sendData.equalizer = [...this.equalizerBands];
@@ -127,8 +134,9 @@ export class FilterManager {
127
134
  this.filters.rotation = this.data.rotation.rotationHz !== 0;
128
135
  this.filters.vibrato = this.data.vibrato.frequency !== 0 || this.data.vibrato.depth !== 0;
129
136
  this.filters.tremolo = this.data.tremolo.frequency !== 0 || this.data.tremolo.depth !== 0;
130
- this.filters.echo = this.data.echo.decay !== 0 || this.data.echo.delay !== 0;
131
- this.filters.reverb = this.data.reverb.decay !== 0 || this.data.reverb.delay !== 0;
137
+ const lavalinkFilterData = (this.data.pluginFilters?.["lavalink-filter-plugin"] || { echo: { decay: 0, delay: 0 }, reverb: { gains: [], delays: [] } });
138
+ this.filters.echo = lavalinkFilterData.echo.decay !== 0 || lavalinkFilterData.echo.delay !== 0;
139
+ this.filters.reverb = lavalinkFilterData.reverb?.delays?.length !== 0 || lavalinkFilterData.reverb?.gains?.length !== 0;
132
140
  this.filters.lowPass = this.data.lowPass.smoothing !== 0;
133
141
  this.filters.karaoke = Object.values(this.data.karaoke).some(v => v !== 0);
134
142
  if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
@@ -177,8 +185,8 @@ export class FilterManager {
177
185
  decay: 0
178
186
  },
179
187
  reverb: {
180
- delay: 0,
181
- decay: 0
188
+ delays: [],
189
+ gains: []
182
190
  },
183
191
  rotation: {
184
192
  rotationHz: 0
@@ -355,23 +363,35 @@ export class FilterManager {
355
363
  async toggleEcho(delay = 1, decay = 0.5) {
356
364
  if (this.player.node.info && !this.player.node.info?.filters?.includes("echo"))
357
365
  throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)");
358
- this.data.echo.delay = this.filters.echo ? 0 : delay;
359
- this.data.echo.decay = this.filters.echo ? 0 : decay;
366
+ if (!this.data)
367
+ this.data = {};
368
+ if (!this.data.pluginFilters)
369
+ this.data.pluginFilters = {};
370
+ if (!this.data.pluginFilters["lavalink-filter-plugin"])
371
+ this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
372
+ this.data.pluginFilters["lavalink-filter-plugin"].echo.delay = this.filters.echo ? 0 : delay;
373
+ this.data.pluginFilters["lavalink-filter-plugin"].echo.decay = this.filters.echo ? 0 : decay;
360
374
  this.filters.echo = !this.filters.echo;
361
375
  await this.applyPlayerFilters();
362
376
  return this.filters.echo;
363
377
  }
364
378
  /**
365
379
  * Enabels / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
366
- * @param delay
367
- * @param decay
380
+ * @param delays
381
+ * @param gains
368
382
  * @returns
369
383
  */
370
- async toggleReverb(delay = 1, decay = 0.5) {
384
+ async toggleReverb(delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) {
371
385
  if (this.player.node.info && !this.player.node.info?.filters?.includes("reverb"))
372
386
  throw new Error("Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)");
373
- this.data.reverb.delay = this.filters.reverb ? 0 : delay;
374
- this.data.reverb.decay = this.filters.reverb ? 0 : decay;
387
+ if (!this.data)
388
+ this.data = {};
389
+ if (!this.data.pluginFilters)
390
+ this.data.pluginFilters = {};
391
+ if (!this.data.pluginFilters["lavalink-filter-plugin"])
392
+ this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
393
+ this.data.pluginFilters["lavalink-filter-plugin"].reverb.delays = this.filters.reverb ? [] : delays;
394
+ this.data.pluginFilters["lavalink-filter-plugin"].reverb.gains = this.filters.reverb ? [] : gains;
375
395
  this.filters.reverb = !this.filters.reverb;
376
396
  await this.applyPlayerFilters();
377
397
  return this.filters.reverb;
@@ -37,7 +37,10 @@ export const DefaultSources = {
37
37
  // speak PLUGIN
38
38
  "speak": "speak",
39
39
  "tts": "tts",
40
- "ftts": "ftts"
40
+ "ftts": "ftts",
41
+ // Client sided search platforms
42
+ "bandcamp": "bcsearch",
43
+ "bcsearch": "bcsearch",
41
44
  };
42
45
  export const LavalinkPlugins = {
43
46
  DuncteBot_Plugin: "DuncteBot-plugin",
@@ -2,7 +2,7 @@
2
2
  import { Dispatcher, Pool } from "undici";
3
3
  import { NodeManager } from "./NodeManager";
4
4
  import internal from "stream";
5
- import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64 } from "./Utils";
5
+ import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64, SearchResult } from "./Utils";
6
6
  import { DestroyReasonsType } from "./Player";
7
7
  import { Track } from "./Track";
8
8
  /** Modifies any outgoing REST requests. */
@@ -135,6 +135,7 @@ export declare class LavalinkNode {
135
135
  * @returns The returned data
136
136
  */
137
137
  request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<unknown>;
138
+ search(querySourceString: string, requestUser: unknown): Promise<SearchResult>;
138
139
  /**
139
140
  * Update the Player State on the Lavalink Server
140
141
  * @param data
@@ -92,6 +92,25 @@ export class LavalinkNode {
92
92
  throw new Error(`Node Request resulted into an error, request-URL: ${url} | headers: ${JSON.stringify(request.headers)}`);
93
93
  return parseAsText ? await request.body.text() : await request.body.json();
94
94
  }
95
+ async search(querySourceString, requestUser) {
96
+ const res = await this.request(`/loadsearch?query=${encodeURIComponent(decodeURIComponent(querySourceString))}`);
97
+ // transform the data which can be Error, Track or Track[] to enfore [Track]
98
+ const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
99
+ return {
100
+ loadType: res.loadType,
101
+ exception: res.loadType === "error" ? res.data : null,
102
+ pluginInfo: res.pluginInfo || {},
103
+ playlist: res.loadType === "playlist" ? {
104
+ title: res.data.info?.name || res.data.pluginInfo?.name || null,
105
+ author: res.data.info?.author || res.data.pluginInfo?.author || null,
106
+ 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,
107
+ 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,
108
+ 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,
109
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || 0), 0) : 0,
110
+ } : null,
111
+ tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
112
+ };
113
+ }
95
114
  /**
96
115
  * Update the Player State on the Lavalink Server
97
116
  * @param data
@@ -350,8 +369,8 @@ export class LavalinkNode {
350
369
  player.filterManager.data.timescale = data.playerOptions.filters.timescale;
351
370
  if (data.playerOptions.filters.distortion)
352
371
  player.filterManager.data.distortion = data.playerOptions.filters.distortion;
353
- if (data.playerOptions.filters.echo)
354
- player.filterManager.data.echo = data.playerOptions.filters.echo;
372
+ if (data.playerOptions.filters.pluginFilters)
373
+ player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
355
374
  if (data.playerOptions.filters.vibrato)
356
375
  player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
357
376
  if (data.playerOptions.filters.volume)
@@ -142,7 +142,7 @@ export declare class Player {
142
142
  query: string;
143
143
  source: LavaSrcSearchPlatformBase;
144
144
  types?: LavaSearchType[];
145
- }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
145
+ }, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult | LavaSearchResponse>;
146
146
  /**
147
147
  *
148
148
  * @param query Query for your data
@@ -151,7 +151,7 @@ export declare class Player {
151
151
  search(query: {
152
152
  query: string;
153
153
  source?: SearchPlatform;
154
- } | string, requestUser: unknown): Promise<SearchResult>;
154
+ } | string, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult>;
155
155
  /**
156
156
  * Pause the player
157
157
  */
@@ -1,3 +1,4 @@
1
+ import { bandCampSearch } from "./CustomSearches/BandCampSearch";
1
2
  import { FilterManager } from "./Filters";
2
3
  import { DefaultSources } from "./LavalinkManagerStatics";
3
4
  import { Queue, QueueSaver } from "./Queue";
@@ -258,6 +259,9 @@ export class Player {
258
259
  this.LavalinkManager.utils.validateQueryString(this.node, Query.source);
259
260
  else if (Query.source)
260
261
  this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
262
+ if (["bcsearch", "bandcamp"].includes(Query.source)) {
263
+ return await bandCampSearch(this, Query.query, requestUser);
264
+ }
261
265
  // ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
262
266
  // request the data
263
267
  const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
@@ -411,7 +415,6 @@ export class Player {
411
415
  const data = this.toJSON();
412
416
  await this.node.destroyPlayer(this.guildId);
413
417
  this.node = updateNode;
414
- await this.connect();
415
418
  const now = performance.now();
416
419
  await this.node.updatePlayer({
417
420
  guildId: this.guildId,
@@ -421,6 +424,8 @@ export class Player {
421
424
  volume: Math.round(Math.max(Math.min(data.volume, 1000), 0)),
422
425
  paused: data.paused,
423
426
  filters: { ...data.filters, equalizer: data.equalizer },
427
+ voice: this.voice,
428
+ // track: this.queue.current,
424
429
  },
425
430
  });
426
431
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
@@ -15,8 +15,10 @@ 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
17
  export type DuncteSearchPlatform = "speak" | "tts";
18
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
19
- export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
18
+ export type LavalinkClientSearchPlatform = "bcsearch";
19
+ export type LavalinkClientSearchPlatformResolve = "bandcamp";
20
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
21
+ export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
20
22
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
21
23
  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";
22
24
  export interface PlaylistInfo {
@@ -42,6 +44,13 @@ export interface SearchResult {
42
44
  playlist: PlaylistInfo | null;
43
45
  tracks: Track[];
44
46
  }
47
+ export interface UnresolvedSearchResult {
48
+ loadType: LoadTypes;
49
+ exception: Exception | null;
50
+ pluginInfo: PluginInfo;
51
+ playlist: PlaylistInfo | null;
52
+ tracks: UnresolvedTrack[];
53
+ }
45
54
  export declare class ManagerUtils {
46
55
  LavalinkManager: LavalinkManager | null;
47
56
  constructor(LavalinkManager?: LavalinkManager);
@@ -53,7 +53,7 @@ export class ManagerUtils {
53
53
  info: query.info ? query.info : query.title ? query : undefined,
54
54
  requester: typeof this.LavalinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.LavalinkManager?.options?.playerOptions?.requesterTransformer((query?.requester || requester)) : requester,
55
55
  async resolve(player) {
56
- const closest = await getClosestTrack(this, player, player.LavalinkManager.utils);
56
+ const closest = await getClosestTrack(this, player);
57
57
  if (!closest)
58
58
  throw new SyntaxError("No closest Track found");
59
59
  Object.getOwnPropertyNames(this).forEach(prop => delete this[prop]);
@@ -145,7 +145,7 @@ export class ManagerUtils {
145
145
  return typeof data === "object" && !("info" in data) && typeof data.title === "string";
146
146
  }
147
147
  async getClosestTrack(data, player) {
148
- return getClosestTrack(data, player, this);
148
+ return getClosestTrack(data, player);
149
149
  }
150
150
  validateQueryString(node, queryString) {
151
151
  if (!node.info)
@@ -309,12 +309,12 @@ async function applyUnresolvedData(resTrack, data, utils) {
309
309
  resTrack.info[key] = data.info[key]; // add non-existing values
310
310
  return resTrack;
311
311
  }
312
- async function getClosestTrack(data, player, utils) {
312
+ async function getClosestTrack(data, player) {
313
313
  if (!player || !player.node)
314
314
  throw new RangeError("No player with a lavalink node was provided");
315
- if (utils.isTrack(data))
316
- return utils.buildTrack(data, data.requester);
317
- if (!utils.isUnresolvedTrack(data))
315
+ if (player.LavalinkManager.utils.isTrack(data))
316
+ return player.LavalinkManager.utils.buildTrack(data, data.requester);
317
+ if (!player.LavalinkManager.utils.isUnresolvedTrack(data))
318
318
  throw new RangeError("Track is not an unresolved Track");
319
319
  if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
320
320
  throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
@@ -324,31 +324,31 @@ async function getClosestTrack(data, player, utils) {
324
324
  if (typeof data.encoded === "string") {
325
325
  const r = await player.node.decode.singleTrack(data.encoded, data.requester);
326
326
  if (r)
327
- return applyUnresolvedData(r, data, utils);
327
+ return applyUnresolvedData(r, data, player.LavalinkManager.utils);
328
328
  }
329
329
  // try to fetch the track via a uri if possible
330
330
  if (typeof data.info.uri === "string") {
331
- const r = await player.search({ query: data?.info?.uri }, data.requester).then(v => v.tracks[0]);
331
+ const r = await player.search({ query: data?.info?.uri }, data.requester).then(v => v.tracks?.[0]);
332
332
  if (r)
333
- return applyUnresolvedData(r, data, utils);
333
+ return applyUnresolvedData(r, data, player.LavalinkManager.utils);
334
334
  }
335
335
  // search the track as closely as possible
336
336
  const query = [data.info?.title, data.info?.author].filter(str => !!str).join(" by ");
337
337
  const sourceName = data.info?.sourceName;
338
338
  return await player.search({
339
- query, source: sourceName !== "bandcamp" && sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform,
340
- }, data.requester).then(res => {
339
+ query, source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform,
340
+ }, data.requester).then((res) => {
341
341
  let trackToUse = null;
342
342
  // try to find via author name
343
343
  if (data.info.author && !trackToUse)
344
- trackToUse = res.tracks.find(track => [data.info.author, `${data.info.author} - Topic`].some(name => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author)) || new RegExp(`^${escapeRegExp(data.info.title)}$`, "i").test(track.info.title));
344
+ trackToUse = res.tracks.find(track => [data.info?.author || "", `${data.info?.author} - Topic`].some(name => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)) || new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title));
345
345
  // try to find via duration
346
346
  if (data.info.duration && !trackToUse)
347
- trackToUse = res.tracks.find(track => (track.info.duration >= (data.info.duration - 1500)) && (track.info.duration <= (data.info.duration + 1500)));
347
+ trackToUse = res.tracks.find(track => (track.info?.duration >= (data.info?.duration - 1500)) && (track?.info.duration <= (data.info?.duration + 1500)));
348
348
  // try to find via isrc
349
349
  if (data.info.isrc && !trackToUse)
350
- trackToUse = res.tracks.find(track => track.info.isrc === data.info.isrc);
350
+ trackToUse = res.tracks.find(track => track.info?.isrc === data.info?.isrc);
351
351
  // apply unresolved data and return the track
352
- return applyUnresolvedData(trackToUse || res.tracks[0], data, utils);
352
+ return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
353
353
  });
354
354
  }
@@ -0,0 +1,3 @@
1
+ import { Player } from "../Player";
2
+ import { UnresolvedSearchResult } from "../Utils";
3
+ export declare const bandCampSearch: (player: Player, query: string, requestUser: unknown) => Promise<UnresolvedSearchResult>;
@@ -95,11 +95,11 @@ export declare class FilterManager {
95
95
  toggleEcho(delay?: number, decay?: number): Promise<boolean>;
96
96
  /**
97
97
  * Enabels / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
98
- * @param delay
99
- * @param decay
98
+ * @param delays
99
+ * @param gains
100
100
  * @returns
101
101
  */
102
- toggleReverb(delay?: number, decay?: number): Promise<boolean>;
102
+ toggleReverb(delays?: number[], gains?: number[]): Promise<boolean>;
103
103
  /**
104
104
  * Enables / Disabels a Nightcore-like filter Effect. Disables/Overwrides both: custom and Vaporwave Filter
105
105
  * @param speed
@@ -276,8 +276,8 @@ export interface EchoFilter {
276
276
  * A Plugin Filter
277
277
  */
278
278
  export interface ReverbFilter {
279
- delay: number;
280
- decay: number;
279
+ delays: number[];
280
+ gains: number[];
281
281
  }
282
282
  /**
283
283
  * Filter Data stored in the Client and partially sent to Lavalink
@@ -292,6 +292,13 @@ export interface FilterData {
292
292
  distortion?: DistortionFilter;
293
293
  channelMix?: ChannelMixFilter;
294
294
  lowPass?: LowPassFilter;
295
+ pluginFilters?: Record<PluginFiltersKey, PluginFiltersValues>;
296
+ }
297
+ export type PluginFiltersKey = "lavalink-filter-plugin" | string;
298
+ export interface PluginFiltersValues extends LavalinkFiltersPlugin {
299
+ [key: string]: any;
300
+ }
301
+ export interface LavalinkFiltersPlugin {
295
302
  echo: EchoFilter;
296
303
  reverb: ReverbFilter;
297
304
  }
@@ -2,7 +2,7 @@
2
2
  import { Dispatcher, Pool } from "undici";
3
3
  import { NodeManager } from "./NodeManager";
4
4
  import internal from "stream";
5
- import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64 } from "./Utils";
5
+ import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64, SearchResult } from "./Utils";
6
6
  import { DestroyReasonsType } from "./Player";
7
7
  import { Track } from "./Track";
8
8
  /** Modifies any outgoing REST requests. */
@@ -135,6 +135,7 @@ export declare class LavalinkNode {
135
135
  * @returns The returned data
136
136
  */
137
137
  request(endpoint: string, modify?: ModifyRequest, parseAsText?: boolean): Promise<unknown>;
138
+ search(querySourceString: string, requestUser: unknown): Promise<SearchResult>;
138
139
  /**
139
140
  * Update the Player State on the Lavalink Server
140
141
  * @param data
@@ -142,7 +142,7 @@ export declare class Player {
142
142
  query: string;
143
143
  source: LavaSrcSearchPlatformBase;
144
144
  types?: LavaSearchType[];
145
- }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
145
+ }, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult | LavaSearchResponse>;
146
146
  /**
147
147
  *
148
148
  * @param query Query for your data
@@ -151,7 +151,7 @@ export declare class Player {
151
151
  search(query: {
152
152
  query: string;
153
153
  source?: SearchPlatform;
154
- } | string, requestUser: unknown): Promise<SearchResult>;
154
+ } | string, requestUser: unknown): Promise<import("./Utils").UnresolvedSearchResult | SearchResult>;
155
155
  /**
156
156
  * Pause the player
157
157
  */
@@ -15,8 +15,10 @@ 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
17
  export type DuncteSearchPlatform = "speak" | "tts";
18
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
19
- export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
18
+ export type LavalinkClientSearchPlatform = "bcsearch";
19
+ export type LavalinkClientSearchPlatformResolve = "bandcamp";
20
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
21
+ export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
20
22
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
21
23
  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";
22
24
  export interface PlaylistInfo {
@@ -42,6 +44,13 @@ export interface SearchResult {
42
44
  playlist: PlaylistInfo | null;
43
45
  tracks: Track[];
44
46
  }
47
+ export interface UnresolvedSearchResult {
48
+ loadType: LoadTypes;
49
+ exception: Exception | null;
50
+ pluginInfo: PluginInfo;
51
+ playlist: PlaylistInfo | null;
52
+ tracks: UnresolvedTrack[];
53
+ }
45
54
  export declare class ManagerUtils {
46
55
  LavalinkManager: LavalinkManager | null;
47
56
  constructor(LavalinkManager?: LavalinkManager);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
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",