A full-stack video downloader with a built-in trim feature. Supports YouTube, X (Twitter), Instagram, and Facebook. Download any quality, or trim the video to an exact clip before downloading — all for free with a real-time progress bar.
- Download videos from YouTube, X, Instagram, and Facebook
- Choose download quality (144p up to 1080p+)
- Trim videos to an exact start and end time before downloading
- Real-time progress bar during download and trim phases
- Automatic server-side caching — re-downloading the same video is instant
- Files auto-deleted from server after 2 hours
Frontend
- React + Vite
- Tailwind CSS v4
- React Router DOM
- Axios
Backend
- Node.js + Express
- yt-dlp (video fetching and downloading)
- ffmpeg (video trimming and re-encoding)
- Server-Sent Events (SSE) for real-time progress streaming
Make sure these are installed globally on your machine before running the project.
Node.js — https://nodejs.org (LTS version)
yt-dlp
winget install yt-dlpffmpeg
winget install ffmpegVerify both are on your PATH:
yt-dlp --version
ffmpeg -versionyoutube-downloader/
├── backend/
│ ├── src/
│ │ ├── controllers/
│ │ │ ├── video.controller.js # Fetch video metadata
│ │ │ └── download.controller.js # SSE stream, serve files
│ │ ├── middleware/
│ │ │ ├── cors.js
│ │ │ └── errorHandler.js
│ │ ├── routes/
│ │ │ ├── video.routes.js
│ │ │ └── download.routes.js
│ │ ├── services/
│ │ │ ├── ytdlp.service.js # yt-dlp wrapper
│ │ │ ├── ffmpeg.service.js # ffmpeg trim wrapper
│ │ │ └── cache.service.js # disk cache + cleanup
│ │ ├── app.js
│ │ └── server.js
│ ├── temp/ # Raw yt-dlp downloads (auto-created)
│ ├── output/ # Trimmed files (auto-created)
│ ├── .env
│ └── package.json
│
└── frontend/
├── src/
│ ├── components/
│ │ ├── PlatformSelector.jsx
│ │ ├── VideoCard.jsx
│ │ ├── QualitySelector.jsx
│ │ ├── TrimControls.jsx
│ │ └── ProgressBar.jsx
│ ├── pages/
│ │ ├── HomePage.jsx
│ │ ├── VideoPage.jsx
│ │ └── ResultPage.jsx
│ ├── services/
│ │ └── api.js
│ ├── App.jsx
│ ├── main.jsx
│ └── index.css
└── package.json
cd backend
npm installCreate a .env file in the backend/ folder:
PORT=5000
TEMP_DIR=./temp
OUTPUT_DIR=./output
MAX_FILE_AGE_HOURS=2Start the dev server:
npm run devBackend runs on http://localhost:5000
cd frontend
npm install
npm run devFrontend runs on http://localhost:5173
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/video/info?url= |
Fetch video metadata and available qualities |
| POST | /api/download/start |
Start download or trim+download via SSE stream |
| GET | /api/download/file/:filename |
Serve the final file for download |
{
"url": "https://www.youtube.com/watch?v=...",
"formatId": "137",
"height": 1080,
"trim": { "start": 30, "end": 90 }
}trim is optional — omit it for a normal download without trimming.
The /api/download/start endpoint streams Server-Sent Events:
event: progress
data: { "phase": "download", "percent": 45.3 }
event: progress
data: { "phase": "trim", "percent": 72.1 }
event: done
data: { "filename": "abc123.mp4" }
event: error
data: { "message": "Download failed" }
- User pastes a video URL on the home page and clicks Fetch
- Backend calls
yt-dlp --dump-jsonto get metadata without downloading - Frontend shows video info, available qualities, and Download / Trim & Download tabs
- User picks quality and optionally sets trim start/end points
- Frontend opens a
fetch()stream to the backend (POST with JSON body) - Backend spawns
yt-dlpto download the video totemp/, streaming progress via SSE - If trimming, backend then spawns
ffmpegto re-encode the clip tooutput/, also streaming progress - When complete, backend sends a
doneevent with the filename - Frontend navigates to the result page where the user can save the file
- Files are automatically deleted from the server after 2 hours
The first time a URL + quality combination is downloaded, the file is saved to temp/ with an MD5 hash of the URL+formatId as the filename. If the same video and quality are requested again within 2 hours, the cached file is used immediately — no re-download.
- For personal use only. Respect content creators and platform terms of service.
- Trim re-encodes the video using
libx264at CRF 18 (near-lossless) to avoid keyframe boundary artifacts that occur with stream copying. - The progress bar uses SSE instead of WebSockets because progress only flows in one direction — server to client — making SSE simpler and sufficient.