Discord.js V13 队列存在时player.getQueue()返回undefined
问题原因
player.getQueue()返回undefined的核心原因是你在每一个命令文件执行时都重新创建了全新的Player实例:
play.js执行时new Player()生成的实例A,队列是存在实例A的内部存储里的skip.js执行时又new Player()生成了全新的实例B,实例B的内部存储是空的,自然找不到之前创建的队列
不同Player实例之间的队列数据完全不互通,这是最根本的问题。
另外你的代码里还有两个会导致异常的小问题:
- 语音频道权限校验的两个分支没有加
return,校验不通过时代码还会继续往下执行创建队列、搜索歌曲的逻辑 - 手动给
queue.playing赋值的写法是错的,这个状态由discord-player内部自动维护,手动修改会导致状态判断异常
修复方法
1. 全局初始化唯一Player实例
在你的机器人入口文件(通常是index.js/bot.js,也就是你初始化Discord Client、注册命令的文件)中,只初始化一次Player,并且挂载到client上供所有命令复用:
// 入口文件示例 const { Client, Intents } = require('discord.js'); const { Player } = require('discord-player'); // 初始化Discord客户端 const client = new Client({ intents: [ Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_VOICE_STATES, // 你其他需要的intents配置 ] }); // 全局只初始化一次Player const player = new Player(client); // 挂载到client上,所有命令都能拿到同一个实例 client.player = player; // 注册discord-player必要的事件监听(可选但推荐) player.on('trackStart', (queue, track) => { queue.metadata?.send(`🎶 正在播放:${track.title}`); }); player.on('error', (queue, error) => { console.log(`播放队列错误:${error.message}`); }); // 后面保留你原本的client.login、命令注册等逻辑即可
2. 修改play.js代码
把play.js里const player = new Player(interaction.client);这行删掉,替换成从client上取全局实例,同时修复之前提到的两个逻辑问题:
const { SlashCommandBuilder } = require('@discordjs/builders'); const { QueryType } = require('discord-player'); module.exports = { data: new SlashCommandBuilder() .setName('play') .setDescription('播放音乐') .addStringOption(option => option .setName("song") .setDescription("要播放的歌曲名称或链接") .setRequired(true)), async execute(interaction, Discord) { const voice_channel = interaction.member.voice.channel; const bot_channel = interaction.guild.me.voice.channel; if (!voice_channel) { await interaction.reply({ embeds: [new Discord.MessageEmbed() .setColor('#FFDAB9') .setDescription('你需要先进入语音频道才能播放音乐!')], ephemeral: true }); // 加return阻止后续代码执行 return; } if (bot_channel && bot_channel.id != voice_channel.id) { await interaction.reply({ embeds: [new Discord.MessageEmbed() .setColor('#FFDAB9') .setDescription(`我已经在${bot_channel.toString()}频道连接了`)] , ephemeral: true }); // 加return阻止后续代码执行 return; } const song = interaction.options.getString("song"); // 直接取全局唯一的player实例,不要重新创建 const player = interaction.client.player; const searchResult = await player.search(song, { requestedBy: interaction.user, searchEngine: QueryType.AUTO }).catch(() => { }); if (!searchResult || !searchResult.tracks.length) { return interaction.reply({ content: '没有找到对应音乐!', ephemeral: true }); } // 创建队列时传入interaction.channel作为metadata,方便后续事件发送消息 const queue = player.createQueue(interaction.guild, { metadata: interaction.channel }); queue.options.initialVolume = 50; queue.options.leaveOnEmptyCooldown = 5000; try { if (!queue.connection) await queue.connect(voice_channel); } catch { void player.deleteQueue(interaction.guildId); return void interaction.reply({ content: "无法加入你的语音频道!", ephemeral: true }); } searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[0]); // 删掉手动给queue.playing赋值的错误写法,内部会自动维护状态 if (!queue.playing) { await queue.play(); } const row = new Discord.MessageActionRow() .addComponents( new Discord.MessageButton() .setCustomId('pause') .setLabel('⏸') .setStyle('PRIMARY'), new Discord.MessageButton() .setCustomId('resume') .setLabel('▶') .setStyle('PRIMARY'), ); const play_embed = await interaction.reply({ embeds: [new Discord.MessageEmbed() .setColor('#FFDAB9') .setTitle(`${queue.nowPlaying().title}`) .setURL(`${queue.nowPlaying().url}`) .setThumbnail(`${queue.nowPlaying().thumbnail}`) .setAuthor({ name: ` | 正在播放`, iconURL: `https://imgur.com/krzRxsN.png` }) .addField('时长:', `\`${queue.nowPlaying().duration}\``, true) .addField('作者:', `\`${queue.nowPlaying().author}\``, true) .setTimestamp() .setFooter({ text: `点播者:${interaction.user.username}`, iconURL: `${interaction.user.displayAvatarURL()}` }) ], // components: [row] }); } }
3. 修改skip.js代码
同样替换player实例的获取方式,不要重新创建:
const { SlashCommandBuilder } = require('@discordjs/builders'); module.exports = { data: new SlashCommandBuilder() .setName('skip') .setDescription('跳过队列中当前播放的音乐'), async execute(interaction, Discord) { await interaction.deferReply(); // 取全局唯一的player实例 const player = interaction.client.player; const queue = player.getQueue(interaction.guildId); if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | 当前没有正在播放的音乐!" }); const currentTrack = queue.current; const success = queue.skip(); return void interaction.followUp({ content: success ? `✅ | 已跳过 **${currentTrack}**!` : "❌ | 跳过操作失败!" }); } }
改完之后重启机器人,所有命令复用同一个Player实例,队列就能正常获取了。
内容的提问来源于stack exchange,提问作者LordBaljeet
相关产品推荐
相关产品推荐

