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 Wavelink¶

This guide is for migrating from Wavelink to SonoLink.

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

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

What stays familiar¶

  • Lavalink remains the backend.

  • SonoLink supports discord.py, py-cord, disnake, and nextcord, each using a custom discord.VoiceProtocol for voice integration.

  • The main runtime objects are still a client-level coordinator, nodes, players, queues, tracks, playlists, and filters.

What changes¶

Most of the work comes down to replacing Wavelink’s top-level helper APIs with explicit SonoLink objects and adjusting for the return types used in Wavelink 3.x.

Concept mapping¶

Wavelink

SonoLink

wavelink.Pool

sonolink.Client

wavelink.Node

sonolink.Node

wavelink.Player

sonolink.Player

wavelink.Playable.search(...)

sonolink.Client.search_track()

wavelink.Pool.fetch_tracks(...)

sonolink.Node.search_track()

wavelink.Search

sonolink.models.SearchResult

wavelink.Filters

sonolink.models.Filters

Connection lifecycle¶

In Wavelink, node management is centered on the class-level pool API. The Wavelink docs show connecting with wavelink.Pool.connect(...) after building wavelink.Node(...) objects.

In SonoLink, the equivalent flow is explicit and instance-based:

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 once your Discord client is ready, typically in discord.Client.setup_hook() (discord.py), discord.on_connect() (py-cord), disnake.on_connect() (disnake) or nextcord.on_connect() (nextcord) rather than in the on_ready event.

Settings¶

SonoLink introduces a settings system with no direct Wavelink equivalent. Settings are dataclass-like objects passed at node or player creation time, giving you structured control over behavior that Wavelink left to ad-hoc configuration:

  • 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. Autoplay requires history to be enabled — see Autoplay for details.

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,
    ),
)

player = sl_client.get_best_node().create_player(
    autoplay_settings=AutoPlaySettings(
        mode=AutoPlayMode.ENABLED,
        discovery_count=10,
    ),
    history_settings=HistorySettings(
        enabled=True,
        max_items=100,
    ),
)

Searching¶

In Wavelink, the search flow is usually:

# Wavelink
tracks = await wavelink.Playable.search(query)
if not tracks:
    return

if isinstance(tracks, wavelink.Playlist):
    play_track = tracks.tracks[0]
    rest = tracks.tracks[1:]
else:
    play_track = tracks[0]
    rest = tracks[1:]

In SonoLink, searching returns a wrapper object that makes the result type explicit:

# 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
rest = []

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

sonolink.models.SearchResult covers all possible outcomes: a single track, a playlist, a search result list, an empty result, or an error result.

Players and voice connection¶

You still connect through Discord with:

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

If you need to pre-configure a player with volume, filters, queue mode, autoplay, or history settings before connecting, you can either instantiate sonolink.Player directly or use sonolink.Node.create_player() — both are equivalent:

from sonolink.models import Filters, Karaoke

player = sonolink.Player(
    node=node,
    volume=100,
    filters=Filters(
        karaoke=Karaoke(level=0.5),
    ),
)

# or

player = node.create_player(
    volume=100,
    filters=Filters(
        karaoke=Karaoke(level=0.5),
    ),
)

await voice_channel.connect(cls=player)

See Working With Players for the full player reference.

Playback flow¶

SonoLink does not expose a playing property — use sonolink.Player.current instead (see State helpers). The common pattern is to call sonolink.Player.play() directly when nothing is currently playing, and put remaining tracks into the queue. Queue progression after a track ends is handled automatically:

# play_track and rest come from the search result — see Searching above
if not vc.current:
    await vc.play(play_track)
else:
    rest = [play_track, *rest]

for track in rest:
    await vc.queue.put_wait(track)

When a track ends, SonoLink automatically calls sonolink.Player.skip() internally, which pulls the next track from the queue, falls back to autoplay if the queue is empty, and stops the player if neither applies. You do not need to drive this manually.

Filters¶

Wavelink uses a single wavelink.Filters object that is mutated in place and re-applied via player.set_filters:

# Wavelink
filters = player.filters
filters.karaoke.set(level=0.5)
await player.set_filters(filters)

SonoLink works similarly but constructs a fresh sonolink.models.Filters object each time, passing filter instances directly as constructor arguments. 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)

See Filters And Playback State for the full filter reference.

Events¶

Wavelink dispatches events through the Discord client with the wavelink_ prefix, while SonoLink works the same way, using the sonolink_ prefix instead:

Wavelink

SonoLink

on_wavelink_node_ready(payload)

on_sonolink_node_ready(payload)

on_wavelink_node_closed(node, disconnected)

on_sonolink_node_close(node)

on_wavelink_player_update(payload)

on_sonolink_player_update(player, payload)

on_wavelink_track_start(payload)

on_sonolink_track_start(player, payload)

on_wavelink_track_end(payload)

on_sonolink_track_end(player, payload)

on_wavelink_track_exception(payload)

on_sonolink_track_exception(player, payload)

on_wavelink_track_stuck(payload)

on_sonolink_track_stuck(player, payload)

on_wavelink_websocket_closed(payload)

on_sonolink_websocket_closed(player, payload)

on_wavelink_extra_event(payload)

on_sonolink_unknown_event(player, payload)

on_wavelink_stats_update(payload)

on_sonolink_stats_receive(node, payload)

on_wavelink_inactive_player(player)

Handled internally; configure via InactivitySettings

(no equivalent)

on_sonolink_player_disconnect(player, payload)

Autoplay¶

Wavelink exposes an auto_queue concept. SonoLink’s 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 — autoplay is completely disabled. The player stops when the queue empties.

  • sonolink.AutoPlayMode.PARTIAL — SonoLink manages 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. Tracks added to the standard queue are treated as priority.

The search provider used for discovery is configured via sonolink.SearchProvider in sonolink.models.AutoPlaySettings:

  • sonolink.SearchProvider.YOUTUBE — YouTube Radio mix based on the track identifier.

  • sonolink.SearchProvider.SPOTIFY — Spotify recommendations based on the track identifier.

  • sonolink.SearchProvider.DEEZER — Deezer track or artist radio based on the identifier.

Warning

Autoplay uses the track history as its seed. Ensure sonolink.models.HistorySettings has history enabled when creating the player, otherwise autoplay will have no reference track to discover from. See Settings for an example.

State helpers¶

Wavelink exposes player.playing and player.paused as the primary state checks. SonoLink does not have a playing property — replace any player.playing checks with player.current is not None:

# Wavelink
if player.playing:
    ...

# SonoLink
if player.current is not None:
    ...

The full public player state in SonoLink is:

  • sonolink.Player.current — the track currently playing, or None.

  • sonolink.Player.paused — whether the player is paused.

  • sonolink.Player.position — the current playback position in milliseconds.

  • sonolink.Player.volume — the current volume, between 0 and 1000.

  • sonolink.Player.queue — the sonolink.Queue holding upcoming and historical tracks.

  • sonolink.Player.autoplay — the current sonolink.AutoPlayMode for this player.

Useful references¶

  • Wavelink repository

  • Wavelink migrating guide

  • Wavelink API reference

Next
Moving From Mafic
Previous
Migrating to SonoLink
Copyright © 2026, vmphase, Soheab, DA-344
Made with Sphinx and @pradyunsg's Furo
On this page
  • Moving From Wavelink
    • What stays familiar
    • What changes
    • Concept mapping
    • Connection lifecycle
    • Settings
    • Searching
    • Players and voice connection
    • Playback flow
    • Filters
    • Events
    • Autoplay
    • State helpers
    • Useful references