hoshimi 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4577 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AudioOutput: () => AudioOutput,
24
+ DSPXPluginFilter: () => DSPXPluginFilter,
25
+ DebugLevels: () => DebugLevels,
26
+ DestroyReasons: () => DestroyReasons,
27
+ Events: () => Events,
28
+ FilterManager: () => FilterManager,
29
+ FilterType: () => FilterType,
30
+ Hoshimi: () => Hoshimi,
31
+ HttpMethods: () => HttpMethods,
32
+ HttpStatusCodes: () => HttpStatusCodes,
33
+ LavalinkPluginFilter: () => LavalinkPluginFilter,
34
+ LoadType: () => LoadType,
35
+ LoopMode: () => LoopMode,
36
+ ManagerError: () => ManagerError,
37
+ MemoryAdapter: () => MemoryAdapter,
38
+ Node: () => Node,
39
+ NodeDestroyReasons: () => NodeDestroyReasons,
40
+ NodeError: () => NodeError,
41
+ NodeSortTypes: () => NodeSortTypes,
42
+ OpCodes: () => OpCodes,
43
+ OptionError: () => OptionError,
44
+ Player: () => Player,
45
+ PlayerError: () => PlayerError,
46
+ PlayerEventType: () => PlayerEventType,
47
+ PluginInfoType: () => PluginInfoType,
48
+ PluginNames: () => PluginNames,
49
+ Queue: () => Queue,
50
+ ResolveError: () => ResolveError,
51
+ Rest: () => Rest,
52
+ SearchEngines: () => SearchEngines,
53
+ Severity: () => Severity,
54
+ SourceNames: () => SourceNames,
55
+ State: () => State,
56
+ StorageAdapter: () => StorageAdapter,
57
+ StorageError: () => StorageError,
58
+ Structures: () => Structures,
59
+ Track: () => Track,
60
+ TrackEndReason: () => TrackEndReason,
61
+ UnresolvedTrack: () => UnresolvedTrack,
62
+ WebsocketCloseCodes: () => WebsocketCloseCodes,
63
+ createHoshimi: () => createHoshimi
64
+ });
65
+ module.exports = __toCommonJS(index_exports);
66
+
67
+ // src/types/Node.ts
68
+ var State = /* @__PURE__ */ ((State2) => {
69
+ State2[State2["Connecting"] = 1] = "Connecting";
70
+ State2[State2["Connected"] = 2] = "Connected";
71
+ State2[State2["Disconnected"] = 3] = "Disconnected";
72
+ State2[State2["Reconnecting"] = 4] = "Reconnecting";
73
+ State2[State2["Reconnected"] = 5] = "Reconnected";
74
+ State2[State2["Destroyed"] = 6] = "Destroyed";
75
+ State2[State2["Idle"] = 7] = "Idle";
76
+ return State2;
77
+ })(State || {});
78
+ var OpCodes = /* @__PURE__ */ ((OpCodes2) => {
79
+ OpCodes2["Ready"] = "ready";
80
+ OpCodes2["PlayerUpdate"] = "playerUpdate";
81
+ OpCodes2["Stats"] = "stats";
82
+ OpCodes2["Event"] = "event";
83
+ return OpCodes2;
84
+ })(OpCodes || {});
85
+ var LoadType = /* @__PURE__ */ ((LoadType2) => {
86
+ LoadType2["Track"] = "track";
87
+ LoadType2["Playlist"] = "playlist";
88
+ LoadType2["Search"] = "search";
89
+ LoadType2["Empty"] = "empty";
90
+ LoadType2["Error"] = "error";
91
+ return LoadType2;
92
+ })(LoadType || {});
93
+ var SourceNames = /* @__PURE__ */ ((SourceNames2) => {
94
+ SourceNames2["Youtube"] = "youtube";
95
+ SourceNames2["YoutubeMusic"] = "youtubemusic";
96
+ SourceNames2["Soundcloud"] = "soundcloud";
97
+ SourceNames2["Bandcamp"] = "bandcamp";
98
+ SourceNames2["Twitch"] = "twitch";
99
+ SourceNames2["Vimeo"] = "vimeo";
100
+ SourceNames2["Mixer"] = "mixer";
101
+ SourceNames2["Spotify"] = "spotify";
102
+ SourceNames2["Deezer"] = "deezer";
103
+ SourceNames2["VKMusic"] = "vkmusic";
104
+ SourceNames2["Tidal"] = "tidal";
105
+ SourceNames2["JioSaavn"] = "jiosaavn";
106
+ SourceNames2["AppleMusic"] = "applemusic";
107
+ SourceNames2["YandexMusic"] = "yandexmusic";
108
+ SourceNames2["FloweryTTS"] = "flowery-tts";
109
+ SourceNames2["HTTP"] = "http";
110
+ SourceNames2["PornHub"] = "pornhub";
111
+ SourceNames2["TextToSpeech"] = "tts";
112
+ SourceNames2["Clypit"] = "clypit";
113
+ SourceNames2["StreamDeckAudio"] = "StreamDeckAudio";
114
+ SourceNames2["GetYarn"] = "getyarn.io";
115
+ SourceNames2["MixCloud"] = "mixcloud";
116
+ SourceNames2["OCRemix"] = "ocremix";
117
+ SourceNames2["PixelDrain"] = "pixeldrain";
118
+ SourceNames2["Reddit"] = "reddit";
119
+ SourceNames2["SoundGasm"] = "soundgasm";
120
+ SourceNames2["TikTok"] = "tiktok";
121
+ return SourceNames2;
122
+ })(SourceNames || {});
123
+ var Severity = /* @__PURE__ */ ((Severity2) => {
124
+ Severity2["Common"] = "common";
125
+ Severity2["Suspicious"] = "suspicious";
126
+ Severity2["Fault"] = "fault";
127
+ return Severity2;
128
+ })(Severity || {});
129
+ var PluginInfoType = /* @__PURE__ */ ((PluginInfoType2) => {
130
+ PluginInfoType2["Album"] = "album";
131
+ PluginInfoType2["Playlist"] = "playlist";
132
+ PluginInfoType2["Artist"] = "artist";
133
+ PluginInfoType2["Recommendations"] = "recommendations";
134
+ return PluginInfoType2;
135
+ })(PluginInfoType || {});
136
+ var NodeDestroyReasons = /* @__PURE__ */ ((NodeDestroyReasons2) => {
137
+ NodeDestroyReasons2["Destroy"] = "Node-Destroy";
138
+ NodeDestroyReasons2["MissingSession"] = "Missing-Session";
139
+ return NodeDestroyReasons2;
140
+ })(NodeDestroyReasons || {});
141
+ var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
142
+ WebsocketCloseCodes2[WebsocketCloseCodes2["NormalClosure"] = 1e3] = "NormalClosure";
143
+ WebsocketCloseCodes2[WebsocketCloseCodes2["GoingAway"] = 1001] = "GoingAway";
144
+ WebsocketCloseCodes2[WebsocketCloseCodes2["ProtocolError"] = 1002] = "ProtocolError";
145
+ WebsocketCloseCodes2[WebsocketCloseCodes2["UnsupportedData"] = 1003] = "UnsupportedData";
146
+ WebsocketCloseCodes2[WebsocketCloseCodes2["NoStatusReceived"] = 1005] = "NoStatusReceived";
147
+ WebsocketCloseCodes2[WebsocketCloseCodes2["AbnormalClosure"] = 1006] = "AbnormalClosure";
148
+ WebsocketCloseCodes2[WebsocketCloseCodes2["InvalidFramePayloadData"] = 1007] = "InvalidFramePayloadData";
149
+ WebsocketCloseCodes2[WebsocketCloseCodes2["PolicyViolation"] = 1008] = "PolicyViolation";
150
+ WebsocketCloseCodes2[WebsocketCloseCodes2["MessageTooBig"] = 1009] = "MessageTooBig";
151
+ WebsocketCloseCodes2[WebsocketCloseCodes2["MandatoryExtension"] = 1010] = "MandatoryExtension";
152
+ WebsocketCloseCodes2[WebsocketCloseCodes2["InternalError"] = 1011] = "InternalError";
153
+ WebsocketCloseCodes2[WebsocketCloseCodes2["ServiceRestart"] = 1012] = "ServiceRestart";
154
+ WebsocketCloseCodes2[WebsocketCloseCodes2["TryAgainLater"] = 1013] = "TryAgainLater";
155
+ WebsocketCloseCodes2[WebsocketCloseCodes2["BadGateway"] = 1014] = "BadGateway";
156
+ WebsocketCloseCodes2[WebsocketCloseCodes2["TLSHandshakeFailure"] = 1015] = "TLSHandshakeFailure";
157
+ WebsocketCloseCodes2[WebsocketCloseCodes2["Unauthorized"] = 3e3] = "Unauthorized";
158
+ WebsocketCloseCodes2[WebsocketCloseCodes2["Forbidden"] = 3003] = "Forbidden";
159
+ WebsocketCloseCodes2[WebsocketCloseCodes2["Timeout"] = 3008] = "Timeout";
160
+ return WebsocketCloseCodes2;
161
+ })(WebsocketCloseCodes || {});
162
+ var PluginNames = /* @__PURE__ */ ((PluginNames2) => {
163
+ PluginNames2["LavaSrc"] = "lavasrc-plugin";
164
+ PluginNames2["JavaLyrics"] = "java-lyrics-plugin";
165
+ PluginNames2["LavaLyrics"] = "lavalyrics-plugin";
166
+ PluginNames2["LavaSearch"] = "lavasearch-plugin";
167
+ PluginNames2["SponsorBlock"] = "sponsorblock-plugin";
168
+ PluginNames2["LavaDspx"] = "lavadspx-plugin";
169
+ PluginNames2["Youtube"] = "youtube-plugin";
170
+ PluginNames2["Skybot"] = "skybot-lavalink-plugin";
171
+ PluginNames2["LavaXm"] = "lava-xm-plugin";
172
+ PluginNames2["Jiosaavn"] = "jiosaavn-plugin";
173
+ PluginNames2["FilterPlugin"] = "lavalink-filter-plugin";
174
+ return PluginNames2;
175
+ })(PluginNames || {});
176
+ var NodeSortTypes = /* @__PURE__ */ ((NodeSortTypes2) => {
177
+ NodeSortTypes2["Memory"] = "memory";
178
+ NodeSortTypes2["Cpu"] = "cpu";
179
+ NodeSortTypes2["Players"] = "players";
180
+ NodeSortTypes2["PlayingPlayers"] = "playingPlayers";
181
+ NodeSortTypes2["SystemLoad"] = "systemLoad";
182
+ NodeSortTypes2["LavalinkLoad"] = "lavalinkLoad";
183
+ return NodeSortTypes2;
184
+ })(NodeSortTypes || {});
185
+
186
+ // src/classes/Errors.ts
187
+ var ManagerError = class extends Error {
188
+ constructor(message) {
189
+ super(message);
190
+ this.name = "Hoshimi [ManagerError]";
191
+ }
192
+ };
193
+ var OptionError = class extends Error {
194
+ constructor(message) {
195
+ super(message);
196
+ this.name = "Hoshimi [OptionError]";
197
+ }
198
+ };
199
+ var PlayerError = class extends Error {
200
+ constructor(message) {
201
+ super(message);
202
+ this.name = "Hoshimi [PlayerError]";
203
+ }
204
+ };
205
+ var NodeError = class extends Error {
206
+ constructor({ message, id }) {
207
+ super(message);
208
+ this.name = `Hoshimi [NodeError | ${id}]`;
209
+ }
210
+ };
211
+ var StorageError = class extends Error {
212
+ constructor(message) {
213
+ super(message);
214
+ this.name = "Hoshimi [StorageError]";
215
+ }
216
+ };
217
+ var ResolveError = class extends Error {
218
+ constructor(message) {
219
+ super(message);
220
+ this.name = "Hoshimi [ResolveError]";
221
+ }
222
+ };
223
+
224
+ // src/types/Manager.ts
225
+ var SearchEngines = /* @__PURE__ */ ((SearchEngines2) => {
226
+ SearchEngines2["Youtube"] = "ytsearch";
227
+ SearchEngines2["YoutubeMusic"] = "ytmsearch";
228
+ SearchEngines2["Spotify"] = "spsearch";
229
+ SearchEngines2["SpotifyRecommendations"] = "sprec";
230
+ SearchEngines2["SpotifyArtistMix"] = "sprec:mix:artist";
231
+ SearchEngines2["SpotifyAlbumMix"] = "sprec:mix:album";
232
+ SearchEngines2["SpotifyTrackMix"] = "sprec:mix:track";
233
+ SearchEngines2["SpotifyISRCMix"] = "sprec:mix:isrc";
234
+ SearchEngines2["SoundCloud"] = "scsearch";
235
+ SearchEngines2["AppleMusic"] = "amsearch";
236
+ SearchEngines2["BandCamp"] = "bcsearch";
237
+ SearchEngines2["Deezer"] = "dzsearch";
238
+ SearchEngines2["DeezerISRC"] = "dzisrc";
239
+ SearchEngines2["DeezerRecommendations"] = "dzrec";
240
+ SearchEngines2["YandexMusic"] = "ymsearch";
241
+ SearchEngines2["YandexMusicRecommendations"] = "ymrec";
242
+ SearchEngines2["VKMusic"] = "vksearch";
243
+ SearchEngines2["VKMusicRecommendations"] = "vkrec";
244
+ SearchEngines2["Tidal"] = "tdsearch";
245
+ SearchEngines2["TidalRecommendations"] = "tdrec";
246
+ SearchEngines2["Qobuz"] = "qbsearch";
247
+ SearchEngines2["QobuzISRC"] = "qbisrc";
248
+ SearchEngines2["QobuzRecommendations"] = "qbrec";
249
+ SearchEngines2["JioSaavn"] = "jssearch";
250
+ SearchEngines2["JioSaavnRecommendations"] = "jsrec";
251
+ SearchEngines2["Twitch"] = "twsearch";
252
+ SearchEngines2["Mixer"] = "mxsearch";
253
+ SearchEngines2["Vimeo"] = "vmsearch";
254
+ SearchEngines2["FloweryTTS"] = "ftts";
255
+ SearchEngines2["Local"] = "local";
256
+ SearchEngines2["PornHub"] = "phsearch";
257
+ SearchEngines2["TextToSpeech"] = "speak";
258
+ return SearchEngines2;
259
+ })(SearchEngines || {});
260
+ var DebugLevels = /* @__PURE__ */ ((DebugLevels2) => {
261
+ DebugLevels2[DebugLevels2["Manager"] = 1] = "Manager";
262
+ DebugLevels2[DebugLevels2["Node"] = 2] = "Node";
263
+ DebugLevels2[DebugLevels2["Player"] = 3] = "Player";
264
+ DebugLevels2[DebugLevels2["Rest"] = 4] = "Rest";
265
+ DebugLevels2[DebugLevels2["Queue"] = 5] = "Queue";
266
+ DebugLevels2[DebugLevels2["Test"] = 6] = "Test";
267
+ return DebugLevels2;
268
+ })(DebugLevels || {});
269
+ var Events = /* @__PURE__ */ ((Events2) => {
270
+ Events2["Debug"] = "debug";
271
+ Events2["Error"] = "error";
272
+ Events2["NodeRaw"] = "nodeRaw";
273
+ Events2["NodeError"] = "nodeError";
274
+ Events2["NodeReady"] = "nodeReady";
275
+ Events2["NodeDisconnect"] = "nodeDisconnect";
276
+ Events2["NodeReconnecting"] = "nodeReconnecting";
277
+ Events2["NodeDestroy"] = "nodeDestroy";
278
+ Events2["NodeResumed"] = "nodeResumed";
279
+ Events2["NodeCreate"] = "nodeCreate";
280
+ Events2["PlayerCreate"] = "playerCreate";
281
+ Events2["PlayerUpdate"] = "playerUpdate";
282
+ Events2["PlayerDestroy"] = "playerDestroy";
283
+ Events2["TrackStart"] = "trackStart";
284
+ Events2["TrackEnd"] = "trackEnd";
285
+ Events2["TrackStuck"] = "trackStuck";
286
+ Events2["TrackError"] = "trackError";
287
+ Events2["LyricsFound"] = "lyricsFound";
288
+ Events2["LyricsNotFound"] = "lyricsNotFound";
289
+ Events2["LyricsLine"] = "lyricsLine";
290
+ Events2["QueueEnd"] = "queueEnd";
291
+ Events2["QueueUpdate"] = "queueUpdate";
292
+ Events2["WebSocketClosed"] = "socketClosed";
293
+ return Events2;
294
+ })(Events || {});
295
+ var DestroyReasons = /* @__PURE__ */ ((DestroyReasons2) => {
296
+ DestroyReasons2["Stop"] = "Player-Stop";
297
+ DestroyReasons2["Requested"] = "Player-Requested";
298
+ DestroyReasons2["Empty"] = "Player-Empty";
299
+ DestroyReasons2["NodeDisconnected"] = "Player-NodeDisconnected";
300
+ DestroyReasons2["NodeDestroy"] = "Player-NodeDestroy";
301
+ DestroyReasons2["VoiceChannelDeleted"] = "Player-VoiceChannelDeleted";
302
+ return DestroyReasons2;
303
+ })(DestroyReasons || {});
304
+
305
+ // src/types/Player.ts
306
+ var LoopMode = /* @__PURE__ */ ((LoopMode2) => {
307
+ LoopMode2[LoopMode2["Track"] = 1] = "Track";
308
+ LoopMode2[LoopMode2["Queue"] = 2] = "Queue";
309
+ LoopMode2[LoopMode2["Off"] = 3] = "Off";
310
+ return LoopMode2;
311
+ })(LoopMode || {});
312
+ var PlayerEventType = /* @__PURE__ */ ((PlayerEventType2) => {
313
+ PlayerEventType2["TrackStart"] = "TrackStartEvent";
314
+ PlayerEventType2["TrackEnd"] = "TrackEndEvent";
315
+ PlayerEventType2["TrackException"] = "TrackExceptionEvent";
316
+ PlayerEventType2["TrackStuck"] = "TrackStuckEvent";
317
+ PlayerEventType2["LyricsFound"] = "LyricsFoundEvent";
318
+ PlayerEventType2["LyricsNotFound"] = "LyricsNotFoundEvent";
319
+ PlayerEventType2["LyricsLine"] = "LyricsLineEvent";
320
+ PlayerEventType2["WebsocketClosed"] = "WebSocketClosedEvent";
321
+ return PlayerEventType2;
322
+ })(PlayerEventType || {});
323
+ var TrackEndReason = /* @__PURE__ */ ((TrackEndReason2) => {
324
+ TrackEndReason2["Finished"] = "finished";
325
+ TrackEndReason2["LoadFailed"] = "loadFailed";
326
+ TrackEndReason2["Stopped"] = "stopped";
327
+ TrackEndReason2["Replaced"] = "replaced";
328
+ TrackEndReason2["Cleanup"] = "cleanup";
329
+ return TrackEndReason2;
330
+ })(TrackEndReason || {});
331
+
332
+ // package.json
333
+ var package_default = {
334
+ name: "hoshimi",
335
+ version: "0.2.0",
336
+ description: "A lavalink@v4 client easy to use, up-to-date and all ears.",
337
+ main: "./dist/index.js",
338
+ module: "./dist/index.mjs",
339
+ types: "./dist/index.d.ts",
340
+ packageManager: "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd",
341
+ files: [
342
+ "dist"
343
+ ],
344
+ "lint-staged": {
345
+ "*.{ts,json}": [
346
+ "pnpm format"
347
+ ]
348
+ },
349
+ repository: {
350
+ url: "https://github.com/Ganyu-Studios/Hoshimi.git",
351
+ type: "git"
352
+ },
353
+ scripts: {
354
+ build: "pnpm typecheck && tsup",
355
+ typecheck: "tsc --noEmit",
356
+ lint: "biome lint .",
357
+ format: "biome format --write .",
358
+ prepare: "husky && pnpm build",
359
+ "tool:prepublish": "pnpm build && tsx ./scripts/prepublish.mts && cd .npm && npm pack",
360
+ "tool:publish": "cd .npm && npm publish"
361
+ },
362
+ keywords: [
363
+ "discord",
364
+ "lavalink",
365
+ "lavalink-v4",
366
+ "discord",
367
+ "distube",
368
+ "music-bot",
369
+ "lavalink-player",
370
+ "lavalink-client",
371
+ "music"
372
+ ],
373
+ author: "Ganyu Studios",
374
+ license: "MIT",
375
+ devDependencies: {
376
+ "@biomejs/biome": "^2.3.2",
377
+ "@types/node": "^24.9.2",
378
+ "@types/ws": "^8.18.1",
379
+ husky: "^9.1.7",
380
+ "lint-staged": "^16.2.6",
381
+ tsup: "^8.5.0",
382
+ tsx: "^4.20.6",
383
+ typescript: "^5.9.3"
384
+ },
385
+ dependencies: {
386
+ ws: "^8.18.3"
387
+ },
388
+ pnpm: {
389
+ onlyBuiltDependencies: [
390
+ "esbuild"
391
+ ]
392
+ }
393
+ };
394
+
395
+ // src/types/Filters.ts
396
+ var AudioOutput = /* @__PURE__ */ ((AudioOutput2) => {
397
+ AudioOutput2["Mono"] = "mono";
398
+ AudioOutput2["Stereo"] = "stereo";
399
+ AudioOutput2["Left"] = "left";
400
+ AudioOutput2["Right"] = "right";
401
+ return AudioOutput2;
402
+ })(AudioOutput || {});
403
+ var FilterType = /* @__PURE__ */ ((FilterType2) => {
404
+ FilterType2["Volume"] = "volume";
405
+ FilterType2["AudioOutput"] = "audioOutput";
406
+ FilterType2["LowPass"] = "lowPass";
407
+ FilterType2["Karaoke"] = "karaoke";
408
+ FilterType2["Rotation"] = "rotation";
409
+ FilterType2["Tremolo"] = "tremolo";
410
+ FilterType2["Vibrato"] = "vibrato";
411
+ FilterType2["Custom"] = "custom";
412
+ FilterType2["Timescale"] = "timescale";
413
+ FilterType2["Distortion"] = "distortion";
414
+ FilterType2["Echo"] = "echo";
415
+ FilterType2["Reverb"] = "reverb";
416
+ FilterType2["DSPXLowpass"] = "low-pass";
417
+ FilterType2["DSPXHighpass"] = "high-pass";
418
+ FilterType2["DSPXEcho"] = "echo";
419
+ FilterType2["DSPXNormalization"] = "normalization";
420
+ return FilterType2;
421
+ })(FilterType || {});
422
+
423
+ // src/util/constants.ts
424
+ var HoshimiAgent = `hoshimi/v${package_default.version} (${package_default.repository.url})`;
425
+ var UrlRegex = /^(https?:\/\/)?([a-zA-Z0-9\-_]+\.)+[a-zA-Z]{2,}(\/[^\s]*)?$/;
426
+ var ValidEngines = Object.values(SearchEngines);
427
+ var ValidSources = new Map(
428
+ Object.entries({
429
+ ["youtube" /* Youtube */]: "ytsearch" /* Youtube */,
430
+ ["youtubemusic" /* YoutubeMusic */]: "ytmsearch" /* YoutubeMusic */,
431
+ ["soundcloud" /* Soundcloud */]: "scsearch" /* SoundCloud */,
432
+ ["bandcamp" /* Bandcamp */]: "bcsearch" /* BandCamp */,
433
+ ["twitch" /* Twitch */]: "twsearch" /* Twitch */,
434
+ ["vimeo" /* Vimeo */]: "vmsearch" /* Vimeo */,
435
+ ["mixer" /* Mixer */]: "mxsearch" /* Mixer */,
436
+ ["spotify" /* Spotify */]: "spsearch" /* Spotify */,
437
+ ["deezer" /* Deezer */]: "dzsearch" /* Deezer */,
438
+ ["applemusic" /* AppleMusic */]: "amsearch" /* AppleMusic */,
439
+ ["yandexmusic" /* YandexMusic */]: "ymsearch" /* YandexMusic */,
440
+ ["flowery-tts" /* FloweryTTS */]: "ftts" /* FloweryTTS */,
441
+ ["jiosaavn" /* JioSaavn */]: "jssearch" /* JioSaavn */,
442
+ ["vkmusic" /* VKMusic */]: "vksearch" /* VKMusic */,
443
+ ["tidal" /* Tidal */]: "tdsearch" /* Tidal */,
444
+ ["tts" /* TextToSpeech */]: "speak" /* TextToSpeech */,
445
+ ["pornhub" /* PornHub */]: "phsearch" /* PornHub */
446
+ })
447
+ );
448
+ var AudioOutputData = {
449
+ ["mono" /* Mono */]: {
450
+ leftToLeft: 0.5,
451
+ leftToRight: 0.5,
452
+ rightToLeft: 0.5,
453
+ rightToRight: 0.5
454
+ },
455
+ ["stereo" /* Stereo */]: {
456
+ leftToLeft: 1,
457
+ leftToRight: 0,
458
+ rightToLeft: 0,
459
+ rightToRight: 1
460
+ },
461
+ ["left" /* Left */]: {
462
+ leftToLeft: 1,
463
+ leftToRight: 0,
464
+ rightToLeft: 1,
465
+ rightToRight: 0
466
+ },
467
+ ["right" /* Right */]: {
468
+ leftToLeft: 0,
469
+ leftToRight: 1,
470
+ rightToLeft: 0,
471
+ rightToRight: 1
472
+ }
473
+ };
474
+ var DefaultFilterPreset = {
475
+ Karaoke: { level: 1, monoLevel: 1, filterBand: 220, filterWidth: 100 },
476
+ Vaporwave: { speed: 0.8500000238418579, pitch: 0.800000011920929, rate: 1 },
477
+ Nightcore: { speed: 1.289999523162842, pitch: 1.289999523162842, rate: 0.9365999523162842 },
478
+ Lowpass: { smoothing: 20 },
479
+ Tremolo: { frequency: 4, depth: 0.8 },
480
+ Vibrato: { frequency: 4, depth: 0.8 },
481
+ Distortion: { cosOffset: 0.4, sinOffset: 0.4, tanOffset: 0.4, offset: 0.4, scale: 1.5, cosScale: 1.5, sinScale: 1.5, tanScale: 1.5 },
482
+ DSPXHighPass: { boostFactor: 1, cutoffFrequency: 80 },
483
+ DSPXLowPass: { boostFactor: 1, cutoffFrequency: 80 },
484
+ DSPXNormalization: { adaptive: true, maxAmplitude: 0.1 },
485
+ DSPXEcho: { decay: 0.5, echoLength: 0.5 },
486
+ PluginEcho: { decay: 0.8, delay: 4 },
487
+ PluginReverb: { delays: [0.037, 0.042, 0.048, 0.053], gains: [0.84, 0.83, 0.82, 0.81] }
488
+ };
489
+ var DefaultPlayerFilters = {
490
+ volume: 1,
491
+ lowPass: {
492
+ smoothing: 0
493
+ },
494
+ karaoke: {
495
+ level: 0,
496
+ monoLevel: 0,
497
+ filterBand: 0,
498
+ filterWidth: 0
499
+ },
500
+ timescale: {
501
+ speed: 1,
502
+ pitch: 1,
503
+ rate: 1
504
+ },
505
+ rotation: {
506
+ rotationHz: 0
507
+ },
508
+ tremolo: {
509
+ frequency: 0,
510
+ depth: 0
511
+ },
512
+ vibrato: {
513
+ frequency: 0,
514
+ depth: 0
515
+ },
516
+ pluginFilters: {
517
+ "high-pass": {
518
+ boostFactor: 0,
519
+ cutoffFrequency: 0
520
+ },
521
+ "low-pass": {
522
+ boostFactor: 0,
523
+ cutoffFrequency: 0
524
+ },
525
+ "lavalink-filter-plugin": {
526
+ echo: {
527
+ delay: 0,
528
+ decay: 0
529
+ },
530
+ reverb: {
531
+ delays: [],
532
+ gains: []
533
+ }
534
+ },
535
+ echo: {
536
+ decay: 0,
537
+ delay: 0,
538
+ echoLength: 0
539
+ },
540
+ normalization: {
541
+ adaptive: false,
542
+ maxAmplitude: 0
543
+ }
544
+ },
545
+ equalizer: [],
546
+ distortion: {
547
+ cosOffset: 0,
548
+ sinOffset: 0,
549
+ tanOffset: 0,
550
+ offset: 0,
551
+ scale: 1,
552
+ cosScale: 1,
553
+ sinScale: 1,
554
+ tanScale: 1
555
+ },
556
+ channelMix: AudioOutputData.mono
557
+ };
558
+
559
+ // src/classes/Track.ts
560
+ var escapeRegExp = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
561
+ var Track = class {
562
+ /**
563
+ * The base64 encoded track.
564
+ * @type {string}
565
+ */
566
+ encoded;
567
+ /**
568
+ * The track info.
569
+ * @type {TrackInfo}
570
+ */
571
+ info;
572
+ /**
573
+ * The plugin info of the track.
574
+ * @type {PluginInfo}
575
+ */
576
+ pluginInfo;
577
+ /**
578
+ * The track user data.
579
+ * @type {Record<string, unknown>}
580
+ */
581
+ userData;
582
+ /**
583
+ * The requester of the track.
584
+ * @type {TrackRequester}
585
+ */
586
+ requester;
587
+ /**
588
+ * The constructor for the track.
589
+ * @param {LavalinkTrack} track The track to construct the track from.
590
+ * @param {TrackRequester} requester The requester of the track.
591
+ * @example
592
+ * ```ts
593
+ * const track = new Track({
594
+ * encoded: "base64",
595
+ * info: {
596
+ * title: "Track Title",
597
+ * uri: "https://example.com",
598
+ * duration: 300000,
599
+ * },
600
+ * // the rest of the track info
601
+ * }, requester);
602
+ *
603
+ * console.log(track.encoded); // the track encoded in base64
604
+ * ```
605
+ */
606
+ constructor(track, requester) {
607
+ if (!track) throw new ResolveError("Track is not defined for construction.");
608
+ this.info = track.info;
609
+ this.encoded = track.encoded;
610
+ this.requester = requester ?? {};
611
+ this.pluginInfo = track.pluginInfo;
612
+ this.userData = track.userData ?? {};
613
+ }
614
+ /**
615
+ *
616
+ * Get the hyperlink of the track.
617
+ * @param {boolean} [embedable=true] Whether the hyperlink should be embedable or not.
618
+ * @returns {string} The hyperlink of the track.
619
+ * @example
620
+ * ```ts
621
+ * const track = queue.current;
622
+ * console.log(track.toHyperlink()); // [Track Title](https://example.com)
623
+ * console.log(track.toHyperlink(false)); // [Track Title](<https://example.com>)
624
+ * ```
625
+ */
626
+ toHyperlink(embedable = true) {
627
+ if (embedable) return `[${this.info.title}](${this.info.uri})`;
628
+ return `[${this.info.title}](<${this.info.uri}>)`;
629
+ }
630
+ };
631
+ var UnresolvedTrack = class {
632
+ /**
633
+ * The base64 encoded track.
634
+ * @type {string | undefined}
635
+ */
636
+ encoded;
637
+ /**
638
+ * The track info.
639
+ * @type {UnresolvedTrackInfo}
640
+ */
641
+ info;
642
+ /**
643
+ * The plugin info of the track.
644
+ * @type {Partial<PluginInfo>}
645
+ */
646
+ pluginInfo;
647
+ /**
648
+ * The track user data.
649
+ * @type {Record<string, unknown> | undefined}
650
+ */
651
+ userData;
652
+ /**
653
+ * The requester of the track.
654
+ * @type {TrackRequester | undefined}
655
+ */
656
+ requester;
657
+ /**
658
+ * The constructor for the track.
659
+ * @param {UnresolvedLavalinkTrack} track The track to construct the track from.
660
+ * @param {TrackRequester} requester The requester of the track.
661
+ * @example
662
+ * ```ts
663
+ * const track = new UnresolvedTrack({
664
+ * encoded: "base64",
665
+ * info: {
666
+ * title: "Track Title",
667
+ * },
668
+ * // the rest of the track info
669
+ * }, requester);
670
+ *
671
+ * console.log(track.encoded); // the track encoded in base64
672
+ * ```
673
+ */
674
+ constructor(track, requester) {
675
+ this.info = track.info;
676
+ this.encoded = track.encoded;
677
+ this.requester = requester;
678
+ this.pluginInfo = track.pluginInfo;
679
+ this.userData = track.userData ?? {};
680
+ }
681
+ /**
682
+ * Resolves the track to a playable track.
683
+ * @param {PlayerStructure} player The player to resolve the track for.
684
+ * @returns {Promise<Track>} The resolved track.
685
+ * @throws {ResolveError} If the track cannot be resolved.
686
+ */
687
+ async resolve(player) {
688
+ if (!player) throw new ResolveError("Player is not defined for track resolution.");
689
+ if (isTrack(this)) return new Track(this, this.requester);
690
+ if (!isUnresolvedTrack(this)) throw new ResolveError("Track is not an unresolved track.");
691
+ if (!this.requester) throw new ResolveError("Requester is not defined for track resolution.");
692
+ if (!this.info.title && !this.encoded && !this.info.uri)
693
+ throw new ResolveError("Track is missing required properties for resolution.");
694
+ player.manager.emit("debug" /* Debug */, 3 /* Player */, `[Unresolved] -> [Track] Resolving the track: ${this.info.title}`);
695
+ if (this.encoded) return player.node.decode.single(this.encoded, this.requester);
696
+ if (this.info.uri) {
697
+ const track = await player.search({ query: this.info.uri, requester: this.requester }).then((result) => result.tracks[0]);
698
+ if (!track) throw new ResolveError("Track could not be resolved from URI.");
699
+ player.manager.emit("debug" /* Debug */, 3 /* Player */, `[Unresolved] -> [Track] Resolved the track from URI: ${this.info.uri}`);
700
+ return track;
701
+ }
702
+ const query = [this.info.title, this.info.uri].filter(Boolean).join(" by ");
703
+ const excluded = ["twitch" /* Twitch */, "flowery-tts" /* FloweryTTS */, "mixer" /* Mixer */, "vimeo" /* Vimeo */];
704
+ const engine = this.info.sourceName && !excluded.includes(this.info.sourceName) ? validateEngine(this.info.sourceName) : player.manager.options.defaultSearchEngine;
705
+ player.manager.emit(
706
+ "debug" /* Debug */,
707
+ 3 /* Player */,
708
+ `[Unresolved] -> [Track] Searching for track with query: ${query} using engine: ${engine}`
709
+ );
710
+ return player.search({ query, engine, requester: this.requester }).then((result) => {
711
+ let track = result.tracks[0] ?? null;
712
+ if (this.info.author && !track)
713
+ track = result.tracks.find(
714
+ (t) => [this.info.author ?? "", `${this.info.author} - Topic`].some(
715
+ (name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(t.info.author)
716
+ ) || new RegExp(`^${escapeRegExp(this.info.title)}$`, "i").test(t.info.title)
717
+ ) ?? null;
718
+ if (this.info.length && !track)
719
+ track = result.tracks.find((t) => {
720
+ const length = this.info.length;
721
+ if (!length) return false;
722
+ return t.info.length >= length - 1500 && t.info.length <= length + 1500;
723
+ }) ?? null;
724
+ if (this.info.isrc && !track) track = result.tracks.find((t) => t.info.isrc === this.info.isrc) ?? null;
725
+ if (!track) throw new ResolveError("Track could not be resolved from search query.");
726
+ player.manager.emit(
727
+ "debug" /* Debug */,
728
+ 3 /* Player */,
729
+ `[Unresolved] -> [Track] Resolved the track ${track.info.title} from search query: ${query}`
730
+ );
731
+ return track;
732
+ });
733
+ }
734
+ };
735
+
736
+ // src/classes/queue/adapters/abstract.ts
737
+ var StorageAdapter = class {
738
+ };
739
+
740
+ // src/util/functions/utils.ts
741
+ function validateManagerOptions(options) {
742
+ if (!Array.isArray(options.nodes) || !options.nodes.every(isNode) || !options.nodes.length)
743
+ throw new OptionError("The manager option 'options.nodes' must be a valid array of nodes and atleast one valid node.");
744
+ if (typeof options.sendPayload !== "function")
745
+ throw new OptionError("The manager option 'options.sendPayload' must be a vaid function.");
746
+ if (typeof options.queueOptions !== "undefined" && typeof options.queueOptions.maxPreviousTracks !== "number")
747
+ throw new OptionError("The manager option 'options.queueOptions.maxPreviousTracks' must be a number.");
748
+ if (typeof options.queueOptions !== "undefined" && typeof options.queueOptions.autoplayFn !== "function")
749
+ throw new OptionError("The manager option 'options.queueOptions.autoplayFn' must be a function.");
750
+ if (typeof options.queueOptions?.storage !== "undefined" && !(options.queueOptions.storage instanceof StorageAdapter))
751
+ throw new OptionError("The manager option 'options.queueOptions.storage' must be a valid storage manager.");
752
+ if (typeof options.defaultSearchEngine !== "undefined" && !ValidEngines.includes(options.defaultSearchEngine))
753
+ throw new OptionError("The manager option 'options.defaultSearchEngine' Must be a valid search engine.");
754
+ if (typeof options.client !== "undefined" && typeof options.client !== "object")
755
+ throw new OptionError("The manager option 'options.client' Must be a valid object.");
756
+ if (typeof options.client !== "undefined" && typeof options.client.id !== "undefined" && typeof options.client.id !== "string")
757
+ throw new OptionError("The manager option 'options.client.id' Must be a valid string.");
758
+ if (typeof options.client !== "undefined" && typeof options.client.id !== "undefined" && typeof options.client.username !== "string")
759
+ throw new OptionError("The manager option 'options.client.username' must be a valid string.");
760
+ }
761
+ function validateQuery(search) {
762
+ if (typeof search !== "object") throw new OptionError("The 'query' must be a valid object.");
763
+ if (typeof search.query !== "string") throw new OptionError("The query option 'query.query' must be a valid string.");
764
+ if (typeof search.engine !== "string") throw new OptionError("The query option 'query.engine' must be a valid search engine.");
765
+ search.engine = validateEngine(search.engine);
766
+ if (!ValidEngines.includes(search.engine)) throw new OptionError(`The query option 'query.engine' must be a valid search engine.`);
767
+ const query = search.query.trim();
768
+ const engineKey = Object.values(SearchEngines).find((key) => query.toLowerCase().startsWith(key));
769
+ if (engineKey && query.toLowerCase().startsWith(`${engineKey}:`)) {
770
+ const sliced = query.slice(engineKey.length + 1).trim();
771
+ const isUrl2 = UrlRegex.test(sliced);
772
+ if (isUrl2) return sliced;
773
+ return `${engineKey}:${sliced}`;
774
+ }
775
+ const isUrl = UrlRegex.test(query);
776
+ if (isUrl) return query;
777
+ if (search.engine === "ftts" /* FloweryTTS */) return `${search.engine}://${query}`;
778
+ if (search.engine !== "local" /* Local */) return `${search.engine}:${query}`;
779
+ return query;
780
+ }
781
+ function validatePlayerOptions(options) {
782
+ if (typeof options.guildId !== "string") throw new OptionError("The player option 'options.guildId' must be a string.");
783
+ if (typeof options.voiceId !== "string") throw new OptionError("The player option 'options.voiceId' Must be a string.");
784
+ if (typeof options.textId !== "undefined" && typeof options.textId !== "string")
785
+ throw new OptionError("The player option 'options.textId' Must be a string.");
786
+ if (typeof options.selfDeaf !== "undefined" && typeof options.selfDeaf !== "boolean")
787
+ throw new OptionError("The player option 'options.selfDeaf' Must be a boolean.");
788
+ if (typeof options.selfMute !== "undefined" && typeof options.selfMute !== "boolean")
789
+ throw new OptionError("The player option 'options.selfMute' Mute must be a boolean.");
790
+ if (typeof options.volume !== "undefined" && typeof options.volume !== "number")
791
+ throw new OptionError("The player option 'options.volume' Must be a number.");
792
+ }
793
+ function validatePlayerData(data) {
794
+ if (typeof data === "object" && typeof data.playerOptions === "object" && typeof data.guildId === "string" && Object.keys(data.playerOptions).length > 0) {
795
+ const player = this.nodeManager.manager.getPlayer(data.guildId);
796
+ if (!player) return;
797
+ if (typeof data.playerOptions.voice === "object") player.voice = data.playerOptions.voice;
798
+ if (typeof data.playerOptions.paused === "boolean") {
799
+ player.paused = data.playerOptions.paused;
800
+ player.playing = !data.playerOptions.paused;
801
+ }
802
+ if (typeof data.playerOptions.volume === "number") player.volume = data.playerOptions.volume;
803
+ if (typeof data.playerOptions.position === "number") player.position = data.playerOptions.position;
804
+ }
805
+ }
806
+ function validateNodePlugins(node, plugins) {
807
+ const info = node.info;
808
+ if (!info) throw new NodeError({ id: node.id, message: "Node is not ready yet." });
809
+ if (!info.plugins.length)
810
+ throw new NodeError({
811
+ id: node.id,
812
+ message: "No plugins found in the node."
813
+ });
814
+ const missings = plugins.filter((name) => !info.plugins.some((p) => p.name === name));
815
+ if (missings.length)
816
+ throw new NodeError({
817
+ id: node.id,
818
+ message: `The node does not support the following plugins: ${missings.join(", ")}.`
819
+ });
820
+ }
821
+ function validateEngine(type) {
822
+ const source = ValidEngines.find((engine) => engine === type) ?? ValidSources.get(type);
823
+ if (!source) throw new OptionError(`The engine '${type}' is not a valid engine.`);
824
+ return source;
825
+ }
826
+ function validateTrack(player, track) {
827
+ if (!track) return Promise.resolve(null);
828
+ if (isTrack(track)) return Promise.resolve(new Track(track, track.requester));
829
+ if (!isUnresolvedTrack(track)) throw new ResolveError("The track is not a valid unresolved track.");
830
+ if (!track.resolve || typeof track.resolve !== "function") return new UnresolvedTrack(track, track.requester).resolve(player);
831
+ return track.resolve(player);
832
+ }
833
+ var isTrack = (track) => {
834
+ if (!track) return false;
835
+ return typeof track.encoded === "string" && typeof track.info === "object" && !("resolve" in track && typeof track.resolve === "function");
836
+ };
837
+ function isUnresolvedTrack(track) {
838
+ if (!track) return false;
839
+ return typeof track.encoded === "string" || typeof track.info === "object" && typeof track.info.title === "string" && "resolve" in track && typeof track.resolve === "function";
840
+ }
841
+ function isValid(value) {
842
+ return typeof value !== "undefined" && value !== null;
843
+ }
844
+ function isNode(options) {
845
+ return typeof options.host === "string" && typeof options.port === "number" && typeof options.password === "string" && (typeof options.id === "string" || typeof options.id === "undefined") && (typeof options.secure === "boolean" || typeof options.secure === "undefined") && (typeof options.sessionId === "string" || typeof options.sessionId === "undefined") && (typeof options.retryAmount === "number" || typeof options.retryAmount === "undefined") && (typeof options.retryDelay === "number" || typeof options.retryDelay === "undefined");
846
+ }
847
+
848
+ // src/util/events/player.ts
849
+ async function onEnd() {
850
+ if (this.queue.current && !this.queue.history.find(
851
+ (x) => x.info.identifier === this.queue.current.info.identifier && x.info.title === this.queue.current.info.title
852
+ )) {
853
+ this.queue.history.unshift(this.queue.current);
854
+ if (this.queue.history.length > this.manager.options.queueOptions.maxPreviousTracks)
855
+ this.queue.history.splice(this.manager.options.queueOptions.maxPreviousTracks, this.queue.history.length);
856
+ await this.queue.utils.save();
857
+ this.manager.emit(
858
+ "debug" /* Debug */,
859
+ 3 /* Player */,
860
+ `[Player] -> [Previous] The track: ${this.queue.current.info.title} has been added to the previous track list.`
861
+ );
862
+ }
863
+ if (this.loop === 1 /* Track */ && this.queue.current) this.queue.unshift(this.queue.current);
864
+ if (this.loop === 2 /* Queue */ && this.queue.current) this.queue.add(this.queue.current);
865
+ if (!this.queue.current) this.queue.current = await validateTrack(this, this.queue.shift());
866
+ await this.queue.utils.save();
867
+ return;
868
+ }
869
+ async function queueEnd(track, payload) {
870
+ this.playing = false;
871
+ this.paused = false;
872
+ this.queue.current = null;
873
+ if (typeof this.manager.options.queueOptions.autoplayFn === "function") {
874
+ await this.manager.options.queueOptions.autoplayFn(this, track);
875
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, "[Queue] -> [Autoplay] Autoplay function executed.");
876
+ if (this.queue.size > 0) await onEnd.call(this);
877
+ if (this.queue.current) {
878
+ if (payload.type === "TrackEndEvent" /* TrackEnd */) this.manager.emit("trackEnd" /* TrackEnd */, this, track, payload);
879
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, "[Queue] -> [Autoplay] Track(s) queued from autoplay function.");
880
+ return this.play({ noReplace: true, paused: false });
881
+ }
882
+ }
883
+ if (track) await this.queue.utils.save();
884
+ if (payload.type === "TrackEndEvent" /* TrackEnd */ && payload.reason !== "stopped" /* Stopped */) await this.queue.utils.save();
885
+ this.manager.emit("queueEnd" /* QueueEnd */, this, this.queue);
886
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Queue] The queue has ended.");
887
+ }
888
+ async function trackStart(payload) {
889
+ this.paused = false;
890
+ this.playing = true;
891
+ this.manager.emit("trackStart" /* TrackStart */, this, this.queue.current, payload);
892
+ this.manager.emit(
893
+ "debug" /* Debug */,
894
+ 3 /* Player */,
895
+ `[Player] -> [Start] The track: ${this.queue.current?.info.title ?? "Unknown"} has started playing.`
896
+ );
897
+ }
898
+ async function trackEnd(payload) {
899
+ const current = this.queue.current;
900
+ if (!this.queue.size && this.loop === 3 /* Off */) return queueEnd.call(this, current, payload);
901
+ switch (payload.reason) {
902
+ case "stopped" /* Stopped */:
903
+ break;
904
+ case "replaced" /* Replaced */: {
905
+ this.manager.emit("trackEnd" /* TrackEnd */, this, current, payload);
906
+ return;
907
+ }
908
+ case "loadFailed" /* LoadFailed */:
909
+ case "cleanup" /* Cleanup */: {
910
+ this.playing = false;
911
+ await onEnd.call(this);
912
+ if (!this.queue.size || !this.queue.current) return queueEnd.call(this, current, payload);
913
+ this.manager.emit("trackEnd" /* TrackEnd */, this, current, payload);
914
+ this.manager.emit(
915
+ "debug" /* Debug */,
916
+ 3 /* Player */,
917
+ `[Player] -> [End] The track: ${current?.info.title ?? "Unknown"} has ended.`
918
+ );
919
+ this.queue.current = null;
920
+ return this.play();
921
+ }
922
+ }
923
+ if (current) await this.queue.utils.save();
924
+ await onEnd.call(this);
925
+ this.queue.current = null;
926
+ if (!this.queue.size) {
927
+ this.playing = false;
928
+ return queueEnd.call(this, current, payload);
929
+ }
930
+ this.manager.emit("trackEnd" /* TrackEnd */, this, current, payload);
931
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [End] The track: ${current?.info.title ?? "Uhknown"} has ended.`);
932
+ return this.play();
933
+ }
934
+ async function trackStuck(payload) {
935
+ this.manager.emit("trackStuck" /* TrackStuck */, this, this.queue.current, payload);
936
+ this.manager.emit(
937
+ "debug" /* Debug */,
938
+ 3 /* Player */,
939
+ `[Player] -> [Stuck] The track: ${this.queue.current?.info.title ?? "Unknown"} has stuck.`
940
+ );
941
+ if (!this.queue.size && this.loop === 3 /* Off */) {
942
+ try {
943
+ await this.node.updatePlayer({
944
+ guildId: this.guildId,
945
+ playerOptions: { track: { encoded: null } }
946
+ });
947
+ return;
948
+ } catch {
949
+ return queueEnd.call(this, this.queue.current, payload);
950
+ }
951
+ }
952
+ await onEnd.call(this);
953
+ if (!this.queue.current) return queueEnd.call(this, this.queue.current, payload);
954
+ }
955
+ async function trackError(payload) {
956
+ this.manager.emit("trackError" /* TrackError */, this, this.queue.current, payload);
957
+ this.manager.emit(
958
+ "debug" /* Debug */,
959
+ 3 /* Player */,
960
+ `[Player] -> [Error] The track: ${this.queue.current?.info.title ?? "Unknown"} has error.`
961
+ );
962
+ }
963
+ async function playerUpdate(payload) {
964
+ const player = this.nodeManager.manager.getPlayer(payload.guildId);
965
+ if (!player) return;
966
+ const oldPlayer = player.toJSON();
967
+ player.ping = payload.state.ping;
968
+ player.connected = payload.state.connected;
969
+ player.createdTimestamp = payload.state.time;
970
+ player.position = payload.state.position;
971
+ this.nodeManager.manager.emit("playerUpdate" /* PlayerUpdate */, player, oldPlayer, payload);
972
+ this.nodeManager.manager.emit(
973
+ "debug" /* Debug */,
974
+ 2 /* Node */,
975
+ `[Player] -> [Update] Player updated: ${player.guildId} | Payload: ${JSON.stringify(payload)}`
976
+ );
977
+ }
978
+ async function lyricsFound(track, payload) {
979
+ this.manager.emit("lyricsFound" /* LyricsFound */, this, track, payload);
980
+ this.manager.emit(
981
+ "debug" /* Debug */,
982
+ 3 /* Player */,
983
+ `[Player] -> [Lyrics] The lyrics have been found: ${this.guildId} | Payload: ${JSON.stringify(payload)}`
984
+ );
985
+ }
986
+ async function lyricsLine(track, payload) {
987
+ this.manager.emit("lyricsLine" /* LyricsLine */, this, track, payload);
988
+ this.manager.emit(
989
+ "debug" /* Debug */,
990
+ 3 /* Player */,
991
+ `[Player] -> [Lyrics] The lyrics line has been found: ${this.guildId} | Payload: ${JSON.stringify(payload)}`
992
+ );
993
+ }
994
+ async function lyricsNotFound(track, payload) {
995
+ this.manager.emit("lyricsNotFound" /* LyricsNotFound */, this, track, payload);
996
+ this.manager.emit(
997
+ "debug" /* Debug */,
998
+ 3 /* Player */,
999
+ `[Player] -> [Lyrics] The lyrics were not found: ${this.guildId} | Payload: ${JSON.stringify(payload)}`
1000
+ );
1001
+ }
1002
+ async function socketClosed(payload) {
1003
+ this.manager.emit("socketClosed" /* WebSocketClosed */, this, payload);
1004
+ this.manager.emit(
1005
+ "debug" /* Debug */,
1006
+ 3 /* Player */,
1007
+ `[Player] -> [Socket] The socket has closed: ${this.guildId} | Payload: ${JSON.stringify(payload)}`
1008
+ );
1009
+ }
1010
+
1011
+ // src/util/events/websocket.ts
1012
+ function onOpen(res) {
1013
+ const isResume = res.headers["session-resumed"] === "true";
1014
+ const apiVersion = res.headers["lavalink-api-version"] ?? "unknown";
1015
+ this.retryAmount = this.options.retryAmount;
1016
+ this.nodeManager.manager.emit(
1017
+ "debug" /* Debug */,
1018
+ 2 /* Node */,
1019
+ `[Socket] -> [${this.id}]: Connection handshake complete with ${this.address}. | API Version: ${apiVersion} | Resumed: ${isResume}`
1020
+ );
1021
+ }
1022
+ function onClose(code, reason) {
1023
+ this.nodeManager.manager.emit(
1024
+ "debug" /* Debug */,
1025
+ 2 /* Node */,
1026
+ `[Socket] -> [${this.id}]: Connection closed with ${this.address}. | Code: ${code} | Reason: ${reason}`
1027
+ );
1028
+ this.nodeManager.manager.emit("nodeDisconnect" /* NodeDisconnect */, this);
1029
+ if (code !== 1e3 /* NormalClosure */ || reason !== "Node-Destroy" /* Destroy */) {
1030
+ if (this.nodeManager.nodes.has(this.id)) this.reconnect();
1031
+ }
1032
+ }
1033
+ function onError(error) {
1034
+ if (!error) return;
1035
+ if (this.reconnectTimeout) {
1036
+ clearInterval(this.reconnectTimeout);
1037
+ this.reconnectTimeout = null;
1038
+ }
1039
+ this.nodeManager.manager.emit(
1040
+ "debug" /* Debug */,
1041
+ 2 /* Node */,
1042
+ `[Socket] -> [${this.id}]: Connection error with ${this.address}. | Error: ${error.message}`
1043
+ );
1044
+ this.nodeManager.manager.emit("nodeError" /* NodeError */, this, error);
1045
+ }
1046
+ async function onMessage(message) {
1047
+ if (Array.isArray(message)) message = Buffer.concat(message);
1048
+ else if (message instanceof ArrayBuffer) message = Buffer.from(message);
1049
+ try {
1050
+ const payload = JSON.parse(message.toString());
1051
+ if (!payload.op) return;
1052
+ this.nodeManager.manager.emit("nodeRaw" /* NodeRaw */, this, payload);
1053
+ switch (payload.op) {
1054
+ case "stats" /* Stats */:
1055
+ {
1056
+ this.stats = payload;
1057
+ this.nodeManager.manager.emit(
1058
+ "debug" /* Debug */,
1059
+ 2 /* Node */,
1060
+ `[Socket] <- [${this.id}]: Received stats. | System load: ${this.penalties}`
1061
+ );
1062
+ }
1063
+ break;
1064
+ case "ready" /* Ready */:
1065
+ {
1066
+ if (!payload.sessionId) {
1067
+ this.nodeManager.manager.emit(
1068
+ "debug" /* Debug */,
1069
+ 2 /* Node */,
1070
+ `[Socket] -> [${this.id}]: Session id was not provided. Breaking up the connection...`
1071
+ );
1072
+ this.nodeManager.manager.emit("nodeDestroy" /* NodeDestroy */, this, {
1073
+ code: 1006 /* AbnormalClosure */,
1074
+ reason: "Missing-Session" /* MissingSession */
1075
+ });
1076
+ return this.disconnect();
1077
+ }
1078
+ this.state = 2 /* Connected */;
1079
+ this.sessionId = payload.sessionId;
1080
+ this.session.resuming = payload.resumed;
1081
+ if (payload.resumed) {
1082
+ const players = await this.rest.request({
1083
+ endpoint: `/sessions/${payload.sessionId}/players`
1084
+ }) ?? [];
1085
+ const timeout = this.nodeManager.manager.options.nodeOptions.resumeTimeout;
1086
+ this.nodeManager.manager.emit("nodeResumed" /* NodeResumed */, this, players, payload);
1087
+ this.nodeManager.manager.emit(
1088
+ "debug" /* Debug */,
1089
+ 2 /* Node */,
1090
+ `[Socket] <- [${this.id}]: Resumed session. | Session id: ${payload.sessionId} | Players: ${players.length} | Resumed: ${payload.resumed} | Timeout: ${timeout}ms`
1091
+ );
1092
+ }
1093
+ this.info = await this.rest.request({ endpoint: "/info" });
1094
+ this.nodeManager.manager.emit(
1095
+ "debug" /* Debug */,
1096
+ 2 /* Node */,
1097
+ `[Socket] <- [${this.id}]: Received ready event. | Session id: ${payload.sessionId} | Resumed: ${payload.resumed}`
1098
+ );
1099
+ this.nodeManager.manager.emit("nodeReady" /* NodeReady */, this, this.retryAmount, payload);
1100
+ const isResumable = this.nodeManager.manager.options.nodeOptions.resumable;
1101
+ if (isResumable) {
1102
+ const timeout = this.nodeManager.manager.options.nodeOptions.resumeTimeout;
1103
+ this.nodeManager.manager.emit(
1104
+ "debug" /* Debug */,
1105
+ 2 /* Node */,
1106
+ `[Socket] -> [${this.id}]: Resuming session... | Timeout: ${timeout}ms`
1107
+ );
1108
+ await this.updateSession(isResumable, timeout);
1109
+ }
1110
+ }
1111
+ break;
1112
+ case "event" /* Event */: {
1113
+ const player = this.nodeManager.manager.getPlayer(payload.guildId);
1114
+ if (!player) return;
1115
+ switch (payload.type) {
1116
+ //
1117
+ // Events related to tracks.
1118
+ //
1119
+ case "TrackEndEvent" /* TrackEnd */:
1120
+ await trackEnd.call(player, payload);
1121
+ break;
1122
+ case "TrackStartEvent" /* TrackStart */:
1123
+ await trackStart.call(player, payload);
1124
+ break;
1125
+ case "TrackExceptionEvent" /* TrackException */:
1126
+ await trackError.call(player, payload);
1127
+ break;
1128
+ case "TrackStuckEvent" /* TrackStuck */:
1129
+ await trackStuck.call(player, payload);
1130
+ break;
1131
+ //
1132
+ // Events related to lyrics.
1133
+ //
1134
+ case "LyricsFoundEvent" /* LyricsFound */:
1135
+ await lyricsFound.call(player, player.queue.current, payload);
1136
+ break;
1137
+ case "LyricsNotFoundEvent" /* LyricsNotFound */:
1138
+ await lyricsNotFound.call(player, player.queue.current, payload);
1139
+ break;
1140
+ case "LyricsLineEvent" /* LyricsLine */:
1141
+ await lyricsLine.call(player, player.queue.current, payload);
1142
+ break;
1143
+ //
1144
+ // Events related to the websocket.
1145
+ //
1146
+ case "WebSocketClosedEvent" /* WebsocketClosed */:
1147
+ await socketClosed.call(player, payload);
1148
+ break;
1149
+ }
1150
+ break;
1151
+ }
1152
+ case "playerUpdate" /* PlayerUpdate */: {
1153
+ await playerUpdate.call(this, payload);
1154
+ break;
1155
+ }
1156
+ }
1157
+ this.nodeManager.manager.emit(
1158
+ "debug" /* Debug */,
1159
+ 2 /* Node */,
1160
+ `[Socket] -> [${this.id}]: Received payload: ${JSON.stringify(payload)}`
1161
+ );
1162
+ } catch (error) {
1163
+ this.nodeManager.manager.emit("nodeError" /* NodeError */, this, error);
1164
+ }
1165
+ }
1166
+
1167
+ // src/types/Rest.ts
1168
+ var HttpMethods = /* @__PURE__ */ ((HttpMethods2) => {
1169
+ HttpMethods2["Get"] = "GET";
1170
+ HttpMethods2["Post"] = "POST";
1171
+ HttpMethods2["Put"] = "PUT";
1172
+ HttpMethods2["Patch"] = "PATCH";
1173
+ HttpMethods2["Delete"] = "DELETE";
1174
+ HttpMethods2["Head"] = "HEAD";
1175
+ return HttpMethods2;
1176
+ })(HttpMethods || {});
1177
+ var HttpStatusCodes = /* @__PURE__ */ ((HttpStatusCodes2) => {
1178
+ HttpStatusCodes2[HttpStatusCodes2["OK"] = 200] = "OK";
1179
+ HttpStatusCodes2[HttpStatusCodes2["Created"] = 201] = "Created";
1180
+ HttpStatusCodes2[HttpStatusCodes2["Accepted"] = 202] = "Accepted";
1181
+ HttpStatusCodes2[HttpStatusCodes2["NoContent"] = 204] = "NoContent";
1182
+ HttpStatusCodes2[HttpStatusCodes2["MovedPermanently"] = 301] = "MovedPermanently";
1183
+ HttpStatusCodes2[HttpStatusCodes2["Found"] = 302] = "Found";
1184
+ HttpStatusCodes2[HttpStatusCodes2["NotModified"] = 304] = "NotModified";
1185
+ HttpStatusCodes2[HttpStatusCodes2["BadRequest"] = 400] = "BadRequest";
1186
+ HttpStatusCodes2[HttpStatusCodes2["Unauthorized"] = 401] = "Unauthorized";
1187
+ HttpStatusCodes2[HttpStatusCodes2["Forbidden"] = 403] = "Forbidden";
1188
+ HttpStatusCodes2[HttpStatusCodes2["NotFound"] = 404] = "NotFound";
1189
+ HttpStatusCodes2[HttpStatusCodes2["MethodNotAllowed"] = 405] = "MethodNotAllowed";
1190
+ HttpStatusCodes2[HttpStatusCodes2["RequestTimeout"] = 408] = "RequestTimeout";
1191
+ HttpStatusCodes2[HttpStatusCodes2["Conflict"] = 409] = "Conflict";
1192
+ HttpStatusCodes2[HttpStatusCodes2["Gone"] = 410] = "Gone";
1193
+ HttpStatusCodes2[HttpStatusCodes2["TooManyRequests"] = 429] = "TooManyRequests";
1194
+ HttpStatusCodes2[HttpStatusCodes2["InternalServerError"] = 500] = "InternalServerError";
1195
+ HttpStatusCodes2[HttpStatusCodes2["NotImplemented"] = 501] = "NotImplemented";
1196
+ HttpStatusCodes2[HttpStatusCodes2["BadGateway"] = 502] = "BadGateway";
1197
+ HttpStatusCodes2[HttpStatusCodes2["ServiceUnavailable"] = 503] = "ServiceUnavailable";
1198
+ HttpStatusCodes2[HttpStatusCodes2["GatewayTimeout"] = 504] = "GatewayTimeout";
1199
+ return HttpStatusCodes2;
1200
+ })(HttpStatusCodes || {});
1201
+
1202
+ // src/classes/node/Node.ts
1203
+ var import_ws = require("ws");
1204
+
1205
+ // src/classes/node/Lyrics.ts
1206
+ var LyricsManager = class {
1207
+ /**
1208
+ * The node instance.
1209
+ * @type {NodeStructure}
1210
+ * @readonly
1211
+ */
1212
+ node;
1213
+ /**
1214
+ * Create a new LyricsManager instance.
1215
+ * @param {NodeStructure} node The node instance.
1216
+ * @example
1217
+ * ```ts
1218
+ * const node = manager.nodeManager.get("nodeId");
1219
+ * const lyricsManager = new LyricsManager(node);
1220
+ * ```
1221
+ */
1222
+ constructor(node) {
1223
+ this.node = node;
1224
+ }
1225
+ /**
1226
+ *
1227
+ * Get the current lyrics for the current track.
1228
+ * @param {boolean} skipSource Whether to skip the track source or not.
1229
+ * @returns {Promise<LyricsResult | null>} The lyrics result or null if not found.
1230
+ * @example
1231
+ * ```ts
1232
+ * const player = manager.getPlayer("guildId");
1233
+ * const lyrics = await player.lyricsManager.current();
1234
+ * ```
1235
+ */
1236
+ async current(guildId, skipSource = false) {
1237
+ if (!this.node.sessionId) return null;
1238
+ validateNodePlugins(this.node, ["lavalyrics-plugin" /* LavaLyrics */, "java-lyrics-plugin" /* JavaLyrics */, "lavasrc-plugin" /* LavaSrc */]);
1239
+ return this.node.rest.request({
1240
+ endpoint: `/sessions/${this.node.sessionId}/players/${guildId}/track/lyrics`,
1241
+ params: {
1242
+ skipTrackSource: `${skipSource}`
1243
+ }
1244
+ });
1245
+ }
1246
+ /**
1247
+ *
1248
+ * Get the lyrics for a specific track.
1249
+ * @param {Track} track The track to get the lyrics for.
1250
+ * @param {boolean} skipSource Whether to skip the track source or not.
1251
+ * @returns {Promise<LyricsResult | null>} The lyrics result or null if not found.
1252
+ * @example
1253
+ * ```ts
1254
+ * const node = manager.nodeManager.get("nodeId");
1255
+ * const lyrics = await node.lyricsManager.get(track);
1256
+ * ```
1257
+ */
1258
+ async get(track, skipSource = false) {
1259
+ if (!this.node.sessionId) return null;
1260
+ validateNodePlugins(this.node, ["lavalyrics-plugin" /* LavaLyrics */, "java-lyrics-plugin" /* JavaLyrics */, "lavasrc-plugin" /* LavaSrc */]);
1261
+ return this.node.rest.request({
1262
+ endpoint: "/lyrics",
1263
+ params: {
1264
+ track: track.encoded,
1265
+ skipTrackSource: `${skipSource}`
1266
+ }
1267
+ });
1268
+ }
1269
+ /**
1270
+ *
1271
+ * Subscribe to the lyrics for a specific guild.
1272
+ * @param {string} guildId The guild id to subscribe to.
1273
+ * @param {boolean} skipSource Whether to skip the track source or not.
1274
+ * @returns {Promise<void>} Let's start the sing session!
1275
+ * @example
1276
+ * ```ts
1277
+ * const node = manager.nodeManager.get("nodeId");
1278
+ * await node.lyricsManager.subscribe("guildId");
1279
+ * ```
1280
+ */
1281
+ async subscribe(guildId, skipSource = false) {
1282
+ if (!this.node.sessionId) return;
1283
+ validateNodePlugins(this.node, ["lavalyrics-plugin" /* LavaLyrics */, "java-lyrics-plugin" /* JavaLyrics */, "lavasrc-plugin" /* LavaSrc */]);
1284
+ await this.node.rest.request({
1285
+ endpoint: `/sessions/${this.node.sessionId}/players/${guildId}/lyrics/subscribe`,
1286
+ method: "POST" /* Post */,
1287
+ params: {
1288
+ skipTrackSource: `${skipSource}`
1289
+ }
1290
+ });
1291
+ }
1292
+ /**
1293
+ *
1294
+ * Unsubscribe from the lyrics for a specific guild.
1295
+ * @param {string} guildId The guild id to unsubscribe from.
1296
+ * @returns {Promise<void>} Let's stop the sing session!
1297
+ * @example
1298
+ * ```ts
1299
+ * const node = manager.nodeManager.get("nodeId");
1300
+ * await node.lyricsManager.unsubscribe("guildId");
1301
+ * ```
1302
+ */
1303
+ async unsubscribe(guildId) {
1304
+ if (!this.node.sessionId) return;
1305
+ validateNodePlugins(this.node, ["lavalyrics-plugin" /* LavaLyrics */, "java-lyrics-plugin" /* JavaLyrics */, "lavasrc-plugin" /* LavaSrc */]);
1306
+ await this.node.rest.request({
1307
+ endpoint: `/sessions/${this.node.sessionId}/players/${guildId}/lyrics/subscribe`,
1308
+ method: "DELETE" /* Delete */
1309
+ });
1310
+ }
1311
+ };
1312
+
1313
+ // src/util/collection.ts
1314
+ var Collection = class extends Map {
1315
+ /**
1316
+ * Removes elements from the collection based on a filter function.
1317
+ * @param fn The filter function that determines which elements to remove.
1318
+ * @param thisArg The value to use as `this` when executing the filter function.
1319
+ * @returns The number of elements removed from the collection.
1320
+ * @example
1321
+ * const collection = new Collection<number, string>();
1322
+ * collection.set(1, 'one');
1323
+ * collection.set(2, 'two');
1324
+ * collection.set(3, 'three');
1325
+ * const removedCount = collection.sweep((value, key) => key % 2 === 0);
1326
+ * console.log(removedCount); // Output: 1
1327
+ * console.log(collection.size); // Output: 2
1328
+ */
1329
+ sweep(fn) {
1330
+ if (typeof fn !== "function") throw new TypeError("The filter function must be a function.");
1331
+ const previous = this.size;
1332
+ for (const [key, val] of this) {
1333
+ if (fn(val, key, this)) this.delete(key);
1334
+ }
1335
+ return previous - this.size;
1336
+ }
1337
+ /**
1338
+ * Creates a new array with the results of calling a provided function on every element in the collection.
1339
+ * @param fn The function that produces an element of the new array.
1340
+ * @param thisArg The value to use as `this` when executing the map function.
1341
+ * @returns A new array with the results of calling the provided function on every element in the collection.
1342
+ * @example
1343
+ * const collection = new Collection<number, string>();
1344
+ * collection.set(1, 'one');
1345
+ * collection.set(2, 'two');
1346
+ * collection.set(3, 'three');
1347
+ * const mappedArray = collection.map((value, key) => `${key}: ${value}`);
1348
+ * console.log(mappedArray); // Output: ['1: one', '2: two', '3: three']
1349
+ */
1350
+ map(fn) {
1351
+ if (typeof fn !== "function") throw new TypeError("The filter function must be a function.");
1352
+ const result = [];
1353
+ for (const [key, value] of this.entries()) {
1354
+ result.push(fn(value, key, this));
1355
+ }
1356
+ return result;
1357
+ }
1358
+ /**
1359
+ * Creates a new array with all elements that pass the test implemented by the provided function.
1360
+ * @param fn The function to test each element of the collection.
1361
+ * @param thisArg The value to use as `this` when executing the filter function.
1362
+ * @returns A new array with the elements that pass the test.
1363
+ * @example
1364
+ * const collection = new Collection<number, string>();
1365
+ * collection.set(1, 'one');
1366
+ * collection.set(2, 'two');
1367
+ * collection.set(3, 'three');
1368
+ * const filteredArray = collection.filter((value, key) => key % 2 === 0);
1369
+ * console.log(filteredArray); // Output: ['two']
1370
+ */
1371
+ filter(fn) {
1372
+ if (typeof fn !== "function") throw new TypeError("The filter function must be a function.");
1373
+ const result = [];
1374
+ for (const [key, value] of this.entries()) {
1375
+ if (fn(value, key, this)) result.push(value);
1376
+ }
1377
+ return result;
1378
+ }
1379
+ /**
1380
+ * Returns the value of the first element in the collection that satisfies the provided testing function.
1381
+ * @param fn The function to test each element of the collection.
1382
+ * @returns The value of the first element that passes the test. `undefined` if no element passes the test.
1383
+ * @example
1384
+ * const collection = new Collection<number, number>();
1385
+ * collection.set(1, 1);
1386
+ * collection.set(2, 2);
1387
+ * collection.set(3, 3);
1388
+ * const firstEvenValue = collection.find(value => value % 2 === 0);
1389
+ * console.log(firstEvenValue); // Output: 2
1390
+ */
1391
+ find(fn) {
1392
+ if (typeof fn !== "function") throw new TypeError("The filter function must be a function.");
1393
+ for (const [key, value] of this.entries()) {
1394
+ if (fn(value, key, this)) {
1395
+ return value;
1396
+ }
1397
+ }
1398
+ return void 0;
1399
+ }
1400
+ };
1401
+
1402
+ // src/classes/node/Manager.ts
1403
+ var NodeManager = class {
1404
+ /**
1405
+ * The manager for the node.
1406
+ * @type {Hoshimi}
1407
+ * @readonly
1408
+ */
1409
+ manager;
1410
+ /**
1411
+ * The nodes for the manager.
1412
+ * @type {Collection<string, Node>}
1413
+ * @readonly
1414
+ */
1415
+ nodes = new Collection();
1416
+ /**
1417
+ *
1418
+ * The constructor for the node manager.
1419
+ * @param {Hoshimi} manager The manager for the node.
1420
+ * @example
1421
+ * ```ts
1422
+ * const manager = new Hoshimi();
1423
+ * const nodeManager = new NodeManager(manager);
1424
+ *
1425
+ * console.log(nodeManager.nodes.size); // 0
1426
+ * ```
1427
+ */
1428
+ constructor(manager) {
1429
+ this.manager = manager;
1430
+ }
1431
+ /**
1432
+ *
1433
+ * Delete the node.
1434
+ * @param {string} id The id of the node to delete.
1435
+ * @returns {boolean} If the node was deleted.
1436
+ * @example
1437
+ * ```ts
1438
+ * const node = manager.nodeManager.get("node1");
1439
+ * if (node) manager.nodeManager.delete(node.id); // true if the node was deleted
1440
+ * ```
1441
+ */
1442
+ delete(id) {
1443
+ return this.nodes.delete(id);
1444
+ }
1445
+ /**
1446
+ *
1447
+ * Get the node by id.
1448
+ * @param {string} id The id of the node.
1449
+ * @returns {NodeStructure | undefined} The node or undefined if not found.
1450
+ * @example
1451
+ * ```ts
1452
+ * const node = manager.nodeManager.get("node1");
1453
+ * if (node) console.log(node.id); // node1
1454
+ * ```
1455
+ */
1456
+ get(id) {
1457
+ return this.nodes.get(id);
1458
+ }
1459
+ /**
1460
+ *
1461
+ * Get the least used node.
1462
+ * @returns {NodeStructure} The least used node.
1463
+ * @example
1464
+ * ```ts
1465
+ * const node = manager.nodeManager.getLeastUsed();
1466
+ * if (node) {
1467
+ * console.log(node.id); // node1
1468
+ * console.log(node.penalties); // the penalties of the node
1469
+ * console.log(node.state); // the state of the node
1470
+ * }
1471
+ * ```
1472
+ */
1473
+ getLeastUsed(sortType = "players" /* Players */) {
1474
+ const nodes = this.nodes.filter((node2) => node2.state === 2 /* Connected */);
1475
+ if (!nodes.length) throw new Error("No connected nodes available.");
1476
+ let node;
1477
+ switch (sortType) {
1478
+ case "players" /* Players */: {
1479
+ const sorted = nodes.sort((a, b) => (a.stats?.players ?? 0) - (b.stats?.players ?? 0));
1480
+ node = sorted[0];
1481
+ break;
1482
+ }
1483
+ case "playingPlayers" /* PlayingPlayers */: {
1484
+ const sorted = nodes.sort((a, b) => (a.stats?.playingPlayers ?? 0) - (b.stats?.playingPlayers ?? 0));
1485
+ node = sorted[0];
1486
+ break;
1487
+ }
1488
+ case "systemLoad" /* SystemLoad */: {
1489
+ const sorted = nodes.sort((a, b) => (a.stats?.cpu.systemLoad ?? 0) - (b.stats?.cpu.systemLoad ?? 0));
1490
+ node = sorted[0];
1491
+ break;
1492
+ }
1493
+ case "lavalinkLoad" /* LavalinkLoad */: {
1494
+ const sorted = nodes.sort((a, b) => (a.stats?.cpu.lavalinkLoad ?? 0) - (b.stats?.cpu.lavalinkLoad ?? 0));
1495
+ node = sorted[0];
1496
+ break;
1497
+ }
1498
+ }
1499
+ if (!node) node = nodes.reduce((a, b) => a.penalties < b.penalties ? a : b);
1500
+ return node;
1501
+ }
1502
+ /**
1503
+ *
1504
+ * Create a new node.
1505
+ * @param {NodeOptions} options The options for the node.
1506
+ * @returns {NodeStructure} The created node.
1507
+ * @example
1508
+ * ```ts
1509
+ * const node = manager.nodeManager.create({
1510
+ * host: "localhost",
1511
+ * port: 2333,
1512
+ * password: "password",
1513
+ * secure: false,
1514
+ * });
1515
+ *
1516
+ * console.log(node.id); // localhost:2333
1517
+ */
1518
+ create(options) {
1519
+ options.id ??= `${options.host}:${options.port}`;
1520
+ const oldNode = this.nodes.get(options.id);
1521
+ if (oldNode) return oldNode;
1522
+ const node = Structures.Node(this, options);
1523
+ this.nodes.set(node.id, node);
1524
+ this.manager.emit("nodeCreate" /* NodeCreate */, node);
1525
+ return node;
1526
+ }
1527
+ /**
1528
+ *
1529
+ * Reconnect the nodes.
1530
+ * @returns {void}
1531
+ * @example
1532
+ * ```ts
1533
+ * const node = manager.nodeManager.get("node1");
1534
+ * if (node) node.reconnect();
1535
+ * ```
1536
+ */
1537
+ reconnect() {
1538
+ if (!this.nodes.size) return;
1539
+ for (const node of this.nodes.filter((node2) => node2.state !== 2 /* Connected */)) {
1540
+ node.reconnect();
1541
+ }
1542
+ }
1543
+ /**
1544
+ * Disconnect the nodes.
1545
+ * @returns {void}
1546
+ * @example
1547
+ * ```ts
1548
+ * const node = manager.nodeManager.get("node1");
1549
+ * if (node) node.disconnect();
1550
+ * ```
1551
+ */
1552
+ disconnect() {
1553
+ if (!this.nodes.size) return;
1554
+ for (const node of this.nodes.filter((node2) => node2.state !== 3 /* Disconnected */)) {
1555
+ node.disconnect();
1556
+ }
1557
+ }
1558
+ /**
1559
+ * Connect the nodes.
1560
+ * @returns {void}
1561
+ * @example
1562
+ * ```ts
1563
+ * const node = manager.nodeManager.get("node1");
1564
+ * if (node) node.connect();
1565
+ * ```
1566
+ */
1567
+ connect() {
1568
+ if (!this.nodes.size) return;
1569
+ for (const node of this.nodes.filter((node2) => node2.state !== 2 /* Connected */)) {
1570
+ node.connect();
1571
+ }
1572
+ }
1573
+ /**
1574
+ * Destroy the nodes.
1575
+ * @returns {void}
1576
+ * @example
1577
+ * ```ts
1578
+ * const node = manager.nodeManager.get("node1");
1579
+ * if (node) node.destroy();
1580
+ * ```
1581
+ */
1582
+ destroy() {
1583
+ if (!this.nodes.size) return;
1584
+ for (const node of this.nodes.values()) {
1585
+ node.destroy();
1586
+ }
1587
+ }
1588
+ };
1589
+
1590
+ // src/classes/node/Rest.ts
1591
+ var RestError = class extends Error {
1592
+ /**
1593
+ * The timestamp of the response.
1594
+ * @type {number}
1595
+ */
1596
+ timestamp;
1597
+ /**
1598
+ * The status of the response.
1599
+ * @type {number}
1600
+ */
1601
+ status;
1602
+ /**
1603
+ * The error of the response.
1604
+ * @type {string}
1605
+ */
1606
+ error;
1607
+ /**
1608
+ * The message of the response.
1609
+ * @type {string}
1610
+ */
1611
+ path;
1612
+ /**
1613
+ * The trace of the response.
1614
+ * @type {string}
1615
+ */
1616
+ trace;
1617
+ /**
1618
+ *
1619
+ * Create a new REST error.
1620
+ */
1621
+ constructor({ timestamp, status, error, trace, message, path }) {
1622
+ super(`Rest request failed with response code: ${status}${message ? ` | message: ${message}` : ""}`);
1623
+ this.name = "Hoshimi [RestError]";
1624
+ this.timestamp = timestamp;
1625
+ this.status = status;
1626
+ this.error = error;
1627
+ this.trace = trace;
1628
+ this.message = message;
1629
+ this.path = path;
1630
+ }
1631
+ };
1632
+ var Rest = class {
1633
+ /**
1634
+ * The URL for the REST.
1635
+ * @type {string}
1636
+ */
1637
+ url;
1638
+ /**
1639
+ * The version for the REST.
1640
+ * @type {string}
1641
+ */
1642
+ version = "v4";
1643
+ /**
1644
+ * The timeout for the REST.
1645
+ * @type {number}
1646
+ */
1647
+ restTimeout;
1648
+ /**
1649
+ * The user agent for the REST.
1650
+ * @type {UserAgent}
1651
+ */
1652
+ userAgent;
1653
+ /**
1654
+ * The node for the REST.
1655
+ * @type {Node}
1656
+ */
1657
+ node;
1658
+ /**
1659
+ *
1660
+ * Create a new REST.
1661
+ * @param {NodeStructure} node The node for the REST.
1662
+ * @example
1663
+ * ```ts
1664
+ * const node = new Node({
1665
+ * host: "localhost",
1666
+ * port: 2333,
1667
+ * password: "youshallnotpass",
1668
+ * secure: false,
1669
+ * });
1670
+ *
1671
+ * const rest = new Rest(node);
1672
+ * console.log(rest.restUrl); // http://localhost:2333/v4
1673
+ * ```
1674
+ */
1675
+ constructor(node) {
1676
+ const manager = node.nodeManager.manager;
1677
+ this.url = `${node.options.secure ? "https" : "http"}://${node.options.host}:${node.options.port}/${this.version}`;
1678
+ this.restTimeout = node.options.restTimeout ?? manager.options.restOptions.resumeTimeout ?? 1e4;
1679
+ this.userAgent = manager.options.nodeOptions.userAgent ?? HoshimiAgent;
1680
+ this.node = node;
1681
+ }
1682
+ /**
1683
+ * The REST URL to make requests.
1684
+ * @type {string}
1685
+ */
1686
+ get restUrl() {
1687
+ return this.url;
1688
+ }
1689
+ /**
1690
+ * The session id of the node.
1691
+ * @type {string}
1692
+ */
1693
+ get sessionId() {
1694
+ return this.node.sessionId;
1695
+ }
1696
+ /**
1697
+ *
1698
+ * Make a request to the node.
1699
+ * @param {RestOptions} options The options to make the request.
1700
+ * @returns {Promise<T | null>} The response from the node.
1701
+ */
1702
+ async request(options) {
1703
+ const headers = {
1704
+ ...options.headers,
1705
+ "Content-Type": "application/json",
1706
+ "User-Agent": this.userAgent,
1707
+ Authorization: this.node.options.password
1708
+ };
1709
+ options.method ??= "GET" /* Get */;
1710
+ const url = new URL(`${this.url}${options.endpoint}`);
1711
+ if (options.params) {
1712
+ for (const [key, value] of Object.entries(options.params)) {
1713
+ url.searchParams.append(key, value);
1714
+ }
1715
+ }
1716
+ url.searchParams.append("trace", "true");
1717
+ const abortController = new AbortController();
1718
+ const timeout = setTimeout(() => abortController.abort(), this.restTimeout);
1719
+ const fetchOptions = {
1720
+ headers,
1721
+ method: options.method,
1722
+ signal: abortController.signal
1723
+ };
1724
+ if (!["GET" /* Get */, "HEAD" /* Head */].includes(options.method) && options.body) {
1725
+ if (typeof options.body === "string") fetchOptions.body = options.body;
1726
+ else fetchOptions.body = JSON.stringify(options.body);
1727
+ }
1728
+ this.node.nodeManager.manager.emit(
1729
+ "debug" /* Debug */,
1730
+ 4 /* Rest */,
1731
+ `[Rest] -> [${this.node.id} : ${options.method}]: Url: ${this.restUrl} | Endpoint: ${options.endpoint} | Params: ${url.search} | Body: ${options.body ? JSON.stringify(options.body) : "None"} | Headers: ${JSON.stringify(headers)}`
1732
+ );
1733
+ const response = await fetch(url.toString(), fetchOptions).finally(() => clearTimeout(timeout));
1734
+ if (!response.ok) {
1735
+ const restError = await response.json().catch(() => null);
1736
+ throw new RestError(
1737
+ restError ?? {
1738
+ timestamp: Date.now(),
1739
+ status: response.status,
1740
+ error: "Unknown Error",
1741
+ message: "Unexpected error response from Lavalink server",
1742
+ path: options.endpoint
1743
+ }
1744
+ );
1745
+ }
1746
+ if (response.status === 204 /* NoContent */) return null;
1747
+ return response.json().catch(() => null);
1748
+ }
1749
+ /**
1750
+ *
1751
+ * Update the player data.
1752
+ * @param {Partial<UpdatePlayerInfo>} data The player data to update.
1753
+ * @returns {LavalinkPlayer | null} The updated player data.
1754
+ * @example
1755
+ * ```ts
1756
+ * const player = await node.rest.updatePlayer({
1757
+ * guildId: "guildId",
1758
+ * noReplace: true,
1759
+ * playerOptions: {
1760
+ * paused: false,
1761
+ * track: { encoded: "encoded track" },
1762
+ * },
1763
+ * });
1764
+ *
1765
+ * console.log(player); // The updated lavalink player data
1766
+ * ```
1767
+ */
1768
+ updatePlayer(data) {
1769
+ if (!this.sessionId) return Promise.resolve(null);
1770
+ this.node.nodeManager.manager.emit(
1771
+ "debug" /* Debug */,
1772
+ 4 /* Rest */,
1773
+ `[Rest] -> [${this.node.id}]: Updated player data for guild: ${data.guildId} | Payload: ${JSON.stringify(data)}`
1774
+ );
1775
+ validatePlayerData.call(this.node, data);
1776
+ return this.request({
1777
+ method: "PATCH" /* Patch */,
1778
+ endpoint: `/sessions/${this.sessionId}/players/${data.guildId}`,
1779
+ body: { ...data.playerOptions },
1780
+ params: { noReplace: `${data.noReplace ?? false}` }
1781
+ });
1782
+ }
1783
+ /**
1784
+ *
1785
+ * Stop the track in player for the guild.
1786
+ * @param {string} guildId the guild id to stop the player
1787
+ * @returns {Promise<LavalinkPlayer | null>} The updated player data.
1788
+ * @example
1789
+ * ```ts
1790
+ * const player = await node.rest.stopPlayer("guildId");
1791
+ * if (player) console.log(player); // The lavalink player
1792
+ * ```
1793
+ */
1794
+ stopPlayer(guildId) {
1795
+ if (!this.sessionId) return Promise.resolve(null);
1796
+ this.node.nodeManager.manager.emit(
1797
+ "debug" /* Debug */,
1798
+ 4 /* Rest */,
1799
+ `[Rest] -> [${this.node.id}]: Stopped player for guild: ${guildId}`
1800
+ );
1801
+ return this.updatePlayer({
1802
+ guildId,
1803
+ playerOptions: {
1804
+ paused: false,
1805
+ track: { encoded: null }
1806
+ }
1807
+ });
1808
+ }
1809
+ /**
1810
+ *
1811
+ * Destroy the player for the guild.
1812
+ * @param {string} guildId The guild id to destroy the player.
1813
+ * @returns {Promise<void>} The updated player data.
1814
+ * @example
1815
+ * ```ts
1816
+ * await node.rest.destroyPlayer("guildId");
1817
+ * ```
1818
+ * @example
1819
+ */
1820
+ async destroyPlayer(guildId) {
1821
+ if (!this.sessionId) return;
1822
+ this.node.nodeManager.manager.emit(
1823
+ "debug" /* Debug */,
1824
+ 4 /* Rest */,
1825
+ `[Rest] -> [${this.node.id}]: Destroyed player for guild: ${guildId}`
1826
+ );
1827
+ await this.request({
1828
+ method: "DELETE" /* Delete */,
1829
+ endpoint: `/sessions/${this.sessionId}/players/${guildId}`
1830
+ });
1831
+ }
1832
+ /**
1833
+ *
1834
+ * Update the session for the node
1835
+ * @param {boolean} resuming Enable resuming for the session.
1836
+ * @param {number | null} timeout The timeout for the session.
1837
+ * @returns {Promise<LavalinkSession | null>} The updated session data.
1838
+ * @example
1839
+ * ```ts
1840
+ * const session = await node.rest.updateSession(true, 10000);
1841
+ * if (session) console.log(session); // The lavalink session data
1842
+ * ```
1843
+ */
1844
+ updateSession(resuming, timeout = null) {
1845
+ if (!this.sessionId) return Promise.resolve(null);
1846
+ this.node.nodeManager.manager.emit(
1847
+ "debug" /* Debug */,
1848
+ 4 /* Rest */,
1849
+ `[Rest] -> [${this.node.id}]: Updated session for resumed: ${resuming} | Timeout: ${timeout ?? "None"}`
1850
+ );
1851
+ return this.request({
1852
+ method: "PATCH" /* Patch */,
1853
+ endpoint: `/sessions/${this.sessionId}`,
1854
+ body: { resuming, timeout }
1855
+ });
1856
+ }
1857
+ };
1858
+
1859
+ // src/classes/player/filters/DSPXPlugin.ts
1860
+ var DSPXPluginFilter = class {
1861
+ /**
1862
+ * The filter manager instance.
1863
+ * @type {FilterManagerStructure}
1864
+ * @readonly
1865
+ */
1866
+ manager;
1867
+ /**
1868
+ * Create a new DSPXPluginFilter instance.
1869
+ * @param {FilterManagerStructure} manager The filter manager instance.
1870
+ */
1871
+ constructor(manager) {
1872
+ this.manager = manager;
1873
+ }
1874
+ /**
1875
+ *
1876
+ * Set the low-pass filter with the given settings.
1877
+ * @param {FilterPluginPassSettings} [settings=DefaultFilter.DSPXLowPass] The settings for the low-pass filter.
1878
+ * @returns {Promise<boolean>} Whether the filter is now active.
1879
+ */
1880
+ async setLowPass(settings = DefaultFilterPreset.DSPXLowPass) {
1881
+ validateNodePlugins(this.manager.player.node, ["lavadspx-plugin" /* LavaDspx */]);
1882
+ if (!this.manager.player.node.info?.filters?.includes("low-pass" /* DSPXLowpass */))
1883
+ throw new PlayerError("Node filters does not include the 'low-pass' filter. (Or the node doesn't have it enabled)");
1884
+ if (!this.manager.data) this.manager.data = {};
1885
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
1886
+ if (!this.manager.data.pluginFilters["low-pass"]) this.manager.data.pluginFilters["low-pass"] = {};
1887
+ if (this.manager.filters.lavalinkLavaDspxPlugin.lowPass) {
1888
+ delete this.manager.data.pluginFilters["low-pass"];
1889
+ } else {
1890
+ this.manager.data.pluginFilters["low-pass"] = { ...settings };
1891
+ }
1892
+ this.manager.filters.lavalinkLavaDspxPlugin.lowPass = !this.manager.filters.lavalinkLavaDspxPlugin.lowPass;
1893
+ await this.manager.apply();
1894
+ return this.manager.filters.lavalinkLavaDspxPlugin.lowPass;
1895
+ }
1896
+ /**
1897
+ *
1898
+ * Set the high-pass filter with the given settings.
1899
+ * @param {FilterPluginPassSettings} [settings=DefaultFilter.DSPXHighPass] The settings for the high-pass filter.
1900
+ * @returns {Promise<boolean>} Whether the filter is now active.
1901
+ */
1902
+ async setHighPass(settings = DefaultFilterPreset.DSPXHighPass) {
1903
+ validateNodePlugins(this.manager.player.node, ["lavadspx-plugin" /* LavaDspx */]);
1904
+ if (this.manager.player.node.info && !this.manager.player.node.info?.filters?.includes("high-pass" /* DSPXHighpass */))
1905
+ throw new PlayerError("Node filters does not include the 'high-pass' filter. (Or the node doesn't have it enabled)");
1906
+ if (!this.manager.data) this.manager.data = {};
1907
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
1908
+ if (!this.manager.data.pluginFilters["high-pass"]) this.manager.data.pluginFilters["high-pass"] = {};
1909
+ if (this.manager.filters.lavalinkLavaDspxPlugin.highPass) {
1910
+ delete this.manager.data.pluginFilters["high-pass"];
1911
+ } else {
1912
+ this.manager.data.pluginFilters["high-pass"] = { ...settings };
1913
+ }
1914
+ this.manager.filters.lavalinkLavaDspxPlugin.highPass = !this.manager.filters.lavalinkLavaDspxPlugin.highPass;
1915
+ await this.manager.apply();
1916
+ return this.manager.filters.lavalinkLavaDspxPlugin.highPass;
1917
+ }
1918
+ /**
1919
+ *
1920
+ * Set the normalization filter with the given settings.
1921
+ * @param {NormalizationSettings} [settings=DefaultFilter.DSPXNormalization] The settings for the normalization filter.
1922
+ * @returns {Promise<boolean>} Whether the filter is now active.
1923
+ */
1924
+ async setNormalization(settings = DefaultFilterPreset.DSPXNormalization) {
1925
+ validateNodePlugins(this.manager.player.node, ["lavadspx-plugin" /* LavaDspx */]);
1926
+ if (!this.manager.player.node.info?.filters?.includes("normalization" /* DSPXNormalization */))
1927
+ throw new PlayerError("Node filters does not include the 'normalization' filter. (Or the node doesn't have it enabled)");
1928
+ if (!this.manager.data) this.manager.data = {};
1929
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
1930
+ if (!this.manager.data.pluginFilters.normalization) this.manager.data.pluginFilters.normalization = {};
1931
+ if (this.manager.filters.lavalinkLavaDspxPlugin.normalization) {
1932
+ delete this.manager.data.pluginFilters.normalization;
1933
+ } else {
1934
+ this.manager.data.pluginFilters.normalization = { ...settings };
1935
+ }
1936
+ this.manager.filters.lavalinkLavaDspxPlugin.normalization = !this.manager.filters.lavalinkLavaDspxPlugin.normalization;
1937
+ await this.manager.apply();
1938
+ return this.manager.filters.lavalinkLavaDspxPlugin.normalization;
1939
+ }
1940
+ /**
1941
+ *
1942
+ * Set the echo filter with the given settings.
1943
+ * @param {EchoSettings} [settings=DefaultFilter.DSPXEcho] The settings for the echo filter.
1944
+ * @returns {Promise<boolean>} Whether the filter is now active.
1945
+ */
1946
+ async setEcho(settings = DefaultFilterPreset.DSPXEcho) {
1947
+ validateNodePlugins(this.manager.player.node, ["lavadspx-plugin" /* LavaDspx */]);
1948
+ if (!this.manager.player.node.info?.filters?.includes("echo" /* DSPXEcho */))
1949
+ throw new PlayerError("Node filters does not include the 'echo' filter. (Or the node doesn't have it enabled)");
1950
+ if (!this.manager.data) this.manager.data = {};
1951
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
1952
+ if (!this.manager.data.pluginFilters.echo) this.manager.data.pluginFilters.echo = {};
1953
+ if (this.manager.filters.lavalinkLavaDspxPlugin.echo) {
1954
+ delete this.manager.data.pluginFilters.echo;
1955
+ } else {
1956
+ this.manager.data.pluginFilters.echo = { ...settings };
1957
+ }
1958
+ this.manager.filters.lavalinkLavaDspxPlugin.echo = !this.manager.filters.lavalinkLavaDspxPlugin.echo;
1959
+ await this.manager.apply();
1960
+ return this.manager.filters.lavalinkLavaDspxPlugin.echo;
1961
+ }
1962
+ };
1963
+
1964
+ // src/classes/player/filters/LavalinkPlugin.ts
1965
+ var LavalinkPluginFilter = class {
1966
+ /**
1967
+ * The filter manager instance.
1968
+ * @type {FilterManagerStructure}
1969
+ * @private
1970
+ * @readonly
1971
+ */
1972
+ manager;
1973
+ /**
1974
+ * Creates an instance of LavalinkPluginFilter.
1975
+ * @param {FilterManagerStructure} filters - The filter manager instance.
1976
+ */
1977
+ constructor(filters) {
1978
+ this.manager = filters;
1979
+ }
1980
+ /**
1981
+ *
1982
+ * Set the echo filter with the given settings.
1983
+ * @param {Omit<EchoSettings, "echoLength">} [settings=DefaultFilter.PluginEcho] The settings for the echo filter.
1984
+ * @returns {Promise<boolean>} Whether the filter is now active.
1985
+ */
1986
+ async setEcho(settings = DefaultFilterPreset.PluginEcho) {
1987
+ validateNodePlugins(this.manager.player.node, ["lavalink-filter-plugin" /* FilterPlugin */]);
1988
+ if (!this.manager.player.node.info?.filters?.includes("echo" /* Echo */))
1989
+ throw new PlayerError("Node filters does not include the 'echo' filter. (Or the node doesn't have it enabled)");
1990
+ if (!this.manager.data) this.manager.data = {};
1991
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
1992
+ if (!this.manager.data.pluginFilters["lavalink-filter-plugin"])
1993
+ this.manager.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
1994
+ if (!this.manager.data.pluginFilters["lavalink-filter-plugin"].echo)
1995
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].echo = { decay: 0, delay: 0 };
1996
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].echo.delay = this.manager.filters.lavalinkFilterPlugin.echo ? 0 : settings.delay;
1997
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].echo.decay = this.manager.filters.lavalinkFilterPlugin.echo ? 0 : settings.decay;
1998
+ this.manager.filters.lavalinkFilterPlugin.echo = !this.manager.filters.lavalinkFilterPlugin.echo;
1999
+ await this.manager.apply();
2000
+ return this.manager.filters.lavalinkFilterPlugin.echo;
2001
+ }
2002
+ /**
2003
+ *
2004
+ * Set the reverb filter with the given settings.
2005
+ * @param {Partial<LavalinkFilterPluginReverbSettings>} [settings=DefaultFilter.PluginReverb] The settings for the reverb filter.
2006
+ * @returns {Promise<boolean>} Whether the filter is now active.
2007
+ */
2008
+ async setReverb(settings = DefaultFilterPreset.PluginReverb) {
2009
+ validateNodePlugins(this.manager.player.node, ["lavalink-filter-plugin" /* FilterPlugin */]);
2010
+ if (!this.manager.player.node.info?.filters?.includes("reverb" /* Reverb */))
2011
+ throw new PlayerError("Node filters does not include the 'reverb' filter. (Or the node doesn't have it enabled)");
2012
+ if (!this.manager.data) this.manager.data = {};
2013
+ if (!this.manager.data.pluginFilters) this.manager.data.pluginFilters = {};
2014
+ if (!this.manager.data.pluginFilters["lavalink-filter-plugin"])
2015
+ this.manager.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
2016
+ if (!this.manager.data.pluginFilters["lavalink-filter-plugin"].reverb)
2017
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].reverb = { delays: [], gains: [] };
2018
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].reverb.delays = this.manager.filters.lavalinkFilterPlugin.reverb ? [] : settings.delays;
2019
+ this.manager.data.pluginFilters["lavalink-filter-plugin"].reverb.gains = this.manager.filters.lavalinkFilterPlugin.reverb ? [] : settings.gains;
2020
+ this.manager.filters.lavalinkFilterPlugin.reverb = !this.manager.filters.lavalinkFilterPlugin.reverb;
2021
+ await this.manager.apply();
2022
+ return this.manager.filters.lavalinkFilterPlugin.reverb;
2023
+ }
2024
+ };
2025
+
2026
+ // src/classes/player/filters/Manager.ts
2027
+ var FilterManager = class {
2028
+ /**
2029
+ * The player this filter manager belongs to.
2030
+ * @type {PlayerStructure}
2031
+ * @public
2032
+ * @readonly
2033
+ */
2034
+ player;
2035
+ /**
2036
+ * The bands applied to the player.
2037
+ * @type {EQBandSettings[]}
2038
+ * @readonly
2039
+ */
2040
+ bands = [];
2041
+ /**
2042
+ * The current filter settings applied to the player.
2043
+ * @type {FilterSettings}
2044
+ * @public
2045
+ */
2046
+ data = { ...DefaultPlayerFilters };
2047
+ /**
2048
+ * The enabled filters for the player.
2049
+ * @type {EnabledPlayerFilters}
2050
+ */
2051
+ filters = {
2052
+ audioOutput: "stereo" /* Stereo */,
2053
+ volume: false,
2054
+ vaporwave: false,
2055
+ custom: false,
2056
+ nightcore: false,
2057
+ rotation: false,
2058
+ karaoke: false,
2059
+ tremolo: false,
2060
+ vibrato: false,
2061
+ lowPass: false,
2062
+ distortion: false,
2063
+ timescale: false,
2064
+ lavalinkFilterPlugin: {
2065
+ echo: false,
2066
+ reverb: false
2067
+ },
2068
+ lavalinkLavaDspxPlugin: {
2069
+ lowPass: false,
2070
+ highPass: false,
2071
+ normalization: false,
2072
+ echo: false
2073
+ }
2074
+ };
2075
+ /**
2076
+ * The lavalink plugin filters manager.
2077
+ * @type {LavalinkPluginFilter}
2078
+ * @readonly
2079
+ */
2080
+ plugin;
2081
+ /**
2082
+ * The DSPX plugin filters manager.
2083
+ * @type {DSPXPluginFilter}
2084
+ * @readonly
2085
+ */
2086
+ dspx;
2087
+ /**
2088
+ *
2089
+ * Creates a new filter manager.
2090
+ * @param {PlayerStructure} player The player this filter manager belongs to.
2091
+ */
2092
+ constructor(player) {
2093
+ this.player = player;
2094
+ this.plugin = new LavalinkPluginFilter(this);
2095
+ this.dspx = new DSPXPluginFilter(this);
2096
+ }
2097
+ /**
2098
+ *
2099
+ * Checks if a custom filter is active.
2100
+ * @returns {boolean} True if a custom filter is active, false otherwise.
2101
+ */
2102
+ isCustom() {
2103
+ this.filters.custom = !this.filters.nightcore && !this.filters.vaporwave && Object.values(this.data.timescale ?? {}).some((d) => d !== 1);
2104
+ return this.filters.custom;
2105
+ }
2106
+ /**
2107
+ * Resets all filters to their default values.
2108
+ * @returns {Promise<void>} A promise that resolves when the filters have been reset.
2109
+ */
2110
+ async reset() {
2111
+ this.filters = {
2112
+ audioOutput: "stereo" /* Stereo */,
2113
+ volume: false,
2114
+ vaporwave: false,
2115
+ custom: false,
2116
+ nightcore: false,
2117
+ rotation: false,
2118
+ karaoke: false,
2119
+ tremolo: false,
2120
+ vibrato: false,
2121
+ lowPass: false,
2122
+ distortion: false,
2123
+ timescale: false,
2124
+ lavalinkFilterPlugin: {
2125
+ echo: false,
2126
+ reverb: false
2127
+ },
2128
+ lavalinkLavaDspxPlugin: {
2129
+ lowPass: false,
2130
+ highPass: false,
2131
+ normalization: false,
2132
+ echo: false
2133
+ }
2134
+ };
2135
+ for (const [key, value] of Object.entries(DefaultPlayerFilters)) {
2136
+ this.data[key] = value;
2137
+ }
2138
+ await this.apply();
2139
+ }
2140
+ /**
2141
+ *
2142
+ * Applies the current filters to the player.
2143
+ * @returns {Promise<void>} A promise that resolves when the filters have been applied.
2144
+ */
2145
+ async apply() {
2146
+ if (!this.player.node.sessionId) return;
2147
+ this.check();
2148
+ this.isCustom();
2149
+ const filters = { ...this.data };
2150
+ if (!this.filters.volume) delete filters.volume;
2151
+ if (!this.filters.tremolo) delete filters.tremolo;
2152
+ if (!this.filters.vibrato) delete filters.vibrato;
2153
+ if (!this.filters.lavalinkFilterPlugin.echo) delete filters.pluginFilters?.["lavalink-filter-plugin"]?.echo;
2154
+ if (!this.filters.lavalinkFilterPlugin.reverb) delete filters.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
2155
+ if (!this.filters.lavalinkLavaDspxPlugin.echo) delete filters.pluginFilters?.echo;
2156
+ if (!this.filters.lavalinkLavaDspxPlugin.normalization) delete filters.pluginFilters?.normalization;
2157
+ if (!this.filters.lavalinkLavaDspxPlugin.highPass) delete filters.pluginFilters?.["high-pass"];
2158
+ if (!this.filters.lavalinkLavaDspxPlugin.lowPass) delete filters.pluginFilters?.["low-pass"];
2159
+ if (filters.pluginFilters?.["lavalink-filter-plugin"] && !Object.values(filters.pluginFilters["lavalink-filter-plugin"]).length)
2160
+ delete filters.pluginFilters["lavalink-filter-plugin"];
2161
+ if (filters.pluginFilters && Object.values(filters.pluginFilters).length === 0) delete filters.pluginFilters;
2162
+ if (this.filters.audioOutput === "stereo" /* Stereo */) delete filters.channelMix;
2163
+ if (!this.filters.lowPass) delete filters.lowPass;
2164
+ if (!this.filters.karaoke) delete filters.karaoke;
2165
+ if (!this.filters.rotation) delete filters.rotation;
2166
+ if (!this.filters.distortion) delete filters.distortion;
2167
+ if (!this.filters.timescale) delete filters.timescale;
2168
+ if (this.data.timescale && Object.values(this.data.timescale).every((v) => v === 1)) delete filters.timescale;
2169
+ filters.equalizer = [...this.bands];
2170
+ if (!filters.equalizer.length) delete filters.equalizer;
2171
+ for (const key in filters) {
2172
+ if (!this.player.node.info?.filters?.includes(key)) delete filters[key];
2173
+ }
2174
+ await this.player.updatePlayer({ playerOptions: { filters } });
2175
+ }
2176
+ /**
2177
+ * Checks if the current filters are active.
2178
+ * @param {TimescaleSettings} timescale The timescale settings to check against.
2179
+ * @returns {void} Nothing!
2180
+ */
2181
+ check(timescale) {
2182
+ this.filters.rotation = this.data.rotation?.rotationHz !== 0;
2183
+ this.filters.vibrato = this.data.vibrato?.frequency !== 0 || this.data.vibrato?.depth !== 0;
2184
+ this.filters.tremolo = this.data.tremolo?.frequency !== 0 || this.data.tremolo?.depth !== 0;
2185
+ const lavalinkPluginFilters = this.data.pluginFilters?.["lavalink-filter-plugin"] ?? {};
2186
+ this.filters.lavalinkFilterPlugin.echo = lavalinkPluginFilters.echo?.decay !== 0 || lavalinkPluginFilters.echo?.delay !== 0;
2187
+ this.filters.lavalinkFilterPlugin.reverb = lavalinkPluginFilters.reverb?.delays?.length !== 0 || lavalinkPluginFilters.reverb?.gains?.length !== 0;
2188
+ this.filters.lavalinkLavaDspxPlugin.highPass = Object.values(this.data.pluginFilters?.["high-pass"] ?? {}).length > 0;
2189
+ this.filters.lavalinkLavaDspxPlugin.lowPass = Object.values(this.data.pluginFilters?.["low-pass"] ?? {}).length > 0;
2190
+ this.filters.lavalinkLavaDspxPlugin.normalization = Object.values(this.data.pluginFilters?.normalization ?? {}).length > 0;
2191
+ this.filters.lavalinkLavaDspxPlugin.echo = Object.values(this.data.pluginFilters?.echo ?? {}).length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined";
2192
+ this.filters.lowPass = this.data.lowPass?.smoothing !== 0;
2193
+ this.filters.karaoke = Object.values(this.data.karaoke ?? {}).some((v) => v !== 0);
2194
+ this.filters.distortion = Object.values(this.data.distortion ?? {}).some((v) => v !== 0 && v !== 1);
2195
+ this.filters.timescale = Object.values(this.data.timescale ?? {}).some((v) => v !== 1);
2196
+ if ((this.filters.nightcore || this.filters.vaporwave) && timescale) {
2197
+ if (timescale.pitch !== this.data.timescale?.pitch || timescale.rate !== this.data.timescale?.rate || timescale.speed !== this.data.timescale?.speed) {
2198
+ this.filters.custom = Object.values(this.data.timescale ?? {}).some((v) => v !== 1);
2199
+ this.filters.nightcore = false;
2200
+ this.filters.vaporwave = false;
2201
+ }
2202
+ }
2203
+ }
2204
+ /**
2205
+ *
2206
+ * Checks if a specific filter is active.
2207
+ * @param {FilterType} filter The filter type to check.
2208
+ * @returns {boolean} True if the filter is active, false otherwise.
2209
+ */
2210
+ has(filter) {
2211
+ const dspx = this.filters.lavalinkLavaDspxPlugin[filter];
2212
+ if (isValid(dspx)) return dspx;
2213
+ const plugin = this.filters.lavalinkFilterPlugin[filter];
2214
+ if (isValid(plugin)) return plugin;
2215
+ const kind = this.filters[filter];
2216
+ if (typeof kind === "boolean") return kind;
2217
+ if (typeof kind === "string") return kind !== "stereo" /* Stereo */;
2218
+ return false;
2219
+ }
2220
+ /**
2221
+ *
2222
+ * Sets the volume for the player.
2223
+ * @param {number} volume The volume level to set (between 0 and 5).
2224
+ * @returns {Promise<boolean>} A promise that resolves to true if the volume was changed, false otherwise.
2225
+ */
2226
+ async setVolume(volume) {
2227
+ if (typeof volume !== "number" || Number.isNaN(volume) || volume < 0 || volume > 5)
2228
+ throw new PlayerError("Volume must be a number between 0 and 5.");
2229
+ this.data = { volume };
2230
+ this.filters.volume = volume !== 1;
2231
+ await this.apply();
2232
+ return this.filters.volume;
2233
+ }
2234
+ /**
2235
+ * Sets the audio output for the player.
2236
+ * @param {AudioOutput} output The audio output to set.
2237
+ * @returns {Promise<AudioOutput>} A promise that resolves to the set audio output.
2238
+ */
2239
+ async setAudioOutput(output) {
2240
+ const outputs = Object.values(AudioOutput);
2241
+ if (!outputs.includes(output)) throw new PlayerError(`Audio output must be one of the following: ${outputs.join(", ")}.`);
2242
+ this.filters.audioOutput = output;
2243
+ this.data.channelMix = AudioOutputData[output];
2244
+ await this.apply();
2245
+ return this.filters.audioOutput;
2246
+ }
2247
+ /**
2248
+ *
2249
+ * Sets the speed for the player.
2250
+ * @param {number} speed The speed to set (default is 1).
2251
+ * @returns {Promise<boolean>} A promise that resolves to true if a custom filter is active, false otherwise.
2252
+ */
2253
+ async setSpeed(speed = 1) {
2254
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2255
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2256
+ if (this.filters.nightcore || this.filters.vaporwave) {
2257
+ this.data.timescale = {
2258
+ speed: 1,
2259
+ pitch: 1,
2260
+ rate: 1
2261
+ };
2262
+ this.filters.nightcore = false;
2263
+ this.filters.vaporwave = false;
2264
+ }
2265
+ this.data.timescale.speed = speed;
2266
+ await this.apply();
2267
+ return this.filters.custom;
2268
+ }
2269
+ /**
2270
+ *
2271
+ * Sets the rate for the player.
2272
+ * @param {number} rate The rate to set (default is 1).
2273
+ * @returns {Promise<boolean>} A promise that resolves to true if a custom filter is active, false otherwise.
2274
+ */
2275
+ async setRate(rate = 1) {
2276
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2277
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2278
+ if (this.filters.nightcore || this.filters.vaporwave) {
2279
+ this.data.timescale = {
2280
+ speed: 1,
2281
+ pitch: 1,
2282
+ rate: 1
2283
+ };
2284
+ this.filters.nightcore = false;
2285
+ this.filters.vaporwave = false;
2286
+ }
2287
+ this.data.timescale.rate = rate;
2288
+ await this.apply();
2289
+ return this.filters.custom;
2290
+ }
2291
+ /**
2292
+ *
2293
+ * Sets the pitch for the player.
2294
+ * @param {number} pitch The pitch
2295
+ * @returns {Promise<boolean>} A promise that resolves to true if a custom filter is active, false otherwise.
2296
+ */
2297
+ async setPitch(pitch = 1) {
2298
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2299
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2300
+ if (this.filters.nightcore || this.filters.vaporwave) {
2301
+ this.data.timescale = {
2302
+ speed: 1,
2303
+ pitch: 1,
2304
+ rate: 1
2305
+ };
2306
+ this.filters.nightcore = false;
2307
+ this.filters.vaporwave = false;
2308
+ }
2309
+ this.data.timescale.pitch = pitch;
2310
+ await this.apply();
2311
+ return this.filters.custom;
2312
+ }
2313
+ /**
2314
+ *
2315
+ * Sets the EQ bands for the player.
2316
+ * @param {RestOrArray<EQBandSettings>} bands The EQ band settings to set.
2317
+ * @returns {Promise<this>} A promise that resolves to the instance of the manager.
2318
+ */
2319
+ async setEQBand(...bands) {
2320
+ bands = bands.flat();
2321
+ if (!bands.length || !bands.every((band) => typeof band.band === "number" && typeof band.gain === "number"))
2322
+ throw new PlayerError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
2323
+ for (const { band, gain } of bands) this.bands[band] = { band, gain };
2324
+ await this.apply();
2325
+ return this;
2326
+ }
2327
+ /**
2328
+ *
2329
+ * Clears all EQ bands for the player.
2330
+ * @returns {Promise<this>} A promise that resolves to the instance of the manager.
2331
+ */
2332
+ async clearEQBands() {
2333
+ return this.setEQBand(Array.from({ length: 15 }, (_, i) => ({ band: i, gain: 0 })));
2334
+ }
2335
+ /**
2336
+ *
2337
+ * Set the vibrato filter with the given settings.
2338
+ * @param {TremoloSettings} [settings=DefaultFilterPreset.Vibrato] The settings for the vibrato filter.
2339
+ * @returns {Promise<boolean>} Whether the filter is now active.
2340
+ */
2341
+ async setVibrato(settings = DefaultFilterPreset.Vibrato) {
2342
+ if (!this.player.node.info?.filters?.includes("vibrato" /* Vibrato */))
2343
+ throw new PlayerError("Node filters does not include the 'vibrato' filter. (Or the node doesn't have it enabled)");
2344
+ this.data.vibrato = {
2345
+ frequency: this.filters.vibrato ? 0 : settings.frequency,
2346
+ depth: this.filters.vibrato ? 0 : settings.depth
2347
+ };
2348
+ this.filters.vibrato = !this.filters.vibrato;
2349
+ await this.apply();
2350
+ return this.filters.vibrato;
2351
+ }
2352
+ /**
2353
+ *
2354
+ * Set the tremolo filter with the given settings.
2355
+ * @param {TremoloSettings} [settings=DefaultFilterPreset.Tremolo] The settings for the tremolo filter.
2356
+ * @returns {Promise<boolean>} Whether the filter is now active.
2357
+ */
2358
+ async setTremolo(settings = DefaultFilterPreset.Tremolo) {
2359
+ if (!this.player.node.info?.filters?.includes("tremolo" /* Tremolo */))
2360
+ throw new PlayerError("Node filters does not include the 'tremolo' filter. (Or the node doesn't have it enabled)");
2361
+ this.data.tremolo = {
2362
+ frequency: this.filters.tremolo ? 0 : settings.frequency,
2363
+ depth: this.filters.tremolo ? 0 : settings.depth
2364
+ };
2365
+ this.filters.tremolo = !this.filters.tremolo;
2366
+ await this.apply();
2367
+ return this.filters.tremolo;
2368
+ }
2369
+ /**
2370
+ *
2371
+ * Set the low-pass filter with the given settings.
2372
+ * @param {LowPassSettings} [settings=DefaultFilterPreset.Lowpass] The settings for the low-pass filter.
2373
+ * @returns {Promise<boolean>} Whether the filter is now active.
2374
+ */
2375
+ async setLowPass(settings = DefaultFilterPreset.Lowpass) {
2376
+ if (!this.player.node.info?.filters?.includes("lowPass" /* LowPass */))
2377
+ throw new PlayerError("Node filters does not include the 'lowPass' filter. (Or the node doesn't have it enabled)");
2378
+ this.data.lowPass = { smoothing: this.filters.lowPass ? 0 : settings.smoothing };
2379
+ this.filters.lowPass = !this.filters.lowPass;
2380
+ await this.apply();
2381
+ return this.filters.lowPass;
2382
+ }
2383
+ /**
2384
+ * Set the nightcore filter with the given settings.
2385
+ * @param {Partial<TimescaleSettings>} [settings=DefaultFilterPreset.Nightcore] The settings for the nightcore filter.
2386
+ * @returns {Promise<boolean>} Whether the filter is now active.
2387
+ */
2388
+ async setNightcore(settings = DefaultFilterPreset.Nightcore) {
2389
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2390
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2391
+ this.data.timescale = {
2392
+ speed: this.filters.nightcore ? 1 : settings.speed,
2393
+ pitch: this.filters.nightcore ? 1 : settings.pitch,
2394
+ rate: this.filters.nightcore ? 1 : settings.rate
2395
+ };
2396
+ this.filters.nightcore = !this.filters.nightcore;
2397
+ this.filters.vaporwave = false;
2398
+ this.filters.custom = false;
2399
+ await this.apply();
2400
+ return this.filters.nightcore;
2401
+ }
2402
+ /**
2403
+ *
2404
+ * Set the vaporwave filter with the given settings.
2405
+ * @param {Partial<TimescaleSettings>} [settings=DefaultFilterPreset.Vaporwave] The settings for the vaporwave filter.
2406
+ * @returns {Promise<boolean>} Whether the filter is now active.
2407
+ */
2408
+ async setVaporwave(settings = DefaultFilterPreset.Vaporwave) {
2409
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2410
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2411
+ this.data.timescale = {
2412
+ speed: this.filters.vaporwave ? 1 : settings.speed,
2413
+ pitch: this.filters.vaporwave ? 1 : settings.pitch,
2414
+ rate: this.filters.vaporwave ? 1 : settings.rate
2415
+ };
2416
+ this.filters.vaporwave = !this.filters.vaporwave;
2417
+ this.filters.nightcore = false;
2418
+ this.filters.custom = false;
2419
+ await this.apply();
2420
+ return this.filters.vaporwave;
2421
+ }
2422
+ /**
2423
+ *
2424
+ * Set the karaoke filter with the given settings.
2425
+ * @param {KaraokeSettings} [settings=DefaultFilterPreset.Karaoke] The settings for the karaoke filter.
2426
+ * @returns {Promise<boolean>} Whether the filter is now active.
2427
+ */
2428
+ async setKaraoke(settings = DefaultFilterPreset.Karaoke) {
2429
+ if (!this.player.node.info?.filters?.includes("karaoke" /* Karaoke */))
2430
+ throw new PlayerError("Node filters does not include the 'karaoke' filter. (Or the node doesn't have it enabled)");
2431
+ this.data.karaoke = {
2432
+ level: this.data.karaoke.level ? 0 : settings.level,
2433
+ monoLevel: this.data.karaoke.monoLevel ? 0 : settings.monoLevel,
2434
+ filterBand: this.data.karaoke.filterBand ? 0 : settings.filterBand,
2435
+ filterWidth: this.data.karaoke.filterWidth ? 0 : settings.filterWidth
2436
+ };
2437
+ this.filters.karaoke = !this.filters.karaoke;
2438
+ await this.apply();
2439
+ return this.filters.karaoke;
2440
+ }
2441
+ /**
2442
+ *
2443
+ * Set the distortion filter with the given settings.
2444
+ * @param {Partial<DistortionSettings>} [settings=DefaultFilterPreset.Distortion] The settings for the distortion filter.
2445
+ * @returns {Promise<boolean>} Whether the filter is now active.
2446
+ */
2447
+ async setDistortion(settings = DefaultFilterPreset.Distortion) {
2448
+ if (!this.player.node.info?.filters?.includes("distortion" /* Distortion */))
2449
+ throw new PlayerError("Node filters does not include the 'distortion' filter. (Or the node doesn't have it enabled)");
2450
+ this.data.distortion = {
2451
+ sinOffset: this.filters.distortion ? 0 : settings.sinOffset,
2452
+ sinScale: this.filters.distortion ? 1 : settings.sinScale,
2453
+ cosOffset: this.filters.distortion ? 0 : settings.cosOffset,
2454
+ cosScale: this.filters.distortion ? 1 : settings.cosScale,
2455
+ tanOffset: this.filters.distortion ? 0 : settings.tanOffset,
2456
+ offset: this.filters.distortion ? 0 : settings.offset,
2457
+ scale: this.filters.distortion ? 1 : settings.scale
2458
+ };
2459
+ this.filters.distortion = !this.filters.distortion;
2460
+ await this.apply();
2461
+ return this.filters.distortion;
2462
+ }
2463
+ /**
2464
+ * Set the timescale filter with the given settings.
2465
+ * @param {Partial<TimescaleSettings>} settings The timescale settings to set.
2466
+ * @returns {Promise<boolean>} Whether the filter is now active.
2467
+ */
2468
+ async setTimescale(settings) {
2469
+ if (!this.player.node.info?.filters?.includes("timescale" /* Timescale */))
2470
+ throw new PlayerError("Node filters does not include the 'timescale' filter. (Or the node doesn't have it enabled)");
2471
+ this.data.timescale = {
2472
+ pitch: settings.pitch ?? 1,
2473
+ rate: settings.rate ?? 1,
2474
+ speed: settings.speed ?? 1
2475
+ };
2476
+ this.filters.timescale = !this.filters.timescale;
2477
+ await this.apply();
2478
+ return this.filters.timescale;
2479
+ }
2480
+ };
2481
+
2482
+ // src/classes/player/Storage.ts
2483
+ var PlayerStorage = class {
2484
+ /**
2485
+ * The internal storage.
2486
+ * @type {Map<K, V>}
2487
+ * @private
2488
+ * @readonly
2489
+ * @internal
2490
+ */
2491
+ internal = /* @__PURE__ */ new Map();
2492
+ /**
2493
+ *
2494
+ * Get the value for a key in the storage.
2495
+ * @param {K} key The key to get the value for.
2496
+ * @returns {V | undefined} The value for the key, or undefined if it doesn't exist.
2497
+ */
2498
+ get(key) {
2499
+ return this.internal.get(key);
2500
+ }
2501
+ /**
2502
+ * Set the value for a key in the storage.
2503
+ * @param {K} key The key to set the value for.
2504
+ * @param {V} value The value to set for the key.
2505
+ */
2506
+ set(key, value) {
2507
+ this.internal.set(key, value);
2508
+ }
2509
+ /**
2510
+ * Check if the storage has a key.
2511
+ * @param {K} key The key to check for.
2512
+ * @returns {boolean} True if the storage has the key, false otherwise.
2513
+ */
2514
+ has(key) {
2515
+ return this.internal.has(key);
2516
+ }
2517
+ /**
2518
+ * Delete a key from the storage.
2519
+ * @param {K} key The key to delete.
2520
+ * @returns {boolean} True if the key was deleted, false otherwise.
2521
+ */
2522
+ delete(key) {
2523
+ return this.internal.delete(key);
2524
+ }
2525
+ /**
2526
+ * Get all keys in the storage.
2527
+ * @returns {K[]} The keys in the storage.
2528
+ */
2529
+ keys() {
2530
+ return [...this.internal.keys()];
2531
+ }
2532
+ /**
2533
+ * Get all values in the storage.
2534
+ * @returns {V[]} The values in the storage.
2535
+ */
2536
+ values() {
2537
+ return [...this.internal.values()];
2538
+ }
2539
+ /**
2540
+ * Get all entries in the storage.
2541
+ * @returns {[K, V][]} The entries in the storage.
2542
+ */
2543
+ entries() {
2544
+ return [...this.internal.entries()];
2545
+ }
2546
+ /**
2547
+ *
2548
+ * Get all key-value pairs in the storage.
2549
+ * @returns {Record<K[number], V>} An object containing all key-value pairs in the storage, excluding internal keys.
2550
+ */
2551
+ all() {
2552
+ return Object.fromEntries([...this.internal.entries()].filter(([key]) => !key.startsWith("internal_")));
2553
+ }
2554
+ /**
2555
+ * Clear the storage.
2556
+ */
2557
+ clear() {
2558
+ this.internal.clear();
2559
+ }
2560
+ /**
2561
+ * Get the size of the storage.
2562
+ * @returns {number} The number of entries in the storage.
2563
+ */
2564
+ get size() {
2565
+ return this.internal.size;
2566
+ }
2567
+ };
2568
+
2569
+ // src/classes/player/Player.ts
2570
+ var Player = class {
2571
+ /**
2572
+ * The data for the player.
2573
+ * @type {PlayerStorage}
2574
+ * @readonly
2575
+ */
2576
+ data = new PlayerStorage();
2577
+ /**
2578
+ * The options for the player.
2579
+ * @type {PlayerOptions}
2580
+ * @readonly
2581
+ */
2582
+ options;
2583
+ /**
2584
+ * The manager for the player.
2585
+ * @type {Hoshimi}
2586
+ * @readonly
2587
+ */
2588
+ manager;
2589
+ /**
2590
+ * The queue for the player.
2591
+ * @type {Queue}
2592
+ * @readonly
2593
+ */
2594
+ queue;
2595
+ /**
2596
+ * The filter manager for the player.
2597
+ * @type {FilterManager}
2598
+ * @readonly
2599
+ */
2600
+ filterManager;
2601
+ /**
2602
+ * The node for the player.
2603
+ * @type {NodeStructure}
2604
+ */
2605
+ node;
2606
+ /**
2607
+ * Check if the player is self deafened.
2608
+ * @type {boolean}
2609
+ */
2610
+ selfDeaf = false;
2611
+ /**
2612
+ * Check if the player is self muted.
2613
+ * @type {boolean}
2614
+ */
2615
+ selfMute = false;
2616
+ /**
2617
+ * Loop mode of the player.
2618
+ * @type {LoopMode}
2619
+ * @default LoopMode.Off
2620
+ */
2621
+ loop = 3 /* Off */;
2622
+ /**
2623
+ * Check if the player is playing.
2624
+ * @type {boolean}
2625
+ * @default false
2626
+ */
2627
+ playing = false;
2628
+ /**
2629
+ * Check if the player is paused.
2630
+ * @type {boolean}
2631
+ * @default false
2632
+ */
2633
+ paused = false;
2634
+ /**
2635
+ * Check if the player is connected.
2636
+ * @type {boolean}
2637
+ * @default false
2638
+ */
2639
+ connected = false;
2640
+ /**
2641
+ * Volume of the player.
2642
+ * @type {number}
2643
+ * @default 100
2644
+ */
2645
+ volume = 100;
2646
+ /**
2647
+ * Guild ig of the player.
2648
+ * @type {string}
2649
+ */
2650
+ guildId;
2651
+ /**
2652
+ * Voice channel idof the player.
2653
+ * @type {string | undefined}
2654
+ */
2655
+ voiceId = void 0;
2656
+ /**
2657
+ * Text channel id of the player.
2658
+ * @type {string | undefined}
2659
+ */
2660
+ textId = void 0;
2661
+ /**
2662
+ * The ping of the player.
2663
+ * @type {number}
2664
+ */
2665
+ ping = 0;
2666
+ /**
2667
+ * The timestamp when the player was created.
2668
+ * @type {number}
2669
+ */
2670
+ createdTimestamp = 0;
2671
+ /**
2672
+ * The position of the player.
2673
+ * @type {number}
2674
+ */
2675
+ position = 0;
2676
+ /**
2677
+ * The voice connection details.
2678
+ * @type {PlayerVoice}
2679
+ */
2680
+ voice = {
2681
+ endpoint: null,
2682
+ sessionId: null,
2683
+ token: null
2684
+ };
2685
+ /**
2686
+ *
2687
+ * Create a new player.
2688
+ * @param {Hoshimi} manager The manager for the player.
2689
+ * @param {PlayOptions} options The options for the player.
2690
+ * @example
2691
+ * ```ts
2692
+ * const player = new Player(manager, {
2693
+ * guildId: "guildId",
2694
+ * voiceId: "voiceId",
2695
+ * textId: "textId",
2696
+ * selfDeaf: true,
2697
+ * selfMute: false,
2698
+ * volume: 100,
2699
+ * });
2700
+ *
2701
+ * console.log(player.guildId); // guildId
2702
+ * console.log(player.voiceId); // voiceId
2703
+ * console.log(player.textId); // textId
2704
+ */
2705
+ constructor(manager, options) {
2706
+ this.manager = manager;
2707
+ this.options = options;
2708
+ this.guildId = options.guildId;
2709
+ this.voiceId = options.voiceId;
2710
+ this.selfDeaf = options.selfDeaf ?? true;
2711
+ this.selfMute = options.selfMute ?? false;
2712
+ this.volume = options.volume ?? 100;
2713
+ this.textId = options.textId;
2714
+ this.node = (typeof this.options.node === "string" ? this.manager.nodeManager.get(this.options.node) : this.options.node) ?? this.manager.nodeManager.getLeastUsed();
2715
+ validatePlayerOptions(this.options);
2716
+ this.queue = Structures.Queue(this);
2717
+ this.filterManager = Structures.FilterManager(this);
2718
+ }
2719
+ /**
2720
+ * The lyrics methods for the player.
2721
+ * @type {LyricsMethods}
2722
+ * @readonly
2723
+ */
2724
+ lyrics = {
2725
+ subscribe: (skipSource) => this.node.lyricsManager.subscribe(this.guildId, skipSource),
2726
+ unsubscribe: () => this.node.lyricsManager.unsubscribe(this.guildId),
2727
+ current: (skipSource) => this.node.lyricsManager.current(this.guildId, skipSource),
2728
+ get: (track, skipSource) => this.node.lyricsManager.get(track, skipSource)
2729
+ };
2730
+ /**
2731
+ *
2732
+ * Search for a track or playlist.
2733
+ * @param {SearchOptions} options The options for the search.
2734
+ * @returns {Promise<QueryResult>} The search result.
2735
+ * @example
2736
+ * ```ts
2737
+ * const player = manager.getPlayer("guildId");
2738
+ * const result = await player.search({
2739
+ * query: "track name",
2740
+ * engine: SearchEngine.Youtube,
2741
+ * requester: {},
2742
+ * });
2743
+ *
2744
+ * console.log(result) // the search result
2745
+ * ```
2746
+ */
2747
+ search(options) {
2748
+ return this.manager.search({
2749
+ ...options,
2750
+ node: this.node
2751
+ });
2752
+ }
2753
+ /**
2754
+ *
2755
+ * Play the next track in the queue.
2756
+ * @param {number} [to=0] The amount of tracks to skip.
2757
+ * @param {boolean} [throwError=true] Whether to throw an error if there are no tracks to skip.
2758
+ * @returns {Promise<void>}
2759
+ * @throws {PlayerError} If there are no tracks to skip.
2760
+ * @example
2761
+ * ```ts
2762
+ * const player = manager.getPlayer("guildId");
2763
+ * player.skip(2); // skip 2 tracks
2764
+ * player.skip(); // skip 1 track
2765
+ * ```
2766
+ */
2767
+ async skip(to = 0, throwError = true) {
2768
+ if (!this.queue.size) {
2769
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Skip] No tracks to skip.");
2770
+ if (throwError) throw new PlayerError("No tracks to skip.");
2771
+ }
2772
+ if (typeof to === "number" && to > 0) {
2773
+ if (to > this.queue.size) throw new PlayerError("Cannot skip to a track that doesn't exist.");
2774
+ if (to < 0) throw new PlayerError("Cannot skip to a negative number.");
2775
+ this.queue.splice(0, to - 1);
2776
+ }
2777
+ if (!this.playing && !this.queue.current) return this.play();
2778
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Skip] Skipping to next track for guild: ${this.guildId}`);
2779
+ await this.node.stopPlayer(this.guildId);
2780
+ }
2781
+ /**
2782
+ *
2783
+ * Seek to a specific position in the current track.
2784
+ * @param {number} position The position to seek to in milliseconds.
2785
+ * @returns {Promise<void>}
2786
+ * @throws {PlayerError} If the position is invalid.
2787
+ * @example
2788
+ * ```ts
2789
+ * const player = manager.getPlayer("guildId");
2790
+ * player.seek(30000); // seek to 30 seconds
2791
+ * ```
2792
+ */
2793
+ async seek(position) {
2794
+ if (typeof position !== "number" || Number.isNaN(position) || position < 0)
2795
+ throw new PlayerError("Position must be a positive number.");
2796
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Seek] Seeking to ${position} for guild: ${this.guildId}`);
2797
+ await this.updatePlayer({ playerOptions: { position } });
2798
+ }
2799
+ /**
2800
+ *
2801
+ * Disconnect the player from the voice channel.
2802
+ * @returns {Promise<this>} The player instance.
2803
+ * @example
2804
+ * ```ts
2805
+ * const player = manager.getPlayer("guildId");
2806
+ * player.disconnect();
2807
+ * ```
2808
+ */
2809
+ async disconnect() {
2810
+ if (!this.voiceId) return this;
2811
+ await this.manager.options.sendPayload(this.guildId, {
2812
+ op: 4,
2813
+ d: {
2814
+ guild_id: this.guildId,
2815
+ channel_id: null,
2816
+ self_deaf: this.selfDeaf,
2817
+ self_mute: this.selfMute
2818
+ }
2819
+ });
2820
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Disconnect] Player disconnected for guild: ${this.guildId}`);
2821
+ this.connected = false;
2822
+ return this;
2823
+ }
2824
+ /**
2825
+ *
2826
+ * Destroy and disconnect the player.
2827
+ * @param {DestroyReasons} [reason] The reason for destroying the player.
2828
+ * @returns {Promise<void>}
2829
+ * @example
2830
+ * ```ts
2831
+ * const player = manager.getPlayer("guildId");
2832
+ * player.destroy(DestroyReasons.Stop);
2833
+ * ```
2834
+ */
2835
+ async destroy(reason = "Player-Stop" /* Stop */) {
2836
+ await this.disconnect();
2837
+ await this.node.destroyPlayer(this.guildId);
2838
+ this.manager.emit("playerDestroy" /* PlayerDestroy */, this, reason);
2839
+ this.manager.emit(
2840
+ "debug" /* Debug */,
2841
+ 3 /* Player */,
2842
+ `[Player] -> [Destroy] Destroyed player for guild: ${this.guildId} | Reason: ${reason}`
2843
+ );
2844
+ return this.manager.deletePlayer(this.guildId);
2845
+ }
2846
+ /**
2847
+ *
2848
+ * Play a track in the player.
2849
+ * @param {Partial<PlayOptions>} [options] The options to play the track.
2850
+ * @returns {Promise<void>}
2851
+ * @throws {PlayerError} If there are no tracks to play.
2852
+ * @example
2853
+ * ```ts
2854
+ * const player = manager.getPlayer("guildId");
2855
+ *
2856
+ * player.play({
2857
+ * track: track,
2858
+ * noReplace: true,
2859
+ * });
2860
+ * ```
2861
+ */
2862
+ async play(options = {}) {
2863
+ if (typeof options !== "object") throw new PlayerError("The play options must be an object.");
2864
+ if (options.track) this.queue.current = await validateTrack(this, options.track);
2865
+ else if (!this.queue.current) this.queue.current = await validateTrack(this, this.queue.shift());
2866
+ if (!this.queue.current) throw new PlayerError("No track to play.");
2867
+ if (!isTrack(this.queue.current) && !isUnresolvedTrack(this.queue.current))
2868
+ throw new PlayerError("The track must be a valid Track or UnresolvedTrack instance.");
2869
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Play] A new track is playing: ${this.queue.current.info.title}`);
2870
+ await this.updatePlayer({
2871
+ noReplace: options.noReplace,
2872
+ playerOptions: {
2873
+ ...options,
2874
+ track: {
2875
+ userData: this.queue.current.userData,
2876
+ encoded: this.queue.current.encoded
2877
+ }
2878
+ }
2879
+ });
2880
+ return;
2881
+ }
2882
+ /**
2883
+ * Connect the player to the voice channel.
2884
+ * @returns {Promise<this>} The player instance.
2885
+ * @example
2886
+ * ```ts
2887
+ * const player = manager.getPlayer("guildId");
2888
+ * player.connect();
2889
+ * ```
2890
+ */
2891
+ async connect() {
2892
+ if (!this.voiceId) return this;
2893
+ await this.manager.options.sendPayload(this.guildId, {
2894
+ op: 4,
2895
+ d: {
2896
+ guild_id: this.guildId,
2897
+ channel_id: this.voiceId,
2898
+ self_deaf: this.selfDeaf,
2899
+ self_mute: this.selfMute
2900
+ }
2901
+ });
2902
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Connect] Player connected for guild: ${this.guildId}`);
2903
+ this.connected = true;
2904
+ return this;
2905
+ }
2906
+ /**
2907
+ *
2908
+ * Stop the player from playing.
2909
+ * @param {boolean} [destroy=true] Whether to destroy the player or not.
2910
+ * @returns {Promise<void>}
2911
+ * @example
2912
+ * ```ts
2913
+ * const player = manager.getPlayer("guildId");
2914
+ * player.stop();
2915
+ * ```
2916
+ */
2917
+ async stop(destroy = true) {
2918
+ await this.node.stopPlayer(this.guildId);
2919
+ if (destroy) await this.destroy("Player-Stop" /* Stop */);
2920
+ this.manager.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Stop] Player stopped for guild: ${this.guildId}`);
2921
+ this.playing = false;
2922
+ this.paused = false;
2923
+ this.position = 0;
2924
+ this.queue.current = null;
2925
+ return;
2926
+ }
2927
+ /**
2928
+ *
2929
+ * Pause or resume the player.
2930
+ * @returns {Promise<void>}
2931
+ * @example
2932
+ * ```ts
2933
+ * const player = manager.getPlayer("guildId");
2934
+ * player.setPaused();
2935
+ * ```
2936
+ */
2937
+ async setPaused(paused = !this.paused) {
2938
+ this.manager.emit(
2939
+ "debug" /* Debug */,
2940
+ 3 /* Player */,
2941
+ `[Player] -> [Pause] Player is now ${paused ? "paused" : "resumed"} for guild: ${this.guildId}`
2942
+ );
2943
+ await this.updatePlayer({ playerOptions: { paused } });
2944
+ return paused;
2945
+ }
2946
+ /**
2947
+ *
2948
+ * Set the volume of the player.
2949
+ * @param {number} volume The volume to set.
2950
+ * @returns {Promise<void>}
2951
+ * @example
2952
+ * ```ts
2953
+ * const player = manager.getPlayer("guildId");
2954
+ * player.setVolume(50); // set the volume to 50%
2955
+ * ```
2956
+ */
2957
+ async setVolume(volume) {
2958
+ if (typeof volume !== "number" || Number.isNaN(volume) || volume < 0 || volume > 100)
2959
+ throw new PlayerError("Volume must be a number between 0 and 100.");
2960
+ await this.updatePlayer({ playerOptions: { volume } });
2961
+ this.volume = volume;
2962
+ this.manager.emit(
2963
+ "debug" /* Debug */,
2964
+ 3 /* Player */,
2965
+ `[Player] -> [Volume] Player volume set to ${volume}% for guild: ${this.guildId}`
2966
+ );
2967
+ return;
2968
+ }
2969
+ /**
2970
+ *
2971
+ * Set the loop mode of the player.
2972
+ * @param {LoopMode} mode The loop mode to set.
2973
+ * @throws {PlayerError} If the loop mode is invalid.
2974
+ * @example
2975
+ * ```ts
2976
+ * const player = manager.getPlayer("guildId");
2977
+ * player.setLoop(LoopMode.Track);
2978
+ * ```
2979
+ */
2980
+ setLoop(mode) {
2981
+ const loopValues = Object.values(LoopMode).filter((v) => typeof v === "number");
2982
+ if (!loopValues.includes(mode)) throw new PlayerError(`Invalid loop mode. Valid modes are: ${loopValues.join(", ")}`);
2983
+ this.loop = mode;
2984
+ this.manager.emit(
2985
+ "debug" /* Debug */,
2986
+ 3 /* Player */,
2987
+ `[Player] -> [Loop] Player loop mode set to ${mode} for guild: ${this.guildId}`
2988
+ );
2989
+ return this;
2990
+ }
2991
+ /**
2992
+ * Set the voice of the player.
2993
+ * @param {Partial<VoiceChannelUpdate>} voice The voice state to set.
2994
+ * @returns {Promise<void>}
2995
+ * @example
2996
+ * ```ts
2997
+ * const player = manager.getPlayer("guildId");
2998
+ * player.setVoice({ voiceId: "newVoiceId" });
2999
+ * ```
3000
+ */
3001
+ async setVoice(voice) {
3002
+ if (voice.voiceId === this.voiceId) return;
3003
+ if (voice.voiceId) {
3004
+ this.voiceId = voice.voiceId;
3005
+ this.options.voiceId = voice.voiceId;
3006
+ }
3007
+ if (voice.selfDeaf) {
3008
+ this.selfDeaf = voice.selfDeaf;
3009
+ this.options.selfDeaf = voice.selfDeaf;
3010
+ }
3011
+ if (voice.selfMute) {
3012
+ this.selfMute = voice.selfMute;
3013
+ this.options.selfMute = voice.selfMute;
3014
+ }
3015
+ await this.manager.options.sendPayload(this.guildId, {
3016
+ op: 4,
3017
+ d: {
3018
+ guild_id: this.guildId,
3019
+ self_deaf: this.selfDeaf,
3020
+ self_mute: this.selfMute,
3021
+ channel_id: this.voiceId ?? null
3022
+ }
3023
+ });
3024
+ this.manager.emit(
3025
+ "debug" /* Debug */,
3026
+ 3 /* Player */,
3027
+ `[Player] -> [VoiceState] Updated voice state for guild: ${this.guildId} with voiceId: ${this.voiceId}`
3028
+ );
3029
+ }
3030
+ /**
3031
+ *
3032
+ * Change the node the player is connected to.
3033
+ * @param {NodeIdentifier} node The node to change to.
3034
+ * @returns {Promise<void>} A promise that resolves when the node has been changed.
3035
+ * @throws {PlayerError} If the target node is not found, not connected, or missing source managers.
3036
+ * @example
3037
+ * ```ts
3038
+ * const player = manager.getPlayer("guildId");
3039
+ * player.move("newNodeId");
3040
+ * ```
3041
+ */
3042
+ async move(node) {
3043
+ const id = typeof node === "string" ? node : node.id;
3044
+ const target = this.manager.nodeManager.get(id);
3045
+ if (!target) throw new PlayerError("Target node not found.");
3046
+ if (!target.info) throw new PlayerError("Target node info not available.");
3047
+ if (target.state !== 2 /* Connected */) throw new PlayerError("Target node is not connected.");
3048
+ if (target.id === this.node.id) return;
3049
+ if (this.queue.current || this.queue.size) {
3050
+ const sources = [this.queue.current, ...this.queue.tracks].filter((t) => t != null || typeof t !== "undefined").map((t) => t.info.sourceName).filter((s) => s != null || typeof s !== "undefined");
3051
+ const missings = [...new Set(sources)].filter((s) => !target.info.sourceManagers.includes(s));
3052
+ if (missings.length) throw new PlayerError(`Target node is missing source managers for: ${missings.join(", ")}`);
3053
+ }
3054
+ const current = this.queue.current;
3055
+ if (!this.voice.endpoint || !this.voice.sessionId || !this.voice.token)
3056
+ throw new PlayerError("Player voice connection data is incomplete.");
3057
+ if (this.node.state === 2 /* Connected */) await this.node.destroyPlayer(this.guildId);
3058
+ this.node = target;
3059
+ await this.updatePlayer({
3060
+ playerOptions: {
3061
+ voice: this.voice,
3062
+ ...current && {
3063
+ track: current.encoded,
3064
+ position: this.position,
3065
+ volume: this.volume
3066
+ }
3067
+ }
3068
+ });
3069
+ await this.filterManager.apply();
3070
+ this.manager.emit(
3071
+ "debug" /* Debug */,
3072
+ 3 /* Player */,
3073
+ `[Player] -> [Move] Player moved to node: ${target.id} for guild: ${this.guildId}`
3074
+ );
3075
+ }
3076
+ /**
3077
+ * Update the player with new data.
3078
+ * @param {NonGuildUpdatePlayerInfo} data The data to update the player with.
3079
+ * @returns {Promise<LavalinkPlayer | null>} The updated player data.
3080
+ */
3081
+ async updatePlayer(data) {
3082
+ return this.node.updatePlayer({
3083
+ guildId: this.guildId,
3084
+ ...data
3085
+ });
3086
+ }
3087
+ /**
3088
+ *
3089
+ * Return the player as a json object.
3090
+ * @returns {PlayerJson}
3091
+ * @example
3092
+ * ```ts
3093
+ * const player = manager.getPlayer("guildId");
3094
+ * const json = player.toJSON();
3095
+ * console.log(json); // the player as a json object
3096
+ * ```
3097
+ */
3098
+ toJSON() {
3099
+ return {
3100
+ volume: this.volume,
3101
+ loop: this.loop,
3102
+ paused: this.paused,
3103
+ playing: this.playing,
3104
+ voiceId: this.voiceId,
3105
+ guildId: this.guildId,
3106
+ selfMute: this.selfMute,
3107
+ selfDeaf: this.selfDeaf,
3108
+ options: this.options,
3109
+ voice: this.voice,
3110
+ queue: this.queue.toJSON(),
3111
+ node: this.node.toJSON()
3112
+ };
3113
+ }
3114
+ };
3115
+
3116
+ // src/classes/queue/Store.ts
3117
+ var QueueStore = class {
3118
+ /**
3119
+ * Storage manager instance.
3120
+ * @type {StorageAdapter}
3121
+ * @private
3122
+ * @readonly
3123
+ * @internal
3124
+ */
3125
+ storage;
3126
+ /**
3127
+ *
3128
+ * Constructor of the queue store.
3129
+ * @param {StorageAdapter} storage Storage manager instance.
3130
+ * @example
3131
+ * ```ts
3132
+ * const storage = new CustomStorage();
3133
+ * const queueStore = new QueueStore(storage);
3134
+ *
3135
+ * console.log(queueStore);
3136
+ * ```
3137
+ */
3138
+ constructor(storage) {
3139
+ this.storage = storage;
3140
+ }
3141
+ /**
3142
+ *
3143
+ * Get the value using the key.
3144
+ * @param {string} key The key to get the value from.
3145
+ * @returns <Awaitable<QueueJson | undefined>> The value of the key.
3146
+ * @example
3147
+ * ```ts
3148
+ * const value = await queueStore.get("key");
3149
+ * console.log(value); // { id: "key", data: "value" }
3150
+ * ```
3151
+ */
3152
+ get(key) {
3153
+ return this.storage.get(key);
3154
+ }
3155
+ /**
3156
+ *
3157
+ * Set the value using the key.
3158
+ * @param {string} key The key to set the value to.
3159
+ * @param {unknown} value The value to set.
3160
+ * @returns <Awaitable<void>> Ganyu is the best waifu.
3161
+ * @example
3162
+ * ```ts
3163
+ * await queueStore.set("key", { id: "key", data: "value" });
3164
+ * ```
3165
+ */
3166
+ set(key, value) {
3167
+ return this.storage.set(key, value);
3168
+ }
3169
+ /**
3170
+ *
3171
+ * Delete the value using the key.
3172
+ * @param {string} key The key to delete the value from.
3173
+ * @returns <Awaitable<boolean>> Returns true if the key was deleted.
3174
+ * @example
3175
+ * ```ts
3176
+ * const success = await queueStore.delete("key");
3177
+ * console.log(success); // true
3178
+ * ```
3179
+ */
3180
+ delete(key) {
3181
+ return this.storage.delete(key);
3182
+ }
3183
+ };
3184
+
3185
+ // src/classes/queue/Utils.ts
3186
+ var QueueUtils = class {
3187
+ /**
3188
+ * Player instance.
3189
+ * @type {Queue}
3190
+ * @private
3191
+ * @readonly
3192
+ * @internal
3193
+ */
3194
+ queue;
3195
+ /**
3196
+ * Queue store.
3197
+ * @type {QueueStore}
3198
+ * @private
3199
+ * @readonly
3200
+ * @internal
3201
+ */
3202
+ store;
3203
+ /**
3204
+ * Options for the queue.
3205
+ * @type {HoshimiQueueOptions}
3206
+ * @private
3207
+ * @readonly
3208
+ * @internal
3209
+ */
3210
+ options;
3211
+ /**
3212
+ *
3213
+ * Constructor of the queue utils.
3214
+ * @param {QueueStructure} queue The queue instance.
3215
+ */
3216
+ constructor(queue) {
3217
+ this.queue = queue;
3218
+ this.options = queue.player.manager.options.queueOptions;
3219
+ this.store = new QueueStore(this.options.storage);
3220
+ }
3221
+ /**
3222
+ *
3223
+ * Save the queue.
3224
+ * @returns {Awaitable<void>}
3225
+ * @example
3226
+ * ```ts
3227
+ * await player.queue.utils.save();
3228
+ * ```
3229
+ */
3230
+ save() {
3231
+ const max = this.options.maxPreviousTracks;
3232
+ const length = this.queue.tracks.length;
3233
+ const json = this.queue.toJSON();
3234
+ if (length > max) this.queue.history.splice(0, length - max);
3235
+ this.queue.player.manager.emit(
3236
+ "debug" /* Debug */,
3237
+ 5 /* Queue */,
3238
+ `[Queue] -> [Adapter] Saving queue for ${this.queue.player.guildId} | Object: ${JSON.stringify(json)}`
3239
+ );
3240
+ return this.store.set(this.queue.player.guildId, json);
3241
+ }
3242
+ /**
3243
+ *
3244
+ * Destroy the queue.
3245
+ * @returns {Promise<void>}
3246
+ * @example
3247
+ * ```ts
3248
+ * await player.queue.utils.destroy();
3249
+ * ```
3250
+ */
3251
+ destroy() {
3252
+ this.queue.player.manager.emit(
3253
+ "debug" /* Debug */,
3254
+ 5 /* Queue */,
3255
+ `[Queue] -> [Adapter] Destroying queue for ${this.queue.player.guildId}`
3256
+ );
3257
+ return this.store.delete(this.queue.player.guildId);
3258
+ }
3259
+ /**
3260
+ *
3261
+ * Sync the queue.
3262
+ * @returns {Awaitable<void>}
3263
+ * @example
3264
+ * ```ts
3265
+ * await player.queue.utils.sync();
3266
+ * ```
3267
+ */
3268
+ async sync(override = true, syncCurrent = false) {
3269
+ const data = await this.store.get(this.queue.player.guildId);
3270
+ if (!data) throw new StorageError(`No data found to sync for guildId: ${this.queue.player.guildId}`);
3271
+ if (syncCurrent && data.current && !this.queue.current && isTrack(data.current)) this.queue.current = data.current;
3272
+ const tracks = data.tracks.filter((track) => isTrack(track)) || [];
3273
+ const history = data.history.filter((track) => isTrack(track)) || [];
3274
+ const length = this.queue.tracks.length;
3275
+ if (tracks.length) this.queue.tracks.splice(override ? 0 : length, override ? length : 0, ...tracks);
3276
+ if (history.length) this.queue.history.splice(0, override ? length : 0, ...history);
3277
+ this.queue.player.manager.emit(
3278
+ "debug" /* Debug */,
3279
+ 5 /* Queue */,
3280
+ `[Queue] -> [Adapter] Syncing queue for ${this.queue.player.guildId} | Object: ${JSON.stringify(data)}`
3281
+ );
3282
+ await this.save();
3283
+ }
3284
+ };
3285
+
3286
+ // src/classes/queue/Queue.ts
3287
+ var Queue = class {
3288
+ /**
3289
+ * Tracks of the queue.
3290
+ * @type {HoshimiTrack[]}
3291
+ */
3292
+ tracks = [];
3293
+ /**
3294
+ * Previous tracks of the queue.
3295
+ * @type {Track[]}
3296
+ */
3297
+ history = [];
3298
+ /**
3299
+ * Current track of the queue.
3300
+ * @type {Track | null}
3301
+ */
3302
+ current = null;
3303
+ /**
3304
+ * The player instance.
3305
+ * @type {PlayerStructure}
3306
+ */
3307
+ player;
3308
+ /**
3309
+ * The queue utils instance.
3310
+ * @type {QueueUtils}
3311
+ * @readonly
3312
+ */
3313
+ utils;
3314
+ /**
3315
+ *
3316
+ * Constructor of the queue.
3317
+ * @param {PlayerStructure} player Player instance.
3318
+ * @example
3319
+ * ```ts
3320
+ * const player = new Player();
3321
+ * const queue = new Queue(player);
3322
+ *
3323
+ * console.log(queue.size); // 0
3324
+ * queue.add(track);
3325
+ * console.log(queue.size); // 1
3326
+ * ```
3327
+ */
3328
+ constructor(player) {
3329
+ this.player = player;
3330
+ this.utils = new QueueUtils(this);
3331
+ }
3332
+ /**
3333
+ * Get the track size of the queue.
3334
+ * @type {number}
3335
+ * @returns {number} The track size of the queue.
3336
+ * @readonly
3337
+ * @example
3338
+ * ```ts
3339
+ * const queue = player.queue;
3340
+ *
3341
+ * console.log(queue.size); // 0
3342
+ * queue.add(track);
3343
+ *
3344
+ * console.log(queue.size); // 1
3345
+ * queue.add([track1, track2]);
3346
+ *
3347
+ * console.log(queue.size); // 3
3348
+ * queue.shift();
3349
+ * console.log(queue.size); // 2
3350
+ *
3351
+ * queue.clear();
3352
+ * console.log(queue.size); // 0
3353
+ * ```
3354
+ */
3355
+ get size() {
3356
+ return this.tracks.length;
3357
+ }
3358
+ /**
3359
+ * Get the total track size of the queue (Includes the current track).
3360
+ * @type {number}
3361
+ * @returns {number} The total track size of the queue.
3362
+ * @readonly
3363
+ * @example
3364
+ * ```ts
3365
+ * const queue = player.queue;
3366
+ *
3367
+ * console.log(queue.totalSize); // 0
3368
+ * queue.add(track);
3369
+ *
3370
+ * console.log(queue.totalSize); // 1
3371
+ * queue.add([track1, track2]);
3372
+ *
3373
+ * console.log(queue.totalSize); // 3
3374
+ * queue.shift();
3375
+ * console.log(queue.totalSize); // 2
3376
+ *
3377
+ * queue.clear();
3378
+ * console.log(queue.totalSize); // 0
3379
+ * ```
3380
+ */
3381
+ get totalSize() {
3382
+ return this.size + Number(!!this.current);
3383
+ }
3384
+ /**
3385
+ *
3386
+ * Check if the queue is empty.
3387
+ * @type {boolean}
3388
+ * @returns {boolean} True if the queue is empty.
3389
+ * @readonly
3390
+ * @example
3391
+ * ```ts
3392
+ * const queue = player.queue;
3393
+ *
3394
+ * console.log(queue.isEmpty()); // true
3395
+ * queue.add(track);
3396
+ *
3397
+ * console.log(queue.isEmpty()); // false
3398
+ * queue.clear();
3399
+ *
3400
+ * console.log(queue.isEmpty()); // true
3401
+ * ```
3402
+ */
3403
+ isEmpty() {
3404
+ return this.size === 0;
3405
+ }
3406
+ /**
3407
+ *
3408
+ * Get the previous track of the queue.
3409
+ * @param {boolean} [remove=false] Whether to remove the track from the previous queue.
3410
+ * @returns {Track | null} The previous track of the queue.
3411
+ * @example
3412
+ * ```ts
3413
+ * const queue = player.queue;
3414
+ *
3415
+ * console.log(queue.previous()); // null
3416
+ * queue.add(track);
3417
+ * queue.add(track2);
3418
+ *
3419
+ * console.log(queue.previous()); // track
3420
+ * console.log(queue.previous(true)); // track and remove it from the previous tracks
3421
+ * ```
3422
+ */
3423
+ previous(remove = false) {
3424
+ if (remove) return this.history.shift() ?? null;
3425
+ return this.history[0] ?? null;
3426
+ }
3427
+ /**
3428
+ *
3429
+ * Add a track or tracks to the queue.
3430
+ * @param {Track | Track[]} track The track or tracks to add.
3431
+ * @param {number} [position] The position to add the track or tracks.
3432
+ * @returns {this} The queue instance.
3433
+ * @example
3434
+ * ```ts
3435
+ * const queue = player.queue;
3436
+ *
3437
+ * console.log(queue.size); // 0
3438
+ *
3439
+ * queue.add(track);
3440
+ * console.log(queue.size); // 1
3441
+ *
3442
+ * queue.add([track1, track2]);
3443
+ * console.log(queue.size); // 3
3444
+ *
3445
+ * queue.add(track3, 1);
3446
+ * console.log(queue.size); // 4
3447
+ * console.log(queue.tracks); // [track1, track3, track2, track]
3448
+ * ```
3449
+ */
3450
+ add(track, position) {
3451
+ const tracks = Array.isArray(track) ? track : [track];
3452
+ if (typeof position === "number" && position >= 0 && position < this.tracks.length) return this.splice(position, 0, ...tracks);
3453
+ this.tracks.push(...tracks);
3454
+ this.player.manager.emit("queueUpdate" /* QueueUpdate */, this.player, this);
3455
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, `[Queue] -> [Add] Added ${this.tracks.length} tracks to the queue.`);
3456
+ return this;
3457
+ }
3458
+ /**
3459
+ *
3460
+ * Get the first track of the queue.
3461
+ * @returns {HoshimiTrack | null} The first track of the queue.
3462
+ * @example
3463
+ * ```ts
3464
+ * const queue = player.queue;
3465
+ *
3466
+ * console.log(queue.shift()); // null
3467
+ * queue.add(track);
3468
+ *
3469
+ * console.log(queue.shift()); // track
3470
+ * queue.add(track2);
3471
+ * ```
3472
+ */
3473
+ shift() {
3474
+ return this.tracks.shift() ?? null;
3475
+ }
3476
+ /**
3477
+ *
3478
+ * Add tracks to the beginning of the queue.
3479
+ * @param {Track[]} tracks The tracks to add.
3480
+ * @returns {this} The queue instance.
3481
+ * @example
3482
+ * ```ts
3483
+ * const queue = player.queue;
3484
+ *
3485
+ * console.log(queue.size); // 0
3486
+ * queue.unshift(track);
3487
+ *
3488
+ * console.log(queue.size); // 1
3489
+ * queue.unshift([track1, track2]);
3490
+ *
3491
+ * console.log(queue.size); // 3
3492
+ * console.log(queue.tracks); // [track1, track2, track]
3493
+ * ```
3494
+ */
3495
+ unshift(...tracks) {
3496
+ this.tracks.unshift(...tracks);
3497
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, `[Queue] -> [Unshift] Added ${this.tracks.length} tracks to the queue.`);
3498
+ return this;
3499
+ }
3500
+ /**
3501
+ *
3502
+ * Shuffle the queue.
3503
+ * @returns {this} The queue instance.
3504
+ * @example
3505
+ * ```ts
3506
+ * const queue = player.queue;
3507
+ *
3508
+ * console.log(queue.size); // 0
3509
+ * queue.add(track);
3510
+ * queue.add([track1, track2]);
3511
+ *
3512
+ * console.log(queue.size); // 3
3513
+ * console.log(queue.tracks); // [track, track1, track2]
3514
+ *
3515
+ * queue.shuffle();
3516
+ * console.log(queue.tracks); // [track2, track, track1]
3517
+ * ```
3518
+ */
3519
+ shuffle() {
3520
+ if (this.size <= 1) return this;
3521
+ if (this.size === 2) [this.tracks[0], this.tracks[1]] = [this.tracks[1], this.tracks[0]];
3522
+ else {
3523
+ for (let i = this.tracks.length - 1; i > 0; i--) {
3524
+ const j = Math.floor(Math.random() * (i + 1));
3525
+ [this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
3526
+ }
3527
+ }
3528
+ this.player.manager.emit("queueUpdate" /* QueueUpdate */, this.player, this);
3529
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, "[Queue] -> [Shuffle] Shuffled the queue.");
3530
+ return this;
3531
+ }
3532
+ /**
3533
+ *
3534
+ * Clear the queue.
3535
+ * @returns {this} The queue instance.
3536
+ * @example
3537
+ * ```ts
3538
+ * const queue = player.queue;
3539
+ *
3540
+ * console.log(queue.size); // 0
3541
+ * queue.add(track);
3542
+ * queue.add([track1, track2]);
3543
+ *
3544
+ * console.log(queue.size); // 3
3545
+ * queue.clear();
3546
+ * console.log(queue.size); // 0
3547
+ * ```
3548
+ */
3549
+ clear() {
3550
+ this.tracks = [];
3551
+ this.history = [];
3552
+ this.current = null;
3553
+ this.player.manager.emit("queueUpdate" /* QueueUpdate */, this.player, this);
3554
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, "[Queue] -> [Clear] Cleared the queue.");
3555
+ return this;
3556
+ }
3557
+ /**
3558
+ *
3559
+ * Move a track to a specific position in the queue.
3560
+ * @param {Track} track The track to move.
3561
+ * @param {number} to The position to move.
3562
+ * @returns {this} The queue instance.
3563
+ */
3564
+ move(track, to) {
3565
+ const index = this.tracks.indexOf(track);
3566
+ if (index === -1) return this;
3567
+ this.splice(index, 1);
3568
+ this.add(track, to - 1);
3569
+ this.player.manager.emit("queueUpdate" /* QueueUpdate */, this.player, this);
3570
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, `[Queue] -> [Move] Moved track ${track.info.title} to position ${to}.`);
3571
+ return this;
3572
+ }
3573
+ /**
3574
+ *
3575
+ * Delete tracks from the queue.
3576
+ * @param {number} start The start index.
3577
+ * @param {number} deleteCount The number of tracks to delete.
3578
+ * @param {Track | Track[]} [tracks] The tracks to add.
3579
+ * @returns {this} The queue instance.
3580
+ */
3581
+ splice(start, deleteCount, tracks) {
3582
+ if (!this.size && tracks) this.add(tracks);
3583
+ if (tracks) this.tracks.splice(start, deleteCount, ...Array.isArray(tracks) ? tracks : [tracks]);
3584
+ else this.tracks.splice(start, deleteCount);
3585
+ this.player.manager.emit("queueUpdate" /* QueueUpdate */, this.player, this);
3586
+ this.player.manager.emit("debug" /* Debug */, 5 /* Queue */, `[Queue] -> [Splice] Removed ${deleteCount} tracks from the queue.`);
3587
+ return this;
3588
+ }
3589
+ /**
3590
+ *
3591
+ * Convert the queue to a JSON object.
3592
+ * @returns {QueueJson} The queue JSON object.
3593
+ */
3594
+ toJSON() {
3595
+ const max = this.player.manager.options.queueOptions.maxPreviousTracks;
3596
+ if (this.history.length > max) this.history.splice(max, this.history.length);
3597
+ return {
3598
+ tracks: this.tracks,
3599
+ history: this.history,
3600
+ current: this.current
3601
+ };
3602
+ }
3603
+ };
3604
+
3605
+ // src/types/Structures.ts
3606
+ var Structures = {
3607
+ Player(...args) {
3608
+ return new Player(...args);
3609
+ },
3610
+ Rest(...args) {
3611
+ return new Rest(...args);
3612
+ },
3613
+ Node(...args) {
3614
+ return new Node(...args);
3615
+ },
3616
+ Queue(...args) {
3617
+ return new Queue(...args);
3618
+ },
3619
+ LyricsManager(...args) {
3620
+ return new LyricsManager(...args);
3621
+ },
3622
+ NodeManager(...args) {
3623
+ return new NodeManager(...args);
3624
+ },
3625
+ FilterManager(...args) {
3626
+ return new FilterManager(...args);
3627
+ }
3628
+ };
3629
+
3630
+ // src/classes/node/Node.ts
3631
+ var Node = class {
3632
+ /**
3633
+ * The options for the node.
3634
+ * @type {Required<NodeOptions>}
3635
+ */
3636
+ options;
3637
+ /**
3638
+ * The REST for the node.
3639
+ * @type {RestStructure}
3640
+ */
3641
+ rest;
3642
+ /**
3643
+ * The manager for the node.
3644
+ * @type {NodeManagerStructure}
3645
+ */
3646
+ nodeManager;
3647
+ /**
3648
+ * The lyrics manager for the node.
3649
+ * @type {LyricsManager}
3650
+ */
3651
+ lyricsManager;
3652
+ /**
3653
+ * The delay between reconnect attempts.
3654
+ * @type {number}
3655
+ */
3656
+ retryDelay;
3657
+ /**
3658
+ * The amount of reconnect attempts left.
3659
+ * @type {number}
3660
+ */
3661
+ retryAmount;
3662
+ /**
3663
+ * The WebSocket for the node.
3664
+ * @type {WebSocket | null}
3665
+ */
3666
+ ws = null;
3667
+ /**
3668
+ * The state of the node.
3669
+ * @type {State}
3670
+ */
3671
+ state = 7 /* Idle */;
3672
+ /**
3673
+ * The session id of the node.
3674
+ */
3675
+ sessionId = null;
3676
+ /**
3677
+ * The interval for the reconnect.
3678
+ * @type {NodeJS.Timeout | null}
3679
+ */
3680
+ reconnectTimeout = null;
3681
+ /**
3682
+ * The public stats of the node.
3683
+ * @type {Stats | null}
3684
+ */
3685
+ stats = null;
3686
+ /**
3687
+ * The public info of the node.
3688
+ * @type {NodeInfo | null}
3689
+ */
3690
+ info = null;
3691
+ /**
3692
+ * The session of the node.
3693
+ * @type {NullableLavalinkSession}
3694
+ */
3695
+ session = {
3696
+ timeout: null,
3697
+ resuming: false
3698
+ };
3699
+ /**
3700
+ *
3701
+ * Create a new Lavalink node.
3702
+ * @param {NodeManagerStructure} nodeManager The manager for the node.
3703
+ * @param {NodeOptions} options The options for the node.
3704
+ * @example
3705
+ * ```ts
3706
+ * const node = new Node(nodeManager, {
3707
+ * host: "localhost",
3708
+ * port: 2333,
3709
+ * password: "youshallnotpass",
3710
+ * id: "node1",
3711
+ * secure: false,
3712
+ * retryAmount: 5,
3713
+ * retryDelay: 20000,
3714
+ * restTimeout: 10000,
3715
+ * sessionId: null,
3716
+ * });
3717
+ *
3718
+ * node.connect();
3719
+ * console.log(node.id); // node1
3720
+ * console.log(node.address); // ws://localhost:2333/v4/websocket
3721
+ * console.log(node.penalties); // the penalties of the node
3722
+ * console.log(node.state); // the state of the node
3723
+ * ```
3724
+ */
3725
+ constructor(nodeManager, options) {
3726
+ this.options = {
3727
+ ...options,
3728
+ sessionId: options.sessionId ?? "",
3729
+ id: options.id ?? `${options.host}:${options.port}`,
3730
+ restTimeout: options.restTimeout ?? 1e4,
3731
+ secure: options.secure ?? false,
3732
+ retryAmount: options.retryAmount ?? 5,
3733
+ retryDelay: options.retryDelay ?? 2e4
3734
+ };
3735
+ this.retryAmount = this.options.retryAmount;
3736
+ this.retryDelay = this.options.retryDelay;
3737
+ this.nodeManager = nodeManager;
3738
+ if (this.options.secure && this.options.port !== 443) this.options.port = 443;
3739
+ this.rest = Structures.Rest(this);
3740
+ this.lyricsManager = Structures.LyricsManager(this);
3741
+ }
3742
+ /**
3743
+ * The decode methods for the node.
3744
+ * @type {DecodeMethods}
3745
+ * @readonly
3746
+ */
3747
+ decode = {
3748
+ single: async (track, requester) => {
3749
+ const raw = await this.rest.request({
3750
+ endpoint: "/decodetrack",
3751
+ params: { encodedTrack: track }
3752
+ });
3753
+ return new Track(raw, requester);
3754
+ },
3755
+ multiple: async (tracks, requester) => {
3756
+ const raw = await this.rest.request({
3757
+ endpoint: "/decodetracks",
3758
+ method: "POST" /* Post */,
3759
+ body: JSON.stringify(tracks)
3760
+ }) ?? [];
3761
+ return raw.map((track) => new Track(track, requester));
3762
+ }
3763
+ };
3764
+ /**
3765
+ * The id of the node.
3766
+ * @type {string}
3767
+ * @readonly
3768
+ * @example
3769
+ * ```ts
3770
+ * const node = manager.nodeManager.get("node1");
3771
+ * if (node) {
3772
+ * console.log(node.id); // node1
3773
+ * }
3774
+ * ```
3775
+ */
3776
+ get id() {
3777
+ return this.options.id;
3778
+ }
3779
+ /**
3780
+ * The socket address to connect the node.
3781
+ * @type {string}
3782
+ * @readonly
3783
+ * @example
3784
+ * ```ts
3785
+ * const node = manager.nodeManager.get("node1");
3786
+ * if (node) {
3787
+ * console.log(node.id); // node1
3788
+ * console.log(node.address); // ws://localhost:2333/v4/websocket
3789
+ * }
3790
+ * }
3791
+ */
3792
+ get address() {
3793
+ return `${this.options.secure ? "wss" : "ws"}://${this.options.host}:${this.options.port}/${this.rest.version}/websocket`;
3794
+ }
3795
+ /**
3796
+ * The penalties of the node.
3797
+ * @type {number}
3798
+ * @readonly
3799
+ * @example
3800
+ * ```ts
3801
+ * const node = manager.nodeManager.get("node1");
3802
+ * if (node) {
3803
+ * console.log(node.id); // node1
3804
+ * console.log(node.address); // ws://localhost:2333/v4/websocket
3805
+ * console.log(node.penalties); // the penalties of the node
3806
+ * }
3807
+ * ```
3808
+ */
3809
+ get penalties() {
3810
+ if (!this.stats) return 0;
3811
+ const { players, cpu, frameStats } = this.stats;
3812
+ const cpuPenalty = Math.round(1.05 ** (100 * cpu.systemLoad) * 10 - 10);
3813
+ const framePenalty = frameStats ? frameStats.deficit + frameStats.nulled * 2 : 0;
3814
+ return players + cpuPenalty + framePenalty;
3815
+ }
3816
+ /**
3817
+ *
3818
+ * Search for a query.
3819
+ * @param {SearchQuery} search The query to search for.
3820
+ * @returns {Promise<LavalinkSearchResponse | null>}
3821
+ * @example
3822
+ * ```ts
3823
+ * const node = manager.nodeManager.get("node1");
3824
+ * if (node) {
3825
+ * const search = await node.search({
3826
+ * query: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
3827
+ * engine: SearchEngines.Youtube,
3828
+ * });
3829
+ *
3830
+ * console.log(search); // the search result
3831
+ * }
3832
+ * ```
3833
+ */
3834
+ search(search) {
3835
+ search.engine ??= this.nodeManager.manager.options.defaultSearchEngine;
3836
+ const identifier = validateQuery(search);
3837
+ return this.rest.request({
3838
+ endpoint: "/loadtracks",
3839
+ params: {
3840
+ identifier,
3841
+ ...search.params
3842
+ }
3843
+ });
3844
+ }
3845
+ /**
3846
+ * Connect the node to the websocket.
3847
+ * @returns {void}
3848
+ * @throws {NodeError} If the client data is not valid
3849
+ * @example
3850
+ * ```ts
3851
+ * const node = manager.nodeManager.get("node1");
3852
+ * if (node) node.connect();
3853
+ * ```
3854
+ */
3855
+ connect() {
3856
+ if (!this.nodeManager.manager.options.client)
3857
+ throw new NodeError({
3858
+ message: "No valid client data provided.",
3859
+ id: this.id
3860
+ });
3861
+ if (!this.nodeManager.manager.options.client.id)
3862
+ throw new NodeError({
3863
+ message: "No valid client id provided.",
3864
+ id: this.id
3865
+ });
3866
+ this.state = 1 /* Connecting */;
3867
+ const headers = {
3868
+ Authorization: this.options.password,
3869
+ "User-Id": this.nodeManager.manager.options.client.id,
3870
+ "Client-Name": this.nodeManager.manager.options.client.username,
3871
+ "User-Agent": this.rest.userAgent
3872
+ };
3873
+ if (this.options.sessionId) {
3874
+ headers["Session-Id"] = this.options.sessionId;
3875
+ this.sessionId = this.options.sessionId;
3876
+ this.nodeManager.manager.emit(
3877
+ "debug" /* Debug */,
3878
+ 2 /* Node */,
3879
+ `[Socket] -> [${this.id}]: The session id is present. | Session: ${this.sessionId} | Resuming: ${this.session.resuming}`
3880
+ );
3881
+ }
3882
+ this.ws = new import_ws.WebSocket(this.address, { headers: { ...headers } });
3883
+ this.ws.on("upgrade", onOpen.bind(this));
3884
+ this.ws.on("close", onClose.bind(this));
3885
+ this.ws.on("error", onError.bind(this));
3886
+ this.ws.on("message", onMessage.bind(this));
3887
+ this.nodeManager.manager.emit(
3888
+ "debug" /* Debug */,
3889
+ 2 /* Node */,
3890
+ `[Socket] -> [${this.id}]: Connecting to ${this.address} | State: ${this.state} | Session: ${this.sessionId} | Resumed: ${this.session.resuming} | Penalties: ${this.penalties} | Reconnects: ${this.retryAmount} | Headers: ${JSON.stringify(headers)}`
3891
+ );
3892
+ }
3893
+ /**
3894
+ *
3895
+ * Stop the track in player for the guild.
3896
+ * @param {string} guildId the guild id to stop the player
3897
+ * @returns {Promise<LavalinkPlayer | null>}
3898
+ * @example
3899
+ * ```ts
3900
+ * const node = manager.nodeManager.get("node1");
3901
+ * if (node) {
3902
+ * const player = await node.stopPlayer("guildId");
3903
+ * console.log(player); // the lavalink player
3904
+ * }
3905
+ * ```
3906
+ */
3907
+ stopPlayer(guildId) {
3908
+ return this.rest.stopPlayer(guildId);
3909
+ }
3910
+ /**
3911
+ *
3912
+ * Update the player data.
3913
+ * @param {Partial<UpdatePlayerInfo>} data The player data to update.
3914
+ * @returns {Promise<LavalinkPlayer | null>}
3915
+ * @example
3916
+ * ```ts
3917
+ * const node = manager.nodeManager.get("node1");
3918
+ * if (node) {
3919
+ * const player = await node.updatePlayer({
3920
+ * guildId: "guildId",
3921
+ * noReplace: true,
3922
+ * playerOptions: {
3923
+ * paused: false,
3924
+ * track: { encoded: "encoded track" },
3925
+ * },
3926
+ * });
3927
+ *
3928
+ * console.log(player); // the lavalink player
3929
+ * }
3930
+ * ```
3931
+ */
3932
+ updatePlayer(data) {
3933
+ return this.rest.updatePlayer(data);
3934
+ }
3935
+ /**
3936
+ * Destroy the player.
3937
+ * @returns {Promise<void>}
3938
+ * @param {string} guildId The guild id to destroy the player.
3939
+ * @example
3940
+ * ```ts
3941
+ * const node = manager.nodeManager.get("node1");
3942
+ * if (node) await node.destroyPlayer("guildId");
3943
+ * console.log("Player destroyed");
3944
+ * ```
3945
+ */
3946
+ destroyPlayer(guildId) {
3947
+ return this.rest.destroyPlayer(guildId);
3948
+ }
3949
+ /**
3950
+ *
3951
+ * Disconnect the node from the websocket.
3952
+ * @param {NodeDisconnectInfo} [disconnect] The disconnect options for the node.
3953
+ * @returns {void}
3954
+ * @example
3955
+ * ```ts
3956
+ * const node = manager.nodeManager.get("node1");
3957
+ * if (node) node.disconnect();
3958
+ * console.log("Node disconnected");
3959
+ * ```
3960
+ */
3961
+ disconnect(disconnect = {}) {
3962
+ if (this.state !== 2 /* Connected */) return;
3963
+ this.ws?.close(disconnect.code, disconnect.reason);
3964
+ this.ws?.removeAllListeners();
3965
+ this.ws = null;
3966
+ this.state = 3 /* Disconnected */;
3967
+ this.retryAmount = this.options.retryAmount;
3968
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
3969
+ this.nodeManager.manager.emit("nodeDisconnect" /* NodeDisconnect */, this);
3970
+ }
3971
+ /**
3972
+ *
3973
+ * Destroy the node.
3974
+ * @param {NodeDestroyInfo} [destroy] The destroy options for the node.
3975
+ * @returns {void}
3976
+ * @example
3977
+ * ```ts
3978
+ * const node = manager.nodeManager.get("node1");
3979
+ * if (node) node.destroy();
3980
+ * console.log("Node destroyed");
3981
+ * ```
3982
+ */
3983
+ destroy(destroy = {}) {
3984
+ if (this.state !== 2 /* Connected */) return;
3985
+ this.ws?.close(destroy.code, destroy.reason);
3986
+ this.ws?.removeAllListeners();
3987
+ this.ws = null;
3988
+ this.state = 6 /* Destroyed */;
3989
+ this.retryAmount = 0;
3990
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
3991
+ this.nodeManager.manager.emit("nodeDestroy" /* NodeDestroy */, this, destroy);
3992
+ this.nodeManager.delete(this.id);
3993
+ }
3994
+ /**
3995
+ *
3996
+ * Update the session for the node
3997
+ * @param {boolean} resuming Enable resuming for the session.
3998
+ * @param {number | null} timeout The timeout for the session.
3999
+ * @returns {Promise<LavalinkSession | null>}
4000
+ * @example
4001
+ * ```ts
4002
+ * const node = manager.nodeManager.get("node1");
4003
+ * if (node) {
4004
+ * const session = await node.updateSession(true, 60);
4005
+ * console.log(session); // the lavalink session
4006
+ * }
4007
+ * ```
4008
+ */
4009
+ async updateSession(resuming, timeout = null) {
4010
+ if (!this.sessionId) return null;
4011
+ const session = await this.rest.updateSession(resuming, timeout);
4012
+ if (session) this.session = session;
4013
+ return session;
4014
+ }
4015
+ /**
4016
+ * Reconnect the node.
4017
+ * @returns {void}
4018
+ * @throws {NodeError} If the node is not connected
4019
+ * @example
4020
+ * ```ts
4021
+ * const node = manager.nodeManager.get("node1");
4022
+ * if (node) node.reconnect();
4023
+ * console.log("Node reconnected");
4024
+ * ```
4025
+ */
4026
+ reconnect() {
4027
+ this.state = 7 /* Idle */;
4028
+ this.nodeManager.manager.emit("nodeReconnecting" /* NodeReconnecting */, this, this.retryAmount, this.retryDelay);
4029
+ this.reconnectTimeout = setTimeout(() => {
4030
+ this.reconnectTimeout = null;
4031
+ if (this.retryAmount === 0) {
4032
+ this.destroy({
4033
+ code: 1e3 /* NormalClosure */,
4034
+ reason: "Node-Destroy" /* Destroy */
4035
+ });
4036
+ this.nodeManager.manager.emit(
4037
+ "nodeError" /* NodeError */,
4038
+ this,
4039
+ new NodeError({
4040
+ message: `Failed to reconnect after ${this.options.retryAmount} retries.`,
4041
+ id: this.id
4042
+ })
4043
+ );
4044
+ return;
4045
+ }
4046
+ this.ws?.removeAllListeners();
4047
+ this.ws = null;
4048
+ this.retryAmount--;
4049
+ this.connect();
4050
+ }, this.options.retryDelay);
4051
+ }
4052
+ /**
4053
+ *
4054
+ * Convert the node to JSON.
4055
+ * @returns {NodeJson} The JSON representation of the node.
4056
+ * @example
4057
+ * ```ts
4058
+ * const node = manager.nodeManager.get("node1");
4059
+ * if (node) {
4060
+ * const json = node.toJSON();
4061
+ * console.log(json);
4062
+ * }
4063
+ * ```
4064
+ */
4065
+ toJSON() {
4066
+ return {
4067
+ id: this.id,
4068
+ sessionId: this.sessionId
4069
+ };
4070
+ }
4071
+ };
4072
+
4073
+ // src/classes/queue/adapters/memory.ts
4074
+ var MemoryAdapter = class extends StorageAdapter {
4075
+ /**
4076
+ * Memory storage.
4077
+ * @type {Map<string, QueueJson>}
4078
+ * @private
4079
+ * @readonly
4080
+ * @internal
4081
+ */
4082
+ storage = /* @__PURE__ */ new Map();
4083
+ get(key) {
4084
+ return this.parse(this.storage.get(key));
4085
+ }
4086
+ set(key, value) {
4087
+ this.storage.set(key, this.stringify(value));
4088
+ }
4089
+ delete(key) {
4090
+ return this.storage.delete(key);
4091
+ }
4092
+ clear() {
4093
+ this.storage.clear();
4094
+ }
4095
+ has(key) {
4096
+ return this.storage.has(key);
4097
+ }
4098
+ parse(value) {
4099
+ return value;
4100
+ }
4101
+ stringify(value) {
4102
+ return value;
4103
+ }
4104
+ };
4105
+
4106
+ // src/classes/Hoshimi.ts
4107
+ var import_node_events = require("events");
4108
+
4109
+ // src/util/functions/autoplay.ts
4110
+ var maxTracks = 10;
4111
+ async function autoplayFn(player, lastTrack) {
4112
+ if (!lastTrack) return;
4113
+ const isEnabled = !!player.data.get("enabledAutoplay") || player.manager.options.queueOptions.autoPlay;
4114
+ if (!isEnabled) return;
4115
+ const filter = (tracks) => tracks.filter(
4116
+ (track) => !(player.queue.history.some((t) => t.info.identifier === track.info.identifier) || lastTrack.info.identifier === track.info.identifier)
4117
+ );
4118
+ switch (lastTrack.info.sourceName) {
4119
+ case "spotify" /* Spotify */: {
4120
+ const filtered = player.queue.history.filter(({ info }) => info.sourceName === "spotify" /* Spotify */).slice(0, 1);
4121
+ if (!filtered.length) filtered.push(lastTrack);
4122
+ const ids = filtered.map(
4123
+ ({ info }) => info.identifier ?? info.uri?.split("/").reverse()?.[0] ?? info.uri?.split("/").reverse()?.[1]
4124
+ );
4125
+ const res = await player.search({
4126
+ query: `seed_tracks=${ids.join(",")}`,
4127
+ engine: "sprec" /* SpotifyRecommendations */,
4128
+ requester: lastTrack.requester
4129
+ });
4130
+ if (res.tracks.length) {
4131
+ const index = Math.floor(Math.random() * res.tracks.length);
4132
+ const track = filter(res.tracks)[index];
4133
+ if (!track) return;
4134
+ player.queue.add(track);
4135
+ }
4136
+ break;
4137
+ }
4138
+ case "youtube" /* Youtube */:
4139
+ case "youtubemusic" /* YoutubeMusic */: {
4140
+ const search = `https://www.youtube.com/watch?v=${lastTrack.info.identifier}&list=RD${lastTrack.info.identifier}`;
4141
+ const res = await player.search({
4142
+ query: search,
4143
+ requester: lastTrack.requester
4144
+ });
4145
+ if (res.tracks.length) {
4146
+ const random = Math.floor(Math.random() * res.tracks.length);
4147
+ const tracks = filter(res.tracks).slice(random, random + maxTracks);
4148
+ player.queue.add(tracks);
4149
+ }
4150
+ }
4151
+ }
4152
+ }
4153
+
4154
+ // src/classes/Hoshimi.ts
4155
+ var Hoshimi = class extends import_node_events.EventEmitter {
4156
+ /**
4157
+ * The options for the manager.
4158
+ * @type {HoshimiOptions}
4159
+ */
4160
+ options;
4161
+ /**
4162
+ * The players for the manager.
4163
+ * @type {Collection<string, PlayerStructure>}
4164
+ * @readonly
4165
+ */
4166
+ players = new Collection();
4167
+ /**
4168
+ * THe node manager for the manager.
4169
+ * @type {NodeManager}
4170
+ * @readonly
4171
+ */
4172
+ nodeManager;
4173
+ /**
4174
+ * If the manager is ready.
4175
+ * @type {boolean}
4176
+ */
4177
+ ready = false;
4178
+ /**
4179
+ * The constructor for the manager.
4180
+ * @param {HoshimiOptions} options The options for the manager.
4181
+ * @throws {ManagerError} If the options are not provided.
4182
+ * @example
4183
+ * ```ts
4184
+ * const manager = new Hoshimi({
4185
+ * nodes: [
4186
+ * {
4187
+ * host: "localhost",
4188
+ * port: 2333,
4189
+ * password: "youshallnotpass",
4190
+ * secure: false,
4191
+ * },
4192
+ * ],
4193
+ * client: {
4194
+ * id: "clientId",
4195
+ * username: "clientUsername",
4196
+ * },
4197
+ * defaultSearchEngine: SearchEngines.Youtube,
4198
+ * restOptions: {
4199
+ * resumeTimeout: 10000,
4200
+ * },
4201
+ * nodeOptions: {
4202
+ * userAgent: HoshimiAgent,
4203
+ * resumable: false,
4204
+ * resumeByLibrary: false,
4205
+ * },
4206
+ * queueOptions: {
4207
+ * maxPreviousTracks: 25,
4208
+ * autoplayFn: autoplayFn,
4209
+ * autoPlay: false,
4210
+ * },
4211
+ * });
4212
+ *
4213
+ * console.log(manager); // The manager instance
4214
+ * ```
4215
+ */
4216
+ constructor(options) {
4217
+ super();
4218
+ if (!options) throw new ManagerError("You must provide the options for the manager.");
4219
+ this.options = {
4220
+ ...options,
4221
+ defaultSearchEngine: options.defaultSearchEngine ?? "ytsearch" /* Youtube */,
4222
+ restOptions: {
4223
+ resumeTimeout: options.restOptions?.resumeTimeout ?? 1e4
4224
+ },
4225
+ nodeOptions: {
4226
+ userAgent: options.nodeOptions?.userAgent ?? HoshimiAgent,
4227
+ resumable: options.nodeOptions?.resumable ?? false,
4228
+ resumeByLibrary: options.nodeOptions?.resumeByLibrary ?? false,
4229
+ resumeTimeout: options.nodeOptions?.resumeTimeout ?? 1e4
4230
+ },
4231
+ queueOptions: {
4232
+ maxPreviousTracks: options.queueOptions?.maxPreviousTracks ?? 25,
4233
+ autoplayFn: options.queueOptions?.autoplayFn ?? autoplayFn,
4234
+ autoPlay: options.queueOptions?.autoPlay ?? false,
4235
+ storage: options.queueOptions?.storage ?? new MemoryAdapter()
4236
+ },
4237
+ client: {
4238
+ id: options.client?.id ?? "",
4239
+ username: options.client?.username ?? "hoshimi-client"
4240
+ }
4241
+ };
4242
+ validateManagerOptions(this.options);
4243
+ this.nodeManager = Structures.NodeManager(this);
4244
+ }
4245
+ /**
4246
+ * Check if the manager is useable.
4247
+ * @returns {boolean} If the manager is useable.
4248
+ * @example
4249
+ * ```ts
4250
+ * if (manager.isUseable()) {
4251
+ * console.log("The manager is useable.");
4252
+ * } else {
4253
+ * console.log("The manager is not useable.");
4254
+ * }
4255
+ * ```
4256
+ */
4257
+ isUseable() {
4258
+ const nodes = this.nodeManager.nodes.filter((node) => node.state === 2 /* Connected */);
4259
+ return this.ready && nodes.length > 0;
4260
+ }
4261
+ /**
4262
+ *
4263
+ * Get the player for the guild.
4264
+ * @param {string} guildId The guild id to get the player.
4265
+ * @returns {PlayerStructure | undefined} The player for the guild.
4266
+ * @example
4267
+ * ```ts
4268
+ * const player = manager.getPlayer(guildId);
4269
+ * if (player) {
4270
+ * console.log(`The player for ${guildId} is ${player}`);
4271
+ * } else {
4272
+ * console.log(`The player for ${guildId} is not found.`);
4273
+ * }
4274
+ * ```
4275
+ */
4276
+ getPlayer(guildId) {
4277
+ return this.players.get(guildId);
4278
+ }
4279
+ /**
4280
+ * Delete the player for the guild.
4281
+ * @param {string} guildId The guild id to delete the player.
4282
+ * @returns {boolean} If the player was deleted.
4283
+ * @example
4284
+ * ```ts
4285
+ * const player = manager.deletePlayer(guildId);
4286
+ * if (player) {
4287
+ * console.log(`The player for ${guildId} was deleted.`);
4288
+ * } else {
4289
+ * console.log(`The player for ${guildId} was not found.`);
4290
+ * }
4291
+ * ```
4292
+ */
4293
+ deletePlayer(guildId) {
4294
+ return this.players.delete(guildId);
4295
+ }
4296
+ /**
4297
+ *
4298
+ * Handle the raw packet for voice state and voice server updates.
4299
+ * @param {GatewayPackets} packet The packet to handle
4300
+ * @returns {Promise<void>}
4301
+ * @example
4302
+ * ```ts
4303
+ * client.on("raw", (packet) => manager.updateVoiceState(packet));
4304
+ * ```
4305
+ */
4306
+ async updateVoiceState(packet) {
4307
+ if (!this.ready) {
4308
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Manager] The manager is not ready.");
4309
+ return;
4310
+ }
4311
+ if (!("t" in packet)) {
4312
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Voice] The packet does not have a type.");
4313
+ return;
4314
+ }
4315
+ switch (packet.t) {
4316
+ case "CHANNEL_DELETE": {
4317
+ const data = packet.d;
4318
+ const player = this.getPlayer(data.guild_id);
4319
+ if (!player) {
4320
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Voice] The player is not found.");
4321
+ return;
4322
+ }
4323
+ if (data.id === player.voiceId) {
4324
+ this.emit(
4325
+ "debug" /* Debug */,
4326
+ 3 /* Player */,
4327
+ `[Player] -> [Voice] The channel ${data.id} was deleted, disconnecting the player.`
4328
+ );
4329
+ await player.destroy("Player-VoiceChannelDeleted" /* VoiceChannelDeleted */);
4330
+ } else {
4331
+ this.emit(
4332
+ "debug" /* Debug */,
4333
+ 3 /* Player */,
4334
+ `[Player] -> [Voice] The channel ${data.id} was deleted, but it is not the player's channel.`
4335
+ );
4336
+ }
4337
+ break;
4338
+ }
4339
+ case "VOICE_SERVER_UPDATE":
4340
+ case "VOICE_STATE_UPDATE":
4341
+ {
4342
+ const data = packet.d;
4343
+ if (!("guild_id" in data)) {
4344
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Voice] The guild id is missing.");
4345
+ return;
4346
+ }
4347
+ if ("user_id" in data && data.user_id !== this.options.client?.id) {
4348
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Voice] The user id does not match the client id.");
4349
+ return;
4350
+ }
4351
+ const player = this.getPlayer(data.guild_id);
4352
+ if (!player) {
4353
+ this.emit("debug" /* Debug */, 3 /* Player */, "[Player] -> [Voice] The player is not found.");
4354
+ return;
4355
+ }
4356
+ if ("session_id" in data) player.voice.sessionId = data.session_id;
4357
+ if ("token" in data) player.voice.token = data.token;
4358
+ if ("endpoint" in data) player.voice.endpoint = data.endpoint;
4359
+ if (player.voice.sessionId && player.voice.token && player.voice.endpoint) {
4360
+ await player.node.updatePlayer({
4361
+ guildId: data.guild_id,
4362
+ playerOptions: {
4363
+ voice: player.voice
4364
+ }
4365
+ });
4366
+ this.emit(
4367
+ "debug" /* Debug */,
4368
+ 3 /* Player */,
4369
+ `[Player] -> [Voice] Updated the player voice for: ${data.guild_id} | Session: ${player.voice.sessionId} | Token: ${player.voice.token} | Endpoint: ${player.voice.endpoint}`
4370
+ );
4371
+ return;
4372
+ }
4373
+ this.emit("debug" /* Debug */, 3 /* Player */, `[Player] -> [Voice] The player voice is missing for: ${data.guild_id}`);
4374
+ }
4375
+ break;
4376
+ default:
4377
+ break;
4378
+ }
4379
+ }
4380
+ /**
4381
+ *
4382
+ * Initialize the manager.
4383
+ * @param {ClientData} info The client data to use.
4384
+ * @returns {void}
4385
+ * @example
4386
+ * ```ts
4387
+ * manager.init({
4388
+ * id: "clientId",
4389
+ * username: "clientUsername",
4390
+ * });
4391
+ * ```
4392
+ */
4393
+ init(info) {
4394
+ if (this.ready) return;
4395
+ this.options.client = {
4396
+ ...this.options.client,
4397
+ ...info
4398
+ };
4399
+ if (!this.options.client.id) throw new ManagerError("You must provide the client id.");
4400
+ if (typeof this.options.client.id !== "string") throw new OptionError("The client info 'info.client.id': must be a string.");
4401
+ let amount = 0;
4402
+ for (const options of this.options.nodes) {
4403
+ const node = this.nodeManager.create(options);
4404
+ try {
4405
+ node.connect();
4406
+ amount++;
4407
+ } catch (error) {
4408
+ this.emit("nodeError" /* NodeError */, node, error);
4409
+ }
4410
+ }
4411
+ this.ready = amount > 0;
4412
+ this.emit(
4413
+ "debug" /* Debug */,
4414
+ 3 /* Player */,
4415
+ `[Manager] -> [Init] The manager is ready: ${this.ready} | Nodes: ${amount} of ${this.nodeManager.nodes.size}`
4416
+ );
4417
+ }
4418
+ /**
4419
+ *
4420
+ * Create a new player.
4421
+ * @param {PlayerOptions} options The options for the player.
4422
+ * @returns {Player} The created player.
4423
+ * @example
4424
+ * ```ts
4425
+ * const player = manager.createPlayer({
4426
+ * guildId: "guildId",
4427
+ * voiceId: "voiceId",
4428
+ * });
4429
+ *
4430
+ * console.log(player); // The created player
4431
+ *
4432
+ * player.connect();
4433
+ * player.play(track);
4434
+ * ```
4435
+ */
4436
+ createPlayer(options) {
4437
+ const oldPlayer = this.getPlayer(options.guildId);
4438
+ if (oldPlayer) return oldPlayer;
4439
+ const player = Structures.Player(this, options);
4440
+ this.players.set(options.guildId, player);
4441
+ this.emit("playerCreate" /* PlayerCreate */, player);
4442
+ return player;
4443
+ }
4444
+ /**
4445
+ *
4446
+ * Search for a track or playlist.
4447
+ * @param {SearchOptions} options The options for the search.
4448
+ * @returns {Promise<QueryResult>} The search result.
4449
+ * @example
4450
+ * ```ts
4451
+ * const result = await manager.search({
4452
+ * query: "track name",
4453
+ * engine: SearchEngines.Youtube,
4454
+ * });
4455
+ *
4456
+ * console.log(result); // The search result
4457
+ * ```
4458
+ */
4459
+ async search(options) {
4460
+ let node = null;
4461
+ if (options.node) {
4462
+ const nodeId = typeof options.node === "string" ? options.node : options.node.id;
4463
+ node = this.nodeManager.get(nodeId) ?? null;
4464
+ } else {
4465
+ node = this.nodeManager.getLeastUsed();
4466
+ }
4467
+ if (!node) throw new ManagerError("No nodes are available.");
4468
+ const res = await node.search(options);
4469
+ if (!res)
4470
+ return {
4471
+ loadType: "empty" /* Empty */,
4472
+ exception: null,
4473
+ playlist: null,
4474
+ pluginInfo: null,
4475
+ tracks: []
4476
+ };
4477
+ this.emit(
4478
+ "debug" /* Debug */,
4479
+ 1 /* Manager */,
4480
+ `[Manager] -> [Search] Searching for: ${options.query} (${options.engine ?? "unknown"}) | Result: ${JSON.stringify(res)}`
4481
+ );
4482
+ switch (res.loadType) {
4483
+ case "empty" /* Empty */: {
4484
+ return {
4485
+ loadType: res.loadType,
4486
+ exception: null,
4487
+ playlist: null,
4488
+ pluginInfo: null,
4489
+ tracks: []
4490
+ };
4491
+ }
4492
+ case "error" /* Error */: {
4493
+ return {
4494
+ loadType: res.loadType,
4495
+ exception: res.data,
4496
+ playlist: null,
4497
+ pluginInfo: null,
4498
+ tracks: []
4499
+ };
4500
+ }
4501
+ case "playlist" /* Playlist */: {
4502
+ return {
4503
+ loadType: res.loadType,
4504
+ exception: null,
4505
+ playlist: res.data,
4506
+ pluginInfo: res.data.pluginInfo,
4507
+ tracks: res.data.tracks.map((t) => new Track(t, options.requester))
4508
+ };
4509
+ }
4510
+ case "search" /* Search */: {
4511
+ return {
4512
+ loadType: res.loadType,
4513
+ exception: null,
4514
+ playlist: null,
4515
+ pluginInfo: null,
4516
+ tracks: res.data.map((t) => new Track(t, options.requester))
4517
+ };
4518
+ }
4519
+ case "track" /* Track */: {
4520
+ return {
4521
+ loadType: res.loadType,
4522
+ exception: null,
4523
+ playlist: null,
4524
+ pluginInfo: res.data.pluginInfo,
4525
+ tracks: [new Track(res.data, options.requester)]
4526
+ };
4527
+ }
4528
+ }
4529
+ }
4530
+ };
4531
+ function createHoshimi(...args) {
4532
+ return new Hoshimi(...args);
4533
+ }
4534
+ // Annotate the CommonJS export names for ESM import in node:
4535
+ 0 && (module.exports = {
4536
+ AudioOutput,
4537
+ DSPXPluginFilter,
4538
+ DebugLevels,
4539
+ DestroyReasons,
4540
+ Events,
4541
+ FilterManager,
4542
+ FilterType,
4543
+ Hoshimi,
4544
+ HttpMethods,
4545
+ HttpStatusCodes,
4546
+ LavalinkPluginFilter,
4547
+ LoadType,
4548
+ LoopMode,
4549
+ ManagerError,
4550
+ MemoryAdapter,
4551
+ Node,
4552
+ NodeDestroyReasons,
4553
+ NodeError,
4554
+ NodeSortTypes,
4555
+ OpCodes,
4556
+ OptionError,
4557
+ Player,
4558
+ PlayerError,
4559
+ PlayerEventType,
4560
+ PluginInfoType,
4561
+ PluginNames,
4562
+ Queue,
4563
+ ResolveError,
4564
+ Rest,
4565
+ SearchEngines,
4566
+ Severity,
4567
+ SourceNames,
4568
+ State,
4569
+ StorageAdapter,
4570
+ StorageError,
4571
+ Structures,
4572
+ Track,
4573
+ TrackEndReason,
4574
+ UnresolvedTrack,
4575
+ WebsocketCloseCodes,
4576
+ createHoshimi
4577
+ });