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

Discord.py音乐机器人如何实现队列歌曲播放完成后自动依次播放下一首

实现思路

利用discord.py中VoiceClient.play()方法的after回调参数实现自动切歌:该参数会在歌曲播放完成(或播放出错)时自动执行指定函数,我们可以在该回调中触发下一首歌曲的加载和播放逻辑。


完整修改代码

1. 存储结构调整

首先将原servers字典调整为多服务器隔离的嵌套结构,避免跨服务器数据冲突:

# 全局servers初始化示例,可兼容原有逻辑
servers = {}
# 每个新服务器首次使用时自动初始化结构:
# servers[guild_id] = {"queue": {}, "cur_id": None, "ctx": None}

2. 命令逻辑修改

import asyncio

@commands.command(pass_context=True, aliases=["aq"])
async def add_queue(self, ctx, *, url):
    guild_id = ctx.message.guild.id
    if guild_id not in servers:
        servers[guild_id] = {"queue": {}, "cur_id": None, "ctx": ctx}
    server_data = servers[guild_id]
    try: 
        server_data["queue"][len(server_data["queue"])] = url 
        user = ctx.message.author.mention
        await ctx.send(f'`{url}` 已被 {user} 添加到播放队列!')
    except:
        await ctx.send(f"无法将 {url} 添加到播放队列!")

@commands.command(pass_context=True, aliases=["qp"], case_insensitive=True)
async def pq(self,ctx, number):
    guild_id = ctx.message.guild.id
    if guild_id not in servers:
        await ctx.send("当前服务器无可用播放队列!")
        return
    server_data = servers[guild_id]
    number = int(number)
    if number in server_data["queue"]:
        source = server_data["queue"][number]
        server_data["cur_id"] = number
        server_data["ctx"] = ctx
        await ctx.send(f"**当前播放:** {source}")
        await self.transformer(ctx, source)
    else:
        await ctx.send("队列中不存在对应序号的歌曲!")

3. 播放逻辑+自动切歌实现

async def transformer(self,ctx, url):
    player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
    if not ctx.message.author.voice:
        await ctx.send("你未连接到任何语音频道!")
        return
    elif ctx.voice_client and ctx.voice_client.is_connected():
        print('已连接到语音频道')
        pass
    else:
        channel = ctx.message.author.voice.channel
        await ctx.send(f'已连接到 `{channel}`')
        await channel.connect()
    
    # 播放完成回调函数
    def after_callback(error):
        if error:
            print(f'播放出错: {error}')
        # 将异步切歌逻辑提交到事件循环执行
        fut = asyncio.run_coroutine_threadsafe(self.play_next(ctx.guild.id), self.bot.loop)
        try:
            fut.result()
        except Exception as e:
            print(f'切歌出错: {e}')
    
    ctx.voice_client.play(player, after=after_callback)

# 新增自动切歌方法
async def play_next(self, guild_id):
    if guild_id not in servers:
        return
    server_data = servers[guild_id]
    ctx = server_data["ctx"]
    next_id = server_data["cur_id"] + 1
    if next_id in server_data["queue"]:
        source = server_data["queue"][next_id]
        server_data["cur_id"] = next_id
        await ctx.send(f"**自动播放下一首:** {source}")
        await self.transformer(ctx, source)
    else:
        await ctx.send("队列所有歌曲已播放完毕!")
        server_data["cur_id"] = None
        # 如需播放完自动断开语音连接,可取消下方注释
        # if ctx.voice_client:
        #     await ctx.voice_client.disconnect()

可选优化

如果需要支持队列插入、删除等操作,建议将队列存储格式从字典改为列表,用下标对应歌曲序号,操作效率更高。

内容的提问来源于stack exchange,提问作者xBatmanx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 02:15:05