hs-m3u8 0.1.5__tar.gz → 0.1.7__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hs-m3u8
3
- Version: 0.1.5
3
+ Version: 0.1.7
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
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Python: >=3.10
23
- Requires-Dist: hssp>=0.4.4
23
+ Requires-Dist: hssp>=0.4.18
24
24
  Requires-Dist: m3u8>=6.0.0
25
25
  Description-Content-Type: text/markdown
26
26
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hs-m3u8"
3
- version = "0.1.5"
3
+ version = "0.1.7"
4
4
  description = "m3u8 下载器"
5
5
  authors = [
6
6
  { name = "昊色居士", email = "xhrtxh@gmail.com" }
@@ -26,7 +26,7 @@ requires-python = ">= 3.10"
26
26
 
27
27
  dependencies = [
28
28
  "m3u8>=6.0.0",
29
- "hssp>=0.4.4",
29
+ "hssp>=0.4.18",
30
30
  ]
31
31
 
32
32
  [project.urls]
@@ -38,26 +38,14 @@ documentation = "https://github.com/x-haose/hs-m3u8"
38
38
  requires = ["hatchling"]
39
39
  build-backend = "hatchling.build"
40
40
 
41
- [tool.rye]
42
- managed = true
43
- dev-dependencies = [
44
- "pre-commit>=4.0.1",
45
- "twine>=6.0.1",
46
- ]
41
+ [tool.hatch.build.targets.sdist]
47
42
  include = [
48
- "src/hs_m3u8/"
43
+ "src/hs_m3u8",
49
44
  ]
50
45
 
51
- [tool.hatch.metadata]
52
- allow-direct-references = true
53
-
54
46
  [tool.hatch.build.targets.wheel]
55
47
  packages = ["src/hs_m3u8"]
56
48
 
57
- [[tool.rye.sources]]
58
- name = "default"
59
- url = "https://pypi.tuna.tsinghua.edu.cn/simple"
60
-
61
49
  [tool.bandit]
62
50
  skips = [
63
51
  "B404",
@@ -127,20 +115,10 @@ ignore = [
127
115
  # do not perform function calls in argument defaults
128
116
  "B008",
129
117
  # too complex
130
- "C901"
118
+ "C901",
131
119
  ]
132
120
  fixable = ["ALL"]
133
121
  unfixable = []
134
122
 
135
123
  [tool.ruff.lint.per-file-ignores]
136
124
  "__init__.py" = ["F401"]
137
-
138
-
139
- [tool.rye.scripts]
140
- publish_testpypi = { cmd = "rye run twine upload -r testpypi dist/*" }
141
- publish_pypi = { cmd = "rye run twine upload dist/*" }
142
- sb = { cmd = "rye build --clean" }
143
- spt = { chain = ["sb", "publish_testpypi"] }
144
- sp = { chain = ["sb", "publish_pypi"] }
145
- check_i = { cmd = "rye run pre-commit install" }
146
- check = { cmd = "rye run pre-commit run --all-files" }
@@ -1,3 +1,3 @@
1
1
  from hs_m3u8.main import M3u8Downloader, M3u8Key
2
2
 
3
- __version__ = "0.1.5"
3
+ __version__ = "0.1.6"
@@ -16,6 +16,8 @@ from zipfile import ZipFile
16
16
 
17
17
  import m3u8
18
18
  from hssp import Net
19
+ from hssp.models.net import RequestModel
20
+ from hssp.network.response import Response
19
21
  from hssp.utils import crypto
20
22
  from loguru import logger
21
23
 
@@ -80,6 +82,12 @@ class M3u8Downloader:
80
82
  headers=None,
81
83
  key: M3u8Key = None,
82
84
  get_m3u8_func: Callable = None,
85
+ m3u8_request_before: Callable[[RequestModel], RequestModel] = None,
86
+ m3u8_response_after: Callable[[Response], Response] = None,
87
+ key_request_before: Callable[[RequestModel], RequestModel] = None,
88
+ key_response_after: Callable[[Response], Response] = None,
89
+ ts_request_before: Callable[[RequestModel], RequestModel] = None,
90
+ ts_response_after: Callable[[Response], Response] = None,
83
91
  ):
84
92
  """
85
93
 
@@ -91,11 +99,38 @@ class M3u8Downloader:
91
99
  headers: 情求头
92
100
  get_m3u8_func: 处理m3u8情求的回调函数。适用于m3u8地址不是真正的地址,
93
101
  而是包含m3u8内容的情求,会把m3u8_url的响应传递给get_m3u8_func,要求返回真正的m3u8内容
102
+ m3u8_request_before: m3u8请求前的回调函数
103
+ m3u8_response_after: m3u8响应后的回调函数
104
+ key_request_before: key请求前的回调函数
105
+ key_response_after: key响应后的回调函数
106
+ ts_request_before: ts请求前的回调函数
107
+ ts_response_after: ts响应后的回调函数
94
108
  """
95
109
 
96
110
  sem = asyncio.Semaphore(max_workers) if max_workers else None
97
111
  self.headers = headers
98
- self.net = Net(sem=sem)
112
+
113
+ # m3u8 内容的请求器
114
+ self.m3u8_net = Net(sem=sem)
115
+ if m3u8_request_before:
116
+ self.m3u8_net.request_before_signal.connect(m3u8_request_before)
117
+ if m3u8_response_after:
118
+ self.m3u8_net.response_after_signal.connect(m3u8_response_after)
119
+
120
+ # 加密key的请求器
121
+ self.key_net = Net()
122
+ if key_request_before:
123
+ self.key_net.request_before_signal.connect(key_request_before)
124
+ if key_response_after:
125
+ self.key_net.response_after_signal.connect(key_response_after)
126
+
127
+ # ts内容的请求器
128
+ self.ts_net = Net()
129
+ if ts_request_before:
130
+ self.ts_net.request_before_signal.connect(ts_request_before)
131
+ if ts_response_after:
132
+ self.ts_net.response_after_signal.connect(ts_response_after)
133
+
99
134
  self.decrypt = decrypt
100
135
  self.m3u8_url = urlparse(m3u8_url)
101
136
  self.get_m3u8_func = get_m3u8_func
@@ -112,7 +147,9 @@ class M3u8Downloader:
112
147
 
113
148
  async def run(self, merge=True, del_hls=False):
114
149
  await self.start(merge, del_hls)
115
- await self.net.close()
150
+ await self.m3u8_net.close()
151
+ await self.key_net.close()
152
+ await self.ts_net.close()
116
153
 
117
154
  async def start(self, merge=True, del_hls=False):
118
155
  """
@@ -122,6 +159,7 @@ class M3u8Downloader:
122
159
  :return:
123
160
  """
124
161
  mp4_path = self.save_dir.parent / f"{self.save_name}.mp4"
162
+ mp4_path = mp4_path.absolute()
125
163
  if Path(mp4_path).exists():
126
164
  self.logger.info(f"{mp4_path}已存在")
127
165
  if del_hls:
@@ -131,7 +169,7 @@ class M3u8Downloader:
131
169
  self.logger.info(
132
170
  f"开始下载: 合并ts为mp4={merge}, "
133
171
  f"删除hls信息={del_hls}, "
134
- f"下载地址为:{self.m3u8_url.geturl()}. 保存路径为:{self.save_dir}"
172
+ f"下载地址为:{self.m3u8_url.geturl()}. 保存路径为:{self.save_dir.absolute()}"
135
173
  )
136
174
 
137
175
  await self._download()
@@ -141,7 +179,7 @@ class M3u8Downloader:
141
179
  self.logger.info(f"TS应下载数量为:{count_1}, 实际下载数量为:{count_2}")
142
180
  if count_1 == 0 or count_2 == 0:
143
181
  self.logger.error("ts数量为0,请检查!!!")
144
- return
182
+ return None
145
183
 
146
184
  if count_2 != count_1:
147
185
  self.logger.error(f"ts下载数量与实际数量不符合!!!应该下载数量为:{count_1}, 实际下载数量为:{count_2}")
@@ -182,7 +220,7 @@ class M3u8Downloader:
182
220
  :param url:
183
221
  :return:
184
222
  """
185
- resp = await self.net.get(url.geturl(), headers=self.headers)
223
+ resp = await self.m3u8_net.get(url.geturl(), headers=self.headers)
186
224
  m3u8_text = self.get_m3u8_func(resp.text) if self.get_m3u8_func else resp.text
187
225
  m3u8_obj = m3u8.loads(m3u8_text)
188
226
  prefix = f"{url.scheme}://{url.netloc}"
@@ -220,7 +258,7 @@ class M3u8Downloader:
220
258
  if len(m3u8_obj.keys) > 0 and m3u8_obj.keys[0]:
221
259
  iv = m3u8_obj.keys[0].iv
222
260
  if not self.custom_key:
223
- resp = await self.net.get(m3u8_obj.keys[0].absolute_uri, headers=self.headers)
261
+ resp = await self.key_net.get(m3u8_obj.keys[0].absolute_uri, headers=self.headers)
224
262
  key_data = resp.content
225
263
  else:
226
264
  key_data = self.custom_key.key
@@ -252,13 +290,13 @@ class M3u8Downloader:
252
290
  if Path(ts_path).exists():
253
291
  self.ts_path_list[index] = str(ts_path)
254
292
  return
255
- resp = await self.net.get(ts_item["uri"], self.headers)
293
+ resp = await self.ts_net.get(ts_item["uri"], self.headers)
256
294
  ts_content = resp.content
257
295
  if ts_content is None:
258
296
  return
259
297
 
260
298
  if self.ts_key and self.decrypt:
261
- ts_content = crypto.decrypt_aes_256_cbc_pad7(ts_content, self.ts_key.key, self.ts_key.iv)
299
+ ts_content = crypto.decrypt_aes_256_cbc(ts_content, self.ts_key.key, self.ts_key.iv)
262
300
 
263
301
  self.save_file(ts_content, ts_path)
264
302
  self.logger.info(f"{ts_uri}下载成功")
@@ -282,10 +320,10 @@ class M3u8Downloader:
282
320
  # mp4路径
283
321
  mp4_path = self.save_dir.parent / f"{self.save_name}.mp4"
284
322
 
285
- # 如果保护mp4的头,则把ts放到后面
323
+ # 如果有mp4的头,则把ts放到后面
286
324
  mp4_head_data = b""
287
325
  if self.mp4_head_hrl:
288
- resp = await self.net.get(self.mp4_head_hrl)
326
+ resp = await self.ts_net.get(self.mp4_head_hrl)
289
327
  mp4_head_data = resp.content
290
328
  mp4_head_file = self.save_dir / "head.mp4"
291
329
  mp4_head_file.write_bytes(mp4_head_data)
@@ -297,7 +335,7 @@ class M3u8Downloader:
297
335
  with open(path, "rb") as ts_file:
298
336
  data = ts_file.read()
299
337
  if self.ts_key:
300
- data = crypto.decrypt_aes_256_cbc_pad7(data, self.ts_key.key, self.ts_key.iv)
338
+ data = crypto.decrypt_aes_256_cbc(data, self.ts_key.key, self.ts_key.iv)
301
339
  big_ts_file.write(data)
302
340
  big_ts_file.close()
303
341
  self.logger.info("ts文件整合完毕")
@@ -1,18 +0,0 @@
1
- # See https://pre-commit.com for more information
2
- # See https://pre-commit.com/hooks.html for more hooks
3
- repos:
4
- # bandit
5
- - repo: https://github.com/PyCQA/bandit
6
- rev: 1.7.10
7
- hooks:
8
- - id: bandit
9
- args: [ "-c", "pyproject.toml" ]
10
- additional_dependencies: [ "bandit[toml]" ]
11
-
12
- # ruff
13
- - repo: https://github.com/astral-sh/ruff-pre-commit
14
- rev: 'v0.6.9'
15
- hooks:
16
- - id: ruff
17
- args: [ --fix, --exit-non-zero-on-fix, --show-fixes ]
18
- - id: ruff-format
@@ -1,209 +0,0 @@
1
- # generated by rye
2
- # use `rye lock` or `rye sync` to update this lockfile
3
- #
4
- # last locked with the following flags:
5
- # pre: false
6
- # features: []
7
- # all-features: false
8
- # with-sources: false
9
- # generate-hashes: false
10
- # universal: false
11
-
12
- -e file:.
13
- aiohttp==3.9.5
14
- # via hssp
15
- aiosignal==1.3.1
16
- # via aiohttp
17
- annotated-types==0.7.0
18
- # via pydantic
19
- anyio==4.6.2.post1
20
- # via httpx
21
- apscheduler==3.10.4
22
- # via hssp
23
- attrs==24.2.0
24
- # via aiohttp
25
- blinker==1.9.0
26
- # via hssp
27
- certifi==2024.8.30
28
- # via curl-cffi
29
- # via httpcore
30
- # via httpx
31
- # via requests
32
- cffi==1.17.1
33
- # via curl-cffi
34
- cfgv==3.4.0
35
- # via pre-commit
36
- charset-normalizer==3.4.0
37
- # via requests
38
- click==8.1.7
39
- # via drissionpage
40
- cssselect==1.2.0
41
- # via drissionpage
42
- # via parsel
43
- curl-cffi==0.7.3
44
- # via hssp
45
- datarecorder==3.6.2
46
- # via downloadkit
47
- distlib==0.3.9
48
- # via virtualenv
49
- docutils==0.21.2
50
- # via readme-renderer
51
- downloadkit==2.0.5
52
- # via drissionpage
53
- drissionpage==4.1.0.12
54
- # via hssp
55
- et-xmlfile==2.0.0
56
- # via openpyxl
57
- fake-useragent==1.5.1
58
- # via hssp
59
- filelock==3.16.1
60
- # via tldextract
61
- # via virtualenv
62
- frozenlist==1.5.0
63
- # via aiohttp
64
- # via aiosignal
65
- furl==2.1.3
66
- # via hssp
67
- h11==0.14.0
68
- # via httpcore
69
- h2==4.1.0
70
- # via httpx
71
- hpack==4.0.0
72
- # via h2
73
- hssp==0.4.7
74
- # via hs-m3u8
75
- httpcore==1.0.6
76
- # via httpx
77
- httpx==0.27.2
78
- # via hssp
79
- hyperframe==6.0.1
80
- # via h2
81
- identify==2.6.2
82
- # via pre-commit
83
- idna==3.10
84
- # via anyio
85
- # via httpx
86
- # via requests
87
- # via tldextract
88
- # via yarl
89
- jaraco-classes==3.4.0
90
- # via keyring
91
- jaraco-context==6.0.1
92
- # via keyring
93
- jaraco-functools==4.1.0
94
- # via keyring
95
- jmespath==1.0.1
96
- # via parsel
97
- keyring==25.6.0
98
- # via twine
99
- loguru==0.7.2
100
- # via hssp
101
- lxml==5.3.0
102
- # via drissionpage
103
- # via parsel
104
- m3u8==6.0.0
105
- # via hs-m3u8
106
- markdown-it-py==3.0.0
107
- # via rich
108
- mdurl==0.1.2
109
- # via markdown-it-py
110
- more-itertools==10.6.0
111
- # via jaraco-classes
112
- # via jaraco-functools
113
- multidict==6.1.0
114
- # via aiohttp
115
- # via yarl
116
- nh3==0.2.20
117
- # via readme-renderer
118
- nodeenv==1.9.1
119
- # via pre-commit
120
- openpyxl==3.1.5
121
- # via datarecorder
122
- orderedmultidict==1.0.1
123
- # via furl
124
- packaging==24.2
125
- # via parsel
126
- # via twine
127
- parsel==1.9.1
128
- # via hssp
129
- pkginfo==1.12.0
130
- # via twine
131
- platformdirs==4.3.6
132
- # via virtualenv
133
- pre-commit==4.0.1
134
- propcache==0.2.0
135
- # via yarl
136
- psutil==6.1.0
137
- # via drissionpage
138
- pycparser==2.22
139
- # via cffi
140
- pycryptodomex==3.21.0
141
- # via hssp
142
- pydantic==2.9.2
143
- # via hssp
144
- # via pydantic-settings
145
- pydantic-core==2.23.4
146
- # via pydantic
147
- pydantic-settings==2.6.1
148
- # via hssp
149
- pygments==2.19.1
150
- # via readme-renderer
151
- # via rich
152
- python-dotenv==1.0.1
153
- # via pydantic-settings
154
- pytz==2024.2
155
- # via apscheduler
156
- pyyaml==6.0.2
157
- # via pre-commit
158
- # via pydantic-settings
159
- readme-renderer==44.0
160
- # via twine
161
- requests==2.32.3
162
- # via downloadkit
163
- # via drissionpage
164
- # via hssp
165
- # via requests-file
166
- # via requests-toolbelt
167
- # via tldextract
168
- # via twine
169
- requests-file==2.1.0
170
- # via tldextract
171
- requests-toolbelt==1.0.0
172
- # via twine
173
- rfc3986==2.0.0
174
- # via twine
175
- rich==13.9.4
176
- # via twine
177
- six==1.16.0
178
- # via apscheduler
179
- # via furl
180
- # via orderedmultidict
181
- sniffio==1.3.1
182
- # via anyio
183
- # via httpx
184
- tenacity==9.0.0
185
- # via hssp
186
- tldextract==5.1.3
187
- # via drissionpage
188
- tomli==2.1.0
189
- # via pydantic-settings
190
- twine==6.0.1
191
- typing-extensions==4.12.2
192
- # via curl-cffi
193
- # via pydantic
194
- # via pydantic-core
195
- tzlocal==5.2
196
- # via apscheduler
197
- urllib3==2.2.3
198
- # via requests
199
- # via twine
200
- uvloop==0.21.0
201
- # via hssp
202
- virtualenv==20.27.1
203
- # via pre-commit
204
- w3lib==2.2.1
205
- # via parsel
206
- websocket-client==1.8.0
207
- # via drissionpage
208
- yarl==1.17.1
209
- # via aiohttp
@@ -1,157 +0,0 @@
1
- # generated by rye
2
- # use `rye lock` or `rye sync` to update this lockfile
3
- #
4
- # last locked with the following flags:
5
- # pre: false
6
- # features: []
7
- # all-features: false
8
- # with-sources: false
9
- # generate-hashes: false
10
- # universal: false
11
-
12
- -e file:.
13
- aiohttp==3.9.5
14
- # via hssp
15
- aiosignal==1.3.1
16
- # via aiohttp
17
- annotated-types==0.7.0
18
- # via pydantic
19
- anyio==4.6.2.post1
20
- # via httpx
21
- apscheduler==3.10.4
22
- # via hssp
23
- attrs==24.2.0
24
- # via aiohttp
25
- blinker==1.9.0
26
- # via hssp
27
- certifi==2024.8.30
28
- # via curl-cffi
29
- # via httpcore
30
- # via httpx
31
- # via requests
32
- cffi==1.17.1
33
- # via curl-cffi
34
- charset-normalizer==3.4.0
35
- # via requests
36
- click==8.1.7
37
- # via drissionpage
38
- cssselect==1.2.0
39
- # via drissionpage
40
- # via parsel
41
- curl-cffi==0.7.3
42
- # via hssp
43
- datarecorder==3.6.2
44
- # via downloadkit
45
- downloadkit==2.0.5
46
- # via drissionpage
47
- drissionpage==4.1.0.12
48
- # via hssp
49
- et-xmlfile==2.0.0
50
- # via openpyxl
51
- fake-useragent==1.5.1
52
- # via hssp
53
- filelock==3.16.1
54
- # via tldextract
55
- frozenlist==1.5.0
56
- # via aiohttp
57
- # via aiosignal
58
- furl==2.1.3
59
- # via hssp
60
- h11==0.14.0
61
- # via httpcore
62
- h2==4.1.0
63
- # via httpx
64
- hpack==4.0.0
65
- # via h2
66
- hssp==0.4.7
67
- # via hs-m3u8
68
- httpcore==1.0.6
69
- # via httpx
70
- httpx==0.27.2
71
- # via hssp
72
- hyperframe==6.0.1
73
- # via h2
74
- idna==3.10
75
- # via anyio
76
- # via httpx
77
- # via requests
78
- # via tldextract
79
- # via yarl
80
- jmespath==1.0.1
81
- # via parsel
82
- loguru==0.7.2
83
- # via hssp
84
- lxml==5.3.0
85
- # via drissionpage
86
- # via parsel
87
- m3u8==6.0.0
88
- # via hs-m3u8
89
- multidict==6.1.0
90
- # via aiohttp
91
- # via yarl
92
- openpyxl==3.1.5
93
- # via datarecorder
94
- orderedmultidict==1.0.1
95
- # via furl
96
- packaging==24.2
97
- # via parsel
98
- parsel==1.9.1
99
- # via hssp
100
- propcache==0.2.0
101
- # via yarl
102
- psutil==6.1.0
103
- # via drissionpage
104
- pycparser==2.22
105
- # via cffi
106
- pycryptodomex==3.21.0
107
- # via hssp
108
- pydantic==2.9.2
109
- # via hssp
110
- # via pydantic-settings
111
- pydantic-core==2.23.4
112
- # via pydantic
113
- pydantic-settings==2.6.1
114
- # via hssp
115
- python-dotenv==1.0.1
116
- # via pydantic-settings
117
- pytz==2024.2
118
- # via apscheduler
119
- pyyaml==6.0.2
120
- # via pydantic-settings
121
- requests==2.32.3
122
- # via downloadkit
123
- # via drissionpage
124
- # via hssp
125
- # via requests-file
126
- # via tldextract
127
- requests-file==2.1.0
128
- # via tldextract
129
- six==1.16.0
130
- # via apscheduler
131
- # via furl
132
- # via orderedmultidict
133
- sniffio==1.3.1
134
- # via anyio
135
- # via httpx
136
- tenacity==9.0.0
137
- # via hssp
138
- tldextract==5.1.3
139
- # via drissionpage
140
- tomli==2.1.0
141
- # via pydantic-settings
142
- typing-extensions==4.12.2
143
- # via curl-cffi
144
- # via pydantic
145
- # via pydantic-core
146
- tzlocal==5.2
147
- # via apscheduler
148
- urllib3==2.2.3
149
- # via requests
150
- uvloop==0.21.0
151
- # via hssp
152
- w3lib==2.2.1
153
- # via parsel
154
- websocket-client==1.8.0
155
- # via drissionpage
156
- yarl==1.17.1
157
- # via aiohttp
File without changes
@@ -1,14 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = "https://surrit.com/6d3bb2b2-d707-4b79-adf0-89542cb1383c/playlist.m3u8"
8
- name = "SDAB-129"
9
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
10
- await dl.run(del_hls=False, merge=True)
11
-
12
-
13
- if __name__ == "__main__":
14
- asyncio.run(main())
@@ -1,33 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- def get_m3u8(resp_text: str):
7
- """
8
- 获取m3u8真实文本
9
- Args:
10
- resp_text: m3u8_url 获取到的响应文本
11
-
12
- Returns:
13
- 返回真正的m3u8文本
14
-
15
- """
16
- return resp_text
17
-
18
-
19
- async def main():
20
- url = "https://surrit.com/6d3bb2b2-d707-4b79-adf0-89542cb1383c/playlist.m3u8"
21
- name = "SDAB-129"
22
- headers = {
23
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
24
- "(KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
25
- }
26
- dl = M3u8Downloader(
27
- m3u8_url=url, save_path=f"../../downloads/{name}", headers=headers, max_workers=64, get_m3u8_func=get_m3u8
28
- )
29
- await dl.run(del_hls=False, merge=True)
30
-
31
-
32
- if __name__ == "__main__":
33
- asyncio.run(main())
@@ -1,14 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = "https://surrit.com/85f671be-4ebc-4cad-961e-a8d339483cc6/playlist.m3u8"
8
- name = "CUS-2413"
9
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
10
- await dl.run(del_hls=False, merge=True)
11
-
12
-
13
- if __name__ == "__main__":
14
- asyncio.run(main())
@@ -1,43 +0,0 @@
1
- # pip install hs-m3u8==0.1.4
2
-
3
- import asyncio
4
-
5
- from hs_m3u8 import M3u8Downloader, M3u8Key
6
-
7
-
8
- async def main():
9
- url = "https://r1-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/68b6bed7-d093-7c8c-9133-95cf8205d21d.t/zh-CN/1712805650284/transcode/videos/68b6bed7-d093-7c8c-9133-95cf8205d21d-1920x1080-true-47fe81c5c8d91daf25e9fffd7082f934-eed6db5a85074b6dbbe5fa71f1243b26.m3u8"
10
- name = "ykt"
11
- headers = {
12
- "Accept": "*/*",
13
- "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
14
- "Cache-Control": "no-cache",
15
- "Connection": "keep-alive",
16
- "Origin": "https://basic.smartedu.cn",
17
- "Pragma": "no-cache",
18
- "Referer": "https://basic.smartedu.cn/",
19
- "Sec-Fetch-Dest": "empty",
20
- "Sec-Fetch-Mode": "cors",
21
- "Sec-Fetch-Site": "cross-site",
22
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
23
- "Chrome/126.0.0.0 Safari/537.36",
24
- "X-ND-AUTH": 'MAC id="7F938B205F876FC3C7550081F114A1A4028222C3BFB978FD9B439192D004CB8EEB65E66BB'
25
- 'C63E66FED6DD51F34F99411A6039E623E9A9D05",nonce="1742462574752:Z4IGAAV6"'
26
- ',mac="EUr56dXrCO1YGd3Ub1fj9MyJY9NxQPi7ZI14N/GFwpQ="',
27
- "sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
28
- "sec-ch-ua-mobile": "?0",
29
- "sec-ch-ua-platform": '"Windows"',
30
- }
31
- key = M3u8Key(key=bytes.fromhex("34623235336163353939353834643437"))
32
- dl = M3u8Downloader(
33
- m3u8_url=url,
34
- save_path=f"../../downloads/{name}",
35
- max_workers=64,
36
- headers=headers,
37
- key=key,
38
- )
39
- await dl.run(del_hls=False, merge=True)
40
-
41
-
42
- if __name__ == "__main__":
43
- asyncio.run(main())
@@ -1,14 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = "https://v3.dious.cc/20220422/EZWdBGuQ/index.m3u8"
8
- name = "日日是好日"
9
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
10
- await dl.run(del_hls=False, merge=True)
11
-
12
-
13
- if __name__ == "__main__":
14
- asyncio.run(main())
@@ -1,14 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = "https://v4.qrssv.com/202412/05/CCu6EzN8tR20/video/index.m3u8"
8
- name = "毒液:最后一舞 HD-索尼"
9
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
10
- await dl.run(del_hls=False, merge=True)
11
-
12
-
13
- if __name__ == "__main__":
14
- asyncio.run(main())
@@ -1,14 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = "http://1251107588.vod2.myqcloud.com/40f34e4dvodtransgzp1251107588/722ec94f387702303749471522/voddrm.token.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9~eyJ0eXBlIjoiRHJtVG9rZW4iLCJhcHBJZCI6MTI1MTEwNzU4OCwiZmlsZUlkIjoiMzg3NzAyMzAzNzQ5NDcxNTIyIiwiY3VycmVudFRpbWVTdGFtcCI6MCwiZXhwaXJlVGltZVN0YW1wIjoyMTQ3NDgzNjQ3LCJyYW5kb20iOjAsIm92ZXJsYXlLZXkiOiIiLCJvdmVybGF5SXYiOiIiLCJjaXBoZXJlZE92ZXJsYXlLZXkiOiI0NTQ2NzliYjY5Zjg1N2M3NGZjY2YxOGM1ZTk4ZjQyMjk4YTMxMzhkYjgyMTNkZThmYjA4ZDM2MzY0MjQ0NjQ3OWFhYmZlYjk0MmYzZTE1MWNkZjM2OGQ5NGEwMzhlNjI4YjlmMTM1OGM4ODkwMjlkMzcwMjZiYzQ3MTY0MGViMWI5ODExYTg5MWU1ZmYxODk3MjliNGIyOWU4ZWExZjNkZWNkZWJmYTVjZmY0N2U4YzBjYjU4OGE2MWUxZmMzNzNmZWQxYWZhOGU5MmJmMGQ4YjFmNjIwMjM4YWJjMzYyM2FlYWVjYjlkNDI3MmI2ZmMzMmRmNjBlN2VmYjc1NjkzIiwiY2lwaGVyZWRPdmVybGF5SXYiOiIwMTYxOTE2YmI5NWRjOWJlZDgyNmI5NmE0MTY1OThkM2IyZmYyYzJmM2JhZDVmNzg2NDEyMWFlYjJiMWVmNjQwNzg0NmNmZmI5YjkyN2E1YzFlMTFhMGE0MDNlMzg5MWE4Y2VkMTJhMTYyNWRlZDFlYWYwMmJkNTI2ZGFjZWE2OGRjNjc1NTE5ZmE3MjBhYTcxNGU3MGE2MjdhM2IwM2I0YzFjMWJjNjJmODYxMDAyN2M3ZjhlYzc4MTg1MDhlNDc0YTJiZmM3NTZjOTVmMWY3NGMxYWQ5NTkyNjBhZTM0NDczNDA4OWZlMGY5YWIzZmYwYzQxZTFlNGNjYWE4YjcxIiwia2V5SWQiOjEsInN0cmljdE1vZGUiOjAsInBlcnNpc3RlbnQiOiIiLCJyZW50YWxEdXJhdGlvbiI6MCwiZm9yY2VMMVRyYWNrVHlwZXMiOm51bGx9~OvCFF2x41XbwQK_C1fUE6Orr77BeCttlt6-H5Uj8izg.video_1407500_1.m3u8?encdomain=cmv1&sign=b574e853145eeb93fcec3b26d28b9665&t=7fffffff"
8
- name = "siyuanren"
9
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
10
- await dl.run(del_hls=False, merge=True)
11
-
12
-
13
- if __name__ == "__main__":
14
- asyncio.run(main())
@@ -1,16 +0,0 @@
1
- import asyncio
2
-
3
- from hs_m3u8 import M3u8Downloader
4
-
5
-
6
- async def main():
7
- url = (
8
- "https://video.twimg.com/ext_tw_video/1879556885663342592/pu/pl/Vcvv0UK9lOhezJt1.m3u8?variant_version=1&tag=12"
9
- )
10
- name = "x"
11
- dl = M3u8Downloader(m3u8_url=url, save_path=f"../../downloads/{name}", max_workers=64)
12
- await dl.run(del_hls=False, merge=True)
13
-
14
-
15
- if __name__ == "__main__":
16
- asyncio.run(main())
File without changes
File without changes