You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:17:28