lavalink-client 2.1.6 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +11 -0
  2. package/dist/cjs/structures/CustomSearches/BandCampSearch.js +3 -2
  3. package/dist/cjs/structures/LavalinkManager.d.ts +5 -4
  4. package/dist/cjs/structures/LavalinkManager.js +1 -0
  5. package/dist/cjs/structures/LavalinkManagerStatics.js +5 -1
  6. package/dist/cjs/structures/Node.d.ts +10 -13
  7. package/dist/cjs/structures/Node.js +55 -56
  8. package/dist/cjs/structures/NodeManager.d.ts +1 -1
  9. package/dist/cjs/structures/Player.js +10 -10
  10. package/dist/cjs/structures/Queue.js +1 -1
  11. package/dist/cjs/structures/Track.d.ts +2 -0
  12. package/dist/cjs/structures/Utils.d.ts +12 -7
  13. package/dist/cjs/structures/Utils.js +6 -4
  14. package/dist/esm/structures/CustomSearches/BandCampSearch.js +2 -1
  15. package/dist/esm/structures/LavalinkManager.d.ts +5 -4
  16. package/dist/esm/structures/LavalinkManager.js +1 -0
  17. package/dist/esm/structures/LavalinkManagerStatics.js +5 -1
  18. package/dist/esm/structures/Node.d.ts +10 -13
  19. package/dist/esm/structures/Node.js +55 -56
  20. package/dist/esm/structures/NodeManager.d.ts +1 -1
  21. package/dist/esm/structures/Player.js +10 -10
  22. package/dist/esm/structures/Queue.js +1 -1
  23. package/dist/esm/structures/Track.d.ts +2 -0
  24. package/dist/esm/structures/Utils.d.ts +12 -7
  25. package/dist/esm/structures/Utils.js +6 -4
  26. package/dist/types/structures/LavalinkManager.d.ts +5 -4
  27. package/dist/types/structures/Node.d.ts +10 -13
  28. package/dist/types/structures/NodeManager.d.ts +1 -1
  29. package/dist/types/structures/Track.d.ts +2 -0
  30. package/dist/types/structures/Utils.d.ts +12 -7
  31. package/package.json +4 -2
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
@@ -143,12 +139,13 @@ class LavalinkNode {
143
139
  exception: res.loadType === "error" ? res.data : null,
144
140
  pluginInfo: res.pluginInfo || {},
145
141
  playlist: res.loadType === "playlist" ? {
142
+ name: res.data.info?.name || res.data.pluginInfo?.name || null,
146
143
  title: res.data.info?.name || res.data.pluginInfo?.name || null,
147
144
  author: res.data.info?.author || res.data.pluginInfo?.author || null,
148
145
  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,
149
146
  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,
150
147
  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,
151
- duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || 0), 0) : 0,
148
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.length || 0), 0) : 0,
152
149
  } : null,
153
150
  tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
154
151
  };
@@ -168,7 +165,7 @@ class LavalinkNode {
168
165
  const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
169
166
  if (throwOnEmpty === true)
170
167
  throw new Error("Nothing found");
171
- const res = (request.statusCode === 204 ? {} : await request.body.json());
168
+ const res = (request.status === 204 ? {} : await request.json());
172
169
  return {
173
170
  tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
174
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)) })) || [],
@@ -193,7 +190,7 @@ class LavalinkNode {
193
190
  r.headers["Content-Type"] = "application/json";
194
191
  r.body = JSON.stringify(data.playerOptions);
195
192
  if (data.noReplace) {
196
- const url = new URL(`${this.poolAddress}${r.path}`);
193
+ const url = new URL(`${this.restAddress}${r.path}`);
197
194
  url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
198
195
  r.path = url.pathname + url.search;
199
196
  }
@@ -474,7 +471,7 @@ class LavalinkNode {
474
471
  }
475
472
  return true;
476
473
  }
477
- get poolAddress() {
474
+ get restAddress() {
478
475
  return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
479
476
  }
480
477
  reconnect(instaReconnect = false) {
@@ -509,9 +506,9 @@ class LavalinkNode {
509
506
  clearTimeout(this.reconnectTimeout);
510
507
  // reset the reconnect attempts amount
511
508
  this.reconnectAttempts = 1;
512
- this.info = await this.fetchInfo().catch(() => null);
509
+ this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
513
510
  if (!this.info && ["v3", "v4"].includes(this.version)) {
514
- 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`;
515
512
  throw new Error(errorString);
516
513
  }
517
514
  this.NodeManager.emit("connect", this);
@@ -541,48 +538,50 @@ class LavalinkNode {
541
538
  this.stats = { ...payload };
542
539
  break;
543
540
  case "playerUpdate":
544
- const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
545
- if (!player)
546
- return;
547
- const oldPlayer = player?.toJSON();
548
- if (player.get("internal_updateInterval"))
549
- clearInterval(player.get("internal_updateInterval"));
550
- // override the position
551
- player.position = payload.state.position || 0;
552
- player.lastPosition = payload.state.position || 0;
553
- player.connected = payload.state.connected;
554
- player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
555
- if (!player.createdTimeStamp && payload.state.time)
556
- player.createdTimeStamp = payload.state.time;
557
- if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
558
- player.set("internal_updateInterval", setInterval(() => {
559
- player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
560
- if (player.filterManager.filterUpdatedState >= 1) {
561
- player.filterManager.filterUpdatedState++;
562
- const maxMins = 8;
563
- const currentDuration = player.queue.current?.info?.duration || 0;
564
- if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri)) {
565
- if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
541
+ {
542
+ const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
543
+ if (!player)
544
+ return;
545
+ const oldPlayer = player?.toJSON();
546
+ if (player.get("internal_updateInterval"))
547
+ clearInterval(player.get("internal_updateInterval"));
548
+ // override the position
549
+ player.position = payload.state.position || 0;
550
+ player.lastPosition = payload.state.position || 0;
551
+ player.connected = payload.state.connected;
552
+ player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
553
+ if (!player.createdTimeStamp && payload.state.time)
554
+ player.createdTimeStamp = payload.state.time;
555
+ if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
556
+ player.set("internal_updateInterval", setInterval(() => {
557
+ player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
558
+ if (player.filterManager.filterUpdatedState >= 1) {
559
+ player.filterManager.filterUpdatedState++;
560
+ const maxMins = 8;
561
+ const currentDuration = player.queue.current?.info?.duration || 0;
562
+ if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri)) {
563
+ if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
564
+ player.filterManager.filterUpdatedState = 0;
565
+ player.seek(player.position);
566
+ }
567
+ }
568
+ else {
566
569
  player.filterManager.filterUpdatedState = 0;
567
- player.seek(player.position);
568
570
  }
569
571
  }
570
- else {
571
- player.filterManager.filterUpdatedState = 0;
572
- }
572
+ }, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
573
+ }
574
+ else {
575
+ if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
576
+ const maxMins = 8;
577
+ const currentDuration = player.queue.current?.info?.duration || 0;
578
+ if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))
579
+ player.seek(player.position);
580
+ player.filterManager.filterUpdatedState = 0;
573
581
  }
574
- }, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
575
- }
576
- else {
577
- if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
578
- const maxMins = 8;
579
- const currentDuration = player.queue.current?.info?.duration || 0;
580
- if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))
581
- player.seek(player.position);
582
- player.filterManager.filterUpdatedState = 0;
583
582
  }
583
+ this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
584
584
  }
585
- this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
586
585
  break;
587
586
  case "event":
588
587
  this.handleEvent(payload);
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
5
5
  import { DestroyReasonsType } from "./Player";
6
6
  import { LavalinkPlayer, MiniMap } from "./Utils";
7
7
  type LavalinkNodeIdentifier = string;
8
- interface NodeManagerEvents {
8
+ export interface NodeManagerEvents {
9
9
  /**
10
10
  * Emitted when a Node is created.
11
11
  * @event Manager.nodeManager#create
@@ -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);
@@ -152,10 +152,10 @@ class Player {
152
152
  // handle play identifier options manually // TODO let it resolve by lavalink!
153
153
  const res = await this.search({
154
154
  query: options?.track?.identifier
155
- }, options?.track?.identifier || 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 || {}) };
155
+ }, options?.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
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)))
@@ -55,6 +55,8 @@ export interface PluginInfo {
55
55
  type?: "album" | "playlist" | "artist" | "recommendations" | string;
56
56
  /** The Identifier provided by a plugin */
57
57
  albumName?: string;
58
+ /** The url of the album */
59
+ albumUrl?: string;
58
60
  /** The url of the album art */
59
61
  albumArtUrl?: string;
60
62
  /** The url of the artist */
@@ -14,23 +14,23 @@ export type IntegerNumber = Opaque<number, 'Int'>;
14
14
  export type FloatNumber = Opaque<number, 'Float'>;
15
15
  export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
16
16
  export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
17
- export type DuncteSearchPlatform = "speak" | "tts";
17
+ export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
18
18
  export type LavalinkClientSearchPlatform = "bcsearch";
19
19
  export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
20
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
20
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
21
21
  export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
22
22
  export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
23
23
  "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
24
24
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
25
25
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
26
26
  export interface PlaylistInfo {
27
- /** The playlist title. */
28
- title: string;
29
- /** The playlist name (if provided instead of title) */
27
+ /** The playlist name */
30
28
  name: string;
31
- /** The Playlist Author */
29
+ /** The playlist title (same as name) */
30
+ title: string;
31
+ /** The playlist Author */
32
32
  author?: string;
33
- /** The Playlist Thumbnail */
33
+ /** The playlist Thumbnail */
34
34
  thumbnail?: string;
35
35
  /** A Uri to the playlist */
36
36
  uri?: string;
@@ -168,8 +168,13 @@ export declare class MiniMap<K, V> extends Map<K, V> {
168
168
  export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;
169
169
  export type Severity = "COMMON" | "SUSPICIOUS" | "FAULT";
170
170
  export interface Exception {
171
+ /** Severity of the error */
171
172
  severity: Severity;
173
+ /** Nodejs Error */
174
+ error?: Error;
175
+ /** Message by lavalink */
172
176
  message: string;
177
+ /** Cause by lavalink */
173
178
  cause: string;
174
179
  }
175
180
  export interface PlayerEvent {
@@ -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,