Skip to content

johannesWen/Video-Merger

Repository files navigation

Video Merger

A browser-first MP4 merger built with Vite, React, TypeScript, @dnd-kit, and ffmpeg.wasm. Drop in your clips, reorder them on a timeline, trim segments, and export a single MP4 — all without leaving your browser.


GitHub release (latest by date) License GitHub issues GitHub stars GitHub forks

TypeScript React Vite FFmpeg Node


Preview

Main Timeline

Video Merger Main View

Edit & Trim Dialog

Edit Dialog


Table of Contents


Features

  • Drag & Drop Upload – Add multiple .mp4 files via picker, drop zone, or paste.
  • Smart Auto-Sort – Clips are automatically ordered by file modification time.
  • Clip Inspector – See duration, resolution, aspect ratio, and audio stream info per clip.
  • Sortable Timeline – Reorder clips with mouse or keyboard via @dnd-kit.
  • Trim Segments – Cut in/out points on any clip before merging.
  • Flexible Output – Choose from common output aspect ratios (16:9, 9:16, 1:1, 4:3, etc.).
  • Aspect Ratio Fit – Mismatched source frames are placed over a blurred background to fill the canvas.
  • Audio Normalization – Clips without audio receive a silent AAC track so mixed projects merge cleanly.
  • Hybrid Engine – Falls back to native backend FFmpeg for larger projects; browser FFmpeg for everything else.
  • 100% Private – Files are processed locally in the browser. Nothing is uploaded to a server.
  • Modern UI – React 18 + Lucide icons, accessible keyboard interactions.

Demo

  1. Clone the repo and run the dev server (see Installation).
  2. Open http://localhost:5173.
  3. Drop a few .mp4 files into the upload area.
  4. Reorder / trim as needed.
  5. Pick an output ratio and click Merge.

Sample clips are available in the examples/ directory (git-ignored) for quick testing.


Tech Stack

Layer Technology
UI Framework React 18
Build Tool Vite 6
Language TypeScript 5
Drag & Drop @dnd-kit
Browser Engine @ffmpeg/ffmpeg (WebAssembly)
Backend Engine Native ffmpeg / ffprobe via Express
HTTP Server Express 5
File Uploads multer
Icons lucide-react
Testing Vitest + jsdom
Concurrency concurrently

Architecture

┌────────────────────────┐         ┌──────────────────────────┐
│  React Frontend (Vite) │  /api   │  Express Backend (Node)  │
│  localhost:5173        │ ──────► │  localhost:5174          │
│                        │         │                          │
│  • Drag & Drop UI      │         │  • Upload endpoint       │
│  • Timeline / Trimmer  │         │  • FFmpeg/ffprobe probe  │
│  • Browser FFmpeg.wasm │         │  • Native merge fallback │
└────────────────────────┘         └──────────────────────────┘

The UI is engine-agnostic. A small MergeEngine interface lets the app swap between:

  • BrowserFfmpegEngineffmpeg.wasm running in a Web Worker. Always available, no install needed.
  • BackendFfmpegEngine – Native ffmpeg on the Node backend. Faster for large projects.
  • Hybrid (default) – Picks the best engine per project size and backend availability.

Installation

Prerequisites

  • Node.js >= 20.x (tested on 24.x)
  • npm >= 10.x
  • FFmpeg (optional, for the backend engine)

Clone

git clone https://github.com/johannesWen/Video-Merger.git
cd Video-Merger

Install dependencies

npm install

(Optional) Install native FFmpeg

The browser engine works out of the box. To enable the faster backend engine:

# Debian / Ubuntu
sudo apt install ffmpeg

# macOS
brew install ffmpeg

# Windows
choco install ffmpeg

Verify with:

ffmpeg -version
ffprobe -version

Getting Started

Start both the frontend and backend dev servers in one command:

npm run dev

This uses concurrently to run:

Service URL Description
Frontend http://localhost:5173 The React app (Vite dev server)
Backend http://localhost:5174 Express API for uploads and native FFmpeg jobs

The Vite dev server automatically sets the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers required by WebAssembly workers, and proxies /api/* to the backend.

Open the frontend URL in a modern browser (Chrome, Edge, or Firefox recommended).


Usage

  1. Add clips
    • Click the upload area, drag & drop files, or paste from clipboard.
    • Only .mp4 is supported (H.264 / AAC recommended).
  2. Review & reorder
    • Clips are auto-sorted by File.lastModified.
    • Drag clips on the timeline to reorder, or use the keyboard with @dnd-kit (Tab + Space + Arrows).
  3. Trim (optional)
    • Open the Edit dialog on any clip to set in/out points and preview.
  4. Choose output
    • Pick an aspect ratio (the default blurs the background to keep mismatched sources looking clean).
  5. Merge
    • Click Merge and wait for processing.
    • Download the resulting MP4.

Keyboard Shortcuts

Action Shortcut
Focus next clip Tab
Pick up / drop clip Space
Move clip Arrow Keys
Cancel drag Escape
Open clip editor Enter on focused

Engine Modes

You can switch the engine from the Engine control in the UI:

Mode Behavior
Hybrid Backend FFmpeg when available and project is large, otherwise browser.
Backend Always uses native FFmpeg on the Node server. Requires ffmpeg install.
Browser Always uses ffmpeg.wasm in the browser worker. No backend needed.

Hybrid is the default and recommended mode for the best balance of speed and portability.


Scripts

npm run dev            # Run frontend + backend together
npm run dev:frontend   # Vite dev server only (5173)
npm run dev:backend    # Backend only with tsx watch (5174)
npm run build          # Type-check (tsc) and build the production bundle
npm run preview        # Preview the production build
npm test               # Run the Vitest test suite once

Project Structure

Video-Merger/
├── assets/                 # README screenshots
│   ├── video_merger.png
│   └── edit_dialog.png
├── examples/               # Sample MP4s (git-ignored)
├── src/
│   ├── backend/            # Express server, multer uploads, native FFmpeg
│   ├── frontend/           # React UI: App, ClipPreviewModal, MissingClipsDialog
│   ├── processing/         # Engine adapters (Browser, Backend, filters, segments)
│   └── shared/             # Public types, media utils, session file, trim helpers
├── index.html              # Vite entry
├── vite.config.ts          # Vite config (proxy + COOP/COEP)
├── tsconfig*.json          # TypeScript project references
└── package.json

Key files:

  • src/frontend/App.tsx – Root React component, drag & drop, engine selection.
  • src/frontend/ClipPreviewModal.tsx – Per-clip editor with trim controls.
  • src/processing/BrowserFfmpegEngine.tsffmpeg.wasm adapter.
  • src/processing/BackendFfmpegEngine.ts – Native FFmpeg adapter.
  • src/processing/ffmpegFilters.ts – Shared filter graph builder.
  • src/backend/server.ts – Express API for uploads and merges.
  • src/shared/types.ts – Public media types and output settings.

Roadmap

  • Additional container formats (.mov, .webm)
  • GPU-accelerated encoding passthrough
  • Persistent projects (IndexedDB sessions)
  • Plugin system for custom filters

Contributing

Contributions are welcome!

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feat/amazing-feature
  3. Commit your changes: git commit -m "feat: add amazing feature"
  4. Push to your branch: git push origin feat/amazing-feature
  5. Open a Pull Request.

Please run npm test and npm run build before submitting.


License

Released under the MIT License.


Acknowledgements

  • FFmpeg.wasm for in-browser video processing.
  • @dnd-kit for accessible drag and drop.
  • Lucide for the icon set.
  • The React, Vite, and TypeScript teams for the amazing tooling.

Made with care by johannesWen · If you like this project, consider giving it a star!

About

A browser-first MP4 merger built with Vite, React, TypeScript, @dnd-kit, and ffmpeg.wasm. Drop in your clips, reorder them on a timeline, trim segments, and export a single MP4 — all without leaving your browser.

Topics

Resources

License

Stars

Watchers

Forks

Contributors