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.
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/PKG-INFO +2 -2
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/pyproject.toml +5 -27
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/src/hs_m3u8/__init__.py +1 -1
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/src/hs_m3u8/main.py +49 -11
- hs_m3u8-0.1.5/.pre-commit-config.yaml +0 -18
- hs_m3u8-0.1.5/requirements-dev.lock +0 -209
- hs_m3u8-0.1.5/requirements.lock +0 -157
- hs_m3u8-0.1.5/src/example/__init__.py +0 -0
- hs_m3u8-0.1.5/src/example/jav_1.py +0 -14
- hs_m3u8-0.1.5/src/example/jav_2.py +0 -33
- hs_m3u8-0.1.5/src/example/jav_3.py +0 -14
- hs_m3u8-0.1.5/src/example/kyt.py +0 -43
- hs_m3u8-0.1.5/src/example/movie_1.py +0 -14
- hs_m3u8-0.1.5/src/example/movie_2.py +0 -14
- hs_m3u8-0.1.5/src/example/siyuanren.py +0 -14
- hs_m3u8-0.1.5/src/example/x.py +0 -16
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/.gitignore +0 -0
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/README.md +0 -0
- {hs_m3u8-0.1.5 → hs_m3u8-0.1.7}/src/hs_m3u8/res/ffmpeg_win.exe.zip +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hs-m3u8
|
|
3
|
-
Version: 0.1.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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" }
|
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
#
|
|
323
|
+
# 如果有mp4的头,则把ts放到后面
|
|
286
324
|
mp4_head_data = b""
|
|
287
325
|
if self.mp4_head_hrl:
|
|
288
|
-
resp = await self.
|
|
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.
|
|
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
|
hs_m3u8-0.1.5/requirements.lock
DELETED
|
@@ -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())
|
hs_m3u8-0.1.5/src/example/kyt.py
DELETED
|
@@ -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())
|
hs_m3u8-0.1.5/src/example/x.py
DELETED
|
@@ -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
|
|
File without changes
|