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

基于ytdl-core的音频转换服务出现FFMPEG 403错误求助

YouTube音频下载转换服务403错误排查与解决

问题描述

基于Node.js、Express、ytdl-core及fluent-ffmpeg搭建的YouTube音频下载转换服务,在Replit平台运行时持续报错:

FFMPEG - Error during conversion: Input stream error: Status code: 403

此前ChatGPT针对410错误的分析为资源不可用,但当前问题为403权限拦截,需针对性排查。

服务代码

const express = require('express');
const ytdl = require('ytdl-core');
const ffmpeg = require('fluent-ffmpeg');
const ffmpegStatic = require('ffmpeg-static');

const app = express();
app.use(express.static('public'));

app.get('/videoInfo', async (req, res) => {
    const { url } = req.query;
    if (!ytdl.validateURL(url)) {
        return res.status(400).send('Invalid URL');
    }

    const requestOptions = {
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
        }
    };

    try {
        const info = await ytdl.getInfo(url, requestOptions);
        const audioFormats = ytdl.filterFormats(info.formats, 'audioonly');
        const highestQualityAudio = audioFormats.reduce((prev, curr) => (prev.audioBitrate > curr.audioBitrate ? prev : curr));

        res.json({
            videoTitle: info.videoDetails.title,
            videoThumbnail: info.videoDetails.thumbnails[info.videoDetails.thumbnails.length - 1].url, 
            audioFormat: highestQualityAudio
        });
    } catch (error) {
        console.error('Error fetching video info:', error);
        res.status(500).send('Failed to retrieve video info.');
    }
});

app.get('/download', (req, res) => {
    const { url } = req.query;

    function downloadAndConvertAudio(retryCount = 0) {
        const requestOptions = {
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
            }
        };

        res.header('Content-Disposition', 'attachment; filename="audio.mp3"');
        const videoStream = ytdl(url, { quality: 'highestaudio', requestOptions: requestOptions });

        ffmpeg(videoStream)
            .setFfmpegPath(ffmpegStatic)
            .audioBitrate(128)
            .toFormat('mp3')
            .on('error', (error) => {
                console.error('Error in ffmpeg conversion:', error);
                if (error.message.includes('403') && retryCount < 3) {
                    console.log('Retrying download...');
                    setTimeout(() => downloadAndConvertAudio(retryCount + 1), 2000); 
                } else {
                    res.status(500).send(`Error during conversion: ${error.message}`);
                }
            })
            .pipe(res);
    }

    downloadAndConvertAudio();
});

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});

排查与解决方案

1. 更新User-Agent请求头

当前使用的Chrome 58版本User-Agent过于陈旧,YouTube会拦截这类自动化请求。同步更新/videoInfo和/download接口中的请求头:

headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
    'Accept-Language': 'en-US,en;q=0.9',
    'Referer': 'https://www.youtube.com/'
}

2. 升级ytdl-core版本

旧版本ytdl-core无法适配YouTube最新反爬机制,执行以下命令更新:

npm update ytdl-core

若更新后仍无效,可切换到维护更及时的fork版本:

npm uninstall ytdl-core
npm install @distube/ytdl-core

修改代码中的引入语句:

const ytdl = require('@distube/ytdl-core');

3. 修复重试逻辑的响应头问题

原代码中重试时会重复发送Content-Disposition响应头,导致请求异常。调整代码如下:

app.get('/download', (req, res) => {
    const { url } = req.query;
    // 提前设置响应头,避免重复发送
    res.header('Content-Disposition', 'attachment; filename="audio.mp3"');

    function downloadAndConvertAudio(retryCount = 0) {
        const requestOptions = {
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
                'Accept-Language': 'en-US,en;q=0.9',
                'Referer': 'https://www.youtube.com/'
            }
        };

        const videoStream = ytdl(url, { quality: 'highestaudio', requestOptions: requestOptions });

        ffmpeg(videoStream)
            .setFfmpegPath(ffmpegStatic)
            .audioBitrate(128)
            .toFormat('mp3')
            .on('error', (error) => {
                console.error('Error in ffmpeg conversion:', error);
                if (error.message.includes('403') && retryCount < 3) {
                    console.log('Retrying download...');
                    setTimeout(() => downloadAndConvertAudio(retryCount + 1), 2000); 
                } else {
                    // 确保未发送响应时再返回错误
                    if (!res.headersSent) {
                        res.status(500).send(`Error during conversion: ${error.message}`);
                    }
                }
            })
            .pipe(res);
    }

    downloadAndConvertAudio();
});

4. 验证视频权限

测试公开无限制的YouTube视频(如官方宣传片),若仅特定视频报错,说明该视频受版权/年龄限制,无法通过代码解决。

5. 应对Replit IP限制

Replit服务器IP可能被YouTube标记为自动化来源,若上述方法无效,可尝试在requestOptions中添加更多模拟浏览器的请求头,或更换运行环境测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 17:19:51