lavalink-client 1.0.1 → 1.0.3
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 +30 -8
- package/dist/cjs/structures/LavalinkManager.d.ts +12 -1
- package/dist/cjs/structures/LavalinkManagerStatics.js +1 -0
- package/dist/cjs/structures/Node.js +11 -5
- package/dist/cjs/structures/Player.js +3 -2
- package/dist/cjs/structures/Queue.js +0 -1
- package/dist/cjs/structures/Track.d.ts +9 -1
- package/dist/cjs/structures/Utils.d.ts +1 -1
- package/dist/cjs/structures/Utils.js +6 -2
- package/dist/esm/structures/LavalinkManager.d.ts +12 -1
- package/dist/esm/structures/LavalinkManagerStatics.js +1 -0
- package/dist/esm/structures/Node.js +11 -5
- package/dist/esm/structures/Player.js +3 -2
- package/dist/esm/structures/Queue.js +0 -1
- package/dist/esm/structures/Track.d.ts +9 -1
- package/dist/esm/structures/Utils.d.ts +1 -1
- package/dist/esm/structures/Utils.js +6 -2
- package/dist/structures/LavalinkManager.d.ts +1 -1
- package/dist/types/structures/LavalinkManager.d.ts +12 -1
- package/dist/types/structures/Track.d.ts +9 -1
- package/dist/types/structures/Utils.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,21 +14,34 @@ npm install tomato6966/lavalink-client
|
|
|
14
14
|
|
|
15
15
|
# Features
|
|
16
16
|
|
|
17
|
-
-
|
|
17
|
+
- 💯 Lavalink v4 Supported only (with Lavalink Plugins)
|
|
18
|
+
|
|
19
|
+
- ✅ Player-Destroy Reasons like:
|
|
18
20
|
- Channel got deleted, Player got disconnected...
|
|
19
|
-
|
|
21
|
+
|
|
22
|
+
- ✨ Choose able queue stores (maps, collections, redis, databases, ...)
|
|
20
23
|
- You can create your own queueStore, thus make it easy to sync queues accross multiple connections (e.g. dashboard-bot)
|
|
21
|
-
|
|
22
|
-
-
|
|
24
|
+
|
|
25
|
+
- 😍 Included Filter & Equalizer Management
|
|
26
|
+
|
|
27
|
+
- 👍 Multiple Player Options *for easier use*
|
|
23
28
|
- onDisconnect -> Player Destroy / auto Reconnect
|
|
24
29
|
- onEmptyQueue -> Player Destroy / leave After x Time
|
|
25
30
|
- instaFixFilter -> seek the player after applying a filter, to instantly apply it's effect (only works for little-durational-songs)
|
|
26
31
|
- applyVolumeAsFilter -> instead of using lavalink.volume, it uses lavalink.filters.volume which is much different!
|
|
27
|
-
|
|
32
|
+
|
|
33
|
+
- 🛡️ Lavalink Validations
|
|
28
34
|
- It only let's you use the filters / plugins / sources, if Lavalink actually has it enabled
|
|
29
|
-
|
|
35
|
+
|
|
36
|
+
- 🧑💻 Memory friendly and easy style
|
|
30
37
|
- Only the required data is displayed, and the store-way & types match Lavalink#IMPLEMENTATION.md
|
|
31
|
-
|
|
38
|
+
|
|
39
|
+
- 😘 Automated Handlings
|
|
40
|
+
- Skips the songs, on TrackEnd, TrackStuck, TrackError,
|
|
41
|
+
- Destroys the player on channeldelete
|
|
42
|
+
- Pauses / resumes the player if it get's muted / unmuted (server-wide) [soon]
|
|
43
|
+
- ...
|
|
44
|
+
- 😁 Much much more!
|
|
32
45
|
|
|
33
46
|
# Documentation
|
|
34
47
|
|
|
@@ -113,4 +126,13 @@ await player.play(); // you can provide specific track, or let the manager choos
|
|
|
113
126
|
|
|
114
127
|
## Example (typescript)
|
|
115
128
|
|
|
116
|
-
Can be found in the [
|
|
129
|
+
Can be found in the [`/testBot`](https://github.com/Tomato6966/lavalink-client/blob/main/testBot/README.md) Directory
|
|
130
|
+
|
|
131
|
+
It contains the following features:
|
|
132
|
+
|
|
133
|
+
- Example Use for `Redis based Queue`
|
|
134
|
+
- Example Use for `Queue Changes Watcher Logger`
|
|
135
|
+
- Example Use for `Filters`, `Audio Output`, `equalizers`
|
|
136
|
+
- How to make an easy, yet advanced `Slash Commands Only Bot`
|
|
137
|
+
- `Slim Memory Usage`
|
|
138
|
+
- `All lavalink-client events` displayments
|
|
@@ -18,15 +18,26 @@ export interface BotClientOptions {
|
|
|
18
18
|
[x: string | number | symbol | undefined]: any;
|
|
19
19
|
}
|
|
20
20
|
export interface LavalinkPlayerOptions {
|
|
21
|
+
/** If the Lavalink Volume should be decremented by x number */
|
|
21
22
|
volumeDecrementer?: number;
|
|
22
|
-
|
|
23
|
+
/** How often it should update the the player Position */
|
|
24
|
+
clientBasedPositionUpdateInterval?: number;
|
|
25
|
+
/** What should be used as a searchPlatform, if no source was provided during the query */
|
|
23
26
|
defaultSearchPlatform?: SearchPlatform;
|
|
27
|
+
/** Applies the volume via a filter, not via the lavalink volume transformer */
|
|
24
28
|
applyVolumeAsFilter?: boolean;
|
|
29
|
+
/** Transforms the saved data of a requested user */
|
|
30
|
+
requesterTransformer?: (requester: unknown) => unknown;
|
|
31
|
+
/** What lavalink-client should do when the player reconnects */
|
|
25
32
|
onDisconnect?: {
|
|
33
|
+
/** Try to reconnect? -> If fails -> Destroy */
|
|
26
34
|
autoReconnect?: boolean;
|
|
35
|
+
/** Instantly destroy player (overrides autoReconnect) */
|
|
27
36
|
destroyPlayer?: boolean;
|
|
28
37
|
};
|
|
29
38
|
onEmptyQueue?: {
|
|
39
|
+
/** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
|
|
40
|
+
autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
|
|
30
41
|
destroyAfterMs?: number;
|
|
31
42
|
};
|
|
32
43
|
}
|
|
@@ -449,15 +449,15 @@ class LavalinkNode {
|
|
|
449
449
|
player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
|
|
450
450
|
if (!player.createdTimeStamp && payload.state.time)
|
|
451
451
|
player.createdTimeStamp = payload.state.time;
|
|
452
|
-
if (typeof this.NodeManager.LavalinkManager.options.playerOptions.
|
|
452
|
+
if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
|
|
453
453
|
player.set("internal_updateInterval", setInterval(() => {
|
|
454
|
-
player.position += this.NodeManager.LavalinkManager.options.playerOptions.
|
|
454
|
+
player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
|
|
455
455
|
if (player.filterManager.filterUpdatedState >= 1) {
|
|
456
456
|
player.filterManager.filterUpdatedState++;
|
|
457
457
|
const maxMins = 8;
|
|
458
458
|
const currentDuration = player.queue.current?.info?.duration || 0;
|
|
459
459
|
if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri)) {
|
|
460
|
-
if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.
|
|
460
|
+
if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
|
|
461
461
|
player.filterManager.filterUpdatedState = 0;
|
|
462
462
|
player.seek(player.position);
|
|
463
463
|
}
|
|
@@ -466,7 +466,7 @@ class LavalinkNode {
|
|
|
466
466
|
player.filterManager.filterUpdatedState = 0;
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
|
-
}, this.NodeManager.LavalinkManager.options.playerOptions.
|
|
469
|
+
}, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
|
|
470
470
|
}
|
|
471
471
|
else {
|
|
472
472
|
if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
|
|
@@ -495,7 +495,6 @@ class LavalinkNode {
|
|
|
495
495
|
const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
|
|
496
496
|
if (!player)
|
|
497
497
|
return;
|
|
498
|
-
console.log(payload.type);
|
|
499
498
|
switch (payload.type) {
|
|
500
499
|
case "TrackStartEvent":
|
|
501
500
|
this.trackStart(player, player.queue.current, payload);
|
|
@@ -555,6 +554,13 @@ class LavalinkNode {
|
|
|
555
554
|
async queueEnd(player, track, payload) {
|
|
556
555
|
player.queue.current = null;
|
|
557
556
|
player.playing = false;
|
|
557
|
+
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function") {
|
|
558
|
+
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
559
|
+
if (player.queue.tracks.length > 0)
|
|
560
|
+
await (0, Utils_1.queueTrackEnd)(player.queue, player.repeatMode === "queue");
|
|
561
|
+
if (player.queue.current)
|
|
562
|
+
return player.play({ track: player.queue.current, noReplace: true, paused: false });
|
|
563
|
+
}
|
|
558
564
|
if (payload?.reason !== "stopped") {
|
|
559
565
|
await player.queue.utils.save();
|
|
560
566
|
}
|
|
@@ -203,8 +203,9 @@ class Player {
|
|
|
203
203
|
Query.source = LavalinkManagerStatics_1.DefaultSources[foundSource]; // set the source to ytsearch:
|
|
204
204
|
Query.query = Query.query.replace(`${foundSource}:`, ""); // remove ytsearch: from the query
|
|
205
205
|
}
|
|
206
|
-
//
|
|
207
|
-
|
|
206
|
+
// ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
|
|
207
|
+
// request the data
|
|
208
|
+
const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
|
|
208
209
|
// transform the data which can be Error, Track or Track[] to enfore [Track]
|
|
209
210
|
const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
|
|
210
211
|
return {
|
|
@@ -74,7 +74,6 @@ class Queue {
|
|
|
74
74
|
this.current = this.managerUtils.isTrack(data.current) ? data.current : null;
|
|
75
75
|
this.previous = Array.isArray(data.previous) && data.previous.some(track => this.managerUtils.isTrack(track)) ? data.previous.filter(track => this.managerUtils.isTrack(track)) : [];
|
|
76
76
|
this.tracks = Array.isArray(data.tracks) && data.tracks.some(track => this.managerUtils.isTrack(track)) ? data.tracks.filter(track => this.managerUtils.isTrack(track)) : [];
|
|
77
|
-
this.utils.sync(false, true);
|
|
78
77
|
}
|
|
79
78
|
/**
|
|
80
79
|
* Utils for a Queue
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Base64 } from "./Utils";
|
|
2
|
+
type LavalinkSourceNames = "youtube" | "youtubemusic" | "soundcloud" | "bandcamp" | "twitch";
|
|
3
|
+
type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts";
|
|
4
|
+
type SourceNames = LavalinkSourceNames | LavalinkPlugin_LavaSrc_SourceNames;
|
|
2
5
|
export interface TrackInfo {
|
|
3
6
|
identifier: string;
|
|
4
7
|
title: string;
|
|
@@ -6,7 +9,7 @@ export interface TrackInfo {
|
|
|
6
9
|
duration: number;
|
|
7
10
|
artworkUrl: string | null;
|
|
8
11
|
uri: string;
|
|
9
|
-
sourceName:
|
|
12
|
+
sourceName: SourceNames;
|
|
10
13
|
isSeekable: boolean;
|
|
11
14
|
isStream: boolean;
|
|
12
15
|
isrc: string | null;
|
|
@@ -24,6 +27,10 @@ export interface PluginInfo {
|
|
|
24
27
|
url?: string;
|
|
25
28
|
/** The Url provided by a Plugin */
|
|
26
29
|
uri?: string;
|
|
30
|
+
/** You can put specific track information here, to transform the tracks... */
|
|
31
|
+
clientData?: {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
export interface LavalinkTrack {
|
|
29
36
|
/** The Base 64 encoded String */
|
|
@@ -45,3 +52,4 @@ export interface UnresolvedQuery {
|
|
|
45
52
|
/** The Track's Requester */
|
|
46
53
|
requester?: unknown;
|
|
47
54
|
}
|
|
55
|
+
export {};
|
|
@@ -8,7 +8,7 @@ export declare const TrackSymbol: unique symbol;
|
|
|
8
8
|
export declare const UnresolvedTrackSymbol: unique symbol;
|
|
9
9
|
export declare const QueueSymbol: unique symbol;
|
|
10
10
|
export declare const NodeSymbol: unique symbol;
|
|
11
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts";
|
|
11
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts" | "ftts";
|
|
12
12
|
export type ClientSearchPlatform = "youtube" | "yt" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "yandex music" | "sp" | "sprec" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandexmusic";
|
|
13
13
|
export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
|
|
14
14
|
export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
|
|
@@ -24,7 +24,7 @@ class ManagerUitls {
|
|
|
24
24
|
title: data.info?.title,
|
|
25
25
|
author: data.info?.author,
|
|
26
26
|
duration: data.info?.length,
|
|
27
|
-
artworkUrl: data.info?.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl
|
|
27
|
+
artworkUrl: data.info?.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
|
|
28
28
|
uri: data.info?.uri,
|
|
29
29
|
sourceName: data.info?.sourceName,
|
|
30
30
|
isSeekable: data.info?.isSeekable,
|
|
@@ -32,7 +32,7 @@ class ManagerUitls {
|
|
|
32
32
|
isrc: data.info?.isrc,
|
|
33
33
|
},
|
|
34
34
|
pluginInfo: data.pluginInfo || data.plugin || {},
|
|
35
|
-
requester: data?.requester || requester,
|
|
35
|
+
requester: typeof this.manager.options?.playerOptions?.requesterTransformer === "function" ? this.manager.options?.playerOptions?.requesterTransformer(data?.requester || requester) : requester,
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(r, exports.TrackSymbol, { configurable: true, value: true });
|
|
38
38
|
return r;
|
|
@@ -121,6 +121,10 @@ class ManagerUitls {
|
|
|
121
121
|
if (source === "tts" && !node.info.sourceManagers.includes("tts")) {
|
|
122
122
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
123
123
|
}
|
|
124
|
+
if (source === "ftts" && !node.info.sourceManagers.includes("ftts")) {
|
|
125
|
+
console.log(node.info.sourceManagers);
|
|
126
|
+
throw new Error("Lavalink Node has not 'ftts' enabled, which is required to have 'ftts' work");
|
|
127
|
+
}
|
|
124
128
|
if (source === "ymsearch" && !node.info.sourceManagers.includes("yandexmusic")) {
|
|
125
129
|
throw new Error("Lavalink Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
|
|
126
130
|
}
|
|
@@ -18,15 +18,26 @@ export interface BotClientOptions {
|
|
|
18
18
|
[x: string | number | symbol | undefined]: any;
|
|
19
19
|
}
|
|
20
20
|
export interface LavalinkPlayerOptions {
|
|
21
|
+
/** If the Lavalink Volume should be decremented by x number */
|
|
21
22
|
volumeDecrementer?: number;
|
|
22
|
-
|
|
23
|
+
/** How often it should update the the player Position */
|
|
24
|
+
clientBasedPositionUpdateInterval?: number;
|
|
25
|
+
/** What should be used as a searchPlatform, if no source was provided during the query */
|
|
23
26
|
defaultSearchPlatform?: SearchPlatform;
|
|
27
|
+
/** Applies the volume via a filter, not via the lavalink volume transformer */
|
|
24
28
|
applyVolumeAsFilter?: boolean;
|
|
29
|
+
/** Transforms the saved data of a requested user */
|
|
30
|
+
requesterTransformer?: (requester: unknown) => unknown;
|
|
31
|
+
/** What lavalink-client should do when the player reconnects */
|
|
25
32
|
onDisconnect?: {
|
|
33
|
+
/** Try to reconnect? -> If fails -> Destroy */
|
|
26
34
|
autoReconnect?: boolean;
|
|
35
|
+
/** Instantly destroy player (overrides autoReconnect) */
|
|
27
36
|
destroyPlayer?: boolean;
|
|
28
37
|
};
|
|
29
38
|
onEmptyQueue?: {
|
|
39
|
+
/** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
|
|
40
|
+
autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
|
|
30
41
|
destroyAfterMs?: number;
|
|
31
42
|
};
|
|
32
43
|
}
|
|
@@ -445,15 +445,15 @@ export class LavalinkNode {
|
|
|
445
445
|
player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
|
|
446
446
|
if (!player.createdTimeStamp && payload.state.time)
|
|
447
447
|
player.createdTimeStamp = payload.state.time;
|
|
448
|
-
if (typeof this.NodeManager.LavalinkManager.options.playerOptions.
|
|
448
|
+
if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval >= 10) {
|
|
449
449
|
player.set("internal_updateInterval", setInterval(() => {
|
|
450
|
-
player.position += this.NodeManager.LavalinkManager.options.playerOptions.
|
|
450
|
+
player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250;
|
|
451
451
|
if (player.filterManager.filterUpdatedState >= 1) {
|
|
452
452
|
player.filterManager.filterUpdatedState++;
|
|
453
453
|
const maxMins = 8;
|
|
454
454
|
const currentDuration = player.queue.current?.info?.duration || 0;
|
|
455
455
|
if (currentDuration <= maxMins * 6e4 || isAbsolute(player.queue.current?.info?.uri)) {
|
|
456
|
-
if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.
|
|
456
|
+
if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250) > 400 ? 2 : 3)) {
|
|
457
457
|
player.filterManager.filterUpdatedState = 0;
|
|
458
458
|
player.seek(player.position);
|
|
459
459
|
}
|
|
@@ -462,7 +462,7 @@ export class LavalinkNode {
|
|
|
462
462
|
player.filterManager.filterUpdatedState = 0;
|
|
463
463
|
}
|
|
464
464
|
}
|
|
465
|
-
}, this.NodeManager.LavalinkManager.options.playerOptions.
|
|
465
|
+
}, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedPositionUpdateInterval || 250));
|
|
466
466
|
}
|
|
467
467
|
else {
|
|
468
468
|
if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
|
|
@@ -491,7 +491,6 @@ export class LavalinkNode {
|
|
|
491
491
|
const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
|
|
492
492
|
if (!player)
|
|
493
493
|
return;
|
|
494
|
-
console.log(payload.type);
|
|
495
494
|
switch (payload.type) {
|
|
496
495
|
case "TrackStartEvent":
|
|
497
496
|
this.trackStart(player, player.queue.current, payload);
|
|
@@ -551,6 +550,13 @@ export class LavalinkNode {
|
|
|
551
550
|
async queueEnd(player, track, payload) {
|
|
552
551
|
player.queue.current = null;
|
|
553
552
|
player.playing = false;
|
|
553
|
+
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function") {
|
|
554
|
+
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
555
|
+
if (player.queue.tracks.length > 0)
|
|
556
|
+
await queueTrackEnd(player.queue, player.repeatMode === "queue");
|
|
557
|
+
if (player.queue.current)
|
|
558
|
+
return player.play({ track: player.queue.current, noReplace: true, paused: false });
|
|
559
|
+
}
|
|
554
560
|
if (payload?.reason !== "stopped") {
|
|
555
561
|
await player.queue.utils.save();
|
|
556
562
|
}
|
|
@@ -200,8 +200,9 @@ export class Player {
|
|
|
200
200
|
Query.source = DefaultSources[foundSource]; // set the source to ytsearch:
|
|
201
201
|
Query.query = Query.query.replace(`${foundSource}:`, ""); // remove ytsearch: from the query
|
|
202
202
|
}
|
|
203
|
-
//
|
|
204
|
-
|
|
203
|
+
// ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
|
|
204
|
+
// request the data
|
|
205
|
+
const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
|
|
205
206
|
// transform the data which can be Error, Track or Track[] to enfore [Track]
|
|
206
207
|
const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
|
|
207
208
|
return {
|
|
@@ -68,7 +68,6 @@ export class Queue {
|
|
|
68
68
|
this.current = this.managerUtils.isTrack(data.current) ? data.current : null;
|
|
69
69
|
this.previous = Array.isArray(data.previous) && data.previous.some(track => this.managerUtils.isTrack(track)) ? data.previous.filter(track => this.managerUtils.isTrack(track)) : [];
|
|
70
70
|
this.tracks = Array.isArray(data.tracks) && data.tracks.some(track => this.managerUtils.isTrack(track)) ? data.tracks.filter(track => this.managerUtils.isTrack(track)) : [];
|
|
71
|
-
this.utils.sync(false, true);
|
|
72
71
|
}
|
|
73
72
|
/**
|
|
74
73
|
* Utils for a Queue
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Base64 } from "./Utils";
|
|
2
|
+
type LavalinkSourceNames = "youtube" | "youtubemusic" | "soundcloud" | "bandcamp" | "twitch";
|
|
3
|
+
type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts";
|
|
4
|
+
type SourceNames = LavalinkSourceNames | LavalinkPlugin_LavaSrc_SourceNames;
|
|
2
5
|
export interface TrackInfo {
|
|
3
6
|
identifier: string;
|
|
4
7
|
title: string;
|
|
@@ -6,7 +9,7 @@ export interface TrackInfo {
|
|
|
6
9
|
duration: number;
|
|
7
10
|
artworkUrl: string | null;
|
|
8
11
|
uri: string;
|
|
9
|
-
sourceName:
|
|
12
|
+
sourceName: SourceNames;
|
|
10
13
|
isSeekable: boolean;
|
|
11
14
|
isStream: boolean;
|
|
12
15
|
isrc: string | null;
|
|
@@ -24,6 +27,10 @@ export interface PluginInfo {
|
|
|
24
27
|
url?: string;
|
|
25
28
|
/** The Url provided by a Plugin */
|
|
26
29
|
uri?: string;
|
|
30
|
+
/** You can put specific track information here, to transform the tracks... */
|
|
31
|
+
clientData?: {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
export interface LavalinkTrack {
|
|
29
36
|
/** The Base 64 encoded String */
|
|
@@ -45,3 +52,4 @@ export interface UnresolvedQuery {
|
|
|
45
52
|
/** The Track's Requester */
|
|
46
53
|
requester?: unknown;
|
|
47
54
|
}
|
|
55
|
+
export {};
|
|
@@ -8,7 +8,7 @@ export declare const TrackSymbol: unique symbol;
|
|
|
8
8
|
export declare const UnresolvedTrackSymbol: unique symbol;
|
|
9
9
|
export declare const QueueSymbol: unique symbol;
|
|
10
10
|
export declare const NodeSymbol: unique symbol;
|
|
11
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts";
|
|
11
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts" | "ftts";
|
|
12
12
|
export type ClientSearchPlatform = "youtube" | "yt" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "yandex music" | "sp" | "sprec" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandexmusic";
|
|
13
13
|
export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
|
|
14
14
|
export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
|
|
@@ -21,7 +21,7 @@ export class ManagerUitls {
|
|
|
21
21
|
title: data.info?.title,
|
|
22
22
|
author: data.info?.author,
|
|
23
23
|
duration: data.info?.length,
|
|
24
|
-
artworkUrl: data.info?.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl
|
|
24
|
+
artworkUrl: data.info?.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
|
|
25
25
|
uri: data.info?.uri,
|
|
26
26
|
sourceName: data.info?.sourceName,
|
|
27
27
|
isSeekable: data.info?.isSeekable,
|
|
@@ -29,7 +29,7 @@ export class ManagerUitls {
|
|
|
29
29
|
isrc: data.info?.isrc,
|
|
30
30
|
},
|
|
31
31
|
pluginInfo: data.pluginInfo || data.plugin || {},
|
|
32
|
-
requester: data?.requester || requester,
|
|
32
|
+
requester: typeof this.manager.options?.playerOptions?.requesterTransformer === "function" ? this.manager.options?.playerOptions?.requesterTransformer(data?.requester || requester) : requester,
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(r, TrackSymbol, { configurable: true, value: true });
|
|
35
35
|
return r;
|
|
@@ -118,6 +118,10 @@ export class ManagerUitls {
|
|
|
118
118
|
if (source === "tts" && !node.info.sourceManagers.includes("tts")) {
|
|
119
119
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
120
120
|
}
|
|
121
|
+
if (source === "ftts" && !node.info.sourceManagers.includes("ftts")) {
|
|
122
|
+
console.log(node.info.sourceManagers);
|
|
123
|
+
throw new Error("Lavalink Node has not 'ftts' enabled, which is required to have 'ftts' work");
|
|
124
|
+
}
|
|
121
125
|
if (source === "ymsearch" && !node.info.sourceManagers.includes("yandexmusic")) {
|
|
122
126
|
throw new Error("Lavalink Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
|
|
123
127
|
}
|
|
@@ -17,7 +17,7 @@ export interface BotClientOptions {
|
|
|
17
17
|
}
|
|
18
18
|
export interface LavalinkPlayerOptions {
|
|
19
19
|
volumeDecrementer?: number;
|
|
20
|
-
|
|
20
|
+
clientBasedPositionUpdateInterval?: number;
|
|
21
21
|
defaultSearchPlatform?: SearchPlatform;
|
|
22
22
|
applyVolumeAsFilter?: boolean;
|
|
23
23
|
}
|
|
@@ -18,15 +18,26 @@ export interface BotClientOptions {
|
|
|
18
18
|
[x: string | number | symbol | undefined]: any;
|
|
19
19
|
}
|
|
20
20
|
export interface LavalinkPlayerOptions {
|
|
21
|
+
/** If the Lavalink Volume should be decremented by x number */
|
|
21
22
|
volumeDecrementer?: number;
|
|
22
|
-
|
|
23
|
+
/** How often it should update the the player Position */
|
|
24
|
+
clientBasedPositionUpdateInterval?: number;
|
|
25
|
+
/** What should be used as a searchPlatform, if no source was provided during the query */
|
|
23
26
|
defaultSearchPlatform?: SearchPlatform;
|
|
27
|
+
/** Applies the volume via a filter, not via the lavalink volume transformer */
|
|
24
28
|
applyVolumeAsFilter?: boolean;
|
|
29
|
+
/** Transforms the saved data of a requested user */
|
|
30
|
+
requesterTransformer?: (requester: unknown) => unknown;
|
|
31
|
+
/** What lavalink-client should do when the player reconnects */
|
|
25
32
|
onDisconnect?: {
|
|
33
|
+
/** Try to reconnect? -> If fails -> Destroy */
|
|
26
34
|
autoReconnect?: boolean;
|
|
35
|
+
/** Instantly destroy player (overrides autoReconnect) */
|
|
27
36
|
destroyPlayer?: boolean;
|
|
28
37
|
};
|
|
29
38
|
onEmptyQueue?: {
|
|
39
|
+
/** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
|
|
40
|
+
autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
|
|
30
41
|
destroyAfterMs?: number;
|
|
31
42
|
};
|
|
32
43
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Base64 } from "./Utils";
|
|
2
|
+
type LavalinkSourceNames = "youtube" | "youtubemusic" | "soundcloud" | "bandcamp" | "twitch";
|
|
3
|
+
type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts";
|
|
4
|
+
type SourceNames = LavalinkSourceNames | LavalinkPlugin_LavaSrc_SourceNames;
|
|
2
5
|
export interface TrackInfo {
|
|
3
6
|
identifier: string;
|
|
4
7
|
title: string;
|
|
@@ -6,7 +9,7 @@ export interface TrackInfo {
|
|
|
6
9
|
duration: number;
|
|
7
10
|
artworkUrl: string | null;
|
|
8
11
|
uri: string;
|
|
9
|
-
sourceName:
|
|
12
|
+
sourceName: SourceNames;
|
|
10
13
|
isSeekable: boolean;
|
|
11
14
|
isStream: boolean;
|
|
12
15
|
isrc: string | null;
|
|
@@ -24,6 +27,10 @@ export interface PluginInfo {
|
|
|
24
27
|
url?: string;
|
|
25
28
|
/** The Url provided by a Plugin */
|
|
26
29
|
uri?: string;
|
|
30
|
+
/** You can put specific track information here, to transform the tracks... */
|
|
31
|
+
clientData?: {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
export interface LavalinkTrack {
|
|
29
36
|
/** The Base 64 encoded String */
|
|
@@ -45,3 +52,4 @@ export interface UnresolvedQuery {
|
|
|
45
52
|
/** The Track's Requester */
|
|
46
53
|
requester?: unknown;
|
|
47
54
|
}
|
|
55
|
+
export {};
|
|
@@ -8,7 +8,7 @@ export declare const TrackSymbol: unique symbol;
|
|
|
8
8
|
export declare const UnresolvedTrackSymbol: unique symbol;
|
|
9
9
|
export declare const QueueSymbol: unique symbol;
|
|
10
10
|
export declare const NodeSymbol: unique symbol;
|
|
11
|
-
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts";
|
|
11
|
+
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts" | "ftts";
|
|
12
12
|
export type ClientSearchPlatform = "youtube" | "yt" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "yandex music" | "sp" | "sprec" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandexmusic";
|
|
13
13
|
export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
|
|
14
14
|
export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
|
package/package.json
CHANGED