Migrating#
Version 3 of wavelink has brought about many changes. This short guide should help you get started when moving from version 2.
Some things may be missing from this page. If you see anything wrong or missing please make an issue on GitHub.
Key Notes#
Version 3 is now fully typed and compliant with pyright strict.
Version 3 only works on Lavalink v4+.
Version 3 now uses black and isort formatting, for consistency.
Version 3 now better uses positional-only and keyword-only arguments in many places.
Version 3 has better error handling with requests to your Lavalink nodes.
Version 3 has Lavalink websocket completeness. All events have been implemented, with corresponding payloads.
Version 3 has an experimental LFU request cache, saving you requests to YouTube and Spotify etc.
Removed#
- Spotify Extension
Wavelink no longer has it’s own Spotify Extension. Instead it has native support for LavaSrc and other source plugins.
Using LavaSrc with Wavelink 3 is just as simple as using the built-in Lavalink search types.
- Removed all Track Types E.g. (YouTubeTrack, SoundCloudTrack)
Wavelink 3 uses one class for all tracks.
wavelink.Playable
.wavelink.Playable
is a much easier and simple to use track that provides a powerful interface.
wavelink.TrackSource
removedLocal
andUnknown
Changed#
All events have unique payloads.
Playlists can not be used to search.
wavelink.Playable
was changed significantly. Please see the docs for more info.wavelink.Playable.search()
was changed significantly. Please see the docs for more info.Node.id
is nowNode.identifier
.wavelink.NodePool
is nowwavelink.Pool
.wavelink.Pool.connect()
no longer requires theclient
keyword argument.wavelink.Pool.get_node()
theid
parameter is now known asidentifier
and is positional-only. This parameter is also optional.wavelink.Pool.fetch_tracks()
was previously known as both.get_tracks
and.get_playlist
. This method now returns either the appropriatewavelink.Playlist
or list[wavelink.Playable
]. If there is an error when searching, this method raises either aLavalinkException
(The request failed somehow) orLavalinkLoadException
there was an error loading the search (Request didn’t fail).wavelink.Queue.put_wait()
now has an option to atomically add tracks from awavelink.Playlist
or list[wavelink.Playable
]. This defaults to True. This currently checks if the track in the Playlist is Playable and if any errors occur will not add any tracks from the Playlist to the queue. IF set toFalse
, Playable tracks will be added to the Queue up until an error occurs or every track was successfully added.wavelink.Queue.put_wait()
andwavelink.Queue.put()
now return an int of the amount of tracks added.wavelink.Player.stop()
is now known aswavelink.Player.skip()
, though they both exist as aliases.Player.current_node
is now known aswavelink.Player.node
.Player.is_connected()
is now known aswavelink.Player.connected
.Player.is_paused()
is now known aswavelink.Player.paused
.Player.is_playing()
is now known aswavelink.Player.playing
.wavelink.Player.connect()
now accepts a timeout argument as a float in seconds.wavelink.Player.play()
has had additional arguments added. See the docs.Player.resume()
logic was moved towavelink.Player.pause()
.wavelink.Player.seek()
theposition
parameter is now positional-only, and has a default of0
which restarts the track from the beginning.wavelink.Player.set_volume()
thevalue
parameter is now positional-only, and has a default of100
.wavelink.Player.autoplay
accepts awavelink.AutoPlayMode
instead of a bool. AutoPlay has been changed to be more effecient and better with recomendations.wavelink.Queue
accepts awavelink.QueueMode
inwavelink.Queue.mode
for looping.Filters have been completely reworked. See:
wavelink.Filters
Player.set_filter
is now known aswavelink.Player.set_filters()
Player.filter
is now known aswavelink.Player.filters
Added#
wavelink.Node.client
property was added. This is the Bot/Client associated with the node.wavelink.Node.password
property was added. This is the password used to connect and make requests with this node.wavelink.Node.heartbeat
property was added. This is the seconds as a float that aiohttp will send a heartbeat over websocket.wavelink.Node.session_id
property was added. This is the Lavalink session ID associated with this node.LFU (Least Frequently Used) Cache for request caching.
Connecting#
Connecting in version 3 is similar to version 2.
Important
It is recommended to use discord.py setup_hook
to connect your nodes.
async def setup_hook(self) -> None:
nodes = [wavelink.Node(uri="...", password="...")]
# cache_capacity is EXPERIMENTAL. Turn it off by passing None
await wavelink.Pool.connect(nodes=nodes, client=self, cache_capacity=100)
When your node connects you will recieve the wavelink.NodeReadyEventPayload
via wavelink.on_wavelink_node_ready()
.
Searching and Playing#
Searching and playing tracks in version 3 is different, though should feel quite similar but easier.
# Search for tracks, with the default "ytsearch:" prefix.
tracks: wavelink.Search = await wavelink.Playable.search("Ocean Drive")
if not tracks:
# No tracks were found...
...
# Search for tracks, with a URL.
tracks: wavelink.Search = await wavelink.Playable.search("https://www.youtube.com/watch?v=KDxJlW6cxRk")
# Search for tracks, using Spotify and the LavaSrc Plugin.
tracks: wavelink.Search = await wavelink.Playable.search("4b93D55xv3YCH5mT4p6HPn", source="spsearch")
# Search for tracks, using Spotify and the LavaSrc Plugin, with a URL.
# Notice we don't need to pass a source argument with URL based searches...
tracks: wavelink.Search = await wavelink.Playable.search("https://open.spotify.com/track/4b93D55xv3YCH5mT4p6HPn")
# Search for a playlist, using Spotify and the LavaSrc Plugin.
# or alternatively any other playlist URL from another source like YouTube.
tracks: wavelink.Search = await wavelink.Playable.search("https://open.spotify.com/playlist/37i9dQZF1DWXRqgorJj26U")
wavelink.Search
should be used to annotate your variables.
.search always returns a list[wavelink.Playable
] or wavelink.Playlist
, if no tracks were found
this method will return an empty list
which should be checked, E.g:
tracks: wavelink.Search = await wavelink.Playable.search(query)
if not tracks:
# No tracks were found...
return
if isinstance(tracks, wavelink.Playlist):
# tracks is a playlist...
added: int = await player.queue.put_wait(tracks)
await ctx.send(f"Added the playlist **`{tracks.name}`** ({added} songs) to the queue.")
else:
track: wavelink.Playable = tracks[0]
await player.queue.put_wait(track)
await ctx.send(f"Added **`{track}`** to the queue.")
when playing a song from a command it is advised to check whether the Player is currently playing anything first, with
wavelink.Player.playing
if not player.playing:
await player.play(track)
You can skip adding any track to your history queue in version 3 by passing add_history=False
to wavelink.Player.play()
.
Wavelink does not advise using the wavelink.on_wavelink_track_end()
event in most cases. Use this event only when you plan to
not use AutoPlay
at all. Since version 3 implements wavelink.AutoPlayMode.partial
, a setting which skips fetching and recommending tracks,
using this event is no longer recommended in most use cases.
To send track updates or do player updates, consider using wavelink.on_wavelink_track_start()
instead.
async def on_wavelink_track_start(self, payload: wavelink.TrackStartEventPayload) -> None:
player: wavelink.Player | None = payload.player
if not player:
return
original: wavelink.Playable | None = payload.original
track: wavelink.Playable = payload.track
embed: discord.Embed = discord.Embed(title="Now Playing")
embed.description = f"**{track.title}** by `{track.author}`"
if track.artwork:
embed.set_image(url=track.artwork)
if original and original.recommended:
embed.description += f"\n\n`This track was recommended via {track.source}`"
if track.album.name:
embed.add_field(name="Album", value=track.album.name)
# Send this embed to a channel...
# See: simple.py example on GitHub.
Note
Please read the AutoPlay section for advice on how to properly use version 3 with AutoPlay.
AutoPlay#
Version 3 optimized AutoPlay and how it recommends tracks.
Available are currently 3 different AutoPlay modes.
See: wavelink.AutoPlayMode
Setting wavelink.Player.autoplay
to wavelink.AutoPlayMode.enabled
will allow the player to fetch and recommend tracks
based on your current listening history. This currently works with Spotify, YouTube and YouTube Music. This mode handles everything including looping, and prioritizes the Queue
over the AutoQueue.
Setting wavelink.Player.autoplay
to wavelink.AutoPlayMode.partial
will allow the player to handle the automatic playing of the next track
but will NOT recommend or fetch recommendations for playing in the future. This mode handles everything including looping.
Setting wavelink.Player.autoplay
to wavelink.AutoPlayMode.disabled
will stop the player from automatically playing tracks. You will need
to use wavelink.on_wavelink_track_end()
in this case.
AutoPlay also implements error safety. In the case of too many consecutive errors trying to play a track, AutoPlay will stop attempting until manually restarted
by playing a track E.g. with wavelink.Player.play()
.
Pausing and Resuming#
Version 3 slightly changes pausing behaviour.
All logic is done in wavelink.Player.pause()
and you simply pass a bool (True
to pause and False
to resume).
await player.pause(not player.paused)
Queue#
Version 3 made some internal changes to wavelink.Queue
.
The most noticeable is wavelink.Queue.mode
which allows you to turn the Queue to either,
wavelink.QueueMode.loop
, wavelink.QueueMode.loop_all
or wavelink.QueueMode.normal
.
wavelink.QueueMode.normal
means the queue will not loop at all.wavelink.QueueMode.loop_all
will loop every song in the history when the queue has been exhausted.wavelink.QueueMode.loop
will loop the current track continuously until turned off or skipped viawavelink.Player.skip()
withforce=True
.
Filters#
Version 3 has reworked the filters to hopefully be easier to use and feel more intuitive.
See: Filters
.
See: filters
See: set_filters()
See: play()
Some common recipes:
# Create a brand new Filters and apply it...
# You can use player.set_filters() for an easier way to reset.
filters: wavelink.Filters = wavelink.Filters()
await player.set_filters(filters)
# Retrieve the payload of any Filters instance...
filters: wavelink.Filters = player.filters
print(filters())
# Set some filters...
# You can set and reset individual filters at the same time...
filters: wavelink.Filters = player.filters
filters.timescale.set(pitch=1.2, speed=1.1, rate=1)
filters.rotation.set(rotation_hz=0.2)
filters.equalizer.reset()
await player.set_filters(filters)
# Reset a filter...
filters: wavelink.Filters = player.filters
filters.timescale.reset()
await player.set_filters(filters)
# Reset all filters...
filters: wavelink.Filters = player.filters
filters.reset()
await player.set_filters(filters)
# Reset and apply filters easier method...
await player.set_filters()
Lavalink Plugins#
Version 3 supports plugins in most cases without the need for any extra steps.
In some cases though you may need to send additional data.
You can use wavelink.Node.send()
for this purpose.
See the docs for more info.