Skip to content

Commit b88cbd8

Browse files
authored
1.0.0
1 parent d8d0899 commit b88cbd8

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

index.js

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
const express = require("express");
2+
const cors = require("cors");
3+
const ytdl = require("ytdl-core");
4+
5+
const app = express();
6+
app.use(express.json());
7+
app.use(cors());
8+
app.use((err, req, res, next) => {
9+
if (err instanceof SyntaxError && err.status === 400 && "body" in err) {
10+
return res.status(400).send({ message: "Invalid JSON payload" });
11+
}
12+
next();
13+
});
14+
15+
app.get("/api", async (req, res) => {
16+
try {
17+
const url = req.query.url;
18+
if (!url || url.trim() === "") {
19+
return res.status(400).send({ message: "URL is required" });
20+
}
21+
22+
if (!ytdl.validateURL(url)) {
23+
throw new Error("Invalid video URL");
24+
}
25+
26+
const info = await ytdl.getInfo(url);
27+
28+
const data = {
29+
title: info.videoDetails.title,
30+
description: info.videoDetails.description,
31+
video_url: info.videoDetails.video_url,
32+
viewCount: info.videoDetails.viewCount,
33+
channel: {
34+
name: info.videoDetails.ownerChannelName,
35+
url: info.videoDetails.ownerProfileUrl,
36+
},
37+
publishDate: info.videoDetails.publishDate,
38+
updateDate: info.videoDetails.uploadDate,
39+
embed: info.videoDetails.embed,
40+
thumbnails: info.videoDetails.thumbnails.reverse(),
41+
formats: info.formats
42+
.filter((format) => format.audioBitrate)
43+
.map((format) => ({
44+
url: format.url,
45+
quality: format.qualityLabel,
46+
mimeType: format.mimeType,
47+
bitrate: format.bitrate,
48+
contentLength: format.contentLength,
49+
format: "",
50+
codecs: format.codecs,
51+
type: "",
52+
})),
53+
};
54+
55+
data.formats.forEach((format) => {
56+
if (format.mimeType.includes("video/mp4")) {
57+
format.type = "video";
58+
format.format = "mp4";
59+
} else if (format.mimeType.includes("video/webm")) {
60+
format.type = "video";
61+
format.format = "webm";
62+
} else if (format.mimeType.includes("audio/mp4")) {
63+
format.type = "audio";
64+
format.format = "mp4a";
65+
} else if (format.mimeType.includes("audio/webm")) {
66+
format.type = "audio";
67+
format.format = "webma";
68+
} else {
69+
format.type = "unknown";
70+
format.format = "unknown";
71+
}
72+
});
73+
74+
data.formats.sort((a, b) => {
75+
if (a.type < b.type) return 1;
76+
if (a.type > b.type) return -1;
77+
78+
if (a.type === "video") {
79+
if (a.quality < b.quality) return 1;
80+
if (a.quality > b.quality) return -1;
81+
}
82+
83+
return 0;
84+
});
85+
86+
res.setHeader("Content-Type", "application/json; charset=utf-8");
87+
res.send(JSON.stringify(data, null, 2));
88+
} catch (err) {
89+
res.status(500).send({ message: err.message });
90+
}
91+
});
92+
93+
app.post("/api", async (req, res) => {
94+
try {
95+
const url = req.body.url;
96+
if (!url || url.trim() === "") {
97+
return res.status(400).send({ message: "URL is required" });
98+
}
99+
100+
if (!ytdl.validateURL(url)) {
101+
throw new Error("Invalid video URL");
102+
}
103+
104+
const info = await ytdl.getInfo(url);
105+
106+
const data = {
107+
title: info.videoDetails.title,
108+
description: info.videoDetails.description,
109+
video_url: info.videoDetails.video_url,
110+
viewCount: info.videoDetails.viewCount,
111+
channel: {
112+
name: info.videoDetails.ownerChannelName,
113+
url: info.videoDetails.ownerProfileUrl,
114+
},
115+
publishDate: info.videoDetails.publishDate,
116+
updateDate: info.videoDetails.uploadDate,
117+
embed: info.videoDetails.embed,
118+
thumbnails: info.videoDetails.thumbnails.reverse(),
119+
formats: info.formats
120+
.filter((format) => format.audioBitrate)
121+
.map((format) => ({
122+
url: format.url,
123+
quality: format.qualityLabel,
124+
mimeType: format.mimeType,
125+
bitrate: format.bitrate,
126+
contentLength: format.contentLength,
127+
format: "",
128+
codecs: format.codecs,
129+
type: "",
130+
})),
131+
};
132+
133+
data.formats.forEach((format) => {
134+
if (format.mimeType.includes("video/mp4")) {
135+
format.type = "video";
136+
format.format = "mp4";
137+
} else if (format.mimeType.includes("video/webm")) {
138+
format.type = "video";
139+
format.format = "webm";
140+
} else if (format.mimeType.includes("audio/mp4")) {
141+
format.type = "audio";
142+
format.format = "mp4a";
143+
} else if (format.mimeType.includes("audio/webm")) {
144+
format.type = "audio";
145+
format.format = "webma";
146+
} else {
147+
format.type = "unknown";
148+
format.format = "unknown";
149+
}
150+
});
151+
152+
data.formats.sort((a, b) => {
153+
if (a.type < b.type) return 1;
154+
if (a.type > b.type) return -1;
155+
156+
if (a.type === "video") {
157+
if (a.quality < b.quality) return 1;
158+
if (a.quality > b.quality) return -1;
159+
}
160+
161+
return 0;
162+
});
163+
164+
res.setHeader("Content-Type", "application/json; charset=utf-8");
165+
res.send(JSON.stringify(data, null, 2));
166+
} catch (err) {
167+
res.status(400).send({ message: err.message });
168+
}
169+
});
170+
171+
const port = 3000;
172+
app.listen(port, () => {
173+
console.log(`Server is running on port ${port}`);
174+
});

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "youtube-api",
3+
"version": "1.0.0",
4+
"description": "fast and modern api for download videos/audios/thumbnails and get video info from youtube",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "node index.js"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/ReactMVC/YouTube-Node.git"
12+
},
13+
"author": "Hossein Pira",
14+
"license": "MIT",
15+
"bugs": {
16+
"url": "https://github.com/ReactMVC/YouTube-Node/issues"
17+
},
18+
"homepage": "https://github.com/ReactMVC/YouTube-Node",
19+
"dependencies": {
20+
"cors": "^2.8.5",
21+
"express": "^4.18.2",
22+
"ytdl-core": "^4.11.5"
23+
}
24+
}

0 commit comments

Comments
 (0)