p115client 0.0.5.11.8.2__py3-none-any.whl → 0.0.5.11.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- p115client/_upload.py +52 -27
- p115client/client.py +61 -26
- p115client/tool/__init__.py +1 -0
- p115client/tool/auth.py +62 -0
- p115client/tool/upload.py +442 -5
- p115client/tool/util.py +23 -1
- {p115client-0.0.5.11.8.2.dist-info → p115client-0.0.5.11.9.1.dist-info}/METADATA +1 -1
- {p115client-0.0.5.11.8.2.dist-info → p115client-0.0.5.11.9.1.dist-info}/RECORD +10 -9
- {p115client-0.0.5.11.8.2.dist-info → p115client-0.0.5.11.9.1.dist-info}/LICENSE +0 -0
- {p115client-0.0.5.11.8.2.dist-info → p115client-0.0.5.11.9.1.dist-info}/WHEEL +0 -0
p115client/_upload.py
CHANGED
@@ -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
|
-
|
p115client/client.py
CHANGED
@@ -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: 全部(仅当前目录)
|
@@ -5188,6 +5187,7 @@ class P115OpenClient(ClientRequestMixin):
|
|
5188
5187
|
async_=async_, # type: ignore
|
5189
5188
|
**request_kwargs,
|
5190
5189
|
)
|
5190
|
+
check_response(resp)
|
5191
5191
|
data = resp["data"]
|
5192
5192
|
match data["status"]:
|
5193
5193
|
case 2:
|
@@ -6173,6 +6173,8 @@ class P115Client(P115OpenClient):
|
|
6173
6173
|
|
6174
6174
|
但一个设备的新登录者,并不总是意味着把较早的登录者下线,一般需要触发某个检查机制后,才会把同一设备下除最近一次登录外的所有 cookies 失效
|
6175
6175
|
|
6176
|
+
所以你可以用一个设备的 cookies 专门用于扫码登录,获取另一个设备的 cookies 执行网盘操作,第 2 个 cookies 失效了,则用第 1 个 cookies 扫码,如此可避免单个 cookies 失效后,不能自动获取新的
|
6177
|
+
|
6176
6178
|
:param app: 要登录的 app,如果为 None,则用当前登录设备,如果无当前登录设备,则报错
|
6177
6179
|
:param replace: 替换某个 client 对象的 cookie
|
6178
6180
|
|
@@ -6860,6 +6862,7 @@ class P115Client(P115OpenClient):
|
|
6860
6862
|
self,
|
6861
6863
|
payload: dict,
|
6862
6864
|
/,
|
6865
|
+
app: str = "web",
|
6863
6866
|
base_url: bool | str | Callable[[], str] = False,
|
6864
6867
|
*,
|
6865
6868
|
async_: Literal[False] = False,
|
@@ -6871,6 +6874,7 @@ class P115Client(P115OpenClient):
|
|
6871
6874
|
self,
|
6872
6875
|
payload: dict,
|
6873
6876
|
/,
|
6877
|
+
app: str = "web",
|
6874
6878
|
base_url: bool | str | Callable[[], str] = False,
|
6875
6879
|
*,
|
6876
6880
|
async_: Literal[True],
|
@@ -6881,6 +6885,7 @@ class P115Client(P115OpenClient):
|
|
6881
6885
|
self,
|
6882
6886
|
payload: dict,
|
6883
6887
|
/,
|
6888
|
+
app: str = "web",
|
6884
6889
|
base_url: bool | str | Callable[[], str] = False,
|
6885
6890
|
*,
|
6886
6891
|
async_: Literal[False, True] = False,
|
@@ -6895,7 +6900,7 @@ class P115Client(P115OpenClient):
|
|
6895
6900
|
- aid: int | str 💡 助愿的 id
|
6896
6901
|
- to_cid: int = <default> 💡 助愿中的分享链接转存到你的网盘中目录的 id
|
6897
6902
|
"""
|
6898
|
-
api = complete_api("/api/1.0/
|
6903
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/adopt", "act", base_url=base_url)
|
6899
6904
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6900
6905
|
|
6901
6906
|
@overload
|
@@ -6903,6 +6908,7 @@ class P115Client(P115OpenClient):
|
|
6903
6908
|
self,
|
6904
6909
|
payload: dict,
|
6905
6910
|
/,
|
6911
|
+
app: str = "web",
|
6906
6912
|
base_url: bool | str | Callable[[], str] = False,
|
6907
6913
|
*,
|
6908
6914
|
async_: Literal[False] = False,
|
@@ -6914,6 +6920,7 @@ class P115Client(P115OpenClient):
|
|
6914
6920
|
self,
|
6915
6921
|
payload: dict,
|
6916
6922
|
/,
|
6923
|
+
app: str = "web",
|
6917
6924
|
base_url: bool | str | Callable[[], str] = False,
|
6918
6925
|
*,
|
6919
6926
|
async_: Literal[True],
|
@@ -6924,6 +6931,7 @@ class P115Client(P115OpenClient):
|
|
6924
6931
|
self,
|
6925
6932
|
payload: dict,
|
6926
6933
|
/,
|
6934
|
+
app: str = "web",
|
6927
6935
|
base_url: bool | str | Callable[[], str] = False,
|
6928
6936
|
*,
|
6929
6937
|
async_: Literal[False, True] = False,
|
@@ -6939,7 +6947,7 @@ class P115Client(P115OpenClient):
|
|
6939
6947
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
6940
6948
|
- file_ids: int | str = <default> 💡 文件在你的网盘的 id,多个用逗号 "," 隔开
|
6941
6949
|
"""
|
6942
|
-
api = complete_api("/api/1.0/
|
6950
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/aid_desire", "act", base_url=base_url)
|
6943
6951
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6944
6952
|
|
6945
6953
|
@overload
|
@@ -6947,6 +6955,7 @@ class P115Client(P115OpenClient):
|
|
6947
6955
|
self,
|
6948
6956
|
payload: int | str | dict,
|
6949
6957
|
/,
|
6958
|
+
app: str = "web",
|
6950
6959
|
base_url: bool | str | Callable[[], str] = False,
|
6951
6960
|
*,
|
6952
6961
|
async_: Literal[False] = False,
|
@@ -6958,6 +6967,7 @@ class P115Client(P115OpenClient):
|
|
6958
6967
|
self,
|
6959
6968
|
payload: int | str | dict,
|
6960
6969
|
/,
|
6970
|
+
app: str = "web",
|
6961
6971
|
base_url: bool | str | Callable[[], str] = False,
|
6962
6972
|
*,
|
6963
6973
|
async_: Literal[True],
|
@@ -6968,6 +6978,7 @@ class P115Client(P115OpenClient):
|
|
6968
6978
|
self,
|
6969
6979
|
payload: int | str | dict,
|
6970
6980
|
/,
|
6981
|
+
app: str = "web",
|
6971
6982
|
base_url: bool | str | Callable[[], str] = False,
|
6972
6983
|
*,
|
6973
6984
|
async_: Literal[False, True] = False,
|
@@ -6980,7 +6991,7 @@ class P115Client(P115OpenClient):
|
|
6980
6991
|
:payload:
|
6981
6992
|
- ids: int | str 💡 助愿的 id,多个用逗号 "," 隔开
|
6982
6993
|
"""
|
6983
|
-
api = complete_api("/api/1.0/
|
6994
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/del_aid_desire", "act", base_url=base_url)
|
6984
6995
|
if isinstance(payload, (int, str)):
|
6985
6996
|
payload = {"ids": payload}
|
6986
6997
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -6990,6 +7001,7 @@ class P115Client(P115OpenClient):
|
|
6990
7001
|
self,
|
6991
7002
|
payload: str | dict,
|
6992
7003
|
/,
|
7004
|
+
app: str = "web",
|
6993
7005
|
base_url: bool | str | Callable[[], str] = False,
|
6994
7006
|
*,
|
6995
7007
|
async_: Literal[False] = False,
|
@@ -7001,6 +7013,7 @@ class P115Client(P115OpenClient):
|
|
7001
7013
|
self,
|
7002
7014
|
payload: str | dict,
|
7003
7015
|
/,
|
7016
|
+
app: str = "web",
|
7004
7017
|
base_url: bool | str | Callable[[], str] = False,
|
7005
7018
|
*,
|
7006
7019
|
async_: Literal[True],
|
@@ -7011,6 +7024,7 @@ class P115Client(P115OpenClient):
|
|
7011
7024
|
self,
|
7012
7025
|
payload: str | dict,
|
7013
7026
|
/,
|
7027
|
+
app: str = "web",
|
7014
7028
|
base_url: bool | str | Callable[[], str] = False,
|
7015
7029
|
*,
|
7016
7030
|
async_: Literal[False, True] = False,
|
@@ -7027,7 +7041,7 @@ class P115Client(P115OpenClient):
|
|
7027
7041
|
- limit: int = 10 💡 分页大小
|
7028
7042
|
- sort: int | str = <default> 💡 排序
|
7029
7043
|
"""
|
7030
|
-
api = complete_api("/api/1.0/
|
7044
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/desire_aid_list", "act", base_url=base_url)
|
7031
7045
|
if isinstance(payload, str):
|
7032
7046
|
payload = {"start": 0, "page": 1, "limit": 10, "id": payload}
|
7033
7047
|
else:
|
@@ -7038,6 +7052,7 @@ class P115Client(P115OpenClient):
|
|
7038
7052
|
def act_xys_get_act_info(
|
7039
7053
|
self,
|
7040
7054
|
/,
|
7055
|
+
app: str = "web",
|
7041
7056
|
base_url: bool | str | Callable[[], str] = False,
|
7042
7057
|
*,
|
7043
7058
|
async_: Literal[False] = False,
|
@@ -7048,6 +7063,7 @@ class P115Client(P115OpenClient):
|
|
7048
7063
|
def act_xys_get_act_info(
|
7049
7064
|
self,
|
7050
7065
|
/,
|
7066
|
+
app: str = "web",
|
7051
7067
|
base_url: bool | str | Callable[[], str] = False,
|
7052
7068
|
*,
|
7053
7069
|
async_: Literal[True],
|
@@ -7057,6 +7073,7 @@ class P115Client(P115OpenClient):
|
|
7057
7073
|
def act_xys_get_act_info(
|
7058
7074
|
self,
|
7059
7075
|
/,
|
7076
|
+
app: str = "web",
|
7060
7077
|
base_url: bool | str | Callable[[], str] = False,
|
7061
7078
|
*,
|
7062
7079
|
async_: Literal[False, True] = False,
|
@@ -7066,7 +7083,7 @@ class P115Client(P115OpenClient):
|
|
7066
7083
|
|
7067
7084
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/get_act_info
|
7068
7085
|
"""
|
7069
|
-
api = complete_api("/api/1.0/
|
7086
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/get_act_info", "act", base_url=base_url)
|
7070
7087
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7071
7088
|
|
7072
7089
|
@overload
|
@@ -7074,6 +7091,7 @@ class P115Client(P115OpenClient):
|
|
7074
7091
|
self,
|
7075
7092
|
payload: str | dict,
|
7076
7093
|
/,
|
7094
|
+
app: str = "web",
|
7077
7095
|
base_url: bool | str | Callable[[], str] = False,
|
7078
7096
|
*,
|
7079
7097
|
async_: Literal[False] = False,
|
@@ -7085,6 +7103,7 @@ class P115Client(P115OpenClient):
|
|
7085
7103
|
self,
|
7086
7104
|
payload: str | dict,
|
7087
7105
|
/,
|
7106
|
+
app: str = "web",
|
7088
7107
|
base_url: bool | str | Callable[[], str] = False,
|
7089
7108
|
*,
|
7090
7109
|
async_: Literal[True],
|
@@ -7095,6 +7114,7 @@ class P115Client(P115OpenClient):
|
|
7095
7114
|
self,
|
7096
7115
|
payload: str | dict,
|
7097
7116
|
/,
|
7117
|
+
app: str = "web",
|
7098
7118
|
base_url: bool | str | Callable[[], str] = False,
|
7099
7119
|
*,
|
7100
7120
|
async_: Literal[False, True] = False,
|
@@ -7107,7 +7127,7 @@ class P115Client(P115OpenClient):
|
|
7107
7127
|
:payload:
|
7108
7128
|
- id: str 💡 许愿的 id
|
7109
7129
|
"""
|
7110
|
-
api = complete_api("/api/1.0/
|
7130
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/get_desire_info", "act", base_url=base_url)
|
7111
7131
|
if isinstance(payload, str):
|
7112
7132
|
payload = {"id": payload}
|
7113
7133
|
return self.request(url=api, params=payload, async_=async_, **request_kwargs)
|
@@ -7116,6 +7136,7 @@ class P115Client(P115OpenClient):
|
|
7116
7136
|
def act_xys_home_list(
|
7117
7137
|
self,
|
7118
7138
|
/,
|
7139
|
+
app: str = "web",
|
7119
7140
|
base_url: bool | str | Callable[[], str] = False,
|
7120
7141
|
*,
|
7121
7142
|
async_: Literal[False] = False,
|
@@ -7126,6 +7147,7 @@ class P115Client(P115OpenClient):
|
|
7126
7147
|
def act_xys_home_list(
|
7127
7148
|
self,
|
7128
7149
|
/,
|
7150
|
+
app: str = "web",
|
7129
7151
|
base_url: bool | str | Callable[[], str] = False,
|
7130
7152
|
*,
|
7131
7153
|
async_: Literal[True],
|
@@ -7135,6 +7157,7 @@ class P115Client(P115OpenClient):
|
|
7135
7157
|
def act_xys_home_list(
|
7136
7158
|
self,
|
7137
7159
|
/,
|
7160
|
+
app: str = "web",
|
7138
7161
|
base_url: bool | str | Callable[[], str] = False,
|
7139
7162
|
*,
|
7140
7163
|
async_: Literal[False, True] = False,
|
@@ -7144,7 +7167,7 @@ class P115Client(P115OpenClient):
|
|
7144
7167
|
|
7145
7168
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/home_list
|
7146
7169
|
"""
|
7147
|
-
api = complete_api("/api/1.0/
|
7170
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/home_list", "act", base_url=base_url)
|
7148
7171
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7149
7172
|
|
7150
7173
|
@overload
|
@@ -7152,6 +7175,7 @@ class P115Client(P115OpenClient):
|
|
7152
7175
|
self,
|
7153
7176
|
payload: int | str | dict = 0,
|
7154
7177
|
/,
|
7178
|
+
app: str = "web",
|
7155
7179
|
base_url: bool | str | Callable[[], str] = False,
|
7156
7180
|
*,
|
7157
7181
|
async_: Literal[False] = False,
|
@@ -7163,6 +7187,7 @@ class P115Client(P115OpenClient):
|
|
7163
7187
|
self,
|
7164
7188
|
payload: int | str | dict = 0,
|
7165
7189
|
/,
|
7190
|
+
app: str = "web",
|
7166
7191
|
base_url: bool | str | Callable[[], str] = False,
|
7167
7192
|
*,
|
7168
7193
|
async_: Literal[True],
|
@@ -7173,6 +7198,7 @@ class P115Client(P115OpenClient):
|
|
7173
7198
|
self,
|
7174
7199
|
payload: int | str | dict = 0,
|
7175
7200
|
/,
|
7201
|
+
app: str = "web",
|
7176
7202
|
base_url: bool | str | Callable[[], str] = False,
|
7177
7203
|
*,
|
7178
7204
|
async_: Literal[False, True] = False,
|
@@ -7193,7 +7219,7 @@ class P115Client(P115OpenClient):
|
|
7193
7219
|
- page: int = 1 💡 第几页
|
7194
7220
|
- limit: int = 10 💡 分页大小
|
7195
7221
|
"""
|
7196
|
-
api = complete_api("/api/1.0/
|
7222
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/my_aid_desire", "act", base_url=base_url)
|
7197
7223
|
if isinstance(payload, (int, str)):
|
7198
7224
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7199
7225
|
else:
|
@@ -7205,6 +7231,7 @@ class P115Client(P115OpenClient):
|
|
7205
7231
|
self,
|
7206
7232
|
payload: int | str | dict = 0,
|
7207
7233
|
/,
|
7234
|
+
app: str = "web",
|
7208
7235
|
base_url: bool | str | Callable[[], str] = False,
|
7209
7236
|
*,
|
7210
7237
|
async_: Literal[False] = False,
|
@@ -7216,6 +7243,7 @@ class P115Client(P115OpenClient):
|
|
7216
7243
|
self,
|
7217
7244
|
payload: int | str | dict = 0,
|
7218
7245
|
/,
|
7246
|
+
app: str = "web",
|
7219
7247
|
base_url: bool | str | Callable[[], str] = False,
|
7220
7248
|
*,
|
7221
7249
|
async_: Literal[True],
|
@@ -7226,6 +7254,7 @@ class P115Client(P115OpenClient):
|
|
7226
7254
|
self,
|
7227
7255
|
payload: int | str | dict = 0,
|
7228
7256
|
/,
|
7257
|
+
app: str = "web",
|
7229
7258
|
base_url: bool | str | Callable[[], str] = False,
|
7230
7259
|
*,
|
7231
7260
|
async_: Literal[False, True] = False,
|
@@ -7246,7 +7275,7 @@ class P115Client(P115OpenClient):
|
|
7246
7275
|
- page: int = 1 💡 第几页
|
7247
7276
|
- limit: int = 10 💡 分页大小
|
7248
7277
|
"""
|
7249
|
-
api = complete_api("/api/1.0/
|
7278
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/my_desire", "act", base_url=base_url)
|
7250
7279
|
if isinstance(payload, (int, str)):
|
7251
7280
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7252
7281
|
else:
|
@@ -7258,6 +7287,7 @@ class P115Client(P115OpenClient):
|
|
7258
7287
|
self,
|
7259
7288
|
payload: str | dict,
|
7260
7289
|
/,
|
7290
|
+
app: str = "web",
|
7261
7291
|
base_url: bool | str | Callable[[], str] = False,
|
7262
7292
|
*,
|
7263
7293
|
async_: Literal[False] = False,
|
@@ -7269,6 +7299,7 @@ class P115Client(P115OpenClient):
|
|
7269
7299
|
self,
|
7270
7300
|
payload: str | dict,
|
7271
7301
|
/,
|
7302
|
+
app: str = "web",
|
7272
7303
|
base_url: bool | str | Callable[[], str] = False,
|
7273
7304
|
*,
|
7274
7305
|
async_: Literal[True],
|
@@ -7279,6 +7310,7 @@ class P115Client(P115OpenClient):
|
|
7279
7310
|
self,
|
7280
7311
|
payload: str | dict,
|
7281
7312
|
/,
|
7313
|
+
app: str = "web",
|
7282
7314
|
base_url: bool | str | Callable[[], str] = False,
|
7283
7315
|
*,
|
7284
7316
|
async_: Literal[False, True] = False,
|
@@ -7293,7 +7325,7 @@ class P115Client(P115OpenClient):
|
|
7293
7325
|
- rewardSpace: int = 5 💡 奖励容量,单位是 GB
|
7294
7326
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
7295
7327
|
"""
|
7296
|
-
api = complete_api("/api/1.0/
|
7328
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/wish", "act", base_url=base_url)
|
7297
7329
|
if isinstance(payload, str):
|
7298
7330
|
payload = {"rewardSpace": 5, "content": payload}
|
7299
7331
|
else:
|
@@ -7305,6 +7337,7 @@ class P115Client(P115OpenClient):
|
|
7305
7337
|
self,
|
7306
7338
|
payload: str | dict,
|
7307
7339
|
/,
|
7340
|
+
app: str = "web",
|
7308
7341
|
base_url: bool | str | Callable[[], str] = False,
|
7309
7342
|
*,
|
7310
7343
|
async_: Literal[False] = False,
|
@@ -7316,6 +7349,7 @@ class P115Client(P115OpenClient):
|
|
7316
7349
|
self,
|
7317
7350
|
payload: str | dict,
|
7318
7351
|
/,
|
7352
|
+
app: str = "web",
|
7319
7353
|
base_url: bool | str | Callable[[], str] = False,
|
7320
7354
|
*,
|
7321
7355
|
async_: Literal[True],
|
@@ -7326,6 +7360,7 @@ class P115Client(P115OpenClient):
|
|
7326
7360
|
self,
|
7327
7361
|
payload: str | dict,
|
7328
7362
|
/,
|
7363
|
+
app: str = "web",
|
7329
7364
|
base_url: bool | str | Callable[[], str] = False,
|
7330
7365
|
*,
|
7331
7366
|
async_: Literal[False, True] = False,
|
@@ -7338,7 +7373,7 @@ class P115Client(P115OpenClient):
|
|
7338
7373
|
:payload:
|
7339
7374
|
- ids: str 💡 许愿的 id,多个用逗号 "," 隔开
|
7340
7375
|
"""
|
7341
|
-
api = complete_api("/api/1.0/
|
7376
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/del_wish", "act", base_url=base_url)
|
7342
7377
|
if isinstance(payload, str):
|
7343
7378
|
payload = {"ids": payload}
|
7344
7379
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -7954,7 +7989,6 @@ class P115Client(P115OpenClient):
|
|
7954
7989
|
payload = {"pick_code": payload}
|
7955
7990
|
else:
|
7956
7991
|
payload = {"pick_code": payload["pickcode"]}
|
7957
|
-
request_headers = request_kwargs.get("headers")
|
7958
7992
|
headers = request_kwargs.get("headers")
|
7959
7993
|
if headers:
|
7960
7994
|
if isinstance(headers, Mapping):
|
@@ -8215,7 +8249,6 @@ class P115Client(P115OpenClient):
|
|
8215
8249
|
- full_name: str
|
8216
8250
|
"""
|
8217
8251
|
api = complete_proapi("/2.0/ufile/extract_down_file", base_url, app)
|
8218
|
-
request_headers = request_kwargs.get("headers")
|
8219
8252
|
headers = request_kwargs.get("headers")
|
8220
8253
|
if headers:
|
8221
8254
|
if isinstance(headers, Mapping):
|
@@ -8271,7 +8304,6 @@ class P115Client(P115OpenClient):
|
|
8271
8304
|
- full_name: str
|
8272
8305
|
"""
|
8273
8306
|
api = complete_webapi("/files/extract_down_file", base_url=base_url)
|
8274
|
-
request_headers = request_kwargs.get("headers")
|
8275
8307
|
headers = request_kwargs.get("headers")
|
8276
8308
|
if headers:
|
8277
8309
|
if isinstance(headers, Mapping):
|
@@ -10515,7 +10547,7 @@ class P115Client(P115OpenClient):
|
|
10515
10547
|
- fields: str = <default>
|
10516
10548
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10517
10549
|
- format: str = "json" 💡 返回格式,默认即可
|
10518
|
-
- hide_data: str = <default>
|
10550
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10519
10551
|
- is_q: 0 | 1 = <default>
|
10520
10552
|
- is_share: 0 | 1 = <default>
|
10521
10553
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10645,7 +10677,7 @@ class P115Client(P115OpenClient):
|
|
10645
10677
|
- fields: str = <default>
|
10646
10678
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10647
10679
|
- format: str = "json" 💡 返回格式,默认即可
|
10648
|
-
- hide_data: str = <default>
|
10680
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10649
10681
|
- is_q: 0 | 1 = <default>
|
10650
10682
|
- is_share: 0 | 1 = <default>
|
10651
10683
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10774,7 +10806,7 @@ class P115Client(P115OpenClient):
|
|
10774
10806
|
- fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
|
10775
10807
|
- fields: str = <default>
|
10776
10808
|
- format: str = "json" 💡 返回格式,默认即可
|
10777
|
-
- hide_data: str = <default>
|
10809
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10778
10810
|
- is_asc: 0 | 1 = <default>
|
10779
10811
|
- is_q: 0 | 1 = <default>
|
10780
10812
|
- is_share: 0 | 1 = <default>
|
@@ -11729,7 +11761,7 @@ class P115Client(P115OpenClient):
|
|
11729
11761
|
:payload:
|
11730
11762
|
- offset: int = 0
|
11731
11763
|
- limit: int = 1150
|
11732
|
-
- played_end: 0 | 1 = <default>
|
11764
|
+
- played_end: 0 | 1 = <default> 💡 是否已经播放完
|
11733
11765
|
- type: int = <default> 💡 类型(??表示还未搞清楚),多个用逗号 "," 隔开
|
11734
11766
|
|
11735
11767
|
- 全部: 0
|
@@ -14604,8 +14636,8 @@ class P115Client(P115OpenClient):
|
|
14604
14636
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14605
14637
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14606
14638
|
- source: str = <default>
|
14607
|
-
- star: 0 | 1 = <default>
|
14608
|
-
- suffix: str = <default>
|
14639
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14640
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14609
14641
|
- type: int = <default> 💡 文件类型
|
14610
14642
|
|
14611
14643
|
- 0: 全部(仅当前目录)
|
@@ -14701,8 +14733,8 @@ class P115Client(P115OpenClient):
|
|
14701
14733
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14702
14734
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14703
14735
|
- source: str = <default>
|
14704
|
-
- star: 0 | 1 = <default>
|
14705
|
-
- suffix: str = <default>
|
14736
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14737
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14706
14738
|
- type: int = <default> 💡 文件类型
|
14707
14739
|
|
14708
14740
|
- 0: 全部(仅当前目录)
|
@@ -21144,6 +21176,9 @@ class P115Client(P115OpenClient):
|
|
21144
21176
|
)
|
21145
21177
|
return run_gen_step(gen_step, may_call=False, async_=async_)
|
21146
21178
|
|
21179
|
+
# TODO: 分块上传时,允许一定次数的重试
|
21180
|
+
# TODO: 不妨单独为分块上传做一个封装
|
21181
|
+
# TODO: 减少参数,简化使用方法
|
21147
21182
|
@overload # type: ignore
|
21148
21183
|
def upload_file(
|
21149
21184
|
self,
|
p115client/tool/__init__.py
CHANGED
p115client/tool/auth.py
ADDED
@@ -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
|
+
|
p115client/tool/upload.py
CHANGED
@@ -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
|
15
|
-
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
|
31
|
+
from p115client import check_response, normalize_attr_simple, P115Client, P115OpenClient
|
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,413 @@ 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 | P115OpenClient,
|
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
|
+
use_open_api: bool = False,
|
474
|
+
*,
|
475
|
+
async_: Literal[False] = False,
|
476
|
+
**request_kwargs,
|
477
|
+
) -> dict:
|
478
|
+
...
|
479
|
+
@overload
|
480
|
+
def multipart_upload_init(
|
481
|
+
client: str | P115Client | P115OpenClient,
|
482
|
+
path: str | PathLike | URL | SupportsGeturl,
|
483
|
+
pid: int = 0,
|
484
|
+
filename: str = "",
|
485
|
+
filesize: int = -1,
|
486
|
+
filesha1: str = "",
|
487
|
+
partsize: int = -1,
|
488
|
+
upload_data: None | dict = None,
|
489
|
+
domain: str = ALIYUN_DOMAIN,
|
490
|
+
use_open_api: bool = False,
|
491
|
+
*,
|
492
|
+
async_: Literal[True],
|
493
|
+
**request_kwargs,
|
494
|
+
) -> Coroutine[Any, Any, dict]:
|
495
|
+
...
|
496
|
+
def multipart_upload_init(
|
497
|
+
client: str | P115Client | P115OpenClient,
|
498
|
+
path: str | PathLike | URL | SupportsGeturl,
|
499
|
+
pid: int = 0,
|
500
|
+
filename: str = "",
|
501
|
+
filesize: int = -1,
|
502
|
+
filesha1: str = "",
|
503
|
+
partsize: int = -1,
|
504
|
+
upload_data: None | dict = None,
|
505
|
+
domain: str = ALIYUN_DOMAIN,
|
506
|
+
use_open_api: bool = False,
|
507
|
+
*,
|
508
|
+
async_: Literal[False, True] = False,
|
509
|
+
**request_kwargs,
|
510
|
+
) -> dict | Coroutine[Any, Any, dict]:
|
511
|
+
"""准备分块上传,获取必要信息
|
512
|
+
|
513
|
+
:param client: 115 客户端或 cookies
|
514
|
+
:param path: 路径 或 链接(仅支持 GET 请求,http(s)协议)
|
515
|
+
:param pid: 上传文件到此目录的 id
|
516
|
+
:param filename: 文件名,若为空则自动确定
|
517
|
+
:param filesize: 文件大小,若为负数则自动计算
|
518
|
+
:param filesha1: 文件的 sha1 摘要,若为空则自动计算
|
519
|
+
:param partsize: 分块大小,若不为正数则自动确定
|
520
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
521
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
522
|
+
:param use_open_api: 是否使用 open 接口,如果本身就是 P115OpenClient (而不是其子类)的实例,此值强制为 True
|
523
|
+
:param async_: 是否异步
|
524
|
+
:param request_kwargs: 其它请求参数
|
525
|
+
|
526
|
+
:return: 如果秒传成功,则返回响应信息(有 "status" 字段),否则返回上传配置信息(可用于断点续传)
|
527
|
+
"""
|
528
|
+
if not domain:
|
529
|
+
domain = ALIYUN_DOMAIN
|
530
|
+
if isinstance(client, str):
|
531
|
+
client = P115Client(client, check_for_relogin=True)
|
532
|
+
if type(client) is P115OpenClient:
|
533
|
+
use_open_api = True
|
534
|
+
if use_open_api:
|
535
|
+
upload_file_init = client.upload_file_init_open
|
536
|
+
upload_resume = client.upload_resume_open
|
537
|
+
else:
|
538
|
+
upload_file_init = client.upload_file_init
|
539
|
+
upload_resume = client.upload_resume
|
540
|
+
def gen_step():
|
541
|
+
nonlocal upload_data, path, filename, filesha1, filesize, partsize
|
542
|
+
if upload_data is None:
|
543
|
+
upload_data = {}
|
544
|
+
if isinstance(path, str):
|
545
|
+
is_path = not path.startswith(("http://", "https://"))
|
546
|
+
elif isinstance(path, URL):
|
547
|
+
path = str(path)
|
548
|
+
is_path = False
|
549
|
+
elif isinstance(path, SupportsGeturl):
|
550
|
+
path = path.geturl()
|
551
|
+
is_path = False
|
552
|
+
else:
|
553
|
+
path = fsdecode(path)
|
554
|
+
is_path = True
|
555
|
+
path = cast(str, path)
|
556
|
+
if not filename:
|
557
|
+
if is_path:
|
558
|
+
from os.path import basename
|
559
|
+
filename = basename(path)
|
560
|
+
else:
|
561
|
+
from posixpath import basename
|
562
|
+
filename = basename(unquote(urlsplit(path).path))
|
563
|
+
if not filename:
|
564
|
+
filename = str(uuid4())
|
565
|
+
file: Any
|
566
|
+
if not filesha1:
|
567
|
+
if filesize == 0:
|
568
|
+
filesha1 = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"
|
569
|
+
else:
|
570
|
+
if is_path:
|
571
|
+
if async_:
|
572
|
+
from aiofile import async_open
|
573
|
+
async def request():
|
574
|
+
async with async_open(path, "rb") as file:
|
575
|
+
return await file_digest_async(file, "sha1") # type: ignore
|
576
|
+
filesize, filesha1_obj = yield request
|
577
|
+
else:
|
578
|
+
with open(path, "rb") as file:
|
579
|
+
filesize, filesha1_obj = file_digest(file, "sha1")
|
580
|
+
else:
|
581
|
+
if async_:
|
582
|
+
from httpfile import AsyncHttpxFileReader
|
583
|
+
async def request():
|
584
|
+
file = await AsyncHttpxFileReader.new(path, headers={"user-agent": ""})
|
585
|
+
async with file:
|
586
|
+
return await file_digest_async(file, "sha1")
|
587
|
+
filesize, filesha1_obj = yield request
|
588
|
+
else:
|
589
|
+
from httpfile import HTTPFileReader
|
590
|
+
with HTTPFileReader(path, headers={"user-agent": ""}) as file:
|
591
|
+
filesize, filesha1_obj = file_digest(file, "sha1")
|
592
|
+
filesha1 = filesha1_obj.hexdigest()
|
593
|
+
if filesize < 0:
|
594
|
+
if is_path:
|
595
|
+
filesize = stat(path).st_size
|
596
|
+
else:
|
597
|
+
if async_:
|
598
|
+
file = yield to_thread(urlopen, path)
|
599
|
+
else:
|
600
|
+
file = urlopen(path)
|
601
|
+
try:
|
602
|
+
filesize = get_total_length(file) or 0
|
603
|
+
finally:
|
604
|
+
file.close()
|
605
|
+
if partsize <= 0:
|
606
|
+
partsize = determine_part_size(filesize)
|
607
|
+
read_range_bytes_or_hash: Callable
|
608
|
+
if async_:
|
609
|
+
async def read_range_bytes_or_hash(sign_check: str, /) -> bytes:
|
610
|
+
file: Any
|
611
|
+
if is_path:
|
612
|
+
from aiofile import async_open
|
613
|
+
start, end = map(int, sign_check.split("-"))
|
614
|
+
async with async_open(path, "rb") as file:
|
615
|
+
file.seek(start)
|
616
|
+
return await file.read(end - start + 1)
|
617
|
+
else:
|
618
|
+
file = await to_thread(
|
619
|
+
urlopen,
|
620
|
+
path,
|
621
|
+
headers={"Range": "bytes="+sign_check},
|
622
|
+
)
|
623
|
+
with file:
|
624
|
+
return await to_thread(file.read)
|
625
|
+
else:
|
626
|
+
def read_range_bytes_or_hash(sign_check: str, /) -> bytes:
|
627
|
+
if is_path:
|
628
|
+
start, end = map(int, sign_check.split("-"))
|
629
|
+
with open(path, "rb") as file:
|
630
|
+
file.seek(start)
|
631
|
+
return file.read(end - start + 1)
|
632
|
+
else:
|
633
|
+
with urlopen(path, headers={"Range": "bytes="+sign_check}) as file:
|
634
|
+
return file.read()
|
635
|
+
resp = yield upload_file_init(
|
636
|
+
filename=filename,
|
637
|
+
filesize=filesize,
|
638
|
+
filesha1=filesha1,
|
639
|
+
read_range_bytes_or_hash=read_range_bytes_or_hash, # type: ignore
|
640
|
+
pid=pid,
|
641
|
+
async_=async_, # type: ignore
|
642
|
+
**request_kwargs,
|
643
|
+
)
|
644
|
+
if use_open_api:
|
645
|
+
check_response(resp)
|
646
|
+
data = resp["data"]
|
647
|
+
match data["status"]:
|
648
|
+
case 2:
|
649
|
+
return resp
|
650
|
+
case 1:
|
651
|
+
bucket, object, callback = data["bucket"], data["object"], data["callback"]
|
652
|
+
case _:
|
653
|
+
raise OperationalError(errno.EINVAL, resp)
|
654
|
+
else:
|
655
|
+
status = resp["status"]
|
656
|
+
statuscode = resp.get("statuscode", 0)
|
657
|
+
if status == 2 and statuscode == 0:
|
658
|
+
return resp
|
659
|
+
elif status == 1 and statuscode == 0:
|
660
|
+
bucket, object, callback = resp["bucket"], resp["object"], resp["callback"]
|
661
|
+
else:
|
662
|
+
raise OperationalError(errno.EINVAL, resp)
|
663
|
+
upload_data["bucket"] = bucket
|
664
|
+
upload_data["object"] = object
|
665
|
+
upload_data["callback"] = callback
|
666
|
+
upload_data["filename"] = filename
|
667
|
+
upload_data["filesha1"] = filesha1
|
668
|
+
upload_data["filesize"] = filesize
|
669
|
+
upload_data["partsize"] = partsize
|
670
|
+
upload_data["part_count"] = partsize and -(-filesize // partsize)
|
671
|
+
upload_data["pid"] = pid
|
672
|
+
else:
|
673
|
+
bucket = upload_data["bucket"]
|
674
|
+
object = upload_data["object"]
|
675
|
+
callback_var = loads(upload_data["callback"]["callback_var"])
|
676
|
+
resp = yield upload_resume(
|
677
|
+
{
|
678
|
+
"fileid": object,
|
679
|
+
"file_size": upload_data["filesize"],
|
680
|
+
"target": callback_var["x:target"],
|
681
|
+
"pick_code": callback_var["x:pick_code"],
|
682
|
+
},
|
683
|
+
async_=async_,
|
684
|
+
**request_kwargs,
|
685
|
+
)
|
686
|
+
check_response(resp)
|
687
|
+
url = f"http://{bucket}.{domain}/{object}"
|
688
|
+
token = client.upload_token
|
689
|
+
if upload_id := upload_data.get("upload_id"):
|
690
|
+
parts = yield collect(oss_multipart_part_iter(
|
691
|
+
client.request,
|
692
|
+
url,
|
693
|
+
bucket=bucket,
|
694
|
+
object=object,
|
695
|
+
upload_id=upload_id,
|
696
|
+
token=token,
|
697
|
+
async_=async_,
|
698
|
+
**request_kwargs,
|
699
|
+
))
|
700
|
+
if parts:
|
701
|
+
upload_data["part_number_next"] = len(parts) + (int(parts[-1]["Size"]) == upload_data["partsize"])
|
702
|
+
else:
|
703
|
+
upload_data["part_number_next"] = 1
|
704
|
+
upload_data["parts"] = parts
|
705
|
+
else:
|
706
|
+
upload_data["upload_id"] = yield oss_multipart_upload_init(
|
707
|
+
client.request,
|
708
|
+
url,
|
709
|
+
bucket=bucket,
|
710
|
+
object=object,
|
711
|
+
token=token,
|
712
|
+
async_=async_,
|
713
|
+
**request_kwargs,
|
714
|
+
)
|
715
|
+
upload_data["part_number_next"] = 1
|
716
|
+
upload_data["parts"] = []
|
717
|
+
upload_data["_upload_"] = None
|
718
|
+
return upload_data
|
719
|
+
return run_gen_step(gen_step, async_=async_)
|
720
|
+
|
721
|
+
|
722
|
+
def multipart_upload_url(
|
723
|
+
client: str | P115Client | P115OpenClient | dict,
|
724
|
+
upload_data: dict,
|
725
|
+
part_number: int = 1,
|
726
|
+
domain: str = ALIYUN_DOMAIN,
|
727
|
+
) -> tuple[str, dict]:
|
728
|
+
"""用来获取 上传链接 和 请求头,然后文件需要你自己上传
|
729
|
+
|
730
|
+
:param client: 115 客户端或 cookies,或者是 token(令牌)
|
731
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
732
|
+
:param part_number: 需要上传的分块编号,须从 1 开始递增
|
733
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
734
|
+
|
735
|
+
:return: 上传链接 和 请求头 的 2 元组
|
736
|
+
"""
|
737
|
+
if not domain:
|
738
|
+
domain = ALIYUN_DOMAIN
|
739
|
+
if isinstance(client, dict):
|
740
|
+
token = client
|
741
|
+
else:
|
742
|
+
if isinstance(client, str):
|
743
|
+
client = P115Client(client, check_for_relogin=True)
|
744
|
+
token = client.upload_token
|
745
|
+
return oss_multipart_upload_url(
|
746
|
+
bucket=upload_data["bucket"],
|
747
|
+
object=upload_data["object"],
|
748
|
+
upload_id=upload_data["upload_id"],
|
749
|
+
part_number=part_number,
|
750
|
+
token=token,
|
751
|
+
domain=domain,
|
752
|
+
)
|
753
|
+
|
754
|
+
|
755
|
+
@overload
|
756
|
+
def multipart_upload_complete(
|
757
|
+
client: str | P115Client | P115OpenClient,
|
758
|
+
upload_data: dict,
|
759
|
+
domain: str = ALIYUN_DOMAIN,
|
760
|
+
*,
|
761
|
+
async_: Literal[False] = False,
|
762
|
+
**request_kwargs,
|
763
|
+
) -> dict:
|
764
|
+
...
|
765
|
+
@overload
|
766
|
+
def multipart_upload_complete(
|
767
|
+
client: str | P115Client | P115OpenClient,
|
768
|
+
upload_data: dict,
|
769
|
+
domain: str = ALIYUN_DOMAIN,
|
770
|
+
*,
|
771
|
+
async_: Literal[True],
|
772
|
+
**request_kwargs,
|
773
|
+
) -> Coroutine[Any, Any, dict]:
|
774
|
+
...
|
775
|
+
def multipart_upload_complete(
|
776
|
+
client: str | P115Client | P115OpenClient,
|
777
|
+
upload_data: dict,
|
778
|
+
domain: str = ALIYUN_DOMAIN,
|
779
|
+
*,
|
780
|
+
async_: Literal[False, True] = False,
|
781
|
+
**request_kwargs,
|
782
|
+
) -> dict | Coroutine[Any, Any, dict]:
|
783
|
+
"""完成分块上传
|
784
|
+
|
785
|
+
:param client: 115 客户端或 cookies,或者是 token(令牌)
|
786
|
+
:param upload_data: 上传相关信息,可用于以后的断点续传
|
787
|
+
:param domain: 上传到指定的阿里云集群的网址(netloc)
|
788
|
+
:param async_: 是否异步
|
789
|
+
:param request_kwargs: 其它请求参数
|
790
|
+
|
791
|
+
:return: 接口响应值
|
792
|
+
|
793
|
+
:example:
|
794
|
+
你可以构建自己的分块上传逻辑,下面是一个例子
|
795
|
+
|
796
|
+
.. code:: python
|
797
|
+
|
798
|
+
from pathlib import Path
|
799
|
+
from p115client import *
|
800
|
+
from p115client.tool import *
|
801
|
+
|
802
|
+
client = P115Client(Path("~/115-cookies.txt").expanduser())
|
803
|
+
#client.login_another_open(100195123, replace=True)
|
804
|
+
|
805
|
+
# TODO: 这里填一个文件的路径
|
806
|
+
path = "test.txt"
|
807
|
+
|
808
|
+
upload_data = multipart_upload_init(
|
809
|
+
client,
|
810
|
+
path,
|
811
|
+
pid = 0,
|
812
|
+
filename = "",
|
813
|
+
upload_data = None,
|
814
|
+
)
|
815
|
+
if "_upload_" in upload_data:
|
816
|
+
partsize = upload_data["partsize"]
|
817
|
+
part_number_next = upload_data["part_number_next"]
|
818
|
+
with open(path, "rb") as file:
|
819
|
+
if part_number_next > 1:
|
820
|
+
file.seek(partsize * (part_number_next - 1))
|
821
|
+
for part_number in range(part_number_next, upload_data["part_count"] + 1):
|
822
|
+
url, headers = multipart_upload_url(client, upload_data, part_number)
|
823
|
+
## TODO: 你可以自己改写上传的逻辑
|
824
|
+
## NOTE: 使用 urllib3
|
825
|
+
# from urllib3 import request
|
826
|
+
# request("PUT", url, body=file.read(partsize), headers=headers)
|
827
|
+
## NOTE: 使用 requests
|
828
|
+
# from requests import request
|
829
|
+
# request("PUT", url, data=file.read(partsize), headers=headers)
|
830
|
+
client.request(url=url, method="PUT", data=file.read(partsize), headers=headers, parse=False)
|
831
|
+
resp = multipart_upload_complete(client, upload_data)
|
832
|
+
else:
|
833
|
+
resp = upload_data
|
834
|
+
print(resp)
|
835
|
+
"""
|
836
|
+
if not domain:
|
837
|
+
domain = ALIYUN_DOMAIN
|
838
|
+
bucket = upload_data["bucket"]
|
839
|
+
object = upload_data["object"]
|
840
|
+
upload_id = upload_data["upload_id"]
|
841
|
+
url = f"http://{bucket}.{domain}/{object}"
|
842
|
+
if isinstance(client, str):
|
843
|
+
client = P115Client(client, check_for_relogin=True)
|
844
|
+
def gen_step():
|
845
|
+
token = client.upload_token
|
846
|
+
parts = yield collect(oss_multipart_part_iter(
|
847
|
+
client.request,
|
848
|
+
url,
|
849
|
+
bucket=bucket,
|
850
|
+
object=object,
|
851
|
+
upload_id=upload_id,
|
852
|
+
token=token,
|
853
|
+
async_=async_,
|
854
|
+
**request_kwargs,
|
855
|
+
))
|
856
|
+
return oss_multipart_upload_complete(
|
857
|
+
client.request,
|
858
|
+
url,
|
859
|
+
bucket=bucket,
|
860
|
+
object=object,
|
861
|
+
upload_id=upload_id,
|
862
|
+
token=token,
|
863
|
+
callback=upload_data["callback"],
|
864
|
+
parts=parts,
|
865
|
+
async_=async_,
|
866
|
+
**request_kwargs,
|
867
|
+
)
|
868
|
+
return run_gen_step(gen_step, async_=async_)
|
869
|
+
|
870
|
+
# TODO: 分块上传支持使用开放接口
|
p115client/tool/util.py
CHANGED
@@ -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
|
+
|
@@ -1,12 +1,13 @@
|
|
1
1
|
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
2
2
|
p115client/__init__.py,sha256=1mx7njuAlqcuEWONTjSiiGnXyyNyqOcJyNX1FMHqQ-4,214
|
3
|
-
p115client/_upload.py,sha256=
|
4
|
-
p115client/client.py,sha256=
|
3
|
+
p115client/_upload.py,sha256=3uXwgFsXkZ4X5hjL1N2mx0y8tA8MjVxe49N0BoyYyno,31253
|
4
|
+
p115client/client.py,sha256=sgDyWoMiCIawos2r_68QS6qKFX86kbLzV6AHMbJI9rM,781216
|
5
5
|
p115client/const.py,sha256=KqDGr9KsOnSkNVM3RIdQGptCMHbidMmZ_ffyFPiniww,7654
|
6
6
|
p115client/exception.py,sha256=4SZ8ubOLMRxtcqc0u1kNzXqH1a6wwXJFwGnRDURoEgQ,3708
|
7
7
|
p115client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
p115client/tool/__init__.py,sha256=
|
8
|
+
p115client/tool/__init__.py,sha256=TdGjEH7SD_6BaBdNg9Wd2Zd91KCycDOVN9JbH21z5SA,406
|
9
9
|
p115client/tool/attr.py,sha256=RmNm5uar2PVv1Me7xOWyM0JtZr-JqoglIIpF0WHGues,2979
|
10
|
+
p115client/tool/auth.py,sha256=jUkVaCACg_K38RzpHTMGvve1xvhAdBeyPGtlNIfA_9M,1679
|
10
11
|
p115client/tool/download.py,sha256=CZJE5f1SHi26eCNTPcF5VjSoht8WTUYravEG219i4gk,61534
|
11
12
|
p115client/tool/edit.py,sha256=kRz-Ee7KuNNlHhcVi4uHdh9DSfF1ZxUREIhK9x4hNIE,17771
|
12
13
|
p115client/tool/export_dir.py,sha256=1r2qDuYIirtDkCBQfYXh6lbm06IStRpiom-ZjtIYdbo,24502
|
@@ -16,11 +17,11 @@ p115client/tool/iterdir.py,sha256=dXBMPfxPzRcFhBpnBT3X1LYkhc3l31rYCLRKRFbggSk,20
|
|
16
17
|
p115client/tool/life.py,sha256=ceweN2uNKYxigSOaBQ4Abo5u05zqppRm_P7k-_rHjcA,17301
|
17
18
|
p115client/tool/pool.py,sha256=H65VhoNxQC6xWSL1THq_PximWnBOqB4EfU6kWBTAnlA,13946
|
18
19
|
p115client/tool/request.py,sha256=rjXuQwRganE5Z-4rfgnyPFjE4jzdQSLdIs9s0cIDshU,7043
|
19
|
-
p115client/tool/upload.py,sha256=
|
20
|
-
p115client/tool/util.py,sha256=
|
20
|
+
p115client/tool/upload.py,sha256=21t6X5YABV-S98cifhoBtINsiw4u8NH0NVQNVJ8GyRg,32740
|
21
|
+
p115client/tool/util.py,sha256=pAa8gc4BcnVTpNcXbNZU4tBUMjSB04DGOpzDdzfbto8,3934
|
21
22
|
p115client/tool/xys.py,sha256=vU28Px2yeQzIxxGkopJIpvV6TdOnWJ5xB6NPXpTgM0Y,10306
|
22
23
|
p115client/type.py,sha256=7kOp98uLaYqcTTCgCrb3DRcl8ukMpn7ibsnVvtw2nG8,6250
|
23
|
-
p115client-0.0.5.11.
|
24
|
-
p115client-0.0.5.11.
|
25
|
-
p115client-0.0.5.11.
|
26
|
-
p115client-0.0.5.11.
|
24
|
+
p115client-0.0.5.11.9.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
25
|
+
p115client-0.0.5.11.9.1.dist-info/METADATA,sha256=_FIWJxRFzeaIAbw1imz3VfpUhD5JtOs2OAetuP7gsEg,8194
|
26
|
+
p115client-0.0.5.11.9.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
27
|
+
p115client-0.0.5.11.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|