Download video from youtube using ytdl audio isn't working

28 Views Asked by At

I've created a Next.js TypeScript API to download separate audio_only.mp3 and video_only.mp4 files from a YouTube video. However, I'm encountering a problem where the audio_only.mp3 file is empty, and there's no sound when I try to listen to it. I'm using a MacBook, and I'm wondering if the issue could be related to the Next.js server running on my machine. Can you help me troubleshoot and resolve this audio issue ?

import { NextResponse, NextRequest } from "next/server";
import * as fs from "fs/promises";
import { v4 as uuidv4 } from "uuid";
import { spawn } from "child_process";
import ytdl from "ytdl-core";

export async function POST(request: NextRequest) {
  try {
    const { type, lang, link } = await request.json();

    if (
      typeof type !== "string" ||
      typeof lang !== "string" ||
      typeof link !== "string"
    ) {
      return NextResponse.json(
        {
          message:
            "Invalid input format. Please provide valid strings for 'type', 'lang', and 'link'.",
        },
        { status: 400 }
      );
    }

    let filePath;
    if (type === "local") {
      filePath = link;

      // Generate a unique ID for the video
      const videoId = uuidv4();

      // Create a folder with the unique ID
      const folderPath = `videos/${videoId}`;
      await fs.mkdir(folderPath);

      // Define output file paths within the folder
      const audioOutputPath = `${folderPath}/audio_only.mp3`;
      const videoOutputPath = `${folderPath}/video_only.mp4`;

      const ffmpegProcess = spawn("ffmpeg", [
        "-i",
        filePath,
        "-q:a",
        "0",
        "-map",
        "a",
        audioOutputPath,
        "-q:v",
        "0",
        "-map",
        "v",
        videoOutputPath,
      ]);

      await new Promise((resolve, reject) => {
        ffmpegProcess.on("close", (code) => {
          if (code !== 0) {
            reject(`ffmpeg process exited with code ${code}`);
          }
        });
      });

      return NextResponse.json(
        {
          message: `Video saved at ${folderPath}`,
          videoId: videoId,
        },
        { status: 200 }
      );
    } else if (type === "youtube") {
      const videoId = uuidv4();
      const folderPath = `videos/${videoId}`;
      await fs.mkdir(folderPath);

      // Download audio
      const audioOutputPath = `${folderPath}/audio_only.mp3`;
      const audioStream = ytdl(link, { quality: "highestaudio" });
      await pipeToFile(audioStream, audioOutputPath);

      // Download video
      const videoOutputPath = `${folderPath}/video_only.mp4`;
      const videoStream = ytdl(link, { quality: "highestvideo" });
      await pipeToFile(videoStream, videoOutputPath);

      return NextResponse.json(
        {
          message: `YouTube video saved at ${folderPath}`,
          videoId: videoId,
        },
        { status: 200 }
      );
    } else {
      return NextResponse.json(
        {
          message:
            "Invalid value for 'type'. It should be either 'youtube' or 'local'.",
        },
        { status: 200 }
      );
    }
  } catch (error) {
    console.error("Error:", error);

    return NextResponse.json(
      { error: "Internal server error. Please try again later." },
      { status: 500 }
    );
  }
}

async function pipeToFile(stream: any, filePath: any) {
  const fs = require("fs");
  const fileStream = fs.createWriteStream(filePath);
  return new Promise<void>((resolve, reject) => {
    stream.pipe(fileStream);
    stream.on("end", () => {
      resolve();
    });
    fileStream.on("error", reject);
  });
}
0

There are 0 best solutions below