p123client 0.0.7.1.1__py3-none-any.whl → 0.0.7.2__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.
- p123client/client.py +289 -85
- {p123client-0.0.7.1.1.dist-info → p123client-0.0.7.2.dist-info}/METADATA +1 -1
- {p123client-0.0.7.1.1.dist-info → p123client-0.0.7.2.dist-info}/RECORD +5 -5
- {p123client-0.0.7.1.1.dist-info → p123client-0.0.7.2.dist-info}/LICENSE +0 -0
- {p123client-0.0.7.1.1.dist-info → p123client-0.0.7.2.dist-info}/WHEEL +0 -0
p123client/client.py
CHANGED
@@ -7,6 +7,7 @@ __all__ = ["check_response", "P123OpenClient", "P123Client"]
|
|
7
7
|
|
8
8
|
import errno
|
9
9
|
|
10
|
+
from asyncio import Lock as AsyncLock
|
10
11
|
from base64 import urlsafe_b64decode
|
11
12
|
from collections.abc import (
|
12
13
|
AsyncIterable, Awaitable, Buffer, Callable, Coroutine,
|
@@ -17,7 +18,7 @@ from functools import partial
|
|
17
18
|
from hashlib import md5
|
18
19
|
from http.cookiejar import CookieJar
|
19
20
|
from inspect import isawaitable
|
20
|
-
from itertools import chain
|
21
|
+
from itertools import chain, count
|
21
22
|
from os import fsdecode, fstat, isatty, PathLike
|
22
23
|
from os.path import basename
|
23
24
|
from pathlib import Path, PurePath
|
@@ -25,6 +26,7 @@ from re import compile as re_compile, MULTILINE
|
|
25
26
|
from string import digits, hexdigits, ascii_uppercase
|
26
27
|
from sys import _getframe
|
27
28
|
from tempfile import TemporaryFile
|
29
|
+
from threading import Lock
|
28
30
|
from typing import cast, overload, Any, Final, Literal, Self
|
29
31
|
from urllib.parse import parse_qsl, urlsplit
|
30
32
|
from uuid import uuid4
|
@@ -48,7 +50,8 @@ from yarl import URL
|
|
48
50
|
|
49
51
|
from .const import CLIENT_API_METHODS_MAP, CLIENT_METHOD_API_MAP
|
50
52
|
from .exception import (
|
51
|
-
P123OSError, P123BrokenUpload, P123LoginError,
|
53
|
+
P123Warning, P123OSError, P123BrokenUpload, P123LoginError,
|
54
|
+
P123AuthenticationError, P123FileNotFoundError,
|
52
55
|
)
|
53
56
|
|
54
57
|
|
@@ -210,48 +213,128 @@ def check_response(resp: dict | Awaitable[dict], /) -> dict | Coroutine[Any, Any
|
|
210
213
|
|
211
214
|
|
212
215
|
class P123OpenClient:
|
213
|
-
"""123
|
216
|
+
"""123 网盘客户端,仅使用开放接口
|
214
217
|
|
215
218
|
.. admonition:: Reference
|
216
219
|
|
217
220
|
https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced
|
218
|
-
"""
|
219
221
|
|
222
|
+
:param client_id: 应用标识,创建应用时分配的 appId
|
223
|
+
:param client_secret: 应用密钥,创建应用时分配的 secretId
|
224
|
+
:param token: 123 的访问令牌
|
225
|
+
:param refresh_token: 刷新令牌
|
226
|
+
:param check_for_relogin: 当 access_token 失效时,是否重新登录
|
227
|
+
"""
|
220
228
|
client_id: str = ""
|
221
229
|
client_secret: str = ""
|
222
230
|
refresh_token: str = ""
|
223
231
|
token_path: None | PurePath = None
|
232
|
+
check_for_relogin: bool = False
|
224
233
|
|
225
234
|
def __init__(
|
226
|
-
self,
|
235
|
+
self,
|
236
|
+
/,
|
227
237
|
client_id: str | PathLike = "",
|
228
238
|
client_secret: str = "",
|
229
239
|
token: None | str | PathLike = None,
|
230
240
|
refresh_token: str = "",
|
241
|
+
check_for_relogin: bool = True,
|
231
242
|
):
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
self.login_open()
|
241
|
-
elif isinstance(token, str):
|
242
|
-
self.token = token.removeprefix("Bearer ")
|
243
|
-
else:
|
244
|
-
if isinstance(token, PurePath) and hasattr(token, "open"):
|
245
|
-
self.token_path = token
|
246
|
-
else:
|
247
|
-
self.token_path = Path(fsdecode(token))
|
248
|
-
self._read_token()
|
249
|
-
if not self.token and (client_id and client_secret or refresh_token):
|
250
|
-
self.login_open()
|
243
|
+
self.init(
|
244
|
+
client_id=client_id,
|
245
|
+
client_secret=client_secret,
|
246
|
+
token=token,
|
247
|
+
refresh_token=refresh_token,
|
248
|
+
check_for_relogin=check_for_relogin,
|
249
|
+
instance=self,
|
250
|
+
)
|
251
251
|
|
252
252
|
def __del__(self, /):
|
253
253
|
self.close()
|
254
254
|
|
255
|
+
@overload
|
256
|
+
@classmethod
|
257
|
+
def init(
|
258
|
+
cls,
|
259
|
+
/,
|
260
|
+
client_id: str | PathLike = "",
|
261
|
+
client_secret: str = "",
|
262
|
+
token: None | str | PathLike = None,
|
263
|
+
refresh_token: str = "",
|
264
|
+
check_for_relogin: bool = True,
|
265
|
+
instance: None | Self = None,
|
266
|
+
*,
|
267
|
+
async_: Literal[False] = False,
|
268
|
+
**request_kwargs,
|
269
|
+
) -> P123OpenClient:
|
270
|
+
...
|
271
|
+
@overload
|
272
|
+
@classmethod
|
273
|
+
def init(
|
274
|
+
cls,
|
275
|
+
/,
|
276
|
+
client_id: str | PathLike = "",
|
277
|
+
client_secret: str = "",
|
278
|
+
token: None | str | PathLike = None,
|
279
|
+
refresh_token: str = "",
|
280
|
+
check_for_relogin: bool = True,
|
281
|
+
instance: None | Self = None,
|
282
|
+
*,
|
283
|
+
async_: Literal[True],
|
284
|
+
**request_kwargs,
|
285
|
+
) -> Coroutine[Any, Any, P123OpenClient]:
|
286
|
+
...
|
287
|
+
@classmethod
|
288
|
+
def init(
|
289
|
+
cls,
|
290
|
+
/,
|
291
|
+
client_id: str | PathLike = "",
|
292
|
+
client_secret: str = "",
|
293
|
+
token: None | str | PathLike = None,
|
294
|
+
refresh_token: str = "",
|
295
|
+
check_for_relogin: bool = True,
|
296
|
+
instance: None | Self = None,
|
297
|
+
*,
|
298
|
+
async_: Literal[False, True] = False,
|
299
|
+
**request_kwargs,
|
300
|
+
) -> P123OpenClient | Coroutine[Any, Any, P123OpenClient]:
|
301
|
+
def gen_step():
|
302
|
+
nonlocal token
|
303
|
+
if instance is None:
|
304
|
+
self = cls.__new__(cls)
|
305
|
+
else:
|
306
|
+
self = instance
|
307
|
+
if isinstance(client_id, PathLike):
|
308
|
+
token = client_id
|
309
|
+
else:
|
310
|
+
self.client_id = client_id
|
311
|
+
self.client_secret = client_secret
|
312
|
+
self.refresh_token = refresh_token
|
313
|
+
if token is None:
|
314
|
+
if client_id and client_secret or refresh_token:
|
315
|
+
yield self.login_open(async_=async_, **request_kwargs)
|
316
|
+
elif isinstance(token, str):
|
317
|
+
self.token = token.removeprefix("Bearer ")
|
318
|
+
else:
|
319
|
+
if isinstance(token, PurePath) and hasattr(token, "open"):
|
320
|
+
self.token_path = token
|
321
|
+
else:
|
322
|
+
self.token_path = Path(fsdecode(token))
|
323
|
+
self._read_token()
|
324
|
+
if not self.token and (client_id and client_secret or refresh_token):
|
325
|
+
yield self.login_open(async_=async_, **request_kwargs)
|
326
|
+
self.check_for_relogin = check_for_relogin
|
327
|
+
return self
|
328
|
+
return run_gen_step(gen_step, async_)
|
329
|
+
|
330
|
+
@locked_cacheproperty
|
331
|
+
def request_lock(self, /) -> Lock:
|
332
|
+
return Lock()
|
333
|
+
|
334
|
+
@locked_cacheproperty
|
335
|
+
def request_alock(self, /) -> AsyncLock:
|
336
|
+
return AsyncLock()
|
337
|
+
|
255
338
|
@property
|
256
339
|
def cookies(self, /):
|
257
340
|
"""请求所用的 Cookies 对象(同步和异步共用)
|
@@ -380,6 +463,12 @@ class P123OpenClient:
|
|
380
463
|
self.__dict__.pop("session", None)
|
381
464
|
self.__dict__.pop("async_session", None)
|
382
465
|
|
466
|
+
def can_relogin(self, /) -> bool:
|
467
|
+
return self.check_for_relogin and bool(
|
468
|
+
self.client_id and self.client_secret or
|
469
|
+
getattr(self, "refresh_token")
|
470
|
+
)
|
471
|
+
|
383
472
|
def request(
|
384
473
|
self,
|
385
474
|
/,
|
@@ -397,22 +486,57 @@ class P123OpenClient:
|
|
397
486
|
request_kwargs.setdefault("parse", default_parse)
|
398
487
|
if request is None:
|
399
488
|
request_kwargs["session"] = self.async_session if async_ else self.session
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
489
|
+
request_kwargs["async_"] = async_
|
490
|
+
request = get_default_request()
|
491
|
+
if self.can_relogin():
|
492
|
+
headers = dict(self.headers)
|
493
|
+
if request_headers := request_kwargs.get("headers"):
|
494
|
+
headers.update(request_headers)
|
495
|
+
headers.setdefault("authorization", "")
|
496
|
+
request_kwargs["headers"] = headers
|
406
497
|
else:
|
407
|
-
if headers := request_kwargs.get("headers"):
|
408
|
-
request_kwargs["headers"] = {**self.headers, **headers}
|
409
|
-
else:
|
410
|
-
request_kwargs["headers"] = self.headers
|
411
498
|
return request(
|
412
499
|
url=url,
|
413
500
|
method=method,
|
414
501
|
**request_kwargs,
|
415
502
|
)
|
503
|
+
def gen_step():
|
504
|
+
if async_:
|
505
|
+
lock: Lock | AsyncLock = self.request_alock
|
506
|
+
else:
|
507
|
+
lock = self.request_lock
|
508
|
+
headers = request_kwargs["headers"]
|
509
|
+
if "authorization" not in headers:
|
510
|
+
headers["authorization"] = "Bearer " + self.token
|
511
|
+
for i in count(0):
|
512
|
+
token = headers["authorization"].removeprefix("Bearer ")
|
513
|
+
resp = yield cast(Callable, request)(
|
514
|
+
url=url,
|
515
|
+
method=method,
|
516
|
+
**request_kwargs,
|
517
|
+
)
|
518
|
+
if not (isinstance(resp, dict) and resp.get("code") == 401):
|
519
|
+
return resp
|
520
|
+
yield lock.acquire()
|
521
|
+
try:
|
522
|
+
token_new: str = self.token
|
523
|
+
if token == token_new:
|
524
|
+
if self.__dict__.get("token_path"):
|
525
|
+
token_new = self._read_token() or ""
|
526
|
+
if token != token_new:
|
527
|
+
headers["authorization"] = "Bearer " + self.token
|
528
|
+
continue
|
529
|
+
if i:
|
530
|
+
raise
|
531
|
+
user_id = getattr(self, "user_id", None)
|
532
|
+
warn(f"relogin to refresh token: {user_id=}", category=P123Warning)
|
533
|
+
yield self.login(replace=True, async_=async_)
|
534
|
+
headers["authorization"] = "Bearer " + self.token
|
535
|
+
else:
|
536
|
+
headers["authorization"] = "Bearer " + token_new
|
537
|
+
finally:
|
538
|
+
lock.release()
|
539
|
+
return run_gen_step(gen_step, async_)
|
416
540
|
|
417
541
|
@overload
|
418
542
|
def login(
|
@@ -5549,52 +5673,132 @@ class P123Client(P123OpenClient):
|
|
5549
5673
|
client_id: str = "",
|
5550
5674
|
client_secret: str = "",
|
5551
5675
|
refresh_token: str = "",
|
5676
|
+
check_for_relogin: bool = True,
|
5552
5677
|
):
|
5553
|
-
|
5554
|
-
|
5555
|
-
|
5556
|
-
|
5557
|
-
|
5558
|
-
|
5559
|
-
|
5560
|
-
|
5561
|
-
|
5562
|
-
|
5563
|
-
|
5564
|
-
|
5565
|
-
|
5566
|
-
|
5567
|
-
|
5568
|
-
|
5569
|
-
|
5570
|
-
|
5571
|
-
|
5572
|
-
|
5573
|
-
|
5574
|
-
|
5575
|
-
|
5576
|
-
|
5577
|
-
|
5578
|
-
|
5579
|
-
|
5580
|
-
|
5581
|
-
|
5582
|
-
|
5583
|
-
|
5584
|
-
|
5585
|
-
|
5586
|
-
|
5587
|
-
|
5588
|
-
|
5589
|
-
|
5590
|
-
|
5678
|
+
self.init(
|
5679
|
+
passport=passport,
|
5680
|
+
password=password,
|
5681
|
+
token=token,
|
5682
|
+
client_id=client_id,
|
5683
|
+
client_secret=client_secret,
|
5684
|
+
refresh_token=refresh_token,
|
5685
|
+
check_for_relogin=check_for_relogin,
|
5686
|
+
instance=self,
|
5687
|
+
)
|
5688
|
+
|
5689
|
+
@overload # type: ignore
|
5690
|
+
@classmethod
|
5691
|
+
def init(
|
5692
|
+
cls,
|
5693
|
+
/,
|
5694
|
+
passport: int | str | PathLike = "",
|
5695
|
+
password: str = "",
|
5696
|
+
token: None | str | PathLike = None,
|
5697
|
+
client_id: str = "",
|
5698
|
+
client_secret: str = "",
|
5699
|
+
refresh_token: str = "",
|
5700
|
+
check_for_relogin: bool = True,
|
5701
|
+
instance: None | Self = None,
|
5702
|
+
*,
|
5703
|
+
async_: Literal[False] = False,
|
5704
|
+
**request_kwargs,
|
5705
|
+
) -> P123Client:
|
5706
|
+
...
|
5707
|
+
@overload
|
5708
|
+
@classmethod
|
5709
|
+
def init(
|
5710
|
+
cls,
|
5711
|
+
/,
|
5712
|
+
passport: int | str | PathLike = "",
|
5713
|
+
password: str = "",
|
5714
|
+
token: None | str | PathLike = None,
|
5715
|
+
client_id: str = "",
|
5716
|
+
client_secret: str = "",
|
5717
|
+
refresh_token: str = "",
|
5718
|
+
check_for_relogin: bool = True,
|
5719
|
+
instance: None | Self = None,
|
5720
|
+
*,
|
5721
|
+
async_: Literal[True],
|
5722
|
+
**request_kwargs,
|
5723
|
+
) -> Coroutine[Any, Any, P123Client]:
|
5724
|
+
...
|
5725
|
+
@classmethod
|
5726
|
+
def init(
|
5727
|
+
cls,
|
5728
|
+
/,
|
5729
|
+
passport: int | str | PathLike = "",
|
5730
|
+
password: str = "",
|
5731
|
+
token: None | str | PathLike = None,
|
5732
|
+
client_id: str = "",
|
5733
|
+
client_secret: str = "",
|
5734
|
+
refresh_token: str = "",
|
5735
|
+
check_for_relogin: bool = True,
|
5736
|
+
instance: None | Self = None,
|
5737
|
+
*,
|
5738
|
+
async_: Literal[False, True] = False,
|
5739
|
+
**request_kwargs,
|
5740
|
+
) -> P123Client | Coroutine[Any, Any, P123Client]:
|
5741
|
+
def gen_step():
|
5742
|
+
nonlocal token, refresh_token, client_id, client_secret
|
5743
|
+
if instance is None:
|
5744
|
+
self = cls.__new__(cls)
|
5745
|
+
else:
|
5746
|
+
self = instance
|
5747
|
+
if (isinstance(passport, PathLike) or
|
5748
|
+
not token and
|
5749
|
+
isinstance(passport, str) and
|
5750
|
+
len(passport) >= 128
|
5751
|
+
):
|
5752
|
+
token = passport
|
5753
|
+
elif (not refresh_token and
|
5754
|
+
isinstance(passport, str) and
|
5755
|
+
len(passport) >= 48 and
|
5756
|
+
not passport.strip(digits+ascii_uppercase)
|
5757
|
+
):
|
5758
|
+
refresh_token = passport
|
5759
|
+
elif (not client_id and
|
5760
|
+
isinstance(passport, str) and
|
5761
|
+
len(passport) >= 32 and
|
5762
|
+
not passport.strip(digits+"abcdef")
|
5763
|
+
):
|
5764
|
+
client_id = passport
|
5765
|
+
else:
|
5766
|
+
self.passport = passport
|
5767
|
+
if (not client_secret and
|
5768
|
+
isinstance(password, str)
|
5769
|
+
and len(password) >= 32 and
|
5770
|
+
not password.strip(digits+"abcdef")
|
5771
|
+
):
|
5772
|
+
client_secret = password
|
5773
|
+
else:
|
5774
|
+
self.password = password
|
5775
|
+
self.client_id = client_id
|
5776
|
+
self.client_secret = client_secret
|
5777
|
+
self.refresh_token = refresh_token
|
5778
|
+
if token is None:
|
5779
|
+
yield self.login(async_=async_, **request_kwargs)
|
5780
|
+
elif isinstance(token, str):
|
5781
|
+
self.token = token.removeprefix("Bearer ")
|
5591
5782
|
else:
|
5592
|
-
|
5593
|
-
|
5594
|
-
|
5595
|
-
|
5596
|
-
|
5597
|
-
|
5783
|
+
if isinstance(token, PurePath) and hasattr(token, "open"):
|
5784
|
+
self.token_path = token
|
5785
|
+
else:
|
5786
|
+
self.token_path = Path(fsdecode(token))
|
5787
|
+
self._read_token()
|
5788
|
+
if not self.token:
|
5789
|
+
yield self.login(async_=async_, **request_kwargs)
|
5790
|
+
if not self.passport:
|
5791
|
+
self.passport = self.token_user_info["username"]
|
5792
|
+
self.check_for_relogin = check_for_relogin
|
5793
|
+
return self
|
5794
|
+
return run_gen_step(gen_step, async_)
|
5795
|
+
|
5796
|
+
def can_relogin(self, /) -> bool:
|
5797
|
+
return self.check_for_relogin and bool(
|
5798
|
+
self.passport and self.password or
|
5799
|
+
self.client_id and self.client_secret or
|
5800
|
+
getattr(self, "refresh_token")
|
5801
|
+
)
|
5598
5802
|
|
5599
5803
|
@overload # type: ignore
|
5600
5804
|
def login(
|
@@ -8476,7 +8680,7 @@ class P123Client(P123OpenClient):
|
|
8476
8680
|
@overload
|
8477
8681
|
def offline_task_list(
|
8478
8682
|
self,
|
8479
|
-
payload: dict | int = 1,
|
8683
|
+
payload: dict | int | list[int] | tuple[int] = 1,
|
8480
8684
|
/,
|
8481
8685
|
base_url: str | Callable[[], str] = DEFAULT_BASE_URL,
|
8482
8686
|
*,
|
@@ -8487,7 +8691,7 @@ class P123Client(P123OpenClient):
|
|
8487
8691
|
@overload
|
8488
8692
|
def offline_task_list(
|
8489
8693
|
self,
|
8490
|
-
payload: dict | int = 1,
|
8694
|
+
payload: dict | int | list[int] | tuple[int] = 1,
|
8491
8695
|
/,
|
8492
8696
|
base_url: str | Callable[[], str] = DEFAULT_BASE_URL,
|
8493
8697
|
*,
|
@@ -8497,7 +8701,7 @@ class P123Client(P123OpenClient):
|
|
8497
8701
|
...
|
8498
8702
|
def offline_task_list(
|
8499
8703
|
self,
|
8500
|
-
payload: dict | int = 1,
|
8704
|
+
payload: dict | int | list[int] | tuple[int] = 1,
|
8501
8705
|
/,
|
8502
8706
|
base_url: str | Callable[[], str] = DEFAULT_BASE_URL,
|
8503
8707
|
*,
|
@@ -8514,9 +8718,10 @@ class P123Client(P123OpenClient):
|
|
8514
8718
|
- status_arr: list[ 0 | 1 | 2 | 3 | 4 ] = [0, 1, 2, 3, 4] 💡 状态列表:0:进行中 1:下载失败 2:下载成功 3:重试中
|
8515
8719
|
"""
|
8516
8720
|
if isinstance(payload, int):
|
8517
|
-
payload = {"current_page": payload
|
8518
|
-
|
8519
|
-
payload = {
|
8721
|
+
payload = {"current_page": payload}
|
8722
|
+
elif isinstance(payload, (list, tuple)):
|
8723
|
+
payload = { "status_arr": payload}
|
8724
|
+
payload = {"current_page": 1, "page_size": 100, "status_arr": [0, 1, 2, 3, 4], **payload}
|
8520
8725
|
return self.request(
|
8521
8726
|
"offline_download/task/list",
|
8522
8727
|
"POST",
|
@@ -10513,4 +10718,3 @@ with temp_globals():
|
|
10513
10718
|
except KeyError:
|
10514
10719
|
CLIENT_API_METHODS_MAP[api] = [name]
|
10515
10720
|
|
10516
|
-
# TODO: 实现 check_for_relogin 参数,当报错 401,则重新登录(如果用的是 client_id,账号密码 或 refresh_token),调用 client.login()
|
@@ -1,12 +1,12 @@
|
|
1
1
|
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
2
2
|
p123client/__init__.py,sha256=lcTiBpyNHH1HN4f1ueacumxkaahBNOUyvNA172UtSOU,214
|
3
|
-
p123client/client.py,sha256=
|
3
|
+
p123client/client.py,sha256=GLVJPwLFUhH043q4GXOSD8nqIJHhazYY4gNCUy3Hi_o,357375
|
4
4
|
p123client/const.py,sha256=UYfBrgciCz7RJJPqCX6zwWlMoWiaTUwUU8bpFJyh0sM,348
|
5
5
|
p123client/exception.py,sha256=Vn2UlJulY63z3cF7bnTjMlZ6og7gCw1pAA-dswFwKpQ,3174
|
6
6
|
p123client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
p123client/tool/__init__.py,sha256=hnOwkk1CKk1CLGlhl6hlUl3BWNdqXdy8sWKXAGmOXHE,18812
|
8
8
|
p123client/type.py,sha256=T17OzPQrnIG6w_Hzjc8TF_fFMKa-hQMSn1gff8pVcBc,56
|
9
|
-
p123client-0.0.7.
|
10
|
-
p123client-0.0.7.
|
11
|
-
p123client-0.0.7.
|
12
|
-
p123client-0.0.7.
|
9
|
+
p123client-0.0.7.2.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
10
|
+
p123client-0.0.7.2.dist-info/METADATA,sha256=RwvkD4bZ_ma7fJ7ODAPJg54hOvsC2nYcc765nFObY54,14657
|
11
|
+
p123client-0.0.7.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
12
|
+
p123client-0.0.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|