Skip to main content

Plugin Development

Lavalink supports third-party plugins to add additional functionality such as custom audio sources, custom filters, WebSocket handling, REST endpoints, and much more.
If your plugin is developed in Kotlin, make sure you are using Kotlin v1.8.22

Getting Started

1

Create a new plugin from template

Use the Lavalink plugin template to bootstrap your plugin:
  1. Click “Use this template” on GitHub
  2. Clone your new repository
  3. Update the plugin metadata in build.gradle.kts
2

Add the plugin API dependency

The plugin API is already included in the template. It provides the interfaces and classes you need:
build.gradle.kts
dependencies {
    api("dev.arbjerg.lavalink:plugin-api:VERSION")
}
3

Implement your plugin

Create classes that implement the plugin API interfaces using Spring annotations. See the extension points below.
4

Test your plugin

Run your plugin against Lavalink using the Gradle task:
./gradlew :runLavalink
See the Gradle plugin documentation for more details.
5

Build and distribute

Build your plugin JAR and publish it to a Maven repository for distribution.

Extension Points

Adding Audio Sources

To add a new audio source (like YouTube, Spotify, etc.), simply provide an AudioSourceManager as a Spring bean:
import org.springframework.stereotype.Service;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager;

@Service
class MyAudioSourceManager implements AudioSourceManager {
    @Override
    public String getSourceName() {
        return "my-source";
    }
    
    @Override
    public AudioItem loadItem(AudioPlayerManager manager, AudioReference reference) {
        // Load and return audio items
    }
    
    // ... other required methods
}

Adding Media Container Probes

Add support for new media container formats for HTTP and local sources:
import org.springframework.stereotype.Service;
import com.sedmelluq.discord.lavaplayer.container.MediaContainerProbe;

@Service
class MyMediaContainerProbe implements MediaContainerProbe {
    @Override
    public String getName() {
        return "my-container";
    }
    
    @Override
    public boolean matchesHints(MediaContainerHints hints) {
        // Check if this probe should handle the content
    }
    
    // ... other required methods
}

Custom Audio Filters

Implement the AudioFilterExtension interface to add custom audio filters that can be controlled via the WebSocket filters operation:
import org.springframework.stereotype.Service
import dev.arbjerg.lavalink.api.AudioFilterExtension
import com.sedmelluq.discord.lavaplayer.filter.FloatPcmAudioFilter
import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat
import kotlinx.serialization.json.JsonElement

@Service
class CustomFilterExtension : AudioFilterExtension {
    override val name: String = "customFilter"
    
    override fun isEnabled(data: JsonElement): Boolean {
        // Check if the filter should be enabled based on client data
        return data.jsonObject["enabled"]?.jsonPrimitive?.boolean ?: false
    }
    
    override fun build(
        data: JsonElement,
        format: AudioDataFormat?,
        output: FloatPcmAudioFilter?
    ): FloatPcmAudioFilter? {
        // Create and return your custom filter
        return CustomFilter(format, output)
    }
}
Clients can then use your filter via the WebSocket:
{
  "op": "filters",
  "guildId": "...",
  "customFilter": {
    "enabled": true,
    "parameter": "value"
  }
}

REST Endpoint Interceptors

Intercept and modify existing REST endpoints using the RestInterceptor interface:
import org.springframework.stereotype.Service;
import dev.arbjerg.lavalink.api.RestInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Service
class MyRestInterceptor implements RestInterceptor {
    @Override
    public boolean preHandle(
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler
    ) {
        // Intercept before the request is handled
        // Return false to block the request
        return true;
    }
    
    @Override
    public void postHandle(
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler,
        ModelAndView modelAndView
    ) {
        // Intercept after the request is handled
    }
}

Custom REST Endpoints

Define custom REST endpoints using Spring Web MVC annotations:
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/v4/myplugin")
class MyPluginController {
    @GetMapping("/status")
    fun getStatus(): Map<String, Any> {
        return mapOf(
            "status" to "active",
            "version" to "1.0.0"
        )
    }
    
    @PostMapping("/action")
    fun performAction(@RequestBody data: Map<String, Any>): Response {
        // Handle custom action
        return Response("success")
    }
}
See the Spring Web MVC documentation for more details.

Modifying Track and Playlist Info

Add custom fields to track and playlist JSON responses using AudioPluginInfoModifier:
import org.springframework.stereotype.Service
import dev.arbjerg.lavalink.api.AudioPluginInfoModifier
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

@Service
class MyPluginInfoModifier : AudioPluginInfoModifier {
    override fun modifyAudioTrackPluginInfo(track: AudioTrack): JsonObject? {
        // Add custom fields to track JSON
        return buildJsonObject {
            put("customField", "customValue")
            put("trackMetadata", "additional info")
        }
    }
    
    override fun modifyAudioPlaylistPluginInfo(playlist: AudioPlaylist): JsonObject? {
        // Add custom fields to playlist JSON
        return buildJsonObject {
            put("playlistExtra", "extra data")
        }
    }
}

Configuration Properties

Define type-safe configuration properties using Spring Boot’s configuration system:
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component

@Component
@ConfigurationProperties(prefix = "plugins.myplugin")
data class MyPluginConfig(
    var enabled: Boolean = true,
    var apiKey: String = "",
    var maxRetries: Int = 3
)
Users can then configure your plugin in application.yml:
plugins:
  myplugin:
    enabled: true
    apiKey: "your-api-key"
    maxRetries: 5
See the Spring Boot type-safe configuration documentation for more details.

Building Your Plugin

The plugin template uses Gradle for building. The key tasks are:
# Build the plugin JAR
./gradlew build

# Run Lavalink with your plugin for testing
./gradlew :runLavalink

# Publish to Maven repository
./gradlew publish
The built JAR will be in build/libs/ and includes all necessary metadata.

Plugin Manifest

Your plugin must include metadata in build.gradle.kts:
group = "com.example"
version = "1.0.0"

lavalink {
    pluginName = "MyPlugin"
    pluginVersion = version
    serverVersion = "4.0.0"
    configurePublishing = true
}

Distributing Your Plugin

Publishing to Maven

The official plugin repository is hosted at https://maven.lavalink.dev. To publish there:
  1. Contact the Lavalink team via Discord for credentials
  2. Configure your Maven credentials in ~/.gradle/gradle.properties
  3. Run ./gradlew publish

Custom Repositories

Users can load plugins from custom Maven repositories by configuring application.yml:
lavalink:
  plugins:
    - dependency: "com.example:my-plugin:1.0.0"
      repository: "https://maven.example.com/releases"
      snapshot: false

Default Repository Configuration

Users can override the default repositories:
lavalink:
  defaultPluginRepository: "https://maven.example.com/releases"
  defaultPluginSnapshotRepository: "https://maven.example.com/snapshots"

Plugin Directory

By default, Lavalink downloads and loads plugins from the ./plugins directory. Users can customize this:
lavalink:
  pluginsDir: "./lavalink-plugins"

Best Practices

Always use the official plugin API rather than accessing internal Lavalink classes. If you need functionality not exposed by the API, open an issue or pull request to extend it.
Version your plugin using semantic versioning (MAJOR.MINOR.PATCH) to communicate breaking changes clearly to users.
Provide clear documentation for all configuration properties and their default values.
Catch and log exceptions appropriately. Don’t let plugin errors crash the Lavalink server.

Resources

Plugin API Javadoc

Complete API reference documentation

Plugin Template

Start with the official template

Gradle Plugin Docs

Learn about the Lavalink Gradle plugin

Discord Community

Get help from the community

Next Steps

Plugin Architecture

Learn about extension points and hooks in detail

Available Plugins

Explore existing plugins for inspiration

Build docs developers (and LLMs) love