| title | SaveIt Media Downloader |
|---|---|
| emoji | 📥 |
| colorFrom | blue |
| colorTo | indigo |
| sdk | docker |
| pinned | false |
SaveIt is a self-hosted web application for downloading public videos, audio, GIFs, and image posts from major social platforms. It uses Next.js server actions and streaming route handlers so media can be downloaded without being permanently stored by the application.
| Platform | Video | Audio | Images | Notes |
|---|---|---|---|---|
| YouTube | Up to 1080p, Shorts, playlists | MP3 | - | Playlist items support sequential progress downloads |
| TikTok | No-watermark and original variants | MP3 | Photo posts and slideshows | Short links supported |
| Reels, feed video, IGTV | When available | Photos and carousels | Public posts; cookies improve reliability | |
| Videos and Reels | MP3 | Image posts and multi-image posts | Direct permalinks and cookies are recommended | |
| X / Twitter | Videos and GIFs | When available | Image posts | Public posts only |
| Threads | Video posts | MP3 | Image posts | Experimental; supports canonical, /t, and /share links; use Retry Failed when an interrupted job needs another attempt |
| Native video posts | MP3 | Image posts and galleries | Public reddit.com, gallery, and redd.it post links |
The application also includes URL/platform validation, links to the correct
downloader when a URL is pasted on the wrong page, responsive image previews,
server-side download progress before browser save, sequential playlist queues,
download history stored in the browser, and platform-specific error messages.
Video downloads are normalized on the server to MP4 with iOS-friendly audio and
metadata. The default path remuxes/copies compatible video for speed and only
transcodes audio when needed.
Completed MP4 jobs are finalized with faststart metadata so they are friendlier
for sharing to apps such as WhatsApp.
- Platform extractors can break when a website changes its markup or API.
- Private, deleted, region-blocked, age-restricted, or login-only content may require valid cookies and may still be unavailable.
- YouTube may reject data-center IP addresses with
Sign in to confirm you are not a bot. ConfigureYTDLP_COOKIES_BASE64when this happens. - Facebook share links are resolved to their canonical post automatically, but
logged-out Facebook HTML may expose only the first carousel preview. Configure
SOCIAL_COOKIES_BASE64to expose all public images visible to that account. - TikTok photo CDN URLs are temporary. Click Fetch again if a preview has expired or fails to load.
- Threads accepts both
threads.comandthreads.netlinks in canonical/@user/post/<id>,/t/<id>,/@/post/<id>, and/share/<id>forms. Deleted or expired share links still cannot be recovered. - Reddit supports public native videos, image posts, and galleries. Video downloads depend on the post's separate video/audio tracks remaining available; private, deleted, or externally hosted media is not supported.
- Progress downloads are assembled on the SaveIt server in temporary files and then streamed to the browser after completion. Very large files therefore use homelab disk space and bandwidth rather than browser memory.
- Cross-platform MP4 output uses server-side
ffmpeg. By default SaveIt uses a fast hybrid remux path. SetSAVEIT_FORCE_VIDEO_TRANSCODE=1only when you need strict H.264/AAC re-encoding for stubborn devices or files. - Temporary progress files are stored in the OS temp directory and expire after roughly 30 minutes.
SaveIt uses several extraction paths because no single tool supports every media type:
yt-dlpextracts video and audio.instaloaderextracts Instagram sidecar/carousel media when yt-dlp or gallery-dl return incomplete mixed photo/video metadata.gallery-dlextracts supported image posts and carousels.@tobyg74/tiktok-api-dlprovides an additional TikTok photo/slideshow fallback when TikWM links are stale or incomplete.- Reddit's public post JSON provides direct image/gallery metadata, while yt-dlp selects and merges native video and audio streams.
- Open Graph and embedded page metadata provide additional image fallbacks.
ffmpegmerges YouTube streams and converts supported audio to MP3.- Cheerio parses fallback HTML responses.
When local extractors cannot read a public URL, the application may send that URL to these external extraction services:
- TikTok photo posts: tiktok-api-dl, then TikWM
- Facebook public share links: SnapSave
- Threads public posts: LoveThreads
Authentication cookies remain inside the SaveIt container and are never forwarded to those fallback services. Do not use SaveIt for sensitive or private URLs unless you understand the privacy implications.
| Component | Technology |
|---|---|
| Web framework | Next.js 16 App Router, React 19, TypeScript |
| Styling | Tailwind CSS 4 |
| Runtime | Node.js 22 Alpine |
| Package manager | pnpm 11.3.0 |
| Video extraction | yt-dlp |
| Image extraction | instaloader, gallery-dl, tiktok-api-dl, page metadata, Cheerio |
| Media processing | ffmpeg |
| Deployment | Docker, Docker Compose, Coolify |
Docker is the recommended installation method because the image already
contains yt-dlp, instaloader, gallery-dl, ffmpeg, Python, and runtime
dependencies. The Node image also installs the native libraries needed by the
TikTok fallback dependency.
git clone https://github.com/ffigoperdana/video-downloader.git
cd video-downloader
docker compose up -d --build
docker compose logs -fThe Compose service exposes container port 7860 only to its Docker network.
It intentionally does not bind a host port, which avoids port conflicts on
Coolify and other reverse-proxy platforms.
For plain Docker outside Coolify:
docker build -t saveit .
docker run -d \
--name saveit \
-p 8080:7860 \
--restart unless-stopped \
saveitOpen http://localhost:8080.
- Create a Docker Compose resource from this repository.
- Keep
docker-compose.ymlas the Compose file. - Configure the public domain for service
saveiton container port7860. - Do not add a host mapping such as
8080:7860; Coolify routes traffic over its Docker network. - Add cookie variables as secrets when authentication is required.
- Deploy. The included health check calls
http://localhost:7860/.
The GitHub workflow in .github/workflows/docker-ci.yml validates Compose,
builds the Docker image, starts it, and waits for the container health check.
| Variable | Default | Description |
|---|---|---|
PORT |
7860 in Docker |
Next.js listening port |
YTDLP_BINARY_PATH |
/usr/local/bin/yt-dlp in Docker |
Custom yt-dlp path |
GALLERY_DL_BINARY_PATH |
/usr/bin/gallery-dl in Docker |
Custom gallery-dl path |
PYTHON_BINARY_PATH |
/usr/bin/python3 in Docker |
Custom Python path for Instaloader fallback |
YTDLP_COOKIES_PATH |
empty | Netscape cookies file path for YouTube |
YTDLP_COOKIES_BASE64 |
empty | Base64 Netscape cookies for YouTube |
GALLERY_DL_COOKIES_PATH |
empty | Netscape cookies file path for social platforms |
SOCIAL_COOKIES_BASE64 |
empty | Base64 Netscape cookies for Instagram, Facebook, and TikTok; also used by Instagram's yt-dlp fallback |
INSTAGRAM_COOKIES_BASE64 |
empty | Optional dedicated Base64 Netscape cookies for Instagram. Overrides the Instagram entries in SOCIAL_COOKIES_BASE64 for yt-dlp extraction. |
Export cookies as Netscape cookies.txt using a local browser extension. Use
a dedicated account and never commit the cookie file or Base64 value.
Convert YouTube cookies to Base64 in PowerShell:
[Convert]::ToBase64String(
[IO.File]::ReadAllBytes("$HOME\Downloads\youtube-cookies.txt")
)Store the output as YTDLP_COOKIES_BASE64 in Coolify.
To combine Instagram, Facebook, and TikTok cookies:
$files = @(
"$HOME\Downloads\instagram-cookies.txt",
"$HOME\Downloads\facebook-cookies.txt",
"$HOME\Downloads\tiktok-cookies.txt"
)
$output = "$HOME\Downloads\social-cookies.txt"
"# Netscape HTTP Cookie File" | Set-Content $output -Encoding ASCII
foreach ($file in $files) {
Get-Content $file |
Where-Object {
$_.Trim() -ne "" -and
$_ -notmatch "^# Netscape HTTP Cookie File"
} |
Add-Content $output -Encoding ASCII
}
[Convert]::ToBase64String([IO.File]::ReadAllBytes($output))Store the output as SOCIAL_COOKIES_BASE64. Cookies expire and must be
exported again periodically.
Requirements:
- Node.js 22 or newer
- pnpm 11
- yt-dlp
- instaloader
- gallery-dl
- ffmpeg
pnpm install
pnpm devThe development server runs at http://localhost:3000 unless PORT is set.
Quality checks:
pnpm test
pnpm exec tsc --noEmit
pnpm buildBrowser
-> platform Server Action validates and normalizes the URL
-> yt-dlp, instaloader, gallery-dl, page metadata, or a public fallback extracts media
-> /internal/download/* streams video and audio
-> /internal/media/image proxies image previews and attachments
-> /internal/media/video proxies Threads and Instagram fallback video media
-> ffmpeg merges streams or converts supported audio to MP3
Media URLs from social CDNs are often signed and short-lived. The internal routes re-extract or proxy them so browser downloads receive the required headers without exposing authentication cookies.
.github/workflows/ Docker build and deployment checks
src/actions/ Server actions for each platform
src/app/ Pages and internal streaming routes
src/components/ Shared downloader and media UI
src/core/hooks/ Download history and batch state
src/core/services/ Platform extraction and streaming services
src/core/utils/ URL validation and formatting helpers
Dockerfile Multi-stage production image
docker-compose.yml Coolify-friendly Compose service
Export fresh YouTube cookies and configure YTDLP_COOKIES_BASE64. Use a
dedicated account because cookies grant access to the active login session.
Share links such as /share/p/... are resolved automatically. Confirm the post
is visible to the cookie account and configure SOCIAL_COOKIES_BASE64.
Logged-out Facebook HTML often contains only the carousel cover.
Click Fetch again. TikTok signs image URLs and they can expire quickly.
Use Retry Failed in the progress panel. Progress jobs are prepared on the SaveIt server first, then streamed to the browser when complete.
Do not publish a fixed host port in Coolify. Keep Compose expose: 7860 and
let Coolify's reverse proxy route the domain to that container port.
SaveIt is intended for personal use with content you are authorized to access and download. Respect platform terms, copyright law, privacy, and content creators' rights. The maintainers do not endorse unauthorized redistribution.