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

Discord私人音乐机器人使用ytdl搜索歌曲时出现KeyError: 'formats'问题求助

问题修复方案

嗨,我来帮你搞定这两个问题,你的代码目前有两个核心问题导致了异常:

1. 多关键词搜索只识别第一个词

你的play命令参数定义是async def play(ctx, url):,Discord.py的命令参数默认是按空格分割的单个位置参数,所以当你输入-play Rick Astley - Never Gonna Give You Up时,url只会拿到第一个词"Rick",剩下的参数会直接被忽略。

修复方法:
把参数改成async def play(ctx, *, url):,这里的*表示将所有后续输入作为一个完整的字符串传递给url参数,这样就能正确接收完整的歌曲名称了。

2. 搜索歌曲时抛出KeyError: 'formats'

当你使用搜索关键词而非直接视频URL时,youtube-dl返回的info结构和解析单个URL不一样:搜索结果会被包装成一个播放列表,包含entries字段,真正的视频信息藏在这个列表里,而不是直接在顶层有formats字段。

修复方法:
在获取formats之前,先检查info中是否存在entries,如果存在就取第一个条目作为实际的视频信息;同时增加基础的存在性检查,避免因字段缺失崩溃:

with youtube_dl.YoutubeDL(ydl_opts) as ydl:
    info = ydl.extract_info(url, download=False)
    # 处理搜索结果的播放列表结构
    if 'entries' in info:
        info = info['entries'][0]
    # 确保formats存在再访问
    if 'formats' in info:
        url2 = info['formats'][0]['url']
        source = await discord.FFmpegOpusAudio.from_probe(url2, **ffmpeg_opts)
        vc.play(source)
    else:
        await ctx.send("抱歉,找不到可播放的音频格式!")

另外,我还建议加上一些基础的错误处理,比如用户不在语音频道、搜索无结果的情况,避免机器人莫名崩溃。

完整修改后的代码

@client.command()
async def play(ctx, *, url):
    # 检查用户是否在语音频道
    if not ctx.author.voice:
        await ctx.send("你需要先加入一个语音频道才能播放音乐!")
        return
    
    channel = ctx.author.voice.channel
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    
    if voice and voice.is_connected():
        # 如果已经连接,自动切换到用户所在频道(可选,可根据需求删除)
        await voice.move_to(channel)
    else:
        voice = await channel.connect()
    
    ffmpeg_opts = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
    ydl_opts = {'format': "bestaudio/best", 'default_search': 'auto'}
    vc = ctx.voice_client
    
    try:
        with youtube_dl.YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(url, download=False)
            # 处理搜索结果的播放列表结构
            if 'entries' in info:
                info = info['entries'][0]
            # 检查formats是否存在
            if not 'formats' in info:
                await ctx.send("无法获取音频格式,请尝试其他关键词或URL!")
                return
            url2 = info['formats'][0]['url']
            source = await discord.FFmpegOpusAudio.from_probe(url2, **ffmpeg_opts)
            vc.play(source)
            await ctx.send(f"正在播放:{info.get('title', '未知歌曲')}")
    except Exception as e:
        await ctx.send(f"播放出错了:{str(e)}")
        print(f"Error: {e}")

这个修改后的代码解决了你的两个核心问题,还增加了用户提示和错误捕获,让机器人更稳定友好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:02:36