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

如何在Railway安装FFmpeg?Discord.js代码运行无输出求助

问题排查与解决方案

一、代码无输出/无响应的原因及修复

你的代码存在几个关键问题,导致异步流程静默失败且无日志:

1. 错误监听缺失

所有异步操作(fs.readdir、https.get、ffmpeg进程)都未绑定error事件,一旦出错会直接静默终止,不会打印任何错误信息。必须给每个异步步骤加上错误捕获:

  • 给fs.readdir添加错误回调处理
  • 给https.get的请求对象绑定on('error')
  • 给每个ffmpeg实例添加on('error', (err) => console.error(err))

2. 路径模板字符串错误

modbait变量里的${slash}被包裹在双引号中,不会被解析为变量,导致路径错误:

// 错误写法
var modbait = __dirname + "${slash}tmp${slash}baitt" + rnum
// 正确写法(用模板字符串)
var modbait = `${__dirname}${slash}tmp${slash}baitt${rnum}`

3. 跨平台命令不兼容

你用了Windows的copy /b和Del命令,但Railway是Linux环境,必须替换为Linux命令:

// 替换concat命令为Linux的cat
var concat = `cat "${modbait}" "${B}" > "${output}"`
// 替换DeleteFiles为Linux的rm
var DeleteFiles = `rm -f "${A}" "${B}" "${output}" "${modbait}"`

4. ffmpeg进程未正确监听结束/错误

ffmpeg的end事件仅在成功完成时触发,若进程出错(比如文件不存在、编码失败)会直接触发error事件,不会走end,所以必须同时监听两个事件。

修改后的关键代码片段

function run(interaction, client, slash) {
    const [file, rnum] = [interaction.options.getAttachment("file").url, Math.floor(Math.random() * 999)]

    interaction.reply("adding bypass to your audio   " + slash)

    // 处理fs.readdir错误
    fs.readdir(`${__dirname}${slash}baits`, function (err, Baits) {
        if (err) {
            console.error('读取baits目录失败:', err);
            return interaction.followUp('处理失败:无法读取诱饵文件目录');
        }
        if (!Baits.length) {
            console.error('baits目录为空');
            return interaction.followUp('处理失败:诱饵文件目录为空');
        }

        const req = https.get(file, function (response) {
            console.log('开始下载用户文件');
            const Bait = `${__dirname}${slash}baits${slash}${Baits[Math.floor(Math.random() * Baits.length)]}`;
            const [A, B, output] = [`${__dirname}${slash}tmp${slash}A_${rnum}.ogg`, `${__dirname}${slash}tmp${slash}B_${rnum}.ogg`, `${__dirname}${slash}tmp${slash}bbbbbb${rnum}.ogg`];
            const modbait = `${__dirname}${slash}tmp${slash}baitt${rnum}`;
            // Linux兼容的concat和删除命令
            const concat = `cat "${modbait}" "${B}" > "${output}"`;
            const DeleteFiles = `rm -f "${A}" "${B}" "${output}" "${modbait}"`;

            const endFile = fs.createWriteStream(A);
            const stream = response.pipe(endFile);

            // 监听文件写入错误
            stream.on('error', (err) => {
                console.error('写入临时文件A失败:', err);
                interaction.followUp('处理失败:无法保存上传的音频文件');
            });

            stream.on('finish', function () {
                console.log('用户文件下载完成,开始处理诱饵文件');
                // 处理诱饵文件转码
                ffmpeg(Bait)
                    .toFormat("ogg")
                    .saveToFile(modbait)
                    .on('error', (err) => {
                        console.error('转码诱饵文件失败:', err);
                        interaction.followUp('处理失败:诱饵文件转码出错');
                    })
                    .on('end', function () {
                        console.log('诱饵文件转码完成,开始处理用户音频');
                        // 处理用户音频
                        ffmpeg(A)
                            .audioChannels(2)
                            .audioFrequency(32000)
                            .saveToFile(B)
                            .on('error', (err) => {
                                console.error('转码用户音频失败:', err);
                                interaction.followUp('处理失败:用户音频转码出错');
                            })
                            .on('end', function () {
                                console.log('用户音频转码完成,开始合并文件');
                                // 执行合并命令
                                _exec(concat, function (err) {
                                    if (err) {
                                        console.error('合并文件失败:', err);
                                        return interaction.followUp('处理失败:文件合并出错');
                                    }

                                    interaction.channel.send({
                                        content: "oi",
                                        files: [output]
                                    }).then(() => {
                                        setTimeout(() => {
                                            _exec(DeleteFiles, (err) => {
                                                if (err) console.error('删除临时文件失败:', err);
                                                else console.log('Deleted All Files');
                                            })
                                        }, 15000);
                                    }).catch(err => {
                                        console.error('发送消息失败:', err);
                                    });
                                });
                            });
                    });
            });
        });

        // 监听https请求错误
        req.on('error', (err) => {
            console.error('下载用户文件失败:', err);
            interaction.followUp('处理失败:无法下载上传的音频文件');
        });
    });
}

二、在Railway中安装FFmpeg

Railway支持两种方式安装FFmpeg:

1. 使用Nixpacks配置(推荐)

在项目根目录创建nixpacks.toml文件,添加以下内容:

[phases.install]
nixPkgs = ["...", "ffmpeg"]

Nixpacks在构建时会自动安装FFmpeg。

2. 通过apt-get安装(适用于Node.js项目)

在package.json的scripts里添加预处理脚本:

{
  "scripts": {
    "prestart": "apt-get update && apt-get install -y ffmpeg",
    "start": "node index.js"
  }
}

注意:这种方式每次启动都会执行安装,效率不如Nixpacks,但适合快速测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:45:34