How to combine 2 videos together with ytdl-core and return in a response with Sveltekit?

84 Views Asked by At

so I am making a yt converter with sveltekit and ytdl-core. I have an endpoint that downloads the video the user inputs. It works fine when I just return a new response as a readable stream but I would like for the user to be able to choose resolution and have audio as well. In the ytdl-core docs it says that you have to combine audio-only and video-only streams together if you want higher resolution videos, with audio. In the docs they recommend ffmpeg to combine these videos, they even link to an example: https://github.com/fent/node-ytdl-core/blob/master/example/ffmpeg.js. But when I try this and try to return it as a new response in Sveltekit it actually does start a download but the video file I get is completely unplayable. It might be because ffmpeg returns a type which I can't return in a Sveltekit endpoint. This is my endpoint code so far:

import ytdl from "ytdl-core";
import cp from "child_process";
import ffmpeg from "ffmpeg-static";
import type { Writable } from "stream";

export async function GET({ url }) {
    const link = url.searchParams.get("link");
    const resolution = url.searchParams.get("resolution")
    let videoQuality;

    switch (resolution) {
        case "1080p":
            videoQuality = 137
            break;
        case "720p":
            videoQuality = 136
            break;
        case "480p":
            videoQuality = 136
            break;
        case "360p":
            videoQuality = 135
            break;
        default:
            videoQuality = "highest"
            break;
    }
    
    let audioStream = ytdl(link!, { quality: "highestaudio" });
    let videoStream: any = ytdl(link!, { quality: videoQuality });

    const ffmpegProcess = cp.spawn(ffmpeg!, [
        // Remove ffmpeg's console spamming
        '-loglevel', '8', '-hide_banner',
        // Redirect/Enable progress messages
        '-progress', 'pipe:3',
        // Set inputs
        '-i', 'pipe:4',
        '-i', 'pipe:5',
        // Map audio & video from streams
        '-map', '0:a',
        '-map', '1:v',
        // Keep encoding
        '-c:v', 'copy',
        // Define output file
        'out.mkv',
        ], {
        windowsHide: true,
        stdio: [
            /* Standard: stdin, stdout, stderr */
            'inherit', 'inherit', 'inherit',
            /* Custom: pipe:3, pipe:4, pipe:5 */
            'pipe', 'pipe', 'pipe'
        ],
    });

    ffmpegProcess.on('close', () => {
        console.log('done');
    });

    if (ffmpegProcess.stdio[4] && ffmpegProcess.stdio[5]) {
        audioStream.pipe(ffmpegProcess.stdio[4] as Writable);
        videoStream.pipe(ffmpegProcess.stdio[5] as Writable);
    } else {
        console.error("Error setting up ffmpeg streams");
    }

    return new Response(ffmpegProcess.stdio[3] as any , { headers: { "content-disposition": "attachment" , "content-type": "video/mp4" } });
}

Resolution is just a string that can have the following values "1080p", "720p", "480p" or "360p". Also the line videoStream.pipe(ffmpegProcess.stdio[5] as Writable); gives an error has no element at index '5' even do the list stdio has 6 elements. I have tried to print out the length of stdio and it returns 6. I also tried to print out ffmpegProcess.stdio[5] and it actually returns something, so it does exist. I am using typescript btw.

As a little extra question. Sometimes when I force the video to be 1080p (itag 137) and I parse in a video where 1080p is available on youtube it gives an error that this format is not available even tho it is available in youtube. Example this video it works when I try 1080p: https://www.youtube.com/watch?v=rMMQ8GyxJbw. And this video it doesn't work when I try 1080p: https://www.youtube.com/watch?v=7oU7j2HU8oI. Both videos you can choose 1080p on youtube

0

There are 0 best solutions below