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