Skip to content

Commit

Permalink
Add a filter builder to aid users in setting filters (#14)
Browse files Browse the repository at this point in the history
* Add a filter builder to aid users in setting filters

* Update documentation
  • Loading branch information
duncte123 authored Dec 20, 2023
1 parent c1d5321 commit 115ce11
Show file tree
Hide file tree
Showing 10 changed files with 603 additions and 11 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ Alternatively, you can use `Discord4JUtils.leave(gatewayClient, guildId);` as th
## Examples
The following examples are minimal implementations but show how the library works.
- Java examples
- JDA: [link](src/test/java/JavaJDAExample.java)
- JDA (simple): [link](src/test/java/JavaJDAExample.java)
- JDA (more complex example): [link](testbot/src/main/java/me/duncte123/testbot/Main.java)
- Kotlin examples
- JDA: [link](src/test/kotlin/testScript.kt)
- Discord4J: [link](src/test/kotlin/d4jTestScript.kt)
Expand Down
20 changes: 11 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,22 @@ group = "dev.arbjerg"
version = gitVersion
val archivesBaseName = "lavalink-client"

allprojects {
repositories {
mavenCentral()
maven("https://maven.lavalink.dev/releases")
maven("https://maven.lavalink.dev/snapshots")
maven("https://maven.topi.wtf/releases")
// Note to self: jitpack always comes last
maven("https://jitpack.io")
}
}

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

repositories {
mavenCentral()
maven("https://maven.lavalink.dev/releases")
maven("https://maven.lavalink.dev/snapshots")
maven("https://maven.topi.wtf/releases")
// Note to self: jitpack always comes last
maven("https://jitpack.io")
}

dependencies {
// package libraries
api(kotlin("stdlib"))
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ rootProject.name = "lavalink-client"

enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

include(":testbot")

dependencyResolutionManagement {
versionCatalogs {
create("libs") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ interface IUpdatablePlayer {
* Please use [setVolume] to update the player's volume instead. Setting the volume via filters is
* float based (1.0f is 100% volume) and takes the time of your buffer size to apply.
*
* @param filters The new filters to apply to the player.
* @param filters The new filters to apply to the player. You can use the [dev.arbjerg.lavalink.client.protocol.FilterBuilder] to easily create this object.
*
* @return The updated builder, useful for chaining
*/
Expand Down
214 changes: 214 additions & 0 deletions src/main/kotlin/dev/arbjerg/lavalink/client/protocol/FilterBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package dev.arbjerg.lavalink.client.protocol

import dev.arbjerg.lavalink.internal.toJsonElement
import dev.arbjerg.lavalink.protocol.v4.*
import kotlinx.serialization.json.JsonElement

/**
* Helper class fo builder [Filters].
*/
class FilterBuilder {
private var volume: Omissible<Float> = Omissible.Omitted()
private var equalizer: Omissible<List<Band>> = Omissible.Omitted()
private var karaoke: Omissible<Karaoke?> = Omissible.Omitted()
private var timescale: Omissible<Timescale?> = Omissible.Omitted()
private var tremolo: Omissible<Tremolo?> = Omissible.Omitted()
private var vibrato: Omissible<Vibrato?> = Omissible.Omitted()
private var distortion: Omissible<Distortion?> = Omissible.Omitted()
private var rotation: Omissible<Rotation?> = Omissible.Omitted()
private var channelMix: Omissible<ChannelMix?> = Omissible.Omitted()
private var lowPass: Omissible<LowPass?> = Omissible.Omitted()
private var pluginFilters: MutableMap<String, JsonElement> = mutableMapOf()

/**
* Sets the filter volume. If you just want to change the volume, it is highly recommended to use [dev.arbjerg.lavalink.client.IUpdatablePlayer.setVolume] instead.
*
* This volume takes the time of your buffer size to apply and should only be used if any other filters would increase the overall volume too much.
*
* @param volume The volume to apply to the filter.
*
* @return The updated builder, useful for chaining
*/
fun setVolume(volume: Float) = apply {
this.volume = volume.toOmissible()
}

/**
* Set the equalizer bands on the player.
*
* @param equalizer Each band to set in the equalizer. Default gain is 1.0f.
*
* @return The updated builder, useful for chaining
*/
fun setEqualizer(equalizer: List<Band>) = apply {
this.equalizer = equalizer.toOmissible()
}

/**
* Set a specific band on the equalizer.
*
* @param band The band to set.
* @param gain The gain to apply to the band. Default gain is 1.0f.
*
* @return The updated builder, useful for chaining
*/
@JvmOverloads
fun setEqualizerBand(band: Int, gain: Float = 1.0F) = apply {
val eq = this.equalizer
val bandObj = Band(band, gain)

if (eq.isPresent()) {
val currEq = eq.value.toMutableList()
val bandIndex = currEq.indexOfFirst { it.band == band }

if (bandIndex > -1) {
currEq[bandIndex] = bandObj
} else {
currEq.add(bandObj)
}

this.equalizer = currEq.toOmissible()
} else {
this.equalizer = listOf(bandObj).toOmissible()
}
}

/**
* Set the karaoke filter on the player.
*
* @param karaoke The karaoke filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setKaraoke(karaoke: Karaoke?) = apply {
this.karaoke = Omissible.of(karaoke)
}

/**
* Sets the timescale filter on the player.
*
* @param timescale The timescale filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setTimescale(timescale: Timescale?) = apply {
this.timescale = Omissible.of(timescale)
}

/**
* Sets the tremolo filter on the player.
*
* @param tremolo The tremolo filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setTremolo(tremolo: Tremolo?) = apply {
this.tremolo = Omissible.of(tremolo)
}

/**
* Sets the vibrato filter on the player.
*
* @param vibrato The vibrato filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setVibrato(vibrato: Vibrato?) = apply {
this.vibrato = Omissible.of(vibrato)
}

/**
* Sets the distortion filter on the player.
*
* @param distortion The distortion filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setDistortion(distortion: Distortion?) = apply {
this.distortion = Omissible.of(distortion)
}

/**
* Sets the rotation filter on the player.
*
* @param rotation The rotation filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setRotation(rotation: Rotation?) = apply {
this.rotation = Omissible.of(rotation)
}

/**
* Sets the channel mix filter on the player.
*
* @param channelMix The channel mix filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setChannelMix(channelMix: ChannelMix?) = apply {
this.channelMix = Omissible.of(channelMix)
}

/**
* Sets the low pass filter on the player.
*
* @param lowPass The low pass filter to apply to the player. Set to null to disable the filter.
*
* @return The updated builder, useful for chaining
*/
fun setLowPass(lowPass: LowPass?) = apply {
this.lowPass = Omissible.of(lowPass)
}

/**
* Set custom filter data for a plugin.
*
* @param name the name of the plugin filter
* @param filter the filter data, can be a custom class as it will be serialised to json
* @return The updated builder, useful for chaining
*/
fun setPluginFilter(name: String, filter: Any) = apply {
pluginFilters[name] = toJsonElement(filter)
}

/**
* Set custom filter data for a plugin.
*
* @param name the name of the plugin filter
* @param filter kotlin [JsonElement] that holds the filter data.
* @return The updated builder, useful for chaining
*/
fun setPluginFilter(name: String, filter: JsonElement) = apply {
pluginFilters[name] = filter
}

/**
* Removes a plugin filter with the given name.
*
* @param name the name of the plugin filter
* @return The updated builder, useful for chaining
*/
fun removePluginFilter(name: String) = apply {
pluginFilters.remove(name)
}

/**
* Builds the [Filters] object with the current configuration.
*
* @return the [Filters] object with the current configuration
*/
fun build() = Filters(
volume,
equalizer,
karaoke,
timescale,
tremolo,
vibrato,
distortion,
rotation,
channelMix,
lowPass,
pluginFilters
)
}
21 changes: 21 additions & 0 deletions testbot/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
java
application
}

group = "me.duncte123"
version = "1.0-SNAPSHOT"

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

dependencies {
// Include the lavalink client
implementation(projects.lavalinkClient)

// other libs such as a discord client and a logger
implementation(libs.jda)
implementation(libs.logger.impl)
}
77 changes: 77 additions & 0 deletions testbot/src/main/java/me/duncte123/testbot/AudioLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package me.duncte123.testbot;

import dev.arbjerg.lavalink.client.AbstractAudioLoadResultHandler;
import dev.arbjerg.lavalink.client.Link;
import dev.arbjerg.lavalink.client.protocol.*;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.jetbrains.annotations.NotNull;

import java.util.List;

public class AudioLoader extends AbstractAudioLoadResultHandler {
private final Link link;
private final SlashCommandInteractionEvent event;

public AudioLoader(Link link, SlashCommandInteractionEvent event) {
this.link = link;
this.event = event;
}

@Override
public void ontrackLoaded(@NotNull TrackLoaded result) {
final Track track = result.getTrack();

// Inner class at the end of this file
var userData = new MyUserData(event.getUser().getIdLong());

track.setUserData(userData);

link.createOrUpdatePlayer()
.setTrack(track)
.setVolume(35)
.subscribe((player) -> {
final Track playingTrack = player.getTrack();
final var trackTitle = playingTrack.getInfo().getTitle();
final MyUserData customData = playingTrack.getUserData(MyUserData.class);

event.getHook().sendMessage("Now playing: " + trackTitle + "\nRequested by: <@" + customData.requester() + '>').queue();
});
}

@Override
public void onPlaylistLoaded(@NotNull PlaylistLoaded result) {
final int trackCount = result.getTracks().size();
event.getHook()
.sendMessage("This playlist has " + trackCount + " tracks!")
.queue();
}

@Override
public void onSearchResultLoaded(@NotNull SearchResult result) {
final List<Track> tracks = result.getTracks();

if (tracks.isEmpty()) {
event.getHook().sendMessage("No tracks found!").queue();
return;
}

final Track firstTrack = tracks.get(0);

// This is a different way of updating the player! Choose your preference!
// This method will also create a player if there is not one in the server yet
link.updatePlayer((update) -> update.setTrack(firstTrack).setVolume(35))
.subscribe((ignored) -> {
event.getHook().sendMessage("Now playing: " + firstTrack.getInfo().getTitle()).queue();
});
}

@Override
public void noMatches() {
event.getHook().sendMessage("No matches found for your input!").queue();
}

@Override
public void loadFailed(@NotNull LoadFailed result) {
event.getHook().sendMessage("Failed to load track! " + result.getException().getMessage()).queue();
}
}
Loading

0 comments on commit 115ce11

Please sign in to comment.