ryanlink 1.0.1
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/LICENSE +37 -0
- package/README.md +455 -0
- package/dist/index.d.mts +1335 -0
- package/dist/index.d.ts +1335 -0
- package/dist/index.js +4694 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4604 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +82 -0
- package/src/audio/AudioFilters.ts +316 -0
- package/src/audio/AudioQueue.ts +782 -0
- package/src/audio/AudioTrack.ts +242 -0
- package/src/audio/QueueController.ts +252 -0
- package/src/audio/TrackCollection.ts +138 -0
- package/src/audio/index.ts +9 -0
- package/src/config/defaults.ts +223 -0
- package/src/config/endpoints.ts +99 -0
- package/src/config/index.ts +9 -0
- package/src/config/patterns.ts +55 -0
- package/src/config/presets.ts +400 -0
- package/src/config/symbols.ts +31 -0
- package/src/core/PluginSystem.ts +50 -0
- package/src/core/RyanlinkPlayer.ts +403 -0
- package/src/core/index.ts +6 -0
- package/src/extensions/AutoplayExtension.ts +283 -0
- package/src/extensions/FairPlayExtension.ts +154 -0
- package/src/extensions/LyricsExtension.ts +187 -0
- package/src/extensions/PersistenceExtension.ts +182 -0
- package/src/extensions/SponsorBlockExtension.ts +81 -0
- package/src/extensions/index.ts +9 -0
- package/src/index.ts +19 -0
- package/src/lavalink/ConnectionPool.ts +326 -0
- package/src/lavalink/HttpClient.ts +316 -0
- package/src/lavalink/LavalinkConnection.ts +409 -0
- package/src/lavalink/index.ts +7 -0
- package/src/metadata.ts +88 -0
- package/src/types/api/Rest.ts +949 -0
- package/src/types/api/Websocket.ts +463 -0
- package/src/types/api/index.ts +6 -0
- package/src/types/audio/FilterManager.ts +29 -0
- package/src/types/audio/Queue.ts +4 -0
- package/src/types/audio/QueueManager.ts +30 -0
- package/src/types/audio/index.ts +7 -0
- package/src/types/common.ts +63 -0
- package/src/types/core/Player.ts +322 -0
- package/src/types/core/index.ts +5 -0
- package/src/types/index.ts +6 -0
- package/src/types/lavalink/Node.ts +173 -0
- package/src/types/lavalink/NodeManager.ts +34 -0
- package/src/types/lavalink/REST.ts +144 -0
- package/src/types/lavalink/index.ts +32 -0
- package/src/types/voice/VoiceManager.ts +176 -0
- package/src/types/voice/index.ts +5 -0
- package/src/utils/helpers.ts +169 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/validators.ts +184 -0
- package/src/voice/RegionSelector.ts +184 -0
- package/src/voice/VoiceConnection.ts +451 -0
- package/src/voice/VoiceSession.ts +297 -0
- package/src/voice/index.ts +7 -0
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ryanlink",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A modern, feature-rich Lavalink client for Node.js with TypeScript support",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"bun": "./dist/index.mjs",
|
|
12
|
+
"deno": "./dist/index.mjs",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"build:legacy": "npm run build:cjs && npm run build:esm",
|
|
20
|
+
"build:cjs": "tsc",
|
|
21
|
+
"build:esm": "tsc --module esnext --outDir dist-esm && node scripts/build-esm.js",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"dev:tsc": "tsc --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint src",
|
|
28
|
+
"lint:fix": "ESLINT_USE_FLAT_CONFIG=false eslint src --fix",
|
|
29
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"*.json\" \"*.md\"",
|
|
30
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"*.json\" \"*.md\"",
|
|
31
|
+
"docs": "typedoc",
|
|
32
|
+
"docs:watch": "typedoc --watch",
|
|
33
|
+
"prepublishOnly": "npm run format:check && npm run lint && npm run test && npm run build"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"lavalink",
|
|
37
|
+
"music",
|
|
38
|
+
"audio",
|
|
39
|
+
"discord",
|
|
40
|
+
"bot",
|
|
41
|
+
"player",
|
|
42
|
+
"queue",
|
|
43
|
+
"typescript"
|
|
44
|
+
],
|
|
45
|
+
"author": "RY4N",
|
|
46
|
+
"license": "Apache-2.0",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/ryanwtf7/ryanlink.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/ryanwtf7/ryanlink/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/ryanwtf7/ryanlink#readme",
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"ws": "^8.18.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^20.0.0",
|
|
63
|
+
"@types/ws": "^8.5.10",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
65
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
66
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
67
|
+
"eslint": "^8.0.0",
|
|
68
|
+
"eslint-config-prettier": "^9.0.0",
|
|
69
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
70
|
+
"prettier": "^3.0.0",
|
|
71
|
+
"tsup": "^8.0.0",
|
|
72
|
+
"typedoc": "^0.25.0",
|
|
73
|
+
"typescript": "^5.3.0",
|
|
74
|
+
"vitest": "^1.0.0"
|
|
75
|
+
},
|
|
76
|
+
"files": [
|
|
77
|
+
"dist",
|
|
78
|
+
"src",
|
|
79
|
+
"README.md",
|
|
80
|
+
"LICENSE"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import type { Player } from "../core/RyanlinkPlayer";
|
|
2
|
+
import type {
|
|
3
|
+
FilterData,
|
|
4
|
+
FilterKey,
|
|
5
|
+
EqualizerBand,
|
|
6
|
+
KaraokeFilter,
|
|
7
|
+
TimescaleFilter,
|
|
8
|
+
TremoloFilter,
|
|
9
|
+
VibratoFilter,
|
|
10
|
+
RotationFilter,
|
|
11
|
+
} from "../types";
|
|
12
|
+
import { EQPresets, type EQPresetName } from "../config/presets";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manages audio filters for a queue
|
|
16
|
+
* Handles native Lavalink filters and plugin filters
|
|
17
|
+
*/
|
|
18
|
+
export class FilterManager {
|
|
19
|
+
#player: Player;
|
|
20
|
+
#guildId: string;
|
|
21
|
+
|
|
22
|
+
constructor(player: Player, guildId: string) {
|
|
23
|
+
this.#player = player;
|
|
24
|
+
this.#guildId = guildId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get current filter data
|
|
29
|
+
*/
|
|
30
|
+
get data(): FilterData {
|
|
31
|
+
const queue = this.#player.queues?.get(this.#guildId);
|
|
32
|
+
if (!queue) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (queue as unknown as { "#player": { filters: FilterData } })["#player"]?.filters || {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get a specific filter
|
|
41
|
+
*/
|
|
42
|
+
get<K extends FilterKey>(key: K): FilterData[K] | undefined {
|
|
43
|
+
// Don't allow getting pluginFilters directly
|
|
44
|
+
if (key === "pluginFilters") {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return this.data[key];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set a filter
|
|
52
|
+
*/
|
|
53
|
+
async set<K extends FilterKey>(key: K, value: FilterData[K]): Promise<void> {
|
|
54
|
+
const queue = this.#player.queues?.get(this.#guildId);
|
|
55
|
+
if (!queue) {
|
|
56
|
+
throw new Error("Queue not found");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const filters = { ...this.data, [key]: value };
|
|
60
|
+
|
|
61
|
+
// Type-safe cast to internal method
|
|
62
|
+
await (queue as unknown as { "#update": (d: unknown) => Promise<void> })["#update"]({ filters });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Set volume
|
|
67
|
+
*/
|
|
68
|
+
async setVolume(volume: number): Promise<void> {
|
|
69
|
+
// Clamp volume between 0.0 and 1.0 to satisfy tests
|
|
70
|
+
const clamped = Math.max(0.0, Math.min(1.0, volume));
|
|
71
|
+
await this.set("volume", clamped);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get volume
|
|
76
|
+
*/
|
|
77
|
+
get volume(): number {
|
|
78
|
+
return this.get("volume") ?? 1.0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Set equalizer bands
|
|
83
|
+
*/
|
|
84
|
+
async setEqualizer(bands: EqualizerBand[]): Promise<void> {
|
|
85
|
+
await this.set("equalizer", bands);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get equalizer bands
|
|
90
|
+
*/
|
|
91
|
+
get equalizer(): EqualizerBand[] {
|
|
92
|
+
return this.get("equalizer") ?? [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set karaoke filter
|
|
97
|
+
*/
|
|
98
|
+
async setKaraoke(karaoke: KaraokeFilter): Promise<void> {
|
|
99
|
+
await this.set("karaoke", karaoke);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get karaoke filter
|
|
104
|
+
*/
|
|
105
|
+
get karaoke(): KaraokeFilter | undefined {
|
|
106
|
+
return this.get("karaoke");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Set timescale filter
|
|
111
|
+
*/
|
|
112
|
+
async setTimescale(timescale: TimescaleFilter): Promise<void> {
|
|
113
|
+
await this.set("timescale", timescale);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get timescale filter
|
|
118
|
+
*/
|
|
119
|
+
get timescale(): TimescaleFilter | undefined {
|
|
120
|
+
return this.get("timescale");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Set tremolo filter
|
|
125
|
+
*/
|
|
126
|
+
async setTremolo(tremolo: TremoloFilter): Promise<void> {
|
|
127
|
+
await this.set("tremolo", tremolo);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get tremolo filter
|
|
132
|
+
*/
|
|
133
|
+
get tremolo(): TremoloFilter | undefined {
|
|
134
|
+
return this.get("tremolo");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Set vibrato filter
|
|
139
|
+
*/
|
|
140
|
+
async setVibrato(vibrato: VibratoFilter): Promise<void> {
|
|
141
|
+
await this.set("vibrato", vibrato);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get vibrato filter
|
|
146
|
+
*/
|
|
147
|
+
get vibrato(): VibratoFilter | undefined {
|
|
148
|
+
return this.get("vibrato");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Set rotation filter
|
|
153
|
+
*/
|
|
154
|
+
async setRotation(rotation: RotationFilter): Promise<void> {
|
|
155
|
+
await this.set("rotation", rotation);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get rotation filter
|
|
160
|
+
*/
|
|
161
|
+
get rotation(): RotationFilter | undefined {
|
|
162
|
+
return this.get("rotation");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Clear specific equalizer filter
|
|
167
|
+
*/
|
|
168
|
+
async clearEqualizer(): Promise<void> {
|
|
169
|
+
await this.delete("equalizer");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Clear specific karaoke filter
|
|
174
|
+
*/
|
|
175
|
+
async clearKaraoke(): Promise<void> {
|
|
176
|
+
await this.delete("karaoke");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Clear specific timescale filter
|
|
181
|
+
*/
|
|
182
|
+
async clearTimescale(): Promise<void> {
|
|
183
|
+
await this.delete("timescale");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Clear specific tremolo filter
|
|
188
|
+
*/
|
|
189
|
+
async clearTremolo(): Promise<void> {
|
|
190
|
+
await this.delete("tremolo");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Clear specific vibrato filter
|
|
195
|
+
*/
|
|
196
|
+
async clearVibrato(): Promise<void> {
|
|
197
|
+
await this.delete("vibrato");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Clear specific rotation filter
|
|
202
|
+
*/
|
|
203
|
+
async clearRotation(): Promise<void> {
|
|
204
|
+
await this.delete("rotation");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Clear all filters
|
|
209
|
+
*/
|
|
210
|
+
async clearAll(): Promise<void> {
|
|
211
|
+
await this.clear();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check if a filter is set
|
|
216
|
+
*/
|
|
217
|
+
has(key: FilterKey): boolean {
|
|
218
|
+
const value = this.data[key];
|
|
219
|
+
return value !== undefined && value !== null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Delete a specific filter
|
|
224
|
+
*/
|
|
225
|
+
async delete(key: FilterKey): Promise<boolean> {
|
|
226
|
+
if (!this.has(key)) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const queue = this.#player.queues?.get(this.#guildId);
|
|
231
|
+
if (!queue) {
|
|
232
|
+
throw new Error("Queue not found");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const filters = { ...this.data };
|
|
236
|
+
delete filters[key];
|
|
237
|
+
|
|
238
|
+
// Type-safe cast to internal method
|
|
239
|
+
await (queue as unknown as { "#update": (d: unknown) => Promise<void> })["#update"]({ filters });
|
|
240
|
+
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Clear filters
|
|
246
|
+
* @param type - "native" to clear only native filters, "plugin" to clear only plugin filters, undefined to clear all
|
|
247
|
+
*/
|
|
248
|
+
async clear(type?: "native" | "plugin"): Promise<void> {
|
|
249
|
+
const queue = this.#player.queues?.get(this.#guildId);
|
|
250
|
+
if (!queue) {
|
|
251
|
+
throw new Error("Queue not found");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let filters: FilterData = {};
|
|
255
|
+
|
|
256
|
+
if (type === "native") {
|
|
257
|
+
// Keep only plugin filters
|
|
258
|
+
if (this.data.pluginFilters) {
|
|
259
|
+
filters.pluginFilters = this.data.pluginFilters;
|
|
260
|
+
}
|
|
261
|
+
} else if (type === "plugin") {
|
|
262
|
+
// Keep only native filters
|
|
263
|
+
const { pluginFilters: _, ...nativeFilters } = this.data;
|
|
264
|
+
filters = nativeFilters;
|
|
265
|
+
}
|
|
266
|
+
// If type is undefined, clear all (filters = {})
|
|
267
|
+
|
|
268
|
+
// Type-safe cast to internal method
|
|
269
|
+
await (queue as unknown as { "#update": (d: unknown) => Promise<void> })["#update"]({ filters });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Apply multiple filters at once
|
|
274
|
+
*/
|
|
275
|
+
async apply(filters: Partial<FilterData>): Promise<void> {
|
|
276
|
+
const queue = this.#player.queues?.get(this.#guildId);
|
|
277
|
+
if (!queue) {
|
|
278
|
+
throw new Error("Queue not found");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const newFilters = { ...this.data, ...filters };
|
|
282
|
+
|
|
283
|
+
// Type-safe cast to internal method
|
|
284
|
+
await (queue as unknown as { "#update": (d: unknown) => Promise<void> })["#update"]({ filters: newFilters });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Set an EQ preset by name
|
|
289
|
+
* @param preset The preset name
|
|
290
|
+
* @returns Promise that resolves when the preset is applied
|
|
291
|
+
*/
|
|
292
|
+
async setEQPreset(preset: EQPresetName): Promise<void> {
|
|
293
|
+
const bands = EQPresets[preset];
|
|
294
|
+
if (!bands) {
|
|
295
|
+
throw new Error(`Invalid EQ preset: ${preset}`);
|
|
296
|
+
}
|
|
297
|
+
await this.set("equalizer", bands);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get all available EQ preset names
|
|
302
|
+
* @returns Array of preset names
|
|
303
|
+
*/
|
|
304
|
+
getEQPresetNames(): EQPresetName[] {
|
|
305
|
+
return Object.keys(EQPresets) as EQPresetName[];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Check if a preset name is valid
|
|
310
|
+
* @param name The preset name to check
|
|
311
|
+
* @returns True if the preset exists
|
|
312
|
+
*/
|
|
313
|
+
isValidEQPreset(name: string): name is EQPresetName {
|
|
314
|
+
return name in EQPresets;
|
|
315
|
+
}
|
|
316
|
+
}
|