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

Discord音乐机器人报错:未注册提取器,FFmpeg调用异常求助

解决Discord音乐机器人"未注册提取器"警告问题

警告信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:37:01