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

Discord.py音乐机器人添加歌曲至队列或查看队列时触发NameError问题求助

解决Discord.py音乐机器人的song_queue未定义错误

兄弟,我仔细看了你的代码和报错信息,这个NameError的问题根源很清晰:你没有正确在类中管理song_queue队列变量,导致其他方法无法访问它。下面一步步给你讲怎么修复:

错误根源分析

  1. song_queue是局部变量,不是实例属性:在MusicPlayer类的__init__里,你定义的song_queue = []只是局部变量,类里的其他方法(比如play、queue)根本访问不到它。
  2. 重复定义内部函数导致作用域混乱:你在play命令里又重新定义了play_song和search_song,不仅重复代码,还让内部函数更难访问类的变量。
  3. 未区分不同服务器的队列:如果多个服务器同时使用机器人,共用一个队列会导致混乱,最好用字典按服务器ID存储各自的队列。

具体修改步骤

1. 初始化实例级别的队列字典

修改__init__方法,把song_queue变成类的实例属性,用字典存储每个服务器的独立队列:

class MusicPlayer(commands.Cog):
    def __init__(self, cm):
        self.cm = cm
        # 用字典存储每个服务器的队列,key是服务器ID,value是队列列表
        self.song_queue = {}

2. 修复所有类方法的定义(添加self)

类里的所有方法(包括命令方法)都要把self作为第一个参数,否则无法访问实例属性。比如join命令要改成这样:

@cm.command()
async def join(self, ctx):  # 必须加上self
    if ctx.author.voice is None:
        return await ctx.send("Bạn không ở trong 1 voice channel")
    if ctx.voice_client is not None:
        await ctx.voice_client.disconnect()
    await ctx.author.voice.channel.connect()

同理,leave、skip、pause、resume、search这些命令都要加上self作为第一个参数。

3. 修复search_song和play_song为实例方法

把这两个方法移到play命令外面,改成类的实例方法,并且用self.cm访问bot实例:

async def search_song(self, amount, song, get_url=False):
    info = await self.cm.loop.run_in_executor(
        None, 
        lambda: youtube_dl.YoutubeDL({"format": "bestaudio", "quiet": True}).extract_info(
            f'ytsearch{amount}:{song}', 
            download=False, 
            ie_key="YoutubeSearch"
        )
    )
    if len(info['entries']) == 0:
        return None
    return [entry['webpage_url'] for entry in info['entries']] if get_url else info

async def play_song(self, ctx, song):
    url = pafy.new(song).getbestaudio().url
    # 修复之前的executable拼写错误
    ctx.voice_client.play(
        discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url, executable="C:\\ffmpeg-5.0-essentials_build\\bin\\ffmpeg.exe")),
        after=lambda error: self.cm.loop.create_task(self.check_queue(ctx))
    )
    ctx.voice_client.source.volume = 0.5

4. 修复check_queue方法

改成实例方法,并且获取当前服务器的专属队列:

async def check_queue(self, ctx):
    # 获取当前服务器的队列,不存在则返回空列表
    guild_queue = self.song_queue.get(ctx.guild.id, [])
    if len(guild_queue) > 0:
        ctx.voice_client.stop()
        await self.play_song(ctx, guild_queue[0])
        # 更新队列,移除已播放的歌曲
        self.song_queue[ctx.guild.id] = guild_queue[1:]

5. 修复play命令

去掉内部重复定义的函数,直接调用类的方法,正确访问当前服务器的队列:

@cm.command()
async def play(self, ctx, *, song=None):
    if song is None:
        return await ctx.send("Bạn phải chọn 1 bài để phát")
    if ctx.voice_client is None:
        return await ctx.send("Bot chưa trong 1 voice channel")
    
    # 如果不是YouTube链接,先执行搜索
    if not ("youtube.com/watch" in song or "youtu.be" in song):
        await ctx.send("Đang search, vui lòng đợi")
        result = await self.search_song(1, song, get_url=True)
        if result is None:
            return await ctx.send("Không thể phát bài hát này")
        song = result[0]
    
    # 初始化当前服务器的队列(如果还未创建)
    if ctx.guild.id not in self.song_queue:
        self.song_queue[ctx.guild.id] = []
    
    # 如果当前正在播放,将歌曲添加到队列
    if ctx.voice_client.source is not None:
        self.song_queue[ctx.guild.id].append(song)
        queue_len = len(self.song_queue[ctx.guild.id])
        return await ctx.send(f"Bài hát đã được thêm vào queue ở vị trí: {queue_len}.")
    
    # 直接播放歌曲
    await self.play_song(ctx, song)
    await ctx.send(f"Đang phát: {song}")

6. 修复queue命令

正确获取当前服务器的队列并展示:

@cm.command()
async def queue(self, ctx):
    guild_queue = self.song_queue.get(ctx.guild.id, [])
    if len(guild_queue) == 0:
        return await ctx.send("Không có bài hát nào trong queue")
    
    embed = discord.Embed(title="Quê-ue", description="", colour=discord.Colour.blue())
    for idx, url in enumerate(guild_queue, 1):
        embed.description += f"{idx}) {url}\n"
    embed.set_footer(text="Bot thuong dank ehe")
    await ctx.send(embed=embed)

7. 修复search命令

去掉内部的search_song定义,直接调用类的方法:

@cm.command()
async def search(self, ctx, *, song=None):
    if song is None:
        return await ctx.send("Nhập từ khóa cần tìm kiếm pls")
    await ctx.send("Đang tìm kiếm...")
    
    info = await self.search_song(10, song)
    embed = discord.Embed(title=f"Kết quả tìm kiếm cho '{song}':", description="*Copy và dùng URL để .play chính xác hơn*\n", colour=discord.Colour.blue())
    
    amount = 0
    for entry in info["entries"]:
        embed.description += f"[{entry['title']}]({entry['webpage_url']})\n"
        amount += 1
    
    embed.set_footer(text=f"Đang hiển thị {amount} kết quả đầu tiên.")
    await ctx.send(embed=embed)

额外注意事项

  • 确保你已经安装了所有依赖:discord.py、youtube-dl、pafy、ffmpeg(并且路径配置正确)。
  • youtube-dl已停止维护,建议换成yt-dlp,兼容性更好,只需要把代码里的youtube_dl替换成yt_dlp即可。

这样修改后,你的机器人应该就能正常处理队列,不会再出现song_queue未定义的错误了!

内容的提问来源于stack exchange,提问作者Bắc Xuân

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 17:12:46