lavalink-client 2.5.9 → 2.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
- # Lavalink Client
2
- Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.
1
+ <div style="font-family: Arial, sans-serif; border: 1px solid #ddd; border-radius: 15px; padding: 25px; background-color: #f9f9f9;">
2
+
3
+ <h1 align="center" style="color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px;">Lavalink Client</h1>
4
+ <p align="center" style="font-size: 1.2em; color: #555;">An easy, flexible, and feature-rich Lavalink v4 Client for both beginners and experts.</p>
3
5
 
4
6
  <div align="center">
5
7
  <p>
@@ -8,124 +10,186 @@ Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Profi
8
10
  </p>
9
11
  <p>
10
12
  <a href="https://www.npmjs.com/package/lavalink-client">
11
- <img src="https://img.shields.io/npm/v/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM version" />
13
+ <img src="https://img.shields.io/npm/v/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM Version" />
12
14
  </a>
13
15
  <a href="https://www.npmjs.com/package/lavalink-client">
14
- <img src="https://img.shields.io/npm/dt/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM downloads" />
16
+ <img src="https://img.shields.io/npm/dt/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM Downloads" />
15
17
  </a>
16
18
  <a href="https://tomato6966.github.io/lavalink-client/">
17
- <img src="https://img.shields.io/badge/Documation-%230288D1.svg?style=for-the-badge&logo=gitbook&logoColor=white" alt="Get Started Now">
19
+ <img src="https://img.shields.io/badge/Documentation-%230288D1.svg?style=for-the-badge&logo=gitbook&logoColor=white" alt="Get Started Now">
18
20
  </a>
19
21
  </p>
20
22
  <p>
21
- <a href="https://www.npmjs.com/package/lavalink-client"><img src="https://nodei.co/npm/lavalink-client.png?downloads=true&stars=true" alt="npm install lavalink-client" /></a>
23
+ <a href="https://www.npmjs.com/package/lavalink-client"><img src="https://nodei.co/npm/lavalink-client.png?downloads=true&stars=true" alt="NPM Install: lavalink-client" /></a>
22
24
  </p>
23
25
  </div>
24
26
 
25
- # Install
27
+ ***
26
28
 
27
- Latest stable Version: **`v2.4.0`**
29
+ ## 🚀 Features
30
+ - 💯 **Lavalink v4 Native:** Full support for Lavalink v4, including its powerful plugin ecosystem.
31
+ - ✅ **Detailed Player-Destroy Reasons:** Understand precisely why a player was destroyed (e.g., channel deleted, bot disconnected).
32
+ - ✨ **Flexible Queue Stores:** Use the default in-memory store or bring your own (Redis, databases, etc.) to sync queues across multiple processes.
33
+ - 🎶 **Unresolved Tracks:** Supports unresolved track objects, fetching full data only when a track is about to play, saving API requests and resources.
34
+ - 🎚️ **Built-in Filters & EQ:** Easy-to-use management for audio filters and equalizers.
35
+ - ⚙️ **Advanced Player Options:** Fine-tune player behavior for disconnects, empty queues, volume handling, and more.
36
+ - 🛡️ **Lavalink-Side Validation:** Ensures you only use filters, plugins, and sources that your Lavalink node actually supports.
37
+ - 🔒 **Client-Side Validation:** Whitelist and blacklist URLs or domains to prevent unwanted requests and protect your bot.
38
+ - 🧑‍💻 **Developer-Friendly:** A memory-efficient design with a clean, intuitive API that mirrors Lavalink's own implementation.
39
+ - 🤖 **Automated Handling:** Automatically handles track skipping on errors, voice channel deletions, server-wide mutes, and much more.
28
40
 
29
- <details><summary>👉 via NPM</summary>
41
+ ***
30
42
 
31
- ```bash
32
- npm install --save lavalink-client
33
- ```
43
+ ## 📦 Installation
34
44
 
45
+ **Latest Stable Version: `v2.5.x`**
35
46
 
36
- Dev Version: (Current)
47
+ <details>
48
+ <summary><strong>👉 via NPM</strong></summary>
37
49
 
38
50
  ```bash
39
- npm install --save tomato6966/lavalink-client
51
+ # Stable (install release)
52
+ npm install --save lavalink-client
53
+
54
+ # Development (Install github dev-branch)
55
+ npm install --save tomato6966/lavalink-client
40
56
  ```
41
57
 
42
58
  </details>
43
59
 
44
- <details><summary>👉 via YARN</summary>
60
+ <details>
61
+ <summary><strong>👉 via YARN</strong></summary>
45
62
 
46
63
  ```bash
64
+ # Stable (install release)
47
65
  yarn add lavalink-client
66
+
67
+ # Development (Install github dev-branch)
68
+ yarn add tomato6966/lavalink-client
48
69
  ```
49
70
 
50
- Dev Version: (Current)
71
+ </details>
72
+
73
+ <details>
74
+ <summary><strong>👉 via BUN</strong></summary>
51
75
 
52
76
  ```bash
53
- yarn add tomato6966/lavalink-client
77
+ # Stable (install release)
78
+ bun add lavalink-client
79
+
80
+ # Development (Install github dev-branch)
81
+ bun add tomato6966/lavalink-client
54
82
  ```
55
83
 
56
84
  </details>
57
85
 
58
- # Documentation
59
-
60
- Check out the [Documentation](https://tomato6966.github.io/lavalink-client/)
61
- - See all [Manager-Events, e.g. for Track / player and general errors / logs](https://tomato6966.github.io/lavalink-client/extra/manager-events)
62
- - See all [NodeManager-Events e.g. for Node Errors / logs](https://tomato6966.github.io/lavalink-client/extra/node-events)
63
- - See the Guide on [How to do resuming](https://tomato6966.github.io/lavalink-client/extra/resuming)
86
+ <details>
87
+ <summary><strong>👉 via pnpm</strong></summary>
64
88
 
65
- # Used in:
89
+ ```bash
90
+ # Stable (install release)
91
+ pnpm add lavalink-client
66
92
 
67
- - [**Mivator** *(Bot by Chrissy8283 aka @Tomato6966)*](https://discord.gg/5dUb7M2qCj)
68
- - [Betty *(Bot by good friend of Chrissy, aka fb_sean)*](https://betty.cx/)
69
- - **Bots by Contributors:**
70
- - [Mintone (@appujet)](https://mintone.tech/)
71
- - [Stelle (@EvilG-MC)](https://github.com/Ganyu-Studios/stelle-music)
72
- - [Panais (@LucasB25)](https://panais.xyz/)
73
- - [Akyn (@notdeltaxd)](https://akynbot.vercel.app/)
74
- - **Community:**
75
- - [Soundy (@idMJA)](https://github.com/idMJA/Soundy)
93
+ # Development (Install github dev-branch)
94
+ pnpm add tomato6966/lavalink-client
95
+ ```
76
96
 
97
+ </details>
77
98
 
78
- # Features
99
+ ## 📖 Documentation & Guides
79
100
 
80
- - 💯 Lavalink v4 Supported only (with Lavalink Plugins)
101
+ - **[Full Documentation](https://tomato6966.github.io/lavalink-client/)** - Your starting point for everything.
102
+ - **[Manager Events](https://tomato6966.github.io/lavalink-client/extra/manager-events)** - Handle track, player, and general client events.
103
+ - **[NodeManager Events](https://tomato6966.github.io/lavalink-client/extra/node-events)** - Manage node connections, errors, and logs.
104
+ - **[Session Resuming Guide](https://tomato6966.github.io/lavalink-client/extra/resuming)** - Learn how to implement session resuming for seamless restarts.
81
105
 
82
- - ✅ Player-Destroy Reasons like:
83
- - Channel got deleted, Player got disconnected...
106
+ ***
84
107
 
85
- - Choose able queue stores (maps, collections, redis, databases, ...)
86
- - You can create your own queueStore, thus make it easy to sync queues accross multiple connections (e.g. dashboard-bot)
87
- - Automated Queue Sync methods
88
- - Automated unresolveable Tracks (save the queries as Partial Track Objects -> Fetch the tracks only once they are gonna play)
108
+ ## 💖 Used In
109
+ This client powers various Discord bots:
110
+ - **[Mivator](https://discord.gg/5dUb7M2qCj)** (Public Bot by @Tomato6966)
111
+ - **[Betty](https://betty.cx/)** (Public Bot by fb_sean)
112
+ - **Bots by Contributors:**
113
+ - [Mintone](https://mintone.tech/) (@appujet)
114
+ - [Stelle](https://github.com/Ganyu-Studios/stelle-music) (@EvilG-MC)
115
+ - [Panais](https://panais.xyz/) (@LucasB25)
116
+ - [Akyn](https://akynbot.vercel.app/) (@notdeltaxd)
117
+ - **Bots Community (Users):**
118
+ - [Soundy](https://github.com/idMJA/Soundy) (@idMJA)
89
119
 
90
- - 😍 Included Filter & Equalizer Management
120
+ ***
91
121
 
92
- - 👍 Multiple Player Options *for easier use*
93
- - onDisconnect -> Player Destroy / auto Reconnect
94
- - onEmptyQueue -> Player Destroy / leave After x Time
95
- - instaFixFilter -> seek the player after applying a filter, to instantly apply it's effect (only works for little-durational-songs)
96
- - applyVolumeAsFilter -> instead of using lavalink.volume, it uses lavalink.filters.volume which is much different!
122
+ ## 🛠️ Configuration Examples
97
123
 
98
- - 🛡️ Lavalink Validations
99
- - It only let's you use the filters / plugins / sources, if Lavalink actually has it enabled
124
+ ### Basic Setup
125
+ A minimal example to get you started quickly.
126
+ ```typescript
127
+ import { LavalinkManager } from "lavalink-client";
128
+ import { Client, GatewayIntentBits } from "discord.js"; // example for a discord bot
100
129
 
101
- - 🛡️ Client Validations
102
- - Allows you to whitelist links and even blacklist links / words / domain names, so that it doesn't allow requests you don't want!
103
- - Checks almost all Lavalink Requests for out of bound errors, right before the request is made to prevent process breaking errors.
130
+ // Extend the Client type to include the lavalink manager
131
+ declare module "discord.js" {
132
+ interface Client {
133
+ lavalink: LavalinkManager;
134
+ }
135
+ }
104
136
 
105
- - 🧑‍💻 Memory friendly and easy style
106
- - Only the required data is displayed, and the store-way & types match Lavalink#IMPLEMENTATION.md
137
+ const client = new Client({
138
+ intents: [
139
+ GatewayIntentBits.Guilds,
140
+ GatewayIntentBits.GuildVoiceStates,
141
+ ]
142
+ });
107
143
 
108
- - 😘 Automated Handlings
109
- - Skips the songs, on TrackEnd, TrackStuck, TrackError,
110
- - Destroys the player on channeldelete
111
- - Pauses / resumes the player if it get's muted / unmuted (server-wide) [soon]
112
- - ...
144
+ client.lavalink = new LavalinkManager({
145
+ nodes: [
146
+ {
147
+ authorization: "youshallnotpass", // The password for your Lavalink server
148
+ host: "localhost",
149
+ port: 2333,
150
+ id: "Main Node",
151
+ }
152
+ ],
153
+ // A function to send voice server updates to the Lavalink client
154
+ sendToShard: (guildId, payload) => {
155
+ const guild = client.guilds.cache.get(guildId);
156
+ if (guild) guild.shard.send(payload);
157
+ },
158
+ autoSkip: true,
159
+ client: {
160
+ id: process.env.CLIENT_ID, // Your bot's user ID
161
+ username: "MyBot",
162
+ },
163
+ });
113
164
 
114
- - 😁 Much much more!
165
+ // Listen for the 'raw' event from discord.js and forward it
166
+ client.on("raw", (d) => client.lavalink.sendRawData(d));
115
167
 
116
- ***
168
+ client.on("ready", () => {
169
+ console.log(`Logged in as ${client.user.tag}!`);
170
+ // Initialize the Lavalink client
171
+ client.lavalink.init({ ...client.user });
172
+ });
117
173
 
118
- # Sample Configuration
174
+ client.login(process.env.DISCORD_TOKEN);
175
+ ```
119
176
 
177
+ <details>
178
+ <summary><strong>🔩 Complete Configuration Example (almost all Options)</strong></summary>
120
179
 
121
- <details><summary>Complete Configuration Example, with all available options</summary>
180
+ ```typescript
181
+ import { LavalinkManager, QueueChangesWatcher, QueueStoreManager, StoredQueue } from "lavalink-client";
182
+ import { RedisClientType, createClient } from "redis";
183
+ import { Client, GatewayIntentBits, User } from "discord.js";
122
184
 
123
- ```ts
124
- import { LavalinkManager, QueueChangesWatcher, QueueStoreManager } from "lavalink-client";
125
- import { RedisClientType } from "redis"; // example for custom queue store
126
- import { Client, GatewayIntentBits } from "discord.js"; // example for a discord bot
185
+ // It's recommended to extend the Client type
186
+ declare module "discord.js" {
187
+ interface Client {
188
+ lavalink: LavalinkManager;
189
+ redis: RedisClientType;
190
+ }
191
+ }
127
192
 
128
- // you might want to extend the types of the client, to bind lavalink to it.
129
193
  const client = new Client({
130
194
  intents: [
131
195
  GatewayIntentBits.Guilds,
@@ -133,721 +197,262 @@ const client = new Client({
133
197
  ]
134
198
  });
135
199
 
136
- const previouslyUsedSessions = new Map<string, string>(); //nodeManager.on("connect", node => previouslyUsedSessions.set(node.id, node.sessionId))
137
-
138
200
  client.lavalink = new LavalinkManager({
139
201
  nodes: [
140
202
  {
141
- authorization: "localhoist",
203
+ authorization: "youshallnotpass",
142
204
  host: "localhost",
143
205
  port: 2333,
144
206
  id: "testnode",
145
- // get the previously used session, to restart with "resuming" enabled
146
- sessionId: previouslyUsedSessions.get("testnode"),
147
- requestSignalTimeoutMS: 3000,
148
- closeOnError: true,
149
- heartBeatInterval: 30_000,
150
- enablePingOnStatsCheck: true,
151
- retryDelay: 10e3,
152
- secure: false,
207
+ secure: false, // Set to true for wss://
153
208
  retryAmount: 5,
209
+ retryDelay: 10_000, // 10 seconds
154
210
  }
155
211
  ],
156
212
  sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload),
157
- autoSkip: true,
158
- client: { // client: client.user
159
- id: envConfig.clientId, // REQUIRED! (at least after the .init)
213
+ autoSkip: true, // automatically play the next song of the queue, on: trackend, trackerror, trackexception
214
+ client: {
215
+ id: process.env.CLIENT_ID,
160
216
  username: "TESTBOT",
161
217
  },
162
- autoSkipOnResolveError: true, // skip song, if resolving an unresolved song fails
163
- emitNewSongsOnly: true, // don't emit "looping songs"
164
218
  playerOptions: {
165
- // These are the default prevention methods
166
- maxErrorsPerTime: {
167
- threshold: 10_000,
168
- maxAmount: 3,
169
- },
170
- // only allow an autoplay function to execute, if the previous function was longer ago than this number.
171
- minAutoPlayMs: 10_000,
172
-
173
219
  applyVolumeAsFilter: false,
174
- clientBasedPositionUpdateInterval: 50, // in ms to up-calc player.position
220
+ clientBasedPositionUpdateInterval: 50,
175
221
  defaultSearchPlatform: "ytmsearch",
176
- volumeDecrementer: 0.75, // on client 100% == on lavalink 75%
177
- requesterTransformer: requesterTransformer,
222
+ volumeDecrementer: 0.75,
178
223
  onDisconnect: {
179
- autoReconnect: true, // automatically attempts a reconnect, if the bot disconnects from the voice channel, if it fails, it get's destroyed
180
- destroyPlayer: false // overrides autoReconnect and directly destroys the player if the bot disconnects from the vc
224
+ autoReconnect: true,
225
+ destroyPlayer: false,
181
226
  },
182
227
  onEmptyQueue: {
183
- destroyAfterMs: 30_000, // 0 === instantly destroy | don't provide the option, to don't destroy the player
184
- autoPlayFunction: autoPlayFunction,
228
+ destroyAfterMs: 30_000,
229
+ // function get's called onqueueempty, and if there are songs added to the queue, it continues playing. if not then not (autoplay functionality)
230
+ // autoPlayFunction: async (player) => { /* ... */ },
185
231
  },
186
232
  useUnresolvedData: true,
187
233
  },
188
234
  queueOptions: {
189
235
  maxPreviousTracks: 10,
190
- // only needed if you want and need external storage, don't provide if you don't need to
191
- queueStore: new myCustomStore(client.redis), // client.redis = new redis()
192
- // only needed, if you want to watch changes in the queue via a custom class,
193
- queueChangesWatcher: new myCustomWatcher(client)
236
+ queueStore: new MyCustomRedisStore(client.redis),
237
+ queueChangesWatcher: new MyCustomQueueWatcher(client),
194
238
  },
239
+ // Whitelist/Blacklist links or words
195
240
  linksAllowed: true,
196
- // example: don't allow p*rn / youtube links., you can also use a regex pattern if you want.
197
- // linksBlacklist: ["porn", "youtube.com", "youtu.be"],
198
- linksBlacklist: [],
241
+ linksBlacklist: ["somebadsite.com"],
199
242
  linksWhitelist: [],
200
243
  advancedOptions: {
201
- enableDebugEvents: true,
202
- maxFilterFixDuration: 600_000, // only allow instafixfilterupdate for tracks sub 10mins
203
244
  debugOptions: {
204
245
  noAudio: false,
205
- playerDestroy: {
206
- dontThrowError: false,
207
- debugLog: false,
208
- },
209
- logCustomSearches: false,
246
+ playerDestroy: { dontThrowError: false, debugLog: false },
210
247
  }
211
248
  }
212
249
  });
213
250
 
251
+ client.on("raw", d => client.lavalink.sendRawData(d));
252
+ client.on("ready", () => client.lavalink.init({ ...client.user }));
214
253
 
215
- client.on("raw", d => client.lavalink.sendRawData(d)); // send raw data to lavalink-client to handle stuff
216
-
217
- client.on("ready", () => {
218
- client.lavalink.init(client.user); // init lavalink
219
- });
220
-
221
- // for the custom queue Store create a redis instance
222
- client.redis = createClient({ url: "redis://localhost:6379", password: "securepass" });
223
- client.redis.connect();
224
-
225
-
226
- // Custom external queue Store
227
- export class myCustomStore implements QueueStoreManager {
228
- private redis:RedisClientType;
229
- constructor(redisClient:RedisClientType) {
254
+ // Example Custom Redis Queue Store
255
+ class MyCustomRedisStore implements QueueStoreManager {
256
+ private redis: RedisClientType;
257
+ constructor(redisClient: RedisClientType) {
230
258
  this.redis = redisClient;
231
259
  }
232
- async get(guildId): Promise<any> {
233
- return await this.redis.get(this.id(guildId));
234
- }
235
- async set(guildId, stringifiedQueueData): Promise<any> {
236
- return await this.redis.set(this.id(guildId), stringifiedQueueData);
237
- }
238
- async delete(guildId): Promise<any> {
239
- return await this.redis.del(this.id(guildId));
240
- }
241
- async parse(stringifiedQueueData): Promise<Partial<StoredQueue>> {
242
- return JSON.parse(stringifiedQueueData);
243
- }
244
- async stringify(parsedQueueData): Promise<any> {
245
- return JSON.stringify(parsedQueueData);
246
- }
247
- private id(guildId) {
248
- return `lavalinkqueue_${guildId}`; // transform the id to your belikings
249
- }
260
+ private key(guildId: string) { return `lavalinkqueue_${guildId}`; }
261
+ async get(guildId: string) { return await this.redis.get(this.key(guildId)); }
262
+ async set(guildId: string, data: string) { return await this.redis.set(this.key(guildId), data); }
263
+ async delete(guildId: string) { return await this.redis.del(this.key(guildId)); }
264
+ async parse(data: string): Promise<Partial<StoredQueue>> { return JSON.parse(data); }
265
+ stringify(data: Partial<StoredQueue>): string { return JSON.stringify(data); }
250
266
  }
251
267
 
252
- // Custom Queue Watcher Functions
253
- export class myCustomWatcher implements QueueChangesWatcher {
254
- constructor() {
255
- }
256
- shuffled(guildId, oldStoredQueue, newStoredQueue) {
257
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: Queue got shuffled`)
258
- }
259
- tracksAdd(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
260
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got added into the Queue at position #${position}`);
261
- }
262
- tracksRemoved(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
263
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got removed from the Queue at position #${position}`);
264
- }
268
+ // Example Custom Queue Watcher
269
+ class MyCustomQueueWatcher implements QueueChangesWatcher {
270
+ private client: Client;
271
+ constructor(client: Client) { this.client = client; }
272
+ shuffled(guildId: string) { console.log(`Queue shuffled in guild: ${guildId}`); }
273
+ tracksAdd(guildId: string, tracks: any[], position: number) { console.log(`${tracks.length} tracks added at position ${position} in guild: ${guildId}`); }
274
+ tracksRemoved(guildId: string, tracks: any[], position: number) { console.log(`${tracks.length} tracks removed at position ${position} in guild: ${guildId}`); }
265
275
  }
266
276
  ```
267
277
 
268
278
  </details>
269
279
 
280
+ ***
270
281
 
271
-
272
- ```ts
273
- import { LavalinkManager } from "lavalink-client";
274
- import { Client, GatewayIntentBits } from "discord.js"; // example for a discord bot
275
-
276
- // you might want to extend the types of the client, to bind lavalink to it.
277
- const client = new Client({
278
- intents: [
279
- GatewayIntentBits.Guilds,
280
- GatewayIntentBits.GuildVoiceStates,
281
- ]
282
+ ## 📢 Events
283
+ Listen to events to create interactive and responsive logic.
284
+
285
+ ### Lavalink Manager Events
286
+ These events are emitted from the main `LavalinkManager` instance and relate to players and tracks.
287
+
288
+ - `playerCreate (player)`
289
+ - `playerDestroy (player, reason)`
290
+ - `playerDisconnect (player, voiceChannelId)`
291
+ - `playerMove (player, oldChannelId, newChannelId)`
292
+ - `trackStart (player, track)`
293
+ - `trackEnd (player, track)`
294
+ - `trackStuck (player, track, payload)`
295
+ - `trackError (player, track, payload)`
296
+ - `queueEnd (player)`
297
+
298
+ <details>
299
+ <summary><strong>📢 Example for Manager-Event-Listeners</strong></summary>
300
+
301
+ ```javascript
302
+ // Example: Listening to a track start event
303
+ client.lavalink.on("trackStart", (player, track) => {
304
+ const channel = client.channels.cache.get(player.textChannelId);
305
+ if(channel) channel.send(`Now playing: ${track.info.title}`);
282
306
  });
283
307
 
284
- // create instance
285
- client.lavalink = new LavalinkManager({
286
- nodes: [
287
- {
288
- authorization: "localhoist",
289
- host: "localhost",
290
- port: 2333,
291
- id: "testnode",
292
- }
293
- ],
294
- sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload),
295
- autoSkip: true,
296
- client: {
297
- id: envConfig.clientId,
298
- username: "TESTBOT",
299
- },
308
+ // Example: Handling queue end
309
+ client.lavalink.on("queueEnd", (player) => {
310
+ const channel = client.channels.cache.get(player.textChannelId);
311
+ if(channel) channel.send("The queue has finished. Add more songs!");
312
+ player.destroy();
300
313
  });
314
+ ```
315
+ </details>
301
316
 
302
- client.on("raw", d => client.lavalink.sendRawData(d)); // send raw data to lavalink-client to handle stuff
317
+ ### Node Manager Events
318
+ These events are emitted from `lavalink.nodeManager` and relate to the Lavalink node connections.
303
319
 
304
- client.on("ready", () => {
305
- client.lavalink.init(client.user); // init lavalink
306
- });
320
+ - `create (node)`
321
+ - `connect (node)`
322
+ - `disconnect (node, reason)`
323
+ - `reconnecting (node)`
324
+ - `destroy (node)`
325
+ - `error (node, error, payload)`
326
+ - `resumed (node, payload, players)`
307
327
 
308
- ```
328
+ <details>
329
+ <summary><strong>📢 Example for Node-Event-Listeners</strong></summary>
309
330
 
310
- # All Events:
311
-
312
- ## On **Lavalink-Manager**:
313
- > *Player related logs*
314
- - `playerCreate` ➡️ `(player) => {}`
315
- - `playerDestroy` ➡️ `(player, reason) => {}`
316
- - `playerDisconnect` ➡️ `(player, voiceChannelId) => {}`
317
- - `playerMove` ➡️ `(player, oldChannelId, newChannelId) => {}`
318
- - Updating the voice channel is handled by the client automatically
319
- - `playerSocketClosed` ➡️ `(player, payload) => {}`
320
-
321
- > *Track / Manager related logs*
322
- - `trackStart` ➡️ `(player, track, payload) => {}`
323
- - `trackStuck` ➡️ `(player, track, payload) => {}`
324
- - `trackError` ➡️ `(player, track, payload) => {}`
325
- - `trackEnd` ➡️ `(player, track, payload) => {}`
326
- - `queueEnd` ➡️ `(player, track, payload) => {}`
327
- - `playerUpdate` ➡️ `(player) => {}`
328
-
329
- ```js
330
- client.lavalink.on("create", (node, payload) => {
331
- console.log(`The Lavalink Node #${node.id} connected`);
331
+ ```javascript
332
+ // Example: Logging node connections and errors
333
+ client.lavalink.nodeManager.on("connect", (node) => {
334
+ console.log(`Node "${node.id}" connected!`);
332
335
  });
333
- // for all node based errors:
334
- client.lavalink.on("error", (node, error, payload) => {
335
- console.error(`The Lavalink Node #${node.id} errored: `, error);
336
- console.error(`Error-Payload: `, payload)
337
- });
338
- ```
339
336
 
340
- ## On **Node-Manager**:
341
- - `raw` ➡️ `(node, payload) => {}`
342
- - `disconnect` ➡️ `(node, reason) => {}`
343
- - `connect` ➡️ `(node) => {}`
344
- - `reconnecting` ➡️ `(node) => {}`
345
- - `create` ➡️ `(node) => {}`
346
- - `destroy` ➡️ `(node) => {}`
347
- - `error` ➡️ `(node, error, payload) => {}`
348
- - `resumed` ➡️ `(node, payload, players) => {}`
349
- - Resuming needs to be handled manually by you *(aka add the players to the manager)*
350
- - e.g. of listening to node events:
351
- ```js
352
- client.lavalink.nodeManager.on("create", (node, payload) => {
353
- console.log(`The Lavalink Node #${node.id} connected`);
354
- });
355
- // for all node based errors:
356
- client.lavalink.nodeManager.on("error", (node, error, payload) => {
357
- console.error(`The Lavalink Node #${node.id} errored: `, error);
358
- console.error(`Error-Payload: `, payload)
337
+ client.lavalink.nodeManager.on("error", (node, error) => {
338
+ console.error(`Node "${node.id}" encountered an error:`, error.message);
359
339
  });
360
340
  ```
341
+ </details>
361
342
 
362
- ## How to log queue logs?
363
- > When creating the manager, add the option: `queueOptions.queueChangesWatcher: new myCustomWatcher(botClient)`
364
- > E.g:
365
- ```js
366
- import { QueueChangesWatcher, LavalinkManager } from "lavalink-client";
343
+ ***
367
344
 
368
- class myCustomWatcher implements QueueChangesWatcher {
369
- constructor(client) {
370
- this.client = client;
371
- }
372
- shuffled(guildId, oldStoredQueue, newStoredQueue) {
373
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: Queue got shuffled`)
374
- }
375
- tracksAdd(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
376
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got added into the Queue at position #${position}`);
377
- }
378
- tracksRemoved(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
379
- console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got removed from the Queue at position #${position}`);
380
- }
381
- }
345
+ ## 📚 Advanced How-To Guides
382
346
 
383
- client.lavalink = new LavalinkManager({
384
- // ... other options
385
- queueOptions: {
386
- queueChangesWatcher: new myCustomWatcher(client)
387
- }
388
- })
389
- ```
347
+ ### How to Implement Session Resuming
348
+ Resuming allows your music bot to continue playback even after a restart.
390
349
 
391
- ## How to do resuming
392
-
393
- 1. You need to enable resuming on a __connected__ Lavalink node : **` node.updateSession(true, 360e3) `**
394
- 2. The NodeManager#resumed event will emit when the node resumes, you retrieves all fetchedPlayers (fetched by the client), and thus all you need to do is re-create all player instances (and possibly the queues too)
395
- - For that is the queuestore useful
396
- - To save the playerData you can utilize smt like playerUpdate event.
397
-
398
- ## Resuming full Example
399
- Full code sample: can be found on the [Testbot in here](https://github.com/Tomato6966/lavalink-client/blob/main/testBot/Utils/handleResuming.ts)
400
- ```js
401
- // but here is the schema:
402
- client.lavalink.nodeManager.on("connect", (node) => node.updateSession(true, 360e3));
403
- client.lavalink.nodeManager.on("resumed", (node, payload, fetchedPlayers) => {
404
- // create players:
405
- for(const fetchedPlayer of fetchedPlayers) {
406
- // fetchedPlayer is the live data from lavalink
407
- // saved Player data is the config you should save in a database / file or smt
408
- const savedPlayerData = await getSavedPlayerData(fetchedPlayer.guildId);
409
- const player = client.lavalink.createPlayer({
410
- guildId: fetchedPlayer.guildId,
411
- });
412
- // if lavalink says the bot got disconnected, we can skip the resuming, or force reconnect whatever you want!, here we choose to not do anything and thus delete the saved player data
413
- if(!data.state.connected) {
414
- console.log("skipping resuming player, because it already disconnected");
415
- await deletedSavedPlayerData(data.guildId);
416
- continue;
350
+ 1. **Enable Resuming on the Node:** When a node connects, enable resuming with a timeout.
351
+ 2. **Listen for the `resumed` Event:** This event fires on a successful reconnect, providing all player data from Lavalink.
352
+ 3. **Re-create Players:** Use the data from the `resumed` event and your own saved data (from a database/store) to rebuild the players and their queues.
353
+
354
+ > 💡 **For a complete, working example, see the [official test bot's implementation](https://github.com/Tomato6966/lavalink-client/blob/main/testBot/Utils/handleResuming.ts).**
355
+
356
+ <details>
357
+ <summary><strong>💡 Principle of how to enable **resuming**</strong></summary>
358
+
359
+ ```javascript
360
+ // 1. Enable resuming on connect
361
+ client.lavalink.nodeManager.on("connect", (node) => {
362
+ // Enable resuming for 5 minutes (300,000 ms)
363
+ node.updateSession(true, 300_000);
364
+ });
365
+
366
+ // 2. Listen for the resumed event
367
+ client.lavalink.nodeManager.on("resumed", async (node, payload, fetchedPlayers) => {
368
+ console.log(`Node "${node.id}" successfully resumed with ${fetchedPlayers.length} players.`);
369
+
370
+ for (const lavalinkData of fetchedPlayers) {
371
+ // 3. Get your saved data (e.g., from Redis/DB)
372
+ const savedData = await getFromDatabase(lavalinkData.guildId);
373
+ if (!savedData || !lavalinkData.state.connected) {
374
+ if(savedData) await deleteFromDatabase(lavalinkData.guildId);
375
+ continue; // Skip if no saved data or Lavalink reports disconnected
417
376
  }
418
- // now you can create the player based on the live and saved data
377
+
378
+ // Re-create the player instance
419
379
  const player = client.lavalink.createPlayer({
420
- guildId: data.guildId,
380
+ guildId: lavalinkData.guildId,
381
+ voiceChannelId: savedData.voiceChannelId,
382
+ textChannelId: savedData.textChannelId,
383
+ // Important: Use the same node that was resumed
421
384
  node: node.id,
422
- // you need to update the volume of the player by the volume of lavalink which might got decremented by the volume decrementer
423
- volume: client.lavalink.options.playerOptions?.volumeDecrementer
424
- ? Math.round(data.volume / client.lavalink.options.playerOptions.volumeDecrementer)
425
- : data.volume,
426
- // all of the following options are needed to be provided by some sort of player saving
427
- voiceChannelId: dataOfSaving.voiceChannelId,
428
- textChannelId: dataOfSaving.textChannelId,
429
- // all of the following options can either be saved too, or you can use pre-defined defaults
430
- selfDeaf: dataOfSaving.options?.selfDeaf || true,
431
- selfMute: dataOfSaving.options?.selfMute || false,
432
-
433
- applyVolumeAsFilter: dataOfSaving.options.applyVolumeAsFilter,
434
- instaUpdateFiltersFix: dataOfSaving.options.instaUpdateFiltersFix,
435
- vcRegion: dataOfSaving.options.vcRegion,
385
+ // Set volume from Lavalink's data, accounting for the volume decrementer
386
+ volume: lavalinkData.volume,
387
+ selfDeaf: savedData.selfDeaf,
436
388
  });
437
389
 
438
- // player.voice = data.voice;
439
- // normally just player.voice is enough, but if you restart the entire bot, you need to create a new connection, thus call player.connect();
390
+ // Re-establish voice connection
440
391
  await player.connect();
441
392
 
442
- player.filterManager.data = data.filters; // override the filters data
443
- await player.queue.utils.sync(true, false); // get the queue data including the current track (for the requester)
444
- // override the current track with the data from lavalink
445
- if(data.track) player.queue.current = client.lavalink.utils.buildTrack(data.track, player.queue.current?.requester || client.user);
446
- // override the position of the player
447
- player.lastPosition = data.state.position;
448
- player.lastPositionChange = Date.now();
449
- // you can also override the ping of the player, or wait about 30s till it's done automatically
450
- player.ping.lavalink = data.state.ping;
451
- // important to have skipping work correctly later
452
- player.paused = data.paused;
453
- player.playing = !data.paused && !!data.track;
454
- // That's about it
455
- }
456
- })
457
- client.lavalink.on("playerUpdate", (oldPlayer, newPlayer) => { // automatically sync player data on updates. if you don'T want to save everything you can instead also just save the data on playerCreate
458
- setSavedPlayerData(newPlayer.toJSON());
459
- });
460
- // delete the player again
461
- client.lavalink.on("playerDestroy", (player) => {
462
- deleteSavedPlayerData(player.guildId);
463
- })
464
- ```
465
-
466
- ***
393
+ // Restore player state
394
+ player.paused = lavalinkData.paused;
395
+ player.lastPosition = lavalinkData.state.position;
396
+ player.filterManager.data = lavalinkData.filters;
467
397
 
398
+ // Restore the queue
399
+ await player.queue.utils.sync(true, false); // Syncs with your QueueStore
468
400
 
469
- ### How to use flowertts with custom options
470
-
471
- - First enable flowertts within the lava-src plugin
472
- - Then make sure to pass through the extraQueryUrlParams object
401
+ // Restore the current track
402
+ if (lavalinkData.track) {
403
+ player.queue.current = client.lavalink.utils.buildTrack(lavalinkData.track, savedData.requester);
404
+ }
405
+ }
406
+ });
473
407
 
474
- ```js
475
- const query = interaction.options.getString("text");
476
- const voice = interaction.options.getString("voice");
408
+ // Persist player data on updates to use for resuming later
409
+ client.lavalink.on("playerUpdate", (oldPlayer, newPlayer) => {
410
+ saveToDatabase(newPlayer.toJSON());
411
+ });
477
412
 
478
- const extraParams = new URLSearchParams();
479
- if(voice) extraParams.append(`voice`, voice);
480
-
481
- // all params for flowertts can be found here: https://flowery.pw/docs
482
- const response = await player.search({
483
- query: `${query}`,
484
- extraQueryUrlParams: extraParams, // as of my knowledge this is currently only used for flowertts, adjusting the playback url dynamically mid-request
485
- source: "ftts"
486
- }, interaction.user);
413
+ // Clean up data when a player is permanently destroyed
414
+ client.lavalink.on("playerDestroy", (player) => {
415
+ deleteFromDatabase(player.guildId);
416
+ });
487
417
  ```
418
+ </details>
488
419
 
420
+ ### How to Use Plugins
421
+ Lavalink client supports most of the major lavalink-plugins.
422
+ The client itself is - for beginner friendly reasons - atm not extendable (via plugins)
423
+ You can just use the built in functions (sponsor block, lyrics) or search plattforms (deezer, spotify, apple music, youtube, ...) and use the lavalink-plugins without any configuration on the client side.
424
+
425
+ Some plugins require extra-parameters, such as flowerytts:
426
+ Pass extra parameters to the search function to use plugin-specific features.
489
427
 
490
- ***
491
-
428
+ <details>
429
+ <summary><strong>How to use the flowerytts plugin</strong></summary>
492
430
 
493
- # UpdateLog
494
-
495
-
496
- ## **Version 1.2.0**
497
- - Added `player.stopPlaying()`: When executed it **clears the Queue** and **stops playing**, **without destroying the Player**
498
- - Adjusted `Player.skip()`
499
- - Added `throwError` Property to: `player.skip(skipTo?:number = 0, throwError?:boolean = true)`.
500
- - If throwError = false, and no more tracks are in the queue, it won't throw an error and "ignore it". same thing as stopPlaying.
501
- - Added all Events and Methods from the [SponsorBlock Plugin](https://github.com/topi314/Sponsorblock-Plugin).
502
- - It also validates if the plugin is in the bot, in order so that you can use the functions:
503
- - `player.getSponsorBlock()` / `node.getSponsorBlock()`
504
- - `player.setSponsorBlock(segments:SponsorBlockSegment[])` / `node.setSponsorBlock(segments:SponsorBlockSegment[])`
505
- - `player.deleteSponsorBlock()` / `node.deleteSponsorBlock()`
506
- - That Plugin adds following **Events** to the **Manager**: `"SegmentsLoaded"`, `"SegmentSkipped"`, `"ChapterStarted"`, `"ChaptersLoaded"`
507
- - Example Bot show example in autoplayFunction how to "disable" / "enable" Autoplay with bot data variables.
508
- - Added `ManagerOptions#emitNewSongsOnly`. If set to true, it won't emit "trackStart" Event, when track.loop is active, or the new current track == the previous (current) track.
509
- - Added `ManagerOptions#linksBlacklist` which allows user to specify an array of regExp / strings to match query strings (for links / words) and if a match happens it doesn't allow the request (blacklist)
510
- - Added `ManagerOptions#linksWhitelist` which allows user to specify an array of regExp / strings to match query strings (for links only) and if a match does NOT HAPPEN it doesn't allow the request (whitelist)
511
- - Added `ManagerOptions#linksAllowed` if set to false, it does not allow requests which are links
512
- - Moved `ManaagerOptions#debugOptions` to `ManaagerOptions#advancedOptions.debugOptions`
513
-
514
- ### **Version 1.2.1**
515
- - Adjusted `player.stopPlaying()`
516
- - There are now following parameters. `stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false)`.
517
- - On Default it now clears the queue and stops playing. Also it does not execute Autoplay on default. IF you want the function to behave differently, you can use the 2 states for that.
518
- - Fixed that it looped the current track if repeatmode === "track" / "queue". (it stops playing and loop stays)
519
- - Implemented a `parseLavalinkConnUrl(connectionUrl:string)` Util Function.
520
- - It allows you to parse Lavalink Connection Data of a Lavalink Connection Url.
521
- Pattern: `lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>`
522
- - Note that the nodeId and NodeAuthorization must be encoded via encodeURIComponents before you provide it into the function.
523
- - The function will return the following: `{ id: string, authorization: string, host: string, port: number }`
524
- - Example: `parseLavalinkConnUrl("lavalink://LavalinkNode_1:strong%23password1@localhost:2345")` will give you:
525
- `{ id: "LavalinkNode_1", authorization: "strong#password1", host: "localhost", port: 2345 }`
526
- - Note that the password "strong#password1" when encoded turns into "strong%23password1". For more information check the example bot
527
-
528
- ### **Version 2.0.0**
529
- - Lavalink v4 released, adjusted all features from the stable release, to support it in this client!
530
- ```diff
531
-
532
- # How to load tracks / stop playing has changed for the node.updatePlayer rest endpoint the Client handles it automatically
533
- - await player.node.updatePlayer({ encodedTrack?: Base64|null, track?: Track|UnresolvedTrack, identifer?: string });
534
- + await player.node.updatePlayer({ track: { encoded?: Base64|null, identifier?: string }, clientTrack?: Track|UnresolvedTrack });
535
-
536
- # To satisfy the changes from lavalink updatePlayer endpoint, player play also got adjusted for that (Most users won't need this feature!)
537
- - await player.play({ encodedTrack?: Base64|null, track?: Track|UnresolvedTrack, identifer?: string });
538
- + await player.play({ track: { encoded?: Base64|null, identifier?: string }, clientTrack?: Track|UnresolvedTrack });
539
- # However it' still recommended to do it like that:
540
- # first add tracks to the queue
541
- + await player.queue.add(Track: Track|UnresolvedTrack|(Track|UnresolvedTrack)[]);
542
- # then play the next track from the queue
543
- + await player.play();
544
-
545
- # Node Resuming got supported
546
- # First enable it by doing:
547
- + await player.node.updateSession(true, 360_000);
548
- # then when reconnecting to the node add to the node.createeOptions the sessionId: "" of the previous session
549
- # and after connecting the nodeManager.on("resumed", (node, payload, players) => {}) will be executed, where you can sync the players!
550
-
551
- # Node Options got adjusted # It's a property not a method should be treated readonly
552
- + node.resuming: { enabled: boolean, timeout: number | null };
553
-
554
- # Player function got added to stop playing without disconnecting
555
- + player.stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false);
556
-
557
- # Node functions for sponsorBlock Plugin (https://github.com/topi314/Sponsorblock-Plugin) got added
558
- + deleteSponsorBlock(player:Player)
559
- + setSponsorBlock(player:Player, segments: ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"])
560
- # only works if you ever set the sponsor blocks once before
561
- + getSponsorBlock(player:Player)
562
- # Corresponding nodeManager events got added:
563
- + nodeManager.on("ChapterStarted");
564
- + nodeManager.on("ChaptersLoaded");
565
- + nodeManager.on("SegmentsLoaded");
566
- + nodeManager.on("SegmentSkipped");
567
- # Filters sending got supported for filters.pluginFilters key from lavalink api: https://lavalink.dev/api/rest.html#plugin-filters
568
- # Native implementation for lavaSearch plugin officially updated https://github.com/topi314/LavaSearch
569
- # Native implementation for lavaSrc plugin officially updated https://github.com/topi314/LavaSrc including floweryTTS
570
- # couple other changes, which aren't noticeable by you.
571
-
572
- # Lavalink track.userData got added (basically same feature as my custom pluginInfo.clientData system)
573
- # You only get the track.userData data through playerUpdate object
574
- ```
575
- In one of the next updates, there will be more queueWatcher options and more custom nodeevents to trace
576
-
577
- Most features of this update got tested, but if you encounter any bugs feel free to open an issue!
578
-
579
- ## **Version 2.1.0**
580
- - Fixed that, if you skip and have trackloop enabled, it doesn't skip the track
581
- - I fixed that in the past, but for some reason i removed the fix on accident ig.
582
- - Reworked the Filter Manager for custom filters via [LavalinkFilterPlugin](https://github.com/rohank05/lavalink-filter-plugin) / [LavalinkLavaDSPX-Plugin](https://github.com/devoxin/LavaDSPX-Plugin/)
583
- - Note that the [LavalinkLavaDSPX-Plugin](https://github.com/devoxin/LavaDSPX-Plugin/) is by a Community Member of Lavalink and UNOFFICIAL
584
- - They now have individual state-variabels (booleans): `player.filterManager.filters.lavalinkLavaDspxPlugin`
585
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.echo`
586
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.normalization`
587
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.highPass`
588
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.lowPass`
589
- - and for: `player.filterManager.filters.lavalinkFilterPlugin` (this plugins seems to not work on v4 at the moment)
590
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.echo`
591
- - `player.filterManager.filters.lavalinkLavaDspxPlugin.reverb`
592
- - They also now have individual state-changing-methods: `player.filterManager.lavalinkLavaDspxPlugin`
593
- - `player.filterManager.lavalinkLavaDspxPlugin.toggleEcho(decay:number, echoLength:number)`
594
- - `player.filterManager.lavalinkLavaDspxPlugin.toggleNormalization(maxAmplitude:number, adaptive:boolean)`
595
- - `player.filterManager.lavalinkLavaDspxPlugin.toggleHighPass(boostFactor:number, cutoffFrequency:number)`
596
- - `player.filterManager.lavalinkLavaDspxPlugin.toggleLowPass(boostFactor:number, cutoffFrequency:number)`
597
- - and for: `player.filterManager.lavalinkFilterPlugin`
598
- - `player.filterManager.lavalinkFilterPlugin.toggleEcho(delay:number, decay:number)`
599
- - `player.filterManager.lavalinkFilterPlugin.toggleReverb(delays:number[], gains:number[])`
600
-
601
- ## **Version 2.1.1**
602
- - Enforce link searches for users with following searchPlatform Options: "http" | "https" | "link" | "uri"
603
- - Additionally strongend the code behind that
604
- - Added searchPlatform for local tracks (aka files on the lavalink server...): "local"
605
-
606
- ## **Version 2.2.0**
607
- - Changed console.error to throw error on queue.utils.sync if no data was provided/found
608
- - Changed undici.fetch to native fetch, but requires nodejs v18+ to support other runtimes, e.g. bun
609
- - 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)
610
- - Added sourceName for `phsearch` from the dunktebot plugin, released in v.1.7.0
611
- - Support for youtube still going via the youtube-source plugin (disable youtube for lavalink, and use the plugin instead)
612
- - Exporting events
613
- - Added new debugOption: logCustomSearches
614
- - *(Next version update i will remove the internal interval for position update, to calculations)*
615
-
616
- ## **Version 2.2.1**
617
- - Player position is now calculated instead of using intervals
618
- - Instaplayer fix update now requires quite good internet connection on the lavalink server due to removal of intervals for updating player.position (everything above 300mbps should be good)
619
- - Internal updates for handling query params and url-requests (url-parsing) to fix quite few bugs and make the code more readable, now you don't have to ever provide stuff encoded via encodeURIComponent anymore.
620
- - Added a bunch of jsdoc information, to make the autogenerated docs more accurate!
621
-
622
- - Because of the adjustments from the encoding, you now need to pass url params for stuff like flowery tts like this:
623
-
624
- ```js
431
+ ```javascript
432
+ // Example for flowertts plugin
625
433
  const query = interaction.options.getString("text");
626
- const voice = interaction.options.getString("voice");
434
+ const voice = interaction.options.getString("voice"); // e.g., "MALE_1"
627
435
 
628
436
  const extraParams = new URLSearchParams();
629
- if(voice) extraParams.append(`voice`, voice);
630
-
631
- // all params for flowertts can be found here: https://flowery.pw/docs
632
- const response = await player.search({
633
- query: `${query}`,
634
- extraQueryUrlParams: extraParams, // as of my knowledge this is currently only used for flowertts, adjusting the playback url dynamically mid-request
635
- source: "ftts"
636
- }, interaction.user);
637
- ```
638
-
437
+ if (voice) extraParams.append(`voice`, voice);
438
+
439
+ // All params for flowertts can be found here: https://flowery.pw/docs
440
+ const response = await player.search(
441
+ {
442
+ query: `${query}`,
443
+ // This is used by plugins like ftts to adjust the request
444
+ extraQueryUrlParams: extraParams,
445
+ source: "ftts" // Specify the plugin source
446
+ },
447
+ interaction.user // The requester
448
+ );
639
449
 
640
- ## **Version 2.2.2**
641
- - Fixed a bug in player.pause() where when you pause the track longer than the left over currentTrack.info.duration is, then it would auto skip the track on resume.
642
- - Fixed the handling of the previous track array ( sometimes it adds "null", due to lavalink errors )
643
- - Added new functions for the queue, to make migrations and coding easier for beginners,
644
- - ` const previousTrack = await player.queue.shiftPrevious() ` -> removes the previously played track from the player.queue.previous array, and returns it, so you can use it for something like "play previous"
645
- - *Neat 1-liner: ` await player.queue.shiftPrevious().then(clientTrack => player.play({ clientTrack })) `*
646
- - ` await player.queue.remove(removeQuery) ` -> Remove function to remove stuff from the queue.tracks array., following params are valid:
647
- - Array of Tracks / UnresolvedTracks, e.g. ` await player.queue.remove( player.queue.tracks.slice(4, 10) ) ` *(would remove tracks from #4 (incl.) to #10 (excl.) aka those indexes: 4, 5, 6, 7, 8, 9 - this is how array.slice works)*
648
- - Single Track / UnresolveTrack, e.g. ` await player.queue.remove(player.queue.tracks[player.queue.tracks.length - 1]); ` *(would remove the last track)*
649
- - Array of track-indexes, e.g. ` await player.queue.remove([1, 4, 5]) ` *(Would remove track #1, #4 and #5)*
650
- - Single track index, e.g. ` await player.queue.remove(5) ` *(would remove the #5 track from the queue)*
651
- - **NOTE:** I still highly recommend, to use the ` player.queue.splice() ` function for mutating the queue:
652
- - it is possible to remove single tracks, multiple tracks and insert tracks at specific positions!
653
- - *the remove function haven't been fully tested yet*
654
- - Added `track.pluginInfo.clientData?.previousTrack` handling:
655
- - If a track has this property in the pluginInfo in the clientData object set to "true" then it won't get added to the previous track array. Example:
656
- ```js
657
- const previousTrack = await player.queue.shiftPrevious();
658
- if(previousTrack) {
659
- const previousClientData = previousTrack.pluginInfo.clientData || {};
660
- previousTrack.pluginInfo.clientData = { previousTrack: true, ...previousClientData }
661
- await player.play({ clientTrack: previousTrack });
450
+ // Add the TTS track to the queue
451
+ if (response.tracks.length > 0) {
452
+ player.queue.add(response.tracks[0]);
453
+ if (!player.playing) player.play();
662
454
  }
663
455
  ```
456
+ </details>
664
457
 
665
-
666
- ## **Version 2.3.0**
667
-
668
- - Added a heartbeat + ping-pong system to check wether the client is still connected to the node, if the node doesn't receive a ping in time, it will destroy the node and thus cause a reconnect.
669
- - For that following new nodeOptions got added:
670
- - `enablePingOnStatsCheck: boolean` (default: true)
671
- - `heartBeatInterval: number` (default: 30_000)
672
- - Added new Property on a node:
673
- - `isAlive: boolean` (if it's false, then it's not connected to the node anymore, and will AUTOMATICALLY Cause a reconnect within the heartBeatInterval)
674
- - `heartBeatPing: number` (the ping it takes lavalink to respond to the acknowledge of heartbeat)
675
- - Added new NodeManager Events:
676
- - `reconnectinprogress` (when the client internal reconnect system is triggered, the actual reconnect gets triggered by the node after your retryDelay)
677
- - Refactored internal code for better readability and maintainability
678
- - Removed several intermediate promises
679
- - Added new types for better type safety
680
- - Updated types for better type safety
681
- - Reduced default retryDelay from 30s to 10s
682
- - Added example on the testbot how to store player data easily and how to use the resume feature, and updated the Resuming Example in the README [jump](#how-to-do-resuming) by adding a [full example](#resuming-full-example)
683
-
684
- - **"Breaking Change" for providing track / clientTrack for player.play()**
685
- - Instead of adding the track to the queue and skipping to it, they get directly played by lavalink through replacing the track.
686
- - To make this work, we need to pass the transformed requester object to the userData of the track. (all handled by the client)
687
- - *This is technically better than skipping to a track but i wanted to point it out.*
688
- - You can play with clientTrack like this: `player.play({ clientTrack: searchResult.tracks[0] })`
689
- - You can play with just track like this: `player.play({ track: { encoded: "base64string..." }, requester: interaction.user })`
690
-
691
-
692
- ## **Version 2.3.1**
693
-
694
- - Fixed Export, where types of Manager weren't exported correctly
695
- - Fixed Dist Folder containing old, left over not needed files
696
-
697
- ## **Version 2.3.2** / **Version 2.3.3**
698
- - Added Missing function calls for the QueueWatcher of tracksRemoved within the queue.remove() function:
699
- - Added new DestroyReasons:
700
- - TrackStuckMaxTracksErroredPerTime
701
- - TrackErrorMaxTracksErroredPerTime
702
- - Added new Prevention Systems for CrashbackLoop recognitions:
703
- - `this.NodeManager.LavalinkManager.options.playerOptions.maxErrorsPerTime`:
704
- - object: `{ threshold: number, maxAmount: number }` (set threshold to 0 or maxAmount to -1 to disable)
705
- - Default: `{ threshold: 10_000, maxAmount: 3 }`
706
- - If there are trackError or trackStuck Events > maxAmount within the given treshhold, the player will be destroyed prevent more errors and thus potential ratelimits.
707
- - `this.NodeManager.LavalinkManager.options.playerOptions.minAutoPlayMs`:
708
- - number: `10_000` (default)
709
- - If there is an AutoplayFunction, and it get's executed before that threshold, than it won't trigger the autoplay function again. *(this is ignored for when the player is skipped)*
710
- - This prevents autoplays from happeneing on a crashbackloop
711
- - Set to `0` to disable
712
- - **Added new Event "debug":**
713
- - `LavalinkManager#debug(event:DebugEvents, data:{ state: "log" | "warn" | "error", message:string, functionLayer:string, error?:Error })`
714
- - This function Event will emit, when the following option is set to **` true `**: `LavalinkManager.options.advancedOptions.enableDebugEvents`
715
- - You can use the **` DebugEvents `** Enum to listen to specific events and only show those you care
716
- - You can filter for the **` data.state `** to only show the certain log-level state
717
- - The **` functionLayer `** string will show you where the debug event was triggered from
718
- - The **` message `** string will show what is debugged
719
- - The **` error `** object will show you the error that happened, if there was one.
720
- - *This took quite some time to code, and i am sure there are still many logs you might want, feel free to open an issue
721
-
722
-
723
- ## **Version 2.3.4**
724
- - Improved the package bundling with tsc-alias, to export files with file types
725
- - Added package.json to exported dist, for easier parsing ability and compatibility with older and newer node versions
726
- - Added error handling for resolving unresolved tracks on trackend
727
-
728
-
729
- ## **Version 2.3.5**
730
- - FIXED not able to import :: Accidentally removed tsc-alias configuration, which made importing not work
731
- - FIXED autoplay not working :: Accidentally added an invalid if statement, which made autoplay not working anymore (during the if statement to not prevent autoplay spam)
732
- - Added a new AutoplayExecution Debug Log
733
- - Added more samples to the Testbot related configuration
734
-
735
-
736
- ## **Version 2.3.6**
737
- - Added Lyrics Support:
738
- - New Player Functions:
739
- - **` const lyrics = await player.getCurrentLyrics(false); `** -> *Get lyrics of current playing track*
740
- - **` const lyrics = await player.getLyrics(track, true); `** -> *Get lyrics of a specific track with ignoring it's sources*
741
- - **` player.subscribeLyrics(); `** -> *Subscribe this guild to retrieve "live lyrics" as the song is *playing
742
- - **` player.unsubscribeLyrics(); `** -> *Unsubscribe from lyrics
743
- - New Node Functions ( same as from player, just so you can access it without player too ):*
744
- - **` const lyrics = await player.node.lyrics.getCurrent(player.guildId, false); `**
745
- - **` const lyrics = await player.node.lyrics.get(track, true); `**
746
- - **` player.node.lyrics.subscribe(player.guildId); `**
747
- - **` player.node.lyrics.unsubscribe(player.guildId); `**
748
- - New Manager Event sfor Lyrics:
749
- - **` lavalink.on("LyricsLine", (player, track, lyricsLine) => {}); `**
750
- - **` lavalink.on("LyricsFound", (player, track, data) => {}); `**
751
- - **` lavalink.on("LyricsNotFound", (player, track, lyricsLine) => {}); `**
752
-
753
-
754
- ## **Version 2.4.0**
755
- - Refactored a little the project folder Structure
756
- - Added PR Packages to install all commits / packages at once `https://pkg.pr.new/Tomato6966/lavalink-client`
757
- - Removed the dist folder, and added prepare Scripts
758
- - Added attributes for git linting
759
- - Removed the old (gitbook) documentation, and swapped it to a NEW TSDOC Themed Documentation via astro.dev and mdx
760
- - Added new player events:
761
- - **`playerMuteChange`**** ➡️ **`(player, selfMuted, serverMuted) => {}`
762
- *Triggered when the player's voice state related to muting changed*
763
-
764
- - **`playerDeafChange`** ➡️ `(player, selfDeafed, serverDeafed) => {}`
765
- *Triggered when the player's voice state related to deafing changed*
766
-
767
- - **`playerSuppressChange`** ➡️ `(player, suppress) => {}`
768
- *Triggered when the player's voice state related to suppressing changed*
769
-
770
- - **`playerQueueEmptyStart`** ➡️ `(player, timeoutMs) => {}`
771
- *Triggered when the queue empty handler started (the timeout)*
772
-
773
- - **`playerQueueEmptyEnd`** ➡️ `(player) => {}`
774
- *Triggered when the queue empty handler finished (successfully) and thus destroyed the player*
775
-
776
- - **`playerQueueEmptyCancel`** ➡️ `(player) => {}`
777
- *Triggered when the queue empty handler cancelled (e.g. because a new track got added)*
778
-
779
- ~~- **`playerVoiceEmptyStart`** ➡️ `(player, timeoutMs) => {}`~~
780
- ~~*Triggered when the voice empty handler started (the timeout)*~~
781
- *Removed again because of memory overhall and not wanting to handle voice states*
782
-
783
- ~~- **`playerVoiceEmptyEnd`** ➡️ `(player) => {}`~~
784
- ~~*Triggered when the voice empty handler finished (successfully) and thus destroyed the player*~~
785
- *Removed again because of memory overhall and not wanting to handle voice states*
786
-
787
- ~~- **`playerVoiceEmptyCancel`** ➡️ `(player, userId) => {}`~~
788
- ~~*Triggered when the voice empty handler cancelled (e.g. when a user rejoined)*~~
789
- *Removed again because of memory overhall and not wanting to handle voice states*
790
-
791
- - **`playerVoiceJoin`** ➡️ `(player, userId) => {}`~~
792
- *Added instead of the playerVoiceEmpty handler, emitted when a user joins the player-vc while there is a player*
793
- *Allows you to inmplement a custom playerVoiceEmpty handler*
794
-
795
- - **`playerVoiceLeave`** ➡️ `(player, userId) => {}`~~
796
- *Added instead of the playerVoiceEmpty handler, emitted when a user leaves (or. switches away) the player-vc while there is a player*
797
- *Allows you to inmplement a custom playerVoiceEmpty handler*
798
-
799
- - Added the new events and configuration to the docs
800
-
801
- ## **Version 2.4.1**
802
-
803
- - Did some cleanup and comment removal + removed the playerVoiceEmptyStart because it would mean i'd need to add voice-state tracking, which wasn't the plan of doing by the client.
804
-
805
- ## **Verison 2.4.2**
806
-
807
- - Merged [PR#78](https://github.com/Tomato6966/lavalink-client/pull/78) from @hwangsihu - Added the configs to eslint ignore
808
- - Merged [PR#80](https://github.com/Tomato6966/lavalink-client/pull/80) from @EvilG-MC - Argument Typo fix in resume event type declaration
809
- - Merged [PR#83](https://github.com/Tomato6966/lavalink-client/pull/83) from @EvilG-MC - Fix if statement in Node#syncPlayerData() to allow syncing of "single entry objects"
810
- - Some minor improvements by removing unnecessary spreading
811
-
812
- ## **Version 2.4.3**
813
- - `managerOptions#playerOptions.onDisconnect.autoReconnect`:
814
- - Added the option `managerOptions#playerOptions.onDisconnect.autoReconnectOnlyWithTracks` to control wether to try reconnecting only when there are tracks in the queue / current track or not
815
- - Added a new debug log for that
816
- - Added the try to play the next track if there is no current track
817
- - *There was a problem trying to auto-reconnect on-Disconnect while the queue was empty, which caused the player to get destroyed by that and log the error in console "`There is no Track in the Queue, nor provided in the PlayOptions`"*
818
- - *Now you have to handle that case manually if you want to or set autoReconnectOnlyWithTracks to false (default)*
819
-
820
-
821
- ## **Version 2.4.4 - Version 2.4.6**
822
- - `player.changeNode()` is fixed and works - thanks to @PandaIN95
823
- - The code got re-formatted and re-structured, no code-changes are needed to be made, but it's now cleaner & more readable in some areas
824
- - The same for the testbot Folder(s), also it imports lavalink-client directly, so you can just copy it and move on from it.
825
- - Some minor Fixess:
826
- - Autoplay sometimes doesn't get called when previousAutoplay call failed.
827
- - remove structuredClone so that it works in bun more stable
828
- - Player Options Validation also allows single property objects
829
- - Some typos were fixed
830
-
831
-
832
-
833
- ## **Version 2.5.0**
834
- - Deps got updated
835
- - Handling of parsing url got updated to use the URL object
836
- - Flowerytts requests needs to be changed now:
837
-
838
- ```js
839
- const query = "Hello World How are you?";
840
- const voice = "Ava";
841
- const speed = "1.0";
842
-
843
- const fttsParams = new URLSearchParams();
844
- if(voice) fttsParams.append("voice", voice);
845
- if(speed) fttsParams.append("speed", speed);
846
-
847
- const response = await player.search({
848
- // For flowerytts you need to send a URL
849
- // and if you want to add optiojns, this is how you add the params to the query..
850
- query: `${encodeURI(query)}${fttsParams.size ? `?${fttsParams.toString()}` : ""}`,
851
- source: "ftts"
852
- }, interaction.user);
853
- ```
458
+ </div>
package/dist/index.d.mts CHANGED
@@ -2753,6 +2753,10 @@ interface ManagerPlayerOptions {
2753
2753
  };
2754
2754
  useUnresolvedData?: boolean;
2755
2755
  }
2756
+ type DeepRequired<T> = {
2757
+ [K in keyof T]-?: NonNullable<T[K]> extends object ? DeepRequired<NonNullable<T[K]>> : NonNullable<T[K]>;
2758
+ };
2759
+ type RequiredManagerOptions = DeepRequired<ManagerOptions>;
2756
2760
  /** Manager Options used to create the manager */
2757
2761
  interface ManagerOptions {
2758
2762
  /** The Node Options, for all Nodes! (on init) */
@@ -3043,4 +3047,4 @@ declare const LavalinkPlugins: {
3043
3047
  /** Lavalink Sources regexes for url validations */
3044
3048
  declare const SourceLinksRegexes: Record<SourcesRegex, RegExp>;
3045
3049
 
3046
- export { type AudioOutputs, type Awaitable, type Base64, type BaseNodeStats, type BasePlayOptions, type BotClientOptions, type CPUStats, type ChannelDeletePacket, type ChannelMixFilter, type ClientCustomSearchPlatformUtils, type ClientSearchPlatform, DebugEvents, DefaultQueueStore, DefaultSources, DestroyReasons, type DestroyReasonsType, DisconnectReasons, type DisconnectReasonsType, type DistortionFilter, type DuncteSearchPlatform, type EQBand, EQList, type Exception, type FailingAddress, type FilterData, FilterManager, type FloatNumber, type FrameStats, type GitObject, type GuildShardPayload, type IntegerNumber, type InvalidLavalinkRestRequest, type JioSaavnSearchPlatform, type KaraokeFilter, type LavaSearchFilteredResponse, type LavaSearchQuery, type LavaSearchResponse, type LavaSearchType, type LavaSrcSearchPlatform, type LavaSrcSearchPlatformBase, type LavalinkClientSearchPlatform, type LavalinkClientSearchPlatformResolve, type LavalinkFilterData, type LavalinkInfo, LavalinkManager, type LavalinkManagerEvents, LavalinkNode, type LavalinkNodeIdentifier, type LavalinkNodeOptions, type LavalinkPlayOptions, type LavalinkPlayer, type LavalinkPlayerVoice, type LavalinkPlayerVoiceOptions, type LavalinkPlugin_JioSaavn_SourceNames, type LavalinkPlugin_LavaSrc_SourceNames, LavalinkPlugins, type LavalinkSearchPlatform, type LavalinkSourceNames, type LavalinkTrack, type LavalinkTrackInfo, type LoadTypes, type LowPassFilter, type LyricsEvent, type LyricsEventType, type LyricsFoundEvent, type LyricsLine, type LyricsLineEvent, type LyricsNotFoundEvent, type LyricsResult, type ManagerOptions, type ManagerPlayerOptions, type ManagerQueueOptions, ManagerUtils, type MemoryStats, MiniMap, type MiniMapConstructor, type ModifyRequest, NodeManager, type NodeManagerEvents, type NodeMessage, type NodeStats, NodeSymbol, type Opaque, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerFilters, type PlayerJson, type PlayerOptions, type PlayerUpdateInfo, type PlaylistInfo, type PluginInfo, type PluginObject, Queue, type QueueChangesWatcher, QueueSaver, type QueueStoreManager, QueueSymbol, type RepeatMode, type RotationFilter, type RoutePlanner, type RoutePlannerTypes, type SearchPlatform, type SearchQuery, type SearchResult, type Session, type Severity, SourceLinksRegexes, type SourceNames, type SourcesRegex, type SponsorBlockChapterStarted, type SponsorBlockChaptersLoaded, type SponsorBlockSegment, type SponsorBlockSegmentEventType, type SponsorBlockSegmentEvents, type SponsorBlockSegmentSkipped, type SponsorBlockSegmentsLoaded, type State, type StoredQueue, type TimescaleFilter, type Track, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackInfo, type TrackStartEvent, type TrackStuckEvent, TrackSymbol, type TremoloFilter, type UnresolvedQuery, type UnresolvedSearchResult, type UnresolvedTrack, type UnresolvedTrackInfo, UnresolvedTrackSymbol, type VersionObject, type VibratoFilter, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent, type anyObject, audioOutputsData, parseLavalinkConnUrl, queueTrackEnd, safeStringify, validSponsorBlocks };
3050
+ export { type AudioOutputs, type Awaitable, type Base64, type BaseNodeStats, type BasePlayOptions, type BotClientOptions, type CPUStats, type ChannelDeletePacket, type ChannelMixFilter, type ClientCustomSearchPlatformUtils, type ClientSearchPlatform, DebugEvents, type DeepRequired, DefaultQueueStore, DefaultSources, DestroyReasons, type DestroyReasonsType, DisconnectReasons, type DisconnectReasonsType, type DistortionFilter, type DuncteSearchPlatform, type EQBand, EQList, type Exception, type FailingAddress, type FilterData, FilterManager, type FloatNumber, type FrameStats, type GitObject, type GuildShardPayload, type IntegerNumber, type InvalidLavalinkRestRequest, type JioSaavnSearchPlatform, type KaraokeFilter, type LavaSearchFilteredResponse, type LavaSearchQuery, type LavaSearchResponse, type LavaSearchType, type LavaSrcSearchPlatform, type LavaSrcSearchPlatformBase, type LavalinkClientSearchPlatform, type LavalinkClientSearchPlatformResolve, type LavalinkFilterData, type LavalinkInfo, LavalinkManager, type LavalinkManagerEvents, LavalinkNode, type LavalinkNodeIdentifier, type LavalinkNodeOptions, type LavalinkPlayOptions, type LavalinkPlayer, type LavalinkPlayerVoice, type LavalinkPlayerVoiceOptions, type LavalinkPlugin_JioSaavn_SourceNames, type LavalinkPlugin_LavaSrc_SourceNames, LavalinkPlugins, type LavalinkSearchPlatform, type LavalinkSourceNames, type LavalinkTrack, type LavalinkTrackInfo, type LoadTypes, type LowPassFilter, type LyricsEvent, type LyricsEventType, type LyricsFoundEvent, type LyricsLine, type LyricsLineEvent, type LyricsNotFoundEvent, type LyricsResult, type ManagerOptions, type ManagerPlayerOptions, type ManagerQueueOptions, ManagerUtils, type MemoryStats, MiniMap, type MiniMapConstructor, type ModifyRequest, NodeManager, type NodeManagerEvents, type NodeMessage, type NodeStats, NodeSymbol, type Opaque, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerFilters, type PlayerJson, type PlayerOptions, type PlayerUpdateInfo, type PlaylistInfo, type PluginInfo, type PluginObject, Queue, type QueueChangesWatcher, QueueSaver, type QueueStoreManager, QueueSymbol, type RepeatMode, type RequiredManagerOptions, type RotationFilter, type RoutePlanner, type RoutePlannerTypes, type SearchPlatform, type SearchQuery, type SearchResult, type Session, type Severity, SourceLinksRegexes, type SourceNames, type SourcesRegex, type SponsorBlockChapterStarted, type SponsorBlockChaptersLoaded, type SponsorBlockSegment, type SponsorBlockSegmentEventType, type SponsorBlockSegmentEvents, type SponsorBlockSegmentSkipped, type SponsorBlockSegmentsLoaded, type State, type StoredQueue, type TimescaleFilter, type Track, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackInfo, type TrackStartEvent, type TrackStuckEvent, TrackSymbol, type TremoloFilter, type UnresolvedQuery, type UnresolvedSearchResult, type UnresolvedTrack, type UnresolvedTrackInfo, UnresolvedTrackSymbol, type VersionObject, type VibratoFilter, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent, type anyObject, audioOutputsData, parseLavalinkConnUrl, queueTrackEnd, safeStringify, validSponsorBlocks };
package/dist/index.d.ts CHANGED
@@ -2753,6 +2753,10 @@ interface ManagerPlayerOptions {
2753
2753
  };
2754
2754
  useUnresolvedData?: boolean;
2755
2755
  }
2756
+ type DeepRequired<T> = {
2757
+ [K in keyof T]-?: NonNullable<T[K]> extends object ? DeepRequired<NonNullable<T[K]>> : NonNullable<T[K]>;
2758
+ };
2759
+ type RequiredManagerOptions = DeepRequired<ManagerOptions>;
2756
2760
  /** Manager Options used to create the manager */
2757
2761
  interface ManagerOptions {
2758
2762
  /** The Node Options, for all Nodes! (on init) */
@@ -3043,4 +3047,4 @@ declare const LavalinkPlugins: {
3043
3047
  /** Lavalink Sources regexes for url validations */
3044
3048
  declare const SourceLinksRegexes: Record<SourcesRegex, RegExp>;
3045
3049
 
3046
- export { type AudioOutputs, type Awaitable, type Base64, type BaseNodeStats, type BasePlayOptions, type BotClientOptions, type CPUStats, type ChannelDeletePacket, type ChannelMixFilter, type ClientCustomSearchPlatformUtils, type ClientSearchPlatform, DebugEvents, DefaultQueueStore, DefaultSources, DestroyReasons, type DestroyReasonsType, DisconnectReasons, type DisconnectReasonsType, type DistortionFilter, type DuncteSearchPlatform, type EQBand, EQList, type Exception, type FailingAddress, type FilterData, FilterManager, type FloatNumber, type FrameStats, type GitObject, type GuildShardPayload, type IntegerNumber, type InvalidLavalinkRestRequest, type JioSaavnSearchPlatform, type KaraokeFilter, type LavaSearchFilteredResponse, type LavaSearchQuery, type LavaSearchResponse, type LavaSearchType, type LavaSrcSearchPlatform, type LavaSrcSearchPlatformBase, type LavalinkClientSearchPlatform, type LavalinkClientSearchPlatformResolve, type LavalinkFilterData, type LavalinkInfo, LavalinkManager, type LavalinkManagerEvents, LavalinkNode, type LavalinkNodeIdentifier, type LavalinkNodeOptions, type LavalinkPlayOptions, type LavalinkPlayer, type LavalinkPlayerVoice, type LavalinkPlayerVoiceOptions, type LavalinkPlugin_JioSaavn_SourceNames, type LavalinkPlugin_LavaSrc_SourceNames, LavalinkPlugins, type LavalinkSearchPlatform, type LavalinkSourceNames, type LavalinkTrack, type LavalinkTrackInfo, type LoadTypes, type LowPassFilter, type LyricsEvent, type LyricsEventType, type LyricsFoundEvent, type LyricsLine, type LyricsLineEvent, type LyricsNotFoundEvent, type LyricsResult, type ManagerOptions, type ManagerPlayerOptions, type ManagerQueueOptions, ManagerUtils, type MemoryStats, MiniMap, type MiniMapConstructor, type ModifyRequest, NodeManager, type NodeManagerEvents, type NodeMessage, type NodeStats, NodeSymbol, type Opaque, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerFilters, type PlayerJson, type PlayerOptions, type PlayerUpdateInfo, type PlaylistInfo, type PluginInfo, type PluginObject, Queue, type QueueChangesWatcher, QueueSaver, type QueueStoreManager, QueueSymbol, type RepeatMode, type RotationFilter, type RoutePlanner, type RoutePlannerTypes, type SearchPlatform, type SearchQuery, type SearchResult, type Session, type Severity, SourceLinksRegexes, type SourceNames, type SourcesRegex, type SponsorBlockChapterStarted, type SponsorBlockChaptersLoaded, type SponsorBlockSegment, type SponsorBlockSegmentEventType, type SponsorBlockSegmentEvents, type SponsorBlockSegmentSkipped, type SponsorBlockSegmentsLoaded, type State, type StoredQueue, type TimescaleFilter, type Track, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackInfo, type TrackStartEvent, type TrackStuckEvent, TrackSymbol, type TremoloFilter, type UnresolvedQuery, type UnresolvedSearchResult, type UnresolvedTrack, type UnresolvedTrackInfo, UnresolvedTrackSymbol, type VersionObject, type VibratoFilter, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent, type anyObject, audioOutputsData, parseLavalinkConnUrl, queueTrackEnd, safeStringify, validSponsorBlocks };
3050
+ export { type AudioOutputs, type Awaitable, type Base64, type BaseNodeStats, type BasePlayOptions, type BotClientOptions, type CPUStats, type ChannelDeletePacket, type ChannelMixFilter, type ClientCustomSearchPlatformUtils, type ClientSearchPlatform, DebugEvents, type DeepRequired, DefaultQueueStore, DefaultSources, DestroyReasons, type DestroyReasonsType, DisconnectReasons, type DisconnectReasonsType, type DistortionFilter, type DuncteSearchPlatform, type EQBand, EQList, type Exception, type FailingAddress, type FilterData, FilterManager, type FloatNumber, type FrameStats, type GitObject, type GuildShardPayload, type IntegerNumber, type InvalidLavalinkRestRequest, type JioSaavnSearchPlatform, type KaraokeFilter, type LavaSearchFilteredResponse, type LavaSearchQuery, type LavaSearchResponse, type LavaSearchType, type LavaSrcSearchPlatform, type LavaSrcSearchPlatformBase, type LavalinkClientSearchPlatform, type LavalinkClientSearchPlatformResolve, type LavalinkFilterData, type LavalinkInfo, LavalinkManager, type LavalinkManagerEvents, LavalinkNode, type LavalinkNodeIdentifier, type LavalinkNodeOptions, type LavalinkPlayOptions, type LavalinkPlayer, type LavalinkPlayerVoice, type LavalinkPlayerVoiceOptions, type LavalinkPlugin_JioSaavn_SourceNames, type LavalinkPlugin_LavaSrc_SourceNames, LavalinkPlugins, type LavalinkSearchPlatform, type LavalinkSourceNames, type LavalinkTrack, type LavalinkTrackInfo, type LoadTypes, type LowPassFilter, type LyricsEvent, type LyricsEventType, type LyricsFoundEvent, type LyricsLine, type LyricsLineEvent, type LyricsNotFoundEvent, type LyricsResult, type ManagerOptions, type ManagerPlayerOptions, type ManagerQueueOptions, ManagerUtils, type MemoryStats, MiniMap, type MiniMapConstructor, type ModifyRequest, NodeManager, type NodeManagerEvents, type NodeMessage, type NodeStats, NodeSymbol, type Opaque, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerFilters, type PlayerJson, type PlayerOptions, type PlayerUpdateInfo, type PlaylistInfo, type PluginInfo, type PluginObject, Queue, type QueueChangesWatcher, QueueSaver, type QueueStoreManager, QueueSymbol, type RepeatMode, type RequiredManagerOptions, type RotationFilter, type RoutePlanner, type RoutePlannerTypes, type SearchPlatform, type SearchQuery, type SearchResult, type Session, type Severity, SourceLinksRegexes, type SourceNames, type SourcesRegex, type SponsorBlockChapterStarted, type SponsorBlockChaptersLoaded, type SponsorBlockSegment, type SponsorBlockSegmentEventType, type SponsorBlockSegmentEvents, type SponsorBlockSegmentSkipped, type SponsorBlockSegmentsLoaded, type State, type StoredQueue, type TimescaleFilter, type Track, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackInfo, type TrackStartEvent, type TrackStuckEvent, TrackSymbol, type TremoloFilter, type UnresolvedQuery, type UnresolvedSearchResult, type UnresolvedTrack, type UnresolvedTrackInfo, UnresolvedTrackSymbol, type VersionObject, type VibratoFilter, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent, type anyObject, audioOutputsData, parseLavalinkConnUrl, queueTrackEnd, safeStringify, validSponsorBlocks };
package/dist/index.js CHANGED
@@ -4461,13 +4461,16 @@ var LavalinkManager = class extends import_events2.EventEmitter {
4461
4461
  * @returns
4462
4462
  */
4463
4463
  applyOptions(options) {
4464
- this.options = {
4464
+ const optionsToAssign = {
4465
+ ...options,
4466
+ // allow users to apply other options if they need to.
4465
4467
  client: {
4466
4468
  ...options?.client,
4467
4469
  id: options?.client?.id,
4468
4470
  username: options?.client?.username ?? "lavalink-client"
4469
4471
  },
4470
4472
  sendToShard: options?.sendToShard,
4473
+ autoMove: options?.autoMove ?? false,
4471
4474
  nodes: options?.nodes,
4472
4475
  playerOptions: {
4473
4476
  applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
@@ -4515,6 +4518,7 @@ var LavalinkManager = class extends import_events2.EventEmitter {
4515
4518
  }
4516
4519
  }
4517
4520
  };
4521
+ this.options = optionsToAssign;
4518
4522
  return;
4519
4523
  }
4520
4524
  /**
package/dist/index.mjs CHANGED
@@ -4401,13 +4401,16 @@ var LavalinkManager = class extends EventEmitter2 {
4401
4401
  * @returns
4402
4402
  */
4403
4403
  applyOptions(options) {
4404
- this.options = {
4404
+ const optionsToAssign = {
4405
+ ...options,
4406
+ // allow users to apply other options if they need to.
4405
4407
  client: {
4406
4408
  ...options?.client,
4407
4409
  id: options?.client?.id,
4408
4410
  username: options?.client?.username ?? "lavalink-client"
4409
4411
  },
4410
4412
  sendToShard: options?.sendToShard,
4413
+ autoMove: options?.autoMove ?? false,
4411
4414
  nodes: options?.nodes,
4412
4415
  playerOptions: {
4413
4416
  applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
@@ -4455,6 +4458,7 @@ var LavalinkManager = class extends EventEmitter2 {
4455
4458
  }
4456
4459
  }
4457
4460
  };
4461
+ this.options = optionsToAssign;
4458
4462
  return;
4459
4463
  }
4460
4464
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "2.5.9",
3
+ "version": "2.5.10",
4
4
  "description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -72,5 +72,10 @@
72
72
  "engines": {
73
73
  "node": ">=18.0.0",
74
74
  "bun": ">=1.1.27"
75
+ },
76
+ "pnpm": {
77
+ "onlyBuiltDependencies": [
78
+ "esbuild"
79
+ ]
75
80
  }
76
81
  }