2424
2525from __future__ import annotations
2626
27- from typing import TYPE_CHECKING , Any , TypedDict
27+ from typing import TYPE_CHECKING , Any , Generic , TypedDict , TypeVar
28+
29+ from .types .filters import (
30+ ChannelMix as ChannelMixPayload ,
31+ Distortion as DistortionPayload ,
32+ Equalizer as EqualizerPayload ,
33+ FilterPayload ,
34+ Karaoke as KaraokePayload ,
35+ LowPass as LowPassPayload ,
36+ Rotation as RotationPayload ,
37+ Timescale as TimescalePayload ,
38+ Tremolo as TremoloPayload ,
39+ Vibrato as VibratoPayload ,
40+ )
2841
2942
3043if TYPE_CHECKING :
3144 from typing_extensions import Self , Unpack
3245
33- from .types .filters import (
34- ChannelMix as ChannelMixPayload ,
35- Distortion as DistortionPayload ,
36- Equalizer as EqualizerPayload ,
37- FilterPayload ,
38- Karaoke as KaraokePayload ,
39- LowPass as LowPassPayload ,
40- Rotation as RotationPayload ,
41- Timescale as TimescalePayload ,
42- Tremolo as TremoloPayload ,
43- Vibrato as VibratoPayload ,
44- )
46+
47+ FT = TypeVar ("FT" )
4548
4649
4750__all__ = (
@@ -108,6 +111,19 @@ class ChannelMixOptions(TypedDict):
108111 right_to_right : float | None
109112
110113
114+ class _BaseFilter (Generic [FT ]):
115+ _payload : FT
116+
117+ def __init__ (self , payload : FT ) -> None :
118+ self ._payload = payload
119+ self ._remove_none ()
120+
121+ def _remove_none (self ) -> None :
122+ # Lavalink doesn't allow nullable types in any filters, but they are still not required...
123+ # Passing None makes it easier for the user to remove a field...
124+ self ._payload = {k : v for k , v in self ._payload .items () if v is not None } # type: ignore
125+
126+
111127class Equalizer :
112128 """Equalizer Filter Class.
113129
@@ -182,14 +198,14 @@ def __repr__(self) -> str:
182198 return f"<Equalizer: { self ._payload } >"
183199
184200
185- class Karaoke :
201+ class Karaoke ( _BaseFilter [ KaraokePayload ]) :
186202 """Karaoke Filter class.
187203
188204 Uses equalization to eliminate part of a band, usually targeting vocals.
189205 """
190206
191207 def __init__ (self , payload : KaraokePayload ) -> None :
192- self . _payload = payload
208+ super (). __init__ ( payload = payload )
193209
194210 def set (self , ** options : Unpack [KaraokeOptions ]) -> Self :
195211 """Set the properties of the this filter.
@@ -214,6 +230,7 @@ def set(self, **options: Unpack[KaraokeOptions]) -> Self:
214230 "filterBand" : options .get ("filter_band" , self ._payload .get ("filterBand" )),
215231 "filterWidth" : options .get ("filter_width" , self ._payload .get ("filterWidth" )),
216232 }
233+ self ._remove_none ()
217234 return self
218235
219236 def reset (self ) -> Self :
@@ -236,14 +253,14 @@ def __repr__(self) -> str:
236253 return f"<Karaoke: { self ._payload } >"
237254
238255
239- class Timescale :
256+ class Timescale ( _BaseFilter [ TimescalePayload ]) :
240257 """Timescale Filter class.
241258
242259 Changes the speed, pitch, and rate.
243260 """
244261
245262 def __init__ (self , payload : TimescalePayload ) -> None :
246- self . _payload = payload
263+ super (). __init__ ( payload = payload )
247264
248265 def set (self , ** options : Unpack [TimescalePayload ]) -> Self :
249266 """Set the properties of the this filter.
@@ -261,6 +278,7 @@ def set(self, **options: Unpack[TimescalePayload]) -> Self:
261278 The rate.
262279 """
263280 self ._payload .update (options )
281+ self ._remove_none ()
264282 return self
265283
266284 def reset (self ) -> Self :
@@ -283,15 +301,15 @@ def __repr__(self) -> str:
283301 return f"<Timescale: { self ._payload } >"
284302
285303
286- class Tremolo :
304+ class Tremolo ( _BaseFilter [ TremoloPayload ]) :
287305 """The Tremolo Filter class.
288306
289307 Uses amplification to create a shuddering effect, where the volume quickly oscillates.
290308 Demo: https://en.wikipedia.org/wiki/File:Fuse_Electronics_Tremolo_MK-III_Quick_Demo.ogv
291309 """
292310
293311 def __init__ (self , payload : TremoloPayload ) -> None :
294- self . _payload = payload
312+ super (). __init__ ( payload = payload )
295313
296314 def set (self , ** options : Unpack [TremoloPayload ]) -> Self :
297315 """Set the properties of the this filter.
@@ -307,6 +325,7 @@ def set(self, **options: Unpack[TremoloPayload]) -> Self:
307325 The tremolo depth.
308326 """
309327 self ._payload .update (options )
328+ self ._remove_none ()
310329 return self
311330
312331 def reset (self ) -> Self :
@@ -329,14 +348,14 @@ def __repr__(self) -> str:
329348 return f"<Tremolo: { self ._payload } >"
330349
331350
332- class Vibrato :
351+ class Vibrato ( _BaseFilter [ VibratoPayload ]) :
333352 """The Vibrato Filter class.
334353
335354 Similar to tremolo. While tremolo oscillates the volume, vibrato oscillates the pitch.
336355 """
337356
338357 def __init__ (self , payload : VibratoPayload ) -> None :
339- self . _payload = payload
358+ super (). __init__ ( payload = payload )
340359
341360 def set (self , ** options : Unpack [VibratoPayload ]) -> Self :
342361 """Set the properties of the this filter.
@@ -352,6 +371,7 @@ def set(self, **options: Unpack[VibratoPayload]) -> Self:
352371 The vibrato depth.
353372 """
354373 self ._payload .update (options )
374+ self ._remove_none ()
355375 return self
356376
357377 def reset (self ) -> Self :
@@ -374,15 +394,15 @@ def __repr__(self) -> str:
374394 return f"<Vibrato: { self ._payload } >"
375395
376396
377- class Rotation :
397+ class Rotation ( _BaseFilter [ RotationPayload ]) :
378398 """The Rotation Filter class.
379399
380400 Rotates the sound around the stereo channels/user headphones (aka Audio Panning).
381401 It can produce an effect similar to https://youtu.be/QB9EB8mTKcc (without the reverb).
382402 """
383403
384404 def __init__ (self , payload : RotationPayload ) -> None :
385- self . _payload = payload
405+ super (). __init__ ( payload = payload )
386406
387407 def set (self , ** options : Unpack [RotationOptions ]) -> Self :
388408 """Set the properties of the this filter.
@@ -396,6 +416,7 @@ def set(self, **options: Unpack[RotationOptions]) -> Self:
396416 The frequency of the audio rotating around the listener in Hz. ``0.2`` is similar to the example video.
397417 """
398418 self ._payload : RotationPayload = {"rotationHz" : options .get ("rotation_hz" , self ._payload .get ("rotationHz" ))}
419+ self ._remove_none ()
399420 return self
400421
401422 def reset (self ) -> Self :
@@ -418,14 +439,14 @@ def __repr__(self) -> str:
418439 return f"<Rotation: { self ._payload } >"
419440
420441
421- class Distortion :
442+ class Distortion ( _BaseFilter [ DistortionPayload ]) :
422443 """The Distortion Filter class.
423444
424445 According to Lavalink "It can generate some pretty unique audio effects."
425446 """
426447
427448 def __init__ (self , payload : DistortionPayload ) -> None :
428- self . _payload = payload
449+ super (). __init__ ( payload = payload )
429450
430451 def set (self , ** options : Unpack [DistortionOptions ]) -> Self :
431452 """Set the properties of the this filter.
@@ -462,6 +483,7 @@ def set(self, **options: Unpack[DistortionOptions]) -> Self:
462483 "offset" : options .get ("offset" , self ._payload .get ("offset" )),
463484 "scale" : options .get ("scale" , self ._payload .get ("scale" )),
464485 }
486+ self ._remove_none ()
465487 return self
466488
467489 def reset (self ) -> Self :
@@ -484,7 +506,7 @@ def __repr__(self) -> str:
484506 return f"<Distortion: { self ._payload } >"
485507
486508
487- class ChannelMix :
509+ class ChannelMix ( _BaseFilter [ ChannelMixPayload ]) :
488510 """The ChannelMix Filter class.
489511
490512 Mixes both channels (left and right), with a configurable factor on how much each channel affects the other.
@@ -494,7 +516,7 @@ class ChannelMix:
494516 """
495517
496518 def __init__ (self , payload : ChannelMixPayload ) -> None :
497- self . _payload = payload
519+ super (). __init__ ( payload = payload )
498520
499521 def set (self , ** options : Unpack [ChannelMixOptions ]) -> Self :
500522 """Set the properties of the this filter.
@@ -519,6 +541,7 @@ def set(self, **options: Unpack[ChannelMixOptions]) -> Self:
519541 "rightToLeft" : options .get ("right_to_left" , self ._payload .get ("rightToLeft" )),
520542 "rightToRight" : options .get ("right_to_right" , self ._payload .get ("rightToRight" )),
521543 }
544+ self ._remove_none ()
522545 return self
523546
524547 def reset (self ) -> Self :
@@ -541,15 +564,15 @@ def __repr__(self) -> str:
541564 return f"<ChannelMix: { self ._payload } >"
542565
543566
544- class LowPass :
567+ class LowPass ( _BaseFilter [ LowPassPayload ]) :
545568 """The LowPass Filter class.
546569
547570 Higher frequencies get suppressed, while lower frequencies pass through this filter, thus the name low pass.
548571 Any smoothing values equal to or less than ``1.0`` will disable the filter.
549572 """
550573
551574 def __init__ (self , payload : LowPassPayload ) -> None :
552- self . _payload = payload
575+ super (). __init__ ( payload = payload )
553576
554577 def set (self , ** options : Unpack [LowPassPayload ]) -> Self :
555578 """Set the properties of the this filter.
@@ -563,6 +586,7 @@ def set(self, **options: Unpack[LowPassPayload]) -> Self:
563586 The smoothing factor.
564587 """
565588 self ._payload .update (options )
589+ self ._remove_none ()
566590 return self
567591
568592 def reset (self ) -> Self :
@@ -585,7 +609,7 @@ def __repr__(self) -> str:
585609 return f"<LowPass: { self ._payload } >"
586610
587611
588- class PluginFilters :
612+ class PluginFilters ( _BaseFilter [ dict [ str , Any ]]) :
589613 """The PluginFilters class.
590614
591615 This class handles setting filters on plugins that support setting filter values.
@@ -604,7 +628,7 @@ class PluginFilters:
604628 """
605629
606630 def __init__ (self , payload : dict [str , Any ]) -> None :
607- self . _payload = payload
631+ super (). __init__ ( payload = payload )
608632
609633 def set (self , ** options : dict [str , Any ]) -> Self :
610634 """Set the properties of this filter.
@@ -625,6 +649,7 @@ def set(self, **options: dict[str, Any]) -> Self:
625649 plugin_filters.set(**{"pluginName": {"filterKey": "filterValue", ...}})
626650 """
627651 self ._payload .update (options )
652+ self ._remove_none ()
628653 return self
629654
630655 def reset (self ) -> Self :
0 commit comments