Skip to content

Commit a07f25c

Browse files
authored
v3.2.0-beta.1
2 parents 08757d0 + 7f7bf0c commit a07f25c

11 files changed

Lines changed: 1743 additions & 680 deletions

File tree

app/api/video-converter/route.ts

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
"use server";
22

33
import ffmpeg from "fluent-ffmpeg";
4-
import ytdl from "@distube/ytdl-core";
4+
import * as oldYtdl from "@distube/ytdl-core";
55
import { PassThrough, Readable } from "stream";
66
import path from "path";
77
import * as fs from "fs";
8-
import { initializeConf, sanitizeFilename } from "@/lib/serverUtils";
8+
import {
9+
initializeConf,
10+
sanitizeFilename,
11+
selectYtDlpPath,
12+
} from "@/lib/serverUtils";
913
import { FormatData, VideoData } from "@/lib/types";
1014
import { NextResponse } from "next/server";
1115
import { PrismaClient } from "@prisma/client";
@@ -54,18 +58,32 @@ const downloadStreams = async (
5458
url: string,
5559
audio_path: string,
5660
video_path: string,
57-
quality?: string
61+
formatSelector?: string
5862
) => {
59-
quality = quality || "highestvideo";
60-
61-
const audioStream = ytdl(url, {
62-
quality: "highestaudio",
63-
});
64-
65-
const videoStream = ytdl(url, {
66-
quality: quality,
67-
filter: "videoonly",
68-
});
63+
const ytdl = await selectYtDlpPath();
64+
formatSelector = formatSelector || "bv";
65+
66+
const audioStream = ytdl.exec(url, {
67+
noCheckCertificates: true,
68+
noWarnings: true,
69+
preferFreeFormats: true,
70+
addHeader: ["referer:youtube.com", "user-agent:googlebot"],
71+
format: "ba",
72+
output: "-",
73+
}).stdout;
74+
75+
const videoStream = ytdl.exec(url, {
76+
noCheckCertificates: true,
77+
noWarnings: true,
78+
preferFreeFormats: true,
79+
addHeader: ["referer:youtube.com", "user-agent:googlebot"],
80+
format: formatSelector,
81+
output: "-",
82+
}).stdout;
83+
if (!audioStream || !videoStream) {
84+
throw new Error("Failed to download audio or video stream");
85+
}
86+
console.log("Downloading audio and video streams...");
6987

7088
const audioWriteStream = fs.createWriteStream(audio_path);
7189
const videoWriteStream = fs.createWriteStream(video_path);
@@ -181,13 +199,17 @@ export async function GET(request: Request) {
181199
ffmpeg.setFfmpegPath(ffmpegPath);
182200
}
183201

184-
const video = await ytdl.getBasicInfo(url);
202+
const video = await oldYtdl.getBasicInfo(url);
185203
const formatMap = new Map();
186204
(video.player_response.streamingData.adaptiveFormats as FormatData[]).forEach(
187205
(format: FormatData) => {
188-
if (!formatMap.has(format.qualityLabel)) {
206+
if (format.qualityLabel && !formatMap.has(format.qualityLabel)) {
189207
formatMap.set(format.qualityLabel, format);
190208
}
209+
const itagKey = String(format.itag);
210+
if (!formatMap.has(itagKey)) {
211+
formatMap.set(itagKey, format);
212+
}
191213
}
192214
);
193215

@@ -225,9 +247,18 @@ export async function GET(request: Request) {
225247
};
226248

227249
if (quality === "audio") {
228-
const audioStream = ytdl(url, {
229-
quality: "highestaudio",
230-
});
250+
const ytdl = await selectYtDlpPath();
251+
const audioStream = ytdl.exec(url, {
252+
noCheckCertificates: true,
253+
noWarnings: true,
254+
preferFreeFormats: true,
255+
addHeader: ["referer:youtube.com", "user-agent:googlebot"],
256+
format: "ba",
257+
output: "-",
258+
}).stdout;
259+
if (!audioStream) {
260+
throw new Error("Failed to download audio stream");
261+
}
231262

232263
//Convert audio stream to mp3
233264
const audioPassThrough = new PassThrough();
@@ -302,7 +333,29 @@ export async function GET(request: Request) {
302333
});
303334

304335
if (!requestedFile) {
305-
await downloadStreams(url, AUDIO_FILE_PATH, VIDEO_FILE_PATH, quality);
336+
const selectedFormat = formatMap.get(quality);
337+
if (!selectedFormat) {
338+
console.log("formatMap", formatMap);
339+
console.log("quality", quality);
340+
console.error(
341+
`Cannot find the requested format: ${quality}. Available formats are: ${Array.from(
342+
formatMap.keys()
343+
).join(", ")}`
344+
);
345+
346+
return new Response(
347+
`Cannot find the requested format. Available formats are: ${Array.from(
348+
formatMap.keys()
349+
).join(", ")}`,
350+
{ status: 400 }
351+
);
352+
}
353+
await downloadStreams(
354+
url,
355+
AUDIO_FILE_PATH,
356+
VIDEO_FILE_PATH,
357+
String(selectedFormat.itag)
358+
);
306359

307360
await mergeAudioVideo(
308361
VIDEO_FILE_PATH,

components/custom/GetterInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const GetterInput = () => {
5959
<div className="relative my-4 w-full">
6060
<input
6161
type="text"
62-
placeholder="Please enter a instagram, tiktok, youtube... video URL"
62+
placeholder="Please enter a youtube video URL or a search query"
6363
id="video-url"
6464
name="video-url"
6565
className="block w-full rounded-md border border-[#081721] bg-[#081721] p-2.5 text-white focus:border-blue-500 focus:ring-blue-500"

copy-binaries.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import path from "path";
2+
import fs from "fs";
3+
4+
const sourcePath = path.resolve("./node_modules/youtube-dl-exec/bin/yt-dlp");
5+
const destPath = path.resolve("./.next/server/bin/yt-dlp");
6+
7+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
8+
9+
fs.copyFileSync(sourcePath, destPath);
10+
console.log(`Copied ${sourcePath} to ${destPath}`);

lib/serverUtils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { execSync } from "child_process";
33
import * as os from "os";
44
import * as fs from "fs";
55
import path from "path";
6+
import { create as createYoutubeDl } from "youtube-dl-exec";
67
import { initializeCleanup } from "@/scripts/cleanup";
78

89
type Conf = {
@@ -114,3 +115,23 @@ export async function yt_validate(url: string): Promise<"video" | false> {
114115
}
115116
return false;
116117
}
118+
119+
export async function selectYtDlpPath() {
120+
let youtubedl;
121+
122+
if (process.env.NODE_ENV === "development") {
123+
const devBinaryPath = path.join(
124+
process.cwd(),
125+
"node_modules/youtube-dl-exec/bin/yt-dlp"
126+
);
127+
youtubedl = createYoutubeDl(devBinaryPath);
128+
} else {
129+
const prodBinaryPath = path.join(
130+
process.cwd(),
131+
"node_modules/youtube-dl-exec/bin/yt-dlp"
132+
);
133+
youtubedl = createYoutubeDl(prodBinaryPath);
134+
}
135+
136+
return youtubedl;
137+
}

next.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import path from "path";
44
const packageJsonPath = path.resolve("./package.json");
55
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
66

7+
const __dirname = path.resolve();
8+
79
/** @type {import('next').NextConfig} */
810
const nextConfig = {
911
output: "standalone",

0 commit comments

Comments
 (0)