const express = require('express');
const cors = require('cors');
const ytdl = require('ytdl-core'); // Popular YouTube downloader library
const app = express();
// Allow your React Frontend to talk to this server
app.use(cors());
// 1. Endpoint to get Video Metadata (Title, Formats, etc.)
app.get('/api/info', async (req, res) => {
try {
const { url } = req.query;
if (!ytdl.validateURL(url)) {
return res.status(400).json({ error: 'Invalid YouTube URL' });
}
const info = await ytdl.getInfo(url);
// Filter and map formats to keep it simple for the frontend
const formats = ytdl.filterFormats(info.formats, 'videoandaudio');
res.json({
title: info.videoDetails.title,
thumbnail: info.videoDetails.thumbnails[0].url,
author: info.videoDetails.author.name,
duration: info.videoDetails.lengthSeconds,
formats: formats.map(f => ({
itag: f.itag,
quality: f.qualityLabel,
container: f.container,
hasAudio: f.hasAudio,
hasVideo: f.hasVideo
}))
});
} catch (error) {
console.error("Info Error:", error);
res.status(500).json({ error: 'Failed to fetch video info' });
}
});
// 2. Endpoint to Download the Video
app.get('/api/download', async (req, res) => {
try {
const { url, itag, title } = req.query;
if (!url) return res.status(400).send('URL is required');
// Set Headers to trigger browser download
// Sanitize title to remove special characters
const safeTitle = (title || 'video').replace(/[^a-zA-Z0-9 ]/g, "");
res.header('Content-Disposition', `attachment; filename="${safeTitle}.mp4"`);
// Start the download stream
ytdl(url, {
filter: format => format.itag == itag
}).pipe(res);
} catch (error) {
console.error("Download Error:", error);
res.status(500).send('Download failed');
}
});
const PORT = 4000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});