Skip to content

Commit 0b0c24d

Browse files
committed
[为M3U8下载工具增加HTTP请求头支持]: 扩展M3U8下载工具的功能,新增HTTP请求头参数支持,使工具能够处理需要认证或特定头信息的受保护视频流
-**增加HTTP请求头支持**: 在M3U8下载工具中添加`-H`参数,支持通过命令行直接指定HTTP请求头或通过文件加载请求头,满足需要认证、特定User-Agent等场景 -**灵活的请求头解析**: 实现`parse_headers`函数,支持两种请求头指定方式:直接命令行格式`Header1:value1 Header2:value2`或从文件读取,文件格式每行一个`Header:value`对 -**完整请求头传递**: 将请求头应用于所有网络请求,包括M3U8文件下载、密钥获取和TS分片下载,确保整个下载链路的认证一致性 -**文档和使用示例更新**: 更新工具使用说明,添加使用命令行直接指定HTTP头和从文件加载HTTP头的具体示例,提升用户体验
1 parent 22a8846 commit 0b0c24d

File tree

1 file changed

+67
-5
lines changed

1 file changed

+67
-5
lines changed

src/m3u8_download/m3u8_dl.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
通用 M3U8 下载/解密/合并工具(AES-128/CBC)。
66
77
用法:
8-
python m3u8_dl.py -u <m3u8_url> -o <输出.mp4> [-k <key_hex_or_url>] [-t 超时秒数]
8+
python m3u8_dl.py -u <m3u8_url> -o <输出.mp4> [-k <key_hex_or_url>] [-t 超时秒数] [-H <headers>]
99
1010
示例:
1111
# 无加密
@@ -16,6 +16,12 @@
1616
1717
# key 由服务器下发
1818
python m3u8_dl.py -u https://example.com/index.m3u8 -o movie.mp4 -k https://example.com/key.bin
19+
20+
# 使用命令行直接指定 HTTP 头
21+
python m3u8_dl.py -u https://example.com/index.m3u8 -o movie.mp4 -H "Authorization:token123 User-Agent:Mozilla/5.0"
22+
23+
# 使用头文件
24+
python m3u8_dl.py -u https://example.com/index.m3u8 -o movie.mp4 -H headers.txt
1925
"""
2026
from __future__ import annotations
2127

@@ -39,13 +45,15 @@ def __init__(
3945
output: str,
4046
key: Optional[str] = None,
4147
timeout: int = 30,
48+
headers: Optional[dict] = None,
4249
) -> None:
4350
self.m3u8_url = m3u8_url
4451
self.output = Path(output)
4552
self.key: Optional[bytes] = None
4653
self.iv: Optional[bytes] = None
4754
self.timeout = timeout
4855
self.ts_urls: List[str] = []
56+
self.headers = headers or {}
4957

5058
# 自动创建输出目录
5159
self.output.parent.mkdir(parents=True, exist_ok=True)
@@ -54,7 +62,9 @@ def __init__(
5462
def parse(self) -> None:
5563
logger.info("正在下载与解析 m3u8...")
5664
try:
57-
resp = requests.get(self.m3u8_url, timeout=self.timeout)
65+
resp = requests.get(
66+
self.m3u8_url, headers=self.headers, timeout=self.timeout
67+
)
5868
resp.raise_for_status()
5969
except KeyboardInterrupt:
6070
logger.error("用户中断")
@@ -94,7 +104,9 @@ def _parse_key(self, line: str) -> None:
94104
# 下载 key
95105
if uri_part.startswith("http"):
96106
try:
97-
key_bytes = requests.get(uri_part, timeout=self.timeout).content
107+
key_bytes = requests.get(
108+
uri_part, headers=self.headers, timeout=self.timeout
109+
).content
98110
except KeyboardInterrupt:
99111
logger.error("用户中断")
100112
sys.exit(130)
@@ -124,7 +136,9 @@ def download(self) -> None:
124136
retry = 3
125137
while retry:
126138
try:
127-
resp = requests.get(ts_url, timeout=self.timeout)
139+
resp = requests.get(
140+
ts_url, headers=self.headers, timeout=self.timeout
141+
)
128142
resp.raise_for_status()
129143
break
130144
except KeyboardInterrupt:
@@ -178,6 +192,46 @@ def run(self) -> None:
178192

179193

180194
# ------------------ CLI ------------------
195+
def parse_headers(headers_arg: Optional[str]) -> dict:
196+
"""
197+
解析 headers 参数,支持:
198+
1. 文件路径:读取文件中的每一行作为一个头部
199+
2. 命令行参数:直接解析 "Header1:value1 Header2:value2" 格式
200+
201+
返回一个 headers 字典
202+
"""
203+
headers = {}
204+
if not headers_arg:
205+
return headers
206+
207+
# 尝试作为文件路径处理
208+
file_path = Path(headers_arg)
209+
if file_path.exists() and file_path.is_file():
210+
try:
211+
with open(file_path, "r", encoding="utf-8") as f:
212+
for line in f:
213+
line = line.strip()
214+
if line and ":" in line:
215+
key, value = line.split(":", 1)
216+
headers[key.strip()] = value.strip()
217+
logger.info(f"从文件 {file_path} 加载请求头")
218+
return headers
219+
except Exception as e:
220+
logger.warning(f"读取请求头文件失败: {e}")
221+
222+
# 作为命令行参数解析
223+
header_pairs = headers_arg.split()
224+
for pair in header_pairs:
225+
if ":" in pair:
226+
key, value = pair.split(":", 1)
227+
headers[key.strip()] = value.strip()
228+
229+
if headers:
230+
logger.info(f"解析到 {len(headers)} 个请求头")
231+
232+
return headers
233+
234+
181235
def main() -> None:
182236
parser = argparse.ArgumentParser(description="M3U8 下载/解密/合并工具")
183237
parser.add_argument("-u", "--url", required=True, help="m3u8 地址")
@@ -188,9 +242,17 @@ def main() -> None:
188242
parser.add_argument(
189243
"-t", "--timeout", type=int, default=30, help="超时秒数,默认 30"
190244
)
245+
parser.add_argument(
246+
"-H",
247+
"--headers",
248+
help="HTTP 请求头文件路径或直接输入的头信息(格式: Header1:value1 Header2:value2 或文件路径)",
249+
)
191250
args = parser.parse_args()
192251

193-
dl = M3U8Downloader(args.url, args.output, args.key, args.timeout)
252+
# 解析 headers
253+
headers = parse_headers(args.headers)
254+
255+
dl = M3U8Downloader(args.url, args.output, args.key, args.timeout, headers)
194256
dl.run()
195257

196258

0 commit comments

Comments
 (0)