lavalink-client 2.1.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/cjs/structures/CustomSearches/BandCampSearch.js +3 -2
- package/dist/cjs/structures/LavalinkManager.d.ts +5 -4
- package/dist/cjs/structures/LavalinkManager.js +1 -0
- package/dist/cjs/structures/LavalinkManagerStatics.js +5 -1
- package/dist/cjs/structures/Node.d.ts +10 -13
- package/dist/cjs/structures/Node.js +15 -19
- package/dist/cjs/structures/NodeManager.d.ts +1 -1
- package/dist/cjs/structures/Player.js +9 -9
- package/dist/cjs/structures/Queue.js +1 -1
- package/dist/cjs/structures/Utils.d.ts +2 -2
- package/dist/cjs/structures/Utils.js +6 -4
- package/dist/esm/structures/CustomSearches/BandCampSearch.js +2 -1
- package/dist/esm/structures/LavalinkManager.d.ts +5 -4
- package/dist/esm/structures/LavalinkManager.js +1 -0
- package/dist/esm/structures/LavalinkManagerStatics.js +5 -1
- package/dist/esm/structures/Node.d.ts +10 -13
- package/dist/esm/structures/Node.js +15 -19
- package/dist/esm/structures/NodeManager.d.ts +1 -1
- package/dist/esm/structures/Player.js +9 -9
- package/dist/esm/structures/Queue.js +1 -1
- package/dist/esm/structures/Utils.d.ts +2 -2
- package/dist/esm/structures/Utils.js +6 -4
- package/dist/types/structures/LavalinkManager.d.ts +5 -4
- package/dist/types/structures/Node.d.ts +10 -13
- package/dist/types/structures/NodeManager.d.ts +1 -1
- package/dist/types/structures/Utils.d.ts +2 -2
- 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
|
|
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
|
-
/**
|
|
9
|
-
export type ModifyRequest = (options:
|
|
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
|
-
/**
|
|
34
|
-
|
|
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<
|
|
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<
|
|
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<
|
|
266
|
+
unmarkAllFailedAddresses: () => Promise<any>;
|
|
270
267
|
};
|
|
271
268
|
/** Private Utils */
|
|
272
269
|
private validate;
|
|
273
270
|
private syncPlayerData;
|
|
274
|
-
private get
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
87
|
+
const url = new URL(`${this.restAddress}${options.path}`);
|
|
92
88
|
url.searchParams.append("trace", "true");
|
|
93
|
-
options.path
|
|
94
|
-
const request = await
|
|
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.
|
|
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.
|
|
106
|
+
return parseAsText ? await request.text() : await request.json();
|
|
111
107
|
}
|
|
112
108
|
/**
|
|
113
109
|
* Search something raw on the node, please note only add tracks to players of that node
|
|
@@ -120,8 +116,8 @@ class LavalinkNode {
|
|
|
120
116
|
this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query, Query.source);
|
|
121
117
|
if (Query.source)
|
|
122
118
|
this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
|
|
123
|
-
if (["bcsearch", "bandcamp"].includes(Query.source)) {
|
|
124
|
-
throw new Error("Bandcamp Search only works on the player!");
|
|
119
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.info.sourceManagers.includes("bandcamp")) {
|
|
120
|
+
throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
|
|
125
121
|
}
|
|
126
122
|
let uri = `/loadtracks?identifier=`;
|
|
127
123
|
if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
|
|
@@ -169,7 +165,7 @@ class LavalinkNode {
|
|
|
169
165
|
const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
|
|
170
166
|
if (throwOnEmpty === true)
|
|
171
167
|
throw new Error("Nothing found");
|
|
172
|
-
const res = (request.
|
|
168
|
+
const res = (request.status === 204 ? {} : await request.json());
|
|
173
169
|
return {
|
|
174
170
|
tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
|
|
175
171
|
albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
|
|
@@ -194,7 +190,7 @@ class LavalinkNode {
|
|
|
194
190
|
r.headers["Content-Type"] = "application/json";
|
|
195
191
|
r.body = JSON.stringify(data.playerOptions);
|
|
196
192
|
if (data.noReplace) {
|
|
197
|
-
const url = new URL(`${this.
|
|
193
|
+
const url = new URL(`${this.restAddress}${r.path}`);
|
|
198
194
|
url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
|
|
199
195
|
r.path = url.pathname + url.search;
|
|
200
196
|
}
|
|
@@ -475,7 +471,7 @@ class LavalinkNode {
|
|
|
475
471
|
}
|
|
476
472
|
return true;
|
|
477
473
|
}
|
|
478
|
-
get
|
|
474
|
+
get restAddress() {
|
|
479
475
|
return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
|
|
480
476
|
}
|
|
481
477
|
reconnect(instaReconnect = false) {
|
|
@@ -510,9 +506,9 @@ class LavalinkNode {
|
|
|
510
506
|
clearTimeout(this.reconnectTimeout);
|
|
511
507
|
// reset the reconnect attempts amount
|
|
512
508
|
this.reconnectAttempts = 1;
|
|
513
|
-
this.info = await this.fetchInfo().catch(() => null);
|
|
509
|
+
this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
|
|
514
510
|
if (!this.info && ["v3", "v4"].includes(this.version)) {
|
|
515
|
-
const errorString = `Lavalink Node (${this.
|
|
511
|
+
const errorString = `Lavalink Node (${this.restAddress}) does not provide any /${this.version}/info`;
|
|
516
512
|
throw new Error(errorString);
|
|
517
513
|
}
|
|
518
514
|
this.NodeManager.emit("connect", this);
|
|
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
|
|
|
5
5
|
import { DestroyReasonsType } from "./Player";
|
|
6
6
|
import { LavalinkPlayer, MiniMap } from "./Utils";
|
|
7
7
|
type LavalinkNodeIdentifier = string;
|
|
8
|
-
interface NodeManagerEvents {
|
|
8
|
+
export interface NodeManagerEvents {
|
|
9
9
|
/**
|
|
10
10
|
* Emitted when a Node is created.
|
|
11
11
|
* @event Manager.nodeManager#create
|
|
@@ -132,17 +132,17 @@ class Player {
|
|
|
132
132
|
if (options?.clientTrack && (this.LavalinkManager.utils.isTrack(options?.clientTrack) || this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
|
|
133
133
|
if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))
|
|
134
134
|
await options.clientTrack.resolve(this);
|
|
135
|
-
if (typeof options.track.userData === "object")
|
|
136
|
-
options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track
|
|
135
|
+
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
136
|
+
options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
|
|
137
137
|
await this.queue.add(options?.clientTrack, 0);
|
|
138
138
|
return await this.skip();
|
|
139
139
|
}
|
|
140
140
|
else if (options?.track?.encoded) {
|
|
141
141
|
// handle play encoded options manually // TODO let it resolve by lavalink!
|
|
142
142
|
const track = await this.node.decode.singleTrack(options.track?.encoded, options.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
|
|
143
|
-
if (typeof options.track.userData === "object")
|
|
144
|
-
track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
|
|
145
143
|
if (track) {
|
|
144
|
+
if (typeof options.track?.userData === "object")
|
|
145
|
+
track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
|
|
146
146
|
replaced = true;
|
|
147
147
|
this.queue.add(track, 0);
|
|
148
148
|
await (0, Utils_1.queueTrackEnd)(this);
|
|
@@ -153,9 +153,9 @@ class Player {
|
|
|
153
153
|
const res = await this.search({
|
|
154
154
|
query: options?.track?.identifier
|
|
155
155
|
}, options?.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
|
|
156
|
-
if (typeof options.track.userData === "object")
|
|
157
|
-
res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
|
|
158
156
|
if (res.tracks[0]) {
|
|
157
|
+
if (typeof options.track?.userData === "object")
|
|
158
|
+
res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
|
|
159
159
|
replaced = true;
|
|
160
160
|
this.queue.add(res.tracks[0], 0);
|
|
161
161
|
await (0, Utils_1.queueTrackEnd)(this);
|
|
@@ -167,8 +167,8 @@ class Player {
|
|
|
167
167
|
try {
|
|
168
168
|
// resolve the unresolved track
|
|
169
169
|
await this.queue.current.resolve(this);
|
|
170
|
-
if (typeof options.track
|
|
171
|
-
this.queue.current.userData = { ...(this.queue.current
|
|
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
|
-
|
|
84
|
+
throw new Error(`No data found to sync for guildId: ${this.guildId}`);
|
|
85
85
|
if (!dontSyncCurrent && !this.current && (this.managerUtils.isTrack(data.current)))
|
|
86
86
|
this.current = data.current;
|
|
87
87
|
if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(track => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
|
|
@@ -14,10 +14,10 @@ export type IntegerNumber = Opaque<number, 'Int'>;
|
|
|
14
14
|
export type FloatNumber = Opaque<number, 'Float'>;
|
|
15
15
|
export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
|
|
16
16
|
export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
|
|
17
|
-
export type DuncteSearchPlatform = "speak" | "tts";
|
|
17
|
+
export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
|
|
18
18
|
export type LavalinkClientSearchPlatform = "bcsearch";
|
|
19
19
|
export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
|
|
20
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
20
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
21
21
|
export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
|
|
22
22
|
export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
|
|
23
23
|
"youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
|
|
@@ -50,7 +50,7 @@ class ManagerUtils {
|
|
|
50
50
|
identifier: data.info.identifier,
|
|
51
51
|
title: data.info.title,
|
|
52
52
|
author: data.info.author,
|
|
53
|
-
duration: data.info.
|
|
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:
|
|
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:
|
|
260
|
+
source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
|
|
259
261
|
};
|
|
260
262
|
const foundSource = Object.keys(LavalinkManagerStatics_1.DefaultSources).find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
261
263
|
if (foundSource && LavalinkManagerStatics_1.DefaultSources[foundSource]) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { fetch } from "undici";
|
|
2
1
|
export const bandCampSearch = async (player, query, requestUser) => {
|
|
3
2
|
let error = null;
|
|
4
3
|
let tracks = [];
|
|
4
|
+
if (player.LavalinkManager.options.advancedOptions.debugOptions.logCustomSearches)
|
|
5
|
+
console.log(`Lavalink-Client-Debug | SEARCHING | - ${query} on lavalink-client`);
|
|
5
6
|
player.LavalinkManager.utils.validateQueryString(player.node, query);
|
|
6
7
|
try {
|
|
7
8
|
const data = await fetch(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
|
|
@@ -66,6 +66,8 @@ export interface ManagerOptions {
|
|
|
66
66
|
advancedOptions?: {
|
|
67
67
|
/** optional */
|
|
68
68
|
debugOptions?: {
|
|
69
|
+
/** For logging custom searches */
|
|
70
|
+
logCustomSearches?: boolean;
|
|
69
71
|
/** logs for debugging the "no-Audio" playing error */
|
|
70
72
|
noAudio?: boolean;
|
|
71
73
|
/** For Logging the Destroy function */
|
|
@@ -78,7 +80,7 @@ export interface ManagerOptions {
|
|
|
78
80
|
};
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
|
-
interface LavalinkManagerEvents {
|
|
83
|
+
export interface LavalinkManagerEvents {
|
|
82
84
|
/**
|
|
83
85
|
* Emitted when a Track started playing.
|
|
84
86
|
* @event Manager#trackStart
|
|
@@ -93,12 +95,12 @@ interface LavalinkManagerEvents {
|
|
|
93
95
|
* Emitted when a Track got stuck while playing.
|
|
94
96
|
* @event Manager#trackStuck
|
|
95
97
|
*/
|
|
96
|
-
"trackStuck": (player: Player, track: Track, payload: TrackStuckEvent) => void;
|
|
98
|
+
"trackStuck": (player: Player, track: Track | null, payload: TrackStuckEvent) => void;
|
|
97
99
|
/**
|
|
98
100
|
* Emitted when a Track errored.
|
|
99
101
|
* @event Manager#trackError
|
|
100
102
|
*/
|
|
101
|
-
"trackError": (player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent) => void;
|
|
103
|
+
"trackError": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackExceptionEvent) => void;
|
|
102
104
|
/**
|
|
103
105
|
* Emitted when the Playing finished and no more tracks in the queue.
|
|
104
106
|
* @event Manager#queueEnd
|
|
@@ -347,4 +349,3 @@ export declare class LavalinkManager extends EventEmitter {
|
|
|
347
349
|
*/
|
|
348
350
|
sendRawData(data: VoicePacket | VoiceServer | VoiceState | ChannelDeletePacket): Promise<void>;
|
|
349
351
|
}
|
|
350
|
-
export {};
|
|
@@ -56,6 +56,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
56
56
|
},
|
|
57
57
|
advancedOptions: {
|
|
58
58
|
debugOptions: {
|
|
59
|
+
logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
|
|
59
60
|
noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
|
|
60
61
|
playerDestroy: {
|
|
61
62
|
dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
|
|
@@ -47,10 +47,14 @@ export const DefaultSources = {
|
|
|
47
47
|
"flowery": "ftts",
|
|
48
48
|
"flowery.tts": "ftts",
|
|
49
49
|
"flowerytts": "ftts",
|
|
50
|
-
// Client sided search platforms
|
|
50
|
+
// Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
|
|
51
51
|
"bandcamp": "bcsearch",
|
|
52
52
|
"bc": "bcsearch",
|
|
53
53
|
"bcsearch": "bcsearch",
|
|
54
|
+
// other searches:
|
|
55
|
+
"phsearch": "phsearch",
|
|
56
|
+
"pornhub": "phsearch",
|
|
57
|
+
"porn": "phsearch",
|
|
54
58
|
// local files
|
|
55
59
|
"local": "local",
|
|
56
60
|
// http requests
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import internal from "stream";
|
|
3
|
-
import { Dispatcher, Pool } from "undici";
|
|
4
3
|
import { NodeManager } from "./NodeManager";
|
|
5
4
|
import { DestroyReasonsType, Player } from "./Player";
|
|
6
5
|
import { Track } from "./Track";
|
|
7
6
|
import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
|
|
8
|
-
/**
|
|
9
|
-
export type ModifyRequest = (options:
|
|
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
|
-
/**
|
|
34
|
-
|
|
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<
|
|
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<
|
|
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<
|
|
266
|
+
unmarkAllFailedAddresses: () => Promise<any>;
|
|
270
267
|
};
|
|
271
268
|
/** Private Utils */
|
|
272
269
|
private validate;
|
|
273
270
|
private syncPlayerData;
|
|
274
|
-
private get
|
|
271
|
+
private get restAddress();
|
|
275
272
|
private reconnect;
|
|
276
273
|
private open;
|
|
277
274
|
private close;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isAbsolute } from "path";
|
|
2
|
-
import { Pool } from "undici";
|
|
3
2
|
import WebSocket from "ws";
|
|
4
3
|
import { DestroyReasons } from "./Player";
|
|
5
4
|
import { NodeSymbol, queueTrackEnd } from "./Utils";
|
|
@@ -43,8 +42,6 @@ export class LavalinkNode {
|
|
|
43
42
|
reconnectAttempts = 1;
|
|
44
43
|
/** The Socket of the Lavalink */
|
|
45
44
|
socket = null;
|
|
46
|
-
/** The Rest Server for this Lavalink */
|
|
47
|
-
rest;
|
|
48
45
|
/** Version of what the Lavalink Server should be */
|
|
49
46
|
version = "v4";
|
|
50
47
|
/**
|
|
@@ -57,14 +54,13 @@ export class LavalinkNode {
|
|
|
57
54
|
secure: false,
|
|
58
55
|
retryAmount: 5,
|
|
59
56
|
retryDelay: 30e3,
|
|
60
|
-
|
|
57
|
+
requestSignalTimeoutMS: 10000,
|
|
61
58
|
...options
|
|
62
59
|
};
|
|
63
60
|
this.NodeManager = manager;
|
|
64
61
|
this.validate();
|
|
65
62
|
if (this.options.secure && this.options.port !== 443)
|
|
66
63
|
throw new SyntaxError("If secure is true, then the port must be 443");
|
|
67
|
-
this.rest = new Pool(this.poolAddress, this.options.poolOptions);
|
|
68
64
|
this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
|
|
69
65
|
Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
|
|
70
66
|
}
|
|
@@ -79,15 +75,15 @@ export class LavalinkNode {
|
|
|
79
75
|
path: `/${this.version}/${endpoint.replace(/^\//gm, "")}`,
|
|
80
76
|
method: "GET",
|
|
81
77
|
headers: {
|
|
82
|
-
Authorization: this.options.authorization
|
|
78
|
+
"Authorization": this.options.authorization
|
|
83
79
|
},
|
|
84
|
-
|
|
80
|
+
signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : undefined,
|
|
85
81
|
};
|
|
86
82
|
modify?.(options);
|
|
87
|
-
const url = new URL(`${this.
|
|
83
|
+
const url = new URL(`${this.restAddress}${options.path}`);
|
|
88
84
|
url.searchParams.append("trace", "true");
|
|
89
|
-
options.path
|
|
90
|
-
const request = await
|
|
85
|
+
delete options.path;
|
|
86
|
+
const request = await fetch(url.href, options);
|
|
91
87
|
this.calls++;
|
|
92
88
|
return { request, options };
|
|
93
89
|
}
|
|
@@ -101,9 +97,9 @@ export class LavalinkNode {
|
|
|
101
97
|
const { request, options } = await this.rawRequest(endpoint, modify);
|
|
102
98
|
if (["DELETE", "PUT"].includes(options.method))
|
|
103
99
|
return;
|
|
104
|
-
if (request.
|
|
100
|
+
if (request.status === 404)
|
|
105
101
|
throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${JSON.stringify(request.headers)}`);
|
|
106
|
-
return parseAsText ? await request.
|
|
102
|
+
return parseAsText ? await request.text() : await request.json();
|
|
107
103
|
}
|
|
108
104
|
/**
|
|
109
105
|
* Search something raw on the node, please note only add tracks to players of that node
|
|
@@ -116,8 +112,8 @@ export class LavalinkNode {
|
|
|
116
112
|
this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query, Query.source);
|
|
117
113
|
if (Query.source)
|
|
118
114
|
this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
|
|
119
|
-
if (["bcsearch", "bandcamp"].includes(Query.source)) {
|
|
120
|
-
throw new Error("Bandcamp Search only works on the player!");
|
|
115
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.info.sourceManagers.includes("bandcamp")) {
|
|
116
|
+
throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
|
|
121
117
|
}
|
|
122
118
|
let uri = `/loadtracks?identifier=`;
|
|
123
119
|
if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
|
|
@@ -165,7 +161,7 @@ export class LavalinkNode {
|
|
|
165
161
|
const { request } = await this.rawRequest(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
|
|
166
162
|
if (throwOnEmpty === true)
|
|
167
163
|
throw new Error("Nothing found");
|
|
168
|
-
const res = (request.
|
|
164
|
+
const res = (request.status === 204 ? {} : await request.json());
|
|
169
165
|
return {
|
|
170
166
|
tracks: res.tracks?.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
|
|
171
167
|
albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.NodeManager.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
|
|
@@ -190,7 +186,7 @@ export class LavalinkNode {
|
|
|
190
186
|
r.headers["Content-Type"] = "application/json";
|
|
191
187
|
r.body = JSON.stringify(data.playerOptions);
|
|
192
188
|
if (data.noReplace) {
|
|
193
|
-
const url = new URL(`${this.
|
|
189
|
+
const url = new URL(`${this.restAddress}${r.path}`);
|
|
194
190
|
url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
|
|
195
191
|
r.path = url.pathname + url.search;
|
|
196
192
|
}
|
|
@@ -471,7 +467,7 @@ export class LavalinkNode {
|
|
|
471
467
|
}
|
|
472
468
|
return true;
|
|
473
469
|
}
|
|
474
|
-
get
|
|
470
|
+
get restAddress() {
|
|
475
471
|
return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
|
|
476
472
|
}
|
|
477
473
|
reconnect(instaReconnect = false) {
|
|
@@ -506,9 +502,9 @@ export class LavalinkNode {
|
|
|
506
502
|
clearTimeout(this.reconnectTimeout);
|
|
507
503
|
// reset the reconnect attempts amount
|
|
508
504
|
this.reconnectAttempts = 1;
|
|
509
|
-
this.info = await this.fetchInfo().catch(() => null);
|
|
505
|
+
this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
|
|
510
506
|
if (!this.info && ["v3", "v4"].includes(this.version)) {
|
|
511
|
-
const errorString = `Lavalink Node (${this.
|
|
507
|
+
const errorString = `Lavalink Node (${this.restAddress}) does not provide any /${this.version}/info`;
|
|
512
508
|
throw new Error(errorString);
|
|
513
509
|
}
|
|
514
510
|
this.NodeManager.emit("connect", this);
|
|
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
|
|
|
5
5
|
import { DestroyReasonsType } from "./Player";
|
|
6
6
|
import { LavalinkPlayer, MiniMap } from "./Utils";
|
|
7
7
|
type LavalinkNodeIdentifier = string;
|
|
8
|
-
interface NodeManagerEvents {
|
|
8
|
+
export interface NodeManagerEvents {
|
|
9
9
|
/**
|
|
10
10
|
* Emitted when a Node is created.
|
|
11
11
|
* @event Manager.nodeManager#create
|
|
@@ -129,17 +129,17 @@ export class Player {
|
|
|
129
129
|
if (options?.clientTrack && (this.LavalinkManager.utils.isTrack(options?.clientTrack) || this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
|
|
130
130
|
if (this.LavalinkManager.utils.isUnresolvedTrack(options.clientTrack))
|
|
131
131
|
await options.clientTrack.resolve(this);
|
|
132
|
-
if (typeof options.track.userData === "object")
|
|
133
|
-
options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track
|
|
132
|
+
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
133
|
+
options.clientTrack.userData = { ...(options?.clientTrack.userData || {}), ...(options.track?.userData || {}) };
|
|
134
134
|
await this.queue.add(options?.clientTrack, 0);
|
|
135
135
|
return await this.skip();
|
|
136
136
|
}
|
|
137
137
|
else if (options?.track?.encoded) {
|
|
138
138
|
// handle play encoded options manually // TODO let it resolve by lavalink!
|
|
139
139
|
const track = await this.node.decode.singleTrack(options.track?.encoded, options.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
|
|
140
|
-
if (typeof options.track.userData === "object")
|
|
141
|
-
track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
|
|
142
140
|
if (track) {
|
|
141
|
+
if (typeof options.track?.userData === "object")
|
|
142
|
+
track.userData = { ...(track.userData || {}), ...(options.track.userData || {}) };
|
|
143
143
|
replaced = true;
|
|
144
144
|
this.queue.add(track, 0);
|
|
145
145
|
await queueTrackEnd(this);
|
|
@@ -150,9 +150,9 @@ export class Player {
|
|
|
150
150
|
const res = await this.search({
|
|
151
151
|
query: options?.track?.identifier
|
|
152
152
|
}, options?.track?.requester || this.queue?.current?.requester || this.queue.previous?.[0]?.requester || this.queue.tracks?.[0]?.requester || this.LavalinkManager.options.client);
|
|
153
|
-
if (typeof options.track.userData === "object")
|
|
154
|
-
res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
|
|
155
153
|
if (res.tracks[0]) {
|
|
154
|
+
if (typeof options.track?.userData === "object")
|
|
155
|
+
res.tracks[0].userData = { ...(res.tracks[0].userData || {}), ...(options.track.userData || {}) };
|
|
156
156
|
replaced = true;
|
|
157
157
|
this.queue.add(res.tracks[0], 0);
|
|
158
158
|
await queueTrackEnd(this);
|
|
@@ -164,8 +164,8 @@ export class Player {
|
|
|
164
164
|
try {
|
|
165
165
|
// resolve the unresolved track
|
|
166
166
|
await this.queue.current.resolve(this);
|
|
167
|
-
if (typeof options.track
|
|
168
|
-
this.queue.current.userData = { ...(this.queue.current
|
|
167
|
+
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
168
|
+
this.queue.current.userData = { ...(this.queue.current?.userData || {}), ...(options.track?.userData || {}) };
|
|
169
169
|
}
|
|
170
170
|
catch (error) {
|
|
171
171
|
this.LavalinkManager.emit("trackError", this, this.queue.current, error);
|
|
@@ -261,7 +261,7 @@ export class Player {
|
|
|
261
261
|
*/
|
|
262
262
|
async search(query, requestUser) {
|
|
263
263
|
const Query = this.LavalinkManager.utils.transformQuery(query);
|
|
264
|
-
if (["bcsearch", "bandcamp"].includes(Query.source))
|
|
264
|
+
if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info.sourceManagers.includes("bandcamp"))
|
|
265
265
|
return await bandCampSearch(this, Query.query, requestUser);
|
|
266
266
|
return this.node.search(Query, requestUser);
|
|
267
267
|
}
|
|
@@ -76,7 +76,7 @@ export class Queue {
|
|
|
76
76
|
sync: async (override = true, dontSyncCurrent = true) => {
|
|
77
77
|
const data = await this.QueueSaver.get(this.guildId);
|
|
78
78
|
if (!data)
|
|
79
|
-
|
|
79
|
+
throw new Error(`No data found to sync for guildId: ${this.guildId}`);
|
|
80
80
|
if (!dontSyncCurrent && !this.current && (this.managerUtils.isTrack(data.current)))
|
|
81
81
|
this.current = data.current;
|
|
82
82
|
if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some(track => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
|
|
@@ -14,10 +14,10 @@ export type IntegerNumber = Opaque<number, 'Int'>;
|
|
|
14
14
|
export type FloatNumber = Opaque<number, 'Float'>;
|
|
15
15
|
export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
|
|
16
16
|
export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
|
|
17
|
-
export type DuncteSearchPlatform = "speak" | "tts";
|
|
17
|
+
export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
|
|
18
18
|
export type LavalinkClientSearchPlatform = "bcsearch";
|
|
19
19
|
export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
|
|
20
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
20
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
21
21
|
export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
|
|
22
22
|
export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
|
|
23
23
|
"youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
|
|
@@ -46,7 +46,7 @@ export class ManagerUtils {
|
|
|
46
46
|
identifier: data.info.identifier,
|
|
47
47
|
title: data.info.title,
|
|
48
48
|
author: data.info.author,
|
|
49
|
-
duration: data.info.
|
|
49
|
+
duration: data.info.duration || data.info.length,
|
|
50
50
|
artworkUrl: data.info.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
|
|
51
51
|
uri: data.info.uri,
|
|
52
52
|
sourceName: data.info.sourceName,
|
|
@@ -202,7 +202,7 @@ export class ManagerUtils {
|
|
|
202
202
|
throw new Error("Lavalink Node has not 'soundcloud' enabled");
|
|
203
203
|
}
|
|
204
204
|
if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
|
|
205
|
-
throw new Error("Lavalink Node has not 'bandcamp' enabled");
|
|
205
|
+
throw new Error("Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
|
|
206
206
|
}
|
|
207
207
|
if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
|
|
208
208
|
throw new Error("Lavalink Node has not 'twitch' enabled");
|
|
@@ -234,9 +234,10 @@ export class ManagerUtils {
|
|
|
234
234
|
return;
|
|
235
235
|
}
|
|
236
236
|
transformQuery(query) {
|
|
237
|
+
const sourceOfQuery = typeof query === "string" ? undefined : (DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
|
|
237
238
|
const Query = {
|
|
238
239
|
query: typeof query === "string" ? query : query.query,
|
|
239
|
-
source:
|
|
240
|
+
source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
|
|
240
241
|
};
|
|
241
242
|
const foundSource = Object.keys(DefaultSources).find(source => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
242
243
|
// ignore links...
|
|
@@ -248,10 +249,11 @@ export class ManagerUtils {
|
|
|
248
249
|
}
|
|
249
250
|
transformLavaSearchQuery(query) {
|
|
250
251
|
// transform the query object
|
|
252
|
+
const sourceOfQuery = typeof query === "string" ? undefined : (DefaultSources[(query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? (query.source?.trim?.()?.toLowerCase?.()));
|
|
251
253
|
const Query = {
|
|
252
254
|
query: typeof query === "string" ? query : query.query,
|
|
253
255
|
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(v => query.types?.find(x => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album", /*"text"*/],
|
|
254
|
-
source:
|
|
256
|
+
source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
|
|
255
257
|
};
|
|
256
258
|
const foundSource = Object.keys(DefaultSources).find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
|
|
257
259
|
if (foundSource && DefaultSources[foundSource]) {
|
|
@@ -66,6 +66,8 @@ export interface ManagerOptions {
|
|
|
66
66
|
advancedOptions?: {
|
|
67
67
|
/** optional */
|
|
68
68
|
debugOptions?: {
|
|
69
|
+
/** For logging custom searches */
|
|
70
|
+
logCustomSearches?: boolean;
|
|
69
71
|
/** logs for debugging the "no-Audio" playing error */
|
|
70
72
|
noAudio?: boolean;
|
|
71
73
|
/** For Logging the Destroy function */
|
|
@@ -78,7 +80,7 @@ export interface ManagerOptions {
|
|
|
78
80
|
};
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
|
-
interface LavalinkManagerEvents {
|
|
83
|
+
export interface LavalinkManagerEvents {
|
|
82
84
|
/**
|
|
83
85
|
* Emitted when a Track started playing.
|
|
84
86
|
* @event Manager#trackStart
|
|
@@ -93,12 +95,12 @@ interface LavalinkManagerEvents {
|
|
|
93
95
|
* Emitted when a Track got stuck while playing.
|
|
94
96
|
* @event Manager#trackStuck
|
|
95
97
|
*/
|
|
96
|
-
"trackStuck": (player: Player, track: Track, payload: TrackStuckEvent) => void;
|
|
98
|
+
"trackStuck": (player: Player, track: Track | null, payload: TrackStuckEvent) => void;
|
|
97
99
|
/**
|
|
98
100
|
* Emitted when a Track errored.
|
|
99
101
|
* @event Manager#trackError
|
|
100
102
|
*/
|
|
101
|
-
"trackError": (player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent) => void;
|
|
103
|
+
"trackError": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackExceptionEvent) => void;
|
|
102
104
|
/**
|
|
103
105
|
* Emitted when the Playing finished and no more tracks in the queue.
|
|
104
106
|
* @event Manager#queueEnd
|
|
@@ -347,4 +349,3 @@ export declare class LavalinkManager extends EventEmitter {
|
|
|
347
349
|
*/
|
|
348
350
|
sendRawData(data: VoicePacket | VoiceServer | VoiceState | ChannelDeletePacket): Promise<void>;
|
|
349
351
|
}
|
|
350
|
-
export {};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import internal from "stream";
|
|
3
|
-
import { Dispatcher, Pool } from "undici";
|
|
4
3
|
import { NodeManager } from "./NodeManager";
|
|
5
4
|
import { DestroyReasonsType, Player } from "./Player";
|
|
6
5
|
import { Track } from "./Track";
|
|
7
6
|
import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
|
|
8
|
-
/**
|
|
9
|
-
export type ModifyRequest = (options:
|
|
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
|
-
/**
|
|
34
|
-
|
|
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<
|
|
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<
|
|
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<
|
|
266
|
+
unmarkAllFailedAddresses: () => Promise<any>;
|
|
270
267
|
};
|
|
271
268
|
/** Private Utils */
|
|
272
269
|
private validate;
|
|
273
270
|
private syncPlayerData;
|
|
274
|
-
private get
|
|
271
|
+
private get restAddress();
|
|
275
272
|
private reconnect;
|
|
276
273
|
private open;
|
|
277
274
|
private close;
|
|
@@ -5,7 +5,7 @@ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
|
|
|
5
5
|
import { DestroyReasonsType } from "./Player";
|
|
6
6
|
import { LavalinkPlayer, MiniMap } from "./Utils";
|
|
7
7
|
type LavalinkNodeIdentifier = string;
|
|
8
|
-
interface NodeManagerEvents {
|
|
8
|
+
export interface NodeManagerEvents {
|
|
9
9
|
/**
|
|
10
10
|
* Emitted when a Node is created.
|
|
11
11
|
* @event Manager.nodeManager#create
|
|
@@ -14,10 +14,10 @@ export type IntegerNumber = Opaque<number, 'Int'>;
|
|
|
14
14
|
export type FloatNumber = Opaque<number, 'Float'>;
|
|
15
15
|
export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
|
|
16
16
|
export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
|
|
17
|
-
export type DuncteSearchPlatform = "speak" | "tts";
|
|
17
|
+
export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
|
|
18
18
|
export type LavalinkClientSearchPlatform = "bcsearch";
|
|
19
19
|
export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
|
|
20
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
20
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | LavalinkClientSearchPlatform;
|
|
21
21
|
export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
|
|
22
22
|
export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
|
|
23
23
|
"youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lavalink-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -59,8 +59,10 @@
|
|
|
59
59
|
"typescript": "^5.1.6"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"undici": "^5.23.0",
|
|
63
62
|
"tslib": "^2.6.1",
|
|
64
63
|
"ws": "^8.13.0"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
65
67
|
}
|
|
66
68
|
}
|