A Manifest V3 Chromium extension that detects and downloads videos from the web — HLS, MPEG-DASH, and direct video URLs.
- Multiple Format Support: HLS (
.m3u8), MPEG-DASH (.mpd), and direct video URLs (.mp4,.webm, etc.) - Automatic Video Detection: Content script detects videos via DOM observation and network request interception
- Live Stream Recording: Record live HLS and DASH streams in real-time with a REC button
- Popup Interface: Manual URL input with quality selector for HLS/DASH playlists
- Real-time Progress: Download speed, percentage, and stage displayed live
- Segment Merging: FFmpeg.wasm muxes HLS/DASH segments into MP4 files
- Concurrent Downloads: Up to 10 simultaneous segment downloads (default: 3)
- Partial Save on Cancel: Save whatever segments were collected before cancellation
- AES-128 Decryption: Decrypts encrypted HLS segments transparently
- Header Injection: Injects
Origin/Refererheaders viadeclarativeNetRequestfor CDNs that require them - Download History: Completed, failed, and cancelled downloads are persisted and browsable in the options page History section with infinite scroll
- Notifications: Optional OS notification and auto-open file on download completion
- Cloud Upload: Upload completed downloads to Google Drive or S3-compatible storage from the History page
- Configurable Settings: Recording poll intervals, fetch retry behaviour, detection cache sizes, IDB sync rate — all tunable from the options page
Video processing runs entirely inside the browser, so output files are subject to browser memory limits. In practice, files above roughly 2 GB will fail during the merge stage.
Completed downloads can be uploaded to Google Drive or S3-compatible storage from Options → History → item menu → Upload to cloud.
Google Drive requires you to create your own OAuth credentials (free):
- Go to Google Cloud Console and create a project (or use an existing one).
- Enable the Google Drive API.
- Go to Credentials → Create Credentials → OAuth client ID.
- Set application type to Web application.
- Under Authorized redirect URIs, add your extension's redirect URI.
- Find it in Options → Cloud Providers → Google Drive — it's shown next to the Client ID field.
- It looks like
https://<extension-id>.chromiumapp.org/
- Copy the Client ID and paste it into the options page.
- Click Sign in with Google to authorize.
Note: If you haven't configured a consent screen yet, Google will prompt you to create one. Choose External user type, fill in the required fields, and add yourself as a test user. The app will work in "Testing" mode — no verification needed for personal use.
- In Options → Cloud Providers → S3, enter your bucket name, region, access key ID, and secret access key.
- Your S3 bucket must have a CORS policy that allows the extension origin. The options page generates the correct JSON and provides a Copy CORS Config button — paste it into S3 → Bucket → Permissions → CORS.
- Works with AWS S3, Cloudflare R2, Backblaze B2, Wasabi, MinIO, and any S3-compatible provider.
- Clone the repository:
git clone <repository-url>
cd media-bridge- Install dependencies:
npm install- Build the extension:
npm run build- Load the extension in Chrome:
- Open Chrome and navigate to
chrome://extensions/ - Enable "Developer mode" (toggle in top right)
- Click "Load unpacked"
- Select the
distdirectory
- Open Chrome and navigate to
npm run buildThe built extension will be in the dist directory.
- Click the extension icon in your browser toolbar
- Go to the Manifest tab
- Paste a video URL (HLS, DASH, or direct video)
- Select quality (for HLS/DASH playlists)
- Click "Download"
When visiting a page with video content:
- The extension automatically detects videos via network request interception
- Detected videos appear in the Videos tab
- Click "Download" to start
When a live stream is detected:
- A REC button appears in the popup
- Click REC to start recording segments in real-time
- Click STOP to stop — segments collected so far are merged into an MP4
| Format | Detection | VOD Download | Live Recording |
|---|---|---|---|
HLS (.m3u8 master playlist) |
✅ | ✅ | ✅ |
M3U8 (.m3u8 media playlist) |
✅ | ✅ | — |
DASH (.mpd manifest) |
✅ | ✅ | ✅ |
Direct (.mp4, .webm, etc.) |
✅ | ✅ | — |
Media Bridge has five distinct execution contexts that communicate via chrome.runtime.sendMessage:
- Service Worker (
src/service-worker.ts): Central orchestrator. Routes messages, manages download lifecycle, keeps itself alive via heartbeat. - Content Script (
src/content.ts): Runs on all pages. Detects videos via DOM observation and network interception. Proxies fetch requests through the service worker to bypass CORS. - Offscreen Document (
src/offscreen/): Hidden page that runs FFmpeg.wasm. Reads segment data from IndexedDB, muxes into MP4, returns a blob URL. - Popup (
src/popup/): Extension action UI — Videos tab (detected videos), Downloads tab (in-progress only), Manifest tab (manual URL + quality selector). A History button opens the options page directly on the history section. - Options Page (
src/options/): Full settings UI with sidebar navigation — Download, History, Google Drive, S3, Recording, Notifications, and Advanced sections. All settings changes are confirmed via a bottom toast notification.
HLS / DASH VOD:
- Service worker creates a
DownloadManager, which delegates to the format-specific handler - Handler parses the manifest and selects highest-bandwidth video + audio
- Segments downloaded concurrently (up to
maxConcurrent), stored asUint8Arrayin IndexedDB OFFSCREEN_PROCESS_*message triggers FFmpeg muxing in the offscreen document- FFmpeg returns a blob URL; service worker triggers
chrome.downloads.download()→ saves MP4
Direct URLs: chrome.downloads.download() used directly, no FFmpeg needed.
Live Recording: Handler polls the media playlist/MPD at the stream's native interval, collecting new segments. On stop (or stream end), collected chunks are merged into an MP4.
| Store | Data | Reason |
|---|---|---|
IndexedDB (media-bridge v3) |
downloads (state + history), chunks (segments) |
Survives restarts; supports large ArrayBuffer |
chrome.storage.local |
All config via loadSettings() / AppSettings |
Simple K/V; 10 MB quota |
src/
├── service-worker.ts # Background service worker (central hub)
├── content.ts # Content script (IIFE format, video detection)
├── shared/
│ ├── messages.ts # MessageType enum (all inter-context messages)
│ └── constants.ts # Global defaults (concurrency, timeouts, etc.)
├── core/
│ ├── types/
│ │ └── index.ts # VideoFormat, DownloadState, DownloadStage, Fragment, Level
│ ├── detection/
│ │ ├── detection-manager.ts
│ │ ├── thumbnail-utils.ts
│ │ ├── direct/ # Direct video detection
│ │ ├── hls/ # HLS detection
│ │ └── dash/ # DASH detection
│ ├── downloader/
│ │ ├── download-manager.ts
│ │ ├── base-playlist-handler.ts # Shared segment download logic
│ │ ├── base-recording-handler.ts # Shared live recording logic
│ │ ├── concurrent-workers.ts
│ │ ├── crypto-utils.ts # AES-128 decryption
│ │ ├── header-rules.ts # declarativeNetRequest header injection
│ │ ├── direct/ # Direct download handler
│ │ ├── hls/ # HLS download + recording handlers
│ │ ├── m3u8/ # M3U8 (media playlist) handler
│ │ └── dash/ # DASH download + recording handlers
│ ├── parsers/
│ │ ├── m3u8-parser.ts # HLS playlist parsing (m3u8-parser)
│ │ ├── mpd-parser.ts # DASH/MPD parsing (mpd-parser)
│ │ └── playlist-utils.ts # Shared ParsedPlaylist/ParsedSegment types
│ ├── ffmpeg/
│ │ ├── ffmpeg-bridge.ts # Unified FFmpeg request interface
│ │ ├── ffmpeg-singleton.ts
│ │ └── offscreen-manager.ts
│ ├── database/
│ │ ├── connection.ts # IDB init (media-bridge v3)
│ │ ├── downloads.ts # Download state CRUD
│ │ └── chunks.ts # Segment chunk storage
│ ├── storage/
│ │ ├── chrome-storage.ts # Raw chrome.storage.local access
│ │ └── settings.ts # AppSettings interface + loadSettings() — always use this
│ ├── cloud/ # Google Drive + S3 upload providers
│ │ ├── google-auth.ts # OAuth via launchWebAuthFlow (user-provided client ID)
│ │ ├── google-drive.ts # Resumable upload (chunked for files > 5 MB)
│ │ ├── s3-client.ts # SigV4-signed PUT / multipart upload
│ │ └── upload-manager.ts # Provider registry + routing
│ ├── metadata/
│ │ └── metadata-extractor.ts
│ └── utils/
│ ├── blob-utils.ts
│ ├── cancellation.ts
│ ├── crypto-utils.ts # AES-128 decryption
│ ├── download-utils.ts
│ ├── drm-utils.ts # DRM detection (FairPlay, PlayReady)
│ ├── errors.ts # Custom error classes
│ ├── fetch-utils.ts # CORS-aware fetch with retries
│ ├── file-utils.ts
│ ├── format-utils.ts
│ ├── id-utils.ts
│ ├── logger.ts
│ └── url-utils.ts
├── popup/ # Popup UI (Videos / Downloads / Manifest tabs)
├── options/ # Options page (Download, History, Drive, S3, Recording, Notifications, Advanced)
├── offscreen/ # Offscreen document (FFmpeg.wasm processing)
└── types/
└── mpd-parser.d.ts # Type declarations for mpd-parser
storage— Config persistencedownloads— Save downloaded filesidentity— Google OAuth vialaunchWebAuthFlowactiveTab/scripting— Content script injectionoffscreen— Offscreen document for FFmpeg.wasmunlimitedStorage— Large segment storage in IndexedDBwebRequest— Intercept.m3u8/.mpdnetwork requestsdeclarativeNetRequest— InjectOrigin/Refererheaderstabs/webNavigation— Tab tracking for video detection- Host permissions (
http://*/* https://*/*) — Fetch video content
# Development build with watch mode
npm run dev
# Production build
npm run build
# TypeScript type checking only
npm run type-check| Package | Purpose |
|---|---|
@ffmpeg/ffmpeg @ffmpeg/util |
FFmpeg.wasm segment muxing (≤ ~2 GB output) |
m3u8-parser |
HLS playlist parsing |
mpd-parser |
DASH/MPD manifest parsing |
idb |
IndexedDB wrapper |
uuid |
Unique download IDs |
url-toolkit |
URL resolution for relative segment paths |
- ~2 GB output limit — The muxer runs in browser memory; files larger than ~2 GB fail during merging.
- DRM content — FairPlay and PlayReady protected streams cannot be downloaded.
- CDN restrictions — Some sites block extension requests via token auth or IP restrictions.
- Browser memory — Total concurrent segment data is limited by available RAM.
- Verify the URL is accessible and not DRM-protected
- Check browser console for errors
- Ensure the format is supported (HLS, DASH, or direct)
- File may exceed the ~2 GB limit — try a shorter clip or lower quality
- Increase FFmpeg timeout in Options → Download Settings if processing is slow
- Check the offscreen document console for FFmpeg error output
- Some sites obfuscate network requests or use proprietary players
- Use the Manifest tab to paste the URL manually
- Check browser console for content script errors
MIT License — see LICENSE file for details