Skip to main content

A modern, async, fully-typed Lavalink v4 client for Python Discord bots.

Project description

waterlink

A modern, fully-typed, async Lavalink v4 client for Python Discord bots.

waterlink wraps Lavalink's REST and websocket protocol behind a clean, Pythonic API: node pooling with load-aware routing, a rich queue engine, typed audio filters, autoplay, crossfade, state persistence across restarts, and helpers for popular Lavalink plugins — all with full type hints and zero required dependencies beyond aiohttp.

It auto-detects whichever Discord library you're already using — discord.py, py-cord, nextcord, or disnake — so you don't install anything extra for voice support.

PyPI Python License GitHub


Features

  • Lavalink v4 native — REST + websocket built against the current protocol, including session resuming.
  • Multi-node pooling with pluggable routing strategies (lowest load, round robin, region-aware).
  • Multi-library support — auto-detects discord.py, py-cord, nextcord, or disnake.
  • Rich queue engine — history, shuffle, dedupe, track/queue loop modes, priority insertion.
  • Typed audio filters — equalizer, timescale, karaoke, tremolo, vibrato, rotation, distortion, channel mix, low-pass, all validated.
  • Autoplay — keeps audio flowing with a pluggable "related track" strategy once the queue empties.
  • Clean metadata — turns noisy YouTube-style results like "Tere Liye | Arijit Singh | Viral | T-Series" by "T-Series" into title "Tere Liye" by artist "Arijit Singh", opt-in per client or per search call.
  • Crossfade — smooth client-side volume ramping across track transitions.
  • State persistence — snapshot and restore queues/players across bot restarts (JSON file backend included, or bring your own).
  • Plugin helpers — typed convenience wrappers for LavaSrc and SponsorBlock.
  • Observability — structured logging, an in-process metrics collector (with Prometheus text export), and a watchdog for stalled playback/stale nodes.
  • Fully typed — ships a py.typed marker; passes mypy --strict.

Installation

pip install waterlink[discordpy]
# or: waterlink[pycord] / waterlink[nextcord] / waterlink[disnake]

waterlink only requires aiohttp itself — the extras above just also install a supported Discord library if you don't already have one.

You'll also need a running Lavalink v4 server.

Quick start

import discord
from discord.ext import commands
import waterlink

intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
client: waterlink.WaterlinkClient | None = None

@bot.event
async def on_ready():
    global client
    client = waterlink.WaterlinkClient(bot=bot)
    await client.add_node(host="localhost", port=2333, password="youshallnotpass")
    print(f"waterlink ready, using {client.library_name}")

@bot.command()
async def play(ctx: commands.Context, *, query: str):
    if ctx.author.voice is None:
        return await ctx.send("Join a voice channel first.")

    player = await client.connect(ctx.guild.id, ctx.author.voice.channel.id)

    result = await client.search(query)
    if isinstance(result, waterlink.TrackResult):
        track = result.track
    elif isinstance(result, waterlink.SearchTracksResult) and result.tracks:
        track = result.tracks[0]
    else:
        return await ctx.send("No results found.")

    await player.enqueue(track.with_requester(ctx.author.id))
    await ctx.send(f"Queued **{track.title}**")

bot.run("YOUR_TOKEN")

See examples/ for a fuller bot with queue management, filters, autoplay, and persistence wired up.

Core concepts

Node pool

node = await client.add_node(
    name="main",
    host="lavalink.example.com",
    port=443,
    password="secret",
    secure=True,
    region="us-east",
)

Add as many nodes as you like; client.connect() picks the best one automatically (RoutingStrategy.LOWEST_LOAD by default).

Queue & playback

player = client.get_player(guild_id)
await player.enqueue(track)
await player.skip()
await player.pause()
await player.resume()
await player.seek(30_000)
player.set_loop_mode(waterlink.LoopMode.QUEUE)

Filters

chain = waterlink.FilterChain()
chain.set_timescale(waterlink.Timescale(speed=1.25, pitch=1.1))
chain.set_equalizer(waterlink.Equalizer.bass_boost())
await player.set_filters(chain)

Autoplay

autoplay = waterlink.AutoplayEngine(client.events)
autoplay.enable(guild_id)

Clean metadata

YouTube search results are often uploaded by a label, not the artist — so track.title and track.author can come back as "Tere Liye | Arijit Singh | Viral | T-Series" / "T-Series" instead of "Tere Liye" / "Arijit Singh". Enable automatic cleanup:

client = waterlink.WaterlinkClient(bot=bot, clean_metadata=True)
# or per call:
result = await client.search(query, clean=True)
track = result.tracks[0]
print(track.title)   # "Tere Liye"
print(track.author)  # "Arijit Singh"
print(track.extra["raw_title"])   # original, untouched, if you need it
print(track.extra["raw_author"])

You can also clean a single track directly, or add your own label names (useful for regional labels not already in the default list):

cleaned = waterlink.clean_track(track)

cleaner = waterlink.TitleCleaner(extra_label_names=("my regional label",))
cleaned = cleaner.clean_track(track)

Events

@client.events.on(waterlink.TrackStartEvent)
async def on_track_start(event: waterlink.TrackStartEvent):
    print(f"Now playing {event.track.title} in guild {event.player.guild_id}")

Persistence

backend = waterlink.JSONFileBackend("state/")
snapshot = waterlink.PlayerSnapshot.capture(player)
await backend.save(snapshot)

Documentation

Full API reference and guides live in docs/. Highlights:

Contributing

Contributions are welcome — see CONTRIBUTING.md.

git clone https://github.com/yup-console/waterlink
cd waterlink
pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

waterlink-1.0.3.tar.gz (56.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

waterlink-1.0.3-py3-none-any.whl (56.8 kB view details)

Uploaded Python 3

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page