Discordライブラリのpycordでボイスチャンネルの録音
本稿は9月にQittaに投稿したものと同様のものです。
DiscordAPIには音声を録音する機能があります。
ですがDiscord.pyではプライバシーの面で実装されておらず、Discord.jsは頻繁に変更が施されるため、簡単に録音できる手段は確立されていませんでした。
そんな中で22年3月、Discord.pyの派生ライブラリ、pycordに録音機能が実装されました。
日本語での解説が見当たらなかったので、ここで説明します。
環境
python 3.8.6 64bit(ここに関しては3.10とかでも問題なし)
pip install discord
pip install git+https://github.com/Pycord-Development/pycord
pip install pydub
必ずDiscord.pyをインストールした後にpycordをインストールしましょう!!
また、音声を使用するのでffmpegをインストールすることを忘れずに。
サンプルコード
録音したファイルをDiscord上にアップロードする場合。
import discord
intents = discord.Intents().all()
bot = discord.Bot(intents=intents)
Token=""
@bot.command()
async def start_record(ctx:discord.ApplicationContext):
# コマンドを使用したユーザーのボイスチャンネルに接続
try:
vc = await ctx.author.voice.channel.connect()
await ctx.respond("録音開始...")
except AttributeError:
await ctx.respond("ボイスチャンネルに入ってください。")
return
# 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)
async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
# 録音したユーザーの音声を取り出す
recorded_users = [
f"<@{user_id}>"
for user_id, audio in sink.audio_data.items()
]
# discordにファイル形式で送信。拡張子は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"録音終了! 音声ファイルはこちら! {', '.join(recorded_users)}.", files=files)
@bot.command()
async def stop_recording(ctx:discord.ApplicationContext):
# 録音停止
ctx.voice_client.stop_recording()
await ctx.respond("録音終了!")
await ctx.voice_client.disconnect()
bot.run(Token)
ローカルに保存する場合。
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):
# コマンドを使用したユーザーのボイスチャンネルに接続
try:
vc = await ctx.author.voice.channel.connect()
await ctx.respond("録音開始...")
except AttributeError:
await ctx.respond("ボイスチャンネルに入ってください。")
return
# 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)
@bot.slash_command()
async def stop_recording(ctx:discord.ApplicationContext):
# 録音停止
ctx.voice_client.stop_recording()
await ctx.respond("録音終了!")
await ctx.voice_client.disconnect()
# 録音終了時に呼び出される関数
async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
# 録音したユーザーの音声を取り出す
for user_id, audio in sink.audio_data.items():
# mp3ファイルとして書き込み。その後wavファイルに変換。
song = AudioSegment.from_file(audio.file, format="mp3")
song.export(f"./{user_id}.wav", format='wav')
bot.run(Token)
ポイントとなる点はこちら。
# 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)
これで録音が開始されます。
discord.sinks.MP3Sinkでmp3形式で録音することを指定しています。
これをdiscord.sinks.WaveSinkにするとwavファイル、discord.sinks.M4ASinkにするとm4aファイルとして録音を開始します。
また、finished_callbackは録音終了時に実行される関数でsink(録音データ)とctx(スラッシュコマンドのデータ)を引数として持ってきます。
sinkの中にはユーザーid毎に録音内容のバイナリデータが入っていて、forで一つ一つファイルに変換しています。
その他
録音開始のコマンドを打っても、何か発言しなければ録音は開始されません。
例えばa,b,cの3人がボイスチャンネルにいたとします。
録音を開始し、aだけが発言した後終了するとaの録音データのみが返却されます。
ローカルに音声を保存する場合、mp3は問題ないのですが、なぜかwavで保存するとファイルが壊れます。
原因についてよくわからなかったのでサンプルコードではmp3にした後wavに変換しています。
(2022/09/26 追記)
また、ステージチャンネルでは録音できないようです。
最後に
初投稿なので色々雑な部分が多いですが、お役に立ったら幸いです。
Discussion