-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.js
148 lines (119 loc) · 3.37 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const express = require("express");
const app = express();
const ytdl = require("ytdl-core");
const ffmpegPath = require("ffmpeg-static");
const cp = require("child_process");
// Set up the template engine
app.set("view engine", "ejs");
app.set("views", __dirname + "/views");
app.use(express.static(__dirname + "/public"));
// set up rate limiter: maximum of ten requests per minute
const RateLimit = require("express-rate-limit");
const limiter = RateLimit({
windowMs: 1*60*1000, // 1 minute
max: 10
});
// apply rate limiter to all requests
app.use(limiter);
// Routes
app.get("/", (req, res) => {
res.render("index", { error: null });
});
app.get("/download", async (req, res) => {
try {
const videoItags = [160, 132, 133, 134, 135, 136, 137];
const url = req.query.url;
const info = await ytdl.getInfo(url);
const title = info.videoDetails.title;
const thumbnail = info.videoDetails.thumbnail.thumbnails.reduce(
(prev, curr) => {
return curr.width > prev.width ? curr : prev;
}
);
const channel = info.videoDetails.ownerChannelName;
const duration = info.videoDetails.lengthSeconds;
const urlInfo = { title, thumbnail, channel, duration };
const videoFormats = ytdl
.filterFormats(info.formats, "videoonly")
.filter((format) => videoItags.includes(format.itag));
const audioFormats = ytdl.filterFormats(info.formats, "audioonly");
res.render("download", {
url,
urlInfo,
videoFormats,
audioFormats,
error: null,
});
} catch (error) {
res.render("index", { error: "Invalid YouTube URL" });
}
});
app.get("/download/video", async (req, res) => {
const { url, quality } = req.query;
const info = await ytdl.getBasicInfo(url);
res.header(
"Content-Disposition",
`attachment; filename="${info.videoDetails.title}_${quality}.mp4"`
);
res.header("Content-Type", "video/mp4");
const videoStream = ytdl(url, { quality, format: "mp4" });
const audioStream = ytdl(url, { quality: "highestaudio" });
// create the ffmpeg process for muxing
let ffmpegProcess = cp.spawn(
ffmpegPath,
[
// supress non-crucial messages
"-loglevel",
"8",
"-hide_banner",
// input audio and video by pipe
"-i",
"pipe:3",
"-i",
"pipe:4",
// map audio and video correspondingly
"-map",
"0:a",
"-map",
"1:v",
// no need to change the codec
"-c",
"copy",
// output mp4 and pipe
"-f",
"matroska",
"pipe:5",
],
{
// no popup window for Windows users
windowsHide: true,
stdio: [
"inherit",
"inherit",
"inherit",
// and pipe audio, video, output
"pipe",
"pipe",
"pipe",
],
}
);
audioStream.pipe(ffmpegProcess.stdio[3]);
videoStream.pipe(ffmpegProcess.stdio[4]);
ffmpegProcess.stdio[5].pipe(res);
});
app.get("/download/audio", async (req, res) => {
const { url, quality } = req.query;
const info = await ytdl.getBasicInfo(url);
res.header(
"Content-Disposition",
`attachment; filename="${info.videoDetails.title}_${quality}.mp3"`
);
res.header("Content-Type", "audio/mpeg");
ytdl(url, { quality, format: "mp3" }).pipe(res);
});
const port = process.env.PORT || 3000;
// Start the server
app.listen(port, () => {
console.log("Server is running");
});