From 092ebbb09237177e369d321c4303161fb63d1268 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:06:12 +0200 Subject: [PATCH 01/12] Start voice guides rewriting --- docs/voice/advanced-playing.md | 0 docs/voice/playing-rewritten.md | 166 ++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 docs/voice/advanced-playing.md create mode 100644 docs/voice/playing-rewritten.md diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/voice/playing-rewritten.md b/docs/voice/playing-rewritten.md new file mode 100644 index 00000000..f1eaef34 --- /dev/null +++ b/docs/voice/playing-rewritten.md @@ -0,0 +1,166 @@ +--- +title: Playing Audio in Voice Channels +--- + +# About + +Pycord offers multiple ways to play audio streams in a voice channel keeping it as simple +and easy as possible, so making any kind of Discord bot is easy for all audiences. This +guide provides simple and easy examples of using the multiple ways the library allows +you to play audio. + +## Starting out + +Pycord natively provides an [`discord.AudioSource`](https://docs.pycord.dev/en/stable/api/voice.html#discord.AudioSource) +object. This object defines the structure audio sources must follow in order to be accepted by +the library. For more information on this structure, check the respective documentation. + +You usually do not need to manually create subclasses of this object as the library also provides you +with the most common audio source types. + +### [`PCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMAudio) + +`PCMAudio` is the base class designed to read raw, uncompressed 16-bit 48kHz stereo PCM bytes +directly from an existing byte stream or file. This essentially means that common audio formats +such as `mp3`, `wav`, or `ogg` are not valid, as they are not PCM streams (check `FFmpegPCMAudio`). + +You should only use `PCMAudio` in specialized or advanced scenarios where you have raw PCM bytes +(such as live audio synthesis or reading from pre-recorded `.pcm` data) without needing external +tools. + +```py title="Using PCMAudio" +import io +import discord + +bot = discord.Bot() + +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() + + with open("raw_audio.pcm", "rb") as f: + # we use io.BytesIO as a container for the bytes we are reading + pcm_data = io.BytesIO(f.read()) + + source = discord.PCMAudio(pcm_data) + ctx.voice_client.play(source) + await ctx.respond("Playing PCM audio.") +``` + +### [`FFmpegPCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegPCMAudio) + +Unlike `PCMAudio`, `FFmpegPCMAudio` allows you to pass different audio file formats and automatically +decode it to PCM. This is done by using the `ffmpeg` (or `avconv`) executables to convert these audio +streams into manipulable PCM data in a dedicated sub-process. + +This intermediate PCM step is slightly more CPU-intensive, as it has to encode the raw audio again into +Opus in order to be transmitted to Discord. However, having access to the raw PCM is necessary if you +want to manipulate or modify the audio. + +```py title="Using FFmpegPCMAudio" +import discord + +bot = discord.Bot() + +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() + + # unlike PCMAudio, you do not need to manually open and read + # the file, the library will do it for you. + # FFmpegPCMAudio takes a path to a valid audio file as its first parameter + # this means that both relative and absolute paths are allowed + source = discord.FFmpegPCMAudio("audio_file.mp3") + ctx.voice_client.play(source) + await ctx.respond("Playing FFmpeg PCM audio.") +``` + +### [`PCMVolumeTransformer`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMVolumeTransformer) + +`PCMVolumeTransformer` is an audio source that simplifies the volume changing process in PCM sources +(such as `PCMAudio` or `FFmpegPCMAudio`) by doing it for you. + +This also allows you to dynamically change the volume during playback, by just setting a new value to the +`volume` property. + +```py title="Using PCMVolumeTransformer" +import discord + +bot = discord.Bot() + +@bot.command() +async def play(ctx: discord.ApplicationContext, volume: float = 1.0) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() + + # this can be any non-opus source (such as discord.PCMAudio or discord.FFmpegPCMAudio) + original_source = ... + + source = discord.PCMVolumeTransformer(original_source, volume=volume) + ctx.voice_client.play(source) + await ctx.respond("Playing PCM volume transformed audio.") + + +@bot.command() +async def volume(ctx: discord.ApplicationContext, *, volume: float) -> None: + if not ctx.voice_client or not ctx.voice_client.source: + await ctx.respond("Connect to a voice channel and play something first!") + return + + ctx.voice_client.source.volume = volume + await ctx.respond(f"Changed the audio volume to {volume}") +``` + +### [`FFmpegOpusAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegOpusAudio) + +This is similar to `FFmpegPCMAudio`, but, as the name suggests, this does not produce manipulable PCM +streams and directly encodes audio streams to Opus. + +This is the most efficient and recommended way to play local media files or web streams directly into +a voice channel **only when no extra processing (such as changing volume) is required**. + +`FFmpegOpusAudio` also uses `ffmpeg` (or `avconv`) to encode common audio formats to Opus, which is the +native audio format Discord requires for voice transmission. + +Unlike other sources, this one has to be initialized by using the `FFmpegOpusAudio.from_probe` classmethod. +This analyzes the audio to obtain the codec and bitrate of the audio, so it uses the fastest and +most efficient way to encode it to Opus, this is done by using `ffprobe` (or `avprobe`). + +```py title="Initializing a FFmpegOpusAudio" +source = await discord.FFmpegOpusAudio.from_probe("audio.webm") +voice_client.play(source) +``` + +`FFmpegOpusAudio.from_probe` also allows you to pass a custom `method`, used to determine the codec and +bitrate of the audio source. This can be a string defining whether to use the `native` probe +(`ffprobe` / `avprobe`), or `fallback`, which falls back to use `ffmpeg` / `avconv`. The latter +may be used by Windows users when none of `ffprobe` or `avprobe` is installed. + +This can also take custom functions which take two parameters, the `source` and the `executable`, and +should return a tuple of `(codec, bitrate)`. + +```py title="Using FFmpegOpusAudio" +import discord + +bot = discord.Bot() + +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() + + source = await discord.FFmpegOpusAudio.from_probe("audio.wav") + ctx.voice_client.play(source) + await ctx.respond("Playing FFmpeg Opus audio.") +``` + +And... congratulations! You now know how to implement audio sources for voice channel playback into your +bot! Some audio sources may look more complex than others, but all of them keep an easy and simple design +so you can make complex bots and get even the most advanced of ideas down. + +!!! info "Related Topics" + + - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) + - [Advanced Audio Playback](./advanced-playing.md) From 147fd64575be6a481e86e62bf7fa77dfa269e016 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:15:54 +0200 Subject: [PATCH 02/12] chore: update voice index, and finish advanced playing --- docs/voice/advanced-playing.md | 227 ++++++++++++++++++++++++++++++++ docs/voice/index.md | 2 +- docs/voice/playing-old.md | 132 +++++++++++++++++++ docs/voice/playing-rewritten.md | 166 ----------------------- docs/voice/playing.md | 197 ++++++++++++++++----------- 5 files changed, 477 insertions(+), 247 deletions(-) create mode 100644 docs/voice/playing-old.md delete mode 100644 docs/voice/playing-rewritten.md diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index e69de29b..1b73084e 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -0,0 +1,227 @@ +--- +title: Advanced Audio Playing +--- + +# About + +Pycord provides built-in audio sources for common use cases, but complex bots often require greater +control over audio streams or need to handle voice playback across hundreds of servers simultaneously. +This guide covers advanced audio concepts, such as writing custom audio source implementations +and offloading voice processing to standalone audio nodes, using Lavalink. + +## Starting out + +In the [previous guide](./playing.md), standard audio sources that Pycord provides were covered. While +these built-in classes cover most general needs, [`discord.AudioSource`](https://docs.pycord.dev/en/stable/api/voice.html#discord.AudioSource) +can be subclassed to create custom audio pipelines, synthesizers, or memory stream handlers. + +However, as a bot grows larger, handling multiple local FFmpeg processes and real-time encoding on the +same machine as your bot's running can quickly bottleneck CPU performance. To solve this, advanced +bot architectures decouple playback entirely by relying on external audio servers such as Lavalink and +interacting with them through lightweight client libraries like SonoLink. + +### Custom `AudioSource`s + +All audio sources covered in the previous guide derive from `discord.AudioSource`, and implement +at least a custom `read` method. + +This method should return 20ms worth of audio, following the 16-bit 48kHz stereo PCM requirement. +You may override `is_opus` when the audio returned by the `read` method is already Opus encoded. + +When no more audio is available, you can return an empty bytes-like object to tell the library the +audio reading is over. + +You may also override `cleanup` when extra clean-up processes are needed when the reading is complete. + +A common usecase for subclassing an `AudioSource` is when you want to implement an audio player that extracts audio from an online source, such as YouTube (using `youtube_dl`/`yt_dlp`). + +```py title="Implementing a custom audio source" +import asyncio + +import discord +import youtube_dl # third-party library that requires installing + +ytdl = youtube_dl.YoutubeDL() + + +# we will implement a basic YTDLSource +class YTDLSource(discord.AudioSource): + # we will take a processed audio source, to simplify our handling + def __init__(self, processed_source: discord.AudioSource, *, data: dict) -> None: + self.processed_source: discord.AudioSource = processed_source + self.data: dict = data + + self.title = data.get("title") + self.url = data.get("url") + + # reading from this source is essentially reading from the processed source + def read(self) -> bytes: + return self.processed_source.read() + + def is_opus(self) -> bool: + return self.processed_source.is_opus() + + def cleanup(self) -> None: + self.processed_source.cleanup() + + @classmethod + async def from_url(cls, url: str) -> YTDLSource: + # here, we will implement our logic from obtaining the youtube audio from an url + # we will be using asyncio.to_thread, because extracting the info is a sync operation + # and may block our bot for the duration it takes to finish + data = await asyncio.to_thread(ytdl.extract_info, url, download=True) + + # playlists have an "entries" key, so grab the first item + if "entries" in data: + data = data["entries"][0] + + filename = ytdl.prepare_filename(data) + return cls(discord.FFmpegPCMAudio(filename), data=data) +``` + +For a more advanced explanation and implementation of this source, check +[the `basic_voice.py` example](https://github.com/Pycord-Development/pycord/blob/master/examples/basic_voice.py). + +### Using Lavalink + +Lavalink is the ultimate audio playback server tool, as it can handle both remote and local audio +playing while being easy to handle and setup, and low-usage. + +First, you need to run a [Lavalink Server](https://github.com/lavalink-devs/Lavalink) to connect with. +In case you do not know how, there are multiple documentations to do so that will not be covered here, +but we recommend you the [SonoLink Lavalink Setup Guide](https://sonolink.readthedocs.io/en/latest/guides/lavalink-setup.html). + +To interact with your Lavalink server you must send HTTP requests, but here we will be using +[SonoLink](https://github.com/sonolink/sonolink), an API wrapper for Lavalink. + +To install it, you can simply run: + +```sh title="Installing sonolink" +pip install -U sonolink +``` + +Now, you will need to connect to your Lavalink server using a Node: + +```py title="Connecting to Lavalink" +import discord +import sonolink + +bot = discord.Bot() +sl_client = sonolink.Client(bot) + +sl_client.create_node( + id="main-node", # the unique ID for the node being created + uri="http://0.0.0.0:443", # HTTP(S) protocol is required when passing URI + password="youshallnotpass" +) + +@bot.listen() +async def on_connect() -> None: + await sl_client.start() # starts connection to the created nodes +``` + +
+ +Now you are finished making your node! Next, you will want to: + +1. Make a `play` command +2. Add connection-handling events + +#### Making a `play` command + +The core functionality for a music bot using SonoLink is a `play` command, as it allows users to reproduce +their own songs as they please. + +To do this, you need to create a command that ensures a `sonolink.Player` instance is connected +and available to play audio. + +```py title="Creating a play command" +@bot.slash_command() +async def play(ctx: discord.ApplicationContext, *, search: str) -> None: + # Before proceeding with the logic, we must check the ctx.author is + # connected in a voice channel + if not ctx.author.voice or not ctx.author.voice.channel: + await ctx.respond("You must be in a voice channel first!") + return + + # We need to check we are connected to a voice channel AND the + # voice client connected is a `sonolink.Player` instance. + vc = ctx.voice_client + + # Connect to the voice channel if we are not yet + if not vc: + vc = await ctx.author.voice.channel.connect( + cls=sonolink.Player, + ) + elif not isinstance(vc, sonolink.Player): + # And here, if there is a voice client connected, check it is + # a sonolink.Player instance. + # If not, reconnect with a sonolink.Player instance + await vc.disconnect(force=True) + vc = await ctx.author.voice.channel.connect( + cls=sonolink.Player, + ) + + # This check is not required, but creates a better experience for final users + # We will check that the channel the Player is connected is the same the ctx.author + # is in. + if ctx.author.voice.channel.id != vc.channel.id: + await ctx.respond("You must be in the same voice channel as the bot.") + return + + # We will now search for the song the user provided in the `search` parameter + # We can optionally pass a `source` keyword argument to reduce the locations + # the song will be search from. + result = await sl_client.search_track(search) + + # SonoLink returns a SearchResult instance when searching tracks + # so we will need to check that the result is not empty and is not an error + if result.is_empty() or result.is_error() or not result.result: + await ctx.respond("Song not found!") + return + + # The fetched result can be a list of tracks, a playlist or a single track + # so we must check against all those possibilities + if isinstance(result.result, list): + track = result.result[0] + elif isinstance(result.result, sonolink.models.Playlist): + track = result.result.tracks[0] + else: + track = result.result + + # And finally... we play the track we obtained + await vc.play(song) + await ctx.respond(f"Now playing: `{track.title}`") +``` + +
+ +Now that this is done, the only thing left to do is make your connect events. + +#### Adding connect events + +The final step of this guide is connecting to the node to your server when the bot goes online. + +To make it, you will want to do the following: + +```py title="Adding connect events" +@bot.listen() +async def on_connect() -> None: + await sl_client.start() # Starting the client & connect all nodes + +@bot.event +async def on_sonolink_node_ready(payload: sonolink.gateway.ReadyEvent) -> None: + print(f"Node with ID {payload.node.id!r} has connected!") + print(f"Resumed session: {payload.resumed}") + +bot.run("token") +``` + +Congratulations! You are now able to create custom Audio Sources for advanced playback, and offloading +the playback to an external node using Lavalink and interacting with it using Sonolink! Most bots and +Discord API wrappers don't have this as a feature, so this is quite an accomplishment. Thankfully, +Pycord makes it easy to make complex bots so that you can get the most advanced of ideas down. + +!!! info "Related Topics" + + - [Rules and Common Practises](../getting-started/rules-and-common-practises.md) diff --git a/docs/voice/index.md b/docs/voice/index.md index 88b27eba..7c111a63 100644 --- a/docs/voice/index.md +++ b/docs/voice/index.md @@ -20,6 +20,6 @@ The following features are optional: Some hosts may not come with it, though, so remember to always check if required dependencies are installed. - [`FFmpeg`](https://ffmpeg.org) - used for sending/receiving files other than `.pcm` and `.wav` -- [`Pycord.Wavelink`](https://github.com/Pycord-Development/Pycord.Wavelink), +- [`SonoLink`](https://github.com/sonolink/sonolink) - used for advanced audio playback [`Lavalink.py`](https://github.com/Devoxin/Lavalink.py), [`Wavelink`](https://github.com/PythonistaGuild/Wavelink) or any other Python LavaLink library for music playback. diff --git a/docs/voice/playing-old.md b/docs/voice/playing-old.md new file mode 100644 index 00000000..839ea115 --- /dev/null +++ b/docs/voice/playing-old.md @@ -0,0 +1,132 @@ +--- +title: Wavelink Audio Player +--- + +# About + +Pycord and Wavelink try to keep the playing of audio as simple and easy as possible, to keep making Discord +bots of any kind easy for all audiences. This guide provides simple and easy examples of using the +audio playing feature. + +For users that want extra examples, you can find some in Pycord's +[GitHub repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). + +## Starting out + +First you need to run a [Lavalink Server](https://github.com/freyacodes/Lavalink) to connect with. +There a multiple documentations to do this, so we are not covering that here. + +You also need to install the [wavelink](https://github.com/PythonistaGuild/Wavelink) library. + +```py title="Installing wavelink" +python3 -m pip install wavelink +``` + +You will now want to connect to your server via a node. + +```py title="Connect Node with Lavalink" +import discord +import wavelink + +bot = discord.Bot() + + +async def connect_nodes(): + """Connect to our Lavalink nodes.""" + await bot.wait_until_ready() # wait until the bot is ready + + nodes = [ + wavelink.Node( + identifier="Node1", # This identifier must be unique for all the nodes you are going to use + uri="http://0.0.0.0:443", # Protocol (http/s) is required, port must be 443 as it is the one lavalink uses + password="youshallnotpass", + ) + ] + + await wavelink.Pool.connect(nodes=nodes, client=bot) # Connect our nodes +``` + +
+ +Now you are finished making your node! Next, you will want to: + +1. Making a play command +1. Adding connect events + +### Making a play command + +To make a play command, you will need to make a function to connect and play audio in a voice channel. + +```py title="Play Command Example" +import typing + + +@bot.slash_command(name="play") +async def play(ctx, search: str): + # First we may define our voice client, + # for this, we are going to use typing.cast() + # function just for the type checker know that + # `ctx.voice_client` is going to be from type + # `wavelink.Player` + vc = typing.cast(wavelink.Player, ctx.voice_client) + + if not vc: # We firstly check if there is a voice client + vc = await ctx.author.voice.channel.connect( + cls=wavelink.Player + ) # If there isn't, we connect it to the channel + + # Now we are going to check if the invoker of the command + # is in the same voice channel than the voice client, when defined. + # If not, we return an error message. + if ctx.author.voice.channel.id != vc.channel.id: + return await ctx.respond("You must be in the same voice channel as the bot.") + + # Now we search for the song. You can optionally + # pass the "source" keyword, of type "wavelink.TrackSource" + song = await wavelink.Playable.search(search) + + if not song: # In case the song is not found + return await ctx.respond("No song found.") # we return an error message + + await vc.play(song) # Else, we play it + await ctx.respond(f"Now playing: `{song.title}`") # and return a success message +``` + +``` +# link with playing-now-playing.json +``` + +
+ +Now that you've done this, the only thing left to do is make your connect events. + +### Adding connect events + +The final step to this guide is connecting the node to your server when the bot goes online. + +To make it, you will want to do the following: + +```py title="Adding connect events" +@bot.event +async def on_ready(): + await connect_nodes() # connect to the server + + +@bot.event +async def on_wavelink_node_ready(payload: wavelink.NodeReadyEventPayload): + # Everytime a node is successfully connected, we + # will print a message letting it know. + print(f"Node with ID {payload.session_id} has connected") + print(f"Resumed session: {payload.resumed}") + + +bot.run("token") +``` + +Congratulations! You have now implemented voice playing into your bot! Most bots and Discord API +wrappers don't have this as a feature, so this is quite an accomplishment. Thankfully, Pycord makes +it easy to make complex bots so that you can get even the most advanced of ideas down. + +!!! info "Related Topics" + + - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) diff --git a/docs/voice/playing-rewritten.md b/docs/voice/playing-rewritten.md deleted file mode 100644 index f1eaef34..00000000 --- a/docs/voice/playing-rewritten.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Playing Audio in Voice Channels ---- - -# About - -Pycord offers multiple ways to play audio streams in a voice channel keeping it as simple -and easy as possible, so making any kind of Discord bot is easy for all audiences. This -guide provides simple and easy examples of using the multiple ways the library allows -you to play audio. - -## Starting out - -Pycord natively provides an [`discord.AudioSource`](https://docs.pycord.dev/en/stable/api/voice.html#discord.AudioSource) -object. This object defines the structure audio sources must follow in order to be accepted by -the library. For more information on this structure, check the respective documentation. - -You usually do not need to manually create subclasses of this object as the library also provides you -with the most common audio source types. - -### [`PCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMAudio) - -`PCMAudio` is the base class designed to read raw, uncompressed 16-bit 48kHz stereo PCM bytes -directly from an existing byte stream or file. This essentially means that common audio formats -such as `mp3`, `wav`, or `ogg` are not valid, as they are not PCM streams (check `FFmpegPCMAudio`). - -You should only use `PCMAudio` in specialized or advanced scenarios where you have raw PCM bytes -(such as live audio synthesis or reading from pre-recorded `.pcm` data) without needing external -tools. - -```py title="Using PCMAudio" -import io -import discord - -bot = discord.Bot() - -@bot.command() -async def play(ctx: discord.ApplicationContext) -> None: - if not ctx.voice_client: - await ctx.author.voice.channel.connect() - - with open("raw_audio.pcm", "rb") as f: - # we use io.BytesIO as a container for the bytes we are reading - pcm_data = io.BytesIO(f.read()) - - source = discord.PCMAudio(pcm_data) - ctx.voice_client.play(source) - await ctx.respond("Playing PCM audio.") -``` - -### [`FFmpegPCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegPCMAudio) - -Unlike `PCMAudio`, `FFmpegPCMAudio` allows you to pass different audio file formats and automatically -decode it to PCM. This is done by using the `ffmpeg` (or `avconv`) executables to convert these audio -streams into manipulable PCM data in a dedicated sub-process. - -This intermediate PCM step is slightly more CPU-intensive, as it has to encode the raw audio again into -Opus in order to be transmitted to Discord. However, having access to the raw PCM is necessary if you -want to manipulate or modify the audio. - -```py title="Using FFmpegPCMAudio" -import discord - -bot = discord.Bot() - -@bot.command() -async def play(ctx: discord.ApplicationContext) -> None: - if not ctx.voice_client: - await ctx.author.voice.channel.connect() - - # unlike PCMAudio, you do not need to manually open and read - # the file, the library will do it for you. - # FFmpegPCMAudio takes a path to a valid audio file as its first parameter - # this means that both relative and absolute paths are allowed - source = discord.FFmpegPCMAudio("audio_file.mp3") - ctx.voice_client.play(source) - await ctx.respond("Playing FFmpeg PCM audio.") -``` - -### [`PCMVolumeTransformer`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMVolumeTransformer) - -`PCMVolumeTransformer` is an audio source that simplifies the volume changing process in PCM sources -(such as `PCMAudio` or `FFmpegPCMAudio`) by doing it for you. - -This also allows you to dynamically change the volume during playback, by just setting a new value to the -`volume` property. - -```py title="Using PCMVolumeTransformer" -import discord - -bot = discord.Bot() - -@bot.command() -async def play(ctx: discord.ApplicationContext, volume: float = 1.0) -> None: - if not ctx.voice_client: - await ctx.author.voice.channel.connect() - - # this can be any non-opus source (such as discord.PCMAudio or discord.FFmpegPCMAudio) - original_source = ... - - source = discord.PCMVolumeTransformer(original_source, volume=volume) - ctx.voice_client.play(source) - await ctx.respond("Playing PCM volume transformed audio.") - - -@bot.command() -async def volume(ctx: discord.ApplicationContext, *, volume: float) -> None: - if not ctx.voice_client or not ctx.voice_client.source: - await ctx.respond("Connect to a voice channel and play something first!") - return - - ctx.voice_client.source.volume = volume - await ctx.respond(f"Changed the audio volume to {volume}") -``` - -### [`FFmpegOpusAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegOpusAudio) - -This is similar to `FFmpegPCMAudio`, but, as the name suggests, this does not produce manipulable PCM -streams and directly encodes audio streams to Opus. - -This is the most efficient and recommended way to play local media files or web streams directly into -a voice channel **only when no extra processing (such as changing volume) is required**. - -`FFmpegOpusAudio` also uses `ffmpeg` (or `avconv`) to encode common audio formats to Opus, which is the -native audio format Discord requires for voice transmission. - -Unlike other sources, this one has to be initialized by using the `FFmpegOpusAudio.from_probe` classmethod. -This analyzes the audio to obtain the codec and bitrate of the audio, so it uses the fastest and -most efficient way to encode it to Opus, this is done by using `ffprobe` (or `avprobe`). - -```py title="Initializing a FFmpegOpusAudio" -source = await discord.FFmpegOpusAudio.from_probe("audio.webm") -voice_client.play(source) -``` - -`FFmpegOpusAudio.from_probe` also allows you to pass a custom `method`, used to determine the codec and -bitrate of the audio source. This can be a string defining whether to use the `native` probe -(`ffprobe` / `avprobe`), or `fallback`, which falls back to use `ffmpeg` / `avconv`. The latter -may be used by Windows users when none of `ffprobe` or `avprobe` is installed. - -This can also take custom functions which take two parameters, the `source` and the `executable`, and -should return a tuple of `(codec, bitrate)`. - -```py title="Using FFmpegOpusAudio" -import discord - -bot = discord.Bot() - -@bot.command() -async def play(ctx: discord.ApplicationContext) -> None: - if not ctx.voice_client: - await ctx.author.voice.channel.connect() - - source = await discord.FFmpegOpusAudio.from_probe("audio.wav") - ctx.voice_client.play(source) - await ctx.respond("Playing FFmpeg Opus audio.") -``` - -And... congratulations! You now know how to implement audio sources for voice channel playback into your -bot! Some audio sources may look more complex than others, but all of them keep an easy and simple design -so you can make complex bots and get even the most advanced of ideas down. - -!!! info "Related Topics" - - - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) - - [Advanced Audio Playback](./advanced-playing.md) diff --git a/docs/voice/playing.md b/docs/voice/playing.md index 839ea115..70f16e47 100644 --- a/docs/voice/playing.md +++ b/docs/voice/playing.md @@ -1,132 +1,169 @@ --- -title: Wavelink Audio Player +title: Playing Audio in Voice Channels --- # About -Pycord and Wavelink try to keep the playing of audio as simple and easy as possible, to keep making Discord -bots of any kind easy for all audiences. This guide provides simple and easy examples of using the -audio playing feature. +Pycord offers multiple ways to play audio streams in a voice channel keeping it as simple +and easy as possible, so making any kind of Discord bot is easy for all audiences. This +guide provides simple and easy examples of using the multiple ways the library allows +you to play audio. For users that want extra examples, you can find some in Pycord's -[GitHub repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). +[Github repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). ## Starting out -First you need to run a [Lavalink Server](https://github.com/freyacodes/Lavalink) to connect with. -There a multiple documentations to do this, so we are not covering that here. +Pycord natively provides an [`discord.AudioSource`](https://docs.pycord.dev/en/stable/api/voice.html#discord.AudioSource) +object. This object defines the structure audio sources must follow in order to be accepted by +the library. For more information on this structure, check the respective documentation. -You also need to install the [wavelink](https://github.com/PythonistaGuild/Wavelink) library. +You usually do not need to manually create subclasses of this object as the library also provides you +with the most common audio source types. -```py title="Installing wavelink" -python3 -m pip install wavelink -``` +### [`PCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMAudio) + +`PCMAudio` is the base class designed to read raw, uncompressed 16-bit 48kHz stereo PCM bytes +directly from an existing byte stream or file. This essentially means that common audio formats +such as `mp3`, `wav`, or `ogg` are not valid, as they are not PCM streams (check `FFmpegPCMAudio`). -You will now want to connect to your server via a node. +You should only use `PCMAudio` in specialized or advanced scenarios where you have raw PCM bytes +(such as live audio synthesis or reading from pre-recorded `.pcm` data) without needing external +tools. -```py title="Connect Node with Lavalink" +```py title="Using PCMAudio" +import io import discord -import wavelink bot = discord.Bot() +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() -async def connect_nodes(): - """Connect to our Lavalink nodes.""" - await bot.wait_until_ready() # wait until the bot is ready - - nodes = [ - wavelink.Node( - identifier="Node1", # This identifier must be unique for all the nodes you are going to use - uri="http://0.0.0.0:443", # Protocol (http/s) is required, port must be 443 as it is the one lavalink uses - password="youshallnotpass", - ) - ] + with open("raw_audio.pcm", "rb") as f: + # we use io.BytesIO as a container for the bytes we are reading + pcm_data = io.BytesIO(f.read()) - await wavelink.Pool.connect(nodes=nodes, client=bot) # Connect our nodes + source = discord.PCMAudio(pcm_data) + ctx.voice_client.play(source) + await ctx.respond("Playing PCM audio.") ``` -
+### [`FFmpegPCMAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegPCMAudio) -Now you are finished making your node! Next, you will want to: +Unlike `PCMAudio`, `FFmpegPCMAudio` allows you to pass different audio file formats and automatically +decode it to PCM. This is done by using the `ffmpeg` (or `avconv`) executables to convert these audio +streams into manipulable PCM data in a dedicated sub-process. -1. Making a play command -1. Adding connect events +This intermediate PCM step is slightly more CPU-intensive, as it has to encode the raw audio again into +Opus in order to be transmitted to Discord. However, having access to the raw PCM is necessary if you +want to manipulate or modify the audio. -### Making a play command +```py title="Using FFmpegPCMAudio" +import discord -To make a play command, you will need to make a function to connect and play audio in a voice channel. +bot = discord.Bot() -```py title="Play Command Example" -import typing +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() + + # unlike PCMAudio, you do not need to manually open and read + # the file, the library will do it for you. + # FFmpegPCMAudio takes a path to a valid audio file as its first parameter + # this means that both relative and absolute paths are allowed + source = discord.FFmpegPCMAudio("audio_file.mp3") + ctx.voice_client.play(source) + await ctx.respond("Playing FFmpeg PCM audio.") +``` +### [`PCMVolumeTransformer`](https://docs.pycord.dev/en/stable/api/voice.html#discord.PCMVolumeTransformer) -@bot.slash_command(name="play") -async def play(ctx, search: str): - # First we may define our voice client, - # for this, we are going to use typing.cast() - # function just for the type checker know that - # `ctx.voice_client` is going to be from type - # `wavelink.Player` - vc = typing.cast(wavelink.Player, ctx.voice_client) +`PCMVolumeTransformer` is an audio source that simplifies the volume changing process in PCM sources +(such as `PCMAudio` or `FFmpegPCMAudio`) by doing it for you. - if not vc: # We firstly check if there is a voice client - vc = await ctx.author.voice.channel.connect( - cls=wavelink.Player - ) # If there isn't, we connect it to the channel +This also allows you to dynamically change the volume during playback, by just setting a new value to the +`volume` property. - # Now we are going to check if the invoker of the command - # is in the same voice channel than the voice client, when defined. - # If not, we return an error message. - if ctx.author.voice.channel.id != vc.channel.id: - return await ctx.respond("You must be in the same voice channel as the bot.") +```py title="Using PCMVolumeTransformer" +import discord - # Now we search for the song. You can optionally - # pass the "source" keyword, of type "wavelink.TrackSource" - song = await wavelink.Playable.search(search) +bot = discord.Bot() - if not song: # In case the song is not found - return await ctx.respond("No song found.") # we return an error message +@bot.command() +async def play(ctx: discord.ApplicationContext, volume: float = 1.0) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() - await vc.play(song) # Else, we play it - await ctx.respond(f"Now playing: `{song.title}`") # and return a success message -``` + # this can be any non-opus source (such as discord.PCMAudio or discord.FFmpegPCMAudio) + original_source = ... -``` -# link with playing-now-playing.json + source = discord.PCMVolumeTransformer(original_source, volume=volume) + ctx.voice_client.play(source) + await ctx.respond("Playing PCM volume transformed audio.") + + +@bot.command() +async def volume(ctx: discord.ApplicationContext, *, volume: float) -> None: + if not ctx.voice_client or not ctx.voice_client.source: + await ctx.respond("Connect to a voice channel and play something first!") + return + + ctx.voice_client.source.volume = volume + await ctx.respond(f"Changed the audio volume to {volume}") ``` -
+### [`FFmpegOpusAudio`](https://docs.pycord.dev/en/stable/api/voice.html#discord.FFmpegOpusAudio) -Now that you've done this, the only thing left to do is make your connect events. +This is similar to `FFmpegPCMAudio`, but, as the name suggests, this does not produce manipulable PCM +streams and directly encodes audio streams to Opus. -### Adding connect events +This is the most efficient and recommended way to play local media files or web streams directly into +a voice channel **only when no extra processing (such as changing volume) is required**. -The final step to this guide is connecting the node to your server when the bot goes online. +`FFmpegOpusAudio` also uses `ffmpeg` (or `avconv`) to encode common audio formats to Opus, which is the +native audio format Discord requires for voice transmission. -To make it, you will want to do the following: +Unlike other sources, this one has to be initialized by using the `FFmpegOpusAudio.from_probe` classmethod. +This analyzes the audio to obtain the codec and bitrate of the audio, so it uses the fastest and +most efficient way to encode it to Opus, this is done by using `ffprobe` (or `avprobe`). -```py title="Adding connect events" -@bot.event -async def on_ready(): - await connect_nodes() # connect to the server +```py title="Initializing a FFmpegOpusAudio" +source = await discord.FFmpegOpusAudio.from_probe("audio.webm") +voice_client.play(source) +``` +`FFmpegOpusAudio.from_probe` also allows you to pass a custom `method`, used to determine the codec and +bitrate of the audio source. This can be a string defining whether to use the `native` probe +(`ffprobe` / `avprobe`), or `fallback`, which falls back to use `ffmpeg` / `avconv`. The latter +may be used by Windows users when none of `ffprobe` or `avprobe` is installed. -@bot.event -async def on_wavelink_node_ready(payload: wavelink.NodeReadyEventPayload): - # Everytime a node is successfully connected, we - # will print a message letting it know. - print(f"Node with ID {payload.session_id} has connected") - print(f"Resumed session: {payload.resumed}") +This can also take custom functions which take two parameters, the `source` and the `executable`, and +should return a tuple of `(codec, bitrate)`. + +```py title="Using FFmpegOpusAudio" +import discord + +bot = discord.Bot() +@bot.command() +async def play(ctx: discord.ApplicationContext) -> None: + if not ctx.voice_client: + await ctx.author.voice.channel.connect() -bot.run("token") + source = await discord.FFmpegOpusAudio.from_probe("audio.wav") + ctx.voice_client.play(source) + await ctx.respond("Playing FFmpeg Opus audio.") ``` -Congratulations! You have now implemented voice playing into your bot! Most bots and Discord API -wrappers don't have this as a feature, so this is quite an accomplishment. Thankfully, Pycord makes -it easy to make complex bots so that you can get even the most advanced of ideas down. +And... congratulations! You now know how to implement audio sources for voice channel playback into your +bot! Some audio sources may look more complex than others, but all of them keep an easy and simple design +so you can make complex bots and get even the most advanced of ideas down. !!! info "Related Topics" - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) + - [Advanced Audio Playback](./advanced-playing.md) From 9fe8b37c3c05b8b6a61582c748f27fdb4adb5a89 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:16:24 +0200 Subject: [PATCH 03/12] chore: delete old playing.md --- docs/voice/playing-old.md | 132 -------------------------------------- 1 file changed, 132 deletions(-) delete mode 100644 docs/voice/playing-old.md diff --git a/docs/voice/playing-old.md b/docs/voice/playing-old.md deleted file mode 100644 index 839ea115..00000000 --- a/docs/voice/playing-old.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Wavelink Audio Player ---- - -# About - -Pycord and Wavelink try to keep the playing of audio as simple and easy as possible, to keep making Discord -bots of any kind easy for all audiences. This guide provides simple and easy examples of using the -audio playing feature. - -For users that want extra examples, you can find some in Pycord's -[GitHub repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). - -## Starting out - -First you need to run a [Lavalink Server](https://github.com/freyacodes/Lavalink) to connect with. -There a multiple documentations to do this, so we are not covering that here. - -You also need to install the [wavelink](https://github.com/PythonistaGuild/Wavelink) library. - -```py title="Installing wavelink" -python3 -m pip install wavelink -``` - -You will now want to connect to your server via a node. - -```py title="Connect Node with Lavalink" -import discord -import wavelink - -bot = discord.Bot() - - -async def connect_nodes(): - """Connect to our Lavalink nodes.""" - await bot.wait_until_ready() # wait until the bot is ready - - nodes = [ - wavelink.Node( - identifier="Node1", # This identifier must be unique for all the nodes you are going to use - uri="http://0.0.0.0:443", # Protocol (http/s) is required, port must be 443 as it is the one lavalink uses - password="youshallnotpass", - ) - ] - - await wavelink.Pool.connect(nodes=nodes, client=bot) # Connect our nodes -``` - -
- -Now you are finished making your node! Next, you will want to: - -1. Making a play command -1. Adding connect events - -### Making a play command - -To make a play command, you will need to make a function to connect and play audio in a voice channel. - -```py title="Play Command Example" -import typing - - -@bot.slash_command(name="play") -async def play(ctx, search: str): - # First we may define our voice client, - # for this, we are going to use typing.cast() - # function just for the type checker know that - # `ctx.voice_client` is going to be from type - # `wavelink.Player` - vc = typing.cast(wavelink.Player, ctx.voice_client) - - if not vc: # We firstly check if there is a voice client - vc = await ctx.author.voice.channel.connect( - cls=wavelink.Player - ) # If there isn't, we connect it to the channel - - # Now we are going to check if the invoker of the command - # is in the same voice channel than the voice client, when defined. - # If not, we return an error message. - if ctx.author.voice.channel.id != vc.channel.id: - return await ctx.respond("You must be in the same voice channel as the bot.") - - # Now we search for the song. You can optionally - # pass the "source" keyword, of type "wavelink.TrackSource" - song = await wavelink.Playable.search(search) - - if not song: # In case the song is not found - return await ctx.respond("No song found.") # we return an error message - - await vc.play(song) # Else, we play it - await ctx.respond(f"Now playing: `{song.title}`") # and return a success message -``` - -``` -# link with playing-now-playing.json -``` - -
- -Now that you've done this, the only thing left to do is make your connect events. - -### Adding connect events - -The final step to this guide is connecting the node to your server when the bot goes online. - -To make it, you will want to do the following: - -```py title="Adding connect events" -@bot.event -async def on_ready(): - await connect_nodes() # connect to the server - - -@bot.event -async def on_wavelink_node_ready(payload: wavelink.NodeReadyEventPayload): - # Everytime a node is successfully connected, we - # will print a message letting it know. - print(f"Node with ID {payload.session_id} has connected") - print(f"Resumed session: {payload.resumed}") - - -bot.run("token") -``` - -Congratulations! You have now implemented voice playing into your bot! Most bots and Discord API -wrappers don't have this as a feature, so this is quite an accomplishment. Thankfully, Pycord makes -it easy to make complex bots so that you can get even the most advanced of ideas down. - -!!! info "Related Topics" - - - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) From d4b60375ecc01e5c8ab4a48b1b83c9745f2b1e61 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:21:28 +0200 Subject: [PATCH 04/12] fix: links --- docs/voice/advanced-playing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index 1b73084e..5ea61d18 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -224,4 +224,4 @@ Pycord makes it easy to make complex bots so that you can get the most advanced !!! info "Related Topics" - - [Rules and Common Practises](../getting-started/rules-and-common-practises.md) + - [Rules and Common Practices](../getting-started/rules-and-common-practices.md) From 79036de815cbe296c5d7836ffdc005cc2c4abda7 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:29:58 +0200 Subject: [PATCH 05/12] chore: rename & add missing pages in zensical.toml --- zensical.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zensical.toml b/zensical.toml index d3a7fecc..f9c2efc4 100644 --- a/zensical.toml +++ b/zensical.toml @@ -20,7 +20,7 @@ nav = [ { "Getting Started" = ["getting-started/index.md", { "Creating Your First Bot" = "getting-started/creating-your-first-bot.md" }, { "Rules and Common Practices" = "getting-started/rules-and-common-practices.md" }, { "More Features" = "getting-started/more-features.md" }, { "Hosting Your Pycord Bot" = "getting-started/hosting-your-bot.md" }] }, { "Interactions" = ["interactions/index.md", { "Application Commands" = [{ "Slash Commands" = "interactions/application-commands/slash-commands.md" }, { "Context Menus" = "interactions/application-commands/context-menus.md" }, { "Localizations" = "interactions/application-commands/localizations.md" }] }, { "UI Components" = [{ "Buttons" = "interactions/ui-components/buttons.md" }, { "Select Menus" = "interactions/ui-components/dropdowns.md" }, { "Modal Dialogs" = "interactions/ui-components/modal-dialogs.md" }] }] }, { "Extensions" = ["extensions/index.md", { "Commands" = [{ "Prefixed Commands" = "extensions/commands/prefixed-commands.md" }, { "Command Groups" = "extensions/commands/groups.md" }, { "Help Command" = "extensions/commands/help-command.md" }] }, { "Pages" = [{ "Paginator Basics" = "extensions/pages/paginator-basics.md" }, { "Paginator FAQ" = "extensions/pages/paginator-faq.md" }] }, { "Bridge" = "extensions/bridge.md" }, { "Tasks" = "extensions/tasks/tasks.md" }] }, - { "Voice" = ["voice/index.md", { "Wavelink Audio Player" = "voice/playing.md" }, { "Receiving Voice Samples" = "voice/receiving.md" }] }, + { "Voice" = ["voice/index.md", { "Playing Audio in Voice" = "voice/playing.md" }, { "Advanced Audio Playing" = "voice/advanced-playing.md" }, { "Receiving Voice Samples" = "voice/receiving.md" }] }, { "Popular Topics" = ["popular-topics/index.md", { "Cogs" = "popular-topics/cogs.md" }, { "Error Handling" = "popular-topics/error-handling.md" }, { "Intents" = "popular-topics/intents.md" }, { "Sharding" = "popular-topics/sharding.md" }, { "Subclassing Bots" = "popular-topics/subclassing-bots.md" }, { "Threads" = "popular-topics/threads.md" }] }, { "More" = ["more/index.md", { "Community Resources" = "more/community-resources.md" }, { "Contributing to the Guide" = "more/contributing.md" }, { "Installing Git" = "more/git.md" }, { "Virtual Environments" = "more/virtual-environments.md" }] }, ] From 7810944511fd7daa6d841813c46cf7952f0ca0d8 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 31 Aug 2026 22:59:40 +0200 Subject: [PATCH 06/12] chore: Add debug print statement in advanced-playing example Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/advanced-playing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index 5ea61d18..326c35fd 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -118,6 +118,7 @@ sl_client.create_node( @bot.listen() async def on_connect() -> None: await sl_client.start() # starts connection to the created nodes + print("SonoLink nodes connected successfully!") ```
From 319ad52df9b287c7b5e379ce2440fa31be7ad8b3 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 31 Aug 2026 22:59:53 +0200 Subject: [PATCH 07/12] fix: NameError Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/advanced-playing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index 326c35fd..b04069a9 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -191,7 +191,7 @@ async def play(ctx: discord.ApplicationContext, *, search: str) -> None: track = result.result # And finally... we play the track we obtained - await vc.play(song) + await vc.play(track) await ctx.respond(f"Now playing: `{track.title}`") ``` From 4c9dccbb2025082c83fcdb0da8f5ae34434cb0eb Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 31 Aug 2026 23:00:19 +0200 Subject: [PATCH 08/12] chore: remove duplicate code Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/advanced-playing.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index b04069a9..f2fd9e6e 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -206,10 +206,6 @@ The final step of this guide is connecting to the node to your server when the b To make it, you will want to do the following: ```py title="Adding connect events" -@bot.listen() -async def on_connect() -> None: - await sl_client.start() # Starting the client & connect all nodes - @bot.event async def on_sonolink_node_ready(payload: sonolink.gateway.ReadyEvent) -> None: print(f"Node with ID {payload.node.id!r} has connected!") From 1046f509f321bcfb34db8eb4d5f779529e21cb23 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 31 Aug 2026 23:00:38 +0200 Subject: [PATCH 09/12] chore: rename payload -> event Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/advanced-playing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index f2fd9e6e..be7ed186 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -207,9 +207,9 @@ To make it, you will want to do the following: ```py title="Adding connect events" @bot.event -async def on_sonolink_node_ready(payload: sonolink.gateway.ReadyEvent) -> None: - print(f"Node with ID {payload.node.id!r} has connected!") - print(f"Resumed session: {payload.resumed}") +async def on_sonolink_node_ready(event: sonolink.gateway.ReadyEvent) -> None: + print(f"Node with ID {event.node.id!r} has connected!") + print(f"Resumed session: {event.resumed}") bot.run("token") ``` From fd66ffba4c7215787891b6bfa11ec261cc0db007 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:41:16 +0200 Subject: [PATCH 10/12] =?UTF-8?q?chore:=20=E2=80=9CGithub=E2=80=9D=20->=20?= =?UTF-8?q?=E2=80=9CGitHub=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/playing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/voice/playing.md b/docs/voice/playing.md index 70f16e47..b63a822f 100644 --- a/docs/voice/playing.md +++ b/docs/voice/playing.md @@ -10,7 +10,7 @@ guide provides simple and easy examples of using the multiple ways the library a you to play audio. For users that want extra examples, you can find some in Pycord's -[Github repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). +[GitHub repository](https://github.com/Pycord-Development/pycord/blob/master/examples/). ## Starting out From de121e8dc39a99705b9c0dfeb3bd3a5917106241 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 9 Sep 2026 19:01:56 +0200 Subject: [PATCH 11/12] chore: update advanced-playing guide wording --- docs/voice/advanced-playing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index 5ea61d18..043e6a49 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -11,7 +11,7 @@ and offloading voice processing to standalone audio nodes, using Lavalink. ## Starting out -In the [previous guide](./playing.md), standard audio sources that Pycord provides were covered. While +[Playing Audio in Voice Channels](./playing.md) has covered Pycord-provided standard audio sources. While these built-in classes cover most general needs, [`discord.AudioSource`](https://docs.pycord.dev/en/stable/api/voice.html#discord.AudioSource) can be subclassed to create custom audio pipelines, synthesizers, or memory stream handlers. @@ -217,7 +217,7 @@ async def on_sonolink_node_ready(payload: sonolink.gateway.ReadyEvent) -> None: bot.run("token") ``` -Congratulations! You are now able to create custom Audio Sources for advanced playback, and offloading +Congratulations! You are now able to create custom audio sources for advanced playback, and offloading the playback to an external node using Lavalink and interacting with it using Sonolink! Most bots and Discord API wrappers don't have this as a feature, so this is quite an accomplishment. Thankfully, Pycord makes it easy to make complex bots so that you can get the most advanced of ideas down. From 979d2f2fe67c2d641ca07c1d87d84fe57ed3dbb6 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 9 Sep 2026 19:04:45 +0200 Subject: [PATCH 12/12] chore: update with suggestions - change default Lavalink port - rename `voice/playing.md` title to `Playing Audio in Voice Channels` Co-authored-by: Michael Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com> --- docs/voice/advanced-playing.md | 2 +- zensical.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/voice/advanced-playing.md b/docs/voice/advanced-playing.md index 5f2e2f37..38508919 100644 --- a/docs/voice/advanced-playing.md +++ b/docs/voice/advanced-playing.md @@ -111,7 +111,7 @@ sl_client = sonolink.Client(bot) sl_client.create_node( id="main-node", # the unique ID for the node being created - uri="http://0.0.0.0:443", # HTTP(S) protocol is required when passing URI + uri="http://0.0.0.0:2333", # HTTP(S) protocol is required when passing URI password="youshallnotpass" ) diff --git a/zensical.toml b/zensical.toml index f9c2efc4..e916096b 100644 --- a/zensical.toml +++ b/zensical.toml @@ -20,7 +20,7 @@ nav = [ { "Getting Started" = ["getting-started/index.md", { "Creating Your First Bot" = "getting-started/creating-your-first-bot.md" }, { "Rules and Common Practices" = "getting-started/rules-and-common-practices.md" }, { "More Features" = "getting-started/more-features.md" }, { "Hosting Your Pycord Bot" = "getting-started/hosting-your-bot.md" }] }, { "Interactions" = ["interactions/index.md", { "Application Commands" = [{ "Slash Commands" = "interactions/application-commands/slash-commands.md" }, { "Context Menus" = "interactions/application-commands/context-menus.md" }, { "Localizations" = "interactions/application-commands/localizations.md" }] }, { "UI Components" = [{ "Buttons" = "interactions/ui-components/buttons.md" }, { "Select Menus" = "interactions/ui-components/dropdowns.md" }, { "Modal Dialogs" = "interactions/ui-components/modal-dialogs.md" }] }] }, { "Extensions" = ["extensions/index.md", { "Commands" = [{ "Prefixed Commands" = "extensions/commands/prefixed-commands.md" }, { "Command Groups" = "extensions/commands/groups.md" }, { "Help Command" = "extensions/commands/help-command.md" }] }, { "Pages" = [{ "Paginator Basics" = "extensions/pages/paginator-basics.md" }, { "Paginator FAQ" = "extensions/pages/paginator-faq.md" }] }, { "Bridge" = "extensions/bridge.md" }, { "Tasks" = "extensions/tasks/tasks.md" }] }, - { "Voice" = ["voice/index.md", { "Playing Audio in Voice" = "voice/playing.md" }, { "Advanced Audio Playing" = "voice/advanced-playing.md" }, { "Receiving Voice Samples" = "voice/receiving.md" }] }, + { "Voice" = ["voice/index.md", { "Playing Audio in Voice Channels" = "voice/playing.md" }, { "Advanced Audio Playing" = "voice/advanced-playing.md" }, { "Receiving Voice Samples" = "voice/receiving.md" }] }, { "Popular Topics" = ["popular-topics/index.md", { "Cogs" = "popular-topics/cogs.md" }, { "Error Handling" = "popular-topics/error-handling.md" }, { "Intents" = "popular-topics/intents.md" }, { "Sharding" = "popular-topics/sharding.md" }, { "Subclassing Bots" = "popular-topics/subclassing-bots.md" }, { "Threads" = "popular-topics/threads.md" }] }, { "More" = ["more/index.md", { "Community Resources" = "more/community-resources.md" }, { "Contributing to the Guide" = "more/contributing.md" }, { "Installing Git" = "more/git.md" }, { "Virtual Environments" = "more/virtual-environments.md" }] }, ]