如何让Python Discord音乐Bot代码等待音频播放完成后执行?
解决Discord.py音乐Bot队列等待播放结束的问题
核心问题分析
你的代码存在几个关键问题:
- 使用
time.sleep()会阻塞异步事件循环,导致整个Bot卡死,无法处理其他指令或事件 return url_final_u and title_u逻辑错误,会返回布尔值而非目标数据,应返回元组- 等待逻辑仅执行一次检查,未持续监听播放状态
- 队列触发逻辑不合理,不应在
/play命令内等待,而是应该在播放结束后自动触发下一首
正确实现方案
1. 替换同步阻塞函数,使用异步等待
将time.sleep()替换为asyncio.sleep(),避免阻塞事件循环:
import asyncio async def wait_for_playback_end(self, vc): # 持续检查直到播放停止,每秒检查一次 while vc.is_playing(): await asyncio.sleep(1)
2. 重构队列与播放逻辑(推荐方案)
利用vc.play()的after回调,实现播放结束后自动处理队列,这是discord.py官方推荐的方式:
# 播放音乐函数,播放结束后触发队列处理 async def play_music(self, ctx, url, title): vc = ctx.voice_client # 替换为你实际获取音频源的逻辑(如FFmpegPCMAudio) source = await self.get_audio_source(url) await ctx.send(f"正在播放: {title}") # 播放结束后调用队列处理函数 vc.play(source, after=lambda e: asyncio.run_coroutine_threadsafe(self.handle_queue(ctx), self.bot.loop)) # 队列处理函数,自动播放下一首 async def handle_queue(self, ctx): vc = ctx.voice_client if len(self.queue_list) > 0: # 取出队列首元素(FIFO) next_url, next_title = self.queue_list.pop(0) await self.play_music(ctx, next_url, next_title)
3. 修改/play命令逻辑
@commands.command() async def play(self, ctx, *, query): # 替换为你实际获取歌曲URL和标题的逻辑(如使用youtube-dl) url_final_u, title_u = await self.get_song_info(query) vc = ctx.voice_client # 处理Bot未加入语音频道的情况 if not vc: if not ctx.author.voice: await ctx.send("你需要先加入语音频道!") return vc = await ctx.author.voice.channel.connect() if vc.is_playing(): # 正在播放则加入队列,存储(url, title)元组 self.queue_list.append((url_final_u, title_u)) await ctx.send(f"{title_u} 已加入队列!") else: # 无播放任务则直接开始播放 await self.play_music(ctx, url_final_u, title_u)
4. 若坚持使用原等待函数的修正版
async def wait_for_url(self, ctx, source, title): vc = ctx.voice_client if vc.is_playing(): print("@等待播放结束...@") # 异步等待播放停止 await self.wait_for_playback_end(vc) print("@播放已结束@") # 返回元组而非布尔值 return (source, title)
调用时需正确接收返回值:
source, title = await self.wait_for_url(ctx, url_final_u, title_u)
关键注意事项
- 永远不要在异步函数中使用同步阻塞函数(如
time.sleep()、os.wait()),必须使用对应的异步版本 - 队列建议存储
(URL, 标题)元组,而非单独的URL,方便后续播放时发送提示 - 使用
vc.play()的after回调是处理播放结束最可靠的方式,比循环检查vc.is_playing()更高效
内容的提问来源于stack exchange,提问作者Hackerman123
相关产品推荐
相关产品推荐

