A lightweight M3U8 downloader written in Go (v1.3.0). It parses HLS playlists, downloads TS segments concurrently, decrypts AES-128 and cloud VOD encrypted streams, merges them into a single file, and optionally remuxes to MP4 via ffmpeg.
Two modes are available:
- CLI — one-shot download from the command line
- HTTP API — async task server with progress polling and file download
- Parse and download VOD M3U8 playlists
- Auto-resolve Master playlists (selects the first variant)
- Concurrent TS segment download with configurable workers
- Per-segment retry on failure
- Per-segment resume (re-run with the same
-u,-o, and-fto skip completed segments) - AES-128 segment decryption
- Built-in cloud VOD decryption (Tencent SimpleAES, Alibaba HLS standard encryption)
- Pluggable decrypt scripts (Starlark / external process, configured via
decrypt.yaml) - Merge TS segments into a single file
- Remux to MP4 via ffmpeg (stream copy, no re-encoding)
- Single-line terminal progress bar for download and merge
- Custom HTTP headers, Cookie, and optional TLS skip-verify
- HTTP/HTTPS proxy support (CLI
-proxyorHTTP_PROXY/HTTPS_PROXYenv vars) - HTTP API server for remote parsing, async downloads, progress tracking, and cancellation
- Optional API Key authentication, CORS, task TTL, and automatic cleanup
- Go 1.22+ (for building from source)
- ffmpeg (optional, required when MP4 output is enabled — enabled by default)
make vendor && make build
./m3u8 -u=https://example.com/index.m3u8 -o=./outputOr run from source:
go run -mod=vendor . -u=https://example.com/index.m3u8 -o=./outputm3u8 -u <URL> [options]
m3u8 serve [options] # start HTTP API server# Basic download (outputs ./main.mp4 by default)
./m3u8 -u=https://example.com/index.m3u8
# Specify output directory and filename
./m3u8 -u=https://example.com/index.m3u8 -o=./output -f myvideo
# Keep TS output only (skip MP4 conversion)
./m3u8 -u=https://example.com/index.m3u8 -mp4=false
# Sites requiring Referer or Cookie
./m3u8 -u=https://example.com/index.m3u8 \
-H "Referer: https://example.com/" \
-cookie "session=abc"
# Self-signed HTTPS certificate
./m3u8 -u=https://self-signed.example.com/index.m3u8 -k
# Download via HTTP proxy
./m3u8 -u=https://example.com/index.m3u8 -proxy http://127.0.0.1:7890
# Custom decryption script
./m3u8 -u=https://example.com/index.m3u8 -decrypt-script scripts/custom.star
# Resume an interrupted download (same -u, -o, -f)
./m3u8 -u=https://example.com/index.m3u8 -o=./output -f myvideoLinux & macOS:
./m3u8 -u=https://example.com/index.m3u8 -o=./outputWindows PowerShell:
.\m3u8.exe -u="https://example.com/index.m3u8" -o="D:\data\output"| Flag | Default | Description |
|---|---|---|
-u |
M3U8 URL (required) | |
-o |
. |
Output directory |
-f |
main |
Output base name (.ts / .mp4 extension optional) |
-c |
25 |
Download concurrency |
-r |
10 |
Max retries per segment |
-mp4 |
true |
Remux merged TS to MP4 via ffmpeg (-mp4=false to disable) |
-H |
Custom HTTP header ("Key: Value"), repeatable |
|
-cookie |
Cookie request header | |
-proxy |
HTTP proxy URL (e.g. http://127.0.0.1:7890) |
|
-k |
false |
Skip HTTPS certificate verification (insecure) |
-decrypt-script |
Decrypt script path (.star or .py) |
|
-decrypt-config |
decrypt.yaml |
Decrypt config file path |
-scripts-dir |
scripts |
Decrypt script library directory |
-drm-token |
Tencent Cloud DrmToken (SimpleAES) | |
-pkey |
Tencent Cloud SimpleAES playback key | |
-mts-token |
Alibaba Cloud MtsHlsUriToken | |
-h |
Show help |
VOD playlists only. Some sources rate-limit requests — lower
-cor raise-ras needed.When
-proxyis not set,HTTP_PROXY/HTTPS_PROXYenvironment variables are used automatically.To resume an interrupted download, re-run with the same
-u,-o, and-f. Completed segments ints/are reused automatically.
Built-in support for Tencent Cloud SimpleAES and Alibaba Cloud HLS standard encryption, including URL preprocessing and secondary key decryption. When cloud VOD signatures are detected, the built-in provider is activated automatically — no -decrypt-script is usually needed.
| Provider | Auto-detection (either condition) | Key handling |
|---|---|---|
| Tencent SimpleAES | M3U8 host *.vod2.myqcloud.com; or key URI contains drm.vod2.myqcloud.com + drmType=SimpleAES |
SHA256(pkey) → AES-CBC (zero IV) decrypts ciphertext key |
| Alibaba HLS standard | M3U8 URL contains MtsHlsUriToken=; or key URI contains Ciphertext= |
16-byte binary or Base64 response → 16-byte AES key |
Credentials are passed via CLI flags only — not stored in config files or decrypt.yaml.
| Flag | Description |
|---|---|
-drm-token |
Tencent Cloud DrmToken (inserts voddrm.token.{token} into M3U8 path) |
-pkey |
Tencent Cloud SimpleAES playback key |
-mts-token |
Alibaba Cloud MtsHlsUriToken (appended to M3U8 URL query) |
# Tencent Cloud SimpleAES
./m3u8 -u "https://1500014561.vod2.myqcloud.com/.../adp.12.m3u8?t=...&sign=..." \
-drm-token "eyJhbGci..." \
-pkey "JduzsUuRvGVPRHvIYwLv"
# Alibaba Cloud HLS standard encryption
./m3u8 -u "https://example.aliyundoc.com/test.m3u8?MediaId=xxx" \
-mts-token "your-token"Key hook priority: explicit -decrypt-script > built-in provider > decrypt.yaml / auto-discovered script > raw key. URL preprocessing (-drm-token / -mts-token) still runs when -decrypt-script is set.
Reference Starlark scripts for debugging: scripts/tencent-vod-simpleaes.star, scripts/aliyun-hls-standard.star. See scripts/README.md for details.
Scope: Supports standard HLS AES-128, Tencent SimpleAES, and Alibaba HLS standard encryption. Does not support Alibaba private / License encryption (SDK-only) or commercial DRM (FairPlay / Widevine).
For non-standard encryption (custom key derivation, SAMPLE-AES, etc.), place scripts in scripts/ or configure rules in decrypt.yaml (copy from decrypt.yaml.example).
Script selection priority: CLI -decrypt-script → decrypt.yaml rules → auto-discovery by METHOD / hostname.
| Flag | Description |
|---|---|
-decrypt-script |
Explicit script path (highest priority) |
-decrypt-config |
Config file with host/method matching rules |
-scripts-dir |
Script library directory for auto-discovery |
# Use a config file
./m3u8 -u=https://example.com/index.m3u8 -decrypt-config decrypt.yaml
# Auto-match scripts/AES-128.star or scripts/example.com.py
./m3u8 -u=https://example.com/index.m3u8 -scripts-dir scriptsStarlark (.star) runs in a sandbox; other extensions (.py, etc.) use a long-lived external process with JSON stdin/stdout. When no script matches and METHOD is AES-128, built-in decryption is used unchanged.
See scripts/README.md for hook APIs, JSON protocol, and examples.
Run m3u8 serve to start an HTTP API server for parsing playlists, creating async download tasks, polling progress, and downloading finished files.
The server auto-loads decrypt.yaml from the working directory if present (same script matching as CLI). Pending or running tasks are recovered on restart and resume from existing segments in the task directory.
Cloud VOD credentials (
-drm-token,-pkey,-mts-token) are CLI-only and not available via the HTTP API. Use CLI mode for Tencent / Alibaba encrypted sources.
Development (no auth)
make build
./m3u8 serve --port 8080 --data-dir ./dataProduction (with API Key auth)
./m3u8 serve \
--port 8080 \
--data-dir /var/m3u8/data \
--auth-enabled \
--api-key "your-secret-key" \
--max-tasks 3 \
--task-ttl 24h| Flag | Default | Description |
|---|---|---|
--port |
8080 |
Listen port |
--data-dir |
./data |
Task and output storage directory |
--auth-enabled |
false |
Enable API Key authentication |
--api-key |
API Key (required when --auth-enabled) |
|
--cors-origins |
* |
Allowed CORS origins (comma-separated) |
--max-tasks |
3 |
Max concurrent download tasks |
--task-ttl |
24h |
Retention period for completed tasks |
--cleanup-interval |
1h |
Expired task cleanup interval |
When auth is enabled, all endpoints except health check require X-API-Key: <key> or Authorization: Bearer <key>.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/v1/health |
No | Health check |
POST |
/api/v1/parse |
Yes* | Parse M3U8 playlist |
POST |
/api/v1/tasks |
Yes* | Create download task |
GET |
/api/v1/tasks |
Yes* | List tasks (status, limit, offset query params) |
GET |
/api/v1/tasks/{taskID} |
Yes* | Get task status and progress |
GET |
/api/v1/tasks/{taskID}/download |
Yes* | Download completed task output |
DELETE |
/api/v1/tasks/{taskID} |
Yes* | Cancel a running task |
* Required only when --auth-enabled is set.
pending → running → completed | failed | cancelled | expired
Parse an M3U8 URL (returns first 5 segments by default; use ?full=true for all):
curl -X POST http://localhost:8080/api/v1/parse \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/index.m3u8","proxy":"http://127.0.0.1:7890"}'Create a task and poll until complete:
# Create task
curl -X POST http://localhost:8080/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/index.m3u8","filename":"myvideo","concurrency":25,"to_mp4":true,"proxy":"http://127.0.0.1:7890"}'
# Poll status (replace <taskID> with returned task_id)
curl http://localhost:8080/api/v1/tasks/<taskID>
# Download output (when status is completed)
curl -OJ http://localhost:8080/api/v1/tasks/<taskID>/download
# Cancel a running task
curl -X DELETE http://localhost:8080/api/v1/tasks/<taskID>When auth is enabled, add -H "X-API-Key: your-secret-key" to the requests above.
| Field | Type | Description |
|---|---|---|
url |
string | M3U8 URL (required) |
proxy |
string | HTTP proxy URL |
| Field | Type | Default | Description |
|---|---|---|---|
url |
string | M3U8 URL (required) | |
filename |
string | main |
Output base name |
concurrency |
int | 25 |
Download workers |
to_mp4 |
bool | true |
Remux to MP4 after merge |
proxy |
string | HTTP proxy URL |
Go Modules + vendor mode (-mod=vendor) for reproducible offline builds.
make vendor # populate vendor/
make build # build binary
make test # run testsCross-compile:
make build-linux
make build-darwin-arm64
make build-windows