A modern async Lavalink client library for Python Discord music bots.
Project description
FluxWave
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 0.1.0 (beta). It is usable in real bots today; being pre-1.0,
some public APIs may still change before 1.0.
Features
- Async Lavalink v4 REST + websocket client, fully typed (
py.typed). - Library-agnostic — works with discord.py, py-cord, nextcord, or disnake;
depends only on
aiohttp. - Multi-node pooling (
NodePool/Pool) with health-aware selection, blacklist/cooldown, automatic failover and return-to-home, hot-node draining, and parallel multi-node search. - Players — full playback controls, voice-state recovery, a tagged filter stack, and a voice watchdog for stalled-playback recovery.
- Queue — history, loop modes, shuffle/move/dedupe, capacity limits, and serialization.
- Filters — equalizer, karaoke, timescale, presets (nightcore, vaporwave,
bass boost), interpolation, and
set_filters(..., seek=True). - Search —
ytsearch:/ytmsearch:/URLs/playlists, default sources, LFU caching, and in-flight request coalescing. - Events — typed payloads with
fluxwave_*and Wavelink-style aliases, plus a raw-websocket event for debugging. - Plugins — LavaSrc, LavaLyrics, SponsorBlock, and custom REST routes.
- Autoplay — recommendation providers with duplicate filtering.
- Persistence & observability — in-memory and crash-safe on-disk state stores, metrics, and structured tracing.
Documentation
Full documentation is hosted at fluxwave.readthedocs.io
(source in docs/). Good starting points:
- Getting Started — your first bot, step by step.
- Basic Bot and Advanced Bot — runnable examples.
- API Reference · FAQ · Migration · Changelog
The docs/guide/ folder covers players, nodes & pools, search &
autoplay, queues & filters, events, plugins, persistence & observability, and
troubleshooting.
Requirements
- Python
3.11or newer. - One supported Discord library, which you install yourself:
discord.py2.4+,py-cord2.4+,nextcord2.6+, ordisnake2.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 samediscordimport 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 pre-1.0. On top of a heavy unit-test suite, it has
been validated end-to-end against live Discord and Lavalink — playback,
multi-node failover, every plugin integration, and an extended stress soak.
Public APIs may still change before 1.0.
Support
- Bugs and feature requests: GitHub Issues
- Before opening an issue, see Troubleshooting and the FAQ.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fluxwave-0.1.1.tar.gz.
File metadata
- Download URL: fluxwave-0.1.1.tar.gz
- Upload date:
- Size: 135.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a989e96659c923bed8c09d332b682a133f746b7676cecc06ec6b1f70c34a3731
|
|
| MD5 |
565f554298ebcb7afae724e8156aca44
|
|
| BLAKE2b-256 |
e6df2358c324949b4c819efd59ce88412df9e2094930a65135912593368e18d6
|
Provenance
The following attestation bundles were made for fluxwave-0.1.1.tar.gz:
Publisher:
release.yml on NobitaDeveloper/fluxwave
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fluxwave-0.1.1.tar.gz -
Subject digest:
a989e96659c923bed8c09d332b682a133f746b7676cecc06ec6b1f70c34a3731 - Sigstore transparency entry: 1670549298
- Sigstore integration time:
-
Permalink:
NobitaDeveloper/fluxwave@a41f162739a69dd27b55ca78d280d100b5d9bf15 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/NobitaDeveloper
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a41f162739a69dd27b55ca78d280d100b5d9bf15 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fluxwave-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fluxwave-0.1.1-py3-none-any.whl
- Upload date:
- Size: 85.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bcb7405bd8ac8273ac9ca4c0c9b3c7e9e768d9662370fd30b0c6fe5c4b7d937
|
|
| MD5 |
5caf28420e54cfe8cc70647759507a38
|
|
| BLAKE2b-256 |
2f3a7ef7fd13402f7b5064f283c730530a608e66cea1a56847f7f9a70d13f11c
|
Provenance
The following attestation bundles were made for fluxwave-0.1.1-py3-none-any.whl:
Publisher:
release.yml on NobitaDeveloper/fluxwave
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fluxwave-0.1.1-py3-none-any.whl -
Subject digest:
0bcb7405bd8ac8273ac9ca4c0c9b3c7e9e768d9662370fd30b0c6fe5c4b7d937 - Sigstore transparency entry: 1670549396
- Sigstore integration time:
-
Permalink:
NobitaDeveloper/fluxwave@a41f162739a69dd27b55ca78d280d100b5d9bf15 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/NobitaDeveloper
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a41f162739a69dd27b55ca78d280d100b5d9bf15 -
Trigger Event:
push
-
Statement type: