A M3U8/HLS downloader tool with resume capability and one-click download→merge→compress functionality.
- HTTP proxy support (default: 127.0.0.1:7890, configurable via environment variables or .env file)
- Proxy disable option: Use
--no-proxyflag to disable proxy - Configuration file support: Create
.envfile for persistent settings - Hash-based folder structure: Each download task uses unique folder
data/{hash}/ - Resume capability (manual mode): Automatically skips already downloaded segments
- One-click mode: Download → merge → compress, outputs single file to
data/{hash}/ - Concurrent downloads (manual mode): Default 8 concurrent connections (configurable)
- Progress display and error tolerance
npm installOutput file: data/{hash}/<auto-generated-filename>.mp4 (can specify with --name)
node cli.js "<M3U8_URL>"
# Optional parameters
# --name <file> Specify output base filename (without extension)
# --codec <h264|hevc> Video codec (default: h264; hevc for better compression)
# --h265 Equivalent to --codec hevc
# --crf <num> Quality/size balance (default: 23; lower = higher quality)
# --preset <p> Encoding speed/efficiency (default: medium)
# --audio-bitrate <rate> Audio bitrate (default: 128k)
# --no-proxy Disable proxy usageExamples:
# Basic usage
node cli.js "https://example.com/playlist.m3u8" --name my-video --h265 --crf 26 --preset slow
# Disable proxy
node cli.js "https://example.com/playlist.m3u8" --no-proxy
# Use custom proxy (via .env file)
# Create .env file with: HTTP_PROXY=http://your-proxy:8080
node cli.js "https://example.com/playlist.m3u8"If installed globally (or npm link this repo), you can use directly:
m3u8-one "<M3U8_URL>"Compress existing video files with the same encoding options:
node compress.js <INPUT_VIDEO_FILE>
# Optional parameters
# --output <file> Specify output file path
# --codec <h264|hevc> Video codec (default: h264)
# --h265 Equivalent to --codec hevc
# --crf <num> Quality/size balance (default: 23)
# --preset <p> Encoding speed/efficiency (default: medium)
# --audio-bitrate <rate> Audio bitrate (default: 128k)Examples:
# Basic compression
node compress.js video.mp4 --codec hevc --crf 26
# Specify output file
node compress.js input.mp4 --output compressed.mp4 --preset slow
# Use npm script
npm run compress -- video.mp4 --h265If installed globally:
m3u8-compress video.mp4 --h265 --crf 26node download.js "<M3U8_URL>"
# Disable proxy
node download.js "<M3U8_URL>" --no-proxynode download.js "https://prod-fastly-us-west-1.video.pscp.tv/Transcoding/v1/hls/o6U3UX5qItJ7V-Jmh9HGBb7uuyNgUHZnemnmw-l24VrxJ2Q5ytrb9q_39wwQVGPdsAGqDXXdy85iKfpfGbvybA/transcode/us-west-1/periscope-replay-direct-prod-us-west-1-public/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsInZlcnNpb24iOiIyIn0.eyJFbmNvZGVyU2V0dGluZyI6ImVuY29kZXJfc2V0dGluZ18xMDgwcDYwXzEwIiwiSGVpZ2h0IjoxMDgwLCJIaWdoRnJhbWVSYXRlIjp0cnVlLCJLYnBzIjo4MDAwLCJXaWR0aCI6MTkyMH0.OBq8EsoF4c8ydlmfZFxJzACPHYFjmjUaSER2wvsfHso/playlist_16685069917841765633.m3u8?type=replay"- One-click mode: Generates single
data/{hash}/<name>.mp4 - Manual mode: Segments and playlist saved in
data/{hash}/(playlist.m3u8and multiple.tsfiles) - Hash-based folders: Each URL generates unique hash folder for organized file management
If download is interrupted, simply rerun the same command, the program will:
- Check already downloaded segments
- Automatically skip existing files
- Only download missing or failed segments
After download completes, it shows:
- Total: Total segment count
- Downloaded: Newly downloaded segments in this run
- Skipped: Skipped segments (already exist)
- Failed: Failed segment count
- HTTP/HTTPS proxy:
http://127.0.0.1:7890
Use --no-proxy flag to disable proxy:
node cli.js "<M3U8_URL>" --no-proxy
node download.js "<M3U8_URL>" --no-proxy
node index.js "<M3U8_URL>" --no-proxySet environment variables before running:
export HTTP_PROXY=http://your-proxy:8080
export HTTPS_PROXY=http://your-proxy:8080
node cli.js "<M3U8_URL>"To modify proxy in source code, edit these lines in the respective files:
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://127.0.0.1:7890';
process.env.GLOBAL_AGENT_HTTPS_PROXY = 'http://127.0.0.1:7890';For persistent configuration, create a .env file in the project root:
cp .env.example .env
# Edit .env file with your preferences# Proxy Configuration
DISABLE_PROXY=false # Set to "true" to disable proxy by default
HTTP_PROXY=http://127.0.0.1:7890 # HTTP proxy address
HTTPS_PROXY=http://127.0.0.1:7890 # HTTPS proxy address
# Download Configuration
OUTPUT_DIR=data # Output directory
CONCURRENT_DOWNLOADS=8 # Number of concurrent downloads
# Video Encoding Defaults (cli.js only)
DEFAULT_CODEC=h264 # Default video codec (h264 or hevc)
DEFAULT_CRF=23 # Default CRF value
DEFAULT_PRESET=medium # Default encoding preset
DEFAULT_AUDIO_BITRATE=128k # Default audio bitrate- Command line arguments (highest priority)
- .env file configuration
- Code defaults (lowest priority)
# Use .env configuration
node cli.js "https://example.com/playlist.m3u8"
# Override .env configuration
node cli.js "https://example.com/playlist.m3u8" --no-proxy --codec h264After manual mode download completes, you can use ffmpeg to merge all .ts segments into a complete video:
# Enter data directory
cd data
# Merge using ffmpeg
ffmpeg -i playlist.m3u8 -c copy output.mp4The following examples are for compressing videos after merging or directly from M3U8 (smaller size, convenient for sharing/archiving). One-click mode already includes compression, no need for additional processing.
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k -movflags +faststart output.mp4ffmpeg -i input.mp4 -c:v libx265 -preset medium -crf 28 -c:a aac -b:a 128k output-hevc.mp4Single pass:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -maxrate 1500k -bufsize 3000k -c:a aac -b:a 128k output-1500k.mp4Two-pass (more stable):
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 1500k -pass 1 -an -f mp4 /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 2 -c:a aac -b:a 128k -movflags +faststart output-2pass.mp4ffmpeg -i input.mp4 -vf "scale=-2:720,fps=30" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output-720p.mp4ffmpeg -i data/playlist.m3u8 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4Tip: Add -pix_fmt yuv420p for better compatibility; use -c copy to remux without compression.
If "Client network socket disconnected" error occurs:
- Check if proxy is running properly
- Rerun the command, resume will skip already downloaded segments
Ensure local proxy service (such as Clash, V2Ray, etc.) is running and listening on port 7890.
m3u8/
├── cli.js # One-click download→merge→compress CLI (outputs single file)
├── compress.js # Video compression tool for local files
├── download.js # Optimized download script (with resume support)
├── index.js # Download script based on m3u8-dln
├── package.json
├── .env.example # Configuration file template
├── .env # User configuration file (create from .env.example)
├── data/ # Download output directory
│ ├── {hash1}/ # Task 1 hash folder
│ │ ├── playlist.m3u8
│ │ └── *.ts
│ ├── {hash2}/ # Task 2 hash folder
│ │ ├── playlist.m3u8
│ │ └── *.ts
│ └── ...
└── README.md
MIT