A production-minded media server written in Go that lets you:
- Upload audio, video, and images
- Deliver audio/video via HLS (segmented playback, smooth buffering) and images via direct streaming
- Embed playback on any website using a YouTube-style iframe
- Manage uploads through a simple /admin page protected by an API key
- Return a clean, professional JSON response containing
iframeHTML, playback URLs, and metadata
Designed to stay responsive under load: HLS segments are cacheable, stream endpoints support Range requests, and
ffmpegtranscoding is concurrency-limited.
- Media types
- ✅ Audio → HLS (multi-bitrate AAC)
- ✅ Video → HLS (H.264 + AAC)
- ✅ Image → Stream (direct file)
- Delivery
hls:/hls/{id}/master.m3u8stream:/s/{id}(Range-enabled for audio/video/images)embed:/embed/{id}(iframe-ready)
- Admin
/adminrequires API key (via?apikey=or headerX-API-Key)- Upload UI supports audio/video/image and shows iframe preview
- API
- Upload endpoint returns
json{ iframe, track, filename, media, delivery, ... } - Public metadata endpoint for embed player
- Upload endpoint returns
- Security & Embed
- CSP allows iframe embedding (
frame-ancestors *) - CORS enabled for playback assets (HLS + stream)
- CSP allows iframe embedding (
- Go 1.20+ recommended
- FFmpeg installed and available in PATH (or set
FFMPEG_PATH)
Check FFmpeg:
ffmpeg -versionPORT=3000
BASE_URL=<DOMAIN_SET>
DATA_DIR=./data
ADMIN_API_KEY=KEY_SECRET_API
MAX_UPLOAD_MB=1000
FFMPEG_PATH=ffmpeg
FFMPEG_WORKERS=6
HLS_SEGMENT_SECONDS=5go mod init media-embed
go get github.com/joho/godotenv
go run main.goOpen admin:
<DOMAIN_SET>/admin?apikey=KEY_SECRET_API
DATA_DIR/
uploads/{id}/
original.* # uploaded file
meta.json # metadata
hls/{id}/
master.m3u8 # hls master playlist
v064/ # audio variant
v128/ # audio variant
v192/ # audio variant
seg_*.ts / *.aac # segments
-
Go to:
GET /admin?apikey=YOUR_ADMIN_API_KEY -
Upload a file (audio/video/image)
-
Admin page will show:
- JSON output
- iframe preview
- URLs you can use on other sites
Supply API key using either:
- Header:
X-API-Key: <YOUR_ADMIN_API_KEY> - Query:
?apikey=<YOUR_ADMIN_API_KEY>(admin page convenience)
POST /api/upload
Auth: Required
Body: multipart/form-data
Field:
file(required): audio/video/image
Example (curl):
curl -X POST "<DOMAIN_SET>/api/upload" \
-H "X-API-Key: YOUR_ADMIN_API_KEY" \
-F "file=@/path-to/video.mp4"Success response (example):
{
"ok": true,
"id": "AbC123xYz890",
"filename": "video.mp4",
"media": "video",
"delivery": "hls",
"track": "AbC123xYz890",
"embed_url": "<DOMAIN_SET>/embed/AbC123xYz890",
"stream_url": "<DOMAIN_SET>/s/AbC123xYz890",
"hls_master_url": "<DOMAIN_SET>/hls/AbC123xYz890/master.m3u8",
"iframe": "<iframe src=\"<DOMAIN_SET>/embed/AbC123xYz890\" style=\"width:100%;height:360px;border:0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen loading=\"lazy\"></iframe>",
"player_hints": {
"embed": "/embed/{id}",
"stream": "/s/{id}",
"hls": "/hls/{id}/master.m3u8"
}
}Notes:
mediais one of:audio | video | imagedeliveryis one of:hls | stream- audio/video →
hls - image →
stream
- audio/video →
GET /api/media/{id}
Auth: Not required
Example:
curl "<DOMAIN_SET>/api/media/AbC123xYz890"Returns:
{
"ok": true,
"meta": {
"id": "AbC123xYz890",
"filename": "video.mp4",
"media": "video",
"delivery": "hls",
"created_at": "2026-01-25T12:34:56Z",
"size_bytes": 12345678,
"orig_mime": "video/mp4",
"stream_path": "/s/AbC123xYz890",
"hls_master_url": "/hls/AbC123xYz890/master.m3u8",
"embed_url": "<DOMAIN_SET>/embed/AbC123xYz890"
}
}GET /embed/{id}
Designed to be used in an <iframe> on other websites.
Example:
<iframe src="https://your-domain.com/embed/AbC123xYz890" style="width:100%;height:360px;border:0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen loading="lazy"> </iframe>The embed page:
-
Detects media type from
/api/media/{id} -
Renders:
<img>for images<audio>for audio<video>for video
-
Uses HLS when available, falls back to stream URL if needed
GET /s/{id}
- Supports HTTP Range (great for progressive playback + seeking)
- Used for images and as fallback for media
Example:
curl -I "<DOMAIN_SET>/s/AbC123xYz890"GET /hls/{id}/master.m3u8
And segments under:
/hls/{id}/...
HLS is the recommended delivery mode for audio/video at scale.
-
HLS is cache-friendly Segments (
.ts,.aac) can be cached for a long time, playlists (.m3u8) cached shorter. -
Transcoding is CPU-heavy Use
FFMPEG_WORKERSto cap concurrent ffmpeg jobs:- VPS 2 vCPU →
1–2 - VPS 4 vCPU →
2–3
- VPS 2 vCPU →
-
For large audiences Move
/hlsand/sto object storage + CDN:- Cloudflare R2 + CDN
- S3 + CloudFront
- Any CDN that supports caching and Range
-
Keep
ADMIN_API_KEYlong and secret. -
For stricter embedding:
- Replace
frame-ancestors *with a whitelist, e.g. only your domains.
- Replace
-
Consider adding:
- Upload rate limiting
- Virus scanning (optional)
- Signed URLs (if media must be private)
- Ensure your reverse proxy/CDN isn’t adding
X-Frame-Options: DENYor restrictive CSP. - Confirm the server responds with
Content-Security-Policycontainingframe-ancestors.
- Verify FFmpeg is installed and
FFMPEG_PATHis correct. - Check server logs for
ffmpeg ... failed. - Confirm HLS playlist exists:
/hls/{id}/master.m3u8
- Lower
FFMPEG_WORKERS - Increase segment duration (
HLS_SEGMENT_SECONDS=6) - Consider async job queue + background workers for transcoding
- Async transcoding + job status endpoint (
/api/jobs/{id}) - Multi-bitrate video ABR (360p/720p/1080p)
- Signed embed URLs & token-gated media
- Post-processing: thumbnail extraction, waveform, metadata parsing
MIT (or choose your preferred license).