Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
sonolink 1.2.0a0 documentation
Light Logo Dark Logo

Main

  • Home
  • Installation
  • Guides
    • Getting Started
    • Lavalink Setup
    • Working With Players
    • Filters And Playback State
    • Migrating to SonoLink
      • Moving From Wavelink
      • Moving From Mafic
      • Moving From Lavalink.py
      • Moving From Lavaplay.py
      • Moving From Pomice
  • API Reference
    • Core API
    • Models
    • Enums
    • Events
    • Exceptions
    • Advanced
      • Gateway API
      • REST API
      • Supplementary

Meta

  • Changelog
  • Version Guarantees

Content Width

Back to top
View this page

Moving From Lavalink.py¶

This guide is for migrating from Lavalink.py to SonoLink.

It walks through the main differences between SonoLink and Lavalink.py, so you can get a feel for what changes and what stays familiar.

If you are not migrating an existing Lavalink.py bot, start with Getting Started instead.

What stays familiar¶

  • Lavalink remains the backend.

  • SonoLink supports discord.py, py-cord, disnake, and nextcord — the same libraries Lavalink.py is most commonly paired with.

  • The main runtime objects are still a client, nodes, players, tracks, and filters.

What changes¶

Lavalink.py centres its API on a manually managed Client that owns a NodeManager and a PlayerManager, and exposes a lower-level DefaultPlayer with a basic list-based queue that requires manual management. SonoLink replaces these with a higher-level coordinator, a structured settings system, an automated queue, and automatic node selection and built-in autoplay/history.

Concept mapping¶

Lavalink.py

SonoLink

lavalink.Client

sonolink.Client

lavalink.Client.node_manager / NodeManager

Managed internally; access via sonolink.Client.get_node()

lavalink.Client.player_manager / PlayerManager

Not exposed; players are created via sonolink.Node.create_player() or voice_channel.connect

lavalink.DefaultPlayer

sonolink.Player

lavalink.BasePlayer

sonolink.Player (subclass if needed)

lavalink.AudioTrack

sonolink.models.Playable

lavalink.LoadResult

sonolink.models.SearchResult

lavalink.LoadType

Implicit in sonolink.models.SearchResult result type

lavalink.Filter

sonolink.models.Filters (and individual filter classes)

lavalink.listener

Discord client on_sonolink_* event handlers

Connection lifecycle¶

SonoLink’s coordinator is constructed with no manual voice payload forwarding need, because it registers its own internal listeners:

# SonoLink
import sonolink

sl_client = sonolink.Client(bot)

sl_client.create_node(
    uri="http://localhost:2333",
    password="youshallnotpass",
    id="main",
)

async def setup_hook() -> None:
    await sl_client.start()

Note

sonolink.Client.start() should be called in discord.Client.setup_hook() (discord.py), discord.on_connect() (py-cord), disnake.on_connect() (disnake), or nextcord.on_connect() (nextcord) — not in on_ready.

Node management¶

In Lavalink.py, nodes are registered via Client.add_node and accessed through the NodeManager. Region-based selection and penalty-based load balancing are handled internally by NodeManager, and you can target a specific node by name via NodeManager.find_ideal_node.

SonoLink does not expose a NodeManager at all — node selection for new players is automatic. If you need a specific node, retrieve it by ID:

node = sl_client.get_node(id="main")
player = node.create_player(...)

For most bots the automatic selection is sufficient and you will not need to call this directly.

Players and voice connection¶

Lavalink.py does not integrate with discord.VoiceProtocol. Instead, you call PlayerManager.create_player yourself and then manually send voice state updates:

# Lavalink.py
player = bot.lavalink.player_manager.create(
    guild_id=ctx.guild.id,
    endpoint=str(ctx.guild.region),
)
await ctx.author.voice.channel.guild.change_voice_state(
    channel=ctx.author.voice.channel,
)

In SonoLink, players are created through the standard Discord VoiceProtocol interface:

# SonoLink
player = await voice_channel.connect(cls=sonolink.Player)

If you need to pre-configure a player before connecting:

player = sonolink.Player(
    node=node,
    volume=100,
)

await voice_channel.connect(cls=player)

See Working With Players for the full player reference.

Searching and loading tracks¶

In Lavalink.py, searching is done through Client.get_tracks (or the equivalent on the node), which returns a LoadResult containing a LoadType and a list of AudioTrack objects:

# Lavalink.py
result = await bot.lavalink.get_tracks(query)

if not result or not result.tracks:
    return

if result.load_type == lavalink.LoadType.PLAYLIST:
    track = result.tracks[0]
else:
    track = result.tracks[0]

In SonoLink, searching returns a sonolink.models.SearchResult wrapper that makes the result type explicit without requiring you to branch on a LoadType enum:

# SonoLink
result = await sl_client.search_track(query)
if result.is_error() or result.is_empty() or result.result is None:
    return

data = result.result

if isinstance(data, list):
    track = data[0]
elif isinstance(data, sonolink.models.Playlist):
    track = data.tracks[0]
else:
    track = data

sonolink.models.SearchResult covers all possible outcomes — a single track, a playlist, a search result list, an empty result, or an error — so you no longer need to check LoadType manually.

Tracks¶

Lavalink.py uses lavalink.AudioTrack for individual tracks. The SonoLink equivalent is sonolink.models.Playable. SonoLink tracks additionally carry sonolink.models.Album and sonolink.models.Artist metadata when the source provides it.

Playback¶

sonolink.Player exposes the same playback controls as Lavalink.py’s DefaultPlayer. The main difference is the queue: rather than a plain list, queue is a sonolink.Queue object with history-backed navigation, autoplay integration, and additional configuration via AutoPlaySettings and HistorySettings. Tracks are added via put() rather than player.queue.append.

Pausing is split into explicit pause() and resume() methods rather than set_pause(True/False).

See Working With Players for the full playback reference.

Filters¶

Lavalink.py defines filter classes (Equalizer, Karaoke, Timescale, Tremolo, Vibrato, Rotation, LowPass, ChannelMix, Distortion) that all derive from lavalink.Filter, and applies them via DefaultPlayer.set_filter:

# Lavalink.py
karaoke = lavalink.Karaoke(level=0.5)
player.set_filter(karaoke)
await player.apply_filters()

SonoLink groups all filter configuration into a single sonolink.models.Filters object applied with sonolink.Player.set_filters(). The individual filter types are the same, just passed as constructor arguments to Filters. The seek parameter optionally restarts the current track position after applying:

# SonoLink
from sonolink.models import Filters, Karaoke

filters = Filters(karaoke=Karaoke(level=0.5))
await player.set_filters(filters, seek=True)

Note

Lavalink.py’s lavalink.Volume filter class has no equivalent in SonoLink. Volume is controlled directly on the player via sonolink.Player.volume rather than through the Filters system.

See Filters And Playback State for the full filter reference.

Events¶

Lavalink.py uses a @lavalink.listener decorator on cog methods, registered with Client.add_event_hooks. Event objects are passed directly to the handler:

# Lavalink.py
class MusicCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        bot.lavalink.add_event_hooks(self)

    @lavalink.listener(lavalink.TrackStartEvent)
    async def on_track_start(self, event: lavalink.TrackStartEvent):
        ...

    @lavalink.listener(lavalink.TrackEndEvent)
    async def on_track_end(self, event: lavalink.TrackEndEvent):
        ...

SonoLink dispatches events through the Discord client with the sonolink_ prefix, passing a typed payload object alongside the player. You register handlers as standard Discord event listeners:

# SonoLink
@bot.event
async def on_sonolink_track_start(player: sonolink.Player, payload):
    ...

@bot.event
async def on_sonolink_track_end(player: sonolink.Player, payload):
    ...

The event name mapping is:

Lavalink.py event class

SonoLink event

TrackStartEvent

on_sonolink_track_start(player, payload)

TrackEndEvent

on_sonolink_track_end(player, payload)

TrackExceptionEvent

on_sonolink_track_exception(player, payload)

TrackStuckEvent

on_sonolink_track_stuck(player, payload)

NodeConnectedEvent / NodeReadyEvent

on_sonolink_node_ready(payload)

NodeDisconnectedEvent

on_sonolink_node_close(node)

PlayerUpdateEvent

on_sonolink_player_update(player, payload)

WebSocketClosedEvent

on_sonolink_websocket_closed(player, payload)

QueueEndEvent

(handled internally; configure autoplay instead — see below)

NodeChangedEvent

(handled internally; SonoLink manages node failover automatically)

(no equivalent)

on_sonolink_player_disconnect(player, payload)

See Events for the full event reference and payload types.

Settings¶

SonoLink introduces a settings system with no Lavalink.py equivalent. Settings are dataclass-like objects passed at node or player creation time, giving you structured control over behaviour that Lavalink.py left to manual implementation in event handlers:

  • sonolink.models.InactivitySettings — controls how long a player waits before disconnecting when the channel is inactive, and what counts as inactive.

  • sonolink.models.CacheSettings — configures the node-level LFU search result cache.

  • sonolink.models.AutoPlaySettings — configures autoplay mode, search provider, discovery count, and seed limits.

  • sonolink.models.HistorySettings — enables or limits the track history that previous and autoplay depend on.

from sonolink.models.settings import AutoPlaySettings, CacheSettings, HistorySettings, InactivitySettings
from sonolink.gateway.enums import AutoPlayMode, InactivityMode

sl_client.create_node(
    uri="http://localhost:2333",
    password="youshallnotpass",
    inactivity_settings=InactivitySettings(
        timeout=300,
        mode=InactivityMode.ALL_BOTS,
    ),
    cache_settings=CacheSettings(
        enabled=True,
        max_items=1000,
    ),
)

Autoplay and track history¶

Lavalink.py has no autoplay or track history system — bots that want either must build them manually inside a QueueEndEvent or TrackEndEvent handler. SonoLink introduces both.

Autoplay is configured through sonolink.models.AutoPlaySettings at player creation time and toggled via sonolink.Player.autoplay, which accepts an sonolink.AutoPlayMode value:

  • sonolink.AutoPlayMode.DISABLED — the player stops when the queue empties.

  • sonolink.AutoPlayMode.PARTIAL — SonoLink manages queue progression autonomously but does not fill the queue with recommended tracks.

  • sonolink.AutoPlayMode.ENABLED — when the queue empties, SonoLink discovers related tracks and fills the queue automatically.

Track history is enabled via sonolink.models.HistorySettings and exposed through sonolink.Player.queue as a sonolink.History object. Autoplay uses history as its seed, so the two features work together.

Warning

Autoplay requires history to be enabled. If sonolink.models.HistorySettings is left at its default, autoplay will have no reference track to discover from.

Errors and exceptions¶

Lavalink.py organises its errors under a ClientError base, with AuthenticationError, InvalidTrack, LoadError, and RequestError as subclasses. SonoLink has a separate exception hierarchy:

Lavalink.py

SonoLink

AuthenticationError

sonolink.InvalidNodePassword

RequestError (HTTP failures)

sonolink.HTTPException

InvalidTrack

(validated via sonolink.models.SearchResult — check is_error() )

LoadError

(surface via result.is_error() on sonolink.models.SearchResult )

(no equivalent)

sonolink.QueueEmpty

(no equivalent)

sonolink.HistoryEmpty

(no equivalent)

sonolink.WebSocketError

(no equivalent)

sonolink.NodeURINotFound

See Exceptions for the full exception reference.

Useful references¶

  • Lavalink.py repository

  • Lavalink.py API reference

Next
Moving From Lavaplay.py
Previous
Moving From Mafic
Copyright © 2026, vmphase, Soheab, DA-344
Made with Sphinx and @pradyunsg's Furo
On this page
  • Moving From Lavalink.py
    • What stays familiar
    • What changes
    • Concept mapping
    • Connection lifecycle
    • Node management
    • Players and voice connection
    • Searching and loading tracks
    • Tracks
    • Playback
    • Filters
    • Events
    • Settings
    • Autoplay and track history
    • Errors and exceptions
    • Useful references