lavalink-client 2.2.1 → 2.2.2
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 +45 -19
- package/dist/cjs/structures/Filters.js +4 -4
- package/dist/cjs/structures/LavalinkManager.d.ts +6 -6
- package/dist/cjs/structures/LavalinkManager.js +1 -1
- package/dist/cjs/structures/LavalinkManagerStatics.js +1 -1
- package/dist/cjs/structures/Node.js +11 -4
- package/dist/cjs/structures/NodeManager.js +3 -1
- package/dist/cjs/structures/Player.js +1 -0
- package/dist/cjs/structures/Queue.d.ts +47 -0
- package/dist/cjs/structures/Queue.js +104 -1
- package/dist/cjs/structures/Track.d.ts +1 -0
- package/dist/cjs/structures/Utils.js +5 -4
- package/dist/esm/structures/Filters.js +4 -4
- package/dist/esm/structures/LavalinkManager.d.ts +6 -6
- package/dist/esm/structures/LavalinkManager.js +1 -1
- package/dist/esm/structures/LavalinkManagerStatics.js +1 -1
- package/dist/esm/structures/Node.js +11 -4
- package/dist/esm/structures/NodeManager.js +3 -1
- package/dist/esm/structures/Player.js +1 -0
- package/dist/esm/structures/Queue.d.ts +47 -0
- package/dist/esm/structures/Queue.js +104 -1
- package/dist/esm/structures/Track.d.ts +1 -0
- package/dist/esm/structures/Utils.js +5 -4
- package/dist/types/structures/LavalinkManager.d.ts +6 -6
- package/dist/types/structures/Queue.d.ts +47 -0
- package/dist/types/structures/Track.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.
|
|
3
3
|
|
|
4
4
|
<div align="center">
|
|
5
|
-
<p>
|
|
5
|
+
<p>
|
|
6
6
|
<img src="https://madewithlove.now.sh/at?heart=true&template=for-the-badge" alt="Made with love in Austria">
|
|
7
7
|
<img alt="Made with TypeScript" src="https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white">
|
|
8
8
|
</p>
|
|
@@ -75,7 +75,7 @@ Check out the [Documentation](https://lc4.gitbook.io/lavalink-client) | or the [
|
|
|
75
75
|
|
|
76
76
|
- ✨ Choose able queue stores (maps, collections, redis, databases, ...)
|
|
77
77
|
- You can create your own queueStore, thus make it easy to sync queues accross multiple connections (e.g. dashboard-bot)
|
|
78
|
-
- Automated Queue Sync methods
|
|
78
|
+
- Automated Queue Sync methods
|
|
79
79
|
- Automated unresolveable Tracks (save the queries as Partial Track Objects -> Fetch the tracks only once they are gonna play)
|
|
80
80
|
|
|
81
81
|
- 😍 Included Filter & Equalizer Management
|
|
@@ -91,20 +91,20 @@ Check out the [Documentation](https://lc4.gitbook.io/lavalink-client) | or the [
|
|
|
91
91
|
|
|
92
92
|
- 🛡️ Client Validations
|
|
93
93
|
- Allows you to whitelist links and even blacklist links / words / domain names, so that it doesn't allow requests you don't want!
|
|
94
|
-
- Checks almost all Lavalink Requests for out of bound errors, right before the request is made to prevent process breaking errors.
|
|
94
|
+
- Checks almost all Lavalink Requests for out of bound errors, right before the request is made to prevent process breaking errors.
|
|
95
95
|
|
|
96
96
|
- 🧑💻 Memory friendly and easy style
|
|
97
97
|
- Only the required data is displayed, and the store-way & types match Lavalink#IMPLEMENTATION.md
|
|
98
98
|
|
|
99
99
|
- 😘 Automated Handlings
|
|
100
|
-
- Skips the songs, on TrackEnd, TrackStuck, TrackError,
|
|
100
|
+
- Skips the songs, on TrackEnd, TrackStuck, TrackError,
|
|
101
101
|
- Destroys the player on channeldelete
|
|
102
102
|
- Pauses / resumes the player if it get's muted / unmuted (server-wide) [soon]
|
|
103
103
|
- ...
|
|
104
104
|
|
|
105
105
|
- 😁 Much much more!
|
|
106
106
|
|
|
107
|
-
***
|
|
107
|
+
***
|
|
108
108
|
|
|
109
109
|
# All Events:
|
|
110
110
|
|
|
@@ -169,10 +169,10 @@ class myCustomWatcher implements QueueChangesWatcher {
|
|
|
169
169
|
this.client = client;
|
|
170
170
|
}
|
|
171
171
|
shuffled(guildId, oldStoredQueue, newStoredQueue) {
|
|
172
|
-
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: Queue got shuffled`)
|
|
172
|
+
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: Queue got shuffled`)
|
|
173
173
|
}
|
|
174
174
|
tracksAdd(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
|
|
175
|
-
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got added into the Queue at position #${position}`);
|
|
175
|
+
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got added into the Queue at position #${position}`);
|
|
176
176
|
}
|
|
177
177
|
tracksRemoved(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
|
|
178
178
|
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got removed from the Queue at position #${position}`);
|
|
@@ -208,9 +208,9 @@ client.lavalink.nodeManager.on("resumed", (node, payload, fetchedPlayers) => {
|
|
|
208
208
|
await player.queue.utils.sync(); // only works with a queuestore
|
|
209
209
|
// you can now overwride the player.queue.current track from the fetchedPlayer, or use the one from the queue.uztils.sync function
|
|
210
210
|
// continue with your resuming code...
|
|
211
|
-
}
|
|
211
|
+
}
|
|
212
212
|
})
|
|
213
|
-
```
|
|
213
|
+
```
|
|
214
214
|
|
|
215
215
|
***
|
|
216
216
|
|
|
@@ -228,7 +228,7 @@ const extraParams = new URLSearchParams();
|
|
|
228
228
|
if(voice) extraParams.append(`voice`, voice);
|
|
229
229
|
|
|
230
230
|
// all params for flowertts can be found here: https://flowery.pw/docs
|
|
231
|
-
const response = await player.search({
|
|
231
|
+
const response = await player.search({
|
|
232
232
|
query: `${query}`,
|
|
233
233
|
extraQueryUrlParams: extraParams, // as of my knowledge this is currently only used for flowertts, adjusting the playback url dynamically mid-request
|
|
234
234
|
source: "ftts"
|
|
@@ -236,7 +236,7 @@ const response = await player.search({
|
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
|
|
239
|
-
***
|
|
239
|
+
***
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
# UpdateLog
|
|
@@ -254,8 +254,8 @@ const response = await player.search({
|
|
|
254
254
|
- `player.deleteSponsorBlock()` / `node.deleteSponsorBlock()`
|
|
255
255
|
- That Plugin adds following **Events** to the **Manager**: `"SegmentsLoaded"`, `"SegmentSkipped"`, `"ChapterStarted"`, `"ChaptersLoaded"`
|
|
256
256
|
- Example Bot show example in autoplayFunction how to "disable" / "enable" Autoplay with bot data variables.
|
|
257
|
-
- 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.
|
|
258
|
-
- 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)
|
|
257
|
+
- 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.
|
|
258
|
+
- 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)
|
|
259
259
|
- 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)
|
|
260
260
|
- Added `ManagerOptions#linksAllowed` if set to false, it does not allow requests which are links
|
|
261
261
|
- Moved `ManaagerOptions#debugOptions` to `ManaagerOptions#advancedOptions.debugOptions`
|
|
@@ -263,10 +263,10 @@ const response = await player.search({
|
|
|
263
263
|
### **Version 1.2.1**
|
|
264
264
|
- Adjusted `player.stopPlaying()`
|
|
265
265
|
- There are now following parameters. `stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false)`.
|
|
266
|
-
- 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.
|
|
266
|
+
- 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.
|
|
267
267
|
- Fixed that it looped the current track if repeatmode === "track" / "queue". (it stops playing and loop stays)
|
|
268
268
|
- Implemented a `parseLavalinkConnUrl(connectionUrl:string)` Util Function.
|
|
269
|
-
- It allows you to parse Lavalink Connection Data of a Lavalink Connection Url.
|
|
269
|
+
- It allows you to parse Lavalink Connection Data of a Lavalink Connection Url.
|
|
270
270
|
Pattern: `lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>`
|
|
271
271
|
- Note that the nodeId and NodeAuthorization must be encoded via encodeURIComponents before you provide it into the function.
|
|
272
272
|
- The function will return the following: `{ id: string, authorization: string, host: string, port: number }`
|
|
@@ -298,10 +298,10 @@ const response = await player.search({
|
|
|
298
298
|
# and after connecting the nodeManager.on("resumed", (node, payload, players) => {}) will be executed, where you can sync the players!
|
|
299
299
|
|
|
300
300
|
# Node Options got adjusted # It's a property not a method should be treated readonly
|
|
301
|
-
+ node.resuming: { enabled: boolean, timeout: number | null };
|
|
301
|
+
+ node.resuming: { enabled: boolean, timeout: number | null };
|
|
302
302
|
|
|
303
303
|
# Player function got added to stop playing without disconnecting
|
|
304
|
-
+ player.stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false);
|
|
304
|
+
+ player.stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false);
|
|
305
305
|
|
|
306
306
|
# Node functions for sponsorBlock Plugin (https://github.com/topi314/Sponsorblock-Plugin) got added
|
|
307
307
|
+ deleteSponsorBlock(player:Player)
|
|
@@ -321,7 +321,7 @@ const response = await player.search({
|
|
|
321
321
|
# Lavalink track.userData got added (basically same feature as my custom pluginInfo.clientData system)
|
|
322
322
|
# You only get the track.userData data through playerUpdate object
|
|
323
323
|
```
|
|
324
|
-
In one of the next updates, there will be more queueWatcher options and more custom nodeevents to trace
|
|
324
|
+
In one of the next updates, there will be more queueWatcher options and more custom nodeevents to trace
|
|
325
325
|
|
|
326
326
|
Most features of this update got tested, but if you encounter any bugs feel free to open an issue!
|
|
327
327
|
|
|
@@ -378,9 +378,35 @@ const extraParams = new URLSearchParams();
|
|
|
378
378
|
if(voice) extraParams.append(`voice`, voice);
|
|
379
379
|
|
|
380
380
|
// all params for flowertts can be found here: https://flowery.pw/docs
|
|
381
|
-
const response = await player.search({
|
|
381
|
+
const response = await player.search({
|
|
382
382
|
query: `${query}`,
|
|
383
383
|
extraQueryUrlParams: extraParams, // as of my knowledge this is currently only used for flowertts, adjusting the playback url dynamically mid-request
|
|
384
384
|
source: "ftts"
|
|
385
385
|
}, interaction.user);
|
|
386
386
|
```
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
## **Version 2.2.2**
|
|
390
|
+
- 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.
|
|
391
|
+
- Fixed the handling of the previous track array ( sometimes it adds "null", due to lavalink errors )
|
|
392
|
+
- Added new functions for the queue, to make migrations and coding easier for beginners,
|
|
393
|
+
- ` 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"
|
|
394
|
+
- *Neat 1-liner: ` await player.queue.shiftPrevious().then(clientTrack => player.play({ clientTrack })) `*
|
|
395
|
+
- ` await player.queue.remove(removeQuery) ` -> Remove function to remove stuff from the queue.tracks array., following params are valid:
|
|
396
|
+
- 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)*
|
|
397
|
+
- Single Track / UnresolveTrack, e.g. ` await player.queue.remove(player.queue.tracks[player.queue.tracks.length - 1]); ` *(would remove the last track)*
|
|
398
|
+
- Array of track-indexes, e.g. ` await player.queue.remove([1, 4, 5]) ` *(Would remove track #1, #4 and #5)*
|
|
399
|
+
- Single track index, e.g. ` await player.queue.remove(5) ` *(would remove the #5 track from the queue)*
|
|
400
|
+
- **NOTE:** I still highly recommend, to use the ` player.queue.splice() ` function for mutating the queue:
|
|
401
|
+
- it is possible to remove single tracks, multiple tracks and insert tracks at specific positions!
|
|
402
|
+
- *the remove function haven't been fully tested yet*
|
|
403
|
+
- Added `track.pluginInfo.clientData?.previousTrack` handling:
|
|
404
|
+
- 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:
|
|
405
|
+
```js
|
|
406
|
+
const previousTrack = await player.queue.shiftPrevious();
|
|
407
|
+
if(previousTrack) {
|
|
408
|
+
const previousClientData = previousTrack.pluginInfo.clientData || {};
|
|
409
|
+
previousTrack.pluginInfo.clientData = { previousTrack: true, ...previousClientData }
|
|
410
|
+
await player.play({ clientTrack: previousTrack });
|
|
411
|
+
}
|
|
412
|
+
```
|
|
@@ -78,9 +78,9 @@ class FilterManager {
|
|
|
78
78
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
79
79
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
80
80
|
},
|
|
81
|
-
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
81
|
+
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
82
82
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
83
|
-
// "adaptive": true // false
|
|
83
|
+
// "adaptive": true // false
|
|
84
84
|
},
|
|
85
85
|
"echo": { // Self-explanatory; provides an echo effect.
|
|
86
86
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
@@ -253,9 +253,9 @@ class FilterManager {
|
|
|
253
253
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
254
254
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
255
255
|
},
|
|
256
|
-
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
256
|
+
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
257
257
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
258
|
-
// "adaptive": true // false
|
|
258
|
+
// "adaptive": true // false
|
|
259
259
|
},
|
|
260
260
|
"echo": { // Self-explanatory; provides an echo effect.
|
|
261
261
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
@@ -95,7 +95,7 @@ export interface LavalinkManagerEvents {
|
|
|
95
95
|
* Emitted when a Track finished.
|
|
96
96
|
* @event Manager#trackEnd
|
|
97
97
|
*/
|
|
98
|
-
"trackEnd": (player: Player, track: Track, payload: TrackEndEvent) => void;
|
|
98
|
+
"trackEnd": (player: Player, track: Track | null, payload: TrackEndEvent) => void;
|
|
99
99
|
/**
|
|
100
100
|
* Emitted when a Track got stuck while playing.
|
|
101
101
|
* @event Manager#trackStuck
|
|
@@ -110,7 +110,7 @@ export interface LavalinkManagerEvents {
|
|
|
110
110
|
* Emitted when the Playing finished and no more tracks in the queue.
|
|
111
111
|
* @event Manager#queueEnd
|
|
112
112
|
*/
|
|
113
|
-
"queueEnd": (player: Player, track: Track, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
113
|
+
"queueEnd": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
114
114
|
/**
|
|
115
115
|
* Emitted when a Player is created.
|
|
116
116
|
* @event Manager#playerCreate
|
|
@@ -147,28 +147,28 @@ export interface LavalinkManagerEvents {
|
|
|
147
147
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentsloaded
|
|
148
148
|
* @event Manager#trackError
|
|
149
149
|
*/
|
|
150
|
-
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentsLoaded) => void;
|
|
150
|
+
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentsLoaded) => void;
|
|
151
151
|
/**
|
|
152
152
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
153
153
|
* Emitted when a specific Segment was skipped
|
|
154
154
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentskipped
|
|
155
155
|
* @event Manager#trackError
|
|
156
156
|
*/
|
|
157
|
-
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentSkipped) => void;
|
|
157
|
+
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentSkipped) => void;
|
|
158
158
|
/**
|
|
159
159
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
160
160
|
* Emitted when a specific Chapter starts playing
|
|
161
161
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chapterstarted
|
|
162
162
|
* @event Manager#trackError
|
|
163
163
|
*/
|
|
164
|
-
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChapterStarted) => void;
|
|
164
|
+
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChapterStarted) => void;
|
|
165
165
|
/**
|
|
166
166
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
167
167
|
* Emitted when Chapters are loaded
|
|
168
168
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chaptersloaded
|
|
169
169
|
* @event Manager#trackError
|
|
170
170
|
*/
|
|
171
|
-
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChaptersLoaded) => void;
|
|
171
|
+
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
|
|
172
172
|
}
|
|
173
173
|
export interface LavalinkManager {
|
|
174
174
|
/** @private */
|
|
@@ -257,7 +257,7 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
257
257
|
const oldPlayer = this.getPlayer(guildId);
|
|
258
258
|
if (!oldPlayer)
|
|
259
259
|
return;
|
|
260
|
-
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
260
|
+
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
261
261
|
if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
262
262
|
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
263
263
|
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
|
|
@@ -355,9 +355,11 @@ class LavalinkNode {
|
|
|
355
355
|
destroy(destroyReason, deleteNode = true) {
|
|
356
356
|
if (!this.connected)
|
|
357
357
|
return;
|
|
358
|
-
const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id
|
|
358
|
+
const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id === this.id);
|
|
359
359
|
if (players)
|
|
360
|
-
players.forEach(p =>
|
|
360
|
+
players.forEach(p => {
|
|
361
|
+
p.destroy(destroyReason || Player_1.DestroyReasons.NodeDestroy);
|
|
362
|
+
});
|
|
361
363
|
this.socket.close(1000, "Node-Destroy");
|
|
362
364
|
this.socket.removeAllListeners();
|
|
363
365
|
this.socket = null;
|
|
@@ -880,7 +882,7 @@ class LavalinkNode {
|
|
|
880
882
|
// remove tracks from the queue
|
|
881
883
|
if (player.repeatMode !== "track" || player.get("internal_skipped"))
|
|
882
884
|
await (0, Utils_1.queueTrackEnd)(player);
|
|
883
|
-
else if (player.queue.current) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
885
|
+
else if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
884
886
|
player.queue.previous.unshift(player.queue.current);
|
|
885
887
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
886
888
|
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
@@ -1029,7 +1031,12 @@ class LavalinkNode {
|
|
|
1029
1031
|
}
|
|
1030
1032
|
}
|
|
1031
1033
|
player.set("internal_autoplayStopPlaying", undefined);
|
|
1032
|
-
|
|
1034
|
+
if (track && !track?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
1035
|
+
player.queue.previous.unshift(track);
|
|
1036
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1037
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
1038
|
+
await player.queue.utils.save();
|
|
1039
|
+
}
|
|
1033
1040
|
if (payload?.reason !== "stopped") {
|
|
1034
1041
|
await player.queue.utils.save();
|
|
1035
1042
|
}
|
|
@@ -11,7 +11,9 @@ class NodeManager extends stream_1.EventEmitter {
|
|
|
11
11
|
super();
|
|
12
12
|
this.LavalinkManager = LavalinkManager;
|
|
13
13
|
if (this.LavalinkManager.options.nodes)
|
|
14
|
-
this.LavalinkManager.options.nodes.forEach(node =>
|
|
14
|
+
this.LavalinkManager.options.nodes.forEach(node => {
|
|
15
|
+
this.createNode(node);
|
|
16
|
+
});
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Disconnects all Nodes from lavalink ws sockets
|
|
@@ -281,6 +281,7 @@ class Player {
|
|
|
281
281
|
if (this.paused && !this.playing)
|
|
282
282
|
throw new Error("Player is already paused - not able to pause.");
|
|
283
283
|
this.paused = true;
|
|
284
|
+
this.lastPositionChange = null; // needs to removed to not cause issues
|
|
284
285
|
const now = performance.now();
|
|
285
286
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
|
|
286
287
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
@@ -113,4 +113,51 @@ export declare class Queue {
|
|
|
113
113
|
* @returns {Track} Spliced Track
|
|
114
114
|
*/
|
|
115
115
|
splice(index: number, amount: number, TrackOrTracks?: Track | UnresolvedTrack | (Track | UnresolvedTrack)[]): any;
|
|
116
|
+
/**
|
|
117
|
+
* Remove stuff from the queue.tracks array
|
|
118
|
+
* - single Track | UnresolvedTrack
|
|
119
|
+
* - multiple Track | UnresovedTrack
|
|
120
|
+
* - at the index or multiple indexes
|
|
121
|
+
* @param removeQueryTrack
|
|
122
|
+
* @returns null (if nothing was removed) / { removed } where removed is an array with all removed elements
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```js
|
|
126
|
+
* // remove single track
|
|
127
|
+
*
|
|
128
|
+
* const track = player.queue.tracks[4];
|
|
129
|
+
* await player.queue.remove(track);
|
|
130
|
+
*
|
|
131
|
+
* // if you already have the index you can straight up pass it too
|
|
132
|
+
* await player.queue.remove(4);
|
|
133
|
+
*
|
|
134
|
+
*
|
|
135
|
+
* // if you want to remove multiple tracks, e.g. from position 4 to position 10 you can do smt like this
|
|
136
|
+
* await player.queue.remove(player.queue.tracks.slice(4, 10)) // get's the tracks from 4 - 10, which then get's found in the remove function to be removed
|
|
137
|
+
*
|
|
138
|
+
* // I still highly suggest to use .splice!
|
|
139
|
+
*
|
|
140
|
+
* await player.queue.splice(4, 10); // removes at index 4, 10 tracks
|
|
141
|
+
*
|
|
142
|
+
* await player.queue.splice(1, 1); // removes at index 1, 1 track
|
|
143
|
+
*
|
|
144
|
+
* await player.queue.splice(4, 0, ...tracks) // removes 0 tracks at position 4, and then inserts all tracks after position 4.
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
remove<T extends Track | UnresolvedTrack | number | Track[] | UnresolvedTrack[] | number[] | (number | Track | UnresolvedTrack)[]>(removeQueryTrack: T): Promise<{
|
|
148
|
+
removed: (Track | UnresolvedTrack)[];
|
|
149
|
+
} | null>;
|
|
150
|
+
/**
|
|
151
|
+
* Shifts the previous array, to return the last previous track & thus remove it from the previous queue
|
|
152
|
+
* @returns
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```js
|
|
156
|
+
* // example on how to play the previous track again
|
|
157
|
+
* const previous = await player.queue.shiftPrevious(); // get the previous track and remove it from the previous queue array!!
|
|
158
|
+
* if(!previous) return console.error("No previous track found");
|
|
159
|
+
* await player.play({ clientTrack: previous }); // play it again
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
shiftPrevious(): Promise<Track>;
|
|
116
163
|
}
|
|
@@ -123,7 +123,7 @@ class Queue {
|
|
|
123
123
|
if (this.tracks.length <= 1)
|
|
124
124
|
return this.tracks.length;
|
|
125
125
|
// swap #1 and #2 if only 2 tracks.
|
|
126
|
-
if (this.tracks.length
|
|
126
|
+
if (this.tracks.length === 2) {
|
|
127
127
|
[this.tracks[0], this.tracks[1]] = [this.tracks[1], this.tracks[0]];
|
|
128
128
|
}
|
|
129
129
|
else { // randomly swap places.
|
|
@@ -197,5 +197,108 @@ class Queue {
|
|
|
197
197
|
// return the things
|
|
198
198
|
return spliced.length === 1 ? spliced[0] : spliced;
|
|
199
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Remove stuff from the queue.tracks array
|
|
202
|
+
* - single Track | UnresolvedTrack
|
|
203
|
+
* - multiple Track | UnresovedTrack
|
|
204
|
+
* - at the index or multiple indexes
|
|
205
|
+
* @param removeQueryTrack
|
|
206
|
+
* @returns null (if nothing was removed) / { removed } where removed is an array with all removed elements
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```js
|
|
210
|
+
* // remove single track
|
|
211
|
+
*
|
|
212
|
+
* const track = player.queue.tracks[4];
|
|
213
|
+
* await player.queue.remove(track);
|
|
214
|
+
*
|
|
215
|
+
* // if you already have the index you can straight up pass it too
|
|
216
|
+
* await player.queue.remove(4);
|
|
217
|
+
*
|
|
218
|
+
*
|
|
219
|
+
* // if you want to remove multiple tracks, e.g. from position 4 to position 10 you can do smt like this
|
|
220
|
+
* await player.queue.remove(player.queue.tracks.slice(4, 10)) // get's the tracks from 4 - 10, which then get's found in the remove function to be removed
|
|
221
|
+
*
|
|
222
|
+
* // I still highly suggest to use .splice!
|
|
223
|
+
*
|
|
224
|
+
* await player.queue.splice(4, 10); // removes at index 4, 10 tracks
|
|
225
|
+
*
|
|
226
|
+
* await player.queue.splice(1, 1); // removes at index 1, 1 track
|
|
227
|
+
*
|
|
228
|
+
* await player.queue.splice(4, 0, ...tracks) // removes 0 tracks at position 4, and then inserts all tracks after position 4.
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
async remove(removeQueryTrack) {
|
|
232
|
+
if (typeof removeQueryTrack === "number") {
|
|
233
|
+
const toRemove = this.tracks[removeQueryTrack];
|
|
234
|
+
if (!toRemove)
|
|
235
|
+
return null;
|
|
236
|
+
const removed = this.tracks.splice(removeQueryTrack, 1);
|
|
237
|
+
await this.utils.save();
|
|
238
|
+
console.log("0st", removed, toRemove);
|
|
239
|
+
return { removed };
|
|
240
|
+
}
|
|
241
|
+
if (Array.isArray(removeQueryTrack)) {
|
|
242
|
+
if (removeQueryTrack.every(v => typeof v === "number")) {
|
|
243
|
+
const removed = [];
|
|
244
|
+
for (const i of removeQueryTrack) {
|
|
245
|
+
if (this.tracks[i])
|
|
246
|
+
removed.push(...this.tracks.splice(i, 1));
|
|
247
|
+
}
|
|
248
|
+
console.log("1st", removed, removeQueryTrack);
|
|
249
|
+
if (!removed.length)
|
|
250
|
+
return null;
|
|
251
|
+
await this.utils.save();
|
|
252
|
+
return { removed };
|
|
253
|
+
}
|
|
254
|
+
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(({ v, i }) => removeQueryTrack.find(t => typeof t === "number" && (t === i) ||
|
|
255
|
+
typeof t === "object" && (t.encoded && t.encoded === v.encoded ||
|
|
256
|
+
t.info?.identifier && t.info.identifier === v.info?.identifier ||
|
|
257
|
+
t.info?.uri && t.info.uri === v.info?.uri ||
|
|
258
|
+
t.info?.title && t.info.title === v.info?.title ||
|
|
259
|
+
t.info?.isrc && t.info.isrc === v.info?.isrc ||
|
|
260
|
+
t.info?.artworkUrl && t.info.artworkUrl === v.info?.artworkUrl)));
|
|
261
|
+
if (!tracksToRemove.length)
|
|
262
|
+
return null;
|
|
263
|
+
const removed = [];
|
|
264
|
+
for (const { i } of tracksToRemove) {
|
|
265
|
+
if (this.tracks[i])
|
|
266
|
+
removed.push(...this.tracks.splice(i, 1));
|
|
267
|
+
}
|
|
268
|
+
await this.utils.save();
|
|
269
|
+
console.log("2nd", removed, tracksToRemove);
|
|
270
|
+
return { removed };
|
|
271
|
+
}
|
|
272
|
+
const toRemove = this.tracks.findIndex((v) => removeQueryTrack.encoded && removeQueryTrack.encoded === v.encoded ||
|
|
273
|
+
removeQueryTrack.info?.identifier && removeQueryTrack.info.identifier === v.info?.identifier ||
|
|
274
|
+
removeQueryTrack.info?.uri && removeQueryTrack.info.uri === v.info?.uri ||
|
|
275
|
+
removeQueryTrack.info?.title && removeQueryTrack.info.title === v.info?.title ||
|
|
276
|
+
removeQueryTrack.info?.isrc && removeQueryTrack.info.isrc === v.info?.isrc ||
|
|
277
|
+
removeQueryTrack.info?.artworkUrl && removeQueryTrack.info.artworkUrl === v.info?.artworkUrl);
|
|
278
|
+
if (toRemove < 0)
|
|
279
|
+
return null;
|
|
280
|
+
const removed = this.tracks.splice(toRemove, 1);
|
|
281
|
+
await this.utils.save();
|
|
282
|
+
console.log("3rd", removed, toRemove);
|
|
283
|
+
return { removed };
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Shifts the previous array, to return the last previous track & thus remove it from the previous queue
|
|
287
|
+
* @returns
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```js
|
|
291
|
+
* // example on how to play the previous track again
|
|
292
|
+
* const previous = await player.queue.shiftPrevious(); // get the previous track and remove it from the previous queue array!!
|
|
293
|
+
* if(!previous) return console.error("No previous track found");
|
|
294
|
+
* await player.play({ clientTrack: previous }); // play it again
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
async shiftPrevious() {
|
|
298
|
+
const removed = this.previous.shift();
|
|
299
|
+
if (removed)
|
|
300
|
+
await this.utils.save();
|
|
301
|
+
return removed ?? null;
|
|
302
|
+
}
|
|
200
303
|
}
|
|
201
304
|
exports.Queue = Queue;
|
|
@@ -345,10 +345,11 @@ class MiniMap extends Map {
|
|
|
345
345
|
}
|
|
346
346
|
exports.MiniMap = MiniMap;
|
|
347
347
|
async function queueTrackEnd(player) {
|
|
348
|
-
if (player.queue.current) { //
|
|
348
|
+
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
349
349
|
player.queue.previous.unshift(player.queue.current);
|
|
350
350
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
351
351
|
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
352
|
+
await player.queue.utils.save();
|
|
352
353
|
}
|
|
353
354
|
// and if repeatMode == queue, add it back to the queue!
|
|
354
355
|
if (player.repeatMode === "queue" && player.queue.current)
|
|
@@ -378,11 +379,11 @@ async function applyUnresolvedData(resTrack, data, utils) {
|
|
|
378
379
|
resTrack.info.author = data.info.author;
|
|
379
380
|
}
|
|
380
381
|
else { // only overwrite if undefined / invalid
|
|
381
|
-
if ((resTrack.info.title
|
|
382
|
+
if ((resTrack.info.title === 'Unknown title' || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
382
383
|
resTrack.info.title = data.info.title;
|
|
383
|
-
if (resTrack.info.author
|
|
384
|
+
if (resTrack.info.author !== data.info.author)
|
|
384
385
|
resTrack.info.author = data.info.author;
|
|
385
|
-
if (resTrack.info.artworkUrl
|
|
386
|
+
if (resTrack.info.artworkUrl !== data.info.artworkUrl)
|
|
386
387
|
resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
387
388
|
}
|
|
388
389
|
for (const key of Object.keys(data.info))
|
|
@@ -75,9 +75,9 @@ export class FilterManager {
|
|
|
75
75
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
76
76
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
77
77
|
},
|
|
78
|
-
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
78
|
+
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
79
79
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
80
|
-
// "adaptive": true // false
|
|
80
|
+
// "adaptive": true // false
|
|
81
81
|
},
|
|
82
82
|
"echo": { // Self-explanatory; provides an echo effect.
|
|
83
83
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
@@ -250,9 +250,9 @@ export class FilterManager {
|
|
|
250
250
|
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
|
|
251
251
|
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
|
|
252
252
|
},
|
|
253
|
-
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
253
|
+
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
|
|
254
254
|
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
|
|
255
|
-
// "adaptive": true // false
|
|
255
|
+
// "adaptive": true // false
|
|
256
256
|
},
|
|
257
257
|
"echo": { // Self-explanatory; provides an echo effect.
|
|
258
258
|
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
|
|
@@ -95,7 +95,7 @@ export interface LavalinkManagerEvents {
|
|
|
95
95
|
* Emitted when a Track finished.
|
|
96
96
|
* @event Manager#trackEnd
|
|
97
97
|
*/
|
|
98
|
-
"trackEnd": (player: Player, track: Track, payload: TrackEndEvent) => void;
|
|
98
|
+
"trackEnd": (player: Player, track: Track | null, payload: TrackEndEvent) => void;
|
|
99
99
|
/**
|
|
100
100
|
* Emitted when a Track got stuck while playing.
|
|
101
101
|
* @event Manager#trackStuck
|
|
@@ -110,7 +110,7 @@ export interface LavalinkManagerEvents {
|
|
|
110
110
|
* Emitted when the Playing finished and no more tracks in the queue.
|
|
111
111
|
* @event Manager#queueEnd
|
|
112
112
|
*/
|
|
113
|
-
"queueEnd": (player: Player, track: Track, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
113
|
+
"queueEnd": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
114
114
|
/**
|
|
115
115
|
* Emitted when a Player is created.
|
|
116
116
|
* @event Manager#playerCreate
|
|
@@ -147,28 +147,28 @@ export interface LavalinkManagerEvents {
|
|
|
147
147
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentsloaded
|
|
148
148
|
* @event Manager#trackError
|
|
149
149
|
*/
|
|
150
|
-
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentsLoaded) => void;
|
|
150
|
+
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentsLoaded) => void;
|
|
151
151
|
/**
|
|
152
152
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
153
153
|
* Emitted when a specific Segment was skipped
|
|
154
154
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentskipped
|
|
155
155
|
* @event Manager#trackError
|
|
156
156
|
*/
|
|
157
|
-
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentSkipped) => void;
|
|
157
|
+
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentSkipped) => void;
|
|
158
158
|
/**
|
|
159
159
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
160
160
|
* Emitted when a specific Chapter starts playing
|
|
161
161
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chapterstarted
|
|
162
162
|
* @event Manager#trackError
|
|
163
163
|
*/
|
|
164
|
-
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChapterStarted) => void;
|
|
164
|
+
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChapterStarted) => void;
|
|
165
165
|
/**
|
|
166
166
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
167
167
|
* Emitted when Chapters are loaded
|
|
168
168
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chaptersloaded
|
|
169
169
|
* @event Manager#trackError
|
|
170
170
|
*/
|
|
171
|
-
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChaptersLoaded) => void;
|
|
171
|
+
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
|
|
172
172
|
}
|
|
173
173
|
export interface LavalinkManager {
|
|
174
174
|
/** @private */
|
|
@@ -254,7 +254,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
254
254
|
const oldPlayer = this.getPlayer(guildId);
|
|
255
255
|
if (!oldPlayer)
|
|
256
256
|
return;
|
|
257
|
-
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
257
|
+
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
258
258
|
if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
259
259
|
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
260
260
|
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
|
|
@@ -351,9 +351,11 @@ export class LavalinkNode {
|
|
|
351
351
|
destroy(destroyReason, deleteNode = true) {
|
|
352
352
|
if (!this.connected)
|
|
353
353
|
return;
|
|
354
|
-
const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id
|
|
354
|
+
const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id === this.id);
|
|
355
355
|
if (players)
|
|
356
|
-
players.forEach(p =>
|
|
356
|
+
players.forEach(p => {
|
|
357
|
+
p.destroy(destroyReason || DestroyReasons.NodeDestroy);
|
|
358
|
+
});
|
|
357
359
|
this.socket.close(1000, "Node-Destroy");
|
|
358
360
|
this.socket.removeAllListeners();
|
|
359
361
|
this.socket = null;
|
|
@@ -876,7 +878,7 @@ export class LavalinkNode {
|
|
|
876
878
|
// remove tracks from the queue
|
|
877
879
|
if (player.repeatMode !== "track" || player.get("internal_skipped"))
|
|
878
880
|
await queueTrackEnd(player);
|
|
879
|
-
else if (player.queue.current) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
881
|
+
else if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
880
882
|
player.queue.previous.unshift(player.queue.current);
|
|
881
883
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
882
884
|
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
@@ -1025,7 +1027,12 @@ export class LavalinkNode {
|
|
|
1025
1027
|
}
|
|
1026
1028
|
}
|
|
1027
1029
|
player.set("internal_autoplayStopPlaying", undefined);
|
|
1028
|
-
|
|
1030
|
+
if (track && !track?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
1031
|
+
player.queue.previous.unshift(track);
|
|
1032
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
1033
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
1034
|
+
await player.queue.utils.save();
|
|
1035
|
+
}
|
|
1029
1036
|
if (payload?.reason !== "stopped") {
|
|
1030
1037
|
await player.queue.utils.save();
|
|
1031
1038
|
}
|
|
@@ -8,7 +8,9 @@ export class NodeManager extends EventEmitter {
|
|
|
8
8
|
super();
|
|
9
9
|
this.LavalinkManager = LavalinkManager;
|
|
10
10
|
if (this.LavalinkManager.options.nodes)
|
|
11
|
-
this.LavalinkManager.options.nodes.forEach(node =>
|
|
11
|
+
this.LavalinkManager.options.nodes.forEach(node => {
|
|
12
|
+
this.createNode(node);
|
|
13
|
+
});
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
16
|
* Disconnects all Nodes from lavalink ws sockets
|
|
@@ -278,6 +278,7 @@ export class Player {
|
|
|
278
278
|
if (this.paused && !this.playing)
|
|
279
279
|
throw new Error("Player is already paused - not able to pause.");
|
|
280
280
|
this.paused = true;
|
|
281
|
+
this.lastPositionChange = null; // needs to removed to not cause issues
|
|
281
282
|
const now = performance.now();
|
|
282
283
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
|
|
283
284
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
@@ -113,4 +113,51 @@ export declare class Queue {
|
|
|
113
113
|
* @returns {Track} Spliced Track
|
|
114
114
|
*/
|
|
115
115
|
splice(index: number, amount: number, TrackOrTracks?: Track | UnresolvedTrack | (Track | UnresolvedTrack)[]): any;
|
|
116
|
+
/**
|
|
117
|
+
* Remove stuff from the queue.tracks array
|
|
118
|
+
* - single Track | UnresolvedTrack
|
|
119
|
+
* - multiple Track | UnresovedTrack
|
|
120
|
+
* - at the index or multiple indexes
|
|
121
|
+
* @param removeQueryTrack
|
|
122
|
+
* @returns null (if nothing was removed) / { removed } where removed is an array with all removed elements
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```js
|
|
126
|
+
* // remove single track
|
|
127
|
+
*
|
|
128
|
+
* const track = player.queue.tracks[4];
|
|
129
|
+
* await player.queue.remove(track);
|
|
130
|
+
*
|
|
131
|
+
* // if you already have the index you can straight up pass it too
|
|
132
|
+
* await player.queue.remove(4);
|
|
133
|
+
*
|
|
134
|
+
*
|
|
135
|
+
* // if you want to remove multiple tracks, e.g. from position 4 to position 10 you can do smt like this
|
|
136
|
+
* await player.queue.remove(player.queue.tracks.slice(4, 10)) // get's the tracks from 4 - 10, which then get's found in the remove function to be removed
|
|
137
|
+
*
|
|
138
|
+
* // I still highly suggest to use .splice!
|
|
139
|
+
*
|
|
140
|
+
* await player.queue.splice(4, 10); // removes at index 4, 10 tracks
|
|
141
|
+
*
|
|
142
|
+
* await player.queue.splice(1, 1); // removes at index 1, 1 track
|
|
143
|
+
*
|
|
144
|
+
* await player.queue.splice(4, 0, ...tracks) // removes 0 tracks at position 4, and then inserts all tracks after position 4.
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
remove<T extends Track | UnresolvedTrack | number | Track[] | UnresolvedTrack[] | number[] | (number | Track | UnresolvedTrack)[]>(removeQueryTrack: T): Promise<{
|
|
148
|
+
removed: (Track | UnresolvedTrack)[];
|
|
149
|
+
} | null>;
|
|
150
|
+
/**
|
|
151
|
+
* Shifts the previous array, to return the last previous track & thus remove it from the previous queue
|
|
152
|
+
* @returns
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```js
|
|
156
|
+
* // example on how to play the previous track again
|
|
157
|
+
* const previous = await player.queue.shiftPrevious(); // get the previous track and remove it from the previous queue array!!
|
|
158
|
+
* if(!previous) return console.error("No previous track found");
|
|
159
|
+
* await player.play({ clientTrack: previous }); // play it again
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
shiftPrevious(): Promise<Track>;
|
|
116
163
|
}
|
|
@@ -118,7 +118,7 @@ export class Queue {
|
|
|
118
118
|
if (this.tracks.length <= 1)
|
|
119
119
|
return this.tracks.length;
|
|
120
120
|
// swap #1 and #2 if only 2 tracks.
|
|
121
|
-
if (this.tracks.length
|
|
121
|
+
if (this.tracks.length === 2) {
|
|
122
122
|
[this.tracks[0], this.tracks[1]] = [this.tracks[1], this.tracks[0]];
|
|
123
123
|
}
|
|
124
124
|
else { // randomly swap places.
|
|
@@ -192,4 +192,107 @@ export class Queue {
|
|
|
192
192
|
// return the things
|
|
193
193
|
return spliced.length === 1 ? spliced[0] : spliced;
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Remove stuff from the queue.tracks array
|
|
197
|
+
* - single Track | UnresolvedTrack
|
|
198
|
+
* - multiple Track | UnresovedTrack
|
|
199
|
+
* - at the index or multiple indexes
|
|
200
|
+
* @param removeQueryTrack
|
|
201
|
+
* @returns null (if nothing was removed) / { removed } where removed is an array with all removed elements
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```js
|
|
205
|
+
* // remove single track
|
|
206
|
+
*
|
|
207
|
+
* const track = player.queue.tracks[4];
|
|
208
|
+
* await player.queue.remove(track);
|
|
209
|
+
*
|
|
210
|
+
* // if you already have the index you can straight up pass it too
|
|
211
|
+
* await player.queue.remove(4);
|
|
212
|
+
*
|
|
213
|
+
*
|
|
214
|
+
* // if you want to remove multiple tracks, e.g. from position 4 to position 10 you can do smt like this
|
|
215
|
+
* await player.queue.remove(player.queue.tracks.slice(4, 10)) // get's the tracks from 4 - 10, which then get's found in the remove function to be removed
|
|
216
|
+
*
|
|
217
|
+
* // I still highly suggest to use .splice!
|
|
218
|
+
*
|
|
219
|
+
* await player.queue.splice(4, 10); // removes at index 4, 10 tracks
|
|
220
|
+
*
|
|
221
|
+
* await player.queue.splice(1, 1); // removes at index 1, 1 track
|
|
222
|
+
*
|
|
223
|
+
* await player.queue.splice(4, 0, ...tracks) // removes 0 tracks at position 4, and then inserts all tracks after position 4.
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
async remove(removeQueryTrack) {
|
|
227
|
+
if (typeof removeQueryTrack === "number") {
|
|
228
|
+
const toRemove = this.tracks[removeQueryTrack];
|
|
229
|
+
if (!toRemove)
|
|
230
|
+
return null;
|
|
231
|
+
const removed = this.tracks.splice(removeQueryTrack, 1);
|
|
232
|
+
await this.utils.save();
|
|
233
|
+
console.log("0st", removed, toRemove);
|
|
234
|
+
return { removed };
|
|
235
|
+
}
|
|
236
|
+
if (Array.isArray(removeQueryTrack)) {
|
|
237
|
+
if (removeQueryTrack.every(v => typeof v === "number")) {
|
|
238
|
+
const removed = [];
|
|
239
|
+
for (const i of removeQueryTrack) {
|
|
240
|
+
if (this.tracks[i])
|
|
241
|
+
removed.push(...this.tracks.splice(i, 1));
|
|
242
|
+
}
|
|
243
|
+
console.log("1st", removed, removeQueryTrack);
|
|
244
|
+
if (!removed.length)
|
|
245
|
+
return null;
|
|
246
|
+
await this.utils.save();
|
|
247
|
+
return { removed };
|
|
248
|
+
}
|
|
249
|
+
const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(({ v, i }) => removeQueryTrack.find(t => typeof t === "number" && (t === i) ||
|
|
250
|
+
typeof t === "object" && (t.encoded && t.encoded === v.encoded ||
|
|
251
|
+
t.info?.identifier && t.info.identifier === v.info?.identifier ||
|
|
252
|
+
t.info?.uri && t.info.uri === v.info?.uri ||
|
|
253
|
+
t.info?.title && t.info.title === v.info?.title ||
|
|
254
|
+
t.info?.isrc && t.info.isrc === v.info?.isrc ||
|
|
255
|
+
t.info?.artworkUrl && t.info.artworkUrl === v.info?.artworkUrl)));
|
|
256
|
+
if (!tracksToRemove.length)
|
|
257
|
+
return null;
|
|
258
|
+
const removed = [];
|
|
259
|
+
for (const { i } of tracksToRemove) {
|
|
260
|
+
if (this.tracks[i])
|
|
261
|
+
removed.push(...this.tracks.splice(i, 1));
|
|
262
|
+
}
|
|
263
|
+
await this.utils.save();
|
|
264
|
+
console.log("2nd", removed, tracksToRemove);
|
|
265
|
+
return { removed };
|
|
266
|
+
}
|
|
267
|
+
const toRemove = this.tracks.findIndex((v) => removeQueryTrack.encoded && removeQueryTrack.encoded === v.encoded ||
|
|
268
|
+
removeQueryTrack.info?.identifier && removeQueryTrack.info.identifier === v.info?.identifier ||
|
|
269
|
+
removeQueryTrack.info?.uri && removeQueryTrack.info.uri === v.info?.uri ||
|
|
270
|
+
removeQueryTrack.info?.title && removeQueryTrack.info.title === v.info?.title ||
|
|
271
|
+
removeQueryTrack.info?.isrc && removeQueryTrack.info.isrc === v.info?.isrc ||
|
|
272
|
+
removeQueryTrack.info?.artworkUrl && removeQueryTrack.info.artworkUrl === v.info?.artworkUrl);
|
|
273
|
+
if (toRemove < 0)
|
|
274
|
+
return null;
|
|
275
|
+
const removed = this.tracks.splice(toRemove, 1);
|
|
276
|
+
await this.utils.save();
|
|
277
|
+
console.log("3rd", removed, toRemove);
|
|
278
|
+
return { removed };
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Shifts the previous array, to return the last previous track & thus remove it from the previous queue
|
|
282
|
+
* @returns
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```js
|
|
286
|
+
* // example on how to play the previous track again
|
|
287
|
+
* const previous = await player.queue.shiftPrevious(); // get the previous track and remove it from the previous queue array!!
|
|
288
|
+
* if(!previous) return console.error("No previous track found");
|
|
289
|
+
* await player.play({ clientTrack: previous }); // play it again
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
async shiftPrevious() {
|
|
293
|
+
const removed = this.previous.shift();
|
|
294
|
+
if (removed)
|
|
295
|
+
await this.utils.save();
|
|
296
|
+
return removed ?? null;
|
|
297
|
+
}
|
|
195
298
|
}
|
|
@@ -339,10 +339,11 @@ export class MiniMap extends Map {
|
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
export async function queueTrackEnd(player) {
|
|
342
|
-
if (player.queue.current) { //
|
|
342
|
+
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
343
343
|
player.queue.previous.unshift(player.queue.current);
|
|
344
344
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
345
345
|
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
346
|
+
await player.queue.utils.save();
|
|
346
347
|
}
|
|
347
348
|
// and if repeatMode == queue, add it back to the queue!
|
|
348
349
|
if (player.repeatMode === "queue" && player.queue.current)
|
|
@@ -371,11 +372,11 @@ async function applyUnresolvedData(resTrack, data, utils) {
|
|
|
371
372
|
resTrack.info.author = data.info.author;
|
|
372
373
|
}
|
|
373
374
|
else { // only overwrite if undefined / invalid
|
|
374
|
-
if ((resTrack.info.title
|
|
375
|
+
if ((resTrack.info.title === 'Unknown title' || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
|
|
375
376
|
resTrack.info.title = data.info.title;
|
|
376
|
-
if (resTrack.info.author
|
|
377
|
+
if (resTrack.info.author !== data.info.author)
|
|
377
378
|
resTrack.info.author = data.info.author;
|
|
378
|
-
if (resTrack.info.artworkUrl
|
|
379
|
+
if (resTrack.info.artworkUrl !== data.info.artworkUrl)
|
|
379
380
|
resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
380
381
|
}
|
|
381
382
|
for (const key of Object.keys(data.info))
|
|
@@ -95,7 +95,7 @@ export interface LavalinkManagerEvents {
|
|
|
95
95
|
* Emitted when a Track finished.
|
|
96
96
|
* @event Manager#trackEnd
|
|
97
97
|
*/
|
|
98
|
-
"trackEnd": (player: Player, track: Track, payload: TrackEndEvent) => void;
|
|
98
|
+
"trackEnd": (player: Player, track: Track | null, payload: TrackEndEvent) => void;
|
|
99
99
|
/**
|
|
100
100
|
* Emitted when a Track got stuck while playing.
|
|
101
101
|
* @event Manager#trackStuck
|
|
@@ -110,7 +110,7 @@ export interface LavalinkManagerEvents {
|
|
|
110
110
|
* Emitted when the Playing finished and no more tracks in the queue.
|
|
111
111
|
* @event Manager#queueEnd
|
|
112
112
|
*/
|
|
113
|
-
"queueEnd": (player: Player, track: Track, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
113
|
+
"queueEnd": (player: Player, track: Track | UnresolvedTrack | null, payload: TrackEndEvent | TrackStuckEvent | TrackExceptionEvent) => void;
|
|
114
114
|
/**
|
|
115
115
|
* Emitted when a Player is created.
|
|
116
116
|
* @event Manager#playerCreate
|
|
@@ -147,28 +147,28 @@ export interface LavalinkManagerEvents {
|
|
|
147
147
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentsloaded
|
|
148
148
|
* @event Manager#trackError
|
|
149
149
|
*/
|
|
150
|
-
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentsLoaded) => void;
|
|
150
|
+
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentsLoaded) => void;
|
|
151
151
|
/**
|
|
152
152
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
153
153
|
* Emitted when a specific Segment was skipped
|
|
154
154
|
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentskipped
|
|
155
155
|
* @event Manager#trackError
|
|
156
156
|
*/
|
|
157
|
-
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentSkipped) => void;
|
|
157
|
+
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockSegmentSkipped) => void;
|
|
158
158
|
/**
|
|
159
159
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
160
160
|
* Emitted when a specific Chapter starts playing
|
|
161
161
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chapterstarted
|
|
162
162
|
* @event Manager#trackError
|
|
163
163
|
*/
|
|
164
|
-
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChapterStarted) => void;
|
|
164
|
+
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChapterStarted) => void;
|
|
165
165
|
/**
|
|
166
166
|
* SPONSORBLOCK-PLUGIN EVENT
|
|
167
167
|
* Emitted when Chapters are loaded
|
|
168
168
|
* @link https://github.com/topi314/Sponsorblock-Plugin#chaptersloaded
|
|
169
169
|
* @event Manager#trackError
|
|
170
170
|
*/
|
|
171
|
-
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChaptersLoaded) => void;
|
|
171
|
+
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
|
|
172
172
|
}
|
|
173
173
|
export interface LavalinkManager {
|
|
174
174
|
/** @private */
|
|
@@ -113,4 +113,51 @@ export declare class Queue {
|
|
|
113
113
|
* @returns {Track} Spliced Track
|
|
114
114
|
*/
|
|
115
115
|
splice(index: number, amount: number, TrackOrTracks?: Track | UnresolvedTrack | (Track | UnresolvedTrack)[]): any;
|
|
116
|
+
/**
|
|
117
|
+
* Remove stuff from the queue.tracks array
|
|
118
|
+
* - single Track | UnresolvedTrack
|
|
119
|
+
* - multiple Track | UnresovedTrack
|
|
120
|
+
* - at the index or multiple indexes
|
|
121
|
+
* @param removeQueryTrack
|
|
122
|
+
* @returns null (if nothing was removed) / { removed } where removed is an array with all removed elements
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```js
|
|
126
|
+
* // remove single track
|
|
127
|
+
*
|
|
128
|
+
* const track = player.queue.tracks[4];
|
|
129
|
+
* await player.queue.remove(track);
|
|
130
|
+
*
|
|
131
|
+
* // if you already have the index you can straight up pass it too
|
|
132
|
+
* await player.queue.remove(4);
|
|
133
|
+
*
|
|
134
|
+
*
|
|
135
|
+
* // if you want to remove multiple tracks, e.g. from position 4 to position 10 you can do smt like this
|
|
136
|
+
* await player.queue.remove(player.queue.tracks.slice(4, 10)) // get's the tracks from 4 - 10, which then get's found in the remove function to be removed
|
|
137
|
+
*
|
|
138
|
+
* // I still highly suggest to use .splice!
|
|
139
|
+
*
|
|
140
|
+
* await player.queue.splice(4, 10); // removes at index 4, 10 tracks
|
|
141
|
+
*
|
|
142
|
+
* await player.queue.splice(1, 1); // removes at index 1, 1 track
|
|
143
|
+
*
|
|
144
|
+
* await player.queue.splice(4, 0, ...tracks) // removes 0 tracks at position 4, and then inserts all tracks after position 4.
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
remove<T extends Track | UnresolvedTrack | number | Track[] | UnresolvedTrack[] | number[] | (number | Track | UnresolvedTrack)[]>(removeQueryTrack: T): Promise<{
|
|
148
|
+
removed: (Track | UnresolvedTrack)[];
|
|
149
|
+
} | null>;
|
|
150
|
+
/**
|
|
151
|
+
* Shifts the previous array, to return the last previous track & thus remove it from the previous queue
|
|
152
|
+
* @returns
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```js
|
|
156
|
+
* // example on how to play the previous track again
|
|
157
|
+
* const previous = await player.queue.shiftPrevious(); // get the previous track and remove it from the previous queue array!!
|
|
158
|
+
* if(!previous) return console.error("No previous track found");
|
|
159
|
+
* await player.play({ clientTrack: previous }); // play it again
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
shiftPrevious(): Promise<Track>;
|
|
116
163
|
}
|
package/package.json
CHANGED