Learn how to download and merge m3u8 streaming video files using Python. M3U8 is a common video streaming format widely used by online video platforms.
import asyncio
from pathlib import Path
from typing import Optional
import logging
logger = logging.getLogger(__name__)
async def download_m3u8_video(
url: str,
output_filename: str,
referer: Optional[str] = None,
custom_headers: Optional[dict] = None,
cookies: Optional[str] = None,
download_dir: Path = Path("./downloads")
) -> str:
"""
Download an M3U8 video using yt-dlp asynchronously.
Args:
url: The M3U8 video URL.
output_filename: The output file name without extension.
referer: Optional HTTP Referer header.
custom_headers: Optional dictionary of additional HTTP headers.
cookies: Optional cookie string (semicolon-separated).
download_dir: Directory to save the downloaded file.
Returns:
The full path to the downloaded video file.
"""
# Ensure download directory exists
download_dir.mkdir(parents=True, exist_ok=True)
output_path = download_dir / f"{output_filename}.mp4"
# Build yt-dlp command
cmd = [
"yt-dlp",
"-o", str(output_path),
"--newline", # Print each progress update in a new line
"--progress", # Show progress bar
"--verbose", # Enable detailed output
"--console-title", # Show progress in console title
"--no-quiet", # Disable quiet mode
"--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"--no-check-certificates",
"--retries", "20",
"--fragment-retries", "20",
]
# Add Referer header if provided
if referer:
cmd.extend(["--referer", referer])
# Add custom HTTP headers if provided
if custom_headers:
for key, value in custom_headers.items():
cmd.extend(["--add-header", f"{key}:{value}"])
# Add cookies if provided
if cookies:
cookies_file = download_dir / f"{output_filename}_cookies.txt"
with open(cookies_file, 'w', encoding='utf-8') as f:
f.write("# Netscape HTTP Cookie File\n")
for cookie in cookies.split(';'):
cookie = cookie.strip()
if '=' in cookie:
k, v = cookie.split('=', 1)
f.write(f".example.com\tTRUE\t/\tFALSE\t0\t{k}\t{v}\n")
cmd.extend(["--cookies", str(cookies_file)])
# Append video URL
cmd.append(url)
try:
# Launch yt-dlp process asynchronously
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT
)
logs = []
# Read output lines in real time
async for line in process.stdout:
text = line.decode('utf-8', errors='ignore').strip()
if text:
logs.append(text)
logger.info(f"[yt-dlp] {text}")
await process.wait()
# Check if download succeeded
if process.returncode == 0:
if output_path.exists():
return str(output_path)
else:
raise Exception("Download completed but output file not found")
# If failed, raise exception with last few log lines
raise Exception("\n".join(logs[-20:]))
except FileNotFoundError:
raise Exception("yt-dlp not installed. Install with: pip install yt-dlp")Example: my_video.mp4
M3U8 Format: M3U8 is a playlist file used by HLS (HTTP Live Streaming) protocol, containing links to multiple .ts video segments
HTTP Requests: Use requests library to download m3u8 file and video segments
Multi-threading: Use thread pool to download multiple video segments concurrently for better efficiency
Merge Segments: Merge downloaded .ts segments in order into a complete video file