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
import express from 'express';
import cors from 'cors';
import ytdl from 'ytdl-core';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
const PORT = process.env.PORT || 3001;
// Create downloads directory if it doesn't exist
const downloadsDir = path.join(__dirname, 'downloads');
if (!fs.existsSync(downloadsDir)) {
fs.mkdirSync(downloadsDir);
}
// Middleware
app.use(cors());
app.use(express.json());
app.use('/downloads', express.static(downloadsDir));
// Routes
app.get('/api/info', async (req, res) => {
const { url } = req.query;
if (!url) {
return res.status(400).json({ error: 'URL is required' });
}
try {
const info = await ytdl.getInfo(url);
// Extract relevant video information
const videoDetails = info.videoDetails;
const formats = info.formats;
// Process audio formats (mp3)
const audioFormats = formats
.filter(format => format.hasAudio && !format.hasVideo)
.sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate));
// Process video formats (mp4)
const videoFormats = formats
.filter(format => format.hasVideo && format.container === 'mp4')
.sort((a, b) => Number(b.height) - Number(a.height));
// Group video formats by resolution
const groupedVideoFormats = [];
const resolutions = ['4K', '1080p', '720p', '480p', '360p'];
const targetHeights = [2160, 1080, 720, 480, 360];
targetHeights.forEach((height, index) => {
const format = videoFormats.find(f => f.height === height);
if (format) {
groupedVideoFormats.push({
quality: resolutions[index],
itag: format.itag,
size: format.contentLength ? parseInt(format.contentLength) : 'Unknown'
});
}
});
// Group audio formats by quality
const groupedAudioFormats = [];
if (audioFormats.length > 0) {
groupedAudioFormats.push({
quality: 'High (320 kbps)',
itag: audioFormats[0].itag,
size: audioFormats[0].contentLength ? parseInt(audioFormats[0].contentLength) : 'Unknown'
});
if (audioFormats.length > 1) {
groupedAudioFormats.push({
quality: 'Medium (192 kbps)',
itag: audioFormats[1].itag,
size: audioFormats[1].contentLength ? parseInt(audioFormats[1].contentLength) : 'Unknown'
});
}
if (audioFormats.length > 2) {
groupedAudioFormats.push({
quality: 'Low (128 kbps)',
itag: audioFormats[2].itag,
size: audioFormats[2].contentLength ? parseInt(audioFormats[2].contentLength) : 'Unknown'
});
}
}
res.json({
id: videoDetails.videoId,
title: videoDetails.title,
author: videoDetails.author.name,
thumbnail: videoDetails.thumbnails[videoDetails.thumbnails.length - 1].url,
duration: parseInt(videoDetails.lengthSeconds),
views: parseInt(videoDetails.viewCount),
audioFormats: groupedAudioFormats,
videoFormats: groupedVideoFormats
});
} catch (error) {
console.error('Error fetching video info:', error);
res.status(500).json({ error: 'Failed to fetch video information' });
}
});
app.get('/api/download', async (req, res) => {
const { url, itag, format } = req.query;
if (!url || !itag) {
return res.status(400).json({ error: 'URL and itag are required' });
}
try {
const info = await ytdl.getInfo(url);
const videoDetails = info.videoDetails;
// Create a sanitized filename
const sanitizedTitle = videoDetails.title.replace(/[^\w\s]/gi, '').substring(0, 100);
const filename = `${sanitizedTitle}-${format}.${format === 'mp3' ? 'mp3' : 'mp4'}`;
const filePath = path.join(downloadsDir, filename);
// Set headers for file download
res.header('Content-Disposition', `attachment; filename="${filename}"`);
if (format === 'mp3') {
res.header('Content-Type', 'audio/mpeg');
ytdl(url, { quality: itag }).pipe(res);
} else {
res.header('Content-Type', 'video/mp4');
ytdl(url, { quality: itag }).pipe(res);
}
} catch (error) {
console.error('Error downloading video:', error);
res.status(500).json({ error: 'Failed to download video' });
}
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
console.log('YouTube Downloader API is ready!');