Discord.py 歌曲循环功能异常:循环时机错误且无法取消
问题分析与修复方案
问题概述
我尝试制作一个Discord机器人命令,实现歌曲播放完毕后自动循环,但目前存在三个核心问题:
- 循环会在1-1.5分钟后触发,而非等待歌曲播放完毕
- 开启循环后无法取消,触发
IndexError: list index out of range - 出现FFmpeg读取错误,导致播放提前终止
原代码如下:
@client.command() @commands.is_owner() async def loop(ctx): vc = ctx.voice_client current_song = queuee[ctx.guild.id][0] song_name = songs[ctx.guild.id][0] guild_id = ctx.guild.id if guild_id not in loop_status: loop_status[guild_id] = False if not loop_status[guild_id]: loop_status[guild_id] = True await ctx.send("Looping current song.") loop = True else: loop_status[guild_id] = False loop = False await ctx.send("Unlooping current song.")
async def check_queue(ctx): global loop if ctx.voice_client.is_connected(): if queuee[ctx.guild.id]: if not ctx.voice_client.is_playing(): if loop == False: queuee[ctx.guild.id].pop(0) songs[ctx.guild.id].pop(0) vc = ctx.voice_client player = discord.FFmpegPCMAudio(queuee[ctx.guild.id][0], **ffmpeg_options) vc.play(player) return else: vc = ctx.voice_client player = discord.FFmpegPCMAudio(queuee[ctx.guild.id][0], **ffmpeg_options) vc.play(player) return else: await asyncio.sleep(30) await vc.disconnect() else: return
触发的错误信息:
Traceback (most recent call last): File "/home/container/.local/lib/python3.9/site-packages/discord/client.py", line 409, in _run_event await coro(*args, **kwargs) File "/home/container/main.py", line 1414, in on_command_error raise error File "/home/container/.local/lib/python3.9/site-packages/discord/ext/commands/core.py", line 190, in wrapped ret = await coro(*args, **kwargs) File "/home/container/main.py", line 570, in loop current_song = queuee[ctx.guild.id][0] IndexError: list index out of range
[tls @ 0x564839bd9b40] Error in the pull function. [matroska,webm @ 0x564839bd6900] Read error [tls @ 0x564839bd9b40] The specified session has been invalidated for some reason. Last message repeated 1 times
问题根源
- 循环时机错误:原代码通过轮询
is_playing()状态判断播放完成,轮询间隔过短或状态检测不准确,导致提前触发循环。 - 索引越界错误:
loop命令中强制读取队列第一个元素,取消循环时若队列为空则触发索引错误;同时全局loop变量无法支持多服务器场景。 - FFmpeg读取错误:网络不稳定导致流媒体连接中断,未配置重连参数。
修复方案
1. 优化FFmpeg参数(解决网络读取错误)
添加重连配置,缓解网络波动导致的播放中断:
ffmpeg_options = { 'options': '-vn', 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5' }
2. 修复loop命令(解决索引越界)
移除无用的队列读取操作,改用服务器独立的状态存储:
@client.command() @commands.is_owner() async def loop(ctx): vc = ctx.voice_client if not vc or not vc.is_connected(): await ctx.send("未连接到语音频道!") return guild_id = ctx.guild.id # 初始化当前服务器的循环状态 if guild_id not in loop_status: loop_status[guild_id] = False # 切换循环状态 loop_status[guild_id] = not loop_status[guild_id] if loop_status[guild_id]: await ctx.send("已开启当前歌曲循环。") else: await ctx.send("已关闭歌曲循环。")
3. 重构播放逻辑(解决循环时机错误)
使用Discord.py原生的after回调,精准检测歌曲播放完成事件:
def play_next(ctx): guild_id = ctx.guild.id vc = ctx.voice_client if not vc or not vc.is_connected(): return # 检查队列是否为空 if not queuee.get(guild_id, []): asyncio.create_task(disconnect_after_idle(vc)) return # 根据循环状态处理播放逻辑 if loop_status.get(guild_id, False): # 循环:直接重播当前歌曲 player = discord.FFmpegPCMAudio(queuee[guild_id][0], **ffmpeg_options) vc.play(player, after=lambda e: play_next(ctx)) else: # 不循环:播放下一首 queuee[guild_id].pop(0) songs[guild_id].pop(0) if queuee[guild_id]: player = discord.FFmpegPCMAudio(queuee[guild_id][0], **ffmpeg_options) vc.play(player, after=lambda e: play_next(ctx)) else: asyncio.create_task(disconnect_after_idle(vc)) async def disconnect_after_idle(vc): await asyncio.sleep(30) if vc.is_connected() and not vc.is_playing(): await vc.disconnect()
4. 调用方式修改
在你的play命令中,启动播放时需要绑定after回调:
# 示例play命令中的播放逻辑 player = discord.FFmpegPCMAudio(song_url, **ffmpeg_options) vc.play(player, after=lambda e: play_next(ctx))
修复说明
- 移除了
loop命令中危险的队列强制读取操作,彻底避免索引越界 - 用
loop_status字典替代全局loop变量,支持多服务器独立的循环状态 - 通过
after回调实现精准的播放完成检测,解决提前循环问题 - 添加FFmpeg重连参数,缓解网络导致的播放中断
内容的提问来源于stack exchange,提问作者TheRealKurumi
相关产品推荐
相关产品推荐

