magmastream 2.10.3-dev.2 → 2.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,12 +5,26 @@ const filtersEqualizers_1 = require("../utils/filtersEqualizers");
5
5
  const Enums_1 = require("./Enums");
6
6
  const MagmastreamError_1 = require("./MagmastreamError");
7
7
  class Filters {
8
+ static defaultPluginFilterPlugins = ["lavadspx-plugin", "LavaDSPX-Plugin"];
8
9
  distortion;
9
10
  equalizer;
10
11
  karaoke;
11
12
  rotation;
12
13
  timescale;
14
+ tremoloOptions;
13
15
  vibrato;
16
+ channelMix;
17
+ lowPass;
18
+ highpass;
19
+ echo;
20
+ chorus;
21
+ compressor;
22
+ flanger;
23
+ phaser;
24
+ phonograph;
25
+ spatial;
26
+ tape;
27
+ pluginFilters;
14
28
  reverb;
15
29
  volume;
16
30
  bassBoostlevel;
@@ -23,7 +37,20 @@ class Filters {
23
37
  this.karaoke = null;
24
38
  this.rotation = null;
25
39
  this.timescale = null;
40
+ this.tremoloOptions = null;
26
41
  this.vibrato = null;
42
+ this.channelMix = null;
43
+ this.lowPass = null;
44
+ this.highpass = null;
45
+ this.echo = null;
46
+ this.chorus = null;
47
+ this.compressor = null;
48
+ this.flanger = null;
49
+ this.phaser = null;
50
+ this.phonograph = null;
51
+ this.spatial = null;
52
+ this.tape = null;
53
+ this.pluginFilters = {};
27
54
  this.reverb = null;
28
55
  this.volume = 1.0;
29
56
  this.bassBoostlevel = 0;
@@ -48,19 +75,40 @@ class Filters {
48
75
  * of the Filters class for method chaining.
49
76
  */
50
77
  async updateFilters() {
51
- const { distortion, equalizer, karaoke, rotation, timescale, vibrato, volume } = this;
78
+ const { channelMix, chorus, compressor, distortion, echo, equalizer, flanger, highpass, karaoke, lowPass, phaser, phonograph, pluginFilters, reverb, rotation, spatial, tape, timescale, tremoloOptions, vibrato, volume, } = this;
79
+ const filters = {
80
+ distortion,
81
+ equalizer,
82
+ karaoke,
83
+ pluginFilters,
84
+ rotation,
85
+ timescale,
86
+ tremolo: tremoloOptions,
87
+ vibrato,
88
+ volume,
89
+ channelMix,
90
+ ...(this.player.node.isNodeLink
91
+ ? {
92
+ chorus,
93
+ compressor,
94
+ echo,
95
+ flanger,
96
+ lowpass: lowPass,
97
+ highpass,
98
+ phaser,
99
+ phonograph,
100
+ reverb,
101
+ spatial,
102
+ tape,
103
+ }
104
+ : {
105
+ lowPass,
106
+ }),
107
+ };
52
108
  try {
53
109
  await this.player.node.rest.updatePlayer({
54
110
  data: {
55
- filters: {
56
- distortion,
57
- equalizer,
58
- karaoke,
59
- rotation,
60
- timescale,
61
- vibrato,
62
- volume,
63
- },
111
+ filters,
64
112
  },
65
113
  guildId: this.player.guildId,
66
114
  });
@@ -78,6 +126,33 @@ class Filters {
78
126
  }
79
127
  return this;
80
128
  }
129
+ /** Synchronizes local filter state from a NodeLink FiltersChangedEvent without issuing another REST update. */
130
+ syncFromNode(payload) {
131
+ const filters = "filters" in payload ? payload.filters : payload;
132
+ const equalizer = filters.equalizer;
133
+ this.volume = typeof filters.volume === "number" ? filters.volume : 1.0;
134
+ this.equalizer = Array.isArray(equalizer) ? equalizer : (equalizer?.bands ?? []);
135
+ this.karaoke = filters.karaoke ?? null;
136
+ this.timescale = filters.timescale ?? null;
137
+ this.tremoloOptions = filters.tremolo ?? null;
138
+ this.vibrato = filters.vibrato ?? null;
139
+ this.distortion = filters.distortion ?? null;
140
+ this.rotation = filters.rotation ?? null;
141
+ this.channelMix = filters.channelMix ?? null;
142
+ this.lowPass = filters.lowpass ?? filters.lowPass ?? null;
143
+ this.highpass = filters.highpass ?? null;
144
+ this.echo = filters.echo ?? null;
145
+ this.reverb = filters.reverb ?? null;
146
+ this.chorus = filters.chorus ?? null;
147
+ this.compressor = filters.compressor ?? null;
148
+ this.flanger = filters.flanger ?? null;
149
+ this.phaser = filters.phaser ?? null;
150
+ this.phonograph = filters.phonograph ?? null;
151
+ this.spatial = filters.spatial ?? null;
152
+ this.tape = filters.tape ?? null;
153
+ this.pluginFilters = filters.pluginFilters ?? {};
154
+ return this;
155
+ }
81
156
  /**
82
157
  * Applies a specific filter to the player.
83
158
  *
@@ -101,6 +176,27 @@ class Filters {
101
176
  details: { action: "change" },
102
177
  });
103
178
  }
179
+ assertPluginFiltersSupported(pluginNames) {
180
+ const requiredPlugins = Array.isArray(pluginNames) ? pluginNames : [pluginNames];
181
+ const availablePlugins = this.player.node.info?.plugins ?? [];
182
+ const hasRequiredPlugin = requiredPlugins.some((name) => availablePlugins.some((plugin) => plugin.name.toLowerCase() === name.toLowerCase()));
183
+ if (!hasRequiredPlugin) {
184
+ throw new MagmastreamError_1.MagmaStreamError({
185
+ code: Enums_1.MagmaStreamErrorCode.NODE_PLUGIN_ERROR,
186
+ message: `One of the following plugins must be present in the lavalink node: ${requiredPlugins.map((name) => `"${name}"`).join(" or ")}.`,
187
+ context: { identifier: this.player.node.options.identifier },
188
+ });
189
+ }
190
+ }
191
+ assertNodeLinkExtraFilterSupported(filterName) {
192
+ if (this.player.node.isNodeLink)
193
+ return;
194
+ throw new MagmastreamError_1.MagmaStreamError({
195
+ code: Enums_1.MagmaStreamErrorCode.NODE_PROTOCOL_ERROR,
196
+ message: `The "${filterName}" filter is only supported on NodeLink nodes.`,
197
+ context: { identifier: this.player.node.options.identifier },
198
+ });
199
+ }
104
200
  /**
105
201
  * Sets the status of a specific filter.
106
202
  *
@@ -146,7 +242,21 @@ class Filters {
146
242
  await this.setKaraoke(null);
147
243
  await this.setRotation(null);
148
244
  await this.setTimescale(null);
245
+ await this.setTremolo(null);
149
246
  await this.setVibrato(null);
247
+ await this.setChannelMix(null);
248
+ await this.setLowPass(null);
249
+ await this.setHighPass(null);
250
+ await this.setEcho(null);
251
+ await this.setReverb(null);
252
+ await this.setChorus(null);
253
+ await this.setCompressor(null);
254
+ await this.setFlanger(null);
255
+ await this.setPhaser(null);
256
+ await this.setPhonograph(null);
257
+ await this.setSpatial(null);
258
+ await this.setTape(null);
259
+ await this.setPluginFilters({});
150
260
  await this.updateFilters();
151
261
  this.emitPlayersTasteUpdate(oldPlayer);
152
262
  return this;
@@ -215,6 +325,233 @@ class Filters {
215
325
  this.emitPlayersTasteUpdate(oldPlayer);
216
326
  return this;
217
327
  }
328
+ /**
329
+ * Sets the tremolo options on the audio.
330
+ *
331
+ * @param {TremoloOptions} [tremolo] - The tremolo settings to apply (frequency, depth).
332
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
333
+ */
334
+ async setTremolo(tremolo) {
335
+ const oldPlayer = { ...this };
336
+ await this.applyFilter({ property: "tremoloOptions", value: tremolo ?? null });
337
+ this.setFilterStatus(Enums_1.AvailableFilters.SetTremolo, !!tremolo);
338
+ this.emitPlayersTasteUpdate(oldPlayer);
339
+ return this;
340
+ }
341
+ /**
342
+ * Sets the stereo channel mix options on the audio.
343
+ *
344
+ * @param {ChannelMixOptions} [channelMix] - The channel mix settings to apply.
345
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
346
+ */
347
+ async setChannelMix(channelMix) {
348
+ const oldPlayer = { ...this };
349
+ await this.applyFilter({ property: "channelMix", value: channelMix ?? null });
350
+ this.setFilterStatus(Enums_1.AvailableFilters.SetChannelMix, !!channelMix);
351
+ this.emitPlayersTasteUpdate(oldPlayer);
352
+ return this;
353
+ }
354
+ /**
355
+ * Sets the low pass filter options on the audio.
356
+ *
357
+ * @param {LowPassOptions} [lowPass] - The low pass settings to apply.
358
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
359
+ */
360
+ async setLowPass(lowPass) {
361
+ const oldPlayer = { ...this };
362
+ await this.applyFilter({ property: "lowPass", value: lowPass ?? null });
363
+ this.setFilterStatus(Enums_1.AvailableFilters.SetLowPass, !!lowPass);
364
+ this.emitPlayersTasteUpdate(oldPlayer);
365
+ return this;
366
+ }
367
+ /**
368
+ * Sets the NodeLink high pass filter options on the audio.
369
+ * @param {HighPassOptions} [highpass] - The high pass settings to apply.
370
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
371
+ */
372
+ async setHighPass(highpass) {
373
+ const oldPlayer = { ...this };
374
+ if (highpass)
375
+ this.assertNodeLinkExtraFilterSupported("highpass");
376
+ await this.applyFilter({ property: "highpass", value: highpass ?? null });
377
+ this.setFilterStatus(Enums_1.AvailableFilters.SetHighPass, !!highpass);
378
+ this.emitPlayersTasteUpdate(oldPlayer);
379
+ return this;
380
+ }
381
+ /**
382
+ * Sets the NodeLink echo filter options on the audio.
383
+ * @param {EchoOptions} [echo] - The echo settings to apply.
384
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
385
+ */
386
+ async setEcho(echo) {
387
+ const oldPlayer = { ...this };
388
+ if (echo)
389
+ this.assertNodeLinkExtraFilterSupported("echo");
390
+ await this.applyFilter({ property: "echo", value: echo ?? null });
391
+ this.setFilterStatus(Enums_1.AvailableFilters.SetEcho, !!echo);
392
+ this.emitPlayersTasteUpdate(oldPlayer);
393
+ return this;
394
+ }
395
+ /**
396
+ * Sets the NodeLink reverb filter options on the audio.
397
+ * @param {ReverbOptions} [reverb] - The reverb settings to apply.
398
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
399
+ */
400
+ async setReverb(reverb) {
401
+ const oldPlayer = { ...this };
402
+ if (reverb)
403
+ this.assertNodeLinkExtraFilterSupported("reverb");
404
+ await this.applyFilter({ property: "reverb", value: reverb ?? null });
405
+ this.setFilterStatus(Enums_1.AvailableFilters.SetReverb, !!reverb);
406
+ this.emitPlayersTasteUpdate(oldPlayer);
407
+ return this;
408
+ }
409
+ /**
410
+ * Sets the NodeLink chorus filter options on the audio.
411
+ * @param {ChorusOptions} [chorus] - The chorus settings to apply.
412
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
413
+ */
414
+ async setChorus(chorus) {
415
+ const oldPlayer = { ...this };
416
+ if (chorus)
417
+ this.assertNodeLinkExtraFilterSupported("chorus");
418
+ await this.applyFilter({ property: "chorus", value: chorus ?? null });
419
+ this.setFilterStatus(Enums_1.AvailableFilters.SetChorus, !!chorus);
420
+ this.emitPlayersTasteUpdate(oldPlayer);
421
+ return this;
422
+ }
423
+ /**
424
+ * Sets the NodeLink compressor filter options on the audio.
425
+ * @param {CompressorOptions} [compressor] - The compressor settings to apply.
426
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
427
+ */
428
+ async setCompressor(compressor) {
429
+ const oldPlayer = { ...this };
430
+ if (compressor)
431
+ this.assertNodeLinkExtraFilterSupported("compressor");
432
+ await this.applyFilter({ property: "compressor", value: compressor ?? null });
433
+ this.setFilterStatus(Enums_1.AvailableFilters.SetCompressor, !!compressor);
434
+ this.emitPlayersTasteUpdate(oldPlayer);
435
+ return this;
436
+ }
437
+ /**
438
+ * Sets the NodeLink flanger filter options on the audio.
439
+ * @param {FlangerOptions} [flanger] - The flanger settings to apply.
440
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
441
+ */
442
+ async setFlanger(flanger) {
443
+ const oldPlayer = { ...this };
444
+ if (flanger)
445
+ this.assertNodeLinkExtraFilterSupported("flanger");
446
+ await this.applyFilter({ property: "flanger", value: flanger ?? null });
447
+ this.setFilterStatus(Enums_1.AvailableFilters.SetFlanger, !!flanger);
448
+ this.emitPlayersTasteUpdate(oldPlayer);
449
+ return this;
450
+ }
451
+ /**
452
+ * Sets the NodeLink phaser filter options on the audio.
453
+ * @param {PhaserOptions} [phaser] - The phaser settings to apply.
454
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
455
+ */
456
+ async setPhaser(phaser) {
457
+ const oldPlayer = { ...this };
458
+ if (phaser)
459
+ this.assertNodeLinkExtraFilterSupported("phaser");
460
+ await this.applyFilter({ property: "phaser", value: phaser ?? null });
461
+ this.setFilterStatus(Enums_1.AvailableFilters.SetPhaser, !!phaser);
462
+ this.emitPlayersTasteUpdate(oldPlayer);
463
+ return this;
464
+ }
465
+ /**
466
+ * Sets the NodeLink phonograph filter options on the audio.
467
+ * @param {PhonographOptions} [phonograph] - The phonograph settings to apply.
468
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
469
+ */
470
+ async setPhonograph(phonograph) {
471
+ const oldPlayer = { ...this };
472
+ if (phonograph)
473
+ this.assertNodeLinkExtraFilterSupported("phonograph");
474
+ await this.applyFilter({ property: "phonograph", value: phonograph ?? null });
475
+ this.setFilterStatus(Enums_1.AvailableFilters.SetPhonograph, !!phonograph);
476
+ this.emitPlayersTasteUpdate(oldPlayer);
477
+ return this;
478
+ }
479
+ /**
480
+ * Sets the NodeLink spatial filter options on the audio.
481
+ * @param {SpatialOptions} [spatial] - The spatial settings to apply.
482
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
483
+ */
484
+ async setSpatial(spatial) {
485
+ const oldPlayer = { ...this };
486
+ if (spatial)
487
+ this.assertNodeLinkExtraFilterSupported("spatial");
488
+ await this.applyFilter({ property: "spatial", value: spatial ?? null });
489
+ this.setFilterStatus(Enums_1.AvailableFilters.SetSpatial, !!spatial);
490
+ this.emitPlayersTasteUpdate(oldPlayer);
491
+ return this;
492
+ }
493
+ /**
494
+ * Sets the NodeLink tape filter options on the audio.
495
+ * @param {TapeOptions} [tape] - The tape settings to apply.
496
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
497
+ */
498
+ async setTape(tape) {
499
+ const oldPlayer = { ...this };
500
+ if (tape)
501
+ this.assertNodeLinkExtraFilterSupported("tape");
502
+ await this.applyFilter({ property: "tape", value: tape ?? null });
503
+ this.setFilterStatus(Enums_1.AvailableFilters.SetTape, !!tape);
504
+ this.emitPlayersTasteUpdate(oldPlayer);
505
+ return this;
506
+ }
507
+ async setNodeLinkHighPass(highpass) {
508
+ return this.setHighPass(highpass);
509
+ }
510
+ async setNodeLinkEcho(echo) {
511
+ return this.setEcho(echo);
512
+ }
513
+ async setNodeLinkReverb(reverb) {
514
+ return this.setReverb(reverb);
515
+ }
516
+ async setNodeLinkChorus(chorus) {
517
+ return this.setChorus(chorus);
518
+ }
519
+ async setNodeLinkCompressor(compressor) {
520
+ return this.setCompressor(compressor);
521
+ }
522
+ async setNodeLinkFlanger(flanger) {
523
+ return this.setFlanger(flanger);
524
+ }
525
+ async setNodeLinkPhaser(phaser) {
526
+ return this.setPhaser(phaser);
527
+ }
528
+ async setNodeLinkPhonograph(phonograph) {
529
+ return this.setPhonograph(phonograph);
530
+ }
531
+ async setNodeLinkSpatial(spatial) {
532
+ return this.setSpatial(spatial);
533
+ }
534
+ async setNodeLinkTape(tape) {
535
+ return this.setTape(tape);
536
+ }
537
+ /**
538
+ * Sets plugin-provided filter options on the audio.
539
+ *
540
+ * @param {Record<string, unknown>} [pluginFilters] - Plugin filter settings keyed by plugin filter name.
541
+ * @param {string | string[]} [requiredPluginNames] - The plugin name(s) that can handle the provided filters. Defaults to LavaDSPX.
542
+ * @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
543
+ */
544
+ async setPluginFilters(pluginFilters, requiredPluginNames = Filters.defaultPluginFilterPlugins) {
545
+ const oldPlayer = { ...this };
546
+ const nextPluginFilters = pluginFilters ?? {};
547
+ if (Object.keys(nextPluginFilters).length > 0) {
548
+ this.assertPluginFiltersSupported(requiredPluginNames);
549
+ }
550
+ await this.applyFilter({ property: "pluginFilters", value: nextPluginFilters });
551
+ this.setFilterStatus(Enums_1.AvailableFilters.PluginFilters, Object.keys(nextPluginFilters).length > 0);
552
+ this.emitPlayersTasteUpdate(oldPlayer);
553
+ return this;
554
+ }
218
555
  /**
219
556
  * Sets the own rotation options effect to the audio.
220
557
  *
@@ -557,7 +894,7 @@ class Filters {
557
894
  */
558
895
  async tremolo(status) {
559
896
  const oldPlayer = { ...this };
560
- await this.applyFilter({ property: "vibrato", value: status ? { frequency: 5, depth: 0.5 } : null });
897
+ await this.applyFilter({ property: "tremoloOptions", value: status ? { frequency: 5, depth: 0.5 } : null });
561
898
  this.setFilterStatus(Enums_1.AvailableFilters.Tremolo, status);
562
899
  this.emitPlayersTasteUpdate(oldPlayer);
563
900
  return this;
@@ -135,6 +135,15 @@ export declare class Manager extends EventEmitter {
135
135
  * @returns A promise that resolves when the player states have been loaded.
136
136
  */
137
137
  loadPlayerStates(nodeId: string): Promise<void>;
138
+ private normalizeSearchType;
139
+ private buildLoadTracksIdentifier;
140
+ private isNodeLinkSearchSource;
141
+ private isPrefixedLoadTracksIdentifier;
142
+ private hasNodeLinkSearchTypeModifier;
143
+ private requiresNodeLinkSearchNode;
144
+ private requiresLavalinkSearchNode;
145
+ private getSearchNode;
146
+ private getPreferredNode;
138
147
  /**
139
148
  * Returns the node to use based on the configured `useNode` and `enablePriorityMode` options.
140
149
  * If `enablePriorityMode` is true, the node is chosen based on priority, otherwise it is chosen based on the `useNode` option.