Changelog¶
This page keeps a detailed human friendly rendering of what’s new and changed in specific versions.
v1.3.0 - 2026-07-20¶
Added¶
Added various helper methods to
Queue:Queue.sort()to sort the queue by a given key function. Similar to Python’s built-inlist.sort()method.Example:
queue.sort(key=lambda track: track.title)to sort by track title.
Queue.reverse()to reverse the order of the queue.Queue.dedupe()to remove duplicate tracks from the queue.Queue.__reversed__()to allow the built-inreversed()function on the queue.
Added
History.enabledproperty for checking whether history recording is active.Added
Player.skip_to()to skip directly to a track at a specific queue index.Added
Queue.move()to move a track from one index to another, shifting other tracks accordingly.Added
Queue.remove_at()to remove and return a track by index without side effects on the current track.Added a
keyparameter toQueue.remove()andQueue.remove_wait()for removing tracks by an arbitrary derived value instead of equality, without needing to hold the originalPlayableinstance.Example:
queue.remove([track.identifier], key=lambda t: t.identifier)to remove a track by its identifier.
Added
ShuffleModeandQueue.shuffle_modefor persistent shuffle support.Independent of
QueueMode.While
ShuffleMode.PERSISTENTis active,Queue.get()pops a random track instead of the next one in order, without reordering the underlying queue. Switching back toShuffleMode.DEFAULTresumes playback in the original order.Queue.shuffle()is unaffected and still performs its usual one-time in-place reorder.
Added a
keyparameter toQueue.get()andQueue.get_wait()to avoid picking a track that collides with the current track on an arbitrary derived value, e.g. to avoid repeats.Example:
queue.get(key=lambda track: track.author)to avoid picking a track by the currently playing artist.If every remaining candidate collides, the collision is allowed rather than raising.
Added a
keyparameter toPlayer.skip(), forwarded straight through toQueue.get(), so the same repeat-avoidance behaves consistently whether tracks are pulled from the queue directly or via the player.Example:
await player.skip(key=lambda track: track.author).
Fixed¶
Fixed
Queue.get_wait()not registering the waiter before attempting to retrieve a track, causing a race condition where a concurrentput()could be missed and the waiter would hang indefinitely.Fixed
Queue.historyreturning aHistoryinstance when history is disabled; now correctly returnsNone.Fixed
Player.play()not pushing the previously playing track to history before starting a new track, matching the documented behavior.Fixed
Player.stop()withclear_queue=Truenot clearing AutoPlay-discovered tracks from the queue.Fixed
Player.stop()silently resettingQueue.modetoQueueMode.NORMALwhenclear_queue=True; this is now documented behavior.Fixed
Queue.copy()creating a newHistorythat shared a mutable reference to the original settings object, causing mutations toenabledormax_itemsto leak between copies.Fixed
Queue.remove()with akeyparameter ignoring duplicate lookup values and crashing on unhashable types; both theremove_all=Trueandremove_all=Falsepaths now consistently respect user-provided values.Added missing exports for
AutoPlaySeedMissing,FrameworkClientMismatch, andFrameworkImportErrorto the top-level__all__.Fixed
Player.on_track_end()dispatchingTrackEndEventwith the wrongoriginaltrack whenskip()was triggered, because_original_trackwas overwritten byplay()before the event was emitted.Fixed
Player.previous()pushing the previous track back into history afterplay()was called, creating a duplicate history entry.Fixed
Filtersconstructor not callingsuper().__init__(), leaving_clientand_datauninitialised on direct construction.
Miscellaneous¶
Consolidated the separate
pr-checksandpr-templateworkflows into a singlepr-governanceworkflow.Scoped all CI job permissions to read-only and disabled credential persistence on checkout for improved security.
Added a 15-minute timeout to every CI job.
Enabled
pipcaching across CI workflows.Added concurrency control to the lint workflow to cancel redundant runs.
Added
twine checkand build provenance attestation to the release workflow.Hardened the template-check validation and excluded draft PRs from template enforcement, triggering checks only on
ready_for_review.Added sticky
h2/h3headers, a hover-highlights toggle persisted inlocalStorage, and alphabetical “On this page” TOC sorting to the docs.Enabled
nitpickySphinx mode andsphinx-autodoc-typehints; replaced raw:class:references with proper cross-references throughout.Exposed missing exception classes (
SonoLinkException,NodeError,NetworkError) andVersion/GitInfomodels in the API docs.
v1.2.1 - 2026-07-04¶
Added¶
Added the missing
NetworkErrorexport to the public API.
Fixed¶
Fixed a typo in
__repr_attrs__ofWebSocketClosedEvent.Fixed an incorrect datetime timestamp in
timecaused by differing time units.Fixed an issue where the node status would remain stuck on
NodeStatus.CONNECTINGafter anattempt_connectfailure.Fixed
Filtersnot populating data on the instance.Fixed a race condition in the node reconnection flow that caused an intermittent “Timed out waiting for node READY payload” error after prolonged uptime.
v1.2.0 - 2026-06-23¶
Added¶
Added
Node.is_connectingproperty for checking whether a node is currently establishing a connection.Added the missing auto_reconnect parameter to
Client.create_node()for controlling whether the client should automatically attempt to reconnect to the node on disconnection. This defaults toTrue.Added
Node.reconnect()for manually triggering a reconnection to the node.Added
originalandoriginalto access the original track passed toPlayer.play().Serializable extras are now forwarded to Lavalink as
user_dataand accessible viaextrason reconstructed tracks. Non-serializable values are warned and accessible via.original.extras.Added support for region-aware node selection:
Added
NodeRegionenum representing the available Discord voice channel regions a node can serve.Added
regionsparameter toClient.create_node()andNodeto associate a node with one or moreNodeRegionvalues (or raw strings).Added
regionparameter toClient.get_best_node(),Client.search_track(),Client.decode_track(), andClient.decode_tracks()to drive region-aware node selection.BasePlayernow automatically passes its voice region toClient.get_best_node()when selecting a node on connect.
Fixed¶
Fixed an issue where the client would not attempt to reconnect to a node after a disconnection, even if
auto_reconnectwas enabled.Fixed an issue with using the speed extra, or the
curl-cffipackage where it would raise a CurlError and the library would fail to handle it properly, causing the node to be left in a broken state.Fixed an internal race condition where
Node.connect()could be called on an already-connected node, causing aRuntimeErrorto be silently raised in the background after extended uptime.
v1.1.1 - 2026-05-15¶
Fixed¶
Recursion issue in
sonolink.Playercustom subclass and instance checks that raised aRecursionErrorduringissubclassorisinstancechecks.sonolink.Playernot properly disconnecting after Discord 4014 and 4022 voice close events, which could leave players stuck in an invalid voice session state.sonolink.Playernot disconnecting on stale LavaLink session.Missing typings for
sonolink.gateway.PlayerDisconnectEvent.extra_dataonsonolink.gateway.PlayerDisconnectEvent.
v1.1.0 - 2026-05-14¶
Added¶
Added support for Nextcord via
NextcordPlayer.See the Nextcord examples for usage.
Added
sonolink.get_clientfor retrieving the active client instance.Added
Client.get_node()to retrieve a specific node from the client.Added three new events:
Added
Player.update()to quickly update multiple player settings at once.Player.autoplay_settings,Player.history_settings, andPlayer.queue_modeare now exposed and can be set directly.Added
TrackSourceType.DEEZERas a supported track source.Added
Queue.countandHistory.countproperties.Added
Queue.autoplay_tracksandQueue.put_autoplay()to fix autoplay queue priority via a dedicated stream.autoplayis also now exposed.Added
AutoPlaySeedMissing, a specific error raised when autoplay has no seed tracks to generate from.Added framework-specific import and mismatch exceptions:
Added a Getting Started guide.
Added four new migration guides:
The existing WaveLink guide has also been updated.
Added a py.typed marker file for improved typing compatibility with type checkers.
Added two new keyword arguments to
Client.create_node():hostandportfor specifying the node URI in parts as an alternative to the singleuristring;uriis now optional to allow for this.Added a Code of Conduct for project community guidelines.
Changed¶
WSCloseEventhas been renamed toWebSocketClosedEvent.get_best_node()now prefers nodes with known stats over nodes with no reported stats.Swapped
asyncio.TimeoutErrorwith the built-inTimeoutErrorthroughout.Cleaned up documentation for all enums.
Removed¶
Removed the fallback to
discord.pyas the default framework.Removed the unused
valueargument fromPlayer.pause().Player.resume()is no longer an alias for unpausing — it must be called explicitly to resume playback.
Fixed¶
Fixed incorrect generics on the
PycordPlayer.Fixed
forceparameter handling inPlayer.disconnect.Fixed the library raising
QueueEmptywhen skipping tracks for the user internally.
Miscellaneous¶
sonolink.gateway.Nodehas been split into multiple components internally for better separation of concerns and maintainability. This is not a breaking change as the public API remains the same, but it should improve code readability and future extensibility.Formatted the project with Ruff for consistent code style and linting.
v1.0.1 - 2026-04-12¶
Fixed¶
Inconsistent environment variable usage across code and documentation
Fixed performance and memory issue in PlayerFactory caused by repeated metadata scans:
Reduced connection time (~11x faster)
Reduced memory peak (~12x lower)
Eliminated repeated expensive
importlib.metadata.packages_distributions()calls
v1.0.0 - 2026-04-11¶
Initial release. For more information on what this added, consider referring to the API Reference or the examples.