Metadata-Version: 2.4
Name: fluxwave
Version: 0.1.1
Summary: A modern async Lavalink client library for Python Discord music bots.
Project-URL: Homepage, https://github.com/NobitaDeveloper/fluxwave
Project-URL: Repository, https://github.com/NobitaDeveloper/fluxwave
Project-URL: Documentation, https://fluxwave.readthedocs.io
Project-URL: Issues, https://github.com/NobitaDeveloper/fluxwave/issues
Project-URL: Changelog, https://github.com/NobitaDeveloper/fluxwave/blob/main/CHANGELOG.md
Author: FluxWave Contributors
License-Expression: MIT
License-File: LICENSE
Keywords: asyncio,bot,discord,lavalink,music
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: aiohttp<4,>=3.9
Provides-Extra: dev
Requires-Dist: discord-py<3,>=2.4; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Provides-Extra: discordpy
Requires-Dist: discord-py<3,>=2.4; extra == 'discordpy'
Provides-Extra: disnake
Requires-Dist: disnake<3,>=2.9; extra == 'disnake'
Provides-Extra: docs
Requires-Dist: furo>=2024.8.6; extra == 'docs'
Requires-Dist: myst-parser>=4; extra == 'docs'
Requires-Dist: sphinx>=8; extra == 'docs'
Provides-Extra: nextcord
Requires-Dist: nextcord<3,>=2.6; extra == 'nextcord'
Provides-Extra: pycord
Requires-Dist: py-cord<3,>=2.4; extra == 'pycord'
Description-Content-Type: text/markdown

# FluxWave

[![CI](https://github.com/NobitaDeveloper/fluxwave/actions/workflows/ci.yml/badge.svg)](https://github.com/NobitaDeveloper/fluxwave/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Docs](https://readthedocs.org/projects/fluxwave/badge/?version=latest)](https://fluxwave.readthedocs.io/en/latest/)
[![Typed](https://img.shields.io/badge/typing-strict-informational)](https://peps.python.org/pep-0561/)

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](https://fluxwave.readthedocs.io)**
(source in [`docs/`](docs/)). Good starting points:

- [Getting Started](https://fluxwave.readthedocs.io/en/latest/getting-started.html) — your first bot, step by step.
- [Basic Bot](examples/basic_bot.py) and [Advanced Bot](examples/advanced_bot.py) — runnable examples.
- [API Reference](https://fluxwave.readthedocs.io/en/latest/api/index.html) · [FAQ](https://fluxwave.readthedocs.io/en/latest/guide/faq.html) · [Migration](https://fluxwave.readthedocs.io/en/latest/guide/migration.html) · [Changelog](CHANGELOG.md)

The [`docs/guide/`](https://fluxwave.readthedocs.io/en/latest/) folder covers players, nodes & pools, search &
autoplay, queues & filters, events, plugins, persistence & observability, and
troubleshooting.

## 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:

```bash
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):

```bash
python -m pip install fluxwave discord.py
```

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

```bash
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:

```bash
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:

```python
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`](examples/basic_bot.py) for a fuller command set.
See [`examples/advanced_bot.py`](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](https://fluxwave.readthedocs.io/en/latest/guide/nodes.html), [API Reference: Node API](https://fluxwave.readthedocs.io/en/latest/api/index.html#node-api).

```python
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](https://fluxwave.readthedocs.io/en/latest/guide/nodes.html), [API Reference: Node API](https://fluxwave.readthedocs.io/en/latest/api/index.html#node-api).

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

For multiple nodes:

```python
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](https://fluxwave.readthedocs.io/en/latest/guide/player.html), [API Reference: Player API](https://fluxwave.readthedocs.io/en/latest/api/index.html#player-api).

```python
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](https://fluxwave.readthedocs.io/en/latest/guide/queue-filters.html), [API Reference: Queue API](https://fluxwave.readthedocs.io/en/latest/api/index.html#queue-api).

```python
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](https://fluxwave.readthedocs.io/en/latest/guide/events.html), [API Reference: Events](https://fluxwave.readthedocs.io/en/latest/api/index.html#events).

```python
@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](https://fluxwave.readthedocs.io/en/latest/guide/persistence-observability.html).

```python
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](https://fluxwave.readthedocs.io/en/latest/guide/persistence-observability.html#voice-watchdog).

```python
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:

```python
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:

```bash
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

```bash
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:

```bash
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:

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

Build a wheel/sdist locally:

```bash
.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](https://github.com/NobitaDeveloper/fluxwave/issues)
- Before opening an issue, see [Troubleshooting](https://fluxwave.readthedocs.io/en/latest/guide/troubleshooting.html) and the [FAQ](https://fluxwave.readthedocs.io/en/latest/guide/faq.html).

## Contributing

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

## License

FluxWave is distributed under the MIT License. See [`LICENSE`](LICENSE).
