p115client 0.0.5.11.8.2__tar.gz → 0.0.5.11.9__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.
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/PKG-INFO +1 -1
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/_upload.py +52 -27
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/client.py +60 -26
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/__init__.py +1 -0
- p115client-0.0.5.11.9/p115client/tool/auth.py +62 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/upload.py +406 -4
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/util.py +23 -1
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/pyproject.toml +1 -1
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/LICENSE +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/__init__.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/const.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/exception.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/py.typed +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/attr.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/download.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/edit.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/export_dir.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/fs_files.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/history.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/iterdir.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/life.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/pool.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/request.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/tool/xys.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/p115client/type.py +0 -0
- {p115client-0.0.5.11.8.2 → p115client-0.0.5.11.9}/readme.md +0 -0
@@ -3,19 +3,19 @@
|
|
3
3
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
5
5
|
__all__ = [
|
6
|
-
"make_dataiter", "oss_upload_sign", "
|
7
|
-
"
|
8
|
-
"
|
6
|
+
"make_dataiter", "oss_upload_sign", "oss_multipart_upload_url", "oss_upload_request",
|
7
|
+
"oss_multipart_upload_init", "oss_multipart_upload_complete", "oss_multipart_upload_cancel",
|
8
|
+
"oss_multipart_upload_part", "oss_multipart_upload_part_iter", "oss_multipart_part_iter",
|
9
|
+
"oss_upload", "oss_multipart_upload",
|
9
10
|
]
|
10
11
|
|
11
12
|
from base64 import b64encode
|
12
13
|
from collections.abc import (
|
13
14
|
AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable,
|
14
|
-
Coroutine, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence,
|
15
|
+
Coroutine, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence,
|
15
16
|
)
|
16
17
|
from datetime import datetime
|
17
18
|
from email.utils import formatdate
|
18
|
-
from functools import partial
|
19
19
|
from hmac import digest as hmac_digest
|
20
20
|
from inspect import iscoroutinefunction
|
21
21
|
from itertools import count
|
@@ -135,8 +135,17 @@ def oss_upload_sign(
|
|
135
135
|
method: str = "PUT",
|
136
136
|
params: None | str | Mapping | Sequence[tuple[Any, Any]] = None,
|
137
137
|
headers: None | Mapping[str, str] | Iterable[tuple[str, str]] = None,
|
138
|
-
) -> tuple[
|
138
|
+
) -> tuple[str, dict]:
|
139
139
|
"""计算认证信息,返回带认证信息的请求头
|
140
|
+
|
141
|
+
:param bucket: 存储桶
|
142
|
+
:param object: 对象 id
|
143
|
+
:param token: 令牌
|
144
|
+
:param method: HTTP 请求方法
|
145
|
+
:param params: 其它查询参数
|
146
|
+
:param headers: 默认的请求头
|
147
|
+
|
148
|
+
:return: 构造后的 查询参数 和 请求头 的 2 元组
|
140
149
|
"""
|
141
150
|
# subresource_keys = (
|
142
151
|
# "accessPoint", "accessPointPolicy", "acl", "append", "asyncFetch", "bucketArchiveDirectRead",
|
@@ -188,7 +197,42 @@ def oss_upload_sign(
|
|
188
197
|
/{bucket}/{object}{params}""".encode("utf-8")
|
189
198
|
signature = to_base64(hmac_digest(bytes(token["AccessKeySecret"], "utf-8"), signature_data, "sha1"))
|
190
199
|
headers["authorization"] = "OSS {0}:{1}".format(token["AccessKeyId"], signature)
|
191
|
-
return
|
200
|
+
return params, headers
|
201
|
+
|
202
|
+
|
203
|
+
def oss_multipart_upload_url(
|
204
|
+
bucket: str,
|
205
|
+
object: str,
|
206
|
+
token: dict,
|
207
|
+
upload_id: int | str,
|
208
|
+
part_number: int = 1,
|
209
|
+
domain: str = "oss-cn-shenzhen.aliyuncs.com",
|
210
|
+
) -> tuple[str, dict]:
|
211
|
+
"""构建分块上传的链接和请求头
|
212
|
+
|
213
|
+
:param bucket: 存储桶
|
214
|
+
:param object: 对象 id
|
215
|
+
:param token: 令牌
|
216
|
+
:param upload_id: 上传 id
|
217
|
+
:param domain: 所用的 阿里云网址 或 包含存储桶和对象的完整网址
|
218
|
+
|
219
|
+
:return: 构造后的 上传链接 和 请求头 的 2 元组
|
220
|
+
"""
|
221
|
+
params, headers = oss_upload_sign(
|
222
|
+
bucket,
|
223
|
+
object,
|
224
|
+
token,
|
225
|
+
method="PUT",
|
226
|
+
params={"partNumber": part_number, "uploadId": upload_id},
|
227
|
+
headers={"x-oss-security-token": token["SecurityToken"]},
|
228
|
+
)
|
229
|
+
if domain.startswith(("http://", "https://")):
|
230
|
+
url = domain.rstrip("/")
|
231
|
+
elif domain:
|
232
|
+
url = f"http://{bucket}.{domain}/{object}"
|
233
|
+
else:
|
234
|
+
url = ""
|
235
|
+
return url+params, headers
|
192
236
|
|
193
237
|
|
194
238
|
def oss_upload_request[T](
|
@@ -206,7 +250,7 @@ def oss_upload_request[T](
|
|
206
250
|
) -> T:
|
207
251
|
"""请求阿里云 OSS 的公用函数
|
208
252
|
"""
|
209
|
-
|
253
|
+
params, headers = oss_upload_sign(
|
210
254
|
bucket,
|
211
255
|
object,
|
212
256
|
token,
|
@@ -929,22 +973,3 @@ def oss_multipart_upload(
|
|
929
973
|
yield close_reporthook()
|
930
974
|
return run_gen_step(gen_step, may_call=False, async_=async_)
|
931
975
|
|
932
|
-
|
933
|
-
# class MultipartUploader:
|
934
|
-
# def __init__
|
935
|
-
# def __del__
|
936
|
-
# async def __aiter__
|
937
|
-
# def __iter__
|
938
|
-
# async def __aenter__
|
939
|
-
# async def __aexit__
|
940
|
-
# def __enter__
|
941
|
-
# def __exit__
|
942
|
-
# # 0. 应该设计 1 个类,支持同步和异步,实例化不会进行初始化(为了对异步进行适配)
|
943
|
-
# # 1. 可以作为上下文管理器或者迭代器使用
|
944
|
-
# # 2. 上下文管理器也返回迭代器(迭代器迭代时,如果未打开文件或者没有上传信息,则会初始化以获取)
|
945
|
-
# # 3. 中途可以暂停或取消
|
946
|
-
# # 4. seekable: path, url (支持 range request), file reader (seekable)
|
947
|
-
# # 5. 支持进度条
|
948
|
-
# # 6. 设计一个工具函数,放到 p115client.tool.upload 模块中
|
949
|
-
# ...
|
950
|
-
|
@@ -3274,7 +3274,6 @@ class P115OpenClient(ClientRequestMixin):
|
|
3274
3274
|
api = complete_proapi("/open/ufile/downurl", base_url)
|
3275
3275
|
if isinstance(payload, str):
|
3276
3276
|
payload = {"pick_code": payload}
|
3277
|
-
request_headers = request_kwargs.get("headers")
|
3278
3277
|
headers = request_kwargs.get("headers")
|
3279
3278
|
if headers:
|
3280
3279
|
if isinstance(headers, Mapping):
|
@@ -3462,7 +3461,7 @@ class P115OpenClient(ClientRequestMixin):
|
|
3462
3461
|
- fields: str = <default>
|
3463
3462
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
3464
3463
|
- format: str = "json" 💡 返回格式,默认即可
|
3465
|
-
- hide_data: str = <default>
|
3464
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
3466
3465
|
- is_q: 0 | 1 = <default>
|
3467
3466
|
- is_share: 0 | 1 = <default>
|
3468
3467
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -3771,8 +3770,8 @@ class P115OpenClient(ClientRequestMixin):
|
|
3771
3770
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
3772
3771
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
3773
3772
|
- source: str = <default>
|
3774
|
-
- star: 0 | 1 = <default>
|
3775
|
-
- suffix: str = <default>
|
3773
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
3774
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
3776
3775
|
- type: int = <default> 💡 文件类型
|
3777
3776
|
|
3778
3777
|
- 0: 全部(仅当前目录)
|
@@ -6173,6 +6172,8 @@ class P115Client(P115OpenClient):
|
|
6173
6172
|
|
6174
6173
|
但一个设备的新登录者,并不总是意味着把较早的登录者下线,一般需要触发某个检查机制后,才会把同一设备下除最近一次登录外的所有 cookies 失效
|
6175
6174
|
|
6175
|
+
所以你可以用一个设备的 cookies 专门用于扫码登录,获取另一个设备的 cookies 执行网盘操作,第 2 个 cookies 失效了,则用第 1 个 cookies 扫码,如此可避免单个 cookies 失效后,不能自动获取新的
|
6176
|
+
|
6176
6177
|
:param app: 要登录的 app,如果为 None,则用当前登录设备,如果无当前登录设备,则报错
|
6177
6178
|
:param replace: 替换某个 client 对象的 cookie
|
6178
6179
|
|
@@ -6860,6 +6861,7 @@ class P115Client(P115OpenClient):
|
|
6860
6861
|
self,
|
6861
6862
|
payload: dict,
|
6862
6863
|
/,
|
6864
|
+
app: str = "web",
|
6863
6865
|
base_url: bool | str | Callable[[], str] = False,
|
6864
6866
|
*,
|
6865
6867
|
async_: Literal[False] = False,
|
@@ -6871,6 +6873,7 @@ class P115Client(P115OpenClient):
|
|
6871
6873
|
self,
|
6872
6874
|
payload: dict,
|
6873
6875
|
/,
|
6876
|
+
app: str = "web",
|
6874
6877
|
base_url: bool | str | Callable[[], str] = False,
|
6875
6878
|
*,
|
6876
6879
|
async_: Literal[True],
|
@@ -6881,6 +6884,7 @@ class P115Client(P115OpenClient):
|
|
6881
6884
|
self,
|
6882
6885
|
payload: dict,
|
6883
6886
|
/,
|
6887
|
+
app: str = "web",
|
6884
6888
|
base_url: bool | str | Callable[[], str] = False,
|
6885
6889
|
*,
|
6886
6890
|
async_: Literal[False, True] = False,
|
@@ -6895,7 +6899,7 @@ class P115Client(P115OpenClient):
|
|
6895
6899
|
- aid: int | str 💡 助愿的 id
|
6896
6900
|
- to_cid: int = <default> 💡 助愿中的分享链接转存到你的网盘中目录的 id
|
6897
6901
|
"""
|
6898
|
-
api = complete_api("/api/1.0/
|
6902
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/adopt", "act", base_url=base_url)
|
6899
6903
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6900
6904
|
|
6901
6905
|
@overload
|
@@ -6903,6 +6907,7 @@ class P115Client(P115OpenClient):
|
|
6903
6907
|
self,
|
6904
6908
|
payload: dict,
|
6905
6909
|
/,
|
6910
|
+
app: str = "web",
|
6906
6911
|
base_url: bool | str | Callable[[], str] = False,
|
6907
6912
|
*,
|
6908
6913
|
async_: Literal[False] = False,
|
@@ -6914,6 +6919,7 @@ class P115Client(P115OpenClient):
|
|
6914
6919
|
self,
|
6915
6920
|
payload: dict,
|
6916
6921
|
/,
|
6922
|
+
app: str = "web",
|
6917
6923
|
base_url: bool | str | Callable[[], str] = False,
|
6918
6924
|
*,
|
6919
6925
|
async_: Literal[True],
|
@@ -6924,6 +6930,7 @@ class P115Client(P115OpenClient):
|
|
6924
6930
|
self,
|
6925
6931
|
payload: dict,
|
6926
6932
|
/,
|
6933
|
+
app: str = "web",
|
6927
6934
|
base_url: bool | str | Callable[[], str] = False,
|
6928
6935
|
*,
|
6929
6936
|
async_: Literal[False, True] = False,
|
@@ -6939,7 +6946,7 @@ class P115Client(P115OpenClient):
|
|
6939
6946
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
6940
6947
|
- file_ids: int | str = <default> 💡 文件在你的网盘的 id,多个用逗号 "," 隔开
|
6941
6948
|
"""
|
6942
|
-
api = complete_api("/api/1.0/
|
6949
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/aid_desire", "act", base_url=base_url)
|
6943
6950
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6944
6951
|
|
6945
6952
|
@overload
|
@@ -6947,6 +6954,7 @@ class P115Client(P115OpenClient):
|
|
6947
6954
|
self,
|
6948
6955
|
payload: int | str | dict,
|
6949
6956
|
/,
|
6957
|
+
app: str = "web",
|
6950
6958
|
base_url: bool | str | Callable[[], str] = False,
|
6951
6959
|
*,
|
6952
6960
|
async_: Literal[False] = False,
|
@@ -6958,6 +6966,7 @@ class P115Client(P115OpenClient):
|
|
6958
6966
|
self,
|
6959
6967
|
payload: int | str | dict,
|
6960
6968
|
/,
|
6969
|
+
app: str = "web",
|
6961
6970
|
base_url: bool | str | Callable[[], str] = False,
|
6962
6971
|
*,
|
6963
6972
|
async_: Literal[True],
|
@@ -6968,6 +6977,7 @@ class P115Client(P115OpenClient):
|
|
6968
6977
|
self,
|
6969
6978
|
payload: int | str | dict,
|
6970
6979
|
/,
|
6980
|
+
app: str = "web",
|
6971
6981
|
base_url: bool | str | Callable[[], str] = False,
|
6972
6982
|
*,
|
6973
6983
|
async_: Literal[False, True] = False,
|
@@ -6980,7 +6990,7 @@ class P115Client(P115OpenClient):
|
|
6980
6990
|
:payload:
|
6981
6991
|
- ids: int | str 💡 助愿的 id,多个用逗号 "," 隔开
|
6982
6992
|
"""
|
6983
|
-
api = complete_api("/api/1.0/
|
6993
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/del_aid_desire", "act", base_url=base_url)
|
6984
6994
|
if isinstance(payload, (int, str)):
|
6985
6995
|
payload = {"ids": payload}
|
6986
6996
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -6990,6 +7000,7 @@ class P115Client(P115OpenClient):
|
|
6990
7000
|
self,
|
6991
7001
|
payload: str | dict,
|
6992
7002
|
/,
|
7003
|
+
app: str = "web",
|
6993
7004
|
base_url: bool | str | Callable[[], str] = False,
|
6994
7005
|
*,
|
6995
7006
|
async_: Literal[False] = False,
|
@@ -7001,6 +7012,7 @@ class P115Client(P115OpenClient):
|
|
7001
7012
|
self,
|
7002
7013
|
payload: str | dict,
|
7003
7014
|
/,
|
7015
|
+
app: str = "web",
|
7004
7016
|
base_url: bool | str | Callable[[], str] = False,
|
7005
7017
|
*,
|
7006
7018
|
async_: Literal[True],
|
@@ -7011,6 +7023,7 @@ class P115Client(P115OpenClient):
|
|
7011
7023
|
self,
|
7012
7024
|
payload: str | dict,
|
7013
7025
|
/,
|
7026
|
+
app: str = "web",
|
7014
7027
|
base_url: bool | str | Callable[[], str] = False,
|
7015
7028
|
*,
|
7016
7029
|
async_: Literal[False, True] = False,
|
@@ -7027,7 +7040,7 @@ class P115Client(P115OpenClient):
|
|
7027
7040
|
- limit: int = 10 💡 分页大小
|
7028
7041
|
- sort: int | str = <default> 💡 排序
|
7029
7042
|
"""
|
7030
|
-
api = complete_api("/api/1.0/
|
7043
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/desire_aid_list", "act", base_url=base_url)
|
7031
7044
|
if isinstance(payload, str):
|
7032
7045
|
payload = {"start": 0, "page": 1, "limit": 10, "id": payload}
|
7033
7046
|
else:
|
@@ -7038,6 +7051,7 @@ class P115Client(P115OpenClient):
|
|
7038
7051
|
def act_xys_get_act_info(
|
7039
7052
|
self,
|
7040
7053
|
/,
|
7054
|
+
app: str = "web",
|
7041
7055
|
base_url: bool | str | Callable[[], str] = False,
|
7042
7056
|
*,
|
7043
7057
|
async_: Literal[False] = False,
|
@@ -7048,6 +7062,7 @@ class P115Client(P115OpenClient):
|
|
7048
7062
|
def act_xys_get_act_info(
|
7049
7063
|
self,
|
7050
7064
|
/,
|
7065
|
+
app: str = "web",
|
7051
7066
|
base_url: bool | str | Callable[[], str] = False,
|
7052
7067
|
*,
|
7053
7068
|
async_: Literal[True],
|
@@ -7057,6 +7072,7 @@ class P115Client(P115OpenClient):
|
|
7057
7072
|
def act_xys_get_act_info(
|
7058
7073
|
self,
|
7059
7074
|
/,
|
7075
|
+
app: str = "web",
|
7060
7076
|
base_url: bool | str | Callable[[], str] = False,
|
7061
7077
|
*,
|
7062
7078
|
async_: Literal[False, True] = False,
|
@@ -7066,7 +7082,7 @@ class P115Client(P115OpenClient):
|
|
7066
7082
|
|
7067
7083
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/get_act_info
|
7068
7084
|
"""
|
7069
|
-
api = complete_api("/api/1.0/
|
7085
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/get_act_info", "act", base_url=base_url)
|
7070
7086
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7071
7087
|
|
7072
7088
|
@overload
|
@@ -7074,6 +7090,7 @@ class P115Client(P115OpenClient):
|
|
7074
7090
|
self,
|
7075
7091
|
payload: str | dict,
|
7076
7092
|
/,
|
7093
|
+
app: str = "web",
|
7077
7094
|
base_url: bool | str | Callable[[], str] = False,
|
7078
7095
|
*,
|
7079
7096
|
async_: Literal[False] = False,
|
@@ -7085,6 +7102,7 @@ class P115Client(P115OpenClient):
|
|
7085
7102
|
self,
|
7086
7103
|
payload: str | dict,
|
7087
7104
|
/,
|
7105
|
+
app: str = "web",
|
7088
7106
|
base_url: bool | str | Callable[[], str] = False,
|
7089
7107
|
*,
|
7090
7108
|
async_: Literal[True],
|
@@ -7095,6 +7113,7 @@ class P115Client(P115OpenClient):
|
|
7095
7113
|
self,
|
7096
7114
|
payload: str | dict,
|
7097
7115
|
/,
|
7116
|
+
app: str = "web",
|
7098
7117
|
base_url: bool | str | Callable[[], str] = False,
|
7099
7118
|
*,
|
7100
7119
|
async_: Literal[False, True] = False,
|
@@ -7107,7 +7126,7 @@ class P115Client(P115OpenClient):
|
|
7107
7126
|
:payload:
|
7108
7127
|
- id: str 💡 许愿的 id
|
7109
7128
|
"""
|
7110
|
-
api = complete_api("/api/1.0/
|
7129
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/get_desire_info", "act", base_url=base_url)
|
7111
7130
|
if isinstance(payload, str):
|
7112
7131
|
payload = {"id": payload}
|
7113
7132
|
return self.request(url=api, params=payload, async_=async_, **request_kwargs)
|
@@ -7116,6 +7135,7 @@ class P115Client(P115OpenClient):
|
|
7116
7135
|
def act_xys_home_list(
|
7117
7136
|
self,
|
7118
7137
|
/,
|
7138
|
+
app: str = "web",
|
7119
7139
|
base_url: bool | str | Callable[[], str] = False,
|
7120
7140
|
*,
|
7121
7141
|
async_: Literal[False] = False,
|
@@ -7126,6 +7146,7 @@ class P115Client(P115OpenClient):
|
|
7126
7146
|
def act_xys_home_list(
|
7127
7147
|
self,
|
7128
7148
|
/,
|
7149
|
+
app: str = "web",
|
7129
7150
|
base_url: bool | str | Callable[[], str] = False,
|
7130
7151
|
*,
|
7131
7152
|
async_: Literal[True],
|
@@ -7135,6 +7156,7 @@ class P115Client(P115OpenClient):
|
|
7135
7156
|
def act_xys_home_list(
|
7136
7157
|
self,
|
7137
7158
|
/,
|
7159
|
+
app: str = "web",
|
7138
7160
|
base_url: bool | str | Callable[[], str] = False,
|
7139
7161
|
*,
|
7140
7162
|
async_: Literal[False, True] = False,
|
@@ -7144,7 +7166,7 @@ class P115Client(P115OpenClient):
|
|
7144
7166
|
|
7145
7167
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/home_list
|
7146
7168
|
"""
|
7147
|
-
api = complete_api("/api/1.0/
|
7169
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/home_list", "act", base_url=base_url)
|
7148
7170
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7149
7171
|
|
7150
7172
|
@overload
|
@@ -7152,6 +7174,7 @@ class P115Client(P115OpenClient):
|
|
7152
7174
|
self,
|
7153
7175
|
payload: int | str | dict = 0,
|
7154
7176
|
/,
|
7177
|
+
app: str = "web",
|
7155
7178
|
base_url: bool | str | Callable[[], str] = False,
|
7156
7179
|
*,
|
7157
7180
|
async_: Literal[False] = False,
|
@@ -7163,6 +7186,7 @@ class P115Client(P115OpenClient):
|
|
7163
7186
|
self,
|
7164
7187
|
payload: int | str | dict = 0,
|
7165
7188
|
/,
|
7189
|
+
app: str = "web",
|
7166
7190
|
base_url: bool | str | Callable[[], str] = False,
|
7167
7191
|
*,
|
7168
7192
|
async_: Literal[True],
|
@@ -7173,6 +7197,7 @@ class P115Client(P115OpenClient):
|
|
7173
7197
|
self,
|
7174
7198
|
payload: int | str | dict = 0,
|
7175
7199
|
/,
|
7200
|
+
app: str = "web",
|
7176
7201
|
base_url: bool | str | Callable[[], str] = False,
|
7177
7202
|
*,
|
7178
7203
|
async_: Literal[False, True] = False,
|
@@ -7193,7 +7218,7 @@ class P115Client(P115OpenClient):
|
|
7193
7218
|
- page: int = 1 💡 第几页
|
7194
7219
|
- limit: int = 10 💡 分页大小
|
7195
7220
|
"""
|
7196
|
-
api = complete_api("/api/1.0/
|
7221
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/my_aid_desire", "act", base_url=base_url)
|
7197
7222
|
if isinstance(payload, (int, str)):
|
7198
7223
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7199
7224
|
else:
|
@@ -7205,6 +7230,7 @@ class P115Client(P115OpenClient):
|
|
7205
7230
|
self,
|
7206
7231
|
payload: int | str | dict = 0,
|
7207
7232
|
/,
|
7233
|
+
app: str = "web",
|
7208
7234
|
base_url: bool | str | Callable[[], str] = False,
|
7209
7235
|
*,
|
7210
7236
|
async_: Literal[False] = False,
|
@@ -7216,6 +7242,7 @@ class P115Client(P115OpenClient):
|
|
7216
7242
|
self,
|
7217
7243
|
payload: int | str | dict = 0,
|
7218
7244
|
/,
|
7245
|
+
app: str = "web",
|
7219
7246
|
base_url: bool | str | Callable[[], str] = False,
|
7220
7247
|
*,
|
7221
7248
|
async_: Literal[True],
|
@@ -7226,6 +7253,7 @@ class P115Client(P115OpenClient):
|
|
7226
7253
|
self,
|
7227
7254
|
payload: int | str | dict = 0,
|
7228
7255
|
/,
|
7256
|
+
app: str = "web",
|
7229
7257
|
base_url: bool | str | Callable[[], str] = False,
|
7230
7258
|
*,
|
7231
7259
|
async_: Literal[False, True] = False,
|
@@ -7246,7 +7274,7 @@ class P115Client(P115OpenClient):
|
|
7246
7274
|
- page: int = 1 💡 第几页
|
7247
7275
|
- limit: int = 10 💡 分页大小
|
7248
7276
|
"""
|
7249
|
-
api = complete_api("/api/1.0/
|
7277
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/my_desire", "act", base_url=base_url)
|
7250
7278
|
if isinstance(payload, (int, str)):
|
7251
7279
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7252
7280
|
else:
|
@@ -7258,6 +7286,7 @@ class P115Client(P115OpenClient):
|
|
7258
7286
|
self,
|
7259
7287
|
payload: str | dict,
|
7260
7288
|
/,
|
7289
|
+
app: str = "web",
|
7261
7290
|
base_url: bool | str | Callable[[], str] = False,
|
7262
7291
|
*,
|
7263
7292
|
async_: Literal[False] = False,
|
@@ -7269,6 +7298,7 @@ class P115Client(P115OpenClient):
|
|
7269
7298
|
self,
|
7270
7299
|
payload: str | dict,
|
7271
7300
|
/,
|
7301
|
+
app: str = "web",
|
7272
7302
|
base_url: bool | str | Callable[[], str] = False,
|
7273
7303
|
*,
|
7274
7304
|
async_: Literal[True],
|
@@ -7279,6 +7309,7 @@ class P115Client(P115OpenClient):
|
|
7279
7309
|
self,
|
7280
7310
|
payload: str | dict,
|
7281
7311
|
/,
|
7312
|
+
app: str = "web",
|
7282
7313
|
base_url: bool | str | Callable[[], str] = False,
|
7283
7314
|
*,
|
7284
7315
|
async_: Literal[False, True] = False,
|
@@ -7293,7 +7324,7 @@ class P115Client(P115OpenClient):
|
|
7293
7324
|
- rewardSpace: int = 5 💡 奖励容量,单位是 GB
|
7294
7325
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
7295
7326
|
"""
|
7296
|
-
api = complete_api("/api/1.0/
|
7327
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/wish", "act", base_url=base_url)
|
7297
7328
|
if isinstance(payload, str):
|
7298
7329
|
payload = {"rewardSpace": 5, "content": payload}
|
7299
7330
|
else:
|
@@ -7305,6 +7336,7 @@ class P115Client(P115OpenClient):
|
|
7305
7336
|
self,
|
7306
7337
|
payload: str | dict,
|
7307
7338
|
/,
|
7339
|
+
app: str = "web",
|
7308
7340
|
base_url: bool | str | Callable[[], str] = False,
|
7309
7341
|
*,
|
7310
7342
|
async_: Literal[False] = False,
|
@@ -7316,6 +7348,7 @@ class P115Client(P115OpenClient):
|
|
7316
7348
|
self,
|
7317
7349
|
payload: str | dict,
|
7318
7350
|
/,
|
7351
|
+
app: str = "web",
|
7319
7352
|
base_url: bool | str | Callable[[], str] = False,
|
7320
7353
|
*,
|
7321
7354
|
async_: Literal[True],
|
@@ -7326,6 +7359,7 @@ class P115Client(P115OpenClient):
|
|
7326
7359
|
self,
|
7327
7360
|
payload: str | dict,
|
7328
7361
|
/,
|
7362
|
+
app: str = "web",
|
7329
7363
|
base_url: bool | str | Callable[[], str] = False,
|
7330
7364
|
*,
|
7331
7365
|
async_: Literal[False, True] = False,
|
@@ -7338,7 +7372,7 @@ class P115Client(P115OpenClient):
|
|
7338
7372
|
:payload:
|
7339
7373
|
- ids: str 💡 许愿的 id,多个用逗号 "," 隔开
|
7340
7374
|
"""
|
7341
|
-
api = complete_api("/api/1.0/
|
7375
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/del_wish", "act", base_url=base_url)
|
7342
7376
|
if isinstance(payload, str):
|
7343
7377
|
payload = {"ids": payload}
|
7344
7378
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -7954,7 +7988,6 @@ class P115Client(P115OpenClient):
|
|
7954
7988
|
payload = {"pick_code": payload}
|
7955
7989
|
else:
|
7956
7990
|
payload = {"pick_code": payload["pickcode"]}
|
7957
|
-
request_headers = request_kwargs.get("headers")
|
7958
7991
|
headers = request_kwargs.get("headers")
|
7959
7992
|
if headers:
|
7960
7993
|
if isinstance(headers, Mapping):
|
@@ -8215,7 +8248,6 @@ class P115Client(P115OpenClient):
|
|
8215
8248
|
- full_name: str
|
8216
8249
|
"""
|
8217
8250
|
api = complete_proapi("/2.0/ufile/extract_down_file", base_url, app)
|
8218
|
-
request_headers = request_kwargs.get("headers")
|
8219
8251
|
headers = request_kwargs.get("headers")
|
8220
8252
|
if headers:
|
8221
8253
|
if isinstance(headers, Mapping):
|
@@ -8271,7 +8303,6 @@ class P115Client(P115OpenClient):
|
|
8271
8303
|
- full_name: str
|
8272
8304
|
"""
|
8273
8305
|
api = complete_webapi("/files/extract_down_file", base_url=base_url)
|
8274
|
-
request_headers = request_kwargs.get("headers")
|
8275
8306
|
headers = request_kwargs.get("headers")
|
8276
8307
|
if headers:
|
8277
8308
|
if isinstance(headers, Mapping):
|
@@ -10515,7 +10546,7 @@ class P115Client(P115OpenClient):
|
|
10515
10546
|
- fields: str = <default>
|
10516
10547
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10517
10548
|
- format: str = "json" 💡 返回格式,默认即可
|
10518
|
-
- hide_data: str = <default>
|
10549
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10519
10550
|
- is_q: 0 | 1 = <default>
|
10520
10551
|
- is_share: 0 | 1 = <default>
|
10521
10552
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10645,7 +10676,7 @@ class P115Client(P115OpenClient):
|
|
10645
10676
|
- fields: str = <default>
|
10646
10677
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10647
10678
|
- format: str = "json" 💡 返回格式,默认即可
|
10648
|
-
- hide_data: str = <default>
|
10679
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10649
10680
|
- is_q: 0 | 1 = <default>
|
10650
10681
|
- is_share: 0 | 1 = <default>
|
10651
10682
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10774,7 +10805,7 @@ class P115Client(P115OpenClient):
|
|
10774
10805
|
- fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
|
10775
10806
|
- fields: str = <default>
|
10776
10807
|
- format: str = "json" 💡 返回格式,默认即可
|
10777
|
-
- hide_data: str = <default>
|
10808
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10778
10809
|
- is_asc: 0 | 1 = <default>
|
10779
10810
|
- is_q: 0 | 1 = <default>
|
10780
10811
|
- is_share: 0 | 1 = <default>
|
@@ -11729,7 +11760,7 @@ class P115Client(P115OpenClient):
|
|
11729
11760
|
:payload:
|
11730
11761
|
- offset: int = 0
|
11731
11762
|
- limit: int = 1150
|
11732
|
-
- played_end: 0 | 1 = <default>
|
11763
|
+
- played_end: 0 | 1 = <default> 💡 是否已经播放完
|
11733
11764
|
- type: int = <default> 💡 类型(??表示还未搞清楚),多个用逗号 "," 隔开
|
11734
11765
|
|
11735
11766
|
- 全部: 0
|
@@ -14604,8 +14635,8 @@ class P115Client(P115OpenClient):
|
|
14604
14635
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14605
14636
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14606
14637
|
- source: str = <default>
|
14607
|
-
- star: 0 | 1 = <default>
|
14608
|
-
- suffix: str = <default>
|
14638
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14639
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14609
14640
|
- type: int = <default> 💡 文件类型
|
14610
14641
|
|
14611
14642
|
- 0: 全部(仅当前目录)
|
@@ -14701,8 +14732,8 @@ class P115Client(P115OpenClient):
|
|
14701
14732
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14702
14733
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14703
14734
|
- source: str = <default>
|
14704
|
-
- star: 0 | 1 = <default>
|
14705
|
-
- suffix: str = <default>
|
14735
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14736
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14706
14737
|
- type: int = <default> 💡 文件类型
|
14707
14738
|
|
14708
14739
|
- 0: 全部(仅当前目录)
|
@@ -21144,6 +21175,9 @@ class P115Client(P115OpenClient):
|
|
21144
21175
|
)
|
21145
21176
|
return run_gen_step(gen_step, may_call=False, async_=async_)
|
21146
21177
|
|
21178
|
+
# TODO: 分块上传时,允许一定次数的重试
|
21179
|
+
# TODO: 不妨单独为分块上传做一个封装
|
21180
|
+
# TODO: 减少参数,简化使用方法
|
21147
21181
|
@overload # type: ignore
|
21148
21182
|
def upload_file(
|
21149
21183
|
self,
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
5
|
+
__all__ = ["deauth_open"]
|
6
|
+
__doc__ = "这个模块提供了一些和账号状况有关的函数"
|
7
|
+
|
8
|
+
from collections.abc import Callable, Coroutine
|
9
|
+
from typing import overload, Any, Literal
|
10
|
+
|
11
|
+
from iterutils import run_gen_step
|
12
|
+
from p115client import check_response, P115Client
|
13
|
+
|
14
|
+
|
15
|
+
@overload
|
16
|
+
def deauth_open(
|
17
|
+
client: str | P115Client,
|
18
|
+
predicate: None | Callable = None,
|
19
|
+
*,
|
20
|
+
async_: Literal[False] = False,
|
21
|
+
**request_kwargs,
|
22
|
+
) -> None:
|
23
|
+
...
|
24
|
+
@overload
|
25
|
+
def deauth_open(
|
26
|
+
client: str | P115Client,
|
27
|
+
predicate: None | Callable = None,
|
28
|
+
*,
|
29
|
+
async_: Literal[True],
|
30
|
+
**request_kwargs,
|
31
|
+
) -> Coroutine[Any, Any, None]:
|
32
|
+
...
|
33
|
+
def deauth_open(
|
34
|
+
client: str | P115Client,
|
35
|
+
predicate: None | Callable = None,
|
36
|
+
*,
|
37
|
+
async_: Literal[False, True] = False,
|
38
|
+
**request_kwargs,
|
39
|
+
) -> None | Coroutine[Any, Any, None]:
|
40
|
+
"""批量解绑开放应用
|
41
|
+
|
42
|
+
:param client: 115 客户端或 cookies
|
43
|
+
:param predicate: 筛选条件
|
44
|
+
:param async_: 是否异步
|
45
|
+
:param request_kwargs: 其它请求参数
|
46
|
+
"""
|
47
|
+
if isinstance(client, str):
|
48
|
+
client = P115Client(client, check_for_relogin=True)
|
49
|
+
def gen_step():
|
50
|
+
resp = yield client.login_open_auth_list(
|
51
|
+
async_=async_,
|
52
|
+
**request_kwargs,
|
53
|
+
)
|
54
|
+
check_response(resp)
|
55
|
+
for info in filter(predicate, resp["data"]):
|
56
|
+
yield client.login_open_deauth(
|
57
|
+
info["auth_id"],
|
58
|
+
async_=async_,
|
59
|
+
**request_kwargs,
|
60
|
+
)
|
61
|
+
return run_gen_step(gen_step, async_=async_)
|
62
|
+
|
@@ -2,20 +2,47 @@
|
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
5
|
-
__all__ = [
|
5
|
+
__all__ = [
|
6
|
+
"iter_115_to_115", "iter_115_to_115_resume", "multipart_upload_init",
|
7
|
+
"multipart_upload_url", "multipart_upload_complete",
|
8
|
+
]
|
6
9
|
__doc__ = "这个模块提供了一些和上传有关的函数"
|
7
10
|
|
8
|
-
|
11
|
+
import errno
|
12
|
+
|
13
|
+
from asyncio import to_thread
|
14
|
+
from collections.abc import AsyncIterator, Callable, Coroutine, Iterator
|
9
15
|
from itertools import dropwhile
|
10
|
-
from
|
16
|
+
from os import fsdecode, stat, PathLike
|
17
|
+
from typing import cast, overload, Any, Literal
|
18
|
+
from urllib.parse import unquote, urlsplit
|
19
|
+
from uuid import uuid4
|
11
20
|
|
12
21
|
from asynctools import to_list
|
13
22
|
from concurrenttools import threadpool_map, taskgroup_map, Return
|
14
|
-
from
|
23
|
+
from hashtools import file_digest, file_digest_async
|
24
|
+
from http_request import SupportsGeturl
|
25
|
+
from http_response import get_total_length
|
26
|
+
from iterutils import (
|
27
|
+
as_gen_step, collect, run_gen_step, run_gen_step_iter,
|
28
|
+
with_iter_next, YieldFrom,
|
29
|
+
)
|
30
|
+
from orjson import loads
|
15
31
|
from p115client import check_response, normalize_attr_simple, P115Client
|
32
|
+
from p115client.exception import OperationalError
|
33
|
+
from p115client._upload import (
|
34
|
+
oss_multipart_part_iter, oss_multipart_upload_init,
|
35
|
+
oss_multipart_upload_url, oss_multipart_upload_complete,
|
36
|
+
)
|
37
|
+
from urlopen import urlopen
|
38
|
+
from yarl import URL
|
16
39
|
|
17
40
|
from .download import iter_download_files
|
18
41
|
from .iterdir import iterdir, iter_files_with_path, unescape_115_charref
|
42
|
+
from .util import determine_part_size
|
43
|
+
|
44
|
+
|
45
|
+
ALIYUN_DOMAIN = "oss-cn-shenzhen.aliyuncs.com"
|
19
46
|
|
20
47
|
|
21
48
|
@overload
|
@@ -431,3 +458,378 @@ def iter_115_to_115_resume(
|
|
431
458
|
))
|
432
459
|
return run_gen_step_iter(gen_step, may_call=False, async_=async_)
|
433
460
|
|
461
|
+
|
462
|
+
@overload
|
463
|
+
def multipart_upload_init(
|
464
|
+
client: str | P115Client,
|
465
|
+
path: str | PathLike | URL | SupportsGeturl,
|
466
|
+
pid: int = 0,
|
467
|
+
filename: str = "",
|
468
|
+
filesize: int = -1,
|
469
|
+
filesha1: str = "",
|
470
|
+
partsize: int = -1,
|
471
|
+
upload_data: None | dict = None,
|
472
|
+
domain: str = ALIYUN_DOMAIN,
|
473
|
+
*,
|
474
|
+
async_: Literal[False] = False,
|
475
|
+
**request_kwargs,
|
476
|
+
) -> dict:
|
477
|
+
...
|
478
|
+
@overload
|
479
|
+
def multipart_upload_init(
|
480
|
+
client: str | P115Client,
|
481
|
+
path: str | PathLike | URL | SupportsGeturl,
|
482
|
+
pid: int = 0,
|
483
|
+
filename: str = "",
|
484
|
+
filesize: int = -1,
|
485
|
+
filesha1: str = "",
|
486
|
+
partsize: int = -1,
|
487
|
+
upload_data: None | dict = None,
|
488
|
+
domain: str = ALIYUN_DOMAIN,
|
489
|
+
*,
|
490
|
+
async_: Literal[True],
|
491
|
+
**request_kwargs,
|
492
|
+
) -> Coroutine[Any, Any, dict]:
|
493
|
+
...
|
494
|
+
def multipart_upload_init(
|
495
|
+
client: str | P115Client,
|
496
|
+
path: str | PathLike | URL | SupportsGeturl,
|
497
|
+
pid: int = 0,
|
498
|
+
filename: str = "",
|
499
|
+
filesize: int = -1,
|
500
|
+
filesha1: str = "",
|
501
|
+
partsize: int = -1,
|
502
|
+
upload_data: None | dict = None,
|
503
|
+
domain: str = ALIYUN_DOMAIN,
|
504
|
+
*,
|
505
|
+
async_: Literal[False, True] = False,
|
506
|
+
**request_kwargs,
|
507
|
+
) -> dict | Coroutine[Any, Any, dict]:
|
508
|
+
"""准备分块上传,获取必要信息
|
509
|
+
|
510
|
+
:param client: 115 客户端或 cookies
|
511
|
+
:param path: 路径 或 链接(仅支持 GET 请求,http(s)协议)
|
512
|
+
:param pid: 上传文件到此目录的 id
|
513
|
+
:param filename: 文件名,若为空则自动确定
|
514
|
+
:param filesize: 文件大小,若为负数则自动计算
|
515
|
+
:param filesha1: 文件的 sha1 摘要,若为空则自动计算
|
516
|
+
:param partsize: 分块大小,若不为正数则自动确定
|
517
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
518
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
519
|
+
:param async_: 是否异步
|
520
|
+
:param request_kwargs: 其它请求参数
|
521
|
+
|
522
|
+
:return: 如果秒传成功,则返回响应信息(有 "status" 字段),否则返回上传配置信息(可用于断点续传)
|
523
|
+
"""
|
524
|
+
if isinstance(client, str):
|
525
|
+
client = P115Client(client, check_for_relogin=True)
|
526
|
+
def gen_step():
|
527
|
+
nonlocal upload_data, path, filename, filesha1, filesize, partsize
|
528
|
+
if upload_data is None:
|
529
|
+
upload_data = {}
|
530
|
+
if isinstance(path, str):
|
531
|
+
is_path = not path.startswith(("http://", "https://"))
|
532
|
+
elif isinstance(path, URL):
|
533
|
+
path = str(path)
|
534
|
+
is_path = False
|
535
|
+
elif isinstance(path, SupportsGeturl):
|
536
|
+
path = path.geturl()
|
537
|
+
is_path = False
|
538
|
+
else:
|
539
|
+
path = fsdecode(path)
|
540
|
+
is_path = True
|
541
|
+
path = cast(str, path)
|
542
|
+
if not filename:
|
543
|
+
if is_path:
|
544
|
+
from os.path import basename
|
545
|
+
filename = basename(path)
|
546
|
+
else:
|
547
|
+
from posixpath import basename
|
548
|
+
filename = basename(unquote(urlsplit(path).path))
|
549
|
+
if not filename:
|
550
|
+
filename = str(uuid4())
|
551
|
+
file: Any
|
552
|
+
if not filesha1:
|
553
|
+
if filesize == 0:
|
554
|
+
filesha1 = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"
|
555
|
+
else:
|
556
|
+
if is_path:
|
557
|
+
if async_:
|
558
|
+
from aiofile import async_open
|
559
|
+
async def request():
|
560
|
+
async with async_open(path, "rb") as file:
|
561
|
+
return await file_digest_async(file, "sha1") # type: ignore
|
562
|
+
filesize, filesha1_obj = yield request
|
563
|
+
else:
|
564
|
+
with open(path, "rb") as file:
|
565
|
+
filesize, filesha1_obj = file_digest(file, "sha1")
|
566
|
+
else:
|
567
|
+
if async_:
|
568
|
+
from httpfile import AsyncHttpxFileReader
|
569
|
+
async def request():
|
570
|
+
file = await AsyncHttpxFileReader.new(path, headers={"user-agent": ""})
|
571
|
+
async with file:
|
572
|
+
return await file_digest_async(file, "sha1")
|
573
|
+
filesize, filesha1_obj = yield request
|
574
|
+
else:
|
575
|
+
from httpfile import HTTPFileReader
|
576
|
+
with HTTPFileReader(path, headers={"user-agent": ""}) as file:
|
577
|
+
filesize, filesha1_obj = file_digest(file, "sha1")
|
578
|
+
filesha1 = filesha1_obj.hexdigest()
|
579
|
+
if filesize < 0:
|
580
|
+
if is_path:
|
581
|
+
filesize = stat(path).st_size
|
582
|
+
else:
|
583
|
+
if async_:
|
584
|
+
file = yield to_thread(urlopen, path)
|
585
|
+
else:
|
586
|
+
file = urlopen(path)
|
587
|
+
try:
|
588
|
+
filesize = get_total_length(file) or 0
|
589
|
+
finally:
|
590
|
+
file.close()
|
591
|
+
if partsize <= 0:
|
592
|
+
partsize = determine_part_size(filesize)
|
593
|
+
read_range_bytes_or_hash: Callable
|
594
|
+
if async_:
|
595
|
+
async def read_range_bytes_or_hash(sign_check: str, /) -> bytes:
|
596
|
+
file: Any
|
597
|
+
if is_path:
|
598
|
+
from aiofile import async_open
|
599
|
+
start, end = map(int, sign_check.split("-"))
|
600
|
+
async with async_open(path, "rb") as file:
|
601
|
+
file.seek(start)
|
602
|
+
return await file.read(end - start + 1)
|
603
|
+
else:
|
604
|
+
file = await to_thread(
|
605
|
+
urlopen,
|
606
|
+
path,
|
607
|
+
headers={"Range": "bytes="+sign_check},
|
608
|
+
)
|
609
|
+
with file:
|
610
|
+
return await to_thread(file.read)
|
611
|
+
else:
|
612
|
+
def read_range_bytes_or_hash(sign_check: str, /) -> bytes:
|
613
|
+
if is_path:
|
614
|
+
start, end = map(int, sign_check.split("-"))
|
615
|
+
with open(path, "rb") as file:
|
616
|
+
file.seek(start)
|
617
|
+
return file.read(end - start + 1)
|
618
|
+
else:
|
619
|
+
with urlopen(path, headers={"Range": "bytes="+sign_check}) as file:
|
620
|
+
return file.read()
|
621
|
+
resp = yield client.upload_file_init(
|
622
|
+
filename=filename,
|
623
|
+
filesize=filesize,
|
624
|
+
filesha1=filesha1,
|
625
|
+
read_range_bytes_or_hash=read_range_bytes_or_hash, # type: ignore
|
626
|
+
pid=pid,
|
627
|
+
async_=async_, # type: ignore
|
628
|
+
**request_kwargs,
|
629
|
+
)
|
630
|
+
status = resp["status"]
|
631
|
+
statuscode = resp.get("statuscode", 0)
|
632
|
+
if status == 2 and statuscode == 0:
|
633
|
+
return resp
|
634
|
+
elif status == 1 and statuscode == 0:
|
635
|
+
bucket, object, callback = resp["bucket"], resp["object"], resp["callback"]
|
636
|
+
else:
|
637
|
+
raise OperationalError(errno.EINVAL, resp)
|
638
|
+
upload_data["bucket"] = bucket
|
639
|
+
upload_data["object"] = object
|
640
|
+
upload_data["callback"] = callback
|
641
|
+
upload_data["filename"] = filename
|
642
|
+
upload_data["filesha1"] = filesha1
|
643
|
+
upload_data["filesize"] = filesize
|
644
|
+
upload_data["partsize"] = partsize
|
645
|
+
upload_data["part_count"] = partsize and -(-filesize // partsize)
|
646
|
+
upload_data["pid"] = pid
|
647
|
+
else:
|
648
|
+
bucket = upload_data["bucket"]
|
649
|
+
object = upload_data["object"]
|
650
|
+
callback_var = loads(upload_data["callback"]["callback_var"])
|
651
|
+
resp = yield client.upload_resume(
|
652
|
+
{
|
653
|
+
"fileid": object,
|
654
|
+
"file_size": upload_data["filesize"],
|
655
|
+
"target": callback_var["x:target"],
|
656
|
+
"pick_code": callback_var["x:pick_code"],
|
657
|
+
},
|
658
|
+
async_=async_,
|
659
|
+
**request_kwargs,
|
660
|
+
)
|
661
|
+
check_response(resp)
|
662
|
+
url = f"http://{bucket}.{domain}/{object}"
|
663
|
+
token = client.upload_token
|
664
|
+
if upload_id := upload_data.get("upload_id"):
|
665
|
+
parts = yield collect(oss_multipart_part_iter(
|
666
|
+
client.request,
|
667
|
+
url,
|
668
|
+
bucket=bucket,
|
669
|
+
object=object,
|
670
|
+
upload_id=upload_id,
|
671
|
+
token=token,
|
672
|
+
async_=async_,
|
673
|
+
**request_kwargs,
|
674
|
+
))
|
675
|
+
if parts:
|
676
|
+
upload_data["part_number_next"] = len(parts) + (int(parts[-1]["Size"]) == upload_data["partsize"])
|
677
|
+
else:
|
678
|
+
upload_data["part_number_next"] = 1
|
679
|
+
else:
|
680
|
+
upload_data["upload_id"] = yield oss_multipart_upload_init(
|
681
|
+
client.request,
|
682
|
+
url,
|
683
|
+
bucket=bucket,
|
684
|
+
object=object,
|
685
|
+
token=token,
|
686
|
+
async_=async_,
|
687
|
+
**request_kwargs,
|
688
|
+
)
|
689
|
+
upload_data["part_number_next"] = 1
|
690
|
+
return upload_data
|
691
|
+
return run_gen_step(gen_step, async_=async_)
|
692
|
+
|
693
|
+
|
694
|
+
def multipart_upload_url(
|
695
|
+
client: str | P115Client | dict,
|
696
|
+
upload_data: dict,
|
697
|
+
part_number: int = 1,
|
698
|
+
domain: str = ALIYUN_DOMAIN,
|
699
|
+
) -> tuple[str, dict]:
|
700
|
+
"""用来获取 上传链接 和 请求头,然后文件需要你自己上传
|
701
|
+
|
702
|
+
:param client: 115 客户端或 cookies,或者是 token(令牌)
|
703
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
704
|
+
:param part_number: 需要上传的分块编号,须从 1 开始递增
|
705
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
706
|
+
|
707
|
+
:return: 上传链接 和 请求头 的 2 元组
|
708
|
+
"""
|
709
|
+
if isinstance(client, dict):
|
710
|
+
token = client
|
711
|
+
else:
|
712
|
+
if isinstance(client, str):
|
713
|
+
client = P115Client(client, check_for_relogin=True)
|
714
|
+
token = client.upload_token
|
715
|
+
return oss_multipart_upload_url(
|
716
|
+
bucket=upload_data["bucket"],
|
717
|
+
object=upload_data["object"],
|
718
|
+
upload_id=upload_data["upload_id"],
|
719
|
+
part_number=part_number,
|
720
|
+
token=token,
|
721
|
+
domain=domain,
|
722
|
+
)
|
723
|
+
|
724
|
+
|
725
|
+
@overload
|
726
|
+
def multipart_upload_complete(
|
727
|
+
client: str | P115Client,
|
728
|
+
upload_data: dict,
|
729
|
+
domain: str = ALIYUN_DOMAIN,
|
730
|
+
*,
|
731
|
+
async_: Literal[False] = False,
|
732
|
+
**request_kwargs,
|
733
|
+
) -> dict:
|
734
|
+
...
|
735
|
+
@overload
|
736
|
+
def multipart_upload_complete(
|
737
|
+
client: str | P115Client,
|
738
|
+
upload_data: dict,
|
739
|
+
domain: str = ALIYUN_DOMAIN,
|
740
|
+
*,
|
741
|
+
async_: Literal[True],
|
742
|
+
**request_kwargs,
|
743
|
+
) -> Coroutine[Any, Any, dict]:
|
744
|
+
...
|
745
|
+
def multipart_upload_complete(
|
746
|
+
client: str | P115Client,
|
747
|
+
upload_data: dict,
|
748
|
+
domain: str = ALIYUN_DOMAIN,
|
749
|
+
*,
|
750
|
+
async_: Literal[False, True] = False,
|
751
|
+
**request_kwargs,
|
752
|
+
) -> dict | Coroutine[Any, Any, dict]:
|
753
|
+
"""完成分块上传
|
754
|
+
|
755
|
+
:param client: 115 客户端或 cookies,或者是 token(令牌)
|
756
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
757
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
758
|
+
:param async_: 是否异步
|
759
|
+
:param request_kwargs: 其它请求参数
|
760
|
+
|
761
|
+
:return: 接口响应值
|
762
|
+
|
763
|
+
:example:
|
764
|
+
你可以构建自己的分块上传逻辑,下面是一个例子
|
765
|
+
|
766
|
+
.. code:: python
|
767
|
+
from pathlib import Path
|
768
|
+
from p115client import *
|
769
|
+
from p115client.tool import *
|
770
|
+
|
771
|
+
client = P115Client(Path("~/115-cookies.txt").expanduser())
|
772
|
+
#client.login_another_open(100195123, replace=True)
|
773
|
+
|
774
|
+
# TODO: 这里填一个文件的路径
|
775
|
+
path = "test.txt"
|
776
|
+
upload_data = multipart_upload_init(
|
777
|
+
client,
|
778
|
+
path,
|
779
|
+
pid = 0,
|
780
|
+
filename = "",
|
781
|
+
upload_data = None,
|
782
|
+
)
|
783
|
+
if "status" in upload_data:
|
784
|
+
resp = upload_data
|
785
|
+
else:
|
786
|
+
partsize = upload_data["partsize"]
|
787
|
+
part_number_next = upload_data["part_number_next"]
|
788
|
+
with open(path, "rb") as file:
|
789
|
+
if part_number_next > 1:
|
790
|
+
file.seek(partsize * (part_number_next - 1))
|
791
|
+
for part_number in range(part_number_next, upload_data["part_count"] + 1):
|
792
|
+
url, headers = multipart_upload_url(client, upload_data, part_number)
|
793
|
+
## TODO: 你可以自己改写上传的逻辑
|
794
|
+
## NOTE: 使用 urllib3
|
795
|
+
# from urllib3 import request
|
796
|
+
# request("PUT", url, body=file.read(partsize), headers=headers)
|
797
|
+
## NOTE: 使用 requests
|
798
|
+
# from requests import request
|
799
|
+
# request("PUT", url, data=file.read(partsize), headers=headers)
|
800
|
+
client.request(url=url, method="PUT", data=file.read(partsize), headers=headers, parse=False
|
801
|
+
resp = multipart_upload_complete(client, upload_data)
|
802
|
+
print(resp)
|
803
|
+
"""
|
804
|
+
bucket = upload_data["bucket"]
|
805
|
+
object = upload_data["object"]
|
806
|
+
upload_id = upload_data["upload_id"]
|
807
|
+
url = f"http://{bucket}.{domain}/{object}"
|
808
|
+
if isinstance(client, str):
|
809
|
+
client = P115Client(client, check_for_relogin=True)
|
810
|
+
token = client.upload_token
|
811
|
+
def gen_step():
|
812
|
+
parts = yield collect(oss_multipart_part_iter(
|
813
|
+
client.request,
|
814
|
+
url,
|
815
|
+
bucket=bucket,
|
816
|
+
object=object,
|
817
|
+
upload_id=upload_id,
|
818
|
+
token=token,
|
819
|
+
async_=async_,
|
820
|
+
**request_kwargs,
|
821
|
+
))
|
822
|
+
return oss_multipart_upload_complete(
|
823
|
+
client.request,
|
824
|
+
url,
|
825
|
+
bucket=bucket,
|
826
|
+
object=object,
|
827
|
+
upload_id=upload_id,
|
828
|
+
token=token,
|
829
|
+
callback=upload_data["callback"],
|
830
|
+
parts=parts,
|
831
|
+
async_=async_,
|
832
|
+
**request_kwargs,
|
833
|
+
)
|
834
|
+
return run_gen_step(gen_step, async_=async_)
|
835
|
+
|
@@ -4,7 +4,7 @@
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
5
5
|
__all__ = [
|
6
6
|
"get_status_code", "is_timeouterror", "posix_escape_name", "reduce_image_url_layers",
|
7
|
-
"share_extract_payload", "unescape_115_charref",
|
7
|
+
"share_extract_payload", "unescape_115_charref", "determine_part_size",
|
8
8
|
]
|
9
9
|
__doc__ = "这个模块提供了一些工具函数"
|
10
10
|
|
@@ -105,3 +105,25 @@ def unescape_115_charref(s: str, /) -> str:
|
|
105
105
|
"""
|
106
106
|
return CRE_115_CHARREF_sub(lambda a: chr(int(a[1])), s)
|
107
107
|
|
108
|
+
|
109
|
+
def determine_part_size(
|
110
|
+
size: int,
|
111
|
+
min_part_size: int = 1024 * 1024 * 10,
|
112
|
+
max_part_count: int = 10 ** 4,
|
113
|
+
) -> int:
|
114
|
+
"""确定分片上传(multipart upload)时的分片大小
|
115
|
+
|
116
|
+
:param size: 数据大小
|
117
|
+
:param min_part_size: 用户期望的分片大小
|
118
|
+
:param max_part_count: 最大的分片个数
|
119
|
+
|
120
|
+
:return: 分片大小
|
121
|
+
"""
|
122
|
+
if size <= min_part_size:
|
123
|
+
return size
|
124
|
+
n = -(-size // max_part_count)
|
125
|
+
part_size = min_part_size
|
126
|
+
while part_size < n:
|
127
|
+
part_size <<= 1
|
128
|
+
return part_size
|
129
|
+
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|