iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📝

Recording Discord Voice Channels with Pycord

に公開

This article is the same as the one I posted on Qiita in September.

The Discord API has a feature for recording audio.
However, it hasn't been implemented in Discord.py due to privacy concerns, and since Discord.js undergoes frequent changes, a simple method for recording hadn't been well-established.

In this context, in March 2022, a recording feature was implemented in Pycord, a fork of the Discord.py library.
Since I couldn't find any explanations in Japanese, I will explain it here.

Environment

python 3.8.6 64bit (version 3.10 or others should also be fine)

pip install
pip install discord
pip install git+https://github.com/Pycord-Development/pycord
pip install pydub

Be sure to install Pycord after installing Discord.py!!
Also, don't forget to install ffmpeg as it handles audio.

Sample Code

Uploading recorded files to Discord.

import discord

intents = discord.Intents().all()
bot = discord.Bot(intents=intents)
Token=""

@bot.command()
async def start_record(ctx:discord.ApplicationContext):
    # Connect to the voice channel of the user who used the command
    try:
       vc = await ctx.author.voice.channel.connect()
       await ctx.respond("Starting recording...")
    except AttributeError:
       await ctx.respond("Please join a voice channel.")
       return
        
    # Start recording. It returns as mp3. For some reason, wav gets corrupted.
    ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)


async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
    # Extract the audio of the recorded users
    recorded_users = [
        f"<@{user_id}>"
        for user_id, audio in sink.audio_data.items()
    ]
    # Send to Discord as files. The extension is mp3.
    files = [discord.File(audio.file, f"{user_id}.{sink.encoding}") for user_id, audio in sink.audio_data.items()]
    await ctx.channel.send(f"Recording finished! Here are the audio files! {', '.join(recorded_users)}.", files=files) 

@bot.command()
async def stop_recording(ctx:discord.ApplicationContext):
    # Stop recording
    ctx.voice_client.stop_recording()
    await ctx.respond("Recording finished!")
    await ctx.voice_client.disconnect()


bot.run(Token)

Saving to local.

import discord
from pydub import AudioSegment

intents = discord.Intents().all()
bot = discord.Bot(intents=intents)

Token=""

@bot.slash_command()
async def start_record(ctx:discord.ApplicationContext):

        # Connect to the voice channel of the user who used the command
        try:
            vc = await ctx.author.voice.channel.connect()
            await ctx.respond("Starting recording...")
        except AttributeError:
            await ctx.respond("Please join a voice channel.")
            return
        
        # Start recording. It returns as mp3. For some reason, wav gets corrupted.
        ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)

@bot.slash_command()
async def stop_recording(ctx:discord.ApplicationContext):
        # Stop recording
        ctx.voice_client.stop_recording() 
        await ctx.respond("Recording finished!")
        await ctx.voice_client.disconnect()

# Function called when recording finishes
async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
    # Extract the audio of the recorded users
    for user_id, audio in sink.audio_data.items():
        # Write as an mp3 file. Then convert to a wav file.
        song = AudioSegment.from_file(audio.file, format="mp3")
        song.export(f"./{user_id}.wav", format='wav')


bot.run(Token)

Here are the key points.

# Start recording. It returns as mp3. For some reason, wav gets corrupted.
ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)

This starts the recording.
discord.sinks.MP3Sink specifies that the recording will be in mp3 format.
Changing this to discord.sinks.WaveSink will record as a wav file, and discord.sinks.M4ASink will record as an m4a file.

Also, finished_callback is the function executed when the recording stops, which takes sink (the recorded data) and ctx (slash command data) as arguments.

Inside sink, there is binary data of the recorded content for each user ID, and it iterates through them using a for loop to convert each one into a file.

Miscellaneous

Even if you run the command to start recording, recording will not actually begin until someone speaks.

For example, suppose there are three people—A, B, and C—in a voice channel. If you start recording and then stop after only A has spoken, only A's recording data will be returned.

When saving audio locally, MP3 works fine, but for some reason, the file becomes corrupted when saved as WAV.

I wasn't sure about the cause, so in the sample code, I converted it to WAV after first saving it as MP3.

(Updated on 2022/09/26)
Additionally, it seems that recording is not possible in Stage Channels.

Finally

Since this is my first post, there are many rough parts, but I hope it helps.

References

stackoverflow-discord-receive-audio

Discussion