Red-Lavalink
Red-Lavalink is a client library for the Lavalink audio server, specifically designed for use with Red-DiscordBot but usable in other `discord.py` projects. It enables advanced audio playback features for Discord bots, allowing them to connect to Lavalink nodes for powerful music and audio streaming capabilities. The library is actively maintained, with regular updates to support new `discord.py` versions and Lavalink protocol changes. The current stable version is 0.11.1.
Warnings
- breaking Version 0.11.0 dropped support for `discord.py` 1.x. Users must upgrade their `discord.py` installation to version 2.0.0a+ or newer.
- breaking Version 0.11.0 introduced significant breaking changes to the internal API due to a switch to `discord.py`'s `VoiceProtocol`. This includes removal of `user_id`, `PlayerManager`, `enums.DiscordVoiceSocketResponses`, renaming of `lavalink.player_manager` module to `lavalink.player`, and removal of methods like `Player.manager`, `Node.player_manager`, `Node.get_voice_ws()`, and `Node.join_voice_channel()`.
- breaking The `secured` parameter in `Node.connect()` was removed in version 0.10.0. Secure WebSocket connections are now handled automatically based on the port and host settings.
- gotcha As of `red-lavalink` 0.11.1, the library sends the voice channel ID to the Lavalink server, which is required by newer Lavalink server versions to support Discord's DAVE protocol. Ensure your Lavalink server is also up-to-date to fully utilize this.
- gotcha The `ExceptionSeverity` enum value `FATAL` was renamed to `FAULT` in version 0.10.0 to align with Lavalink's API.
- gotcha Red-Lavalink requires a separate, running Lavalink Java server. For standalone projects, you must deploy and manage a Lavalink server instance. As of recent Red-DiscordBot updates (3.5.23/24), Lavalink servers typically require Java 17 or newer.
Install
-
pip install red-lavalink
Imports
- lavalink
import lavalink
Quickstart
import lavalink
import os
from discord.ext.commands import Bot, Context
import discord
# Replace with your bot token and Lavalink credentials
BOT_TOKEN = os.environ.get('DISCORD_BOT_TOKEN', 'YOUR_BOT_TOKEN')
LAVALINK_HOST = os.environ.get('LAVALINK_HOST', 'localhost')
LAVALINK_PORT = int(os.environ.get('LAVALINK_PORT', '2333'))
LAVALINK_PASSWORD = os.environ.get('LAVALINK_PASSWORD', 'youshallnotpass')
class MyBot(Bot):
def __init__(self):
super().__init__(command_prefix='!', intents=discord.Intents.default())
async def setup_hook(self):
# Initialize Lavalink AFTER the bot is ready
await lavalink.initialize(
self,
host=LAVALINK_HOST,
password=LAVALINK_PASSWORD,
port=LAVALINK_PORT
)
print(f"Lavalink initialized to {LAVALINK_HOST}:{LAVALINK_PORT}")
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')
async def on_voice_state_update(self, member, before, after):
# Handle disconnects if the bot is alone in a voice channel
if member == self.user and not after.channel:
# Bot disconnected from voice
player = lavalink.get_player(member.guild.id)
if player:
await player.disconnect()
async def on_lavalink_event(self, player, event, extra=None):
print(f"Lavalink Event: {event} for player in guild {player.guild.id}")
@commands.command()
async def join(self, ctx: Context, *, channel: discord.VoiceChannel = None):
"""Joins a voice channel."""
if not channel and not ctx.author.voice:
return await ctx.send("You are not in a voice channel nor specified one.")
channel = channel or ctx.author.voice.channel
player = await lavalink.connect(channel)
await ctx.send(f"Joined {channel.name}")
@commands.command()
async def play(self, ctx: Context, *, query: str):
"""Searches and plays a song."""
player = lavalink.get_player(ctx.guild.id)
if not player or not player.is_connected:
return await ctx.send("I am not connected to a voice channel.")
tracks = await player.search_yt(query)
if not tracks:
return await ctx.send("No tracks found.")
player.add(requester=ctx.author, track=tracks[0])
if not player.is_playing:
await player.play()
await ctx.send(f"Now playing: {tracks[0].title}")
else:
await ctx.send(f"Added to queue: {tracks[0].title}")
@commands.command()
async def stop(self, ctx: Context):
"""Stops playback and clears the queue."""
player = lavalink.get_player(ctx.guild.id)
if not player or not player.is_connected:
return await ctx.send("I am not connected to a voice channel.")
await player.stop()
player.queue.clear()
await ctx.send("Playback stopped and queue cleared.")
bot = MyBot()
bot.run(BOT_TOKEN)