使用ytdl-core在Node.js中流式传输音频时遇HTTP 403访问拒绝错误
问题
我正在开发一个Node.js项目,需要使用ytdl-core从YouTube流式传输音频。我编写了一个根据歌曲名和艺术家名获取视频URL的函数,以及一个尝试流式传输音频的函数。但在尝试流式传输或下载音频时,遇到了“Access Denied”(HTTP ERROR 403)错误。
代码如下:
const sanitizeFilename = (name) => { return name.replace(/[\\/*?:"<>|]/g, ""); }; const getVideoUrl = async (songName, artistName) => { songName = sanitizeFilename(songName); artistName = sanitizeFilename(artistName); const query = `${songName} ${artistName} audio`; try { const searchResults = await ytsr(query, { limit: 1 }); console.log(searchResults.items); if (searchResults.items.length === 0) { return { error: 'No results found' }; } const firstItem = searchResults.items[0]; if (!firstItem || !firstItem.id) { return { error: 'Invalid search result item' }; } const videoId = firstItem.id; return { status: 'success', video_id: videoId }; } catch (error) { return { error: `Failed to fetch video URL: ${error.message}` }; } }; const streamAudioFunction = async (req, res) => { const videoId = req.params.video_id; const trackName = decodeURIComponent(req.params.trackName); const artistName = decodeURIComponent(req.params.artistName); const trackGenre = decodeURIComponent(req.params.trackGenre); console.log({ videoId, trackName, artistName, trackGenre }); const url = `http://www.youtube.com/watch?v=${videoId}`; try { const info = await ytdl.getInfo(url); const format = ytdl.chooseFormat(info.formats, { quality: 'highestaudio' }); // Assuming the activity record is created here const activityRecord = await Activity.create({ songName: trackName, artistName: artistName, songGenre: trackGenre }); res.redirect(format.url); } catch (error) { console.error('Error streaming audio:', error.message); res.json({ error: error.message }); } };
访问下载或流式传输端点时,浏览器显示错误:
Access to rr1---sn-pqx5jxaa0a5g-2o96.googlevideo.com was denied
You don't have authorization to view this page.
HTTP ERROR 403
已验证视频ID正确、URL格式无误,且关闭了MacBook Air M2上的Apple AirPlay接收器,请问问题原因及解决办法?
原因与解决方法
核心原因
YouTube的CDN(googlevideo.com)会验证请求的Referer和User-Agent头,直接重定向到音频格式URL时,浏览器发送的请求缺少YouTube认可的头信息,导致403拒绝。此外,ytdl-core版本过旧也可能无法正确生成带有效签名的音频URL。
解决办法
改用服务器代理流式传输(最稳定方案)
不要直接把音频URL返回给浏览器,而是在服务器端用ytdl-core把数据流式传输到客户端,这样能带上YouTube认可的请求头。修改streamAudioFunction:const streamAudioFunction = async (req, res) => { const videoId = req.params.video_id; const trackName = decodeURIComponent(req.params.trackName); const artistName = decodeURIComponent(req.params.artistName); const trackGenre = decodeURIComponent(req.params.trackGenre); console.log({ videoId, trackName, artistName, trackGenre }); const url = `http://www.youtube.com/watch?v=${videoId}`; try { // 创建活动记录 await Activity.create({ songName: trackName, artistName: artistName, songGenre: trackGenre }); // 设置响应头,模拟音频流 res.setHeader('Content-Type', 'audio/mpeg'); res.setHeader('Content-Disposition', `inline; filename="${sanitizeFilename(trackName)}.mp3"`); // 用ytdl-core直接流式传输到响应对象 ytdl(url, { quality: 'highestaudio' }).pipe(res); } catch (error) { console.error('Error streaming audio:', error.message); res.status(500).json({ error: error.message }); } };更新ytdl-core到最新版本
YouTube会频繁更新API和签名机制,旧版本的ytdl-core可能无法生成有效的音频URL。执行命令更新:npm update ytdl-core如果更新后仍有问题,可尝试使用针对流媒体优化的分支:
npm install ytdl-core-discord添加自定义请求头(备选,稳定性低)
如果坚持用重定向方式,可尝试在获取格式时指定符合要求的User-Agent和Referer,但这种方法可能随YouTube规则调整失效:const format = ytdl.chooseFormat(info.formats, { quality: 'highestaudio' }); // 手动构造带有效头的重定向响应 res.redirect(302, format.url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'Referer': 'https://www.youtube.com/' } });
内容的提问来源于stack exchange,提问作者Vedant Kokane

