You need to enable JavaScript to run this app. 如何修复Discord音乐Bot在歌曲未播放完成前随机停止播放的问题
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Discord音乐Bot在歌曲未播放完成前随机停止播放的问题

Discord音乐Bot播放随机中断故障修复方案

故障根因

  • 音频源对象被垃圾回收随机销毁:play方法内创建的player为局部变量,方法执行后无外部引用持有,Python垃圾回收机制会在不确定的时间回收该对象,直接导致音频流中断,表现为播放大半后无报错随机停止。
  • FFmpeg无拉流容错能力:当前FFmpeg配置仅添加-vn参数,未配置网络重连、超时逻辑,Heroku出口网络波动、YouTube临时限流时,FFmpeg收到流EOF会直接终止播放,无重试机制。
  • 临时流URL无缓冲机制:当前stream=True模式下直接将YouTube返回的带签名临时URL喂给FFmpeg,未做预缓冲,加上youtube_dl已停止维护,对YouTube最新的流校验规则兼容性不足,拉流到中段容易出现授权失效断连。
  • 异常回调无兜底处理:当前播放结束的回调仅打印错误日志,未对非用户主动操作触发的中断做恢复处理,只要出现网络波动断连就直接停止,无自动续播逻辑。

修复实现步骤

1. 全局持有当前音频源,避免被垃圾回收

在Music cog的初始化方法中新增实例属性,保存当前播放的播放器对象:

class Music(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        # 新增:全局持有当前播放器,避免局部变量被GC回收
        self.current_player = None

修改play方法中的赋值逻辑,将创建的播放器实例存入全局属性:

@commands.command(description="streams music")
async def play(self, ctx, *, url):
    async with ctx.typing():
        player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
        # 新增:将播放器赋值给全局属性
        self.current_player = player
        ctx.voice_client.play(
            player,
            after=lambda e: self.bot.loop.create_task(self._after_play(ctx, e))
        )
    embed = discord.Embed(title="Now playing", description=f"[{player.title}]({player.url}) [{ctx.author.mention}]")
    await ctx.send(embed=embed)

2. 为FFmpeg配置拉流容错参数

修改ffmpeg_options,补充网络重连、超时相关参数,适配Heroku的网络环境:

ffmpeg_options = {
    'options': '-vn -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 -rw_timeout 10000000'
}

参数作用:

  • -reconnect 1:开启网络断连自动重连
  • -reconnect_streamed 1:针对音频流场景开启重连
  • -reconnect_delay_max 5:单次重连最大等待时长5秒
  • -rw_timeout 10000000:设置10秒网络读写超时,避免无限挂起

3. 优化youtube_dl配置,提升流拉取稳定性

补充ytdl_format_options的相关配置,降低YouTube侧的断连概率:

ytdl_format_options = {
    'format': 'bestaudio/best',
    'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
    'restrictfilenames': True,
    'noplaylist': True,
    'nocheckcertificate': True,
    'ignoreerrors': False,
    'logtostderr': False,
    'quiet': True,
    'no_warnings': True,
    'default_search': 'auto',
    'source_address': '0.0.0.0',
    # 新增流稳定配置
    'geo_bypass': True,
    'extractor_retries': 3,
    'fragment_retries': 10,
    'buffersize': 16777216
}

4. 增加播放异常自动续播逻辑

在Music cog中新增异常处理方法,当出现非用户主动停止的播放中断时,自动尝试重新拉取资源续播:

async def _after_play(self, ctx, error):
    if not error:
        return
    print(f"播放中断错误: {error}")
    # 等待2秒规避临时网络波动
    await asyncio.sleep(2)
    # 校验Bot仍在语音频道、未处于播放状态时,自动重拉流续播
    if ctx.voice_client and ctx.voice_client.is_connected() and not ctx.voice_client.is_playing():
        try:
            new_player = await YTDLSource.from_url(
                self.current_player.data['webpage_url'],
                loop=self.bot.loop,
                stream=True
            )
            self.current_player = new_player
            ctx.voice_client.play(
                new_player,
                after=lambda e: self.bot.loop.create_task(self._after_play(ctx, e))
            )
        except Exception as e:
            print(f"自动续播失败: {e}")

可选优化

将已停止维护的youtube_dl替换为yt-dlp,仅需修改导入语句即可无缝适配现有代码,可大幅提升对YouTube最新流规则的兼容性,从根源减少拉流中断概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.05 21:15:09