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.
- 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 +55 -56
- package/dist/cjs/structures/NodeManager.d.ts +1 -1
- package/dist/cjs/structures/Player.js +10 -10
- package/dist/cjs/structures/Queue.js +1 -1
- package/dist/cjs/structures/Track.d.ts +2 -0
- package/dist/cjs/structures/Utils.d.ts +12 -7
- 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 +55 -56
- package/dist/esm/structures/NodeManager.d.ts +1 -1
- package/dist/esm/structures/Player.js +10 -10
- package/dist/esm/structures/Queue.js +1 -1
- package/dist/esm/structures/Track.d.ts +2 -0
- package/dist/esm/structures/Utils.d.ts +12 -7
- 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/Track.d.ts +2 -0
- package/dist/types/structures/Utils.d.ts +12 -7
- 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
|
|
@@ -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?.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
player.createdTimeStamp
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
player.
|
|
560
|
-
|
|
561
|
-
player.filterManager.filterUpdatedState
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (
|
|
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
|
-
|
|
571
|
-
|
|
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
|
|
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?.
|
|
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
|
|
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)))
|
|
@@ -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
|
|
28
|
-
title: string;
|
|
29
|
-
/** The playlist name (if provided instead of title) */
|
|
27
|
+
/** The playlist name */
|
|
30
28
|
name: string;
|
|
31
|
-
/** The
|
|
29
|
+
/** The playlist title (same as name) */
|
|
30
|
+
title: string;
|
|
31
|
+
/** The playlist Author */
|
|
32
32
|
author?: string;
|
|
33
|
-
/** The
|
|
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.
|
|
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,
|