ryanlink 1.0.2 → 2.0.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.
Files changed (62) hide show
  1. package/LICENSE +201 -37
  2. package/README.md +97 -370
  3. package/dist/index.cjs +4833 -0
  4. package/dist/index.d.cts +1703 -0
  5. package/dist/index.d.ts +1528 -1160
  6. package/dist/index.js +4262 -4197
  7. package/dist/index.mjs +4261 -4106
  8. package/package.json +87 -79
  9. package/dist/index.d.mts +0 -1335
  10. package/dist/index.js.map +0 -1
  11. package/dist/index.mjs.map +0 -1
  12. package/src/audio/AudioFilters.ts +0 -316
  13. package/src/audio/AudioQueue.ts +0 -782
  14. package/src/audio/AudioTrack.ts +0 -242
  15. package/src/audio/QueueController.ts +0 -252
  16. package/src/audio/TrackCollection.ts +0 -138
  17. package/src/audio/index.ts +0 -9
  18. package/src/config/defaults.ts +0 -223
  19. package/src/config/endpoints.ts +0 -99
  20. package/src/config/index.ts +0 -9
  21. package/src/config/patterns.ts +0 -55
  22. package/src/config/presets.ts +0 -400
  23. package/src/config/symbols.ts +0 -31
  24. package/src/core/PluginSystem.ts +0 -50
  25. package/src/core/RyanlinkPlayer.ts +0 -403
  26. package/src/core/index.ts +0 -6
  27. package/src/extensions/AutoplayExtension.ts +0 -283
  28. package/src/extensions/FairPlayExtension.ts +0 -154
  29. package/src/extensions/LyricsExtension.ts +0 -187
  30. package/src/extensions/PersistenceExtension.ts +0 -182
  31. package/src/extensions/SponsorBlockExtension.ts +0 -81
  32. package/src/extensions/index.ts +0 -9
  33. package/src/index.ts +0 -19
  34. package/src/lavalink/ConnectionPool.ts +0 -326
  35. package/src/lavalink/HttpClient.ts +0 -316
  36. package/src/lavalink/LavalinkConnection.ts +0 -409
  37. package/src/lavalink/index.ts +0 -7
  38. package/src/metadata.ts +0 -88
  39. package/src/types/api/Rest.ts +0 -949
  40. package/src/types/api/Websocket.ts +0 -463
  41. package/src/types/api/index.ts +0 -6
  42. package/src/types/audio/FilterManager.ts +0 -29
  43. package/src/types/audio/Queue.ts +0 -4
  44. package/src/types/audio/QueueManager.ts +0 -30
  45. package/src/types/audio/index.ts +0 -7
  46. package/src/types/common.ts +0 -63
  47. package/src/types/core/Player.ts +0 -322
  48. package/src/types/core/index.ts +0 -5
  49. package/src/types/index.ts +0 -6
  50. package/src/types/lavalink/Node.ts +0 -173
  51. package/src/types/lavalink/NodeManager.ts +0 -34
  52. package/src/types/lavalink/REST.ts +0 -144
  53. package/src/types/lavalink/index.ts +0 -32
  54. package/src/types/voice/VoiceManager.ts +0 -176
  55. package/src/types/voice/index.ts +0 -5
  56. package/src/utils/helpers.ts +0 -169
  57. package/src/utils/index.ts +0 -6
  58. package/src/utils/validators.ts +0 -184
  59. package/src/voice/RegionSelector.ts +0 -184
  60. package/src/voice/VoiceConnection.ts +0 -458
  61. package/src/voice/VoiceSession.ts +0 -297
  62. package/src/voice/index.ts +0 -7
package/dist/index.cjs ADDED
@@ -0,0 +1,4833 @@
1
+ /* ryanlink v2.0.0 - High-performance, flexible and feature-rich lavalink-v4 wrapper for Discord bots, | Apache-2.0 License | https://github.com/ryanwtf7/ryanlink.git */
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AudioNodeSymbol: () => AudioNodeSymbol,
34
+ AudioQueueSymbol: () => AudioQueueSymbol,
35
+ AudioTrackSymbol: () => AudioTrackSymbol,
36
+ Autoplay: () => Autoplay,
37
+ BuiltinSources: () => BuiltinSources,
38
+ DebugEvents: () => DebugEvents,
39
+ DefaultQueueStore: () => DefaultQueueStore,
40
+ DestroyReasons: () => DestroyReasons,
41
+ DisconnectReasons: () => DisconnectReasons,
42
+ EQList: () => EQList,
43
+ FilterManager: () => FilterManager,
44
+ LinkMatchers: () => LinkMatchers,
45
+ MiniMap: () => MiniMap,
46
+ NodeLinkExclusiveEvents: () => NodeLinkExclusiveEvents,
47
+ NodeLinkNode: () => NodeLinkNode,
48
+ NodeManager: () => NodeManager,
49
+ Player: () => Player,
50
+ Queue: () => Queue,
51
+ QueueSaver: () => QueueSaver,
52
+ RecommendationsStrings: () => RecommendationsStrings,
53
+ ReconnectionState: () => ReconnectionState,
54
+ RyanlinkManager: () => RyanlinkManager,
55
+ RyanlinkNode: () => RyanlinkNode,
56
+ RyanlinkUtils: () => RyanlinkUtils,
57
+ SourceMappings: () => SourceMappings,
58
+ UnresolvedAudioTrackSymbol: () => UnresolvedAudioTrackSymbol,
59
+ audioOutputsData: () => audioOutputsData,
60
+ parseConnectionUrl: () => parseConnectionUrl,
61
+ queueTrackEnd: () => queueTrackEnd,
62
+ safeStringify: () => safeStringify,
63
+ validSponsorBlocks: () => validSponsorBlocks,
64
+ version: () => version
65
+ });
66
+ module.exports = __toCommonJS(index_exports);
67
+
68
+ // src/core/Manager.ts
69
+ var import_node_events2 = require("events");
70
+
71
+ // src/config/Constants.ts
72
+ var DebugEvents = /* @__PURE__ */ ((DebugEvents2) => {
73
+ DebugEvents2["SetSponsorBlock"] = "SetSponsorBlock";
74
+ DebugEvents2["DeleteSponsorBlock"] = "DeleteSponsorBlock";
75
+ DebugEvents2["TrackEndReplaced"] = "TrackEndReplaced";
76
+ DebugEvents2["AutoplayExecution"] = "AutoplayExecution";
77
+ DebugEvents2["AutoplayNoSongsAdded"] = "AutoplayNoSongsAdded";
78
+ DebugEvents2["AutoplayThresholdSpamLimiter"] = "AutoplayThresholdSpamLimiter";
79
+ DebugEvents2["TriggerQueueEmptyInterval"] = "TriggerQueueEmptyInterval";
80
+ DebugEvents2["QueueEnded"] = "QueueEnded";
81
+ DebugEvents2["TrackStartNewSongsOnly"] = "TrackStartNewSongsOnly";
82
+ DebugEvents2["TrackStartNoTrack"] = "TrackStartNoTrack";
83
+ DebugEvents2["ResumingFetchingError"] = "ResumingFetchingError";
84
+ DebugEvents2["PlayerUpdateNoPlayer"] = "PlayerUpdateNoPlayer";
85
+ DebugEvents2["PlayerUpdateFilterFixApply"] = "PlayerUpdateFilterFixApply";
86
+ DebugEvents2["PlayerUpdateSuccess"] = "PlayerUpdateSuccess";
87
+ DebugEvents2["HeartBeatTriggered"] = "HeartBeatTriggered";
88
+ DebugEvents2["NoSocketOnDestroy"] = "NoSocketOnDestroy";
89
+ DebugEvents2["SocketCleanupError"] = "SocketCleanupError";
90
+ DebugEvents2["SocketTerminateHeartBeatTimeout"] = "SocketTerminateHeartBeatTimeout";
91
+ DebugEvents2["TryingConnectWhileConnected"] = "TryingConnectWhileConnected";
92
+ DebugEvents2["SearchNothingFound"] = "SearchNothingFound";
93
+ DebugEvents2["ValidatingBlacklistLinks"] = "ValidatingBlacklistLinks";
94
+ DebugEvents2["ValidatingWhitelistLinks"] = "ValidatingWhitelistLinks";
95
+ DebugEvents2["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
96
+ DebugEvents2["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
97
+ DebugEvents2["PlayerDestroyingSomewhereElse"] = "PlayerDestroyingSomewhereElse";
98
+ DebugEvents2["PlayerCreateNodeNotFound"] = "PlayerCreateNodeNotFound";
99
+ DebugEvents2["PlayerPlayQueueEmptyTimeoutClear"] = "PlayerPlayQueueEmptyTimeoutClear";
100
+ DebugEvents2["PlayerPlayWithTrackReplace"] = "PlayerPlayWithTrackReplace";
101
+ DebugEvents2["PlayerPlayUnresolvedTrack"] = "PlayerPlayUnresolvedTrack";
102
+ DebugEvents2["PlayerPlayUnresolvedTrackFailed"] = "PlayerPlayUnresolvedTrackFailed";
103
+ DebugEvents2["PlayerVolumeAsFilter"] = "PlayerVolumeAsFilter";
104
+ DebugEvents2["BandcampSearchLokalEngine"] = "BandcampSearchLokalEngine";
105
+ DebugEvents2["PlayerChangeNode"] = "PlayerChangeNode";
106
+ DebugEvents2["BuildTrackError"] = "BuildTrackError";
107
+ DebugEvents2["TransformRequesterFunctionFailed"] = "TransformRequesterFunctionFailed";
108
+ DebugEvents2["GetClosestTrackFailed"] = "GetClosestTrackFailed";
109
+ DebugEvents2["PlayerDeleteInsteadOfDestroy"] = "PlayerDeleteInsteadOfDestroy";
110
+ DebugEvents2["FailedToConnectToNodes"] = "FailedToConnectToNodes";
111
+ DebugEvents2["NoAudioDebug"] = "NoAudioDebug";
112
+ DebugEvents2["PlayerAutoReconnect"] = "PlayerAutoReconnect";
113
+ DebugEvents2["PlayerDestroyFail"] = "PlayerDestroyFail";
114
+ DebugEvents2["PlayerChangeNodeFailNoEligibleNode"] = "PlayerChangeNodeFailNoEligibleNode";
115
+ DebugEvents2["PlayerChangeNodeFail"] = "PlayerChangeNodeFail";
116
+ return DebugEvents2;
117
+ })(DebugEvents || {});
118
+ var DestroyReasons = /* @__PURE__ */ ((DestroyReasons2) => {
119
+ DestroyReasons2["QueueEmpty"] = "QueueEmpty";
120
+ DestroyReasons2["NodeDestroy"] = "NodeDestroy";
121
+ DestroyReasons2["NodeDeleted"] = "NodeDeleted";
122
+ DestroyReasons2["NodeNoVoice"] = "NodeNoVoice";
123
+ DestroyReasons2["NodeReconnectFail"] = "NodeReconnectFail";
124
+ DestroyReasons2["Disconnected"] = "Disconnected";
125
+ DestroyReasons2["PlayerReconnectFail"] = "PlayerReconnectFail";
126
+ DestroyReasons2["PlayerChangeNodeFail"] = "PlayerChangeNodeFail";
127
+ DestroyReasons2["PlayerChangeNodeFailNoEligibleNode"] = "PlayerChangeNodeFailNoEligibleNode";
128
+ DestroyReasons2["ChannelDeleted"] = "ChannelDeleted";
129
+ DestroyReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
130
+ DestroyReasons2["ReconnectAllNodes"] = "ReconnectAllNodes";
131
+ DestroyReasons2["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
132
+ DestroyReasons2["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
133
+ return DestroyReasons2;
134
+ })(DestroyReasons || {});
135
+ var DisconnectReasons = /* @__PURE__ */ ((DisconnectReasons2) => {
136
+ DisconnectReasons2["Disconnected"] = "Disconnected";
137
+ DisconnectReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
138
+ return DisconnectReasons2;
139
+ })(DisconnectReasons || {});
140
+ var validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
141
+ var audioOutputsData = {
142
+ mono: {
143
+ leftToLeft: 0.5,
144
+ leftToRight: 0.5,
145
+ rightToLeft: 0.5,
146
+ rightToRight: 0.5
147
+ },
148
+ stereo: {
149
+ leftToLeft: 1,
150
+ leftToRight: 0,
151
+ rightToLeft: 0,
152
+ rightToRight: 1
153
+ },
154
+ left: {
155
+ leftToLeft: 1,
156
+ leftToRight: 0,
157
+ rightToLeft: 1,
158
+ rightToRight: 0
159
+ },
160
+ right: {
161
+ leftToLeft: 0,
162
+ leftToRight: 1,
163
+ rightToLeft: 0,
164
+ rightToRight: 1
165
+ }
166
+ };
167
+ var EQList = {
168
+ BassboostEarrape: [
169
+ { band: 0, gain: 0.6 * 0.375 },
170
+ { band: 1, gain: 0.67 * 0.375 },
171
+ { band: 2, gain: 0.67 * 0.375 },
172
+ { band: 3, gain: 0.4 * 0.375 },
173
+ { band: 4, gain: -0.5 * 0.375 },
174
+ { band: 5, gain: 0.15 * 0.375 },
175
+ { band: 6, gain: -0.45 * 0.375 },
176
+ { band: 7, gain: 0.23 * 0.375 },
177
+ { band: 8, gain: 0.35 * 0.375 },
178
+ { band: 9, gain: 0.45 * 0.375 },
179
+ { band: 10, gain: 0.55 * 0.375 },
180
+ { band: 11, gain: -0.6 * 0.375 },
181
+ { band: 12, gain: 0.55 * 0.375 },
182
+ { band: 13, gain: -0.5 * 0.375 },
183
+ { band: 14, gain: -0.75 * 0.375 }
184
+ ],
185
+ BassboostHigh: [
186
+ { band: 0, gain: 0.6 * 0.25 },
187
+ { band: 1, gain: 0.67 * 0.25 },
188
+ { band: 2, gain: 0.67 * 0.25 },
189
+ { band: 3, gain: 0.4 * 0.25 },
190
+ { band: 4, gain: -0.5 * 0.25 },
191
+ { band: 5, gain: 0.15 * 0.25 },
192
+ { band: 6, gain: -0.45 * 0.25 },
193
+ { band: 7, gain: 0.23 * 0.25 },
194
+ { band: 8, gain: 0.35 * 0.25 },
195
+ { band: 9, gain: 0.45 * 0.25 },
196
+ { band: 10, gain: 0.55 * 0.25 },
197
+ { band: 11, gain: -0.6 * 0.25 },
198
+ { band: 12, gain: 0.55 * 0.25 },
199
+ { band: 13, gain: -0.5 * 0.25 },
200
+ { band: 14, gain: -0.75 * 0.25 }
201
+ ],
202
+ BassboostMedium: [
203
+ { band: 0, gain: 0.6 * 0.1875 },
204
+ { band: 1, gain: 0.67 * 0.1875 },
205
+ { band: 2, gain: 0.67 * 0.1875 },
206
+ { band: 3, gain: 0.4 * 0.1875 },
207
+ { band: 4, gain: -0.5 * 0.1875 },
208
+ { band: 5, gain: 0.15 * 0.1875 },
209
+ { band: 6, gain: -0.45 * 0.1875 },
210
+ { band: 7, gain: 0.23 * 0.1875 },
211
+ { band: 8, gain: 0.35 * 0.1875 },
212
+ { band: 9, gain: 0.45 * 0.1875 },
213
+ { band: 10, gain: 0.55 * 0.1875 },
214
+ { band: 11, gain: -0.6 * 0.1875 },
215
+ { band: 12, gain: 0.55 * 0.1875 },
216
+ { band: 13, gain: -0.5 * 0.1875 },
217
+ { band: 14, gain: -0.75 * 0.1875 }
218
+ ],
219
+ BassboostLow: [
220
+ { band: 0, gain: 0.6 * 0.125 },
221
+ { band: 1, gain: 0.67 * 0.125 },
222
+ { band: 2, gain: 0.67 * 0.125 },
223
+ { band: 3, gain: 0.4 * 0.125 },
224
+ { band: 4, gain: -0.5 * 0.125 },
225
+ { band: 5, gain: 0.15 * 0.125 },
226
+ { band: 6, gain: -0.45 * 0.125 },
227
+ { band: 7, gain: 0.23 * 0.125 },
228
+ { band: 8, gain: 0.35 * 0.125 },
229
+ { band: 9, gain: 0.45 * 0.125 },
230
+ { band: 10, gain: 0.55 * 0.125 },
231
+ { band: 11, gain: -0.6 * 0.125 },
232
+ { band: 12, gain: 0.55 * 0.125 },
233
+ { band: 13, gain: -0.5 * 0.125 },
234
+ { band: 14, gain: -0.75 * 0.125 }
235
+ ],
236
+ BetterMusic: [
237
+ { band: 0, gain: 0.25 },
238
+ { band: 1, gain: 0.025 },
239
+ { band: 2, gain: 0.0125 },
240
+ { band: 3, gain: 0 },
241
+ { band: 4, gain: 0 },
242
+ { band: 5, gain: -0.0125 },
243
+ { band: 6, gain: -0.025 },
244
+ { band: 7, gain: -0.0175 },
245
+ { band: 8, gain: 0 },
246
+ { band: 9, gain: 0 },
247
+ { band: 10, gain: 0.0125 },
248
+ { band: 11, gain: 0.025 },
249
+ { band: 12, gain: 0.25 },
250
+ { band: 13, gain: 0.125 },
251
+ { band: 14, gain: 0.125 }
252
+ ],
253
+ Rock: [
254
+ { band: 0, gain: 0.3 },
255
+ { band: 1, gain: 0.25 },
256
+ { band: 2, gain: 0.2 },
257
+ { band: 3, gain: 0.1 },
258
+ { band: 4, gain: 0.05 },
259
+ { band: 5, gain: -0.05 },
260
+ { band: 6, gain: -0.15 },
261
+ { band: 7, gain: -0.2 },
262
+ { band: 8, gain: -0.1 },
263
+ { band: 9, gain: -0.05 },
264
+ { band: 10, gain: 0.05 },
265
+ { band: 11, gain: 0.1 },
266
+ { band: 12, gain: 0.2 },
267
+ { band: 13, gain: 0.25 },
268
+ { band: 14, gain: 0.3 }
269
+ ],
270
+ Classic: [
271
+ { band: 0, gain: 0.375 },
272
+ { band: 1, gain: 0.35 },
273
+ { band: 2, gain: 0.125 },
274
+ { band: 3, gain: 0 },
275
+ { band: 4, gain: 0 },
276
+ { band: 5, gain: 0.125 },
277
+ { band: 6, gain: 0.55 },
278
+ { band: 7, gain: 0.05 },
279
+ { band: 8, gain: 0.125 },
280
+ { band: 9, gain: 0.25 },
281
+ { band: 10, gain: 0.2 },
282
+ { band: 11, gain: 0.25 },
283
+ { band: 12, gain: 0.3 },
284
+ { band: 13, gain: 0.25 },
285
+ { band: 14, gain: 0.3 }
286
+ ],
287
+ Pop: [
288
+ { band: 0, gain: 0.2635 },
289
+ { band: 1, gain: 0.22141 },
290
+ { band: 2, gain: -0.21141 },
291
+ { band: 3, gain: -0.1851 },
292
+ { band: 4, gain: -0.155 },
293
+ { band: 5, gain: 0.21141 },
294
+ { band: 6, gain: 0.22456 },
295
+ { band: 7, gain: 0.237 },
296
+ { band: 8, gain: 0.237 },
297
+ { band: 9, gain: 0.237 },
298
+ { band: 10, gain: -0.05 },
299
+ { band: 11, gain: -0.116 },
300
+ { band: 12, gain: 0.192 },
301
+ { band: 13, gain: 0 },
302
+ { band: 14, gain: 0 }
303
+ ],
304
+ Electronic: [
305
+ { band: 0, gain: 0.375 },
306
+ { band: 1, gain: 0.35 },
307
+ { band: 2, gain: 0.125 },
308
+ { band: 3, gain: 0 },
309
+ { band: 4, gain: 0 },
310
+ { band: 5, gain: -0.125 },
311
+ { band: 6, gain: -0.125 },
312
+ { band: 7, gain: 0 },
313
+ { band: 8, gain: 0.25 },
314
+ { band: 9, gain: 0.125 },
315
+ { band: 10, gain: 0.15 },
316
+ { band: 11, gain: 0.2 },
317
+ { band: 12, gain: 0.25 },
318
+ { band: 13, gain: 0.35 },
319
+ { band: 14, gain: 0.4 }
320
+ ],
321
+ FullSound: [
322
+ { band: 0, gain: 0.25 + 0.375 },
323
+ { band: 1, gain: 0.25 + 0.025 },
324
+ { band: 2, gain: 0.25 + 0.0125 },
325
+ { band: 3, gain: 0.25 + 0 },
326
+ { band: 4, gain: 0.25 + 0 },
327
+ { band: 5, gain: 0.25 + -0.0125 },
328
+ { band: 6, gain: 0.25 + -0.025 },
329
+ { band: 7, gain: 0.25 + -0.0175 },
330
+ { band: 8, gain: 0.25 + 0 },
331
+ { band: 9, gain: 0.25 + 0 },
332
+ { band: 10, gain: 0.25 + 0.0125 },
333
+ { band: 11, gain: 0.25 + 0.025 },
334
+ { band: 12, gain: 0.25 + 0.375 },
335
+ { band: 13, gain: 0.25 + 0.125 },
336
+ { band: 14, gain: 0.25 + 0.125 }
337
+ ],
338
+ Gaming: [
339
+ { band: 0, gain: 0.35 },
340
+ { band: 1, gain: 0.3 },
341
+ { band: 2, gain: 0.25 },
342
+ { band: 3, gain: 0.2 },
343
+ { band: 4, gain: 0.15 },
344
+ { band: 5, gain: 0.1 },
345
+ { band: 6, gain: 0.05 },
346
+ { band: 7, gain: -0 },
347
+ { band: 8, gain: -0.05 },
348
+ { band: 9, gain: -0.1 },
349
+ { band: 10, gain: -0.15 },
350
+ { band: 11, gain: -0.2 },
351
+ { band: 12, gain: -0.25 },
352
+ { band: 13, gain: -0.3 },
353
+ { band: 14, gain: -0.35 }
354
+ ]
355
+ };
356
+ var RecommendationsStrings = {
357
+ highCPULoad: (cpuLoad) => `High CPU load (${(cpuLoad * 100).toFixed(1)}%). Consider reducing player count or upgrading CPU.`,
358
+ highSystemLoad: (systemLoad) => `High system load (${(systemLoad * 100).toFixed(1)}%). Check other processes on the server.`,
359
+ highMemoryUsage: (memoryUsagePercent) => `High memory usage (${memoryUsagePercent.toFixed(1)}%). Consider increasing allocated memory or reducing player count.`,
360
+ frameDeficit: (frameDeficit) => `Frame deficit detected (${frameDeficit}). Audio quality may be affected. Check network and CPU.`,
361
+ highLatency: (ping) => `High latency (${ping}ms). Check network connection to the node.`,
362
+ nodeRestart: "Node restart recommended to clear memory and reset connections.",
363
+ highPlayercount: (players) => `High player count (${players}). Consider load balancing across multiple nodes.`,
364
+ nodeOffline: "Node is offline or disconnected",
365
+ checkConnectivity: "Check node connectivity and restart if needed"
366
+ };
367
+ var NodeLinkExclusiveEvents = [
368
+ "PlayerCreatedEvent",
369
+ "PlayerDestroyedEvent",
370
+ "PlayerConnectedEvent",
371
+ "PlayerReconnectingEvent",
372
+ "VolumeChangedEvent",
373
+ "FiltersChangedEvent",
374
+ "SeekEvent",
375
+ "PauseEvent",
376
+ "ConnectionStatusEvent",
377
+ "MixStartedEvent",
378
+ "MixEndedEvent",
379
+ "LyricsFoundEvent",
380
+ "LyricsLineEvent",
381
+ "LyricsNotFoundEvent"
382
+ ];
383
+
384
+ // src/node/NodeManager.ts
385
+ var import_node_events = require("events");
386
+
387
+ // src/node/Node.ts
388
+ var import_node_path = require("path");
389
+ var import_ws = __toESM(require("ws"), 1);
390
+
391
+ // src/types/Node.ts
392
+ var ReconnectionState = /* @__PURE__ */ ((ReconnectionState2) => {
393
+ ReconnectionState2["IDLE"] = "IDLE";
394
+ ReconnectionState2["RECONNECTING"] = "RECONNECTING";
395
+ ReconnectionState2["PENDING"] = "PENDING";
396
+ ReconnectionState2["DESTROYING"] = "DESTROYING";
397
+ return ReconnectionState2;
398
+ })(ReconnectionState || {});
399
+
400
+ // src/utils/Utils.ts
401
+ var import_node_url = require("url");
402
+ var import_types = require("util/types");
403
+
404
+ // src/node/Sources.ts
405
+ var SourceMappings = {
406
+ "youtube music": "ytmsearch",
407
+ youtubemusic: "ytmsearch",
408
+ ytmsearch: "ytmsearch",
409
+ ytm: "ytmsearch",
410
+ musicyoutube: "ytmsearch",
411
+ "music youtube": "ytmsearch",
412
+ youtube: "ytsearch",
413
+ yt: "ytsearch",
414
+ ytsearch: "ytsearch",
415
+ soundcloud: "scsearch",
416
+ scsearch: "scsearch",
417
+ sc: "scsearch",
418
+ "apple music": "amsearch",
419
+ apple: "amsearch",
420
+ applemusic: "amsearch",
421
+ amsearch: "amsearch",
422
+ am: "amsearch",
423
+ musicapple: "amsearch",
424
+ "music apple": "amsearch",
425
+ spotify: "spsearch",
426
+ spsearch: "spsearch",
427
+ sp: "spsearch",
428
+ "spotify.com": "spsearch",
429
+ spotifycom: "spsearch",
430
+ sprec: "sprec",
431
+ spsuggestion: "sprec",
432
+ deezer: "dzsearch",
433
+ dz: "dzsearch",
434
+ dzsearch: "dzsearch",
435
+ dzisrc: "dzisrc",
436
+ dzrec: "dzrec",
437
+ "yandex music": "ymsearch",
438
+ yandexmusic: "ymsearch",
439
+ yandex: "ymsearch",
440
+ ymsearch: "ymsearch",
441
+ ymrec: "ymrec",
442
+ vksearch: "vksearch",
443
+ vkmusic: "vksearch",
444
+ "vk music": "vksearch",
445
+ vkrec: "vkrec",
446
+ vk: "vksearch",
447
+ qbsearch: "qbsearch",
448
+ qobuz: "qbsearch",
449
+ qbisrc: "qbisrc",
450
+ qbrec: "qbrec",
451
+ pandora: "pdsearch",
452
+ pd: "pdsearch",
453
+ pdsearch: "pdsearch",
454
+ pdisrc: "pdisrc",
455
+ pdrec: "pdrec",
456
+ "pandora music": "pdsearch",
457
+ pandoramusic: "pdsearch",
458
+ speak: "speak",
459
+ tts: "tts",
460
+ ftts: "ftts",
461
+ flowery: "ftts",
462
+ "flowery.tts": "ftts",
463
+ flowerytts: "ftts",
464
+ bandcamp: "bcsearch",
465
+ bc: "bcsearch",
466
+ bcsearch: "bcsearch",
467
+ phsearch: "phsearch",
468
+ pornhub: "phsearch",
469
+ porn: "phsearch",
470
+ local: "local",
471
+ http: "http",
472
+ https: "https",
473
+ link: "link",
474
+ uri: "uri",
475
+ tidal: "tdsearch",
476
+ td: "tdsearch",
477
+ "tidal music": "tdsearch",
478
+ tdsearch: "tdsearch",
479
+ tdrec: "tdrec",
480
+ jiosaavn: "jssearch",
481
+ js: "jssearch",
482
+ jssearch: "jssearch",
483
+ jsrec: "jsrec",
484
+ audiomack: "admsearch",
485
+ adm: "admsearch",
486
+ admsearch: "admsearch",
487
+ admrec: "admrec",
488
+ shazam: "shsearch",
489
+ sh: "shsearch",
490
+ shsearch: "shsearch",
491
+ instagram: "igsearch",
492
+ ig: "igsearch",
493
+ igsearch: "igsearch",
494
+ bilibili: "blsearch",
495
+ bl: "blsearch",
496
+ blsearch: "blsearch",
497
+ lastfm: "lfsearch",
498
+ "last.fm": "lfsearch",
499
+ lf: "lfsearch",
500
+ lfsearch: "lfsearch",
501
+ "amazon music": "amzsearch",
502
+ amazonmusic: "amzsearch",
503
+ amz: "amzsearch",
504
+ amzsearch: "amzsearch",
505
+ amzrec: "amzrec",
506
+ gaana: "gnsearch",
507
+ gn: "gnsearch",
508
+ gnsearch: "gnsearch",
509
+ gnrec: "gnrec"
510
+ };
511
+ var BuiltinSources = {
512
+ DuncteBot_Plugin: "duncte-engine",
513
+ LavaSrc: "source-engine",
514
+ GoogleCloudTTS: "tts-engine",
515
+ LavaSearch: "search-engine",
516
+ JioSaavn_Engine: "jio-engine",
517
+ Filter_Engine: "filter-engine",
518
+ TimedLyrics_Engine: "lyrics-engine"
519
+ };
520
+ var LinkMatchers = {
521
+ YoutubeRegex: /https?:\/\/?(?:www\.)?(?:(m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
522
+ YoutubeMusicRegex: /https?:\/\/?(?:www\.)?(?:(music|m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
523
+ SoundCloudRegex: /https?:\/\/(?:on\.)?soundcloud\.com\//,
524
+ SoundCloudMobileRegex: /https?:\/\/(soundcloud\.app\.goo\.gl)\/(\S+)/,
525
+ bandcamp: /https?:\/\/?(?:www\.)?([\d|\w]+)\.bandcamp\.com\/(\S+)/,
526
+ TwitchTv: /https?:\/\/?(?:www\.)?twitch\.tv\/\w+/,
527
+ vimeo: /https?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|)(\d+)(?:|\/\?)/,
528
+ mp3Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(mp3)$/,
529
+ m3uUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m3u)$/,
530
+ m3u8Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m3u8)$/,
531
+ mp4Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(mp4)$/,
532
+ m4aUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m4a)$/,
533
+ wavUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(wav)$/,
534
+ aacpUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(aacp)$/,
535
+ DeezerTrackRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?track\/(\d+)/,
536
+ DeezerPageLinkRegex: /(https?:\/\/|)?(?:www\.)?deezer\.page\.link\/(\S+)/,
537
+ DeezerPlaylistRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?playlist\/(\d+)/,
538
+ DeezerAlbumRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?album\/(\d+)/,
539
+ DeezerArtistRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?artist\/(\d+)/,
540
+ DeezerMixesRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?mixes\/genre\/(\d+)/,
541
+ DeezerEpisodeRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?episode\/(\d+)/,
542
+ AllDeezerRegexWithoutPageLink: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|playlist|album|artist|mixes\/genre|episode)\/(\d+)/,
543
+ AllDeezerRegex: /((https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|playlist|album|artist|mixes\/genre|episode)\/(\d+)|(https?:\/\/|)?(?:www\.)?deezer\.page\.link\/(\S+))/,
544
+ SpotifySongRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?track\/(?<identifier>[a-zA-Z0-9-_]+)/,
545
+ SpotifyPlaylistRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?playlist\/(?<identifier>[a-zA-Z0-9-_]+)/,
546
+ SpotifyArtistRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?artist\/(?<identifier>[a-zA-Z0-9-_]+)/,
547
+ SpotifyEpisodeRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?episode\/(?<identifier>[a-zA-Z0-9-_]+)/,
548
+ SpotifyShowRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?show\/(?<identifier>[a-zA-Z0-9-_]+)/,
549
+ SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
550
+ AllSpotifyRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?(?<type>track|album|playlist|artist|episode|show)\/(?<identifier>[a-zA-Z0-9-_]+)/,
551
+ appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,
552
+ tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
553
+ jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
554
+ PandoraTrackRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>TR[A-Za-z0-9]+)(?:[?#].*)?$/,
555
+ PandoraAlbumRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>AL[A-Za-z0-9]+)(?:[?#].*)?$/,
556
+ PandoraArtistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/artist\/[\w-]+\/(?<identifier>AR[A-Za-z0-9]+)(?:[?#].*)?$/,
557
+ PandoraPlaylistRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/playlist\/(?<identifier>PL:[\d:]+)(?:[?#].*)?$/,
558
+ AllPandoraRegex: /^@?(?:https?:\/\/)?(?:www\.)?pandora\.com\/(?:playlist\/(?<playlistId>PL:[\d:]+)|artist\/[\w-]+(?:\/[\w-]+)*\/(?<identifier>(?:TR|AL|AR)[A-Za-z0-9]+))(?:[?#].*)?$/,
559
+ tiktok: /https:\/\/www\.tiktok\.com\//,
560
+ mixcloud: /https:\/\/www\.mixcloud\.com\//,
561
+ musicYandex: /https:\/\/music\.yandex\.ru\//,
562
+ radiohost: /https?:\/\/[^.\s]+\.radiohost\.de\/(\S+)/
563
+ };
564
+
565
+ // src/utils/Utils.ts
566
+ var AudioTrackSymbol = /* @__PURE__ */ Symbol("Ryanlink-Track");
567
+ var UnresolvedAudioTrackSymbol = /* @__PURE__ */ Symbol("Ryanlink-Track-Unresolved");
568
+ var AudioQueueSymbol = /* @__PURE__ */ Symbol("Ryanlink-Queue");
569
+ var AudioNodeSymbol = /* @__PURE__ */ Symbol("Ryanlink-Node");
570
+ var escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
571
+ function parseConnectionUrl(connectionUrl) {
572
+ if (!connectionUrl.startsWith("ryanlink://") && !connectionUrl.startsWith("nodelink://"))
573
+ throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'ryanlink://' or 'nodelink://'`);
574
+ const parsed = new import_node_url.URL(connectionUrl.replace(/^ryanlink:\/\//, "https://").replace(/^nodelink:\/\//, "https://"));
575
+ return {
576
+ authorization: parsed.password,
577
+ nodeType: connectionUrl.startsWith("ryanlink://") ? "Core" : "NodeLink",
578
+ id: parsed.username,
579
+ host: parsed.hostname,
580
+ port: Number(parsed.port)
581
+ };
582
+ }
583
+ var RyanlinkUtils = class {
584
+ RyanlinkManager = void 0;
585
+ constructor(RyanlinkManager2) {
586
+ this.RyanlinkManager = RyanlinkManager2;
587
+ }
588
+ buildPluginInfo(data, clientData = {}) {
589
+ return {
590
+ clientData,
591
+ ...data.pluginInfo || data.plugin
592
+ };
593
+ }
594
+ buildTrack(data, requester) {
595
+ if (!data?.encoded || typeof data.encoded !== "string") throw new RangeError("Argument 'data.encoded' must be present.");
596
+ if (!data.info) throw new RangeError("Argument 'data.info' must be present.");
597
+ try {
598
+ let transformedRequester = typeof requester === "object" ? this.getTransformedRequester(requester) : void 0;
599
+ if (!transformedRequester && typeof data?.userData?.requester === "object" && data.userData.requester !== null) {
600
+ transformedRequester = this.getTransformedRequester(data.userData.requester);
601
+ }
602
+ const r = {
603
+ encoded: data.encoded,
604
+ info: {
605
+ identifier: data.info.identifier,
606
+ title: data.info.title,
607
+ author: data.info.author,
608
+ duration: data.info?.duration || data.info?.length,
609
+ artworkUrl: data.info.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
610
+ uri: data.info.uri,
611
+ sourceName: data.info.sourceName,
612
+ isSeekable: data.info.isSeekable,
613
+ isStream: data.info.isStream,
614
+ isrc: data.info.isrc
615
+ },
616
+ userData: {
617
+ ...data.userData,
618
+ requester: transformedRequester
619
+ },
620
+ pluginInfo: this.buildPluginInfo(data, "clientData" in data ? data.clientData : {}),
621
+ requester: transformedRequester || this.getTransformedRequester(this.RyanlinkManager?.options?.client)
622
+ };
623
+ Object.defineProperty(r, AudioTrackSymbol, { configurable: true, value: true });
624
+ return r;
625
+ } catch (error) {
626
+ if (this.RyanlinkManager?.options?.advancedOptions?.enableDebugEvents) {
627
+ this.RyanlinkManager?.emit("debug", "BuildTrackError" /* BuildTrackError */, {
628
+ error,
629
+ functionLayer: "RyanlinkUtils > buildTrack()",
630
+ message: "Error while building track",
631
+ state: "error"
632
+ });
633
+ }
634
+ throw new RangeError(`Argument "data" is not a valid track: ${error.message}`);
635
+ }
636
+ }
637
+ buildUnresolvedTrack(query, requester) {
638
+ if (typeof query === "undefined") throw new RangeError('Argument "query" must be present.');
639
+ const unresolvedTrack = {
640
+ encoded: query.encoded || void 0,
641
+ info: query.info ? query.info : query.title ? query : void 0,
642
+ pluginInfo: this.buildPluginInfo(query),
643
+ requester: this.getTransformedRequester(requester),
644
+ async resolve(player) {
645
+ const closest = await getClosestTrack(this, player);
646
+ if (!closest) throw new SyntaxError("No closest Track found");
647
+ for (const prop of Object.getOwnPropertyNames(this)) delete this[prop];
648
+ delete this[UnresolvedAudioTrackSymbol];
649
+ Object.defineProperty(this, AudioTrackSymbol, { configurable: true, value: true });
650
+ return Object.assign(this, closest);
651
+ }
652
+ };
653
+ if (!this.isUnresolvedTrack(unresolvedTrack)) throw SyntaxError("Could not build Unresolved Track");
654
+ Object.defineProperty(unresolvedTrack, UnresolvedAudioTrackSymbol, {
655
+ configurable: true,
656
+ value: true
657
+ });
658
+ return unresolvedTrack;
659
+ }
660
+ isNode(data) {
661
+ if (!data) return false;
662
+ const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(data));
663
+ if (!keys.includes("constructor")) return false;
664
+ if (!keys.length) return false;
665
+ if (![
666
+ "connect",
667
+ "destroy",
668
+ "destroyPlayer",
669
+ "fetchAllPlayers",
670
+ "fetchInfo",
671
+ "fetchPlayer",
672
+ "fetchStats",
673
+ "fetchVersion",
674
+ "request",
675
+ "updatePlayer",
676
+ "updateSession"
677
+ ].every((v) => keys.includes(v)))
678
+ return false;
679
+ return true;
680
+ }
681
+ getTransformedRequester(requester) {
682
+ try {
683
+ return typeof this.RyanlinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.RyanlinkManager?.options?.playerOptions?.requesterTransformer(requester) : requester;
684
+ } catch (e) {
685
+ if (this.RyanlinkManager?.options?.advancedOptions?.enableDebugEvents) {
686
+ this.RyanlinkManager?.emit("debug", "TransformRequesterFunctionFailed" /* TransformRequesterFunctionFailed */, {
687
+ error: e,
688
+ functionLayer: "RyanlinkUtils > getTransformedRequester()",
689
+ message: "Your custom transformRequesterFunction failed to execute, please check your function for errors.",
690
+ state: "error"
691
+ });
692
+ }
693
+ return requester;
694
+ }
695
+ }
696
+ isNodeOptions(data) {
697
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
698
+ if (typeof data.host !== "string" || !data.host.length) return false;
699
+ if (typeof data.port !== "number" || isNaN(data.port) || data.port < 0 || data.port > 65535) return false;
700
+ if (typeof data.authorization !== "string" || !data.authorization.length) return false;
701
+ if ("secure" in data && typeof data.secure !== "boolean" && data.secure !== void 0) return false;
702
+ if ("sessionId" in data && typeof data.sessionId !== "string" && data.sessionId !== void 0) return false;
703
+ if ("id" in data && typeof data.id !== "string" && data.id !== void 0) return false;
704
+ if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0))
705
+ return false;
706
+ if ("poolOptions" in data && typeof data.poolOptions !== "object" && data.poolOptions !== void 0) return false;
707
+ if ("retryAmount" in data && (typeof data.retryAmount !== "number" || isNaN(data.retryAmount) || data.retryAmount <= 0 && data.retryAmount !== void 0))
708
+ return false;
709
+ if ("retryDelay" in data && (typeof data.retryDelay !== "number" || isNaN(data.retryDelay) || data.retryDelay <= 0 && data.retryDelay !== void 0))
710
+ return false;
711
+ if ("requestTimeout" in data && (typeof data.requestTimeout !== "number" || isNaN(data.requestTimeout) || data.requestTimeout <= 0 && data.requestTimeout !== void 0))
712
+ return false;
713
+ return true;
714
+ }
715
+ isNotBrokenTrack(data, minDuration = 29e3) {
716
+ if (typeof data?.info?.duration !== "number" || isNaN(data?.info?.duration)) return false;
717
+ if (data.info.duration <= Math.max(minDuration, 0)) return false;
718
+ if (!data.info) return false;
719
+ return this.isTrack(data);
720
+ }
721
+ isTrack(data) {
722
+ if (!data) return false;
723
+ if (data[AudioTrackSymbol] === true) return true;
724
+ return typeof data?.encoded === "string" && typeof data?.info === "object" && !("resolve" in data);
725
+ }
726
+ isUnresolvedTrack(data) {
727
+ if (!data) return false;
728
+ if (data[UnresolvedAudioTrackSymbol] === true) return true;
729
+ return typeof data === "object" && ("info" in data && typeof data.info.title === "string" || typeof data.encoded === "string") && "resolve" in data && typeof data.resolve === "function";
730
+ }
731
+ isUnresolvedTrackQuery(data) {
732
+ return typeof data === "object" && !("info" in data) && typeof data.title === "string";
733
+ }
734
+ async getClosestTrack(data, player) {
735
+ try {
736
+ return getClosestTrack(data, player);
737
+ } catch (e) {
738
+ if (this.RyanlinkManager?.options?.advancedOptions?.enableDebugEvents) {
739
+ this.RyanlinkManager?.emit("debug", "GetClosestTrackFailed" /* GetClosestTrackFailed */, {
740
+ error: e,
741
+ functionLayer: "RyanlinkUtils > getClosestTrack()",
742
+ message: "Failed to resolve track because the getClosestTrack function failed.",
743
+ state: "error"
744
+ });
745
+ }
746
+ throw e;
747
+ }
748
+ }
749
+ validateQueryString(node, queryString, sourceString) {
750
+ if (!node.info) throw new Error("No Audio Node was provided");
751
+ if (node._checkForSources && !node.info.sourceManagers?.length) throw new Error("Audio Node, has no sourceManagers enabled");
752
+ if (!queryString.trim().length) throw new Error(`Query string is empty, please provide a valid query string.`);
753
+ if (sourceString === "speak" && queryString.length > 100) throw new Error(`Query is speak, which is limited to 100 characters.`);
754
+ if (this.RyanlinkManager.options?.linksBlacklist?.length > 0) {
755
+ if (this.RyanlinkManager.options?.advancedOptions?.enableDebugEvents) {
756
+ this.RyanlinkManager.emit("debug", "ValidatingBlacklistLinks" /* ValidatingBlacklistLinks */, {
757
+ state: "log",
758
+ message: `Validating Query against RyanlinkManager.options.linksBlacklist, query: "${queryString}"`,
759
+ functionLayer: "(RyanlinkNode > node | player) > search() > validateQueryString()"
760
+ });
761
+ }
762
+ if (this.RyanlinkManager.options?.linksBlacklist.some(
763
+ (v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString)
764
+ )) {
765
+ throw new Error(`Query string contains a link / word which is blacklisted.`);
766
+ }
767
+ }
768
+ if (/^https?:\/\//.test(queryString) && this.RyanlinkManager.options?.linksAllowed === false)
769
+ throw new Error("Using links to make a request is not allowed.");
770
+ if (this.RyanlinkManager.options?.linksWhitelist?.length > 0) {
771
+ if (this.RyanlinkManager.options?.advancedOptions?.enableDebugEvents) {
772
+ this.RyanlinkManager.emit("debug", "ValidatingWhitelistLinks" /* ValidatingWhitelistLinks */, {
773
+ state: "log",
774
+ message: `Link was provided to the Query, validating against RyanlinkManager.options.linksWhitelist, query: "${queryString}"`,
775
+ functionLayer: "(RyanlinkNode > node | player) > search() > validateQueryString()"
776
+ });
777
+ }
778
+ if (!this.RyanlinkManager.options?.linksWhitelist.some(
779
+ (v) => typeof v === "string" && queryString.toLowerCase().includes(v.toLowerCase()) || (0, import_types.isRegExp)(v) && v.test(queryString)
780
+ )) {
781
+ throw new Error(`Query string contains a link / word which isn't whitelisted.`);
782
+ }
783
+ }
784
+ if (!node._checkForSources) return;
785
+ if ((LinkMatchers.YoutubeMusicRegex.test(queryString) || LinkMatchers.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
786
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'youtube' enabled");
787
+ }
788
+ if ((LinkMatchers.SoundCloudMobileRegex.test(queryString) || LinkMatchers.SoundCloudRegex.test(queryString)) && !node.info?.sourceManagers?.includes("soundcloud")) {
789
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'soundcloud' enabled");
790
+ }
791
+ if (LinkMatchers.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
792
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'bandcamp' enabled (introduced with audio-engine 2.2.0+)");
793
+ }
794
+ if (LinkMatchers.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
795
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'twitch' enabled");
796
+ }
797
+ if (LinkMatchers.vimeo.test(queryString) && !node.info?.sourceManagers?.includes("vimeo")) {
798
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'vimeo' enabled");
799
+ }
800
+ if (LinkMatchers.tiktok.test(queryString) && !node.info?.sourceManagers?.includes("tiktok")) {
801
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'tiktok' enabled");
802
+ }
803
+ if (LinkMatchers.mixcloud.test(queryString) && !node.info?.sourceManagers?.includes("mixcloud")) {
804
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'mixcloud' enabled");
805
+ }
806
+ if (LinkMatchers.AllSpotifyRegex.test(queryString) && !node.info?.sourceManagers?.includes("spotify")) {
807
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'spotify' enabled");
808
+ }
809
+ if (LinkMatchers.appleMusic.test(queryString) && !node.info?.sourceManagers?.includes("applemusic")) {
810
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'applemusic' enabled");
811
+ }
812
+ if (LinkMatchers.AllDeezerRegex.test(queryString) && !node.info?.sourceManagers?.includes("deezer")) {
813
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'deezer' enabled");
814
+ }
815
+ if (LinkMatchers.musicYandex.test(queryString) && !node.info?.sourceManagers?.includes("yandexmusic")) {
816
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'yandexmusic' enabled");
817
+ }
818
+ if (LinkMatchers.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
819
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'jiosaavn' (via jio-engine) enabled");
820
+ }
821
+ if (LinkMatchers.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
822
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'tidal' enabled");
823
+ }
824
+ if (LinkMatchers.AllPandoraRegex.test(queryString) && !node.info?.sourceManagers?.includes("pandora")) {
825
+ throw new Error("Query / Link Provided for this Source but Audio Node has not 'pandora' enabled");
826
+ }
827
+ return;
828
+ }
829
+ findSourceOfQuery(queryString) {
830
+ const foundSource = Object.keys(SourceMappings).find((source) => queryString?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
831
+ if (foundSource && !["https", "http"].includes(foundSource) && SourceMappings[foundSource]) {
832
+ return foundSource;
833
+ }
834
+ return null;
835
+ }
836
+ extractSourceOfQuery(searchQuery) {
837
+ const foundSource = this.findSourceOfQuery(searchQuery.query);
838
+ if (foundSource) {
839
+ searchQuery.source = SourceMappings[foundSource];
840
+ searchQuery.query = searchQuery.query.slice(`${foundSource}:`.length, searchQuery.query.length);
841
+ }
842
+ return searchQuery;
843
+ }
844
+ typedLowerCase(input) {
845
+ if (!input) return input;
846
+ if (typeof input === "string") return input.toLowerCase();
847
+ return input;
848
+ }
849
+ transformQuery(query) {
850
+ const typedDefault = this.typedLowerCase(this.RyanlinkManager?.options?.playerOptions?.defaultSearchPlatform);
851
+ if (typeof query === "string") {
852
+ const Query = {
853
+ query,
854
+ extraQueryUrlParams: void 0,
855
+ source: typedDefault
856
+ };
857
+ return this.extractSourceOfQuery(Query);
858
+ }
859
+ const providedSource = query?.source?.trim?.()?.toLowerCase?.();
860
+ const validSourceExtracted = SourceMappings[providedSource ?? typedDefault];
861
+ return this.extractSourceOfQuery({
862
+ query: query.query,
863
+ extraQueryUrlParams: query.extraQueryUrlParams,
864
+ source: validSourceExtracted ?? providedSource ?? typedDefault
865
+ });
866
+ }
867
+ transformAudioSearchQuery(query) {
868
+ const typedDefault = this.typedLowerCase(this.RyanlinkManager?.options?.playerOptions?.defaultSearchPlatform);
869
+ if (typeof query === "string") {
870
+ const Query2 = {
871
+ query,
872
+ types: [],
873
+ extraQueryUrlParams: void 0,
874
+ source: typedDefault
875
+ };
876
+ return this.extractSourceOfQuery(Query2);
877
+ }
878
+ const providedSource = query?.source?.trim?.()?.toLowerCase?.();
879
+ const validSourceExtracted = SourceMappings[providedSource ?? typedDefault];
880
+ const Query = {
881
+ query: query.query,
882
+ types: query.types ? ["track", "playlist", "artist", "album", "text"].filter((v) => query.types?.find((x) => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album"],
883
+ source: validSourceExtracted ?? providedSource ?? typedDefault
884
+ };
885
+ return this.extractSourceOfQuery(Query);
886
+ }
887
+ validateSourceString(node, sourceString) {
888
+ if (!sourceString) throw new Error(`No SourceString was provided`);
889
+ const source = SourceMappings[sourceString.toLowerCase().trim()];
890
+ if (!source && !!this.RyanlinkManager.options.playerOptions.allowCustomSources)
891
+ throw new Error(
892
+ `Ryanlink does not support SearchQuerySource: '${sourceString}'. You can disable this check by setting 'ManagerOptions.PlayerOptions.allowCustomSources' to true`
893
+ );
894
+ if (!node.info) throw new Error("Audio Node does not have any info cached yet, not ready yet!");
895
+ if (!node._checkForSources) return;
896
+ if (source === "amsearch" && !node.info?.sourceManagers?.includes("applemusic")) {
897
+ throw new Error("Audio Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
898
+ }
899
+ if (source === "dzisrc" && !node.info?.sourceManagers?.includes("deezer")) {
900
+ throw new Error("Audio Node has not 'deezer' enabled, which is required to have 'dzisrc' work");
901
+ }
902
+ if (source === "dzsearch" && !node.info?.sourceManagers?.includes("deezer")) {
903
+ throw new Error("Audio Node has not 'deezer' enabled, which is required to have 'dzsearch' work");
904
+ }
905
+ if (source === "dzisrc" && node.info?.sourceManagers?.includes("deezer") && !node.info?.sourceManagers?.includes("http")) {
906
+ throw new Error("Audio Node has not 'http' enabled, which is required to have 'dzisrc' to work");
907
+ }
908
+ if (source === "jsrec" && !node.info?.sourceManagers?.includes("jiosaavn")) {
909
+ throw new Error("Audio Node has not 'jiosaavn' (via jio-engine) enabled, which is required to have 'jsrec' to work");
910
+ }
911
+ if (source === "jssearch" && !node.info?.sourceManagers?.includes("jiosaavn")) {
912
+ throw new Error("Audio Node has not 'jiosaavn' (via jio-engine) enabled, which is required to have 'jssearch' to work");
913
+ }
914
+ if (source === "scsearch" && !node.info?.sourceManagers?.includes("soundcloud")) {
915
+ throw new Error("Audio Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
916
+ }
917
+ if (source === "speak" && node._checkForPlugins && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(BuiltinSources.DuncteBot_Plugin.toLowerCase()))) {
918
+ throw new Error("Audio Node has not 'speak' enabled, which is required to have 'speak' work");
919
+ }
920
+ if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
921
+ throw new Error("Audio Node has not 'tidal' enabled, which is required to have 'tdsearch' work");
922
+ }
923
+ if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
924
+ throw new Error("Audio Node has not 'tidal' enabled, which is required to have 'tdrec' work");
925
+ }
926
+ if (source === "tts" && node._checkForPlugins && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(BuiltinSources.GoogleCloudTTS.toLowerCase()))) {
927
+ throw new Error("Audio Node has not 'tts' enabled, which is required to have 'tts' work");
928
+ }
929
+ if (source === "ftts" && !(node.info?.sourceManagers?.includes("ftts") || node.info?.sourceManagers?.includes("flowery-tts") || node.info?.sourceManagers?.includes("flowerytts"))) {
930
+ throw new Error("Audio Node has not 'flowery-tts' enabled, which is required to have 'ftts' work");
931
+ }
932
+ if (source === "ymsearch" && !node.info?.sourceManagers?.includes("yandexmusic")) {
933
+ throw new Error("Audio Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
934
+ }
935
+ if (source === "ytmsearch" && !node.info?.sourceManagers?.includes("youtube")) {
936
+ throw new Error("Audio Node has not 'youtube' enabled, which is required to have 'ytmsearch' work");
937
+ }
938
+ if (source === "ytsearch" && !node.info?.sourceManagers?.includes("youtube")) {
939
+ throw new Error("Audio Node has not 'youtube' enabled, which is required to have 'ytsearch' work");
940
+ }
941
+ if (source === "vksearch" && !node.info?.sourceManagers?.includes("vkmusic")) {
942
+ throw new Error("Audio Node has not 'vkmusic' enabled, which is required to have 'vksearch' work");
943
+ }
944
+ if (source === "vkrec" && !node.info?.sourceManagers?.includes("vkmusic")) {
945
+ throw new Error("Audio Node has not 'vkmusic' enabled, which is required to have 'vkrec' work");
946
+ }
947
+ if (source === "qbsearch" && !node.info?.sourceManagers?.includes("qobuz")) {
948
+ throw new Error("Audio Node has not 'qobuz' enabled, which is required to have 'qbsearch' work");
949
+ }
950
+ if (source === "qbisrc" && !node.info?.sourceManagers?.includes("qobuz")) {
951
+ throw new Error("Audio Node has not 'qobuz' enabled, which is required to have 'qbisrc' work");
952
+ }
953
+ if (source === "qbrec" && !node.info?.sourceManagers?.includes("qobuz")) {
954
+ throw new Error("Audio Node has not 'qobuz' enabled, which is required to have 'qbrec' work");
955
+ }
956
+ if (["pdsearch", "pdisrc", "pdrec"].includes(source) && !node.info?.sourceManagers?.includes("pandora")) {
957
+ throw new Error("Audio Node has not 'pandora' enabled, which is required to have '" + source + "' work");
958
+ }
959
+ return;
960
+ }
961
+ };
962
+ var MiniMap = class extends Map {
963
+ constructor(data = []) {
964
+ super(data);
965
+ }
966
+ filter(fn, thisArg) {
967
+ if (typeof thisArg !== "undefined") fn = fn.bind(thisArg);
968
+ const results = new this.constructor[Symbol.species]();
969
+ for (const [key, val] of this) {
970
+ if (fn(val, key, this)) results.set(key, val);
971
+ }
972
+ return results;
973
+ }
974
+ toJSON() {
975
+ return [...this.entries()];
976
+ }
977
+ map(fn, thisArg) {
978
+ if (typeof thisArg !== "undefined") fn = fn.bind(thisArg);
979
+ const iter = this.entries();
980
+ return Array.from({ length: this.size }, () => {
981
+ const [key, value] = iter.next().value;
982
+ return fn(value, key, this);
983
+ });
984
+ }
985
+ };
986
+ async function queueTrackEnd(player, dontShiftQueue = false) {
987
+ if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) {
988
+ player.queue.previous.unshift(player.queue.current);
989
+ if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
990
+ player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
991
+ await player.queue.utils.save();
992
+ }
993
+ if (player.repeatMode === "queue" && player.queue.current) player.queue.tracks.push(player.queue.current);
994
+ const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
995
+ try {
996
+ if (nextSong && player.RyanlinkManager.utils.isUnresolvedTrack(nextSong)) await nextSong.resolve(player);
997
+ player.queue.current = nextSong || null;
998
+ await player.queue.utils.save();
999
+ } catch (error) {
1000
+ if (player.RyanlinkManager.options?.advancedOptions?.enableDebugEvents) {
1001
+ player.RyanlinkManager.emit("debug", "PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
1002
+ state: "error",
1003
+ error,
1004
+ message: `queueTrackEnd Util was called, tried to resolve the next track, but failed to find the closest matching song`,
1005
+ functionLayer: "Player > play() > resolve currentTrack"
1006
+ });
1007
+ }
1008
+ player.RyanlinkManager.emit("trackError", player, player.queue.current, error);
1009
+ if (!dontShiftQueue && player.RyanlinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0]) return queueTrackEnd(player);
1010
+ }
1011
+ return player.queue.current;
1012
+ }
1013
+ async function applyUnresolvedData(resTrack, data, utils) {
1014
+ if (!resTrack?.info || !data?.info) return;
1015
+ if (data.info.uri) resTrack.info.uri = data.info.uri;
1016
+ if (utils?.RyanlinkManager?.options?.playerOptions?.useUnresolvedData === true) {
1017
+ if (data.info.artworkUrl?.length) resTrack.info.artworkUrl = data.info.artworkUrl;
1018
+ if (data.info.title?.length) resTrack.info.title = data.info.title;
1019
+ if (data.info.author?.length) resTrack.info.author = data.info.author;
1020
+ } else {
1021
+ if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title)
1022
+ resTrack.info.title = data.info.title;
1023
+ if (resTrack.info.author !== data.info.author) resTrack.info.author = data.info.author;
1024
+ if (resTrack.info.artworkUrl !== data.info.artworkUrl) resTrack.info.artworkUrl = data.info.artworkUrl;
1025
+ }
1026
+ for (const key of Object.keys(data.info))
1027
+ if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key]) resTrack.info[key] = data.info[key];
1028
+ return resTrack;
1029
+ }
1030
+ async function getClosestTrack(data, player) {
1031
+ if (!player || !player.node) throw new RangeError("No player with a ryanlink node was provided");
1032
+ if (player.RyanlinkManager.utils.isTrack(data)) return player.RyanlinkManager.utils.buildTrack(data, data.requester);
1033
+ if (!player.RyanlinkManager.utils.isUnresolvedTrack(data)) throw new RangeError("Track is not an unresolved Track");
1034
+ if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri)
1035
+ throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
1036
+ if (!data.requester) throw new SyntaxError("The requester is required");
1037
+ if (typeof data.encoded === "string") {
1038
+ const r = await player.node.decode.singleTrack(data.encoded, data.requester);
1039
+ if (r) return applyUnresolvedData(r, data, player.RyanlinkManager.utils);
1040
+ }
1041
+ if (typeof data.info.uri === "string") {
1042
+ const r = await player.search({ query: data?.info?.uri }, data.requester).then((v) => v.tracks?.[0]);
1043
+ if (r) return applyUnresolvedData(r, data, player.RyanlinkManager.utils);
1044
+ }
1045
+ const query = [data.info?.title, data.info?.author].filter((str) => !!str).join(" by ");
1046
+ const sourceName = data.info?.sourceName;
1047
+ return await player.search(
1048
+ {
1049
+ query,
1050
+ source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.RyanlinkManager.options?.playerOptions?.defaultSearchPlatform
1051
+ },
1052
+ data.requester
1053
+ ).then((res) => {
1054
+ let trackToUse = null;
1055
+ if ((data.info?.title || data.info?.author) && !trackToUse)
1056
+ trackToUse = res.tracks.find(
1057
+ (track) => [data.info?.author || "", `${data.info?.author} - Topic`].some(
1058
+ (name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)
1059
+ ) || new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title)
1060
+ );
1061
+ if (data.info?.isrc && !trackToUse) trackToUse = res.tracks.find((track) => track.info?.isrc === data.info?.isrc);
1062
+ if (data.info?.duration && !trackToUse)
1063
+ trackToUse = res.tracks.find(
1064
+ (track) => track.info?.duration >= data.info?.duration - 1500 && track?.info.duration <= data.info?.duration + 1500
1065
+ );
1066
+ return applyUnresolvedData(trackToUse || res.tracks[0], data, player.RyanlinkManager.utils);
1067
+ });
1068
+ }
1069
+ function safeStringify(obj, padding = 0) {
1070
+ const seen = /* @__PURE__ */ new WeakSet();
1071
+ return JSON.stringify(
1072
+ obj,
1073
+ (key, value) => {
1074
+ if (typeof value === "function") return void 0;
1075
+ if (typeof value === "symbol") return void 0;
1076
+ if (typeof value === "bigint") return value.toString();
1077
+ if (typeof value === "object" && value !== null) {
1078
+ if (seen.has(value)) return "[Circular]";
1079
+ seen.add(value);
1080
+ }
1081
+ return value;
1082
+ },
1083
+ padding
1084
+ );
1085
+ }
1086
+
1087
+ // src/node/Node.ts
1088
+ var RyanlinkNode = class _RyanlinkNode {
1089
+ heartBeatPingTimestamp = 0;
1090
+ heartBeatPongTimestamp = 0;
1091
+ heartBeatInterval;
1092
+ pingTimeout;
1093
+ nodeType = "Core";
1094
+ isAlive = false;
1095
+ static _NodeLinkClass = null;
1096
+ options;
1097
+ calls = 0;
1098
+ stats = {
1099
+ players: 0,
1100
+ playingPlayers: 0,
1101
+ cpu: {
1102
+ cores: 0,
1103
+ audioLoad: 0,
1104
+ systemLoad: 0
1105
+ },
1106
+ memory: {
1107
+ allocated: 0,
1108
+ free: 0,
1109
+ reservable: 0,
1110
+ used: 0
1111
+ },
1112
+ uptime: 0,
1113
+ detailedStats: {
1114
+ api: {
1115
+ requests: {},
1116
+ errors: {}
1117
+ },
1118
+ sources: {},
1119
+ playback: {
1120
+ events: {}
1121
+ }
1122
+ },
1123
+ frameStats: {
1124
+ deficit: 0,
1125
+ nulled: 0,
1126
+ sent: 0
1127
+ }
1128
+ };
1129
+ sessionId = null;
1130
+ resuming = { enabled: true, timeout: null };
1131
+ info = null;
1132
+ reconnectionState = "IDLE" /* IDLE */;
1133
+ NodeManager = null;
1134
+ reconnectTimeout = void 0;
1135
+ reconnectAttempts = [];
1136
+ socket = null;
1137
+ version = "v4";
1138
+ get _LManager() {
1139
+ return this.NodeManager.RyanlinkManager;
1140
+ }
1141
+ get heartBeatPing() {
1142
+ return this.heartBeatPongTimestamp - this.heartBeatPingTimestamp;
1143
+ }
1144
+ get _checkForPlugins() {
1145
+ if (this.nodeType === "NodeLink") return false;
1146
+ return !!this.options?.autoChecks?.pluginValidations;
1147
+ }
1148
+ get _checkForSources() {
1149
+ return !!this.options?.autoChecks?.sourcesValidations;
1150
+ }
1151
+ dispatchDebug(name, eventData) {
1152
+ if (!this._LManager.options?.advancedOptions?.enableDebugEvents) return;
1153
+ this._LManager.emit("debug", name, eventData);
1154
+ }
1155
+ get connected() {
1156
+ return this.socket && this.socket.readyState === import_ws.default.OPEN;
1157
+ }
1158
+ get connectionStatus() {
1159
+ if (!this.socket) throw new Error("no websocket was initialized yet");
1160
+ return ["CONNECTING", "OPEN", "CLOSING", "CLOSED"][this.socket.readyState] || "UNKNOWN";
1161
+ }
1162
+ constructor(options, manager) {
1163
+ this.options = {
1164
+ secure: false,
1165
+ retryAmount: 5,
1166
+ retryDelay: 1e4,
1167
+ retryTimespan: -1,
1168
+ requestSignalTimeoutMS: 1e4,
1169
+ heartBeatInterval: 3e4,
1170
+ enablePingOnStatsCheck: true,
1171
+ closeOnError: true,
1172
+ ...options,
1173
+ autoChecks: {
1174
+ sourcesValidations: options?.autoChecks?.sourcesValidations ?? true,
1175
+ pluginValidations: options?.autoChecks?.pluginValidations ?? true
1176
+ }
1177
+ };
1178
+ if (this.options.nodeType === "NodeLink" && this.constructor.name === "RyanlinkNode" && _RyanlinkNode._NodeLinkClass) {
1179
+ return new _RyanlinkNode._NodeLinkClass(options, manager);
1180
+ }
1181
+ this.nodeType = this.options.nodeType || "Core";
1182
+ this.NodeManager = manager;
1183
+ this.validate();
1184
+ if (this.options.secure && this.options.port !== 443) throw new SyntaxError("If secure is true, then the port must be 443");
1185
+ this.options.regions = (this.options.regions || []).map((a) => a.toLowerCase());
1186
+ Object.defineProperty(this, AudioNodeSymbol, { configurable: true, value: true });
1187
+ }
1188
+ async rawRequest(endpoint, modify) {
1189
+ const options = {
1190
+ path: `/${this.version}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`,
1191
+ method: "GET",
1192
+ headers: {
1193
+ Authorization: this.options.authorization
1194
+ },
1195
+ signal: this.options.requestSignalTimeoutMS && this.options.requestSignalTimeoutMS > 0 ? AbortSignal.timeout(this.options.requestSignalTimeoutMS) : void 0
1196
+ };
1197
+ modify?.(options);
1198
+ const url = new URL(`${this.restAddress}${options.path}`);
1199
+ url.searchParams.append("trace", "true");
1200
+ if (options.extraQueryUrlParams && options.extraQueryUrlParams?.size > 0) {
1201
+ for (const [paramKey, paramValue] of options.extraQueryUrlParams.entries()) {
1202
+ url.searchParams.append(paramKey, paramValue);
1203
+ }
1204
+ }
1205
+ const urlToUse = url.toString();
1206
+ const { path, extraQueryUrlParams, ...fetchOptions } = options;
1207
+ const response = await fetch(urlToUse, fetchOptions);
1208
+ this.calls++;
1209
+ return { response, options };
1210
+ }
1211
+ async request(endpoint, modify, parseAsText) {
1212
+ if (!this.connected) throw new Error("The node is not connected to the Ryanlink Server!, Please call node.connect() first!");
1213
+ const { response, options } = await this.rawRequest(endpoint, modify);
1214
+ if (["DELETE", "PUT"].includes(options.method)) return;
1215
+ if (response.status === 204) return;
1216
+ if (response.status === 404)
1217
+ throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`);
1218
+ return parseAsText ? await response.text() : await response.json();
1219
+ }
1220
+ async search(query, requestUser, throwOnEmpty = false) {
1221
+ const Query = this._LManager.utils.transformQuery(query);
1222
+ this._LManager.utils.validateQueryString(this, Query.query, Query.source);
1223
+ if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
1224
+ if (["bcsearch", "bandcamp"].includes(Query.source) && this._checkForSources && !this.info.sourceManagers.includes("bandcamp")) {
1225
+ throw new Error("Bandcamp Search only works on the player (audio-engine version < 2.2.0!");
1226
+ }
1227
+ const requestUrl = new URL(`${this.restAddress}/loadtracks`);
1228
+ if (/^https?:\/\//.test(Query.query)) {
1229
+ requestUrl.searchParams.append("identifier", Query.query);
1230
+ } else {
1231
+ const fttsPrefix = Query.source === "ftts" ? "//" : "";
1232
+ const prefix = Query.source !== "local" ? `${Query.source}:${fttsPrefix}` : "";
1233
+ requestUrl.searchParams.append("identifier", `${prefix}${Query.query}`);
1234
+ }
1235
+ const requestPathAndSearch = requestUrl.pathname + requestUrl.search;
1236
+ const res = await this.request(requestPathAndSearch, (options) => {
1237
+ if (typeof query === "object" && typeof query.extraQueryUrlParams?.size === "number" && query.extraQueryUrlParams?.size > 0) {
1238
+ options.extraQueryUrlParams = query.extraQueryUrlParams;
1239
+ }
1240
+ });
1241
+ const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
1242
+ if (throwOnEmpty === true && (res.loadType === "empty" || !resTracks.length)) {
1243
+ this.dispatchDebug("SearchNothingFound" /* SearchNothingFound */, {
1244
+ state: "warn",
1245
+ message: `Search found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
1246
+ functionLayer: "(RyanlinkNode > node | player) > search()"
1247
+ });
1248
+ throw new Error("Nothing found");
1249
+ }
1250
+ return {
1251
+ loadType: res.loadType,
1252
+ exception: res.loadType === "error" ? res.data : null,
1253
+ pluginInfo: res.pluginInfo || {},
1254
+ playlist: res.loadType === "playlist" ? {
1255
+ name: res.data.info?.name || res.data.pluginInfo?.name || null,
1256
+ title: res.data.info?.name || res.data.pluginInfo?.name || null,
1257
+ author: res.data.info?.author || res.data.pluginInfo?.author || null,
1258
+ thumbnail: res.data.info?.artworkUrl || res.data.pluginInfo?.artworkUrl || (typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? resTracks[res.data?.info?.selectedTrack]?.info?.artworkUrl || resTracks[res.data?.info?.selectedTrack]?.info?.pluginInfo?.artworkUrl : null) || null,
1259
+ uri: res.data.info?.url || res.data.info?.uri || res.data.info?.link || res.data.pluginInfo?.url || res.data.pluginInfo?.uri || res.data.pluginInfo?.link || null,
1260
+ selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this._LManager.utils.buildTrack(resTracks[res.data?.info?.selectedTrack], requestUser) : null,
1261
+ duration: resTracks.length ? resTracks.reduce(
1262
+ (acc, cur) => acc + (cur?.info?.duration || cur?.info?.length || 0),
1263
+ 0
1264
+ ) : 0
1265
+ } : null,
1266
+ tracks: resTracks.length ? resTracks.map((t) => this._LManager.utils.buildTrack(t, requestUser)) : []
1267
+ };
1268
+ }
1269
+ async audioSearch(query, requestUser, throwOnEmpty = false) {
1270
+ const Query = this._LManager.utils.transformAudioSearchQuery(query);
1271
+ if (Query.source) this._LManager.utils.validateSourceString(this, Query.source);
1272
+ if (/^https?:\/\//.test(Query.query)) return this.search({ query: Query.query, source: Query.source }, requestUser);
1273
+ if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
1274
+ throw new SyntaxError(
1275
+ `Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`
1276
+ );
1277
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "search-engine"))
1278
+ throw new RangeError(`there is no search-engine available in the ryanlink node: ${this.id}`);
1279
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "source-engine"))
1280
+ throw new RangeError(`there is no source-engine available in the ryanlink node: ${this.id}`);
1281
+ const { response } = await this.rawRequest(
1282
+ `/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`
1283
+ );
1284
+ const res = response.status === 204 ? {} : await response.json();
1285
+ if (throwOnEmpty === true && !Object.entries(res).flat().filter(Boolean).length) {
1286
+ this.dispatchDebug("SearchNothingFound" /* SearchNothingFound */, {
1287
+ state: "warn",
1288
+ message: `LavaSearch found nothing for Request: "${Query.source ? `${Query.source}:` : ""}${Query.query}"`,
1289
+ functionLayer: "(RyanlinkNode > node | player) > audioSearch()"
1290
+ });
1291
+ throw new Error("Nothing found");
1292
+ }
1293
+ return {
1294
+ tracks: res.tracks?.map((v) => this._LManager.utils.buildTrack(v, requestUser)) || [],
1295
+ albums: res.albums?.map((v) => ({
1296
+ info: v.info,
1297
+ pluginInfo: v?.plugin || v.pluginInfo,
1298
+ tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
1299
+ })) || [],
1300
+ artists: res.artists?.map((v) => ({
1301
+ info: v.info,
1302
+ pluginInfo: v?.plugin || v.pluginInfo,
1303
+ tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
1304
+ })) || [],
1305
+ playlists: res.playlists?.map((v) => ({
1306
+ info: v.info,
1307
+ pluginInfo: v?.plugin || v.pluginInfo,
1308
+ tracks: v.tracks.map((v2) => this._LManager.utils.buildTrack(v2, requestUser))
1309
+ })) || [],
1310
+ texts: res.texts?.map((v) => ({
1311
+ text: v.text,
1312
+ pluginInfo: v?.plugin || v.pluginInfo
1313
+ })) || [],
1314
+ pluginInfo: res.pluginInfo || res?.plugin
1315
+ };
1316
+ }
1317
+ async updatePlayer(data) {
1318
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
1319
+ this.syncPlayerData(data);
1320
+ const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, (r) => {
1321
+ r.method = "PATCH";
1322
+ r.headers["Content-Type"] = "application/json";
1323
+ r.body = safeStringify(data.playerOptions);
1324
+ if (data.noReplace) {
1325
+ const url = new URL(`${this.restAddress}${r.path}`);
1326
+ url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
1327
+ r.path = url.pathname + url.search;
1328
+ }
1329
+ });
1330
+ this.dispatchDebug("PlayerUpdateSuccess" /* PlayerUpdateSuccess */, {
1331
+ state: "log",
1332
+ message: `Player get's updated with following payload :: ${safeStringify(data.playerOptions, 3)}`,
1333
+ functionLayer: "RyanlinkNode > node > updatePlayer()"
1334
+ });
1335
+ this.syncPlayerData({}, res);
1336
+ return res;
1337
+ }
1338
+ async destroyPlayer(guildId) {
1339
+ if (!this.sessionId) throw new Error("The Ryanlink-Node is either not ready, or not up to date!");
1340
+ return this.request(`/sessions/${this.sessionId}/players/${guildId}`, (r) => {
1341
+ r.method = "DELETE";
1342
+ });
1343
+ }
1344
+ connect(sessionId) {
1345
+ if (this.connected) {
1346
+ this.dispatchDebug("TryingConnectWhileConnected" /* TryingConnectWhileConnected */, {
1347
+ state: "warn",
1348
+ message: `Tryed to connect to node, but it's already connected!`,
1349
+ functionLayer: "RyanlinkNode > node > connect()"
1350
+ });
1351
+ return;
1352
+ }
1353
+ const headers = {
1354
+ Authorization: this.options.authorization,
1355
+ "User-Id": this._LManager.options.client.id,
1356
+ "Client-Name": `${"ryanlink"}/${"2.0.0"}`
1357
+ };
1358
+ if (typeof this.options.sessionId === "string" || typeof sessionId === "string") {
1359
+ headers["Session-Id"] = this.options.sessionId || sessionId;
1360
+ this.sessionId = this.options.sessionId || sessionId;
1361
+ }
1362
+ this.socket = new import_ws.default(`ws${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`, { headers });
1363
+ this.socket.on("open", this.open.bind(this));
1364
+ this.socket.on("close", (code, reason) => this.close(code, reason?.toString()));
1365
+ this.socket.on("message", this.message.bind(this));
1366
+ this.socket.on("error", this.error.bind(this));
1367
+ }
1368
+ heartBeat() {
1369
+ if (this.nodeType !== "Core") return;
1370
+ this.dispatchDebug("HeartBeatTriggered" /* HeartBeatTriggered */, {
1371
+ state: "log",
1372
+ message: `Node Socket Heartbeat triggered, resetting old Timeout to 65000ms (should happen every 60s due to /stats event)`,
1373
+ functionLayer: "RyanlinkNode > nodeEvent > stats > heartBeat()"
1374
+ });
1375
+ this.resetAckTimeouts(false, true);
1376
+ this.pingTimeout = setTimeout(() => {
1377
+ this.pingTimeout = null;
1378
+ if (!this.socket) {
1379
+ return this.dispatchDebug("NoSocketOnDestroy" /* NoSocketOnDestroy */, {
1380
+ state: "error",
1381
+ message: `Heartbeat registered a disconnect, but socket didn't exist therefore can't terminate`,
1382
+ functionLayer: "RyanlinkNode > nodeEvent > stats > heartBeat() > timeoutHit"
1383
+ });
1384
+ }
1385
+ this.dispatchDebug("SocketTerminateHeartBeatTimeout" /* SocketTerminateHeartBeatTimeout */, {
1386
+ state: "warn",
1387
+ message: `Heartbeat registered a disconnect, because timeout wasn't resetted in time. Terminating Web-Socket`,
1388
+ functionLayer: "RyanlinkNode > nodeEvent > stats > heartBeat() > timeoutHit"
1389
+ });
1390
+ this.isAlive = false;
1391
+ this.socket.terminate();
1392
+ }, 65e3);
1393
+ }
1394
+ get id() {
1395
+ return this.options.id || `${this.options.host}:${this.options.port}`;
1396
+ }
1397
+ destroy(destroyReason, deleteNode = true, movePlayers = false) {
1398
+ this.reconnectionState = "IDLE" /* IDLE */;
1399
+ const players = this._LManager.players.filter((p) => p.node.id === this.id);
1400
+ if (!players?.size) {
1401
+ this.socket?.close(1e3, "Node-Destroy");
1402
+ this.socket?.removeAllListeners();
1403
+ this.socket = null;
1404
+ this.resetReconnectionAttempts();
1405
+ if (!deleteNode)
1406
+ return void this.NodeManager.emit("disconnect", this, {
1407
+ code: 1e3,
1408
+ reason: destroyReason
1409
+ });
1410
+ this.NodeManager.emit("destroy", this, destroyReason);
1411
+ this.NodeManager.nodes.delete(this.id);
1412
+ this.resetAckTimeouts(true, true);
1413
+ return;
1414
+ }
1415
+ const handlePlayerOperations = () => {
1416
+ if (!movePlayers) {
1417
+ return Promise.allSettled(
1418
+ Array.from(players.values()).map(
1419
+ (player) => player.destroy(destroyReason || "NodeDestroy" /* NodeDestroy */).catch((error) => {
1420
+ this.dispatchDebug("PlayerDestroyFail" /* PlayerDestroyFail */, {
1421
+ state: "error",
1422
+ message: `Failed to destroy player ${player.guildId}: ${error.message}`,
1423
+ error,
1424
+ functionLayer: "Node > destroy() > movePlayers"
1425
+ });
1426
+ })
1427
+ )
1428
+ );
1429
+ }
1430
+ const nodeToMove = Array.from(this.NodeManager.leastUsedNodes("playingPlayers")).find((n) => n.connected && n.options.id !== this.id);
1431
+ if (!nodeToMove) {
1432
+ return Promise.allSettled(
1433
+ Array.from(players.values()).map(
1434
+ (player) => player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((error) => {
1435
+ this.dispatchDebug("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
1436
+ state: "error",
1437
+ message: `Failed to destroy player ${player.guildId}: ${error.message}`,
1438
+ error,
1439
+ functionLayer: "Node > destroy() > movePlayers"
1440
+ });
1441
+ })
1442
+ )
1443
+ );
1444
+ }
1445
+ return Promise.allSettled(
1446
+ Array.from(players.values()).map(
1447
+ (player) => player.changeNode(nodeToMove.options.id).catch((error) => {
1448
+ this.dispatchDebug("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
1449
+ state: "error",
1450
+ message: `Failed to move player ${player.guildId}: ${error.message}`,
1451
+ error,
1452
+ functionLayer: "Node > destroy() > movePlayers"
1453
+ });
1454
+ return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
1455
+ this.dispatchDebug("PlayerDestroyFail" /* PlayerDestroyFail */, {
1456
+ state: "error",
1457
+ message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
1458
+ error: destroyError,
1459
+ functionLayer: "Node > destroy() > movePlayers"
1460
+ });
1461
+ });
1462
+ })
1463
+ )
1464
+ );
1465
+ };
1466
+ return void handlePlayerOperations().finally(() => {
1467
+ this.socket?.close(1e3, "Node-Destroy");
1468
+ this.socket?.removeAllListeners();
1469
+ this.socket = null;
1470
+ this.resetReconnectionAttempts();
1471
+ if (!deleteNode)
1472
+ return void this.NodeManager.emit("disconnect", this, {
1473
+ code: 1e3,
1474
+ reason: destroyReason
1475
+ });
1476
+ this.NodeManager.emit("destroy", this, destroyReason);
1477
+ this.NodeManager.nodes.delete(this.id);
1478
+ this.resetAckTimeouts(true, true);
1479
+ return;
1480
+ });
1481
+ }
1482
+ disconnect(disconnectReason) {
1483
+ if (!this.connected) return;
1484
+ this.socket?.close(1e3, "Node-Disconnect");
1485
+ this.socket?.removeAllListeners();
1486
+ this.socket = null;
1487
+ this.reconnectionState = "IDLE" /* IDLE */;
1488
+ this.resetReconnectionAttempts();
1489
+ this.NodeManager.emit("disconnect", this, { code: 1e3, reason: disconnectReason });
1490
+ }
1491
+ async fetchAllPlayers() {
1492
+ if (!this.sessionId) throw new Error("The Ryanlink-Node is either not ready, or not up to date!");
1493
+ return this.request(`/sessions/${this.sessionId}/players`) || [];
1494
+ }
1495
+ async fetchPlayer(guildId) {
1496
+ if (!this.sessionId) throw new Error("The Ryanlink-Node is either not ready, or not up to date!");
1497
+ return this.request(`/sessions/${this.sessionId}/players/${guildId}`);
1498
+ }
1499
+ async updateSession(resuming, timeout) {
1500
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1501
+ const data = {};
1502
+ if (typeof resuming === "boolean") data.resuming = resuming;
1503
+ if (typeof timeout === "number" && timeout > 0) data.timeout = timeout;
1504
+ this.resuming = {
1505
+ enabled: typeof resuming === "boolean" ? resuming : false,
1506
+ timeout: typeof resuming === "boolean" && resuming === true ? timeout : null
1507
+ };
1508
+ return this.request(`/sessions/${this.sessionId}`, (r) => {
1509
+ r.method = "PATCH";
1510
+ r.headers = { Authorization: this.options.authorization, "Content-Type": "application/json" };
1511
+ r.body = safeStringify(data);
1512
+ });
1513
+ }
1514
+ decode = {
1515
+ singleTrack: async (encoded, requester) => {
1516
+ if (!encoded) throw new SyntaxError("No encoded (Base64 string) was provided");
1517
+ return this._LManager.utils?.buildTrack(
1518
+ await this.request(`/decodetrack?encodedTrack=${encodeURIComponent(encoded.replace(/\s/g, ""))}`),
1519
+ requester
1520
+ );
1521
+ },
1522
+ multipleTracks: async (encodeds, requester) => {
1523
+ if (!Array.isArray(encodeds) || !encodeds.every((v) => typeof v === "string" && v.length > 1))
1524
+ throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
1525
+ return await this.request(`/decodetracks`, (r) => {
1526
+ r.method = "POST";
1527
+ r.body = safeStringify(encodeds);
1528
+ r.headers["Content-Type"] = "application/json";
1529
+ }).then((r) => r.map((track) => this._LManager.utils.buildTrack(track, requester)));
1530
+ }
1531
+ };
1532
+ lyrics = {
1533
+ get: async (track, skipTrackSource = false) => {
1534
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1535
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
1536
+ throw new RangeError(`there is no lavalyrics-plugin available in the ryanlink node (required for lyrics): ${this.id}`);
1537
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "source-engine") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lyrics-engine"))
1538
+ throw new RangeError(
1539
+ `there is no lyrics source (via source-engine / lyrics-engine) available in the ryanlink node (required for lyrics): ${this.id}`
1540
+ );
1541
+ const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
1542
+ return await this.request(url);
1543
+ },
1544
+ getCurrent: async (guildId, skipTrackSource = false) => {
1545
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1546
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
1547
+ throw new RangeError(`there is no lavalyrics-plugin available in the ryanlink node (required for lyrics): ${this.id}`);
1548
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "source-engine") && this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lyrics-engine"))
1549
+ throw new RangeError(
1550
+ `there is no lyrics source (via source-engine / lyrics-engine) available in the ryanlink node (required for lyrics): ${this.id}`
1551
+ );
1552
+ const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
1553
+ return await this.request(url);
1554
+ },
1555
+ subscribe: async (guildId, skipTrackSource) => {
1556
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1557
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
1558
+ throw new RangeError(`there is no lavalyrics-plugin available in the ryanlink node (required for lyrics): ${this.id}`);
1559
+ return await this.request(
1560
+ `/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe?skipTrackSource=${skipTrackSource ?? false}`,
1561
+ (options) => {
1562
+ options.method = "POST";
1563
+ }
1564
+ );
1565
+ },
1566
+ unsubscribe: async (guildId) => {
1567
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1568
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "lavalyrics-plugin"))
1569
+ throw new RangeError(`there is no lavalyrics-plugin available in the ryanlink node (required for lyrics): ${this.id}`);
1570
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
1571
+ options.method = "DELETE";
1572
+ });
1573
+ }
1574
+ };
1575
+ async fetchStats() {
1576
+ return await this.request(`/stats`);
1577
+ }
1578
+ async fetchConnectionMetrics() {
1579
+ if (this.info && !this.info.isNodelink)
1580
+ throw new Error("There is no Information about wether you are using NodeLink instead of Lavalink, so this function won't work");
1581
+ return await this.request(`/connection`);
1582
+ }
1583
+ async fetchVersion() {
1584
+ return await this.request(
1585
+ `/version`,
1586
+ (r) => {
1587
+ r.path = "/version";
1588
+ },
1589
+ true
1590
+ );
1591
+ }
1592
+ async fetchInfo() {
1593
+ return await this.request(`/info`);
1594
+ }
1595
+ nodeMetricSummary() {
1596
+ if (!this.connected || !this.isAlive)
1597
+ return {
1598
+ systemLoad: 0,
1599
+ cpuLoad: 0,
1600
+ memoryUsage: 0,
1601
+ players: 0,
1602
+ playingPlayers: 0,
1603
+ uptime: 0,
1604
+ ping: 0,
1605
+ frameDeficit: 0
1606
+ };
1607
+ const _memoryUsed = this.stats.memory.used;
1608
+ const _memoryAllocated = this.stats.memory.allocated;
1609
+ return {
1610
+ systemLoad: this.stats.cpu.systemLoad,
1611
+ cpuLoad: this.stats.cpu.audioLoad,
1612
+ memoryUsage: _memoryAllocated > 0 ? _memoryUsed / _memoryAllocated * 100 : 0,
1613
+ players: this.stats.players,
1614
+ playingPlayers: this.stats.playingPlayers,
1615
+ uptime: this.stats.uptime,
1616
+ ping: this.heartBeatPing,
1617
+ frameDeficit: this.stats.frameStats?.deficit || 0
1618
+ };
1619
+ }
1620
+ getHealthStatus(thresholds) {
1621
+ const cpuThresholds = {
1622
+ excellent: 0.3,
1623
+ good: 0.5,
1624
+ fair: 0.7,
1625
+ poor: 0.85,
1626
+ ...thresholds?.cpu
1627
+ };
1628
+ const memoryThresholds = {
1629
+ excellent: 60,
1630
+ good: 75,
1631
+ fair: 85,
1632
+ poor: 95,
1633
+ ...thresholds?.memory
1634
+ };
1635
+ const pingThresholds = {
1636
+ excellent: 50,
1637
+ good: 100,
1638
+ fair: 200,
1639
+ poor: 300,
1640
+ ...thresholds?.ping
1641
+ };
1642
+ const recommendations = [];
1643
+ const metrics = this.nodeMetricSummary();
1644
+ if (!this.connected || !this.isAlive) {
1645
+ return {
1646
+ status: "offline",
1647
+ performance: "poor",
1648
+ isOverloaded: false,
1649
+ needsRestart: true,
1650
+ penaltyScore: 999999,
1651
+ estimatedRemainingCapacity: 0,
1652
+ recommendations: [RecommendationsStrings.nodeOffline, RecommendationsStrings.checkConnectivity],
1653
+ metrics
1654
+ };
1655
+ }
1656
+ let cpuScore = 0;
1657
+ if (metrics.cpuLoad < cpuThresholds.excellent) cpuScore = 4;
1658
+ else if (metrics.cpuLoad < cpuThresholds.good) cpuScore = 3;
1659
+ else if (metrics.cpuLoad < cpuThresholds.fair) cpuScore = 2;
1660
+ else if (metrics.cpuLoad < cpuThresholds.poor) cpuScore = 1;
1661
+ let memoryScore = 0;
1662
+ if (metrics.memoryUsage < memoryThresholds.excellent) memoryScore = 4;
1663
+ else if (metrics.memoryUsage < memoryThresholds.good) memoryScore = 3;
1664
+ else if (metrics.memoryUsage < memoryThresholds.fair) memoryScore = 2;
1665
+ else if (metrics.memoryUsage < memoryThresholds.poor) memoryScore = 1;
1666
+ let pingScore = 0;
1667
+ if (metrics.ping < pingThresholds.excellent) pingScore = 4;
1668
+ else if (metrics.ping < pingThresholds.good) pingScore = 3;
1669
+ else if (metrics.ping < pingThresholds.fair) pingScore = 2;
1670
+ else if (metrics.ping < pingThresholds.poor) pingScore = 1;
1671
+ const avgScore = (cpuScore + memoryScore + pingScore) / 3;
1672
+ let performance2 = "poor";
1673
+ if (avgScore >= 3.5) performance2 = "excellent";
1674
+ else if (avgScore >= 2.5) performance2 = "good";
1675
+ else if (avgScore >= 1.5) performance2 = "fair";
1676
+ const isOverloaded = metrics.cpuLoad > cpuThresholds.fair || metrics.memoryUsage > memoryThresholds.fair || metrics.frameDeficit > 100;
1677
+ const isCritical = metrics.cpuLoad > cpuThresholds.poor || metrics.memoryUsage > memoryThresholds.poor || metrics.frameDeficit > 500;
1678
+ const status = isCritical ? "critical" : isOverloaded ? "degraded" : "healthy";
1679
+ const needsRestart = status === "critical" || isOverloaded && metrics.memoryUsage > 90 || metrics.frameDeficit > 1e3 || this.reconnectionAttemptCount > 0 && this.reconnectionAttemptCount >= this.options.retryAmount / 2;
1680
+ if (metrics.cpuLoad > cpuThresholds.fair) recommendations.push(RecommendationsStrings.highCPULoad(metrics.cpuLoad));
1681
+ if (metrics.systemLoad > 0.8) recommendations.push(RecommendationsStrings.highSystemLoad(metrics.systemLoad));
1682
+ if (metrics.memoryUsage > memoryThresholds.fair) recommendations.push(RecommendationsStrings.highMemoryUsage(metrics.memoryUsage));
1683
+ if (metrics.frameDeficit > 100) recommendations.push(RecommendationsStrings.frameDeficit(metrics.frameDeficit));
1684
+ if (metrics.ping > pingThresholds.fair) recommendations.push(RecommendationsStrings.highLatency(metrics.ping));
1685
+ if (needsRestart) recommendations.push(RecommendationsStrings.nodeRestart);
1686
+ if (metrics.players > 500) recommendations.push(RecommendationsStrings.highPlayercount(metrics.players));
1687
+ const nullFrames = this.stats.frameStats?.nulled || 0;
1688
+ let penaltyScore = metrics.players + Math.pow(metrics.cpuLoad * 100, 2) + Math.pow(metrics.memoryUsage, 1.5) + metrics.ping * 2 + metrics.frameDeficit * 10 + nullFrames * 5;
1689
+ if (status === "critical") penaltyScore += 1e4;
1690
+ else if (status === "degraded") penaltyScore += 5e3;
1691
+ if (this.reconnectionAttemptCount > 0) penaltyScore += this.reconnectionAttemptCount * 1e3;
1692
+ penaltyScore = Math.round(penaltyScore);
1693
+ let estimatedRemainingCapacity = 0;
1694
+ if (status !== "critical") {
1695
+ const cpuCapacity = metrics.players === 0 ? 200 : metrics.cpuLoad > 0 ? Math.max(0, Math.floor((cpuThresholds.fair - metrics.cpuLoad) / metrics.cpuLoad * metrics.players)) : 200;
1696
+ const memoryCapacity = metrics.players === 0 ? 200 : metrics.memoryUsage > 0 ? Math.max(0, Math.floor((memoryThresholds.fair - metrics.memoryUsage) / metrics.memoryUsage * metrics.players)) : 200;
1697
+ estimatedRemainingCapacity = Math.min(Math.min(cpuCapacity, memoryCapacity), 500);
1698
+ if (isOverloaded) estimatedRemainingCapacity = 0;
1699
+ }
1700
+ return {
1701
+ status,
1702
+ performance: performance2,
1703
+ isOverloaded,
1704
+ needsRestart,
1705
+ penaltyScore,
1706
+ estimatedRemainingCapacity,
1707
+ recommendations,
1708
+ metrics
1709
+ };
1710
+ }
1711
+ routePlannerApi = {
1712
+ getStatus: async () => {
1713
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1714
+ return await this.request(`/routeplanner/status`);
1715
+ },
1716
+ unmarkFailedAddress: async (address) => {
1717
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1718
+ return await this.request(`/routeplanner/free/address`, (r) => {
1719
+ r.method = "POST";
1720
+ r.headers["Content-Type"] = "application/json";
1721
+ r.body = safeStringify({ address });
1722
+ });
1723
+ },
1724
+ unmarkAllFailedAddresses: async () => {
1725
+ if (!this.sessionId) throw new Error("the Ryanlink-Node is either not ready, or not up to date!");
1726
+ return await this.request(`/routeplanner/free/all`, (r) => {
1727
+ r.method = "POST";
1728
+ r.headers["Content-Type"] = "application/json";
1729
+ });
1730
+ }
1731
+ };
1732
+ validate() {
1733
+ if (!this.options.authorization) throw new SyntaxError("RyanlinkNode requires 'authorization'");
1734
+ if (!this.options.host) throw new SyntaxError("RyanlinkNode requires 'host'");
1735
+ if (!this.options.port) throw new SyntaxError("RyanlinkNode requires 'port'");
1736
+ if (typeof this.options.port !== "number" || this.options.port < 1 || this.options.port > 65535)
1737
+ throw new SyntaxError("RyanlinkNode.port must be a number within 1 and 65535");
1738
+ if (this.options.closeOnError !== void 0 && typeof this.options.closeOnError !== "boolean")
1739
+ throw new SyntaxError("RyanlinkNode.closeOnError must be either false | true aka boolean");
1740
+ if (this.options.retryDelay !== void 0 && typeof this.options.retryDelay !== "number")
1741
+ throw new SyntaxError("NodeConfiguration.retryDelay must be a number");
1742
+ if (this.options.retryAmount !== void 0 && typeof this.options.retryAmount !== "number")
1743
+ throw new SyntaxError("NodeConfiguration.retryAmount must be a number");
1744
+ if (this.options.retryTimespan !== void 0 && typeof this.options.retryTimespan !== "number")
1745
+ throw new SyntaxError("NodeConfiguration.retryTimespan must be a number");
1746
+ if (this.options.requestSignalTimeoutMS !== void 0 && typeof this.options.requestSignalTimeoutMS !== "number")
1747
+ throw new SyntaxError("NodeConfiguration.requestSignalTimeoutMS must be a number");
1748
+ if (this.options.heartBeatInterval !== void 0 && typeof this.options.heartBeatInterval !== "number")
1749
+ throw new SyntaxError("NodeConfiguration.heartBeatInterval must be a number");
1750
+ if (this.options.enablePingOnStatsCheck !== void 0 && typeof this.options.enablePingOnStatsCheck !== "boolean")
1751
+ throw new SyntaxError("NodeConfiguration.enablePingOnStatsCheck must be either false | true aka boolean");
1752
+ if (this.options.autoChecks !== void 0 && typeof this.options.autoChecks !== "object")
1753
+ throw new SyntaxError("RyanlinkNode.autoChecks must be an object");
1754
+ if (this.options?.autoChecks?.sourcesValidations !== void 0 && typeof this.options?.autoChecks?.sourcesValidations !== "boolean")
1755
+ throw new SyntaxError("RyanlinkNode.autoChecks.sourcesValidations must be either false | true aka boolean");
1756
+ if (this.options?.autoChecks?.pluginValidations !== void 0 && typeof this.options?.autoChecks?.pluginValidations !== "boolean")
1757
+ throw new SyntaxError("RyanlinkNode.autoChecks.pluginValidations must be either false | true aka boolean");
1758
+ if (this.options.regions !== void 0 && (!Array.isArray(this.options.regions) || !this.options.regions.every((r) => typeof r === "string")))
1759
+ throw new SyntaxError("RyanlinkNode.regions must be an Array of strings");
1760
+ }
1761
+ isNodeLink() {
1762
+ return this.nodeType === "NodeLink";
1763
+ }
1764
+ isRyanlinkNode() {
1765
+ return this.nodeType === "Core";
1766
+ }
1767
+ syncPlayerData(data, res) {
1768
+ if (typeof data === "object" && typeof data?.guildId === "string" && typeof data.playerOptions === "object" && Object.keys(data.playerOptions).length > 0) {
1769
+ const player = this._LManager.getPlayer(data.guildId);
1770
+ if (!player) return;
1771
+ if (typeof data.playerOptions.paused !== "undefined") {
1772
+ player.paused = data.playerOptions.paused;
1773
+ player.playing = !data.playerOptions.paused;
1774
+ }
1775
+ if (typeof data.playerOptions.position === "number") {
1776
+ player.lastPosition = data.playerOptions.position;
1777
+ player.lastPositionChange = Date.now();
1778
+ }
1779
+ if (typeof data.playerOptions.voice !== "undefined") player.voice = data.playerOptions.voice;
1780
+ if (typeof data.playerOptions.volume !== "undefined") {
1781
+ if (this._LManager.options.playerOptions.volumeDecrementer) {
1782
+ player.volume = Math.round(data.playerOptions.volume / this._LManager.options.playerOptions.volumeDecrementer);
1783
+ player.internalVolume = Math.round(data.playerOptions.volume);
1784
+ } else {
1785
+ player.volume = Math.round(data.playerOptions.volume);
1786
+ player.internalVolume = Math.round(data.playerOptions.volume);
1787
+ }
1788
+ }
1789
+ if (typeof data.playerOptions.filters !== "undefined") {
1790
+ const oldFilterTimescale = { ...player.filterManager.data.timescale };
1791
+ Object.freeze(oldFilterTimescale);
1792
+ if (data.playerOptions.filters.timescale) player.filterManager.data.timescale = data.playerOptions.filters.timescale;
1793
+ if (data.playerOptions.filters.distortion) player.filterManager.data.distortion = data.playerOptions.filters.distortion;
1794
+ if (data.playerOptions.filters.pluginFilters) player.filterManager.data.pluginFilters = data.playerOptions.filters.pluginFilters;
1795
+ if (data.playerOptions.filters.vibrato) player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
1796
+ if (data.playerOptions.filters.volume) player.filterManager.data.volume = data.playerOptions.filters.volume;
1797
+ if (data.playerOptions.filters.equalizer) player.filterManager.equalizerBands = data.playerOptions.filters.equalizer;
1798
+ if (data.playerOptions.filters.karaoke) player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
1799
+ if (data.playerOptions.filters.lowPass) player.filterManager.data.lowPass = data.playerOptions.filters.lowPass;
1800
+ if (data.playerOptions.filters.rotation) player.filterManager.data.rotation = data.playerOptions.filters.rotation;
1801
+ if (data.playerOptions.filters.tremolo) player.filterManager.data.tremolo = data.playerOptions.filters.tremolo;
1802
+ player.filterManager.checkFiltersState(oldFilterTimescale);
1803
+ }
1804
+ }
1805
+ if (typeof res?.guildId === "string" && typeof res?.voice !== "undefined") {
1806
+ const player = this._LManager.getPlayer(data.guildId);
1807
+ if (!player) return;
1808
+ if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false) {
1809
+ player.destroy("NodeNoVoice" /* NodeNoVoice */);
1810
+ return;
1811
+ }
1812
+ player.ping.ws = res?.voice?.ping || player?.ping.ws;
1813
+ }
1814
+ return;
1815
+ }
1816
+ get restAddress() {
1817
+ return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
1818
+ }
1819
+ get isNodeReconnecting() {
1820
+ return this.reconnectionState !== "IDLE" /* IDLE */;
1821
+ }
1822
+ reconnect(force = false) {
1823
+ if (this.isNodeReconnecting) {
1824
+ return;
1825
+ }
1826
+ this.reconnectionState = "PENDING" /* PENDING */;
1827
+ this.NodeManager.emit("reconnectinprogress", this);
1828
+ if (force) {
1829
+ this.executeReconnect();
1830
+ return;
1831
+ }
1832
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
1833
+ this.reconnectTimeout = setTimeout(() => {
1834
+ this.reconnectTimeout = null;
1835
+ this.executeReconnect();
1836
+ }, this.options.retryDelay || 1e3);
1837
+ }
1838
+ get reconnectionAttemptCount() {
1839
+ const maxAllowedTimestan = this.options.retryTimespan || -1;
1840
+ if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
1841
+ return this.reconnectAttempts.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length;
1842
+ }
1843
+ executeReconnect() {
1844
+ if (this.reconnectionAttemptCount >= this.options.retryAmount) {
1845
+ const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
1846
+ this.reconnectionState = "DESTROYING" /* DESTROYING */;
1847
+ this.NodeManager.emit("error", this, error);
1848
+ this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
1849
+ return;
1850
+ }
1851
+ const MAX_RECONNECT_ATTEMPTS = 1e3;
1852
+ this.reconnectAttempts.push(Date.now());
1853
+ if (this.reconnectAttempts.length > MAX_RECONNECT_ATTEMPTS) {
1854
+ this.reconnectAttempts = this.reconnectAttempts.slice(-MAX_RECONNECT_ATTEMPTS);
1855
+ }
1856
+ this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
1857
+ this.NodeManager.emit("reconnecting", this);
1858
+ this.connect();
1859
+ }
1860
+ resetReconnectionAttempts() {
1861
+ this.reconnectionState = "IDLE" /* IDLE */;
1862
+ this.reconnectAttempts = [];
1863
+ clearTimeout(this.reconnectTimeout);
1864
+ this.reconnectTimeout = null;
1865
+ return;
1866
+ }
1867
+ resetAckTimeouts(heartbeat = true, ping = true) {
1868
+ if (ping) {
1869
+ if (this.pingTimeout) clearTimeout(this.pingTimeout);
1870
+ this.pingTimeout = null;
1871
+ }
1872
+ if (heartbeat) {
1873
+ if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1874
+ this.heartBeatInterval = null;
1875
+ }
1876
+ return;
1877
+ }
1878
+ async open() {
1879
+ this.isAlive = true;
1880
+ this.resetReconnectionAttempts();
1881
+ if (this.nodeType === "Core") {
1882
+ if (this.options.enablePingOnStatsCheck) this.heartBeat();
1883
+ if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1884
+ if (this.options.heartBeatInterval > 0) {
1885
+ this.socket.on("pong", () => {
1886
+ this.heartBeatPongTimestamp = performance.now();
1887
+ this.isAlive = true;
1888
+ });
1889
+ this.heartBeatInterval = setInterval(() => {
1890
+ if (!this.socket) return console.error("Node-Heartbeat-Interval - Socket not available - maybe reconnecting?");
1891
+ if (!this.isAlive) return this.close(500, "Node-Heartbeat-Timeout");
1892
+ this.isAlive = false;
1893
+ this.heartBeatPingTimestamp = performance.now();
1894
+ this.socket?.ping?.();
1895
+ }, this.options.heartBeatInterval || 3e4);
1896
+ }
1897
+ }
1898
+ this.info = await this.fetchInfo().catch((e) => (console.error(e, "ON-OPEN-FETCH"), null));
1899
+ if (!this.info && ["v3", "v4"].includes(this.version)) {
1900
+ const errorString = `Audio Node(${this.restAddress}) does not provide any / ${this.version}/info`;
1901
+ throw new Error(errorString);
1902
+ }
1903
+ this.info.isNodelink = !!this.info.isNodelink;
1904
+ this.NodeManager.emit("connect", this);
1905
+ console.log(`\x1B[32m[Ryanlink]\x1B[0m Node \x1B[36m${this.id}\x1B[0m connected successfully.`);
1906
+ }
1907
+ close(code, reason) {
1908
+ this.resetAckTimeouts(true, true);
1909
+ try {
1910
+ if (this.socket) {
1911
+ this.socket.removeAllListeners();
1912
+ this.socket = null;
1913
+ }
1914
+ } catch (e) {
1915
+ if (this.NodeManager?.RyanlinkManager?.options?.advancedOptions?.enableDebugEvents) {
1916
+ this._LManager.emit("debug", "SocketCleanupError" /* SocketCleanupError */, {
1917
+ state: "warn",
1918
+ message: `An error occurred during socket cleanup in close() (likely a race condition): ${e.message}`,
1919
+ functionLayer: "RyanlinkNode > close()"
1920
+ });
1921
+ }
1922
+ }
1923
+ this.isAlive = false;
1924
+ if (code === 1006 && !reason) reason = "Socket got terminated due to no ping connection";
1925
+ if (code === 1e3 && reason === "Node-Disconnect") return;
1926
+ this.NodeManager.emit("disconnect", this, { code, reason });
1927
+ if (code !== 1e3 || reason !== "Node-Destroy") {
1928
+ if (this.NodeManager.nodes.has(this.id)) {
1929
+ this.reconnect();
1930
+ }
1931
+ }
1932
+ this._LManager.players.filter((p) => p?.node?.options?.id === this?.options?.id).forEach((p) => {
1933
+ if (!this._LManager.options.autoMove) return p.playing = false;
1934
+ if (this._LManager.options.autoMove) {
1935
+ if (this.NodeManager.nodes.filter((n) => n.connected).size === 0) return p.playing = false;
1936
+ p.moveNode();
1937
+ }
1938
+ });
1939
+ }
1940
+ error(error) {
1941
+ if (!error) return;
1942
+ this.NodeManager.emit("error", this, error);
1943
+ this.reconnectionState = "IDLE" /* IDLE */;
1944
+ if (this.options.closeOnError) {
1945
+ if (this.heartBeatInterval) clearInterval(this.heartBeatInterval);
1946
+ if (this.pingTimeout) clearTimeout(this.pingTimeout);
1947
+ this.socket?.close(500, "Node-Error - Force Reconnect");
1948
+ return;
1949
+ }
1950
+ this.reconnect();
1951
+ }
1952
+ async message(d) {
1953
+ if (Array.isArray(d)) d = Buffer.concat(d);
1954
+ else if (d instanceof ArrayBuffer) d = Buffer.from(d);
1955
+ let payload;
1956
+ try {
1957
+ payload = JSON.parse(d.toString());
1958
+ } catch (e) {
1959
+ this.NodeManager.emit("error", this, e);
1960
+ return;
1961
+ }
1962
+ if (!payload.op) return;
1963
+ this.NodeManager.emit("raw", this, payload);
1964
+ switch (payload.op) {
1965
+ case "stats":
1966
+ if (this.options.enablePingOnStatsCheck) this.heartBeat();
1967
+ delete payload.op;
1968
+ this.stats = { ...payload };
1969
+ break;
1970
+ case "playerUpdate":
1971
+ {
1972
+ const player = this._LManager.getPlayer(payload.guildId);
1973
+ if (!player)
1974
+ return this.dispatchDebug("PlayerUpdateNoPlayer" /* PlayerUpdateNoPlayer */, {
1975
+ state: "error",
1976
+ message: `PlayerUpdate Event Triggered, but no player found of payload.guildId: ${payload.guildId}`,
1977
+ functionLayer: "RyanlinkNode > nodeEvent > playerUpdate"
1978
+ });
1979
+ const oldPlayer = player?.toJSON();
1980
+ player.lastPositionChange = Date.now();
1981
+ player.lastPosition = payload.state.position || 0;
1982
+ player.connected = payload.state.connected;
1983
+ player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
1984
+ if (!player.createdTimeStamp && payload.state.time) player.createdTimeStamp = payload.state.time;
1985
+ if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.RyanlinkManager.options.advancedOptions.maxFilterFixDuration || 6e5) || player.queue.current?.info?.uri && (0, import_node_path.isAbsolute)(player.queue.current?.info?.uri))) {
1986
+ player.filterManager.filterUpdatedState = false;
1987
+ this.dispatchDebug("PlayerUpdateFilterFixApply" /* PlayerUpdateFilterFixApply */, {
1988
+ state: "log",
1989
+ message: `Fixing FilterState on "${player.guildId}" because player.options.instaUpdateFiltersFix === true`,
1990
+ functionLayer: "RyanlinkNode > nodeEvent > playerUpdate"
1991
+ });
1992
+ await player.seek(player.position);
1993
+ }
1994
+ this._LManager.emit("playerUpdate", oldPlayer, player);
1995
+ }
1996
+ break;
1997
+ case "event":
1998
+ this.handleEvent(payload);
1999
+ break;
2000
+ case "ready":
2001
+ this.resetReconnectionAttempts();
2002
+ this.sessionId = payload.sessionId;
2003
+ this.resuming.enabled = payload.resumed;
2004
+ if (payload.resumed === true) {
2005
+ try {
2006
+ this.NodeManager.emit("resumed", this, payload, await this.fetchAllPlayers());
2007
+ } catch (e) {
2008
+ this.dispatchDebug("ResumingFetchingError" /* ResumingFetchingError */, {
2009
+ state: "error",
2010
+ message: `Failed to fetch players for resumed event, falling back without players array`,
2011
+ error: e,
2012
+ functionLayer: "RyanlinkNode > nodeEvent > resumed"
2013
+ });
2014
+ this.NodeManager.emit("resumed", this, payload, []);
2015
+ }
2016
+ }
2017
+ break;
2018
+ default:
2019
+ this.NodeManager.emit("error", this, new Error(`Unexpected op "${payload.op}" with data`), payload);
2020
+ return;
2021
+ }
2022
+ }
2023
+ async handleEvent(payload) {
2024
+ if (!payload?.guildId) return;
2025
+ const player = this._LManager.getPlayer(payload.guildId);
2026
+ if (!player) return;
2027
+ const NodeLinkEventType = payload.type;
2028
+ if (NodeLinkExclusiveEvents.includes(NodeLinkEventType) && (!this.info || this.info.isNodelink)) {
2029
+ return this.nodeLinkEventHandler(
2030
+ NodeLinkEventType,
2031
+ player,
2032
+ player.queue.current,
2033
+ payload
2034
+ );
2035
+ }
2036
+ switch (payload.type) {
2037
+ case "TrackStartEvent":
2038
+ this.trackStart(player, player.queue.current, payload);
2039
+ break;
2040
+ case "TrackEndEvent":
2041
+ this.trackEnd(player, player.queue.current, payload);
2042
+ break;
2043
+ case "TrackStuckEvent":
2044
+ this.trackStuck(player, player.queue.current, payload);
2045
+ break;
2046
+ case "TrackExceptionEvent":
2047
+ this.trackError(player, player.queue.current, payload);
2048
+ break;
2049
+ case "WebSocketClosedEvent":
2050
+ this.socketClosed(player, payload);
2051
+ break;
2052
+ case "SegmentsLoaded":
2053
+ this.SponsorBlockSegmentLoaded(player, player.queue.current, payload);
2054
+ break;
2055
+ case "SegmentSkipped":
2056
+ this.SponsorBlockSegmentSkipped(player, player.queue.current, payload);
2057
+ break;
2058
+ case "ChaptersLoaded":
2059
+ this.SponsorBlockChaptersLoaded(player, player.queue.current, payload);
2060
+ break;
2061
+ case "ChapterStarted":
2062
+ this.SponsorBlockChapterStarted(player, player.queue.current, payload);
2063
+ break;
2064
+ case "LyricsLineEvent":
2065
+ this.LyricsLine(player, player.queue.current, payload);
2066
+ break;
2067
+ case "LyricsFoundEvent":
2068
+ this.LyricsFound(player, player.queue.current, payload);
2069
+ break;
2070
+ case "LyricsNotFoundEvent":
2071
+ this.LyricsNotFound(player, player.queue.current, payload);
2072
+ break;
2073
+ default:
2074
+ this.NodeManager.emit(
2075
+ "error",
2076
+ this,
2077
+ new Error(`Node#event unknown event '${payload.type}'.`),
2078
+ payload
2079
+ );
2080
+ break;
2081
+ }
2082
+ return;
2083
+ }
2084
+ async nodeLinkEventHandler(eventName, player, track, payload) {
2085
+ this.NodeManager.emit("nodeLinkEvent", this, eventName, player, track, payload);
2086
+ }
2087
+ getTrackOfPayload(payload) {
2088
+ return "track" in payload ? this._LManager.utils.buildTrack(payload.track, void 0) : null;
2089
+ }
2090
+ async trackStart(player, track, payload) {
2091
+ if (!player.getData("internal_nodeChanging")) {
2092
+ player.playing = true;
2093
+ player.paused = false;
2094
+ }
2095
+ if (this._LManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier) {
2096
+ return this.dispatchDebug("TrackStartNewSongsOnly" /* TrackStartNewSongsOnly */, {
2097
+ state: "log",
2098
+ message: `TrackStart not Emitting, because playing the previous song again.`,
2099
+ functionLayer: "RyanlinkNode > trackStart()"
2100
+ });
2101
+ }
2102
+ if (!player.queue.current) {
2103
+ player.queue.current = this.getTrackOfPayload(payload);
2104
+ if (player.queue.current) {
2105
+ await player.queue.utils.save();
2106
+ } else {
2107
+ this.dispatchDebug("TrackStartNoTrack" /* TrackStartNoTrack */, {
2108
+ state: "warn",
2109
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
2110
+ functionLayer: "RyanlinkNode > trackStart()"
2111
+ });
2112
+ }
2113
+ }
2114
+ this._LManager.emit("trackStart", player, player.queue.current, payload);
2115
+ return;
2116
+ }
2117
+ async trackEnd(player, track, payload) {
2118
+ if (player.getData("internal_nodeChanging") === true) return;
2119
+ const trackToUse = track || this.getTrackOfPayload(payload);
2120
+ if (payload.reason === "replaced") {
2121
+ this.dispatchDebug("TrackEndReplaced" /* TrackEndReplaced */, {
2122
+ state: "warn",
2123
+ message: `TrackEnd Event does not handle any playback, because the track was replaced.`,
2124
+ functionLayer: "RyanlinkNode > trackEnd()"
2125
+ });
2126
+ this._LManager.emit("trackEnd", player, trackToUse, payload);
2127
+ return;
2128
+ }
2129
+ if (!player.queue.tracks.length && (player.repeatMode === "off" || player.getData("internal_stopPlaying")))
2130
+ return this.queueEnd(player, track, payload);
2131
+ if (["loadFailed", "cleanup"].includes(payload.reason)) {
2132
+ if (player.getData("internal_destroystatus") === true) return;
2133
+ await queueTrackEnd(player);
2134
+ if (!player.queue.current) return this.queueEnd(player, trackToUse, payload);
2135
+ this._LManager.emit("trackEnd", player, trackToUse, payload);
2136
+ if (this._LManager.options.autoSkip && player.queue.current) {
2137
+ player.play({ noReplace: true });
2138
+ }
2139
+ return;
2140
+ }
2141
+ if (player.repeatMode !== "track" || player.getData("internal_skipped")) await queueTrackEnd(player);
2142
+ else if (trackToUse && !trackToUse?.pluginInfo?.clientData?.previousTrack) {
2143
+ player.queue.previous.unshift(trackToUse);
2144
+ if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
2145
+ player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
2146
+ await player.queue.utils.save();
2147
+ }
2148
+ if (!player.queue.current) return this.queueEnd(player, trackToUse, payload);
2149
+ player.setData("internal_skipped", false);
2150
+ this._LManager.emit("trackEnd", player, trackToUse, payload);
2151
+ if (this._LManager.options.autoSkip && player.queue.current) {
2152
+ player.play({ noReplace: true });
2153
+ }
2154
+ return;
2155
+ }
2156
+ async trackStuck(player, track, payload) {
2157
+ if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
2158
+ const oldTimestamps = (player.getData("internal_erroredTracksTimestamps") || []).filter(
2159
+ (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2160
+ );
2161
+ player.setData("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2162
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2163
+ this.dispatchDebug("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */, {
2164
+ state: "log",
2165
+ message: `trackStuck Event was triggered too often within a given threshold (RyanlinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
2166
+ functionLayer: "RyanlinkNode > trackStuck()"
2167
+ });
2168
+ player.destroy("TrackStuckMaxTracksErroredPerTime" /* TrackStuckMaxTracksErroredPerTime */);
2169
+ return;
2170
+ }
2171
+ }
2172
+ this._LManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
2173
+ if (!player.queue.tracks.length && (player.repeatMode === "off" || player.getData("internal_stopPlaying"))) {
2174
+ try {
2175
+ await player.node.updatePlayer({
2176
+ guildId: player.guildId,
2177
+ playerOptions: { track: { encoded: null } }
2178
+ });
2179
+ return;
2180
+ } catch {
2181
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
2182
+ }
2183
+ }
2184
+ await queueTrackEnd(player);
2185
+ if (!player.queue.current) {
2186
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
2187
+ }
2188
+ if (this._LManager.options.autoSkip && player.queue.current) {
2189
+ player.play({ track: player.queue.current, noReplace: false });
2190
+ }
2191
+ return;
2192
+ }
2193
+ async trackError(player, track, payload) {
2194
+ if (this._LManager.options.playerOptions.maxErrorsPerTime?.threshold > 0 && this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount >= 0) {
2195
+ const oldTimestamps = (player.getData("internal_erroredTracksTimestamps") || []).filter(
2196
+ (v) => Date.now() - v < this._LManager.options.playerOptions.maxErrorsPerTime?.threshold
2197
+ );
2198
+ player.setData("internal_erroredTracksTimestamps", [...oldTimestamps, Date.now()]);
2199
+ if (oldTimestamps.length >= this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount) {
2200
+ this.dispatchDebug("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */, {
2201
+ state: "log",
2202
+ message: `TrackError Event was triggered too often within a given threshold (RyanlinkManager.options.playerOptions.maxErrorsPerTime). Threshold: "${this._LManager.options.playerOptions.maxErrorsPerTime?.threshold}ms", maxAmount: "${this._LManager.options.playerOptions.maxErrorsPerTime?.maxAmount}"`,
2203
+ functionLayer: "RyanlinkNode > trackError()"
2204
+ });
2205
+ player.destroy("TrackErrorMaxTracksErroredPerTime" /* TrackErrorMaxTracksErroredPerTime */);
2206
+ return;
2207
+ }
2208
+ }
2209
+ this._LManager.emit("trackError", player, track || this.getTrackOfPayload(payload), payload);
2210
+ return;
2211
+ }
2212
+ socketClosed(player, payload) {
2213
+ this._LManager.emit("playerSocketClosed", player, payload);
2214
+ return;
2215
+ }
2216
+ SponsorBlockSegmentLoaded(player, track, payload) {
2217
+ this._LManager.emit("SegmentsLoaded", player, track || this.getTrackOfPayload(payload), payload);
2218
+ return;
2219
+ }
2220
+ SponsorBlockSegmentSkipped(player, track, payload) {
2221
+ this._LManager.emit("SegmentSkipped", player, track || this.getTrackOfPayload(payload), payload);
2222
+ return;
2223
+ }
2224
+ SponsorBlockChaptersLoaded(player, track, payload) {
2225
+ this._LManager.emit("ChaptersLoaded", player, track || this.getTrackOfPayload(payload), payload);
2226
+ return;
2227
+ }
2228
+ SponsorBlockChapterStarted(player, track, payload) {
2229
+ this._LManager.emit("ChapterStarted", player, track || this.getTrackOfPayload(payload), payload);
2230
+ return;
2231
+ }
2232
+ async getSponsorBlock(player) {
2233
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
2234
+ throw new RangeError(`there is no sponsorblock-plugin available in the ryanlink node: ${this.id}`);
2235
+ return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`);
2236
+ }
2237
+ async setSponsorBlock(player, segments = ["sponsor", "selfpromo"]) {
2238
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
2239
+ throw new RangeError(`there is no sponsorblock-plugin available in the ryanlink node: ${this.id}`);
2240
+ if (!segments.length) throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
2241
+ if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
2242
+ throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map((v) => `'${v}'`).join(", ")}`);
2243
+ await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
2244
+ r.method = "PUT";
2245
+ r.headers = {
2246
+ Authorization: this.options.authorization,
2247
+ "Content-Type": "application/json"
2248
+ };
2249
+ r.body = safeStringify(segments.map((v) => v.toLowerCase()));
2250
+ });
2251
+ player.setData(
2252
+ "internal_sponsorBlockCategories",
2253
+ segments.map((v) => v.toLowerCase())
2254
+ );
2255
+ this.dispatchDebug("SetSponsorBlock" /* SetSponsorBlock */, {
2256
+ state: "log",
2257
+ message: `SponsorBlock was set for Player: ${player.guildId} to: ${segments.map((v) => `'${v.toLowerCase()}'`).join(", ")}`,
2258
+ functionLayer: "RyanlinkNode > setSponsorBlock()"
2259
+ });
2260
+ return;
2261
+ }
2262
+ async deleteSponsorBlock(player) {
2263
+ if (this._checkForPlugins && !this.info?.plugins?.find?.((v) => v.name === "sponsorblock-plugin"))
2264
+ throw new RangeError(`there is no sponsorblock-plugin available in the ryanlink node: ${this.id}`);
2265
+ await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
2266
+ r.method = "DELETE";
2267
+ });
2268
+ player.setData("internal_sponsorBlockCategories", []);
2269
+ this.dispatchDebug("DeleteSponsorBlock" /* DeleteSponsorBlock */, {
2270
+ state: "log",
2271
+ message: `SponsorBlock was deleted for Player: ${player.guildId}`,
2272
+ functionLayer: "RyanlinkNode > deleteSponsorBlock()"
2273
+ });
2274
+ return;
2275
+ }
2276
+ async queueEnd(player, track, payload) {
2277
+ if (player.getData("internal_nodeChanging") === true) return;
2278
+ player.queue.current = null;
2279
+ player.playing = false;
2280
+ player.setData("internal_stopPlaying", void 0);
2281
+ this.dispatchDebug("QueueEnded" /* QueueEnded */, {
2282
+ state: "log",
2283
+ message: `Queue Ended because no more Tracks were in the Queue, due to EventName: "${payload.type}"`,
2284
+ functionLayer: "RyanlinkNode > queueEnd()"
2285
+ });
2286
+ if (typeof this._LManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.getData("internal_autoplayStopPlaying") === "undefined") {
2287
+ this.dispatchDebug("AutoplayExecution" /* AutoplayExecution */, {
2288
+ state: "log",
2289
+ message: `Now Triggering Autoplay.`,
2290
+ functionLayer: "RyanlinkNode > queueEnd() > autoplayFunction"
2291
+ });
2292
+ const previousAutoplayTime = player.getData("internal_previousautoplay");
2293
+ const duration = previousAutoplayTime ? Date.now() - previousAutoplayTime : 0;
2294
+ if (!duration || duration > this._LManager.options.playerOptions.minAutoPlayMs || !!player.getData("internal_skipped")) {
2295
+ await this._LManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
2296
+ player.setData("internal_previousautoplay", Date.now());
2297
+ if (player.queue.tracks.length > 0) await queueTrackEnd(player);
2298
+ else
2299
+ this.dispatchDebug("AutoplayNoSongsAdded" /* AutoplayNoSongsAdded */, {
2300
+ state: "warn",
2301
+ message: `Autoplay was triggered but no songs were added to the queue.`,
2302
+ functionLayer: "RyanlinkNode > queueEnd() > autoplayFunction"
2303
+ });
2304
+ }
2305
+ if (player.queue.current) {
2306
+ if (payload.type === "TrackEndEvent") this._LManager.emit("trackEnd", player, track, payload);
2307
+ if (this._LManager.options.autoSkip) return player.play({ noReplace: true, paused: false });
2308
+ } else {
2309
+ this.dispatchDebug("AutoplayThresholdSpamLimiter" /* AutoplayThresholdSpamLimiter */, {
2310
+ state: "warn",
2311
+ message: `Autoplay was triggered after the previousautoplay too early. Threshold is: ${this._LManager.options.playerOptions.minAutoPlayMs}ms and the Duration was ${duration}ms`,
2312
+ functionLayer: "RyanlinkNode > queueEnd() > autoplayFunction"
2313
+ });
2314
+ }
2315
+ }
2316
+ player.setData("internal_skipped", false);
2317
+ player.setData("internal_autoplayStopPlaying", void 0);
2318
+ if (track && !track?.pluginInfo?.clientData?.previousTrack) {
2319
+ player.queue.previous.unshift(track);
2320
+ if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
2321
+ player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
2322
+ await player.queue.utils.save();
2323
+ }
2324
+ if (payload?.reason !== "stopped") {
2325
+ await player.queue.utils.save();
2326
+ }
2327
+ if (typeof this._LManager.options.playerOptions?.onEmptyQueue?.destroyAfterMs === "number" && !isNaN(this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs) && this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs >= 0) {
2328
+ if (this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0) {
2329
+ player.destroy("QueueEmpty" /* QueueEmpty */);
2330
+ return;
2331
+ } else {
2332
+ this.dispatchDebug("TriggerQueueEmptyInterval" /* TriggerQueueEmptyInterval */, {
2333
+ state: "log",
2334
+ message: `Trigger Queue Empty Interval was Triggered because playerOptions.onEmptyQueue.destroyAfterMs is set to ${this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs}ms`,
2335
+ functionLayer: "RyanlinkNode > queueEnd() > destroyAfterMs"
2336
+ });
2337
+ this._LManager.emit("playerQueueEmptyStart", player, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs);
2338
+ if (player.getData("internal_queueempty")) clearTimeout(player.getData("internal_queueempty"));
2339
+ player.setData(
2340
+ "internal_queueempty",
2341
+ setTimeout(() => {
2342
+ player.setData("internal_queueempty", void 0);
2343
+ if (player.queue.current) {
2344
+ return this._LManager.emit("playerQueueEmptyCancel", player);
2345
+ }
2346
+ this._LManager.emit("playerQueueEmptyEnd", player);
2347
+ player.destroy("QueueEmpty" /* QueueEmpty */);
2348
+ }, this._LManager.options.playerOptions.onEmptyQueue?.destroyAfterMs)
2349
+ );
2350
+ }
2351
+ }
2352
+ this._LManager.emit("queueEnd", player, track, payload);
2353
+ return;
2354
+ }
2355
+ async LyricsLine(player, track, payload) {
2356
+ if (!player.queue.current) {
2357
+ player.queue.current = this.getTrackOfPayload(payload);
2358
+ if (player.queue.current) {
2359
+ await player.queue.utils.save();
2360
+ } else {
2361
+ this.dispatchDebug("TrackStartNoTrack" /* TrackStartNoTrack */, {
2362
+ state: "warn",
2363
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
2364
+ functionLayer: "RyanlinkNode > trackStart()"
2365
+ });
2366
+ }
2367
+ }
2368
+ this._LManager.emit("LyricsLine", player, track, payload);
2369
+ return;
2370
+ }
2371
+ async LyricsFound(player, track, payload) {
2372
+ if (!player.queue.current) {
2373
+ player.queue.current = this.getTrackOfPayload(payload);
2374
+ if (player.queue.current) {
2375
+ await player.queue.utils.save();
2376
+ } else {
2377
+ this.dispatchDebug("TrackStartNoTrack" /* TrackStartNoTrack */, {
2378
+ state: "warn",
2379
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
2380
+ functionLayer: "RyanlinkNode > trackStart()"
2381
+ });
2382
+ }
2383
+ }
2384
+ this._LManager.emit("LyricsFound", player, track, payload);
2385
+ return;
2386
+ }
2387
+ async LyricsNotFound(player, track, payload) {
2388
+ if (!player.queue.current) {
2389
+ player.queue.current = this.getTrackOfPayload(payload);
2390
+ if (player.queue.current) {
2391
+ await player.queue.utils.save();
2392
+ } else {
2393
+ this.dispatchDebug("TrackStartNoTrack" /* TrackStartNoTrack */, {
2394
+ state: "warn",
2395
+ message: `Trackstart emitted but there is no track on player.queue.current, trying to get the track of the payload failed too.`,
2396
+ functionLayer: "RyanlinkNode > trackStart()"
2397
+ });
2398
+ }
2399
+ }
2400
+ this._LManager.emit("LyricsNotFound", player, track, payload);
2401
+ return;
2402
+ }
2403
+ };
2404
+
2405
+ // src/node/NodeLink.ts
2406
+ var NodeLinkNode = class extends RyanlinkNode {
2407
+ nodeType = "NodeLink";
2408
+ constructor(options, manager) {
2409
+ super(options, manager);
2410
+ if (this.options.nodeType === "Core" && this.constructor.name === "NodeLink") {
2411
+ return new RyanlinkNode(options, manager);
2412
+ }
2413
+ this.nodeType = "NodeLink";
2414
+ }
2415
+ async setNextTrackGapLess(player, track) {
2416
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2417
+ const nextTrack = track || player.queue.tracks[0];
2418
+ if (!nextTrack) throw new Error("No track provided");
2419
+ await this.updatePlayer({
2420
+ guildId: player.guildId,
2421
+ playerOptions: { nextTrack: { encoded: nextTrack.encoded, userData: nextTrack.userData || {} } }
2422
+ });
2423
+ return true;
2424
+ }
2425
+ async removeNextTrackGapLess(player) {
2426
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2427
+ await this.updatePlayer({
2428
+ guildId: player.guildId,
2429
+ playerOptions: { nextTrack: { encoded: null } }
2430
+ });
2431
+ return true;
2432
+ }
2433
+ async getMeaning(track) {
2434
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2435
+ const encodedTrack = track?.encoded;
2436
+ if (!encodedTrack) throw new Error("No track provided");
2437
+ return await this.request(`/meaning?encodedTrack=${encodedTrack}`, (m) => {
2438
+ m.method = "GET";
2439
+ });
2440
+ }
2441
+ async addMixerLayer(player, trackToAdd, volume) {
2442
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2443
+ return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
2444
+ m.method = "POST";
2445
+ m.body = safeStringify({
2446
+ track: {
2447
+ encoded: trackToAdd.encoded,
2448
+ userData: trackToAdd.userData
2449
+ },
2450
+ volume: volume / 100
2451
+ });
2452
+ });
2453
+ }
2454
+ async listMixerLayers(player) {
2455
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2456
+ return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix`, (m) => {
2457
+ m.method = "GET";
2458
+ });
2459
+ }
2460
+ async updateMixerLayerVolume(player, mixId, volume) {
2461
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2462
+ await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
2463
+ m.method = "PATCH";
2464
+ m.body = safeStringify({
2465
+ volume: volume / 100
2466
+ });
2467
+ });
2468
+ return true;
2469
+ }
2470
+ async removeMixerLayer(player, mixId) {
2471
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2472
+ await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/mix/${mixId}`, (m) => {
2473
+ m.method = "DELETE";
2474
+ });
2475
+ return true;
2476
+ }
2477
+ specificFilters = {
2478
+ echo: async (player, options, disableFilter = false) => {
2479
+ if (disableFilter) delete player.filterManager.data.echo;
2480
+ else player.filterManager.data.echo = options;
2481
+ await player.filterManager.applyPlayerFilters();
2482
+ return player.filterManager.filters.nodeLinkEcho;
2483
+ },
2484
+ chorus: async (player, options, disableFilter = false) => {
2485
+ if (disableFilter) delete player.filterManager.data.chorus;
2486
+ else player.filterManager.data.chorus = options;
2487
+ await player.filterManager.applyPlayerFilters();
2488
+ return player.filterManager.filters.nodeLinkChorus;
2489
+ },
2490
+ compressor: async (player, options, disableFilter = false) => {
2491
+ if (disableFilter) delete player.filterManager.data.compressor;
2492
+ else player.filterManager.data.compressor = options;
2493
+ await player.filterManager.applyPlayerFilters();
2494
+ return player.filterManager.filters.nodeLinkCompressor;
2495
+ },
2496
+ highPass: async (player, options, disableFilter = false) => {
2497
+ if (disableFilter) delete player.filterManager.data.highPass;
2498
+ else player.filterManager.data.highPass = options;
2499
+ await player.filterManager.applyPlayerFilters();
2500
+ return player.filterManager.filters.nodeLinkHighPass;
2501
+ },
2502
+ phaser: async (player, options, disableFilter = false) => {
2503
+ if (disableFilter) delete player.filterManager.data.phaser;
2504
+ else player.filterManager.data.phaser = options;
2505
+ await player.filterManager.applyPlayerFilters();
2506
+ return player.filterManager.filters.nodeLinkPhaser;
2507
+ },
2508
+ spatial: async (player, options, disableFilter = false) => {
2509
+ if (disableFilter) delete player.filterManager.data.spatial;
2510
+ else player.filterManager.data.spatial = options;
2511
+ await player.filterManager.applyPlayerFilters();
2512
+ return player.filterManager.filters.nodeLinkSpatial;
2513
+ },
2514
+ resetNodeLinkFilters: async (player) => {
2515
+ delete player.filterManager.data.spatial;
2516
+ delete player.filterManager.data.echo;
2517
+ delete player.filterManager.data.chorus;
2518
+ delete player.filterManager.data.compressor;
2519
+ delete player.filterManager.data.highPass;
2520
+ delete player.filterManager.data.phaser;
2521
+ player.filterManager.checkFiltersState();
2522
+ await player.filterManager.applyPlayerFilters();
2523
+ return true;
2524
+ }
2525
+ };
2526
+ async nodeLinkLyrics(player, track, language = "en") {
2527
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2528
+ const encodedTrack = track?.encoded || player.queue.current?.encoded;
2529
+ if (!encodedTrack) throw new Error("No track provided");
2530
+ return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/lyrics?encodedTrack=${encodedTrack}&lang=${language}`, (m) => {
2531
+ m.method = "GET";
2532
+ });
2533
+ }
2534
+ async getChapters(player, track) {
2535
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2536
+ const encodedTrack = track?.encoded || player.queue.current?.encoded;
2537
+ if (!encodedTrack) throw new Error("No track provided");
2538
+ return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/chapters?encodedTrack=${encodedTrack}`, (m) => {
2539
+ m.method = "GET";
2540
+ });
2541
+ }
2542
+ async getConnectionMetrics() {
2543
+ return await this.request(`/connection`, (m) => {
2544
+ m.method = "GET";
2545
+ });
2546
+ }
2547
+ async getDirectStream(track) {
2548
+ return await this.request(`/trackstream?encodedTrack=${track.encoded}`, (m) => {
2549
+ m.method = "GET";
2550
+ });
2551
+ }
2552
+ async loadDirectStream(track, volume, position, filters) {
2553
+ let requestPath = `/loadstream?encodedTrack=${track.encoded}`;
2554
+ if (volume && volume > 0 && volume <= 100) requestPath += `&volume=${volume / 100}`;
2555
+ if (position && position > 0) requestPath += `&position=${position}`;
2556
+ if (filters) requestPath += `&filters=${typeof filters === "object" ? safeStringify(filters) : filters}`;
2557
+ const res = await this.rawRequest(requestPath, (m) => {
2558
+ m.method = "GET";
2559
+ });
2560
+ return res.response;
2561
+ }
2562
+ async changeAudioTrackLanguage(player, language_audioTrackId) {
2563
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2564
+ const res = await this.request(`/sessions/${this.sessionId}/players/${player.guildId}`, (r) => {
2565
+ r.method = "PATCH";
2566
+ r.headers["Content-Type"] = "application/json";
2567
+ r.body = safeStringify({
2568
+ track: {
2569
+ encoded: player.queue.current?.encoded,
2570
+ position: player.position,
2571
+ audioTrackId: language_audioTrackId
2572
+ }
2573
+ });
2574
+ });
2575
+ return res;
2576
+ }
2577
+ async updateYoutubeConfig(refreshToken, visitorData) {
2578
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2579
+ const res = await this.request(`/youtube/config`, (r) => {
2580
+ r.method = "PATCH";
2581
+ r.headers["Content-Type"] = "application/json";
2582
+ r.body = safeStringify({
2583
+ refreshToken,
2584
+ visitorData
2585
+ });
2586
+ });
2587
+ return res;
2588
+ }
2589
+ async getYoutubeConfig(validate = false) {
2590
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2591
+ const res = await this.request(`/youtube/config${validate ? "?validate=true" : ""}`, (r) => {
2592
+ r.method = "GET";
2593
+ });
2594
+ return res;
2595
+ }
2596
+ async getYoutubeOAUTH(refreshToken) {
2597
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2598
+ return await this.request(`/youtube/oauth?refreshToken=${refreshToken}`, (m) => {
2599
+ m.method = "GET";
2600
+ });
2601
+ }
2602
+ async updateYoutubeOAUTH(refreshToken) {
2603
+ if (!this.sessionId) throw new Error("The Audio Node is either not ready, or not up to date!");
2604
+ return await this.request(`/youtube/oauth`, (m) => {
2605
+ m.method = "POST";
2606
+ m.body = safeStringify({
2607
+ refreshToken
2608
+ });
2609
+ });
2610
+ }
2611
+ };
2612
+ RyanlinkNode._NodeLinkClass = NodeLinkNode;
2613
+
2614
+ // src/node/NodeManager.ts
2615
+ var NodeManager = class extends import_node_events.EventEmitter {
2616
+ emit(event, ...args) {
2617
+ return super.emit(event, ...args);
2618
+ }
2619
+ on(event, listener) {
2620
+ return super.on(event, listener);
2621
+ }
2622
+ once(event, listener) {
2623
+ return super.once(event, listener);
2624
+ }
2625
+ off(event, listener) {
2626
+ return super.off(event, listener);
2627
+ }
2628
+ removeListener(event, listener) {
2629
+ return super.removeListener(event, listener);
2630
+ }
2631
+ RyanlinkManager;
2632
+ nodes = new MiniMap();
2633
+ constructor(RyanlinkManager2) {
2634
+ super();
2635
+ this.RyanlinkManager = RyanlinkManager2;
2636
+ if (this.RyanlinkManager.options.nodes)
2637
+ this.RyanlinkManager.options.nodes.forEach((node) => {
2638
+ this.createNode(node);
2639
+ });
2640
+ }
2641
+ async disconnectAll(deleteAllNodes = false, destroyPlayers = true) {
2642
+ if (!this.nodes.size) throw new Error("There are no nodes to disconnect (no nodes in the nodemanager)");
2643
+ if (!this.nodes.filter((v) => v.connected).size) throw new Error("There are no nodes to disconnect (all nodes disconnected)");
2644
+ let counter = 0;
2645
+ for (const node of this.nodes.values()) {
2646
+ if (!node.connected) continue;
2647
+ if (destroyPlayers) {
2648
+ await node.destroy("DisconnectAllNodes" /* DisconnectAllNodes */, deleteAllNodes);
2649
+ } else {
2650
+ await node.disconnect("DisconnectAllNodes" /* DisconnectAllNodes */);
2651
+ }
2652
+ counter++;
2653
+ }
2654
+ return counter;
2655
+ }
2656
+ async connectAll() {
2657
+ if (!this.nodes.size) throw new Error("There are no nodes to connect (no nodes in the nodemanager)");
2658
+ if (!this.nodes.filter((v) => !v.connected).size) throw new Error("There are no nodes to connect (all nodes connected)");
2659
+ let counter = 0;
2660
+ for (const node of this.nodes.values()) {
2661
+ if (node.connected) continue;
2662
+ await node.connect();
2663
+ counter++;
2664
+ }
2665
+ return counter;
2666
+ }
2667
+ async reconnectAll() {
2668
+ if (!this.nodes.size) throw new Error("There are no nodes to reconnect (no nodes in the nodemanager)");
2669
+ let counter = 0;
2670
+ for (const node of this.nodes.values()) {
2671
+ const sessionId = node.sessionId ? `${node.sessionId}` : void 0;
2672
+ await node.destroy("ReconnectAllNodes" /* ReconnectAllNodes */, false);
2673
+ await node.connect(sessionId);
2674
+ counter++;
2675
+ }
2676
+ return counter;
2677
+ }
2678
+ createNode(options) {
2679
+ if (this.nodes.has(options.id || `${options.host}:${options.port}`)) return this.nodes.get(options.id || `${options.host}:${options.port}`);
2680
+ const newNode = options.nodeType === "NodeLink" ? new NodeLinkNode(options, this) : new RyanlinkNode(options, this);
2681
+ this.nodes.set(newNode.id, newNode);
2682
+ return newNode;
2683
+ }
2684
+ leastUsedNodes(sortType = "players") {
2685
+ const connectedNodes = Array.from(this.nodes.values()).filter((node) => node.connected);
2686
+ switch (sortType) {
2687
+ case "memory":
2688
+ {
2689
+ return connectedNodes.sort((a, b) => (a.stats?.memory?.used || 0) - (b.stats?.memory?.used || 0));
2690
+ }
2691
+ break;
2692
+ case "cpuLavalink":
2693
+ {
2694
+ return connectedNodes.sort((a, b) => (a.stats?.cpu?.audioLoad || 0) - (b.stats?.cpu?.audioLoad || 0));
2695
+ }
2696
+ break;
2697
+ case "cpuSystem":
2698
+ {
2699
+ return connectedNodes.sort((a, b) => (a.stats?.cpu?.systemLoad || 0) - (b.stats?.cpu?.systemLoad || 0));
2700
+ }
2701
+ break;
2702
+ case "calls":
2703
+ {
2704
+ return connectedNodes.sort((a, b) => a.calls - b.calls);
2705
+ }
2706
+ break;
2707
+ case "playingPlayers":
2708
+ {
2709
+ return connectedNodes.sort((a, b) => (a.stats?.playingPlayers || 0) - (b.stats?.playingPlayers || 0));
2710
+ }
2711
+ break;
2712
+ case "players":
2713
+ {
2714
+ return connectedNodes.sort((a, b) => (a.stats?.players || 0) - (b.stats?.players || 0));
2715
+ }
2716
+ break;
2717
+ default:
2718
+ {
2719
+ return connectedNodes.sort((a, b) => (a.stats?.players || 0) - (b.stats?.players || 0));
2720
+ }
2721
+ break;
2722
+ }
2723
+ }
2724
+ deleteNode(node, movePlayers = false) {
2725
+ const decodeNode = typeof node === "string" ? this.nodes.get(node) : node;
2726
+ if (!(decodeNode instanceof RyanlinkNode)) throw new RangeError("nodeManager.deleteNode: The node you provided is not valid or doesn't exist.");
2727
+ if (typeof movePlayers !== "boolean") throw new TypeError("nodeManager.deleteNode: movePlayers must be a boolean");
2728
+ decodeNode.destroy("NodeDeleted" /* NodeDeleted */, true, movePlayers);
2729
+ this.nodes.delete(decodeNode.id);
2730
+ return;
2731
+ }
2732
+ getNode(node) {
2733
+ const decodeNode = typeof node === "string" ? this.nodes.get(node) : node;
2734
+ if (!decodeNode) return void 0;
2735
+ if (decodeNode.nodeType === "NodeLink") return decodeNode;
2736
+ return decodeNode;
2737
+ }
2738
+ };
2739
+
2740
+ // src/audio/Filters.ts
2741
+ var DEFAULT_FILTER_DATAS = {
2742
+ volume: 1,
2743
+ lowPass: {
2744
+ smoothing: 0
2745
+ },
2746
+ karaoke: {
2747
+ level: 0,
2748
+ monoLevel: 0,
2749
+ filterBand: 0,
2750
+ filterWidth: 0
2751
+ },
2752
+ timescale: {
2753
+ speed: 1,
2754
+ pitch: 1,
2755
+ rate: 1
2756
+ },
2757
+ rotation: {
2758
+ rotationHz: 0
2759
+ },
2760
+ tremolo: {
2761
+ frequency: 0,
2762
+ depth: 0
2763
+ },
2764
+ vibrato: {
2765
+ frequency: 0,
2766
+ depth: 0
2767
+ },
2768
+ channelMix: audioOutputsData.stereo,
2769
+ echo: {
2770
+ delay: 0,
2771
+ feedback: 0,
2772
+ mix: 0
2773
+ },
2774
+ chorus: {
2775
+ rate: 0,
2776
+ depth: 0,
2777
+ delay: 0,
2778
+ mix: 0,
2779
+ feedback: 0
2780
+ },
2781
+ compressor: {
2782
+ threshold: 0,
2783
+ ratio: 1,
2784
+ attack: 0,
2785
+ release: 0,
2786
+ gain: 0
2787
+ },
2788
+ highPass: {
2789
+ smoothing: 0
2790
+ },
2791
+ phaser: {
2792
+ stages: 0,
2793
+ rate: 0,
2794
+ depth: 0,
2795
+ feedback: 0,
2796
+ mix: 0,
2797
+ minFrequency: 0,
2798
+ maxFrequency: 0
2799
+ },
2800
+ spatial: {
2801
+ depth: 0,
2802
+ rate: 0
2803
+ },
2804
+ pluginFilters: {
2805
+ "filter-engine": {
2806
+ echo: {
2807
+ delay: 0,
2808
+ decay: 0
2809
+ },
2810
+ reverb: {
2811
+ delays: [],
2812
+ gains: []
2813
+ }
2814
+ },
2815
+ "high-pass": {},
2816
+ "low-pass": {},
2817
+ normalization: {},
2818
+ echo: {}
2819
+ }
2820
+ };
2821
+ var FilterManager = class {
2822
+ static EQList = EQList;
2823
+ equalizerBands = [];
2824
+ filterUpdatedState = false;
2825
+ filters = {
2826
+ volume: false,
2827
+ vaporwave: false,
2828
+ custom: false,
2829
+ nightcore: false,
2830
+ rotation: false,
2831
+ karaoke: false,
2832
+ tremolo: false,
2833
+ vibrato: false,
2834
+ lowPass: false,
2835
+ nodeLinkEcho: false,
2836
+ nodeLinkChorus: false,
2837
+ nodeLinkCompressor: false,
2838
+ nodeLinkHighPass: false,
2839
+ nodeLinkPhaser: false,
2840
+ nodeLinkSpatial: false,
2841
+ coreFilterPlugin: {
2842
+ echo: false,
2843
+ reverb: false
2844
+ },
2845
+ dspxPlugin: {
2846
+ lowPass: false,
2847
+ highPass: false,
2848
+ normalization: false,
2849
+ echo: false
2850
+ },
2851
+ audioOutput: "stereo"
2852
+ };
2853
+ data = structuredClone(DEFAULT_FILTER_DATAS);
2854
+ player;
2855
+ constructor(player) {
2856
+ this.player = player;
2857
+ }
2858
+ async applyPlayerFilters() {
2859
+ const sendData = { ...this.data };
2860
+ this.checkFiltersState();
2861
+ if (!this.filters.volume) delete sendData.volume;
2862
+ if (!this.filters.tremolo) delete sendData.tremolo;
2863
+ if (!this.filters.vibrato) delete sendData.vibrato;
2864
+ if (!this.filters.coreFilterPlugin.echo) delete sendData.pluginFilters?.["filter-engine"]?.echo;
2865
+ if (!this.filters.coreFilterPlugin.reverb) delete sendData.pluginFilters?.["filter-engine"]?.reverb;
2866
+ if (!this.filters.dspxPlugin.echo) delete sendData.pluginFilters?.echo;
2867
+ if (!this.filters.dspxPlugin.normalization) delete sendData.pluginFilters?.normalization;
2868
+ if (!this.filters.dspxPlugin.highPass) delete sendData.pluginFilters?.["high-pass"];
2869
+ if (!this.filters.dspxPlugin.lowPass) delete sendData.pluginFilters?.["low-pass"];
2870
+ if (sendData.pluginFilters?.["filter-engine"] && Object.values(sendData.pluginFilters?.["filter-engine"]).length === 0)
2871
+ delete sendData.pluginFilters["filter-engine"];
2872
+ if (sendData.pluginFilters && Object.values(sendData.pluginFilters).length === 0) delete sendData.pluginFilters;
2873
+ if (!this.filters.lowPass) delete sendData.lowPass;
2874
+ if (!this.filters.karaoke) delete sendData.karaoke;
2875
+ if (!this.filters.rotation) delete sendData.rotation;
2876
+ if (this.filters.audioOutput === "stereo") delete sendData.channelMix;
2877
+ if (!this.filters.nodeLinkEcho) delete sendData.echo;
2878
+ if (!this.filters.nodeLinkChorus) delete sendData.chorus;
2879
+ if (!this.filters.nodeLinkCompressor) delete sendData.compressor;
2880
+ if (!this.filters.nodeLinkHighPass) delete sendData.highPass;
2881
+ if (!this.filters.nodeLinkPhaser) delete sendData.phaser;
2882
+ if (!this.filters.nodeLinkSpatial) delete sendData.spatial;
2883
+ if (Object.values(this.data.timescale ?? {}).every((v) => v === 1)) delete sendData.timescale;
2884
+ if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
2885
+ sendData.equalizer = [...this.equalizerBands];
2886
+ if (sendData.equalizer.length === 0) delete sendData.equalizer;
2887
+ for (const key of Object.keys(sendData)) {
2888
+ if (key === "pluginFilters") {
2889
+ } else if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.(key)) delete sendData[key];
2890
+ }
2891
+ const now = performance.now();
2892
+ if (this.player.options.instaFixFilter === true) this.filterUpdatedState = true;
2893
+ this.player.syncState();
2894
+ await this.player.node.updatePlayer({
2895
+ guildId: this.player.guildId,
2896
+ playerOptions: {
2897
+ filters: sendData
2898
+ }
2899
+ });
2900
+ this.player.ping.node = Math.round((performance.now() - now) / 10) / 100;
2901
+ return;
2902
+ }
2903
+ privateNot0(value, numToCheckAgains = 0) {
2904
+ return typeof value === "number" && value !== numToCheckAgains;
2905
+ }
2906
+ getAudioFilters() {
2907
+ return this.data.pluginFilters?.["filter-engine"] || {
2908
+ echo: {
2909
+ decay: this.data.pluginFilters?.echo?.decay && !this.data.pluginFilters?.echo?.echoLength ? this.data.pluginFilters?.echo?.decay : 0,
2910
+ delay: this.data.pluginFilters?.echo?.delay || 0
2911
+ },
2912
+ reverb: {
2913
+ gains: [],
2914
+ delays: [],
2915
+ ...this.data.pluginFilters?.reverb
2916
+ }
2917
+ };
2918
+ }
2919
+ checkFiltersState(oldFilterTimescale) {
2920
+ this.data = this.data ?? {};
2921
+ this.filters.rotation = this.privateNot0(this.data.rotation?.rotationHz);
2922
+ this.filters.vibrato = this.privateNot0(this.data.vibrato?.frequency) || this.privateNot0(this.data.vibrato?.depth);
2923
+ this.filters.tremolo = this.privateNot0(this.data.tremolo?.frequency) || this.privateNot0(this.data.tremolo?.depth);
2924
+ const audioFilterData = this.getAudioFilters();
2925
+ this.filters.coreFilterPlugin = {
2926
+ echo: this.privateNot0(audioFilterData?.echo?.decay) || this.privateNot0(audioFilterData?.echo?.delay),
2927
+ reverb: this.privateNot0(audioFilterData?.reverb?.delays?.length) || this.privateNot0(audioFilterData?.reverb?.gains?.length)
2928
+ };
2929
+ this.filters.dspxPlugin = {
2930
+ lowPass: Object.values(this.data.pluginFilters?.["low-pass"] || {})?.length > 0,
2931
+ highPass: Object.values(this.data.pluginFilters?.["high-pass"] || {})?.length > 0,
2932
+ normalization: Object.values(this.data.pluginFilters?.normalization || {})?.length > 0,
2933
+ echo: Object.values(this.data.pluginFilters?.echo || {})?.length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined"
2934
+ };
2935
+ this.filters.lowPass = this.privateNot0(this.data.lowPass?.smoothing);
2936
+ this.filters.nodeLinkEcho = this.privateNot0(this.data.echo?.delay) || this.privateNot0(this.data.echo?.feedback) || this.privateNot0(this.data.echo?.mix);
2937
+ this.filters.nodeLinkChorus = this.privateNot0(this.data.chorus?.rate) || this.privateNot0(this.data.chorus?.depth) || this.privateNot0(this.data.chorus?.delay) || this.privateNot0(this.data.chorus?.mix) || this.privateNot0(this.data.chorus?.feedback);
2938
+ this.filters.nodeLinkCompressor = this.privateNot0(this.data.compressor?.threshold) || this.privateNot0(this.data.compressor?.ratio, 1) || this.privateNot0(this.data.compressor?.attack) || this.privateNot0(this.data.compressor?.release) || this.privateNot0(this.data.compressor?.gain);
2939
+ this.filters.nodeLinkHighPass = this.privateNot0(this.data.highPass?.smoothing);
2940
+ this.filters.nodeLinkPhaser = this.privateNot0(this.data.phaser?.stages) || this.privateNot0(this.data.phaser?.rate) || this.privateNot0(this.data.phaser?.depth) || this.privateNot0(this.data.phaser?.feedback) || this.privateNot0(this.data.phaser?.mix) || this.privateNot0(this.data.phaser?.minFrequency) || this.privateNot0(this.data.phaser?.maxFrequency);
2941
+ this.filters.nodeLinkSpatial = this.privateNot0(this.data.spatial?.depth) || this.privateNot0(this.data.spatial?.rate);
2942
+ this.filters.karaoke = Object.values(this.data.karaoke ?? {}).some((v) => v !== 0);
2943
+ if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
2944
+ if (oldFilterTimescale.pitch !== this.data.timescale?.pitch || oldFilterTimescale.rate !== this.data.timescale?.rate || oldFilterTimescale.speed !== this.data.timescale?.speed) {
2945
+ this.filters.custom = Object.values(this.data.timescale || {}).some((v) => v !== 1);
2946
+ this.filters.nightcore = false;
2947
+ this.filters.vaporwave = false;
2948
+ }
2949
+ }
2950
+ return true;
2951
+ }
2952
+ async resetFilters() {
2953
+ this.filters.dspxPlugin.echo = false;
2954
+ this.filters.dspxPlugin.normalization = false;
2955
+ this.filters.dspxPlugin.highPass = false;
2956
+ this.filters.dspxPlugin.lowPass = false;
2957
+ this.filters.coreFilterPlugin.echo = false;
2958
+ this.filters.coreFilterPlugin.reverb = false;
2959
+ this.filters.nightcore = false;
2960
+ this.filters.lowPass = false;
2961
+ this.filters.rotation = false;
2962
+ this.filters.tremolo = false;
2963
+ this.filters.vibrato = false;
2964
+ this.filters.karaoke = false;
2965
+ this.filters.vaporwave = false;
2966
+ this.filters.volume = false;
2967
+ this.filters.nodeLinkEcho = false;
2968
+ this.filters.nodeLinkChorus = false;
2969
+ this.filters.nodeLinkCompressor = false;
2970
+ this.filters.nodeLinkHighPass = false;
2971
+ this.filters.nodeLinkPhaser = false;
2972
+ this.filters.nodeLinkSpatial = false;
2973
+ this.filters.audioOutput = "stereo";
2974
+ this.data = structuredClone(DEFAULT_FILTER_DATAS);
2975
+ await this.applyPlayerFilters();
2976
+ return this;
2977
+ }
2978
+ async setVolume(volume) {
2979
+ if (volume < 0 || volume > 5) throw new SyntaxError("Volume-Filter must be between 0 and 5");
2980
+ this.data = this.data ?? {};
2981
+ this.data.volume = volume;
2982
+ this.filters.volume = volume !== 1;
2983
+ await this.applyPlayerFilters();
2984
+ return this;
2985
+ }
2986
+ async setAudioOutput(type) {
2987
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("channelMix"))
2988
+ throw new Error("Node#Info#filters does not include the 'channelMix' Filter (Node has it not enable)");
2989
+ if (!type || !audioOutputsData[type]) throw new Error("Invalid audio type added, must be 'mono' / 'stereo' / 'left' / 'right'");
2990
+ this.data = this.data ?? {};
2991
+ this.data.channelMix = audioOutputsData[type];
2992
+ this.filters.audioOutput = type;
2993
+ await this.applyPlayerFilters();
2994
+ return this;
2995
+ }
2996
+ async setSpeed(speed = 1) {
2997
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
2998
+ throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
2999
+ this.data = this.data ?? {};
3000
+ this.filters.nightcore = false;
3001
+ this.filters.vaporwave = false;
3002
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, speed };
3003
+ this.isCustomFilterActive();
3004
+ await this.applyPlayerFilters();
3005
+ return this;
3006
+ }
3007
+ async setPitch(pitch = 1) {
3008
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
3009
+ throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
3010
+ this.data = this.data ?? {};
3011
+ this.filters.nightcore = false;
3012
+ this.filters.vaporwave = false;
3013
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, pitch };
3014
+ this.isCustomFilterActive();
3015
+ await this.applyPlayerFilters();
3016
+ return this;
3017
+ }
3018
+ async setRate(rate = 1) {
3019
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
3020
+ throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
3021
+ this.data = this.data ?? {};
3022
+ this.filters.nightcore = false;
3023
+ this.filters.vaporwave = false;
3024
+ this.data.timescale = { ...DEFAULT_FILTER_DATAS.timescale, ...this.data.timescale, rate };
3025
+ this.isCustomFilterActive();
3026
+ await this.applyPlayerFilters();
3027
+ return this;
3028
+ }
3029
+ async toggleRotation(rotationHz = 0.2) {
3030
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("rotation"))
3031
+ throw new Error("Node#Info#filters does not include the 'rotation' Filter (Node has it not enable)");
3032
+ this.data = this.data ?? {};
3033
+ this.data.rotation = this.filters.rotation ? DEFAULT_FILTER_DATAS.rotation : { rotationHz };
3034
+ this.filters.rotation = !this.filters.rotation;
3035
+ await this.applyPlayerFilters();
3036
+ return this;
3037
+ }
3038
+ async toggleVibrato(frequency = 10, depth = 1) {
3039
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("vibrato"))
3040
+ throw new Error("Node#Info#filters does not include the 'vibrato' Filter (Node has it not enable)");
3041
+ this.data = this.data ?? {};
3042
+ this.data.vibrato = this.filters.vibrato ? DEFAULT_FILTER_DATAS.vibrato : { depth, frequency };
3043
+ this.filters.vibrato = !this.filters.vibrato;
3044
+ await this.applyPlayerFilters();
3045
+ return this;
3046
+ }
3047
+ async toggleTremolo(frequency = 4, depth = 0.8) {
3048
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("tremolo"))
3049
+ throw new Error("Node#Info#filters does not include the 'tremolo' Filter (Node has it not enable)");
3050
+ this.data = this.data ?? {};
3051
+ this.data.tremolo = this.filters.tremolo ? DEFAULT_FILTER_DATAS.tremolo : { depth, frequency };
3052
+ this.filters.tremolo = !this.filters.tremolo;
3053
+ await this.applyPlayerFilters();
3054
+ return this;
3055
+ }
3056
+ async toggleLowPass(smoothing = 20) {
3057
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("lowPass"))
3058
+ throw new Error("Node#Info#filters does not include the 'lowPass' Filter (Node has it not enable)");
3059
+ this.data = this.data ?? {};
3060
+ this.data.lowPass = this.filters.lowPass ? DEFAULT_FILTER_DATAS.lowPass : { smoothing };
3061
+ this.filters.lowPass = !this.filters.lowPass;
3062
+ await this.applyPlayerFilters();
3063
+ return this;
3064
+ }
3065
+ dspxPlugin = {
3066
+ toggleLowPass: async (boostFactor = 1, cutoffFrequency = 80) => {
3067
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
3068
+ throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3069
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("low-pass"))
3070
+ throw new Error("Node#Info#filters does not include the 'low-pass' Filter (Node has it not enable)");
3071
+ this.data = this.data ?? {};
3072
+ this.data.pluginFilters = this.data.pluginFilters ?? {};
3073
+ if (this.filters.dspxPlugin.lowPass) delete this.data.pluginFilters["low-pass"];
3074
+ else this.data.pluginFilters["low-pass"] = { boostFactor, cutoffFrequency };
3075
+ this.filters.dspxPlugin.lowPass = !this.filters.dspxPlugin.lowPass;
3076
+ await this.applyPlayerFilters();
3077
+ return this;
3078
+ },
3079
+ toggleHighPass: async (boostFactor = 1, cutoffFrequency = 80) => {
3080
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
3081
+ throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3082
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("high-pass"))
3083
+ throw new Error("Node#Info#filters does not include the 'high-pass' Filter (Node has it not enable)");
3084
+ this.data = this.data ?? {};
3085
+ this.data.pluginFilters = this.data.pluginFilters ?? {};
3086
+ if (this.filters.dspxPlugin.highPass) delete this.data.pluginFilters["high-pass"];
3087
+ else this.data.pluginFilters["high-pass"] = { boostFactor, cutoffFrequency };
3088
+ this.filters.dspxPlugin.highPass = !this.filters.dspxPlugin.highPass;
3089
+ await this.applyPlayerFilters();
3090
+ return this;
3091
+ },
3092
+ toggleNormalization: async (maxAmplitude = 0.75, adaptive = true) => {
3093
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
3094
+ throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3095
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("normalization"))
3096
+ throw new Error("Node#Info#filters does not include the 'normalization' Filter (Node has it not enable)");
3097
+ this.data = this.data ?? {};
3098
+ this.data.pluginFilters = this.data.pluginFilters ?? {};
3099
+ if (this.filters.dspxPlugin.normalization) delete this.data.pluginFilters.normalization;
3100
+ else this.data.pluginFilters.normalization = { adaptive, maxAmplitude };
3101
+ this.filters.dspxPlugin.normalization = !this.filters.dspxPlugin.normalization;
3102
+ await this.applyPlayerFilters();
3103
+ return this;
3104
+ },
3105
+ toggleEcho: async (decay = 0.5, echoLength = 0.5) => {
3106
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "lavadspx-plugin"))
3107
+ throw new Error("Node#Info#plugins does not include the lavadspx plugin");
3108
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
3109
+ throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable)");
3110
+ this.data = this.data ?? {};
3111
+ this.data.pluginFilters = this.data.pluginFilters ?? {};
3112
+ if (this.filters.dspxPlugin.echo) delete this.data.pluginFilters.echo;
3113
+ else this.data.pluginFilters.echo = { decay, echoLength };
3114
+ this.filters.dspxPlugin.echo = !this.filters.dspxPlugin.echo;
3115
+ await this.applyPlayerFilters();
3116
+ return this;
3117
+ }
3118
+ };
3119
+ coreFilterPlugin = {
3120
+ toggleEcho: async (delay = 4, decay = 0.8) => {
3121
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "filter-engine"))
3122
+ throw new Error("Node#Info#plugins does not include the filter-engine plugin");
3123
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("echo"))
3124
+ throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)");
3125
+ this.data = this.data ?? {};
3126
+ const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["filter-engine"];
3127
+ this.data.pluginFilters = {
3128
+ ...this.data.pluginFilters,
3129
+ ["filter-engine"]: {
3130
+ reverb: this.data.pluginFilters?.["filter-engine"]?.reverb ?? reverb,
3131
+ echo: this.filters.coreFilterPlugin.echo ? echo : { delay, decay }
3132
+ }
3133
+ };
3134
+ this.filters.coreFilterPlugin.echo = !this.filters.coreFilterPlugin.echo;
3135
+ await this.applyPlayerFilters();
3136
+ return this;
3137
+ },
3138
+ toggleReverb: async (delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) => {
3139
+ if (this.player.node._checkForPlugins && !this.player?.node?.info?.plugins?.find?.((v) => v.name === "filter-engine"))
3140
+ throw new Error("Node#Info#plugins does not include the filter-engine plugin");
3141
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("reverb"))
3142
+ throw new Error("Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)");
3143
+ this.data = this.data ?? {};
3144
+ const { echo, reverb } = DEFAULT_FILTER_DATAS.pluginFilters["filter-engine"];
3145
+ this.data.pluginFilters = {
3146
+ ...this.data.pluginFilters,
3147
+ ["filter-engine"]: {
3148
+ echo: this.data.pluginFilters?.["filter-engine"]?.echo ?? echo,
3149
+ reverb: this.filters.coreFilterPlugin.reverb ? reverb : { delays, gains }
3150
+ }
3151
+ };
3152
+ this.filters.coreFilterPlugin.reverb = !this.filters.coreFilterPlugin.reverb;
3153
+ await this.applyPlayerFilters();
3154
+ return this;
3155
+ }
3156
+ };
3157
+ async toggleNightcore(speed = 1.289999523162842, pitch = 1.289999523162842, rate = 0.9365999523162842) {
3158
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
3159
+ throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
3160
+ this.data = this.data ?? {};
3161
+ this.data.timescale = this.filters.nightcore ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
3162
+ this.filters.nightcore = !this.filters.nightcore;
3163
+ this.filters.vaporwave = false;
3164
+ this.filters.custom = false;
3165
+ await this.applyPlayerFilters();
3166
+ return this;
3167
+ }
3168
+ async toggleVaporwave(speed = 0.8500000238418579, pitch = 0.800000011920929, rate = 1) {
3169
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("timescale"))
3170
+ throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
3171
+ this.data = this.data ?? {};
3172
+ this.data.timescale = this.filters.vaporwave ? DEFAULT_FILTER_DATAS.timescale : { speed, pitch, rate };
3173
+ this.filters.vaporwave = !this.filters.vaporwave;
3174
+ this.filters.nightcore = false;
3175
+ this.filters.custom = false;
3176
+ await this.applyPlayerFilters();
3177
+ return this;
3178
+ }
3179
+ async toggleKaraoke(level = 1, monoLevel = 1, filterBand = 220, filterWidth = 100) {
3180
+ if (this.player.node._checkForSources && !this.player?.node?.info?.filters?.includes?.("karaoke"))
3181
+ throw new Error("Node#Info#filters does not include the 'karaoke' Filter (Node has it not enable)");
3182
+ this.data = this.data ?? {};
3183
+ this.data.karaoke = this.filters.karaoke ? DEFAULT_FILTER_DATAS.karaoke : { level, monoLevel, filterBand, filterWidth };
3184
+ this.filters.karaoke = !this.filters.karaoke;
3185
+ await this.applyPlayerFilters();
3186
+ return this;
3187
+ }
3188
+ isCustomFilterActive() {
3189
+ this.filters.custom = !this.filters.nightcore && !this.filters.vaporwave && Object.values(this.data.timescale).some((d) => d !== 1);
3190
+ return this.filters.custom;
3191
+ }
3192
+ async setEQPreset(preset) {
3193
+ const bands = EQList[preset];
3194
+ return this.setEQ(bands);
3195
+ }
3196
+ async setEQ(bands) {
3197
+ if (!Array.isArray(bands)) bands = [bands];
3198
+ if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
3199
+ throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
3200
+ for (const { band, gain } of bands) this.equalizerBands[band] = { band, gain };
3201
+ if (!this.player.node.sessionId) throw new Error("The Lavalink-Node is either not ready or not up to date");
3202
+ const now = performance.now();
3203
+ if (this.player.options.instaFixFilter === true) this.filterUpdatedState = true;
3204
+ this.player.syncState();
3205
+ await this.player.node.updatePlayer({
3206
+ guildId: this.player.guildId,
3207
+ playerOptions: {
3208
+ filters: { equalizer: this.equalizerBands }
3209
+ }
3210
+ });
3211
+ this.player.ping.node = Math.round((performance.now() - now) / 10) / 100;
3212
+ return this;
3213
+ }
3214
+ async clearEQ() {
3215
+ return this.setEQ(Array.from({ length: 15 }, (_v, i) => ({ band: i, gain: 0 })));
3216
+ }
3217
+ };
3218
+
3219
+ // src/audio/Queue.ts
3220
+ var QueueSaver = class {
3221
+ _;
3222
+ options;
3223
+ constructor(options) {
3224
+ this._ = options?.queueStore || new DefaultQueueStore();
3225
+ this.options = {
3226
+ maxPreviousTracks: options?.maxPreviousTracks || 25
3227
+ };
3228
+ }
3229
+ async get(guildId) {
3230
+ return this._.parse(await this._.get(guildId));
3231
+ }
3232
+ async delete(guildId) {
3233
+ return this._.delete(guildId);
3234
+ }
3235
+ async set(guildId, valueToStringify) {
3236
+ return this._.set(guildId, await this._.stringify(valueToStringify));
3237
+ }
3238
+ async sync(guildId) {
3239
+ return this.get(guildId);
3240
+ }
3241
+ };
3242
+ var DefaultQueueStore = class {
3243
+ data = new MiniMap();
3244
+ constructor() {
3245
+ }
3246
+ get(guildId) {
3247
+ return this.data.get(guildId);
3248
+ }
3249
+ set(guildId, valueToStringify) {
3250
+ return this.data.set(guildId, valueToStringify) ? true : false;
3251
+ }
3252
+ delete(guildId) {
3253
+ return this.data.delete(guildId);
3254
+ }
3255
+ stringify(value) {
3256
+ return value;
3257
+ }
3258
+ parse(value) {
3259
+ return value;
3260
+ }
3261
+ };
3262
+ var Queue = class {
3263
+ tracks = [];
3264
+ previous = [];
3265
+ current = null;
3266
+ options = { maxPreviousTracks: 25 };
3267
+ guildId = "";
3268
+ QueueSaver = null;
3269
+ managerUtils = new RyanlinkUtils();
3270
+ queueChanges;
3271
+ constructor(guildId, data = {}, QueueSaver2, queueOptions) {
3272
+ this.queueChanges = queueOptions?.queueChangesWatcher || null;
3273
+ this.guildId = guildId;
3274
+ this.QueueSaver = QueueSaver2;
3275
+ this.options.maxPreviousTracks = this.QueueSaver?.options?.maxPreviousTracks ?? this.options.maxPreviousTracks;
3276
+ this.current = this.managerUtils.isTrack(data.current) ? data.current : null;
3277
+ this.previous = Array.isArray(data.previous) && data.previous.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.previous.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) : [];
3278
+ this.tracks = Array.isArray(data.tracks) && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) ? data.tracks.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)) : [];
3279
+ Object.defineProperty(this, AudioQueueSymbol, { configurable: true, value: true });
3280
+ }
3281
+ utils = {
3282
+ save: async () => {
3283
+ if (this.previous.length > this.options.maxPreviousTracks) this.previous.splice(this.options.maxPreviousTracks, this.previous.length);
3284
+ return await this.QueueSaver.set(this.guildId, this.utils.toJSON());
3285
+ },
3286
+ sync: async (override = true, dontSyncCurrent = true) => {
3287
+ const data = await this.QueueSaver.get(this.guildId);
3288
+ if (!data) throw new Error(`No data found to sync for guildId: ${this.guildId}`);
3289
+ if (!dontSyncCurrent && !this.current && this.managerUtils.isTrack(data.current)) this.current = data.current;
3290
+ if (Array.isArray(data.tracks) && data?.tracks.length && data.tracks.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
3291
+ this.tracks.splice(
3292
+ override ? 0 : this.tracks.length,
3293
+ override ? this.tracks.length : 0,
3294
+ ...data.tracks.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track))
3295
+ );
3296
+ if (Array.isArray(data.previous) && data?.previous.length && data.previous.some((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track)))
3297
+ this.previous.splice(
3298
+ 0,
3299
+ override ? this.previous.length : 0,
3300
+ ...data.previous.filter((track) => this.managerUtils.isTrack(track) || this.managerUtils.isUnresolvedTrack(track))
3301
+ );
3302
+ await this.utils.save();
3303
+ return;
3304
+ },
3305
+ destroy: async () => {
3306
+ return await this.QueueSaver.delete(this.guildId);
3307
+ },
3308
+ toJSON: () => {
3309
+ if (this.previous?.length > this.options?.maxPreviousTracks) this.previous?.splice(this.options?.maxPreviousTracks, this.previous.length);
3310
+ return {
3311
+ current: this.current ? { ...this.current } : null,
3312
+ previous: this.previous ? [...this.previous] : [],
3313
+ tracks: this.tracks ? [...this.tracks] : []
3314
+ };
3315
+ },
3316
+ totalDuration: () => {
3317
+ return this.tracks.reduce((acc, cur) => acc + (cur.info.duration || 0), this.current?.info.duration || 0);
3318
+ },
3319
+ filterTracks: (predicate) => {
3320
+ if (typeof predicate === "function") {
3321
+ return this.tracks.map((track, index) => ({ track, index })).filter(({ track, index }) => predicate(track, index));
3322
+ }
3323
+ return this.tracks.map((track, index) => ({ track, index })).filter(({ track }) => {
3324
+ if (predicate.title && !track.info?.title?.toLowerCase().includes(predicate.title.toLowerCase())) {
3325
+ return false;
3326
+ }
3327
+ if (predicate.author && !track.info?.author?.toLowerCase().includes(predicate.author.toLowerCase())) {
3328
+ return false;
3329
+ }
3330
+ if (predicate.duration !== void 0) {
3331
+ const duration = track.info?.duration || 0;
3332
+ if (typeof predicate.duration === "number") {
3333
+ if (duration !== predicate.duration) return false;
3334
+ } else {
3335
+ if (predicate.duration.min !== void 0 && duration < predicate.duration.min) return false;
3336
+ if (predicate.duration.max !== void 0 && duration > predicate.duration.max) return false;
3337
+ }
3338
+ }
3339
+ if (predicate.uri && track.info?.uri !== predicate.uri) {
3340
+ return false;
3341
+ }
3342
+ if (predicate.identifier && track.info?.identifier !== predicate.identifier) {
3343
+ return false;
3344
+ }
3345
+ if (predicate.sourceName && track.info?.sourceName?.toLowerCase() !== predicate.sourceName.toLowerCase()) {
3346
+ return false;
3347
+ }
3348
+ if (predicate.isStream !== void 0 && track.info?.isStream !== predicate.isStream) {
3349
+ return false;
3350
+ }
3351
+ if (predicate.isSeekable !== void 0 && track.info?.isSeekable !== predicate.isSeekable) {
3352
+ return false;
3353
+ }
3354
+ return true;
3355
+ });
3356
+ },
3357
+ findTrack: (predicate) => {
3358
+ const results = this.utils.filterTracks(predicate);
3359
+ return results.length > 0 ? results[0] : null;
3360
+ }
3361
+ };
3362
+ async shuffle() {
3363
+ const oldStored = typeof this.queueChanges?.shuffled === "function" ? this.utils.toJSON() : null;
3364
+ if (this.tracks.length <= 1) return this.tracks.length;
3365
+ if (this.tracks.length === 2) {
3366
+ ;
3367
+ [this.tracks[0], this.tracks[1]] = [this.tracks[1], this.tracks[0]];
3368
+ } else {
3369
+ for (let i = this.tracks.length - 1; i > 0; i--) {
3370
+ const j = Math.floor(Math.random() * (i + 1));
3371
+ [this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
3372
+ }
3373
+ }
3374
+ if (typeof this.queueChanges?.shuffled === "function") this.queueChanges.shuffled(this.guildId, oldStored, this.utils.toJSON());
3375
+ await this.utils.save();
3376
+ return this.tracks.length;
3377
+ }
3378
+ async add(TrackOrTracks, index) {
3379
+ if (typeof index === "number" && index >= 0 && index < this.tracks.length) {
3380
+ return await this.splice(
3381
+ index,
3382
+ 0,
3383
+ (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
3384
+ );
3385
+ }
3386
+ const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
3387
+ this.tracks.push(
3388
+ ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
3389
+ );
3390
+ if (typeof this.queueChanges?.tracksAdd === "function")
3391
+ try {
3392
+ this.queueChanges.tracksAdd(
3393
+ this.guildId,
3394
+ (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
3395
+ this.tracks.length,
3396
+ oldStored,
3397
+ this.utils.toJSON()
3398
+ );
3399
+ } catch {
3400
+ }
3401
+ await this.utils.save();
3402
+ return this.tracks.length;
3403
+ }
3404
+ async splice(index, amount, TrackOrTracks) {
3405
+ const oldStored = typeof this.queueChanges?.tracksAdd === "function" || typeof this.queueChanges?.tracksRemoved === "function" ? this.utils.toJSON() : null;
3406
+ if (!this.tracks.length) {
3407
+ if (TrackOrTracks) return await this.add(TrackOrTracks);
3408
+ return null;
3409
+ }
3410
+ if (TrackOrTracks && typeof this.queueChanges?.tracksAdd === "function")
3411
+ try {
3412
+ this.queueChanges.tracksAdd(
3413
+ this.guildId,
3414
+ (Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v)),
3415
+ index,
3416
+ oldStored,
3417
+ this.utils.toJSON()
3418
+ );
3419
+ } catch {
3420
+ }
3421
+ const spliced = TrackOrTracks ? this.tracks.splice(
3422
+ index,
3423
+ amount,
3424
+ ...(Array.isArray(TrackOrTracks) ? TrackOrTracks : [TrackOrTracks]).flat(2).filter((v) => this.managerUtils.isTrack(v) || this.managerUtils.isUnresolvedTrack(v))
3425
+ ) : this.tracks.splice(index, amount);
3426
+ if (typeof this.queueChanges?.tracksRemoved === "function")
3427
+ try {
3428
+ this.queueChanges.tracksRemoved(this.guildId, spliced, index, oldStored, this.utils.toJSON());
3429
+ } catch {
3430
+ }
3431
+ await this.utils.save();
3432
+ return spliced.length === 1 ? spliced[0] : spliced;
3433
+ }
3434
+ async remove(removeQueryTrack) {
3435
+ if (removeQueryTrack === null || removeQueryTrack === void 0 || Array.isArray(removeQueryTrack) && removeQueryTrack.length === 0) {
3436
+ return null;
3437
+ }
3438
+ const oldStored = typeof this.queueChanges?.tracksRemoved === "function" ? this.utils.toJSON() : null;
3439
+ if (typeof removeQueryTrack === "number") {
3440
+ const toRemove2 = this.tracks[removeQueryTrack];
3441
+ if (!toRemove2) return null;
3442
+ const removed2 = this.tracks.splice(removeQueryTrack, 1);
3443
+ if (typeof this.queueChanges?.tracksRemoved === "function")
3444
+ try {
3445
+ this.queueChanges.tracksRemoved(this.guildId, removed2, removeQueryTrack, oldStored, this.utils.toJSON());
3446
+ } catch {
3447
+ }
3448
+ await this.utils.save();
3449
+ return { removed: removed2 };
3450
+ }
3451
+ if (Array.isArray(removeQueryTrack)) {
3452
+ if (removeQueryTrack.every((v) => typeof v === "number")) {
3453
+ const removed3 = [];
3454
+ const sortedIndexes = removeQueryTrack.sort((a, b) => b - a);
3455
+ for (const i of sortedIndexes) {
3456
+ if (this.tracks[i]) {
3457
+ removed3.unshift(...this.tracks.splice(i, 1));
3458
+ }
3459
+ }
3460
+ if (!removed3.length) return null;
3461
+ if (typeof this.queueChanges?.tracksRemoved === "function")
3462
+ try {
3463
+ this.queueChanges.tracksRemoved(this.guildId, removed3, removeQueryTrack, oldStored, this.utils.toJSON());
3464
+ } catch {
3465
+ }
3466
+ await this.utils.save();
3467
+ return { removed: removed3 };
3468
+ }
3469
+ const tracksToRemove = this.tracks.map((v, i) => ({ v, i })).filter(
3470
+ ({ v, i }) => removeQueryTrack.find(
3471
+ (t) => typeof t === "number" && t === i || typeof t === "object" && (t.encoded && t.encoded === v.encoded || t.info?.identifier && t.info.identifier === v.info?.identifier || t.info?.uri && t.info.uri === v.info?.uri || t.info?.title && t.info.title === v.info?.title || t.info?.isrc && t.info.isrc === v.info?.isrc || t.info?.artworkUrl && t.info.artworkUrl === v.info?.artworkUrl)
3472
+ )
3473
+ );
3474
+ if (!tracksToRemove.length) return null;
3475
+ const removed2 = [];
3476
+ tracksToRemove.sort((a, b) => b.i - a.i);
3477
+ for (const { i } of tracksToRemove) {
3478
+ if (this.tracks[i]) {
3479
+ removed2.unshift(...this.tracks.splice(i, 1));
3480
+ }
3481
+ }
3482
+ if (typeof this.queueChanges?.tracksRemoved === "function")
3483
+ try {
3484
+ this.queueChanges.tracksRemoved(
3485
+ this.guildId,
3486
+ removed2,
3487
+ tracksToRemove.map((v) => v.i),
3488
+ oldStored,
3489
+ this.utils.toJSON()
3490
+ );
3491
+ } catch {
3492
+ }
3493
+ await this.utils.save();
3494
+ return { removed: removed2 };
3495
+ }
3496
+ const toRemove = this.tracks.findIndex(
3497
+ (v) => removeQueryTrack.encoded && removeQueryTrack.encoded === v.encoded || removeQueryTrack.info?.identifier && removeQueryTrack.info.identifier === v.info?.identifier || removeQueryTrack.info?.uri && removeQueryTrack.info.uri === v.info?.uri || removeQueryTrack.info?.title && removeQueryTrack.info.title === v.info?.title || removeQueryTrack.info?.isrc && removeQueryTrack.info.isrc === v.info?.isrc || removeQueryTrack.info?.artworkUrl && removeQueryTrack.info.artworkUrl === v.info?.artworkUrl
3498
+ );
3499
+ if (toRemove < 0) return null;
3500
+ const removed = this.tracks.splice(toRemove, 1);
3501
+ if (typeof this.queueChanges?.tracksRemoved === "function")
3502
+ try {
3503
+ this.queueChanges.tracksRemoved(this.guildId, removed, toRemove, oldStored, this.utils.toJSON());
3504
+ } catch {
3505
+ }
3506
+ await this.utils.save();
3507
+ return { removed };
3508
+ }
3509
+ async shiftPrevious() {
3510
+ const removed = this.previous.shift();
3511
+ if (removed) await this.utils.save();
3512
+ return removed ?? null;
3513
+ }
3514
+ filter(predicate) {
3515
+ return this.utils.filterTracks(predicate);
3516
+ }
3517
+ find(predicate) {
3518
+ return this.utils.findTrack(predicate);
3519
+ }
3520
+ async sortBy(sortBy, order = "asc") {
3521
+ if (typeof sortBy === "function") {
3522
+ this.tracks.sort(sortBy);
3523
+ } else {
3524
+ this.tracks.sort((a, b) => {
3525
+ let comparison = 0;
3526
+ switch (sortBy) {
3527
+ case "duration":
3528
+ comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
3529
+ break;
3530
+ case "title":
3531
+ comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
3532
+ break;
3533
+ case "author":
3534
+ comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
3535
+ break;
3536
+ default:
3537
+ return 0;
3538
+ }
3539
+ return order === "desc" ? -comparison : comparison;
3540
+ });
3541
+ }
3542
+ await this.utils.save();
3543
+ return this;
3544
+ }
3545
+ toSortedBy(sortBy, order = "asc") {
3546
+ const tracksCopy = [...this.tracks];
3547
+ if (typeof sortBy === "function") {
3548
+ return tracksCopy.sort(sortBy);
3549
+ }
3550
+ return tracksCopy.sort((a, b) => {
3551
+ let comparison = 0;
3552
+ switch (sortBy) {
3553
+ case "duration":
3554
+ comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
3555
+ break;
3556
+ case "title":
3557
+ comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
3558
+ break;
3559
+ case "author":
3560
+ comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
3561
+ break;
3562
+ default:
3563
+ return 0;
3564
+ }
3565
+ return order === "desc" ? -comparison : comparison;
3566
+ });
3567
+ }
3568
+ getTracks(start, end) {
3569
+ return this.tracks.slice(start, end);
3570
+ }
3571
+ };
3572
+
3573
+ // src/audio/Player.ts
3574
+ var Player = class {
3575
+ filterManager;
3576
+ RyanlinkManager;
3577
+ options;
3578
+ node;
3579
+ queue;
3580
+ guildId;
3581
+ voiceChannelId = null;
3582
+ textChannelId = null;
3583
+ playing = false;
3584
+ paused = false;
3585
+ repeatMode = "off";
3586
+ ping = {
3587
+ node: 0,
3588
+ ws: 0
3589
+ };
3590
+ volume = 100;
3591
+ internalVolume = 100;
3592
+ get position() {
3593
+ return this.lastPosition + (this.lastPositionChange ? Date.now() - this.lastPositionChange : 0);
3594
+ }
3595
+ lastPositionChange = null;
3596
+ lastPosition = 0;
3597
+ lastSavedPosition = 0;
3598
+ createdTimeStamp;
3599
+ connected = false;
3600
+ voice = {
3601
+ endpoint: null,
3602
+ sessionId: null,
3603
+ token: null,
3604
+ channelId: void 0
3605
+ };
3606
+ voiceState = {
3607
+ selfDeaf: false,
3608
+ selfMute: false,
3609
+ serverDeaf: false,
3610
+ serverMute: false,
3611
+ suppress: false
3612
+ };
3613
+ data = {};
3614
+ dispatchDebug(name, eventData) {
3615
+ if (!this.RyanlinkManager.options?.advancedOptions?.enableDebugEvents) return;
3616
+ this.RyanlinkManager.emit("debug", name, eventData);
3617
+ }
3618
+ constructor(options, RyanlinkManager2, dontEmitPlayerCreateEvent) {
3619
+ if (typeof options?.customData === "object") for (const [key, value] of Object.entries(options.customData)) this.setData(key, value);
3620
+ this.options = options;
3621
+ this.filterManager = new FilterManager(this);
3622
+ this.RyanlinkManager = RyanlinkManager2;
3623
+ this.guildId = this.options.guildId;
3624
+ this.voiceChannelId = this.options.voiceChannelId;
3625
+ this.textChannelId = this.options.textChannelId || null;
3626
+ this.node = typeof this.options.node === "string" ? this.RyanlinkManager.nodeManager.nodes.get(this.options.node) : this.options.node;
3627
+ if (!this.node || typeof this.node.request !== "function") {
3628
+ if (typeof this.options.node === "string") {
3629
+ this.dispatchDebug("PlayerCreateNodeNotFound" /* PlayerCreateNodeNotFound */, {
3630
+ state: "warn",
3631
+ message: `Player was created with provided node Id: ${this.options.node}, but no node with that Id was found.`,
3632
+ functionLayer: "Player > constructor()"
3633
+ });
3634
+ }
3635
+ const least = this.RyanlinkManager.nodeManager.leastUsedNodes();
3636
+ this.node = least.filter((v) => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || least[0] || null;
3637
+ }
3638
+ if (!this.node) throw new Error("No available Node was found, please add a RyanlinkNode to the Manager via Manager.NodeManager#createNode");
3639
+ if (typeof options.volume === "number" && !isNaN(options.volume)) this.volume = Number(options.volume);
3640
+ this.volume = Math.round(Math.max(Math.min(this.volume, 1e3), 0));
3641
+ this.internalVolume = Math.round(
3642
+ Math.max(
3643
+ Math.min(
3644
+ Math.round(
3645
+ this.RyanlinkManager.options.playerOptions.volumeDecrementer ? this.volume * this.RyanlinkManager.options.playerOptions.volumeDecrementer : this.volume
3646
+ ),
3647
+ 1e3
3648
+ ),
3649
+ 0
3650
+ )
3651
+ );
3652
+ if (!dontEmitPlayerCreateEvent) this.RyanlinkManager.emit("playerCreate", this);
3653
+ this.queue = new Queue(this.guildId, {}, new QueueSaver(this.RyanlinkManager.options.queueOptions), this.RyanlinkManager.options.queueOptions);
3654
+ }
3655
+ set(key, value) {
3656
+ this.data[key] = value;
3657
+ return this;
3658
+ }
3659
+ get(key) {
3660
+ return this.data[key];
3661
+ }
3662
+ setData(key, value) {
3663
+ this.data[key] = value;
3664
+ return this;
3665
+ }
3666
+ getData(key) {
3667
+ return this.data[key];
3668
+ }
3669
+ deleteData(key) {
3670
+ if (key.startsWith("internal_")) return this;
3671
+ delete this.data[key];
3672
+ return this;
3673
+ }
3674
+ clearData() {
3675
+ const toKeep = Object.keys(this.data).filter((v) => v.startsWith("internal_"));
3676
+ for (const key in this.data) {
3677
+ if (toKeep.includes(key)) continue;
3678
+ delete this.data[key];
3679
+ }
3680
+ return this;
3681
+ }
3682
+ getAllData() {
3683
+ return Object.fromEntries(Object.entries(this.data).filter((v) => !v[0].startsWith("internal_")));
3684
+ }
3685
+ async play(options = {}) {
3686
+ if (this.getData("internal_queueempty")) {
3687
+ this.dispatchDebug("PlayerPlayQueueEmptyTimeoutClear" /* PlayerPlayQueueEmptyTimeoutClear */, {
3688
+ state: "log",
3689
+ message: `Player was called to play something, while there was a queueEmpty Timeout set, clearing the timeout.`,
3690
+ functionLayer: "Player > play()"
3691
+ });
3692
+ this.RyanlinkManager.emit("playerQueueEmptyCancel", this);
3693
+ clearTimeout(this.getData("internal_queueempty"));
3694
+ this.setData("internal_queueempty", void 0);
3695
+ }
3696
+ if (options?.clientTrack && (this.RyanlinkManager.utils.isTrack(options?.clientTrack) || this.RyanlinkManager.utils.isUnresolvedTrack(options.clientTrack))) {
3697
+ if (this.RyanlinkManager.utils.isUnresolvedTrack(options.clientTrack)) {
3698
+ try {
3699
+ await options.clientTrack.resolve(this);
3700
+ } catch (error) {
3701
+ this.dispatchDebug("PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
3702
+ state: "error",
3703
+ error,
3704
+ message: `Player Play was called with clientTrack, Song is unresolved, but couldn't resolve it`,
3705
+ functionLayer: "Player > play() > resolve currentTrack"
3706
+ });
3707
+ this.RyanlinkManager.emit("trackError", this, this.queue.current, error);
3708
+ if (options && "clientTrack" in options) delete options.clientTrack;
3709
+ if (options && "track" in options) delete options.track;
3710
+ if (this.RyanlinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0]) return this.play(options);
3711
+ return this;
3712
+ }
3713
+ }
3714
+ if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
3715
+ options.clientTrack.userData = {
3716
+ ...typeof options?.clientTrack?.requester === "object" ? {
3717
+ requester: this.RyanlinkManager.utils.getTransformedRequester(options?.clientTrack?.requester || {})
3718
+ } : {},
3719
+ ...options?.clientTrack.userData,
3720
+ ...options.track?.userData
3721
+ };
3722
+ options.track = {
3723
+ encoded: options.clientTrack?.encoded,
3724
+ requester: options.clientTrack?.requester,
3725
+ userData: options.clientTrack?.userData,
3726
+ audioTrackId: options.track?.audioTrackId ?? options.clientTrack?.audioTrackId
3727
+ };
3728
+ if (options.track.audioTrackId && !this.node.isNodeLink()) {
3729
+ delete options.track.audioTrackId;
3730
+ }
3731
+ }
3732
+ if (options?.track?.encoded || options?.track?.identifier) {
3733
+ this.queue.current = options.clientTrack || null;
3734
+ this.queue.utils.save();
3735
+ if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
3736
+ this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
3737
+ let vol = Number(this.volume);
3738
+ if (this.RyanlinkManager.options.playerOptions.volumeDecrementer) vol *= this.RyanlinkManager.options.playerOptions.volumeDecrementer;
3739
+ this.internalVolume = Math.round(vol);
3740
+ options.volume = this.internalVolume;
3741
+ }
3742
+ const track = Object.fromEntries(
3743
+ Object.entries({
3744
+ encoded: options.track.encoded,
3745
+ identifier: options.track.identifier,
3746
+ userData: {
3747
+ ...typeof options?.track?.requester === "object" ? {
3748
+ requester: this.RyanlinkManager.utils.getTransformedRequester(options?.track?.requester || {})
3749
+ } : {},
3750
+ ...options.track.userData
3751
+ },
3752
+ audioTrackId: options.track.audioTrackId
3753
+ }).filter((v) => typeof v[1] !== "undefined")
3754
+ );
3755
+ this.dispatchDebug("PlayerPlayWithTrackReplace" /* PlayerPlayWithTrackReplace */, {
3756
+ state: "log",
3757
+ message: `Player was called to play something, with a specific track provided. Replacing the current Track and resolving the track on trackStart Event.`,
3758
+ functionLayer: "Player > play()"
3759
+ });
3760
+ if (track.audioTrackId && !this.node.isNodeLink()) {
3761
+ delete track.audioTrackId;
3762
+ }
3763
+ return this.node.updatePlayer({
3764
+ guildId: this.guildId,
3765
+ noReplace: false,
3766
+ playerOptions: Object.fromEntries(
3767
+ Object.entries({
3768
+ track,
3769
+ position: options.position ?? void 0,
3770
+ paused: options.paused ?? void 0,
3771
+ endTime: options?.endTime ?? void 0,
3772
+ filters: options?.filters ?? void 0,
3773
+ volume: options.volume ?? this.internalVolume ?? void 0,
3774
+ voice: options.voice ?? void 0
3775
+ }).filter((v) => typeof v[1] !== "undefined")
3776
+ )
3777
+ });
3778
+ }
3779
+ if (!this.queue.current && this.queue.tracks.length) await queueTrackEnd(this);
3780
+ if (this.queue.current && this.RyanlinkManager.utils.isUnresolvedTrack(this.queue.current)) {
3781
+ this.dispatchDebug("PlayerPlayUnresolvedTrack" /* PlayerPlayUnresolvedTrack */, {
3782
+ state: "log",
3783
+ message: `Player Play was called, current Queue Song is unresolved, resolving the track.`,
3784
+ functionLayer: "Player > play()"
3785
+ });
3786
+ try {
3787
+ await this.queue.current.resolve(this);
3788
+ if (typeof options.track?.userData === "object" && this.queue.current)
3789
+ this.queue.current.userData = {
3790
+ ...typeof this.queue.current?.requester === "object" ? {
3791
+ requester: this.RyanlinkManager.utils.getTransformedRequester(this.queue.current?.requester || {})
3792
+ } : {},
3793
+ ...this.queue.current?.userData,
3794
+ ...options.track?.userData
3795
+ };
3796
+ } catch (error) {
3797
+ this.dispatchDebug("PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
3798
+ state: "error",
3799
+ error,
3800
+ message: `Player Play was called, current Queue Song is unresolved, but couldn't resolve it`,
3801
+ functionLayer: "Player > play() > resolve currentTrack"
3802
+ });
3803
+ this.RyanlinkManager.emit("trackError", this, this.queue.current, error);
3804
+ if (options && "clientTrack" in options) delete options.clientTrack;
3805
+ if (options && "track" in options) delete options.track;
3806
+ await queueTrackEnd(this, true);
3807
+ if (this.RyanlinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0]) return this.play(options);
3808
+ return this;
3809
+ }
3810
+ }
3811
+ if (!this.queue.current) throw new Error(`There is no Track in the Queue, nor provided in the PlayOptions`);
3812
+ if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
3813
+ this.volume = Math.max(Math.min(options?.volume, 1e3), 0);
3814
+ let vol = Number(this.volume);
3815
+ if (this.RyanlinkManager.options.playerOptions.volumeDecrementer) vol *= this.RyanlinkManager.options.playerOptions.volumeDecrementer;
3816
+ this.internalVolume = Math.round(vol);
3817
+ options.volume = this.internalVolume;
3818
+ }
3819
+ const finalOptions = Object.fromEntries(
3820
+ Object.entries({
3821
+ track: {
3822
+ encoded: this.queue.current?.encoded || null,
3823
+ userData: {
3824
+ ...typeof this.queue.current?.requester === "object" ? {
3825
+ requester: this.RyanlinkManager.utils.getTransformedRequester(this.queue.current?.requester || {})
3826
+ } : {},
3827
+ ...options?.track?.userData,
3828
+ ...this.queue.current?.userData
3829
+ },
3830
+ audioTrackId: options?.track?.audioTrackId
3831
+ },
3832
+ volume: this.internalVolume,
3833
+ position: options?.position ?? 0,
3834
+ endTime: options?.endTime ?? void 0,
3835
+ filters: options?.filters ?? void 0,
3836
+ paused: options?.paused ?? void 0,
3837
+ voice: options?.voice ?? void 0
3838
+ }).filter((v) => typeof v[1] !== "undefined")
3839
+ );
3840
+ if (finalOptions.track.audioTrackId && !this.node.isNodeLink()) {
3841
+ delete finalOptions.track.audioTrackId;
3842
+ }
3843
+ if (typeof finalOptions.position !== "undefined" && isNaN(finalOptions.position) || typeof finalOptions.position === "number" && finalOptions.position < 0 || typeof finalOptions.position === "number" && this.queue.current.info.duration > 0 && finalOptions.position >= this.queue.current.info.duration)
3844
+ throw new Error("PlayerOption#position must be a positive number, less than track's duration");
3845
+ if (typeof finalOptions.volume !== "undefined" && isNaN(finalOptions.volume) || typeof finalOptions.volume === "number" && finalOptions.volume < 0)
3846
+ throw new Error("PlayerOption#volume must be a positive number");
3847
+ if (typeof finalOptions.endTime !== "undefined" && isNaN(finalOptions.endTime) || typeof finalOptions.endTime === "number" && finalOptions.endTime < 0 || typeof finalOptions.endTime === "number" && this.queue.current.info.duration > 0 && finalOptions.endTime >= this.queue.current.info.duration)
3848
+ throw new Error("PlayerOption#endTime must be a positive number, less than track's duration");
3849
+ if (typeof finalOptions.position === "number" && typeof finalOptions.endTime === "number" && finalOptions.endTime < finalOptions.position)
3850
+ throw new Error("PlayerOption#endTime must be bigger than PlayerOption#position");
3851
+ const now = performance.now();
3852
+ await this.node.updatePlayer({
3853
+ guildId: this.guildId,
3854
+ noReplace: options?.noReplace ?? false,
3855
+ playerOptions: finalOptions
3856
+ });
3857
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
3858
+ return this;
3859
+ }
3860
+ oldJSON = this.toJSON();
3861
+ syncState() {
3862
+ this.RyanlinkManager.emit("playerClientUpdate", this.oldJSON, this);
3863
+ this.oldJSON = this.toJSON();
3864
+ if (this.filterManager.filterUpdatedState) {
3865
+ this.filterManager.filterUpdatedState = false;
3866
+ if (this.queue.current && this.queue.current.info.duration < (this.RyanlinkManager.options.advancedOptions?.maxFilterFixDuration || 6e5)) {
3867
+ this.seek(this.position);
3868
+ }
3869
+ }
3870
+ }
3871
+ async setVolume(volume, ignoreVolumeDecrementer = false) {
3872
+ volume = Number(volume);
3873
+ if (isNaN(volume)) throw new TypeError("Volume must be a number.");
3874
+ this.volume = Math.round(Math.max(Math.min(volume, 1e3), 0));
3875
+ this.internalVolume = Math.round(
3876
+ Math.max(
3877
+ Math.min(
3878
+ Math.round(
3879
+ this.RyanlinkManager.options.playerOptions.volumeDecrementer && !ignoreVolumeDecrementer ? this.volume * this.RyanlinkManager.options.playerOptions.volumeDecrementer : this.volume
3880
+ ),
3881
+ 1e3
3882
+ ),
3883
+ 0
3884
+ )
3885
+ );
3886
+ this.syncState();
3887
+ const now = performance.now();
3888
+ if (this.RyanlinkManager.options.playerOptions.applyVolumeAsFilter) {
3889
+ this.dispatchDebug("PlayerVolumeAsFilter" /* PlayerVolumeAsFilter */, {
3890
+ state: "log",
3891
+ message: `Player Volume was set as a Filter, because RyanlinkManager option "playerOptions.applyVolumeAsFilter" is true`,
3892
+ functionLayer: "Player > setVolume()"
3893
+ });
3894
+ await this.node.updatePlayer({
3895
+ guildId: this.guildId,
3896
+ playerOptions: { filters: { volume: this.internalVolume / 100 } }
3897
+ });
3898
+ } else {
3899
+ await this.node.updatePlayer({
3900
+ guildId: this.guildId,
3901
+ playerOptions: { volume: this.internalVolume }
3902
+ });
3903
+ }
3904
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
3905
+ return this;
3906
+ }
3907
+ async audioSearch(query, requestUser, throwOnEmpty = false) {
3908
+ return this.node.audioSearch(query, requestUser, throwOnEmpty);
3909
+ }
3910
+ async setSponsorBlock(segments = ["sponsor", "selfpromo"]) {
3911
+ return this.node.setSponsorBlock(this, segments);
3912
+ }
3913
+ async getSponsorBlock() {
3914
+ return this.node.getSponsorBlock(this);
3915
+ }
3916
+ async deleteSponsorBlock() {
3917
+ return this.node.deleteSponsorBlock(this);
3918
+ }
3919
+ async search(query, requestUser, throwOnEmpty = false) {
3920
+ const Query = this.RyanlinkManager.utils.transformQuery(query);
3921
+ if (["bcsearch", "bandcamp"].includes(Query.source) && !this.node.info?.sourceManagers.includes("bandcamp")) {
3922
+ this.dispatchDebug("BandcampSearchLokalEngine" /* BandcampSearchLokalEngine */, {
3923
+ state: "log",
3924
+ message: `Player.search was called with a Bandcamp Query, but no bandcamp search was enabled on the audio node, searching with the custom Search Engine.`,
3925
+ functionLayer: "Player > search()"
3926
+ });
3927
+ return await this._bandCampSearch(Query.query, requestUser);
3928
+ }
3929
+ return this.node.search(Query, requestUser, throwOnEmpty);
3930
+ }
3931
+ async _bandCampSearch(query, requestUser) {
3932
+ let error = null;
3933
+ let tracks = [];
3934
+ if (this.RyanlinkManager.options.advancedOptions.debugOptions.logCustomSearches) console.log(`Audio-Debug | SEARCHING | - ${query} on ryanlink`);
3935
+ this.RyanlinkManager.utils.validateQueryString(this.node, query);
3936
+ try {
3937
+ const requestUrl = new URL("https://bandcamp.com/api/nusearch/2/autocomplete");
3938
+ requestUrl.searchParams.append("q", query);
3939
+ const data = await fetch(requestUrl.toString(), {
3940
+ headers: {
3941
+ "User-Agent": "android-async-http/1.4.1 (http://loopj.com/android-async-http)",
3942
+ Cookie: "$Version=1"
3943
+ }
3944
+ });
3945
+ if (!data.ok) throw new Error(`Bandcamp Error: ${data.statusText}`);
3946
+ let json = null;
3947
+ try {
3948
+ json = await data.json();
3949
+ } catch {
3950
+ throw new Error("Invalid JSON response from Bandcamp");
3951
+ }
3952
+ tracks = json?.results?.filter((x) => !!x && typeof x === "object" && "type" in x && x.type === "t").map?.(
3953
+ (item) => this.RyanlinkManager.utils.buildUnresolvedTrack(
3954
+ {
3955
+ uri: item.url || item.uri,
3956
+ artworkUrl: item.img,
3957
+ author: item.band_name,
3958
+ title: item.name,
3959
+ identifier: item.id ? `${item.id}` : item.url?.split("/")?.reverse()[0]
3960
+ },
3961
+ requestUser
3962
+ )
3963
+ ) || [];
3964
+ } catch (e) {
3965
+ error = e;
3966
+ }
3967
+ return {
3968
+ loadType: "search",
3969
+ exception: error,
3970
+ pluginInfo: {},
3971
+ playlist: null,
3972
+ tracks
3973
+ };
3974
+ }
3975
+ async pause() {
3976
+ this.paused = true;
3977
+ this.lastPositionChange = null;
3978
+ const now = performance.now();
3979
+ this.syncState();
3980
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
3981
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
3982
+ this.RyanlinkManager.emit("playerPaused", this, this.queue.current);
3983
+ return this;
3984
+ }
3985
+ async resume() {
3986
+ if (!this.paused) throw new Error("Player isn't paused - not able to resume.");
3987
+ this.paused = false;
3988
+ const now = performance.now();
3989
+ this.syncState();
3990
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
3991
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
3992
+ this.RyanlinkManager.emit("playerResumed", this, this.queue.current);
3993
+ return this;
3994
+ }
3995
+ async seek(position) {
3996
+ if (!this.queue.current) return void 0;
3997
+ position = Number(position);
3998
+ if (isNaN(position)) throw new RangeError("Position must be a number.");
3999
+ if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream) throw new RangeError("Current Track is not seekable / a stream");
4000
+ if (position < 0 || position > this.queue.current.info.duration) position = Math.max(Math.min(position, this.queue.current.info.duration), 0);
4001
+ this.lastPositionChange = Date.now();
4002
+ this.lastPosition = position;
4003
+ const now = performance.now();
4004
+ this.syncState();
4005
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { position } });
4006
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
4007
+ return this;
4008
+ }
4009
+ async setRepeatMode(repeatMode) {
4010
+ if (!["off", "track", "queue"].includes(repeatMode)) throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
4011
+ this.repeatMode = repeatMode;
4012
+ this.syncState();
4013
+ return this;
4014
+ }
4015
+ async skip(skipTo = 0, throwError = true) {
4016
+ if (!this.queue.tracks.length && (throwError || typeof skipTo === "boolean" && skipTo === true))
4017
+ throw new RangeError("Can't skip more than the queue size");
4018
+ if (typeof skipTo === "number" && skipTo > 1) {
4019
+ if (skipTo > this.queue.tracks.length) throw new RangeError("Can't skip more than the queue size");
4020
+ await this.queue.splice(0, skipTo - 1);
4021
+ }
4022
+ if (!this.playing && !this.queue.current) return this.play(), this;
4023
+ const now = performance.now();
4024
+ this.setData("internal_skipped", true);
4025
+ await this.node.updatePlayer({
4026
+ guildId: this.guildId,
4027
+ playerOptions: { track: { encoded: null }, paused: false }
4028
+ });
4029
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
4030
+ return this;
4031
+ }
4032
+ async stopPlaying(clearQueue = true, executeAutoplay = false) {
4033
+ this.setData("internal_stopPlaying", true);
4034
+ if (this.queue.tracks.length && clearQueue === true) await this.queue.splice(0, this.queue.tracks.length);
4035
+ if (executeAutoplay === false) this.setData("internal_autoplayStopPlaying", true);
4036
+ else this.setData("internal_autoplayStopPlaying", void 0);
4037
+ const now = performance.now();
4038
+ await this.node.updatePlayer({
4039
+ guildId: this.guildId,
4040
+ playerOptions: this.node.isNodeLink() ? {
4041
+ track: { encoded: null },
4042
+ nextTrack: { encoded: null }
4043
+ } : {
4044
+ track: { encoded: null }
4045
+ }
4046
+ });
4047
+ this.paused = false;
4048
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
4049
+ return this;
4050
+ }
4051
+ async connect() {
4052
+ if (!this.options.voiceChannelId) throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
4053
+ await this.RyanlinkManager.options.sendToShard(this.guildId, {
4054
+ op: 4,
4055
+ d: {
4056
+ guild_id: this.guildId,
4057
+ channel_id: this.options.voiceChannelId,
4058
+ self_mute: this.options.selfMute ?? false,
4059
+ self_deaf: this.options.selfDeaf ?? true
4060
+ }
4061
+ });
4062
+ this.voiceChannelId = this.options.voiceChannelId;
4063
+ return this;
4064
+ }
4065
+ async changeVoiceState(data) {
4066
+ if (this.options.voiceChannelId === data.voiceChannelId) throw new RangeError("New Channel can't be equal to the old Channel.");
4067
+ await this.RyanlinkManager.options.sendToShard(this.guildId, {
4068
+ op: 4,
4069
+ d: {
4070
+ guild_id: this.guildId,
4071
+ channel_id: data.voiceChannelId,
4072
+ self_mute: data.selfMute ?? this.options.selfMute ?? false,
4073
+ self_deaf: data.selfDeaf ?? this.options.selfDeaf ?? true
4074
+ }
4075
+ });
4076
+ this.options.voiceChannelId = data.voiceChannelId;
4077
+ this.options.selfMute = data.selfMute;
4078
+ this.options.selfDeaf = data.selfDeaf;
4079
+ this.voiceChannelId = data.voiceChannelId;
4080
+ return this;
4081
+ }
4082
+ async disconnect(force = false) {
4083
+ if (!force && !this.options.voiceChannelId) throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
4084
+ await this.RyanlinkManager.options.sendToShard(this.guildId, {
4085
+ op: 4,
4086
+ d: {
4087
+ guild_id: this.guildId,
4088
+ channel_id: null,
4089
+ self_mute: false,
4090
+ self_deaf: false
4091
+ }
4092
+ });
4093
+ this.voiceChannelId = null;
4094
+ return this;
4095
+ }
4096
+ async destroy(reason, disconnect = true) {
4097
+ if (this.RyanlinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
4098
+ console.log(`Audio-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
4099
+ if (this.getData("internal_queueempty")) {
4100
+ clearTimeout(this.getData("internal_queueempty"));
4101
+ this.setData("internal_queueempty", void 0);
4102
+ }
4103
+ if (this.getData("internal_destroystatus") === true) {
4104
+ this.dispatchDebug("PlayerDestroyingSomewhereElse" /* PlayerDestroyingSomewhereElse */, {
4105
+ state: "warn",
4106
+ message: `Player is already destroying somewhere else..`,
4107
+ functionLayer: "Player > destroy()"
4108
+ });
4109
+ if (this.RyanlinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
4110
+ console.log(`Audio-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
4111
+ return;
4112
+ }
4113
+ this.setData("internal_destroystatus", true);
4114
+ if (disconnect) await this.disconnect(true);
4115
+ else this.setData("internal_destroywithoutdisconnect", true);
4116
+ await this.queue.utils.destroy();
4117
+ this.RyanlinkManager.deletePlayer(this.guildId);
4118
+ await this.node.destroyPlayer(this.guildId);
4119
+ if (this.RyanlinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
4120
+ console.log(`Audio-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Player got destroyed successfully`);
4121
+ this.RyanlinkManager.emit("playerDestroy", this, reason);
4122
+ return this;
4123
+ }
4124
+ async getCurrentLyrics(skipTrackSource) {
4125
+ return await this.node.lyrics.getCurrent(this.guildId, skipTrackSource);
4126
+ }
4127
+ async getLyrics(track, skipTrackSource) {
4128
+ return await this.node.lyrics.get(track, skipTrackSource);
4129
+ }
4130
+ subscribeLyrics(skipTrackSource) {
4131
+ return this.node.lyrics.subscribe(this.guildId, skipTrackSource);
4132
+ }
4133
+ unsubscribeLyrics() {
4134
+ return this.node.lyrics.unsubscribe(this.guildId);
4135
+ }
4136
+ async changeNode(newNode, checkSources = true) {
4137
+ const updateNode = typeof newNode === "string" ? this.RyanlinkManager.nodeManager.nodes.get(newNode) : newNode;
4138
+ if (!updateNode) throw new Error("Could not find the new Node");
4139
+ if (!updateNode.connected) throw new Error("The provided Node is not active or disconnected");
4140
+ if (this.node.id === updateNode.id) throw new Error("Player is already on the provided Node");
4141
+ if (this.getData("internal_nodeChanging") === true) throw new Error("Player is already changing the node please wait");
4142
+ if (checkSources) {
4143
+ const isDefaultSource = () => {
4144
+ try {
4145
+ this.RyanlinkManager.utils.validateSourceString(updateNode, this.RyanlinkManager.options.playerOptions.defaultSearchPlatform);
4146
+ return true;
4147
+ } catch {
4148
+ return false;
4149
+ }
4150
+ };
4151
+ if (!isDefaultSource())
4152
+ throw new RangeError(
4153
+ `defaultSearchPlatform "${this.RyanlinkManager.options.playerOptions.defaultSearchPlatform}" is not supported by the newNode`
4154
+ );
4155
+ if (this.queue.current || this.queue.tracks.length) {
4156
+ const trackSources = new Set([this.queue.current, ...this.queue.tracks].map((track) => track.info.sourceName));
4157
+ const missingSources = [...trackSources].filter((source) => !updateNode.info?.sourceManagers.includes(source));
4158
+ if (updateNode._checkForSources && missingSources.length)
4159
+ throw new RangeError(`Sources missing for Node ${updateNode.id}: ${missingSources.join(", ")}`);
4160
+ }
4161
+ }
4162
+ this.dispatchDebug("PlayerChangeNode" /* PlayerChangeNode */, {
4163
+ state: "log",
4164
+ message: `Player.changeNode() was executed, trying to change from "${this.node.id}" to "${updateNode.id}"`,
4165
+ functionLayer: "Player > changeNode()"
4166
+ });
4167
+ const data = this.toJSON();
4168
+ const currentTrack = this.queue.current;
4169
+ if (!this.voice.endpoint || !this.voice.sessionId || !this.voice.token) throw new Error("Voice Data is missing, can't change the node");
4170
+ this.setData("internal_nodeChanging", true);
4171
+ if (this.node.connected) await this.node.destroyPlayer(this.guildId);
4172
+ this.node = updateNode;
4173
+ const now = performance.now();
4174
+ try {
4175
+ await this.connect();
4176
+ const hasSponsorBlock = !this.node._checkForPlugins || this.node.info?.plugins?.find((v) => v.name === "sponsorblock-plugin");
4177
+ if (hasSponsorBlock) {
4178
+ const sponsorBlockCategories = this.getData("internal_sponsorBlockCategories");
4179
+ if (Array.isArray(sponsorBlockCategories) && sponsorBlockCategories.length) {
4180
+ await this.setSponsorBlock(sponsorBlockCategories).catch((error) => {
4181
+ this.dispatchDebug("PlayerChangeNode" /* PlayerChangeNode */, {
4182
+ state: "error",
4183
+ error,
4184
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
4185
+ functionLayer: "Player > changeNode()"
4186
+ });
4187
+ });
4188
+ } else if (this.RyanlinkManager.options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement !== false) {
4189
+ await this.setSponsorBlock().catch((error) => {
4190
+ this.dispatchDebug("PlayerChangeNode" /* PlayerChangeNode */, {
4191
+ state: "error",
4192
+ error,
4193
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
4194
+ functionLayer: "Player > changeNode()"
4195
+ });
4196
+ });
4197
+ }
4198
+ }
4199
+ await this.node.updatePlayer({
4200
+ guildId: this.guildId,
4201
+ noReplace: false,
4202
+ playerOptions: {
4203
+ ...currentTrack && {
4204
+ track: currentTrack,
4205
+ position: data.lastPosition || 0,
4206
+ volume: this.internalVolume,
4207
+ paused: this.paused
4208
+ },
4209
+ voice: {
4210
+ token: this.voice.token,
4211
+ endpoint: this.voice.endpoint,
4212
+ sessionId: this.voice.sessionId,
4213
+ channelId: this.voice.channelId
4214
+ }
4215
+ }
4216
+ });
4217
+ this.filterManager.applyPlayerFilters();
4218
+ this.ping.node = Math.round((performance.now() - now) / 10) / 100;
4219
+ return this.node.id;
4220
+ } catch (error) {
4221
+ this.dispatchDebug("PlayerChangeNode" /* PlayerChangeNode */, {
4222
+ state: "error",
4223
+ error,
4224
+ message: `Player.changeNode() execution failed`,
4225
+ functionLayer: "Player > changeNode()"
4226
+ });
4227
+ throw new Error(`Failed to change the node: ${error}`);
4228
+ } finally {
4229
+ this.setData("internal_nodeChanging", void 0);
4230
+ }
4231
+ }
4232
+ async moveNode(node) {
4233
+ try {
4234
+ if (!node)
4235
+ node = Array.from(this.RyanlinkManager.nodeManager.leastUsedNodes("playingPlayers")).find(
4236
+ (n) => n.connected && n.options.id !== this.node.options.id
4237
+ ).id;
4238
+ if (!node || !this.RyanlinkManager.nodeManager.nodes.get(node)) throw new RangeError("No nodes are available.");
4239
+ if (this.node.options.id === node) return this;
4240
+ this.RyanlinkManager.emit("debug", "PlayerChangeNode" /* PlayerChangeNode */, {
4241
+ state: "log",
4242
+ message: `Player.moveNode() was executed, trying to move from "${this.node.id}" to "${node}"`,
4243
+ functionLayer: "Player > moveNode()"
4244
+ });
4245
+ const updateNode = this.RyanlinkManager.nodeManager.nodes.get(node);
4246
+ if (!updateNode) throw new RangeError("No nodes are available.");
4247
+ return await this.changeNode(updateNode);
4248
+ } catch (error) {
4249
+ throw new Error(`Failed to move the node: ${error}`);
4250
+ }
4251
+ }
4252
+ toJSON() {
4253
+ return {
4254
+ guildId: this.guildId,
4255
+ options: this.options,
4256
+ voiceChannelId: this.voiceChannelId,
4257
+ textChannelId: this.textChannelId,
4258
+ position: this.position,
4259
+ lastPosition: this.lastPosition,
4260
+ lastPositionChange: this.lastPositionChange,
4261
+ volume: this.volume,
4262
+ internalVolume: this.internalVolume,
4263
+ repeatMode: this.repeatMode,
4264
+ paused: this.paused,
4265
+ playing: this.playing,
4266
+ createdTimeStamp: this.createdTimeStamp,
4267
+ filters: this.filterManager?.data || {},
4268
+ equalizer: this.filterManager?.equalizerBands || [],
4269
+ nodeId: this.node?.id,
4270
+ nodeSessionId: this.node?.sessionId,
4271
+ ping: this.ping,
4272
+ queue: this.queue?.utils?.toJSON?.()
4273
+ };
4274
+ }
4275
+ };
4276
+ var Autoplay = class {
4277
+ static adding = /* @__PURE__ */ new Set();
4278
+ static async defaultAutoplay(player, lastTrack) {
4279
+ if (!lastTrack) return;
4280
+ const config = player.RyanlinkManager.options.playerOptions.autoplayConfig;
4281
+ if (config.enabled === false) return;
4282
+ if (this.adding.has(player.guildId)) return;
4283
+ this.adding.add(player.guildId);
4284
+ try {
4285
+ const playedData = this.buildPlayedData(player);
4286
+ const relatedTracks = await this.fetchRelatedTracks(player, lastTrack, config, playedData);
4287
+ if (relatedTracks.length > 0) {
4288
+ for (const track of relatedTracks) {
4289
+ player.queue.add(track);
4290
+ }
4291
+ if (!player.playing && !player.paused && player.queue.tracks.length > 0) {
4292
+ await player.play();
4293
+ }
4294
+ }
4295
+ } catch (error) {
4296
+ player.RyanlinkManager.emit("debug", "AutoplayError", {
4297
+ state: "error",
4298
+ message: `Autoplay failed: ${error.message}`,
4299
+ error,
4300
+ functionLayer: "Autoplay > defaultAutoplay()"
4301
+ });
4302
+ } finally {
4303
+ this.adding.delete(player.guildId);
4304
+ }
4305
+ }
4306
+ static buildPlayedData(player) {
4307
+ const playedIds = /* @__PURE__ */ new Set();
4308
+ const playedTracks = /* @__PURE__ */ new Set();
4309
+ const addTrack = (track) => {
4310
+ if (!track) return;
4311
+ if (track.info.identifier) playedIds.add(track.info.identifier);
4312
+ if (track.info.isrc) playedIds.add(track.info.isrc);
4313
+ if (track.info.title && track.info.author) {
4314
+ const key = `${track.info.title.toLowerCase()}|${track.info.author.toLowerCase()}`;
4315
+ playedTracks.add(key);
4316
+ }
4317
+ };
4318
+ if (player.queue.current) addTrack(player.queue.current);
4319
+ player.queue.previous.forEach(addTrack);
4320
+ player.queue.tracks.forEach(addTrack);
4321
+ return { playedIds, playedTracks };
4322
+ }
4323
+ static async fetchRelatedTracks(player, lastTrack, config, playedData) {
4324
+ const tracks = [];
4325
+ const source = lastTrack.info.sourceName?.toLowerCase();
4326
+ if (source?.includes("spotify")) {
4327
+ const spotifyTracks = await this.getSpotifyRecommendations(player, lastTrack);
4328
+ tracks.push(...spotifyTracks);
4329
+ }
4330
+ if (tracks.length < (config.limit || 5) && (source?.includes("youtube") || source?.includes("yt"))) {
4331
+ const youtubeTracks = await this.getYouTubeSimilar(player, lastTrack);
4332
+ tracks.push(...youtubeTracks);
4333
+ }
4334
+ if (tracks.length === 0) {
4335
+ const searchTracks = await this.getArtistSearch(player, lastTrack, config.defaultSource || "ytsearch");
4336
+ tracks.push(...searchTracks);
4337
+ }
4338
+ return this.filterTracks(tracks, playedData, config);
4339
+ }
4340
+ static filterTracks(tracks, playedData, config) {
4341
+ const excludeKeywords = config.excludeKeywords?.map((k) => k.toLowerCase()) || [];
4342
+ return tracks.filter((track) => {
4343
+ if (playedData.playedIds.has(track.info.identifier)) return false;
4344
+ if (track.info.isrc && playedData.playedIds.has(track.info.isrc)) return false;
4345
+ const key = `${track.info.title.toLowerCase()}|${track.info.author.toLowerCase()}`;
4346
+ if (playedData.playedTracks.has(key)) return false;
4347
+ if (track.info.duration < (config.minDuration || 2e4) || track.info.duration > (config.maxDuration || 9e5)) {
4348
+ return false;
4349
+ }
4350
+ const title = track.info.title.toLowerCase();
4351
+ if (excludeKeywords.some((keyword) => title.includes(keyword))) return false;
4352
+ return true;
4353
+ }).sort(() => Math.random() - 0.5).slice(0, config.limit || 5);
4354
+ }
4355
+ static async getSpotifyRecommendations(player, track) {
4356
+ try {
4357
+ const res = await player.search({ query: `sprec:seed_tracks=${track.info.identifier}` }, "Autoplay");
4358
+ if (res.loadType === "search" || res.loadType === "track") return res.tracks;
4359
+ if (res.loadType === "playlist") return res.tracks;
4360
+ } catch {
4361
+ }
4362
+ return [];
4363
+ }
4364
+ static async getYouTubeSimilar(player, track) {
4365
+ try {
4366
+ const res = await player.search({ query: `https://www.youtube.com/watch?v=${track.info.identifier}` }, "Autoplay");
4367
+ if (res.loadType === "playlist") return res.tracks;
4368
+ } catch {
4369
+ }
4370
+ return [];
4371
+ }
4372
+ static async getArtistSearch(player, track, source) {
4373
+ try {
4374
+ const res = await player.search({ query: track.info.author, source }, "Autoplay");
4375
+ if (res.loadType === "search" || res.loadType === "track") return res.tracks;
4376
+ } catch {
4377
+ }
4378
+ return [];
4379
+ }
4380
+ };
4381
+
4382
+ // src/core/Manager.ts
4383
+ var RyanlinkManager = class _RyanlinkManager extends import_node_events2.EventEmitter {
4384
+ emit(event, ...args) {
4385
+ return super.emit(event, ...args);
4386
+ }
4387
+ on(event, listener) {
4388
+ return super.on(event, listener);
4389
+ }
4390
+ once(event, listener) {
4391
+ return super.once(event, listener);
4392
+ }
4393
+ off(event, listener) {
4394
+ return super.off(event, listener);
4395
+ }
4396
+ removeListener(event, listener) {
4397
+ return super.removeListener(event, listener);
4398
+ }
4399
+ options;
4400
+ nodeManager;
4401
+ utils;
4402
+ initiated = false;
4403
+ players = new MiniMap();
4404
+ applyOptions(options) {
4405
+ const optionsToAssign = {
4406
+ ...options,
4407
+ client: {
4408
+ ...options?.client,
4409
+ id: options?.client?.id,
4410
+ username: options?.client?.username ?? "ryanlink"
4411
+ },
4412
+ sendToShard: options?.sendToShard,
4413
+ autoMove: options?.autoMove ?? false,
4414
+ nodes: options?.nodes,
4415
+ playerClass: options?.playerClass ?? Player,
4416
+ playerOptions: {
4417
+ applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
4418
+ clientBasedPositionUpdateInterval: options?.playerOptions?.clientBasedPositionUpdateInterval ?? 100,
4419
+ defaultSearchPlatform: options?.playerOptions?.defaultSearchPlatform ?? "ytsearch",
4420
+ allowCustomSources: options?.playerOptions?.allowCustomSources ?? false,
4421
+ onDisconnect: {
4422
+ destroyPlayer: options?.playerOptions?.onDisconnect?.destroyPlayer ?? true,
4423
+ autoReconnect: options?.playerOptions?.onDisconnect?.autoReconnect ?? false,
4424
+ autoReconnectOnlyWithTracks: options?.playerOptions?.onDisconnect?.autoReconnectOnlyWithTracks ?? false
4425
+ },
4426
+ onEmptyQueue: {
4427
+ autoPlayFunction: options?.playerOptions?.onEmptyQueue?.autoPlayFunction ?? Autoplay.defaultAutoplay,
4428
+ destroyAfterMs: options?.playerOptions?.onEmptyQueue?.destroyAfterMs ?? void 0
4429
+ },
4430
+ autoplayConfig: {
4431
+ enabled: options?.playerOptions?.autoplayConfig?.enabled ?? true,
4432
+ defaultSource: options?.playerOptions?.autoplayConfig?.defaultSource ?? "ytsearch",
4433
+ limit: options?.playerOptions?.autoplayConfig?.limit ?? 5,
4434
+ minDuration: options?.playerOptions?.autoplayConfig?.minDuration ?? 2e4,
4435
+ maxDuration: options?.playerOptions?.autoplayConfig?.maxDuration ?? 9e5,
4436
+ excludeKeywords: options?.playerOptions?.autoplayConfig?.excludeKeywords ?? ["nightcore", "bass boosted", "8d audio", "slowed", "reverb"]
4437
+ },
4438
+ volumeDecrementer: options?.playerOptions?.volumeDecrementer ?? 1,
4439
+ requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
4440
+ useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
4441
+ minAutoPlayMs: options?.playerOptions?.minAutoPlayMs ?? 1e4,
4442
+ maxErrorsPerTime: {
4443
+ threshold: options?.playerOptions?.maxErrorsPerTime?.threshold ?? 35e3,
4444
+ maxAmount: options?.playerOptions?.maxErrorsPerTime?.maxAmount ?? 3
4445
+ },
4446
+ enforceSponsorBlockRequestForEventEnablement: options?.playerOptions?.enforceSponsorBlockRequestForEventEnablement ?? true
4447
+ },
4448
+ linksWhitelist: options?.linksWhitelist ?? [],
4449
+ linksBlacklist: options?.linksBlacklist ?? [],
4450
+ linksAllowed: options?.linksAllowed ?? true,
4451
+ autoSkip: options?.autoSkip ?? true,
4452
+ autoSkipOnResolveError: options?.autoSkipOnResolveError ?? true,
4453
+ emitNewSongsOnly: options?.emitNewSongsOnly ?? false,
4454
+ queueOptions: {
4455
+ maxPreviousTracks: options?.queueOptions?.maxPreviousTracks ?? 25,
4456
+ queueChangesWatcher: options?.queueOptions?.queueChangesWatcher ?? null,
4457
+ queueStore: options?.queueOptions?.queueStore ?? new DefaultQueueStore()
4458
+ },
4459
+ advancedOptions: {
4460
+ enableDebugEvents: options?.advancedOptions?.enableDebugEvents ?? false,
4461
+ maxFilterFixDuration: options?.advancedOptions?.maxFilterFixDuration ?? 6e5,
4462
+ debugOptions: {
4463
+ logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
4464
+ noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
4465
+ playerDestroy: {
4466
+ dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
4467
+ debugLog: options?.advancedOptions?.debugOptions?.playerDestroy?.debugLog ?? false
4468
+ }
4469
+ }
4470
+ }
4471
+ };
4472
+ this.options = optionsToAssign;
4473
+ return;
4474
+ }
4475
+ validateOptions(options) {
4476
+ if (typeof options?.sendToShard !== "function") throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
4477
+ if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
4478
+ throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
4479
+ if (options?.autoSkipOnResolveError && typeof options?.autoSkipOnResolveError !== "boolean")
4480
+ throw new SyntaxError("ManagerOption.autoSkipOnResolveError must be either false | true aka boolean");
4481
+ if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean")
4482
+ throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
4483
+ if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every((node) => this.utils.isNodeOptions(node)))
4484
+ throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
4485
+ if (options?.queueOptions?.queueStore) {
4486
+ const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueStore));
4487
+ const requiredKeys = ["get", "set", "stringify", "parse", "delete"];
4488
+ if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueStore[v] === "function"))
4489
+ throw new SyntaxError(`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`);
4490
+ }
4491
+ if (options?.queueOptions?.queueChangesWatcher) {
4492
+ const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueChangesWatcher));
4493
+ const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
4494
+ if (!requiredKeys.every((v) => keys.includes(v)) || !requiredKeys.every((v) => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
4495
+ throw new SyntaxError(
4496
+ `The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`
4497
+ );
4498
+ }
4499
+ if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
4500
+ options.queueOptions.maxPreviousTracks = 25;
4501
+ }
4502
+ dispatchDebug(name, eventData) {
4503
+ if (!this.options?.advancedOptions?.enableDebugEvents) return;
4504
+ this.emit("debug", name, eventData);
4505
+ }
4506
+ static _noAudioDebugPrefix = "Audio-Debug | NO-AUDIO [::] provideVoiceUpdate function, ";
4507
+ _debugNoAudio(state, functionLayer, messages, ...consoleArgs) {
4508
+ this.dispatchDebug("NoAudioDebug" /* NoAudioDebug */, { state, functionLayer, message: messages.message });
4509
+ if (this.options?.advancedOptions?.debugOptions?.noAudio === true) {
4510
+ const consoleMsg = messages.consoleMessage ?? messages.message;
4511
+ console.debug(_RyanlinkManager._noAudioDebugPrefix + consoleMsg, ...consoleArgs);
4512
+ }
4513
+ }
4514
+ constructor(options) {
4515
+ super();
4516
+ if (!options) throw new SyntaxError("No Manager Options Provided");
4517
+ this.utils = new RyanlinkUtils(this);
4518
+ this.applyOptions(options);
4519
+ this.validateOptions(this.options);
4520
+ this.nodeManager = new NodeManager(this);
4521
+ }
4522
+ getPlayer(guildId) {
4523
+ return this.players.get(guildId);
4524
+ }
4525
+ createPlayer(options) {
4526
+ const oldPlayer = this.getPlayer(options?.guildId);
4527
+ if (oldPlayer) return oldPlayer;
4528
+ const newPlayer = new this.options.playerClass(options, this, true);
4529
+ this.players.set(newPlayer.guildId, newPlayer);
4530
+ this.emit("playerCreate", newPlayer);
4531
+ return newPlayer;
4532
+ }
4533
+ destroyPlayer(guildId, destroyReason) {
4534
+ const oldPlayer = this.getPlayer(guildId);
4535
+ if (!oldPlayer) return;
4536
+ return oldPlayer.destroy(destroyReason);
4537
+ }
4538
+ deletePlayer(guildId) {
4539
+ const oldPlayer = this.getPlayer(guildId);
4540
+ if (!oldPlayer) return;
4541
+ if (typeof oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.getData("internal_destroywithoutdisconnect")) {
4542
+ if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
4543
+ throw new Error(`Use Player#destroy() not RyanlinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`);
4544
+ this.dispatchDebug("PlayerDeleteInsteadOfDestroy" /* PlayerDeleteInsteadOfDestroy */, {
4545
+ state: "warn",
4546
+ message: "Use Player#destroy() not RyanlinkManager#deletePlayer() to stop the Player",
4547
+ functionLayer: "RyanlinkManager > deletePlayer()"
4548
+ });
4549
+ }
4550
+ return this.players.delete(guildId);
4551
+ }
4552
+ get useable() {
4553
+ return this.nodeManager.nodes.filter((v) => v.connected).size > 0;
4554
+ }
4555
+ async init(clientData) {
4556
+ if (this.initiated) return this;
4557
+ clientData = clientData ?? {};
4558
+ this.options.client = { ...this.options?.client, ...clientData };
4559
+ if (!this.options?.client.id) throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
4560
+ if (typeof this.options?.client.id !== "string") throw new Error('"client.id" set is not type of "string"');
4561
+ let success = 0;
4562
+ for (const node of this.nodeManager.nodes.values()) {
4563
+ try {
4564
+ await node.connect();
4565
+ success++;
4566
+ } catch (err) {
4567
+ console.error(err);
4568
+ this.nodeManager.emit("error", node, err);
4569
+ }
4570
+ }
4571
+ if (success > 0) this.initiated = true;
4572
+ else
4573
+ this.dispatchDebug("FailedToConnectToNodes" /* FailedToConnectToNodes */, {
4574
+ state: "error",
4575
+ message: "Failed to connect to at least 1 Node",
4576
+ functionLayer: "RyanlinkManager > init()"
4577
+ });
4578
+ return this;
4579
+ }
4580
+ async provideVoiceUpdate(data) {
4581
+ if (!this.initiated) {
4582
+ this._debugNoAudio("log", "RyanlinkManager > provideVoiceUpdate()", {
4583
+ message: "Manager is not initated yet",
4584
+ consoleMessage: "manager is not initated yet"
4585
+ });
4586
+ return;
4587
+ }
4588
+ if (!("t" in data)) {
4589
+ this._debugNoAudio(
4590
+ "error",
4591
+ "RyanlinkManager > provideVoiceUpdate()",
4592
+ {
4593
+ message: "No 't' in payload-data of the raw event:",
4594
+ consoleMessage: "no 't' in payload-data of the raw event:"
4595
+ },
4596
+ data
4597
+ );
4598
+ return;
4599
+ }
4600
+ if ("CHANNEL_DELETE" === data.t) {
4601
+ const update = "d" in data ? data.d : data;
4602
+ if (!update.guild_id) return;
4603
+ const player = this.getPlayer(update.guild_id);
4604
+ if (player && player.voiceChannelId === update.id) return void player.destroy("ChannelDeleted" /* ChannelDeleted */);
4605
+ }
4606
+ if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
4607
+ const update = "d" in data ? data.d : data;
4608
+ if (!update) {
4609
+ this._debugNoAudio(
4610
+ "warn",
4611
+ "RyanlinkManager > provideVoiceUpdate()",
4612
+ {
4613
+ message: `No Update data found in payload :: ${safeStringify(data, 2)}`,
4614
+ consoleMessage: "no update data found in payload:"
4615
+ },
4616
+ data
4617
+ );
4618
+ return;
4619
+ }
4620
+ if (!("token" in update) && !("session_id" in update)) {
4621
+ this._debugNoAudio(
4622
+ "error",
4623
+ "RyanlinkManager > provideVoiceUpdate()",
4624
+ {
4625
+ message: `No 'token' nor 'session_id' found in payload :: ${safeStringify(data, 2)}`,
4626
+ consoleMessage: "no 'token' nor 'session_id' found in payload:"
4627
+ },
4628
+ data
4629
+ );
4630
+ return;
4631
+ }
4632
+ const player = this.getPlayer(update.guild_id);
4633
+ if (!player) {
4634
+ this._debugNoAudio(
4635
+ "warn",
4636
+ "RyanlinkManager > provideVoiceUpdate()",
4637
+ {
4638
+ message: `No Audio Player found via key: 'guild_id' of update-data :: ${safeStringify(update, 2)}`,
4639
+ consoleMessage: "No Audio Player found via key: 'guild_id' of update-data:"
4640
+ },
4641
+ update
4642
+ );
4643
+ return;
4644
+ }
4645
+ if (player.getData("internal_destroystatus") === true) {
4646
+ this._debugNoAudio("warn", "RyanlinkManager > provideVoiceUpdate()", {
4647
+ message: "Player is in a destroying state. can't signal the voice states"
4648
+ });
4649
+ return;
4650
+ }
4651
+ if ("token" in update) {
4652
+ if (!player.node?.sessionId) throw new Error("Audio Node is either not ready or not up to date");
4653
+ const sessionId2Use = player.voice?.sessionId || ("sessionId" in update ? update.sessionId : void 0);
4654
+ const channelId2Use = player.voice?.channelId || ("channel_id" in update ? update.channel_id : void 0);
4655
+ const voiceData = {
4656
+ token: update.token,
4657
+ endpoint: update.endpoint,
4658
+ sessionId: sessionId2Use,
4659
+ channelId: channelId2Use
4660
+ };
4661
+ if (!sessionId2Use) {
4662
+ this._debugNoAudio(
4663
+ "error",
4664
+ "RyanlinkManager > provideVoiceUpdate()",
4665
+ {
4666
+ message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${safeStringify({ voice: voiceData, update, playerVoice: player.voice }, 2)}`,
4667
+ consoleMessage: "Can't send updatePlayer for voice token session - Missing sessionId"
4668
+ },
4669
+ { voice: voiceData, update, playerVoice: player.voice }
4670
+ );
4671
+ } else if (!channelId2Use) {
4672
+ this._debugNoAudio(
4673
+ "error",
4674
+ "RyanlinkManager > provideVoiceUpdate()",
4675
+ {
4676
+ message: `Can't send updatePlayer for voice token session - Missing channelId :: ${safeStringify({ voice: voiceData, update, playerVoice: player.voice }, 2)}`,
4677
+ consoleMessage: "Can't send updatePlayer for voice token session - Missing channelId"
4678
+ },
4679
+ { voice: voiceData, update, playerVoice: player.voice }
4680
+ );
4681
+ } else {
4682
+ await player.node.updatePlayer({
4683
+ guildId: player.guildId,
4684
+ playerOptions: {
4685
+ voice: voiceData
4686
+ }
4687
+ });
4688
+ this._debugNoAudio(
4689
+ "log",
4690
+ "RyanlinkManager > provideVoiceUpdate()",
4691
+ {
4692
+ message: `Sent updatePlayer for voice token session :: ${safeStringify({ voice: voiceData, update, playerVoice: player.voice }, 2)}`,
4693
+ consoleMessage: "Sent updatePlayer for voice token session"
4694
+ },
4695
+ { voice: voiceData, playerVoice: player.voice, update }
4696
+ );
4697
+ }
4698
+ return;
4699
+ }
4700
+ if (update.user_id !== this.options?.client.id) {
4701
+ if (update.user_id && player.voiceChannelId) {
4702
+ this.emit(update.channel_id === player.voiceChannelId ? "playerVoiceJoin" : "playerVoiceLeave", player, update.user_id);
4703
+ }
4704
+ this._debugNoAudio(
4705
+ "warn",
4706
+ "RyanlinkManager > provideVoiceUpdate()",
4707
+ {
4708
+ message: `voice update user is not equal to provided client id of the RyanlinkManager.options.client.id :: user: "${update.user_id}" manager client id: "${this.options?.client.id}"`,
4709
+ consoleMessage: "voice update user is not equal to provided client id of the manageroptions#client#id"
4710
+ },
4711
+ "user:",
4712
+ update.user_id,
4713
+ "manager client id:",
4714
+ this.options?.client.id
4715
+ );
4716
+ return;
4717
+ }
4718
+ if (update.channel_id) {
4719
+ if (player.voiceChannelId !== update.channel_id) this.emit("playerMove", player, player.voiceChannelId, update.channel_id);
4720
+ player.voice.sessionId = update.session_id || player.voice.sessionId;
4721
+ player.voice.channelId = update.channel_id || player.voice.channelId;
4722
+ if (!player.voice.sessionId) {
4723
+ this._debugNoAudio("warn", "RyanlinkManager > provideVoiceUpdate()", {
4724
+ message: `Function to assing sessionId provided, but no found in Payload: ${safeStringify({ update, playerVoice: player.voice }, 2)}`,
4725
+ consoleMessage: `Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`
4726
+ });
4727
+ }
4728
+ player.voiceChannelId = update.channel_id;
4729
+ player.options.voiceChannelId = update.channel_id;
4730
+ const selfMuteChanged = typeof update.self_mute === "boolean" && player.voiceState.selfMute !== update.self_mute;
4731
+ const serverMuteChanged = typeof update.mute === "boolean" && player.voiceState.serverMute !== update.mute;
4732
+ const selfDeafChanged = typeof update.self_deaf === "boolean" && player.voiceState.selfDeaf !== update.self_deaf;
4733
+ const serverDeafChanged = typeof update.deaf === "boolean" && player.voiceState.serverDeaf !== update.deaf;
4734
+ const suppressChange = typeof update.suppress === "boolean" && player.voiceState.suppress !== update.suppress;
4735
+ player.voiceState.selfDeaf = update.self_deaf ?? player.voiceState?.selfDeaf;
4736
+ player.voiceState.selfMute = update.self_mute ?? player.voiceState?.selfMute;
4737
+ player.voiceState.serverDeaf = update.deaf ?? player.voiceState?.serverDeaf;
4738
+ player.voiceState.serverMute = update.mute ?? player.voiceState?.serverMute;
4739
+ player.voiceState.suppress = update.suppress ?? player.voiceState?.suppress;
4740
+ if (selfMuteChanged || serverMuteChanged) {
4741
+ this.emit("playerMuteChange", player, player.voiceState.selfMute, player.voiceState.serverMute);
4742
+ if (player.options.autoPauseOnMute === true) {
4743
+ if (player.voiceState.selfMute || player.voiceState.serverMute) await player.pause();
4744
+ else await player.resume();
4745
+ }
4746
+ }
4747
+ if (selfDeafChanged || serverDeafChanged) this.emit("playerDeafChange", player, player.voiceState.selfDeaf, player.voiceState.serverDeaf);
4748
+ if (suppressChange) this.emit("playerSuppressChange", player, player.voiceState.suppress);
4749
+ } else {
4750
+ const { autoReconnectOnlyWithTracks, destroyPlayer, autoReconnect } = this.options?.playerOptions?.onDisconnect ?? {};
4751
+ if (destroyPlayer === true) {
4752
+ return void await player.destroy("Disconnected" /* Disconnected */);
4753
+ }
4754
+ if (autoReconnect === true) {
4755
+ try {
4756
+ const previousPosition = player.position;
4757
+ const previousPaused = player.paused;
4758
+ this.dispatchDebug("PlayerAutoReconnect" /* PlayerAutoReconnect */, {
4759
+ state: "log",
4760
+ message: `Auto reconnecting player because RyanlinkManager.options.playerOptions.onDisconnect.autoReconnect is true`,
4761
+ functionLayer: "RyanlinkManager > provideVoiceUpdate()"
4762
+ });
4763
+ if (!autoReconnectOnlyWithTracks || autoReconnectOnlyWithTracks && (player.queue.current || player.queue.tracks.length)) {
4764
+ await player.connect();
4765
+ this.emit("playerReconnect", player, player.voiceChannelId);
4766
+ }
4767
+ if (player.queue.current) {
4768
+ return void await player.play({
4769
+ position: previousPosition,
4770
+ paused: previousPaused,
4771
+ clientTrack: player.queue.current
4772
+ });
4773
+ }
4774
+ if (player.queue.tracks.length) {
4775
+ return void await player.play({ paused: previousPaused });
4776
+ }
4777
+ this.dispatchDebug("PlayerAutoReconnect" /* PlayerAutoReconnect */, {
4778
+ state: "log",
4779
+ message: `Auto reconnected, but nothing to play`,
4780
+ functionLayer: "RyanlinkManager > provideVoiceUpdate()"
4781
+ });
4782
+ return;
4783
+ } catch (e) {
4784
+ console.error(e);
4785
+ return void await player.destroy("PlayerReconnectFail" /* PlayerReconnectFail */);
4786
+ }
4787
+ }
4788
+ this.emit("playerDisconnect", player, player.voiceChannelId);
4789
+ player.voiceChannelId = null;
4790
+ player.voice = Object.assign({});
4791
+ return;
4792
+ }
4793
+ }
4794
+ }
4795
+ };
4796
+
4797
+ // src/index.ts
4798
+ var version = "2.0.0";
4799
+ // Annotate the CommonJS export names for ESM import in node:
4800
+ 0 && (module.exports = {
4801
+ AudioNodeSymbol,
4802
+ AudioQueueSymbol,
4803
+ AudioTrackSymbol,
4804
+ Autoplay,
4805
+ BuiltinSources,
4806
+ DebugEvents,
4807
+ DefaultQueueStore,
4808
+ DestroyReasons,
4809
+ DisconnectReasons,
4810
+ EQList,
4811
+ FilterManager,
4812
+ LinkMatchers,
4813
+ MiniMap,
4814
+ NodeLinkExclusiveEvents,
4815
+ NodeLinkNode,
4816
+ NodeManager,
4817
+ Player,
4818
+ Queue,
4819
+ QueueSaver,
4820
+ RecommendationsStrings,
4821
+ ReconnectionState,
4822
+ RyanlinkManager,
4823
+ RyanlinkNode,
4824
+ RyanlinkUtils,
4825
+ SourceMappings,
4826
+ UnresolvedAudioTrackSymbol,
4827
+ audioOutputsData,
4828
+ parseConnectionUrl,
4829
+ queueTrackEnd,
4830
+ safeStringify,
4831
+ validSponsorBlocks,
4832
+ version
4833
+ });