Skip to main content

A modern async Lavalink client library for Python Discord music bots.

Project description

FluxWave

CI Python License: MIT Typed

FluxWave is a typed, async Lavalink v4 client for Python Discord music bots. It provides the Lavalink REST and websocket client, Discord voice integration, node pooling, queueing, filters, events, plugin helpers, autoplay, persistence, and production-oriented recovery tools.

FluxWave is currently 0.1.0a0 pre-release software. The core API is usable for testing and beta bots, but public APIs may still change before 1.0.

Features

  • Async Lavalink v4 REST and websocket client.
  • Voice protocol via FluxPlayer, compatible with discord.py, py-cord, nextcord, and disnake.
  • Multi-node NodePool and global Pool helpers.
  • Health-aware node selection, blacklist/cooldown support, player migration, automatic return to a recovered home node, hot-node draining, and multi-node search.
  • Raw incoming websocket event (websocket_raw) exposing every Lavalink payload for debugging and custom server plugins.
  • Typed models for tracks, playlists, load results, node info, stats, player state, Lavalink errors, plugin metadata, and user data.
  • Queue system with history, loop modes, waiters, shuffle, move, dedupe, serialization, total duration, and loaded/current tracking.
  • Lavalink filters with ergonomic components, presets, plugin filters, interpolation, reset/apply behavior, and set_filters(..., seek=True).
  • Search helpers for ytsearch:, ytmsearch:, direct URLs, playlists, empty results, load errors, default sources, and LFU search caching.
  • Public event payloads and Discord-style events such as on_fluxwave_track_start.
  • Plugin helpers for lyrics, LavaSrc-style payloads, SponsorBlock, custom REST routes, and custom event passthrough.
  • Autoplay/recommendation support with duplicate filtering and source-aware recommendation strategies.
  • Player persistence with an in-memory and a crash-safe on-disk (FileStore) backend, tracing, metrics, and a voice watchdog for stalled playback recovery.
  • In-flight request coalescing so a burst of identical searches hits Lavalink only once.
  • Library-agnostic display helpers: progress_bar, format_duration, and paginate_queue for building now-playing and queue UIs.
  • Strict typing with py.typed.

Documentation Map

Start here:

Guides:

References:

Requirements

  • Python 3.11 or newer.
  • One supported Discord library, which you install yourself: discord.py 2.4+, py-cord 2.4+, nextcord 2.6+, or disnake 2.9+. Like mafic and lavalink.py, FluxWave does not depend on a specific one — it detects whichever is installed.
  • aiohttp.
  • A Lavalink v4 server.

Installation

FluxWave needs two things: FluxWave itself, plus any one Discord library (discord.py, py-cord, nextcord, or disnake — your choice). FluxWave auto-detects whichever one is installed, so there is nothing to configure.

If you already have a Discord library installed, just install FluxWave:

python -m pip install fluxwave

If you are starting fresh, install FluxWave and a Discord library together (discord.py shown here — swap in py-cord, nextcord, or disnake if you prefer):

python -m pip install fluxwave discord.py

Or pull a Discord library in automatically with an extra (discordpy, pycord, nextcord, or disnake):

python -m pip install "fluxwave[discordpy]"

That is the whole setup. Once a Discord library is importable, import fluxwave just works.

Why isn't the Discord library bundled? FluxWave itself depends only on aiohttp. It deliberately does not install a Discord library for you, because discord.py and py-cord share the same discord import name — bundling one would overwrite and break users of the other. Bringing your own library (the same approach as mafic and lavalink.py) keeps every install clash-free.

If you happen to have more than one supported library installed at once, tell FluxWave which to use with the FLUXWAVE_DISCORD_LIBRARY environment variable (discord, nextcord, or disnake).

For local development:

git clone https://github.com/NobitaDeveloper/fluxwave.git
cd fluxwave
python -m pip install -e ".[dev,docs]"

Quick Start

Create a Lavalink node and use FluxPlayer as the Discord voice client:

import os

import discord
from discord.ext import commands

import fluxwave

bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())


@bot.event
async def setup_hook() -> None:
    node = fluxwave.Node(
        uri=os.getenv("LAVALINK_URI", "http://127.0.0.1:2333"),
        password=os.getenv("LAVALINK_PASSWORD", "youshallnotpass"),
        user_id=bot.user.id,
        identifier="main",
    )
    await fluxwave.Pool.connect(nodes=[node], cache_capacity=256)


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

    player = ctx.voice_client
    if not isinstance(player, fluxwave.FluxPlayer):
        player = await ctx.author.voice.channel.connect(cls=fluxwave.FluxPlayer)

    result = await player.enqueue(query)
    if result.added == 0:
        await ctx.reply("No tracks found.")
        return

    if player.current is None:
        await player.skip(force=True)

    await ctx.reply(result.message)


bot.run(os.environ["DISCORD_TOKEN"])

See examples/basic_bot.py for a fuller command set. See examples/advanced_bot.py for autoplay, filters, lyrics, persistence, metrics, and watchdog usage.

Core Concepts

Node

Node represents one Lavalink server. It owns REST, websocket, authentication, session resume, route planner helpers, known Lavalink players, live Discord players, and optional node-local search caching.

Read more: Nodes and Pools, API Reference: Node API.

node = fluxwave.Node(
    uri="http://127.0.0.1:2333",
    password="youshallnotpass",
    user_id=1234567890,
    identifier="main",
    search_cache_capacity=256,
)
await node.connect()

Pool

NodePool is instance-based. Pool is the global facade for bot code that prefers Wavelink-style helpers.

Read more: Nodes and Pools, API Reference: Node API.

await fluxwave.Pool.connect(nodes=[node], cache_capacity=512)
tracks = await fluxwave.Pool.search("never gonna give you up")

For multiple nodes:

results = await fluxwave.Pool.search_all("ytsearch:lofi")
degraded = fluxwave.Pool.get_degraded_nodes()
await fluxwave.Pool.drain(old_node, cooldown=300)

Player

FluxPlayer is a Discord voice protocol (compatible with discord.py, py-cord, nextcord, and disnake). It handles Discord voice updates, sends voice state to Lavalink, owns queues, controls playback, dispatches public events, and can persist/restore playback state.

Read more: Player Guide, API Reference: Player API.

player = await voice_channel.connect(cls=fluxwave.FluxPlayer)
await player.enqueue("ytsearch:lofi beats")
await player.skip(force=True)
await player.set_volume(80)
await player.set_filters(fluxwave.Filters().nightcore(), seek=True)

Queue

Read more: Queues and Filters, API Reference: Queue API.

player.queue.put(track)
player.queue.move(4, 0)
player.queue.dedupe()
next_ten = player.queue.clear_next(10)
duration_ms = player.queue.total_duration
next_track = player.queue.get(bypass_loop=True)
snapshot = player.queue.to_raw_data()

Events

FluxWave dispatches both FluxWave-prefixed and Wavelink-style Discord event names for easier migration:

Read more: Events, API Reference: Events.

@bot.event
async def on_fluxwave_track_start(event: fluxwave.TrackStartEvent) -> None:
    print(f"Started {event.track.title} in guild {event.guild_id}")


@fluxwave.listen("track_end")
async def on_track_end(event: fluxwave.TrackEndEvent) -> None:
    print(event.reason)

Persistence

Read more: Persistence and Observability.

store = fluxwave.MemoryStore()
state = player.save_state(extra={"source": "shutdown"})
await store.save(player.guild.id, state)

restored = await store.load(player.guild.id)
if restored is not None:
    await player.restore_state(restored)

Watchdog

Read more: Persistence and Observability.

watchdog = player.start_watchdog(
    fluxwave.WatchdogConfig(check_interval=5.0, stagnation_threshold=12.0)
)

Common Bot Commands

FluxWave is not a command framework, but it is designed to make common commands simple:

await player.enqueue("song name")
await player.play_next("song name")
await player.skip(force=True)
await player.stop(force=True)
await player.stop(clear_queue=False)  # stop current track but keep queue
await player.set_filters(fluxwave.Filters().bass_boost(), seek=True)
lyrics = await player.current_lyrics()
state = player.save_state()

Security Notes

  • Never hardcode Discord bot tokens in examples or committed code.
  • Read tokens/passwords from environment variables or a secret manager.
  • Treat Lavalink plugin endpoints as trusted-server APIs; validate user input before passing it to custom routes.

Lavalink Configuration

FluxWave expects a Lavalink v4 server reachable over HTTP or HTTPS.

Common environment variables used by examples and integration tests:

export DISCORD_TOKEN="your bot token"
export LAVALINK_URI="http://127.0.0.1:2333"
export LAVALINK_HOST="127.0.0.1"
export LAVALINK_PORT="2333"
export LAVALINK_PASSWORD="youshallnotpass"
export LAVALINK_SECURE="false"

Development

python -m pip install -e ".[dev,docs]"
.venv/bin/python -m ruff check .
.venv/bin/python -m ruff format --check .
.venv/bin/python -m mypy
.venv/bin/python -m pytest

Run integration tests against your own Lavalink server:

LAVALINK_HOST=127.0.0.1 \
LAVALINK_PORT=2333 \
LAVALINK_PASSWORD=youshallnotpass \
LAVALINK_SECURE=false \
.venv/bin/python -m pytest -m integration tests/test_integration_lavalink.py

Build docs locally:

.venv/bin/python -m sphinx -b html docs docs/_build/html

Build a wheel/sdist locally:

.venv/bin/python -m pip install build
.venv/bin/python -m build

Project Status

FluxWave is beta-quality and still pre-1.0. The local test suite covers the core library heavily, including mocked soak flows. Real Discord/Lavalink long-run soak testing is still expected before marking the project stable.

Support

Contributing

Contributions are welcome — see CONTRIBUTING.md for the dev setup and the lint/type/test checks. Security issues: see SECURITY.md.

License

FluxWave is distributed under the MIT License. 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

fluxwave-0.1.0.tar.gz (135.5 kB view details)

Uploaded Source

Built Distribution

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

fluxwave-0.1.0-py3-none-any.whl (85.9 kB view details)

Uploaded Python 3

File details

Details for the file fluxwave-0.1.0.tar.gz.

File metadata

  • Download URL: fluxwave-0.1.0.tar.gz
  • Upload date:
  • Size: 135.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fluxwave-0.1.0.tar.gz
Algorithm Hash digest
SHA256 af5e7bbe300325bdc55f93c8a3bd7af4b1d5583c873f18f5f489f7921763ded3
MD5 172ed883db2307d303b439ad29bfa13d
BLAKE2b-256 409225ba2ed4758fcded3d713f91c9d16ee1f301a68882265e1343bbd6502afa

See more details on using hashes here.

Provenance

The following attestation bundles were made for fluxwave-0.1.0.tar.gz:

Publisher: release.yml on NobitaDeveloper/fluxwave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fluxwave-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: fluxwave-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 85.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fluxwave-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0698b5731fa02f97861f4a15365cfab6dbdf8bd3825d07ca09aeb7c9895f5dc9
MD5 95c4984069f3afbc9ca4d471a4409525
BLAKE2b-256 d60780fecdcc54f2aa6e1fbda01f0816548cc7796971050a10057d3ff5cc2e6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for fluxwave-0.1.0-py3-none-any.whl:

Publisher: release.yml on NobitaDeveloper/fluxwave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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