hs-m3u8 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hs_m3u8/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- from hs_m3u8.main import M3u8Downloader
1
+ from hs_m3u8.main import M3u8Downloader, M3u8Key
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.4"
hs_m3u8/main.py CHANGED
@@ -8,10 +8,8 @@ import posixpath
8
8
  import shutil
9
9
  import subprocess
10
10
  from collections.abc import Callable
11
- from enum import Enum, auto
12
11
  from hashlib import md5
13
12
  from pathlib import Path
14
- from typing import Any
15
13
  from urllib.parse import urljoin, urlparse
16
14
  from zipfile import ZipFile
17
15
 
@@ -46,16 +44,6 @@ def get_ffmpeg():
46
44
  return ffmpeg_bin
47
45
 
48
46
 
49
- class ContentType(Enum):
50
- """
51
- 获取URL数据的,类型枚举
52
- """
53
-
54
- Text = auto()
55
- Json = auto()
56
- Bytes = auto()
57
-
58
-
59
47
  class M3u8Key:
60
48
  """
61
49
  M3u8key
@@ -66,8 +54,9 @@ class M3u8Key:
66
54
  :param key: 密钥
67
55
  :param iv: 偏移
68
56
  """
57
+ iv = bytes.fromhex(iv[2:]) if iv and iv.startswith("0x") else iv
69
58
  self.key = key
70
- self.iv = iv or key
59
+ self.iv = iv
71
60
 
72
61
 
73
62
  class M3u8Downloader:
@@ -80,6 +69,7 @@ class M3u8Downloader:
80
69
  ts_url_list: list = []
81
70
  ts_path_list: list = []
82
71
  ts_key: M3u8Key = None
72
+ mp4_head_hrl: str = None
83
73
  m3u8_md5 = ""
84
74
 
85
75
  def __init__(
@@ -89,6 +79,7 @@ class M3u8Downloader:
89
79
  decrypt=False,
90
80
  max_workers=None,
91
81
  headers=None,
82
+ key: M3u8Key = None,
92
83
  get_m3u8_func: Callable = None,
93
84
  ):
94
85
  """
@@ -112,6 +103,7 @@ class M3u8Downloader:
112
103
  self.save_dir = Path(save_path) / "hls"
113
104
  self.save_name = Path(save_path).name
114
105
  self.key_path = self.save_dir / "key.key"
106
+ self.custom_key = key
115
107
 
116
108
  if not self.save_dir.exists():
117
109
  self.save_dir.mkdir(parents=True)
@@ -165,7 +157,7 @@ class M3u8Downloader:
165
157
  if not merge:
166
158
  return True
167
159
 
168
- if self.merge():
160
+ if await self.merge():
169
161
  self.logger.info("合并成功")
170
162
  else:
171
163
  self.logger.error(
@@ -185,30 +177,6 @@ class M3u8Downloader:
185
177
  self.ts_path_list = [None] * len(self.ts_url_list)
186
178
  await asyncio.gather(*[self._download_ts(url) for url in self.ts_url_list])
187
179
 
188
- async def get_url_content(self, url: str, content_type: ContentType) -> bytes | str | Any:
189
- """
190
- 按照类型获取url内容
191
- :param url: 请求地址
192
- :param content_type: 内容类型
193
- :return:
194
- """
195
- data = None
196
- try:
197
- resp = await self.net.get(url, headers=self.headers)
198
- if content_type == ContentType.Bytes:
199
- data = resp.content
200
- if content_type == ContentType.Text:
201
- data = resp.text
202
- if content_type == ContentType.Json:
203
- data = resp.json
204
- if resp.status_code != 200:
205
- self.logger.error(f"请求{url}内容时返回码不正确,类型为:{content_type}, 返回码为:{resp.status_code}")
206
- return None
207
- except BaseException as exception:
208
- self.logger.error(f"请求{url}内容时发生异常,类型为:{content_type}, 异常信息为:{exception}")
209
-
210
- return data
211
-
212
180
  async def get_ts_list(self, url) -> list[dict]:
213
181
  """
214
182
  解析m3u8并保存至列表
@@ -235,6 +203,14 @@ class M3u8Downloader:
235
203
  self.logger.info(f"选择的播放地址:{play_url},比特率:{bandwidth}")
236
204
  return await self.get_ts_list(urlparse(play_url))
237
205
 
206
+ # 处理具有 #EXT-X-MAP:URI="*.mp4" 的情况
207
+ segment_map_count = len(m3u8_obj.segment_map)
208
+ if segment_map_count > 0:
209
+ if segment_map_count > 1:
210
+ raise ValueError("暂不支持segment_map有多个的情况,请提交issues,并告知m3u8的地址,方便做适配")
211
+ self.mp4_head_hrl = prefix + m3u8_obj.segment_map[0].uri
212
+ m3u8_obj.segment_map[0].uri = "head.mp4"
213
+
238
214
  # 遍历ts文件
239
215
  for index, segments in enumerate(m3u8_obj.segments):
240
216
  ts_uri = segments.uri if "http" in m3u8_obj.segments[index].uri else segments.absolute_uri
@@ -243,10 +219,16 @@ class M3u8Downloader:
243
219
 
244
220
  # 保存解密key
245
221
  if len(m3u8_obj.keys) > 0 and m3u8_obj.keys[0]:
246
- resp = await self.net.get(m3u8_obj.keys[0].absolute_uri, headers=self.headers)
247
- key_data = resp.content
222
+ iv = m3u8_obj.keys[0].iv
223
+ if not self.custom_key:
224
+ resp = await self.net.get(m3u8_obj.keys[0].absolute_uri, headers=self.headers)
225
+ key_data = resp.content
226
+ else:
227
+ key_data = self.custom_key.key
228
+ iv = self.custom_key.iv or iv
229
+
248
230
  self.save_file(key_data, self.key_path)
249
- self.ts_key = M3u8Key(key=key_data, iv=m3u8_obj.keys[0].iv)
231
+ self.ts_key = M3u8Key(key=key_data, iv=iv)
250
232
  key = m3u8_obj.segments[0].key
251
233
  key.uri = "key.key"
252
234
  m3u8_obj.segments[0].key = key
@@ -271,7 +253,7 @@ class M3u8Downloader:
271
253
  if Path(ts_path).exists():
272
254
  self.ts_path_list[index] = str(ts_path)
273
255
  return
274
- resp = await self.net.get(ts_item["uri"])
256
+ resp = await self.net.get(ts_item["uri"], self.headers)
275
257
  ts_content = resp.content
276
258
  if ts_content is None:
277
259
  return
@@ -283,7 +265,7 @@ class M3u8Downloader:
283
265
  self.logger.info(f"{ts_uri}下载成功")
284
266
  self.ts_path_list[index] = str(ts_path)
285
267
 
286
- def merge(self):
268
+ async def merge(self):
287
269
  """
288
270
  合并ts文件为mp4文件
289
271
  :return:
@@ -301,8 +283,17 @@ class M3u8Downloader:
301
283
  # mp4路径
302
284
  mp4_path = self.save_dir.parent / f"{self.save_name}.mp4"
303
285
 
286
+ # 如果保护mp4的头,则把ts放到后面
287
+ mp4_head_data = b""
288
+ if self.mp4_head_hrl:
289
+ resp = await self.net.get(self.mp4_head_hrl)
290
+ mp4_head_data = resp.content
291
+ mp4_head_file = self.save_dir / "head.mp4"
292
+ mp4_head_file.write_bytes(mp4_head_data)
293
+
304
294
  # 把ts文件整合到一起
305
295
  big_ts_file = big_ts_path.open("ab+")
296
+ big_ts_file.write(mp4_head_data)
306
297
  for path in self.ts_path_list:
307
298
  with open(path, "rb") as ts_file:
308
299
  data = ts_file.read()
@@ -316,7 +307,7 @@ class M3u8Downloader:
316
307
  ffmpeg_bin = get_ffmpeg()
317
308
  command = (
318
309
  f'{ffmpeg_bin} -i "{big_ts_path}" '
319
- f'-c copy -map 0:v -map 0:a -bsf:a aac_adtstoasc -threads 32 "{mp4_path}" -y'
310
+ f'-c copy -map 0:v -map 0:a? -bsf:a aac_adtstoasc -threads 32 "{mp4_path}" -y'
320
311
  )
321
312
  self.logger.info(f"ts整合成功,开始转为mp4。 command:{command}")
322
313
  result = subprocess.run(command, shell=True, capture_output=True, text=True)
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: hs-m3u8
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: m3u8 下载器
5
5
  Project-URL: homepage, https://github.com/x-haose/hs-m3u8
6
6
  Project-URL: repository, https://github.com/x-haose/hs-m3u8
7
7
  Project-URL: documentation, https://github.com/x-haose/hs-m3u8
8
8
  Author-email: 昊色居士 <xhrtxh@gmail.com>
9
- License: MIT
9
+ License-Expression: MIT
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: MIT License
@@ -36,6 +36,13 @@ m3u8 视频下载工具。支持大部分的m3u8视频下载。后续增加UI界
36
36
  - 可选择保留ts文件
37
37
  - 内置Windows平台ffmpeg可执行文件(由于Linux及Mac下权限问题,需自行安装ffmpeg文件)
38
38
 
39
+ ## 计划
40
+
41
+ - 增加cli功能,通过终端执行命令去下载
42
+ - 增加支持curl参数功能。直接在curl里面读取请求头及cookie
43
+ - 编写详细文档
44
+ - 选择一个合适的技术栈,增加UI界面
45
+
39
46
  ## 安装
40
47
 
41
48
  ### pip包安装
@@ -0,0 +1,5 @@
1
+ hs_m3u8/__init__.py,sha256=WxYMLrnGwdYDEmbxZmUV-l2DPs9jrYDP0cAnHxkqtFs,72
2
+ hs_m3u8/main.py,sha256=RELt4z7AM7OuXYiRkCe-yf4A1JGQ9FtIaBj6Vov3Ig0,11574
3
+ hs_m3u8-0.1.4.dist-info/METADATA,sha256=ddVI5SkdoINuFHSPS9uFGDeOmCybhT1al4_4SmXPHRs,2204
4
+ hs_m3u8-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
5
+ hs_m3u8-0.1.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,5 +0,0 @@
1
- hs_m3u8/__init__.py,sha256=VWzgd1ZOktIFqMuyNnZFhKltLMFS7g9IOS5FUZXZpoE,63
2
- hs_m3u8/main.py,sha256=tq-JtMzfEy1YTTpZFL9Oas5ZzrON3eigWxlKAduc-oU,11592
3
- hs_m3u8-0.1.2.dist-info/METADATA,sha256=e6BAuDJEiq0k_mhn5uuYUpLcW6f6eEw1yocday1-NW0,1979
4
- hs_m3u8-0.1.2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
5
- hs_m3u8-0.1.2.dist-info/RECORD,,