p115client 0.0.5.11.8.1__py3-none-any.whl → 0.0.5.11.9__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 +67 -34
- p115client/tool/__init__.py +1 -0
- p115client/tool/auth.py +62 -0
- p115client/tool/upload.py +406 -4
- p115client/tool/util.py +23 -1
- {p115client-0.0.5.11.8.1.dist-info → p115client-0.0.5.11.9.dist-info}/METADATA +1 -1
- {p115client-0.0.5.11.8.1.dist-info → p115client-0.0.5.11.9.dist-info}/RECORD +10 -9
- {p115client-0.0.5.11.8.1.dist-info → p115client-0.0.5.11.9.dist-info}/LICENSE +0 -0
- {p115client-0.0.5.11.8.1.dist-info → p115client-0.0.5.11.9.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
@@ -220,12 +220,11 @@ def complete_lixian_api(
|
|
220
220
|
return complete_api(path, base, base_url=base_url)
|
221
221
|
|
222
222
|
|
223
|
-
def try_parse_int(
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
if s.startswith("0") or s.strip(digits):
|
223
|
+
def try_parse_int(
|
224
|
+
s, /,
|
225
|
+
_match=re_compile("-?[1-9][0-9]*").fullmatch,
|
226
|
+
):
|
227
|
+
if not isinstance(s, str) or not s or not _match(s):
|
229
228
|
return s
|
230
229
|
return int(s)
|
231
230
|
|
@@ -1671,7 +1670,7 @@ class ClientRequestMixin:
|
|
1671
1670
|
|
1672
1671
|
GET https://qrcodeapi.115.com/api/1.0/web/1.0/qrcode
|
1673
1672
|
|
1674
|
-
:
|
1673
|
+
:param uid: 二维码的 uid
|
1675
1674
|
|
1676
1675
|
:return: 图片的二进制数据(PNG 图片)
|
1677
1676
|
"""
|
@@ -3275,7 +3274,6 @@ class P115OpenClient(ClientRequestMixin):
|
|
3275
3274
|
api = complete_proapi("/open/ufile/downurl", base_url)
|
3276
3275
|
if isinstance(payload, str):
|
3277
3276
|
payload = {"pick_code": payload}
|
3278
|
-
request_headers = request_kwargs.get("headers")
|
3279
3277
|
headers = request_kwargs.get("headers")
|
3280
3278
|
if headers:
|
3281
3279
|
if isinstance(headers, Mapping):
|
@@ -3463,7 +3461,7 @@ class P115OpenClient(ClientRequestMixin):
|
|
3463
3461
|
- fields: str = <default>
|
3464
3462
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
3465
3463
|
- format: str = "json" 💡 返回格式,默认即可
|
3466
|
-
- hide_data: str = <default>
|
3464
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
3467
3465
|
- is_q: 0 | 1 = <default>
|
3468
3466
|
- is_share: 0 | 1 = <default>
|
3469
3467
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -3772,8 +3770,8 @@ class P115OpenClient(ClientRequestMixin):
|
|
3772
3770
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
3773
3771
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
3774
3772
|
- source: str = <default>
|
3775
|
-
- star: 0 | 1 = <default>
|
3776
|
-
- suffix: str = <default>
|
3773
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
3774
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
3777
3775
|
- type: int = <default> 💡 文件类型
|
3778
3776
|
|
3779
3777
|
- 0: 全部(仅当前目录)
|
@@ -6174,6 +6172,8 @@ class P115Client(P115OpenClient):
|
|
6174
6172
|
|
6175
6173
|
但一个设备的新登录者,并不总是意味着把较早的登录者下线,一般需要触发某个检查机制后,才会把同一设备下除最近一次登录外的所有 cookies 失效
|
6176
6174
|
|
6175
|
+
所以你可以用一个设备的 cookies 专门用于扫码登录,获取另一个设备的 cookies 执行网盘操作,第 2 个 cookies 失效了,则用第 1 个 cookies 扫码,如此可避免单个 cookies 失效后,不能自动获取新的
|
6176
|
+
|
6177
6177
|
:param app: 要登录的 app,如果为 None,则用当前登录设备,如果无当前登录设备,则报错
|
6178
6178
|
:param replace: 替换某个 client 对象的 cookie
|
6179
6179
|
|
@@ -6861,6 +6861,7 @@ class P115Client(P115OpenClient):
|
|
6861
6861
|
self,
|
6862
6862
|
payload: dict,
|
6863
6863
|
/,
|
6864
|
+
app: str = "web",
|
6864
6865
|
base_url: bool | str | Callable[[], str] = False,
|
6865
6866
|
*,
|
6866
6867
|
async_: Literal[False] = False,
|
@@ -6872,6 +6873,7 @@ class P115Client(P115OpenClient):
|
|
6872
6873
|
self,
|
6873
6874
|
payload: dict,
|
6874
6875
|
/,
|
6876
|
+
app: str = "web",
|
6875
6877
|
base_url: bool | str | Callable[[], str] = False,
|
6876
6878
|
*,
|
6877
6879
|
async_: Literal[True],
|
@@ -6882,6 +6884,7 @@ class P115Client(P115OpenClient):
|
|
6882
6884
|
self,
|
6883
6885
|
payload: dict,
|
6884
6886
|
/,
|
6887
|
+
app: str = "web",
|
6885
6888
|
base_url: bool | str | Callable[[], str] = False,
|
6886
6889
|
*,
|
6887
6890
|
async_: Literal[False, True] = False,
|
@@ -6896,7 +6899,7 @@ class P115Client(P115OpenClient):
|
|
6896
6899
|
- aid: int | str 💡 助愿的 id
|
6897
6900
|
- to_cid: int = <default> 💡 助愿中的分享链接转存到你的网盘中目录的 id
|
6898
6901
|
"""
|
6899
|
-
api = complete_api("/api/1.0/
|
6902
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/adopt", "act", base_url=base_url)
|
6900
6903
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6901
6904
|
|
6902
6905
|
@overload
|
@@ -6904,6 +6907,7 @@ class P115Client(P115OpenClient):
|
|
6904
6907
|
self,
|
6905
6908
|
payload: dict,
|
6906
6909
|
/,
|
6910
|
+
app: str = "web",
|
6907
6911
|
base_url: bool | str | Callable[[], str] = False,
|
6908
6912
|
*,
|
6909
6913
|
async_: Literal[False] = False,
|
@@ -6915,6 +6919,7 @@ class P115Client(P115OpenClient):
|
|
6915
6919
|
self,
|
6916
6920
|
payload: dict,
|
6917
6921
|
/,
|
6922
|
+
app: str = "web",
|
6918
6923
|
base_url: bool | str | Callable[[], str] = False,
|
6919
6924
|
*,
|
6920
6925
|
async_: Literal[True],
|
@@ -6925,6 +6930,7 @@ class P115Client(P115OpenClient):
|
|
6925
6930
|
self,
|
6926
6931
|
payload: dict,
|
6927
6932
|
/,
|
6933
|
+
app: str = "web",
|
6928
6934
|
base_url: bool | str | Callable[[], str] = False,
|
6929
6935
|
*,
|
6930
6936
|
async_: Literal[False, True] = False,
|
@@ -6940,7 +6946,7 @@ class P115Client(P115OpenClient):
|
|
6940
6946
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
6941
6947
|
- file_ids: int | str = <default> 💡 文件在你的网盘的 id,多个用逗号 "," 隔开
|
6942
6948
|
"""
|
6943
|
-
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)
|
6944
6950
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
6945
6951
|
|
6946
6952
|
@overload
|
@@ -6948,6 +6954,7 @@ class P115Client(P115OpenClient):
|
|
6948
6954
|
self,
|
6949
6955
|
payload: int | str | dict,
|
6950
6956
|
/,
|
6957
|
+
app: str = "web",
|
6951
6958
|
base_url: bool | str | Callable[[], str] = False,
|
6952
6959
|
*,
|
6953
6960
|
async_: Literal[False] = False,
|
@@ -6959,6 +6966,7 @@ class P115Client(P115OpenClient):
|
|
6959
6966
|
self,
|
6960
6967
|
payload: int | str | dict,
|
6961
6968
|
/,
|
6969
|
+
app: str = "web",
|
6962
6970
|
base_url: bool | str | Callable[[], str] = False,
|
6963
6971
|
*,
|
6964
6972
|
async_: Literal[True],
|
@@ -6969,6 +6977,7 @@ class P115Client(P115OpenClient):
|
|
6969
6977
|
self,
|
6970
6978
|
payload: int | str | dict,
|
6971
6979
|
/,
|
6980
|
+
app: str = "web",
|
6972
6981
|
base_url: bool | str | Callable[[], str] = False,
|
6973
6982
|
*,
|
6974
6983
|
async_: Literal[False, True] = False,
|
@@ -6981,7 +6990,7 @@ class P115Client(P115OpenClient):
|
|
6981
6990
|
:payload:
|
6982
6991
|
- ids: int | str 💡 助愿的 id,多个用逗号 "," 隔开
|
6983
6992
|
"""
|
6984
|
-
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)
|
6985
6994
|
if isinstance(payload, (int, str)):
|
6986
6995
|
payload = {"ids": payload}
|
6987
6996
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -6991,6 +7000,7 @@ class P115Client(P115OpenClient):
|
|
6991
7000
|
self,
|
6992
7001
|
payload: str | dict,
|
6993
7002
|
/,
|
7003
|
+
app: str = "web",
|
6994
7004
|
base_url: bool | str | Callable[[], str] = False,
|
6995
7005
|
*,
|
6996
7006
|
async_: Literal[False] = False,
|
@@ -7002,6 +7012,7 @@ class P115Client(P115OpenClient):
|
|
7002
7012
|
self,
|
7003
7013
|
payload: str | dict,
|
7004
7014
|
/,
|
7015
|
+
app: str = "web",
|
7005
7016
|
base_url: bool | str | Callable[[], str] = False,
|
7006
7017
|
*,
|
7007
7018
|
async_: Literal[True],
|
@@ -7012,6 +7023,7 @@ class P115Client(P115OpenClient):
|
|
7012
7023
|
self,
|
7013
7024
|
payload: str | dict,
|
7014
7025
|
/,
|
7026
|
+
app: str = "web",
|
7015
7027
|
base_url: bool | str | Callable[[], str] = False,
|
7016
7028
|
*,
|
7017
7029
|
async_: Literal[False, True] = False,
|
@@ -7028,7 +7040,7 @@ class P115Client(P115OpenClient):
|
|
7028
7040
|
- limit: int = 10 💡 分页大小
|
7029
7041
|
- sort: int | str = <default> 💡 排序
|
7030
7042
|
"""
|
7031
|
-
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)
|
7032
7044
|
if isinstance(payload, str):
|
7033
7045
|
payload = {"start": 0, "page": 1, "limit": 10, "id": payload}
|
7034
7046
|
else:
|
@@ -7039,6 +7051,7 @@ class P115Client(P115OpenClient):
|
|
7039
7051
|
def act_xys_get_act_info(
|
7040
7052
|
self,
|
7041
7053
|
/,
|
7054
|
+
app: str = "web",
|
7042
7055
|
base_url: bool | str | Callable[[], str] = False,
|
7043
7056
|
*,
|
7044
7057
|
async_: Literal[False] = False,
|
@@ -7049,6 +7062,7 @@ class P115Client(P115OpenClient):
|
|
7049
7062
|
def act_xys_get_act_info(
|
7050
7063
|
self,
|
7051
7064
|
/,
|
7065
|
+
app: str = "web",
|
7052
7066
|
base_url: bool | str | Callable[[], str] = False,
|
7053
7067
|
*,
|
7054
7068
|
async_: Literal[True],
|
@@ -7058,6 +7072,7 @@ class P115Client(P115OpenClient):
|
|
7058
7072
|
def act_xys_get_act_info(
|
7059
7073
|
self,
|
7060
7074
|
/,
|
7075
|
+
app: str = "web",
|
7061
7076
|
base_url: bool | str | Callable[[], str] = False,
|
7062
7077
|
*,
|
7063
7078
|
async_: Literal[False, True] = False,
|
@@ -7067,7 +7082,7 @@ class P115Client(P115OpenClient):
|
|
7067
7082
|
|
7068
7083
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/get_act_info
|
7069
7084
|
"""
|
7070
|
-
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)
|
7071
7086
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7072
7087
|
|
7073
7088
|
@overload
|
@@ -7075,6 +7090,7 @@ class P115Client(P115OpenClient):
|
|
7075
7090
|
self,
|
7076
7091
|
payload: str | dict,
|
7077
7092
|
/,
|
7093
|
+
app: str = "web",
|
7078
7094
|
base_url: bool | str | Callable[[], str] = False,
|
7079
7095
|
*,
|
7080
7096
|
async_: Literal[False] = False,
|
@@ -7086,6 +7102,7 @@ class P115Client(P115OpenClient):
|
|
7086
7102
|
self,
|
7087
7103
|
payload: str | dict,
|
7088
7104
|
/,
|
7105
|
+
app: str = "web",
|
7089
7106
|
base_url: bool | str | Callable[[], str] = False,
|
7090
7107
|
*,
|
7091
7108
|
async_: Literal[True],
|
@@ -7096,6 +7113,7 @@ class P115Client(P115OpenClient):
|
|
7096
7113
|
self,
|
7097
7114
|
payload: str | dict,
|
7098
7115
|
/,
|
7116
|
+
app: str = "web",
|
7099
7117
|
base_url: bool | str | Callable[[], str] = False,
|
7100
7118
|
*,
|
7101
7119
|
async_: Literal[False, True] = False,
|
@@ -7108,7 +7126,7 @@ class P115Client(P115OpenClient):
|
|
7108
7126
|
:payload:
|
7109
7127
|
- id: str 💡 许愿的 id
|
7110
7128
|
"""
|
7111
|
-
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)
|
7112
7130
|
if isinstance(payload, str):
|
7113
7131
|
payload = {"id": payload}
|
7114
7132
|
return self.request(url=api, params=payload, async_=async_, **request_kwargs)
|
@@ -7117,6 +7135,7 @@ class P115Client(P115OpenClient):
|
|
7117
7135
|
def act_xys_home_list(
|
7118
7136
|
self,
|
7119
7137
|
/,
|
7138
|
+
app: str = "web",
|
7120
7139
|
base_url: bool | str | Callable[[], str] = False,
|
7121
7140
|
*,
|
7122
7141
|
async_: Literal[False] = False,
|
@@ -7127,6 +7146,7 @@ class P115Client(P115OpenClient):
|
|
7127
7146
|
def act_xys_home_list(
|
7128
7147
|
self,
|
7129
7148
|
/,
|
7149
|
+
app: str = "web",
|
7130
7150
|
base_url: bool | str | Callable[[], str] = False,
|
7131
7151
|
*,
|
7132
7152
|
async_: Literal[True],
|
@@ -7136,6 +7156,7 @@ class P115Client(P115OpenClient):
|
|
7136
7156
|
def act_xys_home_list(
|
7137
7157
|
self,
|
7138
7158
|
/,
|
7159
|
+
app: str = "web",
|
7139
7160
|
base_url: bool | str | Callable[[], str] = False,
|
7140
7161
|
*,
|
7141
7162
|
async_: Literal[False, True] = False,
|
@@ -7145,7 +7166,7 @@ class P115Client(P115OpenClient):
|
|
7145
7166
|
|
7146
7167
|
GET https://act.115.com/api/1.0/web/1.0/act2024xys/home_list
|
7147
7168
|
"""
|
7148
|
-
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)
|
7149
7170
|
return self.request(url=api, async_=async_, **request_kwargs)
|
7150
7171
|
|
7151
7172
|
@overload
|
@@ -7153,6 +7174,7 @@ class P115Client(P115OpenClient):
|
|
7153
7174
|
self,
|
7154
7175
|
payload: int | str | dict = 0,
|
7155
7176
|
/,
|
7177
|
+
app: str = "web",
|
7156
7178
|
base_url: bool | str | Callable[[], str] = False,
|
7157
7179
|
*,
|
7158
7180
|
async_: Literal[False] = False,
|
@@ -7164,6 +7186,7 @@ class P115Client(P115OpenClient):
|
|
7164
7186
|
self,
|
7165
7187
|
payload: int | str | dict = 0,
|
7166
7188
|
/,
|
7189
|
+
app: str = "web",
|
7167
7190
|
base_url: bool | str | Callable[[], str] = False,
|
7168
7191
|
*,
|
7169
7192
|
async_: Literal[True],
|
@@ -7174,6 +7197,7 @@ class P115Client(P115OpenClient):
|
|
7174
7197
|
self,
|
7175
7198
|
payload: int | str | dict = 0,
|
7176
7199
|
/,
|
7200
|
+
app: str = "web",
|
7177
7201
|
base_url: bool | str | Callable[[], str] = False,
|
7178
7202
|
*,
|
7179
7203
|
async_: Literal[False, True] = False,
|
@@ -7194,7 +7218,7 @@ class P115Client(P115OpenClient):
|
|
7194
7218
|
- page: int = 1 💡 第几页
|
7195
7219
|
- limit: int = 10 💡 分页大小
|
7196
7220
|
"""
|
7197
|
-
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)
|
7198
7222
|
if isinstance(payload, (int, str)):
|
7199
7223
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7200
7224
|
else:
|
@@ -7206,6 +7230,7 @@ class P115Client(P115OpenClient):
|
|
7206
7230
|
self,
|
7207
7231
|
payload: int | str | dict = 0,
|
7208
7232
|
/,
|
7233
|
+
app: str = "web",
|
7209
7234
|
base_url: bool | str | Callable[[], str] = False,
|
7210
7235
|
*,
|
7211
7236
|
async_: Literal[False] = False,
|
@@ -7217,6 +7242,7 @@ class P115Client(P115OpenClient):
|
|
7217
7242
|
self,
|
7218
7243
|
payload: int | str | dict = 0,
|
7219
7244
|
/,
|
7245
|
+
app: str = "web",
|
7220
7246
|
base_url: bool | str | Callable[[], str] = False,
|
7221
7247
|
*,
|
7222
7248
|
async_: Literal[True],
|
@@ -7227,6 +7253,7 @@ class P115Client(P115OpenClient):
|
|
7227
7253
|
self,
|
7228
7254
|
payload: int | str | dict = 0,
|
7229
7255
|
/,
|
7256
|
+
app: str = "web",
|
7230
7257
|
base_url: bool | str | Callable[[], str] = False,
|
7231
7258
|
*,
|
7232
7259
|
async_: Literal[False, True] = False,
|
@@ -7247,7 +7274,7 @@ class P115Client(P115OpenClient):
|
|
7247
7274
|
- page: int = 1 💡 第几页
|
7248
7275
|
- limit: int = 10 💡 分页大小
|
7249
7276
|
"""
|
7250
|
-
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)
|
7251
7278
|
if isinstance(payload, (int, str)):
|
7252
7279
|
payload = {"start": 0, "page": 1, "limit": 10, "type": payload}
|
7253
7280
|
else:
|
@@ -7259,6 +7286,7 @@ class P115Client(P115OpenClient):
|
|
7259
7286
|
self,
|
7260
7287
|
payload: str | dict,
|
7261
7288
|
/,
|
7289
|
+
app: str = "web",
|
7262
7290
|
base_url: bool | str | Callable[[], str] = False,
|
7263
7291
|
*,
|
7264
7292
|
async_: Literal[False] = False,
|
@@ -7270,6 +7298,7 @@ class P115Client(P115OpenClient):
|
|
7270
7298
|
self,
|
7271
7299
|
payload: str | dict,
|
7272
7300
|
/,
|
7301
|
+
app: str = "web",
|
7273
7302
|
base_url: bool | str | Callable[[], str] = False,
|
7274
7303
|
*,
|
7275
7304
|
async_: Literal[True],
|
@@ -7280,6 +7309,7 @@ class P115Client(P115OpenClient):
|
|
7280
7309
|
self,
|
7281
7310
|
payload: str | dict,
|
7282
7311
|
/,
|
7312
|
+
app: str = "web",
|
7283
7313
|
base_url: bool | str | Callable[[], str] = False,
|
7284
7314
|
*,
|
7285
7315
|
async_: Literal[False, True] = False,
|
@@ -7294,7 +7324,7 @@ class P115Client(P115OpenClient):
|
|
7294
7324
|
- rewardSpace: int = 5 💡 奖励容量,单位是 GB
|
7295
7325
|
- images: int | str = <default> 💡 图片文件在你的网盘的 id,多个用逗号 "," 隔开
|
7296
7326
|
"""
|
7297
|
-
api = complete_api("/api/1.0/
|
7327
|
+
api = complete_api(f"/api/1.0/{app}/1.0/act2024xys/wish", "act", base_url=base_url)
|
7298
7328
|
if isinstance(payload, str):
|
7299
7329
|
payload = {"rewardSpace": 5, "content": payload}
|
7300
7330
|
else:
|
@@ -7306,6 +7336,7 @@ class P115Client(P115OpenClient):
|
|
7306
7336
|
self,
|
7307
7337
|
payload: str | dict,
|
7308
7338
|
/,
|
7339
|
+
app: str = "web",
|
7309
7340
|
base_url: bool | str | Callable[[], str] = False,
|
7310
7341
|
*,
|
7311
7342
|
async_: Literal[False] = False,
|
@@ -7317,6 +7348,7 @@ class P115Client(P115OpenClient):
|
|
7317
7348
|
self,
|
7318
7349
|
payload: str | dict,
|
7319
7350
|
/,
|
7351
|
+
app: str = "web",
|
7320
7352
|
base_url: bool | str | Callable[[], str] = False,
|
7321
7353
|
*,
|
7322
7354
|
async_: Literal[True],
|
@@ -7327,6 +7359,7 @@ class P115Client(P115OpenClient):
|
|
7327
7359
|
self,
|
7328
7360
|
payload: str | dict,
|
7329
7361
|
/,
|
7362
|
+
app: str = "web",
|
7330
7363
|
base_url: bool | str | Callable[[], str] = False,
|
7331
7364
|
*,
|
7332
7365
|
async_: Literal[False, True] = False,
|
@@ -7339,7 +7372,7 @@ class P115Client(P115OpenClient):
|
|
7339
7372
|
:payload:
|
7340
7373
|
- ids: str 💡 许愿的 id,多个用逗号 "," 隔开
|
7341
7374
|
"""
|
7342
|
-
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)
|
7343
7376
|
if isinstance(payload, str):
|
7344
7377
|
payload = {"ids": payload}
|
7345
7378
|
return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
|
@@ -7955,7 +7988,6 @@ class P115Client(P115OpenClient):
|
|
7955
7988
|
payload = {"pick_code": payload}
|
7956
7989
|
else:
|
7957
7990
|
payload = {"pick_code": payload["pickcode"]}
|
7958
|
-
request_headers = request_kwargs.get("headers")
|
7959
7991
|
headers = request_kwargs.get("headers")
|
7960
7992
|
if headers:
|
7961
7993
|
if isinstance(headers, Mapping):
|
@@ -8216,7 +8248,6 @@ class P115Client(P115OpenClient):
|
|
8216
8248
|
- full_name: str
|
8217
8249
|
"""
|
8218
8250
|
api = complete_proapi("/2.0/ufile/extract_down_file", base_url, app)
|
8219
|
-
request_headers = request_kwargs.get("headers")
|
8220
8251
|
headers = request_kwargs.get("headers")
|
8221
8252
|
if headers:
|
8222
8253
|
if isinstance(headers, Mapping):
|
@@ -8272,7 +8303,6 @@ class P115Client(P115OpenClient):
|
|
8272
8303
|
- full_name: str
|
8273
8304
|
"""
|
8274
8305
|
api = complete_webapi("/files/extract_down_file", base_url=base_url)
|
8275
|
-
request_headers = request_kwargs.get("headers")
|
8276
8306
|
headers = request_kwargs.get("headers")
|
8277
8307
|
if headers:
|
8278
8308
|
if isinstance(headers, Mapping):
|
@@ -10516,7 +10546,7 @@ class P115Client(P115OpenClient):
|
|
10516
10546
|
- fields: str = <default>
|
10517
10547
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10518
10548
|
- format: str = "json" 💡 返回格式,默认即可
|
10519
|
-
- hide_data: str = <default>
|
10549
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10520
10550
|
- is_q: 0 | 1 = <default>
|
10521
10551
|
- is_share: 0 | 1 = <default>
|
10522
10552
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10646,7 +10676,7 @@ class P115Client(P115OpenClient):
|
|
10646
10676
|
- fields: str = <default>
|
10647
10677
|
- for: str = <default> 💡 文件格式,例如 "doc"
|
10648
10678
|
- format: str = "json" 💡 返回格式,默认即可
|
10649
|
-
- hide_data: str = <default>
|
10679
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10650
10680
|
- is_q: 0 | 1 = <default>
|
10651
10681
|
- is_share: 0 | 1 = <default>
|
10652
10682
|
- min_size: int = 0 💡 最小的文件大小
|
@@ -10775,7 +10805,7 @@ class P115Client(P115OpenClient):
|
|
10775
10805
|
- fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
|
10776
10806
|
- fields: str = <default>
|
10777
10807
|
- format: str = "json" 💡 返回格式,默认即可
|
10778
|
-
- hide_data: str = <default>
|
10808
|
+
- hide_data: str = <default> 💡 是否返回文件数据
|
10779
10809
|
- is_asc: 0 | 1 = <default>
|
10780
10810
|
- is_q: 0 | 1 = <default>
|
10781
10811
|
- is_share: 0 | 1 = <default>
|
@@ -11730,7 +11760,7 @@ class P115Client(P115OpenClient):
|
|
11730
11760
|
:payload:
|
11731
11761
|
- offset: int = 0
|
11732
11762
|
- limit: int = 1150
|
11733
|
-
- played_end: 0 | 1 = <default>
|
11763
|
+
- played_end: 0 | 1 = <default> 💡 是否已经播放完
|
11734
11764
|
- type: int = <default> 💡 类型(??表示还未搞清楚),多个用逗号 "," 隔开
|
11735
11765
|
|
11736
11766
|
- 全部: 0
|
@@ -14605,8 +14635,8 @@ class P115Client(P115OpenClient):
|
|
14605
14635
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14606
14636
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14607
14637
|
- source: str = <default>
|
14608
|
-
- star: 0 | 1 = <default>
|
14609
|
-
- suffix: str = <default>
|
14638
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14639
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14610
14640
|
- type: int = <default> 💡 文件类型
|
14611
14641
|
|
14612
14642
|
- 0: 全部(仅当前目录)
|
@@ -14702,8 +14732,8 @@ class P115Client(P115OpenClient):
|
|
14702
14732
|
- search_value: str = "." 💡 搜索文本,可以是 sha1
|
14703
14733
|
- show_dir: 0 | 1 = 1 💡 是否显示目录
|
14704
14734
|
- source: str = <default>
|
14705
|
-
- star: 0 | 1 = <default>
|
14706
|
-
- suffix: str = <default>
|
14735
|
+
- star: 0 | 1 = <default> 💡 是否星标文件
|
14736
|
+
- suffix: str = <default> 💡 后缀名(优先级高于 `type`)
|
14707
14737
|
- type: int = <default> 💡 文件类型
|
14708
14738
|
|
14709
14739
|
- 0: 全部(仅当前目录)
|
@@ -15468,7 +15498,7 @@ class P115Client(P115OpenClient):
|
|
15468
15498
|
这个接口只支持 web 的 cookies,其它设备会返回空数据,而且获取得到的 m3u8 里的链接,也是 m3u8,会绑定前一次请求时的 user-agent
|
15469
15499
|
|
15470
15500
|
:param pickcode: 视频文件的 pickcode
|
15471
|
-
:
|
15501
|
+
:param definition: 画质,默认列出所有画质。但可进行筛选,常用的为:
|
15472
15502
|
- 0: 各种分辨率(默认)
|
15473
15503
|
- 1: SD 标清(约为 480p)
|
15474
15504
|
- 3: HD 超清(约为 720p)
|
@@ -21145,6 +21175,9 @@ class P115Client(P115OpenClient):
|
|
21145
21175
|
)
|
21146
21176
|
return run_gen_step(gen_step, may_call=False, async_=async_)
|
21147
21177
|
|
21178
|
+
# TODO: 分块上传时,允许一定次数的重试
|
21179
|
+
# TODO: 不妨单独为分块上传做一个封装
|
21180
|
+
# TODO: 减少参数,简化使用方法
|
21148
21181
|
@overload # type: ignore
|
21149
21182
|
def upload_file(
|
21150
21183
|
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
|
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
|
+
|
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=PF2onKoO9z0xF8mf1Q87SXHmwk1NS7fOo34gERt8Ii8,781183
|
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=fJnxozMWeFW7m2T3AKYWGJdABxK0tnMbBaDDc1LHKw4,31278
|
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.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
25
|
+
p115client-0.0.5.11.9.dist-info/METADATA,sha256=ZtZoJ6AlBvgr-sNR4TtYBGFDKpqzZ30HZgAzSbgtg0A,8192
|
26
|
+
p115client-0.0.5.11.9.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
27
|
+
p115client-0.0.5.11.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|