基于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
相关产品推荐
相关产品推荐

