Discord.Py机器人播放多首歌曲时提示已连接语音频道错误问题
错误触发原因
- 核心报错原因:你每次调用
play命令都会直接执行await voiceChannel.connect(),没有先判断机器人是否已经接入当前服务器的语音频道,重复发起连接请求就会抛出ClientException: Already connected to a voice channel异常。 - 其他影响功能的逻辑缺陷:
- 使用了阻塞式的
time.sleep(5),会卡住Discord.py的异步事件循环,导致其他命令无法响应,同时你没有导入time模块,运行到这里本身就会触发变量未定义错误 - 重复编写了两次文件重命名、调用
voice.play的代码,属于冗余逻辑,会导致播放逻辑异常 - 预先定义的
songs异步队列、play_next_song事件完全没有被使用,反而代码里直接引用了未定义的song_queue、song变量,运行到对应分支时会触发报错 - 用单文件
song.mp3覆盖存储歌曲的方式,本身不支持队列播放,每次下载都会覆盖上一首的音频文件
- 使用了阻塞式的
修复后的实现代码
import discord from discord.ext import commands import youtube_dl import os import asyncio # 注意现在Discord.py需要手动开启Intents才能正常获取成员、语音状态等信息,需要到Discord开发者后台开启对应权限 client = commands.Bot(command_prefix="^", intents=discord.Intents.all()) @client.command() async def play(ctx, url: str): song_there = os.path.isfile("song.mp3") try: if song_there: os.remove("song.mp3") except PermissionError: await ctx.send("请等待当前播放的音乐结束,或者使用stop命令") return # 先判断是否已经连接语音频道,避免重复连接 voice = discord.utils.get(client.voice_clients, guild=ctx.guild) if not voice or not voice.is_connected(): if not ctx.author.voice: await ctx.send("你需要先加入一个语音频道") return voiceChannel = ctx.author.voice.channel voice = await voiceChannel.connect() ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'quiet': True } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) for file in os.listdir("./"): if file.endswith(".mp3") and file != "song.mp3": os.rename(file, "song.mp3") # 播放前先停止当前正在播放的音频 if voice.is_playing(): voice.stop() voice.play(discord.FFmpegPCMAudio("song.mp3")) # 异步等待播放结束,避免阻塞事件循环 while voice.is_playing() or voice.is_paused(): await asyncio.sleep(2) # 播放结束后清理文件 song_there = os.path.isfile("song.mp3") if song_there: os.remove("song.mp3") @client.command() async def leave(ctx): voice = discord.utils.get(client.voice_clients, guild=ctx.guild) if voice and voice.is_connected(): await voice.disconnect() # 离开后清理残留文件 if os.path.isfile("song.mp3"): os.remove("song.mp3") else: await ctx.send("机器人当前没有连接到任何语音频道") @client.command() async def pause(ctx): voice = discord.utils.get(client.voice_clients, guild=ctx.guild) if voice and voice.is_playing(): voice.pause() else: await ctx.send("当前没有正在播放的音频") @client.command() async def resume(ctx): voice = discord.utils.get(client.voice_clients, guild=ctx.guild) if voice and voice.is_paused(): voice.resume() else: await ctx.send("当前没有暂停的音频") @client.command() async def stop(ctx): voice = discord.utils.get(client.voice_clients, guild=ctx.guild) if voice and (voice.is_playing() or voice.is_paused()): voice.stop() # 停止后清理文件 if os.path.isfile("song.mp3"): os.remove("song.mp3") else: await ctx.send("当前没有正在播放的音频") client.run('你的机器人Token')
额外说明
如果需要支持队列播放,你可以把预先定义的songs异步队列利用起来,每次调用play命令时先把歌曲URL加入队列,单独起一个异步任务循环消费队列里的歌曲即可,不需要每次都在play命令里处理下载播放逻辑。
内容的提问来源于stack exchange,提问作者datanks
相关产品推荐
相关产品推荐

