Node.js开发YouTube下载器无法获取720p/1080p高清格式的求助
Node.js开发YouTube下载器无法获取720p/1080p高清格式的求助
我正在做一个类似Y2Mate或SaveFrom的YouTube视频下载器,用Node.js配合yt-dlp和FFmpeg来实现。但现在遇到个问题——不管下载哪个视频,最多只能拿到360p的格式,想支持720p和1080p却一直不行,有没有大佬能帮我看看问题出在哪?万分感谢!另外说明下,这个下载器只是我作为开发者的教育学习用途,绝对不会用来做非法活动。
我的Youtube.js代码
const express = require("express"); const router = express.Router(); const { exec } = require("child_process"); const path = require("path"); const YTDLP = path.join(__dirname, "..", "yt-dlp.exe"); const FFMPEG = path.join(__dirname, "..", "ffmpeg.exe"); // -------------------------- // GET VIDEO INFO (FORMATS) // -------------------------- router.post("/video-info", (req, res) => { const { url } = req.body; if (!url) { return res.json({ success: false, error: "No URL provided" }); } const cmd = `"${YTDLP}" --no-warnings -J "${url}"`; exec(cmd, (err, stdout) => { if (err) return res.json({ success: false, error: "Failed to get info" }); try { const info = JSON.parse(stdout); const title = info.title; const thumbnail = info.thumbnail; const formats = []; const audioFormats = []; info.formats.forEach((f) => { if (!f.url) return; // AUDIO if (f.vcodec === "none" && f.acodec !== "none") { audioFormats.push({ itag: f.format_id, quality: "High Quality", ext: "mp3", url: f.url }); } // ONLY MP4 VIDEO QUALITIES if (f.ext === "mp4" && f.vcodec !== "none" && f.acodec !== "none") { formats.push({ itag: f.format_id, quality: f.format_note || f.resolution, ext: "mp4", size: f.filesize || null, url: f.url }); } }); // SORT video formats by quality const qualityOrder = ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p", "144p"]; const sortedFormats = qualityOrder .map(q => formats.find(f => (f.quality + "").includes(q))) .filter(Boolean); // remove null res.json({ success: true, title, thumbnail, formats: sortedFormats, mp3: audioFormats[0] || null }); } catch (e) { console.error(e); res.json({ success: false, error: "Parse error" }); } }); }); // -------------------------- // DOWNLOAD ROUTE // -------------------------- router.get("/download", (req, res) => { const { url, itag, title } = req.query; const safeName = title.replace(/[\/\\:*?"<>|]/g, ""); const output = `${safeName}.mp4`; const cmd = `"${YTDLP}" -f ${itag}+bestaudio --merge-output-format mp4 -o "${output}" "${url}"`; exec(cmd, () => { res.download(output, () => { try { fs.unlinkSync(output); } catch {} }); }); }); module.exports = router;
我的Index.html代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>YouTube Downloader – Y2Mate Style</title> <style> body { margin: 0; padding: 0; font-family: Arial, sans-serif; background: #f5f5f5; } header { padding: 25px; background: #ffffff; box-shadow: 0px 2px 5px rgba(0,0,0,0.1); text-align: center; } h1 { margin: 0; font-size: 30px; color: #333; } .container { max-width: 720px; margin: 40px auto; background: white; padding: 25px; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1); } .input-group { display: flex; gap: 10px; } input { flex: 1; padding: 12px; border: 2px solid #ddd; border-radius: 6px; font-size: 16px; } button { padding: 12px 18px; background: #4caf50; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; font-weight: bold; } button:hover { background: #43a047; } .video-card { margin-top: 25px; display: flex; gap: 15px; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0px 2px 8px rgba(0,0,0,0.1); } .video-card img { width: 160px; height: auto; border-radius: 6px; } table { width: 100%; margin-top: 20px; border-collapse: collapse; } table th, table td { border: 1px solid #eee; padding: 12px; text-align: left; } table th { background: #fafafa; } .download-btn { background: #1e88e5; padding: 8px 14px; color: white; border-radius: 6px; text-decoration: none; } .download-btn:hover { background: #1565c0; } #progressBar { position: fixed; top: 0; left: 0; height: 4px; width: 0%; background: #4caf50; z-index: 9999; transition: width 0.3s ease; } </style> </head> <body> <div id="progressBar"></div> <header> <h1>YouTube Downloader</h1> </header> <div class="container"> <div class="input-group"> <input type="text" id="videoURL" placeholder="Enter YouTube Video URL..."> <button id="getInfoBtn">Get Download Options</button> </div> <div id="videoInfo" style="display: none;"> <div class="video-card"> <img id="thumbnail" src="" alt="Video Thumbnail"> <div> <h2 id="videoTitle"></h2> </div> </div> <table> <thead> <tr> <th>Quality</th> <th>Format</th> <th>Size</th> <th>Action</th> </tr> </thead> <tbody id="formatsTable"></tbody> </table> <a id="mp3Download" class="download-btn" style="margin-top: 10px; display: inline-block;" href="#">Download MP3</a> </div> </div> <script> document.getElementById('getInfoBtn').addEventListener('click', async () => { const url = document.getElementById('videoURL').value; if (!url) return alert('Please enter a URL'); const res = await fetch('/video-info', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url }) }); const data = await res.json(); if (!data.success) return alert(data.error); document.getElementById('videoTitle').textContent = data.title; document.getElementById('thumbnail').src = data.thumbnail; document.getElementById('mp3Download').href = `/download?url=${url}&itag=${data.mp3.itag}&title=${data.title}`; const tableBody = document.getElementById('formatsTable'); tableBody.innerHTML = ''; data.formats.forEach(format => { const row = document.createElement('tr'); row.innerHTML = ` <td>${format.quality}</td> <td>${format.ext}</td> <td>${format.size ? (format.size / 1024 / 1024).toFixed(2) + 'MB' : 'Unknown'}</td> <td><a href="/download?url=${url}&itag=${format.itag}&title=${data.title}" class="download-btn">Download</a></td> `; tableBody.appendChild(row); }); document.getElementById('videoInfo').style.display = 'block'; }); </script> </body> </html>
问题根源分析
你现在拿不到720p/1080p高清格式的核心原因是YouTube的高清视频格式大多是音视频分离的——也就是一个格式只有视频流(vcodec有值,acodec为"none"),另一个格式只有音频流(acodec有值,vcodec为"none");而360p及以下的低清格式才会是音视频合并在同一个文件里的。
看你Youtube.js里的代码,在筛选视频格式时写了:
if (f.ext === "mp4" && f.vcodec !== "none" && f.acodec !== "none") { // 加入formats数组 }
这个条件直接把所有纯视频的高清格式给过滤掉了,导致前端只能看到音视频合并的低清格式。
解决方案
只需要调整/video-info路由里的格式筛选逻辑,把纯视频的高清格式也加入到formats数组中即可:
修改Youtube.js中的格式筛选代码
把原来的"ONLY MP4 VIDEO QUALITIES"部分替换成下面的代码:
// 包含所有MP4视频格式(包括纯视频的高清格式) if (f.ext === "mp4" && f.vcodec !== "none") { formats.push({ itag: f.format_id, quality: f.format_note || f.resolution, ext: "mp4", size: f.filesize || null, url: f.url }); }
为什么这样改就可以?
- 去掉了
f.acodec !== "none"的限制,纯视频的高清格式(比如1080p的mp4视频流)就会被加入到formats数组中,前端就能看到这些选项。 - 你的下载路由其实已经写对了合并逻辑:
-f ${itag}+bestaudio,当用户选择高清纯视频的itag时,yt-dlp会自动把这个视频流和最好的音频流通过FFmpeg合并成一个完整的mp4文件,完全不用你额外写合并代码。
额外注意点
- 确保你的yt-dlp是最新版本,因为YouTube的格式规则可能会更新,旧版本的yt-dlp可能无法识别最新的高清格式。
- 前端表格渲染时,纯视频格式的
size可能为null,你可以在前端做兼容,显示"未知"或者计算合并后的预估大小(不过yt-dlp下载时会自动处理,不影响使用)。
这样修改后,你应该就能在前端看到720p、1080p的下载选项,点击下载后也能得到完整的高清视频文件了!




