Metadata-Version: 2.4
Name: pyauralis
Version: 0.2.2
Summary: Official async Python SDK for the Auralis music platform
Author: Auralis
License: MIT
Keywords: audio,auralis,bot,discord,music
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.10
Requires-Dist: aiohttp>=3.9
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: speedups
Requires-Dist: orjson>=3.9; extra == 'speedups'
Description-Content-Type: text/markdown

# auralis

Official **async Python SDK** for the Auralis music platform.
One dependency (`aiohttp`), a tiny command surface, and the whole queue/skip/loop/shuffle
brain handled for you.

```bash
pip install pyauralis
```

## Quickstart

```python
import asyncio
import json
import os
from urllib.request import urlopen
from auralis import Client, LoopMode

def load_auralis_config():
    config_url = os.environ["BOT_CONFIG_URL"].rstrip("/") + "/internal/auralis-config"
    with urlopen(config_url) as res:
        return json.load(res)

config = load_auralis_config()
client = Client(config["apiBaseUrl"], config["token"])

@client.event
async def on_track_start(player, data):
    print("now playing:", player.current.title)

@client.event
async def on_queue_end(player):
    await player.disconnect()

async def main():
    await client.start()
    player = client.get_player(guild_id=123456789)

    # --- wire your Discord library's voice events to the player (see below) ---

    await player.play("ytsearch:never gonna give you up")
    await player.add("https://youtu.be/dQw4w9WgXcQ")
    player.set_loop(LoopMode.QUEUE)
    await asyncio.Event().wait()

asyncio.run(main())
```

## Player commands

| Command | Does |
|---|---|
| `await player.play(query)` | resolve + enqueue, start if idle |
| `await player.add(query)` | enqueue only |
| `await player.skip()` | advance to next track |
| `await player.stop()` | stop + clear queue (stays connected) |
| `await player.pause()` / `resume()` | pause / resume |
| `await player.seek(ms)` | seek |
| `await player.set_volume(0..1000)` | volume (100 = unity) |
| `player.set_loop(LoopMode.OFF/TRACK/QUEUE)` | loop mode |
| `player.shuffle()` | shuffle the queue |
| `player.now_playing()` | current `Track` |
| `await player.set_filters("bass=g=8")` | raw ffmpeg `-af` filtergraph |
| `await player.disconnect()` | tear down the player |

`query` is a search string (`"ytsearch:…"`, a URL, a playlist URL) or a `Track` you
already loaded via `client.search(...)` / `client.load_tracks(...)`.

## Events

`@client.event` on an `async def on_<name>(...)`, or `client.add_listener("<name>", cb)`:

| Event | Args |
|---|---|
| `on_ready()` | — |
| `on_track_start(player, data)` | track began |
| `on_track_end(player, data)` | track ended (`data["reason"]`) |
| `on_track_exception(player, data)` | playback error |
| `on_track_stuck(player, data)` | stalled |
| `on_player_update(player, data)` | live state (`player.state`) |
| `on_voice_closed(player, data)` | Discord voice closed |
| `on_queue_end(player)` | nothing left to play |
| `on_disconnect()` | websocket dropped (auto-reconnects) |

## Wiring Discord voice

The SDK never touches the Discord gateway — your bot owns that. Join a channel with
your library, then forward the two raw voice payloads:

```python
# discord.py raw gateway events
@bot.event
async def on_socket_raw_receive(...):
    ...  # or use a gateway hook

# when you get VOICE_SERVER_UPDATE for a guild:
client.get_player(guild_id).update_voice_server({"token": ..., "endpoint": ...})

# when you get VOICE_STATE_UPDATE for the bot:
client.get_player(guild_id).update_voice_state(
    {"session_id": ..., "channel_id": ..., "user_id": bot_user_id}
)
```

Once both arrive, the player connects and starts anything already queued.

## Notes

- The queue lives in this process (lost on bot restart).
- Node internals (node ids, endpoints, regions) are never exposed by this SDK.
- `pip install "pyauralis[speedups]"` pulls in `orjson` for faster JSON.
