0

I'm working on a YouTube video downloader similar to Y2Mate or SaveFrom, using Node.js with ytdl and FFmpeg. The problem is that when I download any video, I only get formats up to 360p. I want to support 720p and 1080p, but it's not working.

Can anyone help me figure this out? I'd really appreciate it. This YouTube downloader is only for educational purposes, as I am a developer and will not use it for illegal activities.

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="Paste YouTube link here..." />
    <button onclick="getInfo()">Start</button>
  </div>

  <div id="videoDetails"></div>
  <div id="formatList"></div>

</div>

<script>
const API = "http://localhost:9000/api/video-info";
const DL_API = "http://localhost:9000/api/download";

function startProgress() {
  const bar = document.getElementById("progressBar");
  bar.style.width = "0%";

  // Animate progress bar to 80%
  setTimeout(() => (bar.style.width = "40%"), 100);
  setTimeout(() => (bar.style.width = "70%"), 500);
}

function finishProgress() {
  const bar = document.getElementById("progressBar");
  bar.style.width = "100%";

  setTimeout(() => {
    bar.style.width = "0%";
  }, 300); // hide smoothly
}

function showLoader() {
  document.getElementById("videoDetails").innerHTML = `
    <div style="width:100%; text-align:center; margin-top:30px;">
      <div class="loader"></div>
      <p style="color:#555; margin-top:10px;">Extracting data…</p>
    </div>
  `;
}

async function getInfo() {
  const url = document.getElementById("videoURL").value.trim();
  if (!url) return alert("Enter a valid YouTube link!");

  showLoader();
  startProgress();   // 🔥 Start progress bar

  try {
    const res = await fetch(API, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ url })
    });

    const data = await res.json();

    if (!data.success) {
      document.getElementById("videoDetails").innerHTML =
        "<p style='color:red;'>Failed to fetch video info.</p>";
      finishProgress();
      return;
    }

    renderVideo(data);
    renderFormats(data);

    finishProgress();  // 🔥 Successful load

  } catch (err) {
    document.getElementById("videoDetails").innerHTML =
      "<p style='color:red;'>Server error. Try again.</p>";
    finishProgress();
  }
}

function renderVideo(data) {
  document.getElementById("videoDetails").innerHTML = `
    <div class="video-card">
      <img src="${data.thumbnail}" alt="thumbnail">
      <div>
        <h2>${data.title}</h2>
      </div>
    </div>
  `;
}

function renderFormats(data) {
  let html = `
    <table>
      <tr>
        <th>Quality</th>
        <th>Format</th>
        <th>Action</th>
      </tr>
  `;

  data.formats.forEach(f => {
    html += `
      <tr>
        <td>${f.quality}</td>
        <td>${f.ext.toUpperCase()}</td>
        <td>
          <a class="download-btn"
             href="http://localhost:9000/api/download?url=${encodeURIComponent(document.getElementById("videoURL").value)}&itag=${f.itag}&title=${data.title}">
             Download
          </a>
        </td>
      </tr>
    `;
  });

  if (data.mp3) {
    html += `
      <tr>
        <td>MP3 (Audio)</td>
        <td>MP3</td>
        <td>
          <a class="download-btn"
             href="http://localhost:9000/api/download?url=${encodeURIComponent(document.getElementById("videoURL").value)}&itag=${data.mp3.itag}&title=${data.title}">
             Download MP3
          </a>
        </td>
      </tr>
    `;
  }

  html += `</table>`;
  document.getElementById("formatList").innerHTML = html;

}
</script>


</body>
</html>
3
  • 1
    Ultimately, this isn't a programming question, as either the executable (yt-dlp.exe) does the same when called from the command line, or you're not passing the correct parameters to it. Commented yesterday
  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Commented yesterday
  • Add the YoTube video URl you are trying to download with this code. Commented yesterday

1 Answer 1

0

bro use :ytdl-core with FFmpeg

New contributor
ramazan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Sign up to request clarification or add additional context in comments.

1 Comment

am already using it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.