Discord音乐机器人报错:未注册提取器,FFmpeg调用异常求助
警告信息
Skipping extractors execution since zero extractors were registered (Use node --trace-warnings ... to show where the warning was created)
译文:因未注册任何提取器,跳过提取器执行,可使用node --trace-warnings ...查看警告来源
问题场景
我跟着教程开发一个基于FFmpeg的Discord音乐机器人,运行时弹出上述警告。怀疑是FFmpeg调用方式变更或代码问题,试过多种FFmpeg安装方式都无法解决,以下是我的代码:
index.js
require("dotenv").config(); const { REST } = require("@discordjs/rest"); const { Routes } = require("discord-api-types/v9"); const { Client, Collection, Intents } = require('discord.js'); const { Player } = require("discord-player"); const fs = require("node:fs"); const path = require("node:path"); const client = new Client({ intents: ["Guilds", "GuildMessages", "GuildVoiceStates"] }); // Set up our commands into an array const commands = []; client.commands = new Collection(); const commandsPath = path.join(__dirname, "commands"); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith(".js")); for (const file of commandFiles) { console.log(`${file}`) console.log(`${commandsPath}`) const filePath = path.join(commandsPath, file); console.log(`${filePath}`) const command = require(filePath); client.commands.set(command.data.name, command); commands.push(command.data.toJSON()); } // Create the player, highest quality audio client.player = new Player(client, { ytdlOptions: { quality: "highestaudio", highWaterMark: 1 << 25 } }); // Commands are registered client.on("ready", () => { const guild_ids = client.guilds.cache.map(guild => guild.id); const rest = new REST({ version: "9" }).setToken(process.env.TOKEN); for (const guildId of guild_ids) { rest.put(Routes.applicationGuildCommands(process.env.CLIENT_ID, guildId), { body: commands }) .then(() => console.log(`Added commands to ${guildId}`)) .catch(console.error); } }); client.on("interactionCreate", async interaction => { if (!interaction.isCommand()) return; const command = client.commands.get(interaction.commandName); if (!command) return; try { await command.execute({ client, interaction }); } catch (err) { console.error(err); await interaction.reply("An error occured while executing that command."); } }); console.log(process.env.TOKEN); client.login(process.env.TOKEN);
play.js
const { SlashCommandBuilder } = require("@discordjs/builders"); const { EmbedBuilder } = require("discord.js"); const { QueryType } = require('discord-player'); module.exports = { data: new SlashCommandBuilder() .setName("play") .setDescription("Plays a song.") .addSubcommand(subcommand => { return subcommand .setName("search") .setDescription("Searches for a song.") .addStringOption(option => { return option .setName("searchterms") .setDescription("search keywords") .setRequired(true); }) }) .addSubcommand(subcommand => { return subcommand .setName("playlist") .setDescription("Plays playlist from YT") .addStringOption(option => { return option .setName("url") .setDescription("playlist url") .setRequired(true); }) }) .addSubcommand(subcommand => { return subcommand .setName("song") .setDescription("Plays song from YT") .addStringOption(option => { return option .setName("url") .setDescription("url of song") .setRequired(true); }) }), execute: async ({ client, interaction }) => { if (!interaction.member.voice.channel) { await interaction.reply("You must be in a voice channel to use this command."); return; } const queue = await client.player.nodes.create(interaction.guild); if (!queue.connection) await queue.connect(interaction.member.voice.channel) let embed = new EmbedBuilder(); if (interaction.options.getSubcommand() === "song") { let url = interaction.options.getString('url'); const result = await client.player.search(url, { requestedBy: interaction.user, searchEngine: QueryType.YOUTUBE_VIDEO }); console.log(result.tracks); if (result.tracks.length === 0) { await interaction.reply("no results found"); return } const song = result.tracks[0]; await queue.addTrack(song); embed .setDescription(`Added **[${song.title}](${song.url})** to the queue.`) .setThumbnail(song.thumbnail) .setFooter({ text: `Duration: ${song.duration}` }); } else if (interaction.options.getSubcommand() === "playlist") { let url = interaction.options.getString('url'); const result = await client.player.search(url, { requestedBy: interaction.SlashCommandBuilder, searchEngine: QueryType.YOUTUBE_PLAYLIST, }); if (result.tracks.length === 0) { await interaction.reply("no playlist found"); return } const playlist = result.playlist; await queue.addTracks(playlist); embed .setDescription(`Added **[${playlist.title}](${playlist.url})** to the queue.`) .setThumbnail(playlist.thumbnail) .setFooter({ text: `Duration: ${playlist.duration}` }); } else if (interaction.options.getSubcommand() === "search") { let url = interaction.options.getString('searchterms'); const result = await client.player.search(url, { requestedBy: interaction.SlashCommandBuilder, searchEngine: QueryType.AUTO, }); if (result.tracks.length === 0) { await interaction.reply("no results found"); return } const song = result.tracks[0] await queue.addTrack(song); embed .setDescription(`Added **[${song.title}](${song.url})** to the queue.`) .setThumbnail(song.thumbnail) .setFooter({ text: `Duration: ${song.duration}` }); } if (!queue.playing) await queue.play(); await interaction.reply({ embeds: [embed] }) } }
解决方案
1. 安装提取器包
discord-player v6+不再默认内置媒体提取器,需要手动安装官方提取器包:
npm install @discord-player/extractor
2. 在index.js中注册提取器
修改Player初始化部分的代码,导入并注册所需的提取器(以YouTube为例,如需其他平台可添加对应提取器):
// 原导入代码 const { Player } = require("discord-player"); // 添加提取器导入 const { YouTubeExtractor } = require("@discord-player/extractor"); // 创建Player后添加注册代码 client.player = new Player(client, { ytdlOptions: { quality: "highestaudio", highWaterMark: 1 << 25 } }); // 注册YouTube提取器 client.player.extractors.register(YouTubeExtractor, {});
如果需要支持Spotify、SoundCloud等平台,可导入对应提取器并注册:
const { SpotifyExtractor, SoundCloudExtractor } = require('@discord-player/extractor'); client.player.extractors.register(SpotifyExtractor, {}); client.player.extractors.register(SoundCloudExtractor, {});
3. 修复play.js中的错误
在playlist和search子命令中,requestedBy参数错误地使用了interaction.SlashCommandBuilder,应改为interaction.user,和song子命令保持一致:
// playlist子命令修正 const result = await client.player.search(url, { requestedBy: interaction.user, searchEngine: QueryType.YOUTUBE_PLAYLIST, }); // search子命令修正 const result = await client.player.search(url, { requestedBy: interaction.user, searchEngine: QueryType.AUTO, });
原因说明
这个警告和FFmpeg安装无关,FFmpeg负责音频转码,而提取器是discord-player用来从YouTube等平台获取媒体资源的组件。discord-player从v6版本开始拆分了提取器模块,必须手动安装并注册才能正常使用,这就是警告出现的根本原因。
内容的提问来源于stack exchange,提问作者Parkster00

