p115client 0.0.5.9.3__tar.gz → 0.0.5.10.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/PKG-INFO +4 -4
  2. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/client.py +180 -94
  3. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/const.py +190 -1
  4. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/exception.py +42 -18
  5. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/__init__.py +2 -0
  6. p115client-0.0.5.10.1/p115client/tool/attr.py +104 -0
  7. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/download.py +7 -22
  8. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/edit.py +1 -2
  9. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/fs_files.py +4 -13
  10. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/iterdir.py +56 -101
  11. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/life.py +1 -1
  12. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/pool.py +6 -29
  13. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/request.py +1 -0
  14. p115client-0.0.5.10.1/p115client/tool/util.py +107 -0
  15. p115client-0.0.5.10.1/p115client/tool/xys.py +392 -0
  16. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/type.py +16 -1
  17. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/pyproject.toml +2 -2
  18. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/readme.md +2 -2
  19. p115client-0.0.5.9.3/p115client/tool/xys.py +0 -125
  20. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/LICENSE +0 -0
  21. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/__init__.py +0 -0
  22. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/_upload.py +0 -0
  23. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/py.typed +0 -0
  24. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/export_dir.py +0 -0
  25. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/history.py +0 -0
  26. {p115client-0.0.5.9.3 → p115client-0.0.5.10.1}/p115client/tool/upload.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p115client
3
- Version: 0.0.5.9.3
3
+ Version: 0.0.5.10.1
4
4
  Summary: Python 115 webdisk client.
5
5
  Home-page: https://github.com/ChenyangGao/p115client
6
6
  License: MIT
@@ -40,7 +40,7 @@ Requires-Dist: python-filewrap (>=0.2.8)
40
40
  Requires-Dist: python-hashtools (>=0.0.3.3)
41
41
  Requires-Dist: python-http_request (>=0.0.6)
42
42
  Requires-Dist: python-httpfile (>=0.0.5.2)
43
- Requires-Dist: python-iterutils (>=0.1.10)
43
+ Requires-Dist: python-iterutils (>=0.1.11)
44
44
  Requires-Dist: python-property (>=0.0.3)
45
45
  Requires-Dist: python-startfile (>=0.0.2)
46
46
  Requires-Dist: python-undefined (>=0.0.3)
@@ -108,7 +108,7 @@ client = P115Client(Path("~/115-cookies.txt").expanduser(), check_for_relogin=Tr
108
108
 
109
109
  如果你有一个申请通过的开放接口的应用,则可以创建开放接口的客户端实例
110
110
 
111
- 你可以直接从一个 `P115Client` 实例拿到授权(自动扫码登录并授权)
111
+ 你可以直接从一个 `P115Client` 实例拿到授权(自动扫码登录并授权),如果不提供 `app_id`,则使用默认值
112
112
 
113
113
  ```python
114
114
  app_id = <开放接口应用的 AppID>
@@ -143,7 +143,7 @@ from p115client import P115Client, P115OpenClient
143
143
  from pathlib import Path
144
144
 
145
145
  client = P115Client(Path("~/115-cookies.txt").expanduser(), check_for_relogin=True)
146
- client_open = client.login_another_open(app_id)
146
+ client_open = client.login_another_open()
147
147
  ```
148
148
 
149
149
  ### 2. 接口调用
@@ -16,23 +16,20 @@ from collections.abc import (
16
16
  ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence,
17
17
  )
18
18
  from datetime import date, datetime, timedelta
19
- from errno import EBUSY, EEXIST, EFBIG, EINVAL, EIO, EISDIR, ENODATA, ENOENT, ENOSPC, ENOSYS, ENOTSUP
20
19
  from functools import partial
21
20
  from hashlib import md5, sha1
22
21
  from http.cookiejar import Cookie, CookieJar
23
22
  from http.cookies import Morsel
24
23
  from inspect import isawaitable
25
- from itertools import count, cycle, dropwhile, product, repeat
24
+ from itertools import count, cycle, product, repeat
26
25
  from math import nan
27
26
  from operator import itemgetter
28
- from os import fsdecode, fstat, isatty, stat, PathLike, path as ospath
27
+ from os import fsdecode, fstat, isatty, PathLike, path as ospath
29
28
  from pathlib import Path, PurePath
30
29
  from platform import system
31
30
  from posixpath import splitext
32
31
  from re import compile as re_compile, MULTILINE
33
32
  from string import digits
34
- from sys import exc_info
35
- from _thread import start_new_thread
36
33
  from tempfile import TemporaryFile
37
34
  from threading import Lock
38
35
  from time import time
@@ -57,17 +54,20 @@ from http_response import get_total_length
57
54
  from httpfile import HTTPFileReader, AsyncHTTPFileReader
58
55
  from iterutils import run_gen_step
59
56
  from orjson import dumps, loads
60
- from p115cipher.fast import rsa_encode, rsa_decode, ecdh_encode_token, ecdh_aes_encode, ecdh_aes_decode, make_upload_payload
57
+ from p115cipher.fast import (
58
+ rsa_encode, rsa_decode, ecdh_encode_token, ecdh_aes_encode, ecdh_aes_decode, make_upload_payload,
59
+ )
61
60
  from property import locked_cacheproperty
62
61
  from re import compile as re_compile
63
62
  from startfile import startfile, startfile_async # type: ignore
64
63
  from undefined import undefined
65
64
  from yarl import URL
66
65
 
67
- from .const import CLASS_TO_TYPE, CLIENT_API_MAP, SSOENT_TO_APP, SUFFIX_TO_TYPE
66
+ from .const import CLASS_TO_TYPE, CLIENT_API_MAP, SSOENT_TO_APP, SUFFIX_TO_TYPE, errno
68
67
  from .exception import (
69
68
  AuthenticationError, BusyOSError, DataError, LoginError, NotSupportedError,
70
- P115OSError, OperationalError, P115Warning,
69
+ P115OSError, OperationalError, P115Warning, P115FileExistsError,
70
+ P115FileNotFoundError, P115IsADirectoryError,
71
71
  )
72
72
  from .type import RequestKeywords, MultipartResumeData, P115Cookies, P115URL
73
73
  from ._upload import buffer_length, make_dataiter, oss_upload, oss_multipart_upload
@@ -229,7 +229,7 @@ def json_loads(content: Buffer, /):
229
229
  except Exception as e:
230
230
  if isinstance(content, memoryview):
231
231
  content = content.tobytes()
232
- raise DataError(ENODATA, content) from e
232
+ raise DataError(errno.ENODATA, content) from e
233
233
 
234
234
 
235
235
  def default_parse(resp, content: Buffer, /):
@@ -350,7 +350,7 @@ def check_response(resp: dict | Awaitable[dict], /) -> dict | Coroutine[Any, Any
350
350
  """
351
351
  def check(resp, /) -> dict:
352
352
  if not isinstance(resp, dict):
353
- raise P115OSError(EIO, resp)
353
+ raise P115OSError(errno.EIO, resp)
354
354
  if resp.get("state", True):
355
355
  return resp
356
356
  if code := get_first(resp, "errno", "errNo", "errcode", "errCode", "code"):
@@ -360,121 +360,206 @@ def check_response(resp: dict | Awaitable[dict], /) -> dict | Coroutine[Any, Any
360
360
  match code:
361
361
  # {"state": false, "errno": 99, "error": "请重新登录"}
362
362
  case 99:
363
- raise LoginError(EIO, resp)
363
+ raise LoginError(errno.EAUTH, resp)
364
364
  # {"state": false, "errno": 911, "error": "请验证账号"}
365
365
  case 911:
366
- raise AuthenticationError(EIO, resp)
366
+ raise AuthenticationError(errno.EAUTH, resp)
367
367
  # {"state": false, "errno": 20001, "error": "目录名称不能为空"}
368
368
  case 20001:
369
- raise OperationalError(EINVAL, resp)
369
+ raise OperationalError(errno.EINVAL, resp)
370
370
  # {"state": false, "errno": 20004, "error": "该目录名称已存在。"}
371
371
  case 20004:
372
- raise FileExistsError(EEXIST, resp)
372
+ raise P115FileExistsError(errno.EEXIST, resp)
373
373
  # {"state": false, "errno": 20009, "error": "父目录不存在。"}
374
374
  case 20009:
375
- raise FileNotFoundError(ENOENT, resp)
375
+ raise P115FileNotFoundError(errno.ENOENT, resp)
376
376
  # {"state": false, "errno": 20018, "error": "文件不存在或已删除。"}
377
377
  # {"state": false, "errno": 50015, "error": "文件不存在或已删除。"}
378
378
  # {"state": false, "errno": 90008, "error": "文件(夹)不存在或已经删除。"}
379
379
  # {"state": false, "errno": 430004, "error": "文件(夹)不存在或已删除。"}
380
380
  case 20018 | 50015 | 90008 | 430004:
381
- raise FileNotFoundError(ENOENT, resp)
381
+ raise P115FileNotFoundError(errno.ENOENT, resp)
382
382
  # {"state": false, "errno": 20020, "error": "后缀名不正确,请重新输入"}
383
- case 20020:
384
- raise OperationalError(ENOTSUP, resp)
385
383
  # {"state": false, "errno": 20021, "error": "后缀名不正确,请重新输入"}
386
- case 20021:
387
- raise OperationalError(ENOTSUP, resp)
384
+ case 20020 | 20021:
385
+ raise OperationalError(errno.EINVAL, resp)
388
386
  # {"state": false, "errno": 31001, "error": "所预览的文件不存在。"}
389
387
  case 31001:
390
- raise FileNotFoundError(ENOENT, resp)
388
+ raise P115FileNotFoundError(errno.ENOENT, resp)
391
389
  # {"state": false, "errno": 31004, "error": "文档未上传完整,请上传完成后再进行查看。"}
392
390
  case 31004:
393
- raise FileNotFoundError(ENOENT, resp)
391
+ raise P115FileNotFoundError(errno.ENOENT, resp)
394
392
  # {"state": false, "errno": 50003, "error": "很抱歉,该文件提取码不存在。"}
395
393
  case 50003:
396
- raise FileNotFoundError(ENOENT, resp)
394
+ raise P115FileNotFoundError(errno.ENOENT, resp)
397
395
  # {"state": false, "errno": 91002, "error": "不能将文件复制到自身或其子目录下。"}
398
396
  case 91002:
399
- raise NotSupportedError(ENOTSUP, resp)
397
+ raise NotSupportedError(errno.ENOTSUP, resp)
400
398
  # {"state": false, "errno": 91004, "error": "操作的文件(夹)数量超过5万个"}
401
399
  case 91004:
402
- raise NotSupportedError(ENOTSUP, resp)
400
+ raise NotSupportedError(errno.ENOTSUP, resp)
403
401
  # {"state": false, "errno": 91005, "error": "空间不足,复制失败。"}
404
402
  case 91005:
405
- raise OperationalError(ENOSPC, resp)
403
+ raise OperationalError(errno.ENOSPC, resp)
406
404
  # {"state": false, "errno": 231011, "error": "文件已删除,请勿重复操作"}
407
405
  case 231011:
408
- raise FileNotFoundError(ENOENT, resp)
406
+ raise P115FileNotFoundError(errno.ENOENT, resp)
409
407
  # {"state": false, "errno": 300104, "error": "文件超过200MB,暂不支持播放"}
410
408
  case 300104:
411
- raise P115OSError(EFBIG, resp)
409
+ raise P115OSError(errno.EFBIG, resp)
412
410
  # {"state": false, "errno": 590075, "error": "操作太频繁,请稍候再试"}
413
411
  case 590075:
414
- raise BusyOSError(EBUSY, resp)
412
+ raise BusyOSError(errno.EBUSY, resp)
415
413
  # {"state": false, "errno": 800001, "error": "目录不存在。"}
416
414
  case 800001:
417
- raise FileNotFoundError(ENOENT, resp)
415
+ raise P115FileNotFoundError(errno.ENOENT, resp)
418
416
  # {"state": false, "errno": 980006, "error": "404 Not Found"}
419
417
  case 980006:
420
- raise NotSupportedError(ENOSYS, resp)
418
+ raise NotSupportedError(errno.ENOSYS, resp)
421
419
  # {"state": false, "errno": 990001, "error": "登陆超时,请重新登陆。"}
422
420
  case 990001:
423
421
  # NOTE: 可能就是被下线了
424
- raise AuthenticationError(EIO, resp)
422
+ raise AuthenticationError(errno.EAUTH, resp)
425
423
  # {"state": false, "errno": 990002, "error": "参数错误。"}
426
424
  case 990002:
427
- raise P115OSError(EINVAL, resp)
425
+ raise OperationalError(errno.EINVAL, resp)
428
426
  # {"state": false, "errno": 990003, "error": "操作失败。"}
429
427
  case 990003:
430
- raise OperationalError(EIO, resp)
428
+ raise OperationalError(errno.EIO, resp)
431
429
  # {"state": false, "errno": 990005, "error": "你的账号有类似任务正在处理,请稍后再试!"}
432
430
  case 990005:
433
- raise BusyOSError(EBUSY, resp)
431
+ raise BusyOSError(errno.EBUSY, resp)
434
432
  # {"state": false, "errno": 990009, "error": "删除[...]操作尚未执行完成,请稍后再试!"}
435
433
  # {"state": false, "errno": 990009, "error": "还原[...]操作尚未执行完成,请稍后再试!"}
436
434
  # {"state": false, "errno": 990009, "error": "复制[...]操作尚未执行完成,请稍后再试!"}
437
435
  # {"state": false, "errno": 990009, "error": "移动[...]操作尚未执行完成,请稍后再试!"}
438
436
  case 990009:
439
- raise BusyOSError(EBUSY, resp)
437
+ raise BusyOSError(errno.EBUSY, resp)
440
438
  # {"state": false, "errno": 990023, "error": "操作的文件(夹)数量超过5万个"}
441
439
  case 990023:
442
- raise OperationalError(ENOTSUP, resp)
440
+ raise OperationalError(errno.ENOTSUP, resp)
443
441
  # {"state": 0, "errno": 40100000, "error": "参数错误!"}
442
+ # {"state": 0, "errno": 40100000, "error": "参数缺失"}
444
443
  case 40100000:
445
- raise OperationalError(EINVAL, resp)
444
+ raise OperationalError(errno.EINVAL, resp)
446
445
  # {"state": 0, "errno": 40101004, "error": "IP登录异常,请稍候再登录!"}
447
446
  case 40101004:
448
- raise LoginError(EIO, resp)
447
+ raise LoginError(errno.EAUTH, resp)
449
448
  # {"state": 0, "errno": 40101017, "error": "用户验证失败!"}
450
449
  case 40101017:
451
- raise AuthenticationError(EIO, resp)
450
+ raise AuthenticationError(errno.EAUTH, resp)
452
451
  # {"state": 0, "errno": 40101032, "error": "请重新登录"}
453
452
  case 40101032:
454
- raise LoginError(EIO, resp)
453
+ raise LoginError(errno.EAUTH, resp)
454
+ #################################################################
455
+ # Reference: https://www.yuque.com/115yun/open/rnq0cbz8tt7cu43i #
456
+ #################################################################
457
+ # {"state": 0, "errno": 40110000, "error": "请求异常需要重试"}
458
+ case 40110000:
459
+ raise OperationalError(errno.EAGAIN, resp)
460
+ # {"state": 0, "errno": 40140100, "error": "client_id 错误"}
461
+ case 40140100:
462
+ raise OperationalError(errno.EINVAL, resp)
463
+ # {"state": 0, "errno": 40140101, "error": "code_challenge 必填"}
464
+ case 40140101:
465
+ raise OperationalError(errno.EINVAL, resp)
466
+ # {"state": 0, "errno": 40140102, "error": "code_challenge_method 必须是 sha256、sha1、md5 之一"}
467
+ case 40140102:
468
+ raise OperationalError(errno.EINVAL, resp)
469
+ # {"state": 0, "errno": 40140103, "error": "sign 必填"}
470
+ case 40140103:
471
+ raise OperationalError(errno.EINVAL, resp)
472
+ # {"state": 0, "errno": 40140104, "error": "sign 签名失败"}
473
+ case 40140104:
474
+ raise OperationalError(errno.EINVAL, resp)
475
+ # {"state": 0, "errno": 40140105, "error": "生成二维码失败"}
476
+ case 40140105:
477
+ raise OperationalError(errno.EIO, resp)
478
+ # {"state": 0, "errno": 40140106, "error": "APP ID 无效"}
479
+ case 40140106:
480
+ raise OperationalError(errno.EINVAL, resp)
481
+ # {"state": 0, "errno": 40140107, "error": "应用不存在"}
482
+ case 40140107:
483
+ raise OperationalError(errno.EINVAL, resp)
484
+ # {"state": 0, "errno": 40140108, "error": "应用未审核通过"}
485
+ case 40140108:
486
+ raise OperationalError(errno.EINVAL, resp)
487
+ # {"state": 0, "errno": 40140109, "error": "应用已被停用"}
488
+ case 40140109:
489
+ raise OperationalError(errno.EINVAL, resp)
490
+ # {"state": 0, "errno": 40140110, "error": "应用已过期"}
491
+ case 40140110:
492
+ raise OperationalError(errno.EINVAL, resp)
493
+ # {"state": 0, "errno": 40140111, "error": "APP Secret 错误"}
494
+ case 40140111:
495
+ raise OperationalError(errno.EINVAL, resp)
496
+ # {"state": 0, "errno": 40140112, "error": "code_verifier 长度要求43~128位"}
497
+ case 40140112:
498
+ raise OperationalError(errno.EINVAL, resp)
499
+ # {"state": 0, "errno": 40140113, "error": "code_verifier 验证失败"}
500
+ case 40140113:
501
+ raise OperationalError(errno.EINVAL, resp)
502
+ # {"state": 0, "errno": 40140114, "error": "refresh_token 格式错误(防篡改)"}
503
+ case 40140114:
504
+ raise OperationalError(errno.EINVAL, resp)
505
+ # {"state": 0, "errno": 40140115, "error": "refresh_token 签名校验失败(防篡改)"}
506
+ case 40140115:
507
+ raise OperationalError(errno.EINVAL, resp)
508
+ # {"state": 0, "errno": 40140116, "error": "refresh_token 无效(已解除授权)"}
509
+ case 40140116:
510
+ raise OperationalError(errno.EIO, resp)
511
+ # {"state": 0, "errno": 40140117, "error": "access_token 刷新太频繁"}
512
+ case 40140117:
513
+ raise BusyOSError(errno.EBUSY, resp)
514
+ # {"state": 0, "errno": 40140118, "error": "开发者认证已过期"}
515
+ case 40140118:
516
+ raise OperationalError(errno.EINVAL, resp)
517
+ # {"state": 0, "errno": 40140119, "error": "refresh_token 已过期"}
518
+ case 40140119:
519
+ raise OperationalError(errno.EINVAL, resp)
520
+ # {"state": 0, "errno": 40140120, "error": "refresh_token 检验失败(防篡改)"}
521
+ case 40140120:
522
+ raise OperationalError(errno.EINVAL, resp)
523
+ # {"state": 0, "errno": 40140121, "error": "access_token 刷新失败"}
524
+ case 40140121:
525
+ raise OperationalError(errno.EINVAL, resp)
526
+ # {"state": 0, "errno": 40140122, "error": "超出授权应用个数上限"}
527
+ case 40140122:
528
+ raise OperationalError(errno.EINVAL, resp)
529
+ # {"state": 0, "errno": 40140123, "error": "access_token 格式错误(防篡改)"}
530
+ case 40140123:
531
+ raise OperationalError(errno.EINVAL, resp)
532
+ # {"state": 0, "errno": 40140124, "error": "access_token 签名校验失败(防篡改)"}
533
+ case 40140124:
534
+ raise OperationalError(errno.EINVAL, resp)
535
+ # {"state": 0, "errno": 40140125, "error": "access_token 无效(已过期或者已解除授权)"}
536
+ case 40140125:
537
+ raise OperationalError(errno.EINVAL, resp)
538
+ # {"state": 0, "errno": 40140126, "error": "access_token 校验失败(防篡改)"}
539
+ case 40140126:
540
+ raise OperationalError(errno.EINVAL, resp)
455
541
  elif "msg_code" in resp:
456
542
  match resp["msg_code"]:
457
543
  case 50028:
458
- raise P115OSError(EFBIG, resp)
544
+ raise P115OSError(errno.EFBIG, resp)
459
545
  case 70004:
460
- raise IsADirectoryError(EISDIR, resp)
546
+ raise P115IsADirectoryError(errno.EISDIR, resp)
461
547
  case 70005 | 70008:
462
- raise FileNotFoundError(ENOENT, resp)
548
+ raise P115FileNotFoundError(errno.ENOENT, resp)
463
549
  elif "error" in resp:
464
550
  match resp["error"]:
465
551
  case "目录不存在或已转移":
466
- raise FileNotFoundError(ENOENT, resp)
552
+ raise P115FileNotFoundError(errno.ENOENT, resp)
467
553
  case "更新的数据为空":
468
- raise OperationalError(EINVAL, resp)
469
- raise P115OSError(EIO, resp)
554
+ raise OperationalError(errno.EINVAL, resp)
555
+ raise P115OSError(errno.EIO, resp)
470
556
  if isinstance(resp, dict):
471
557
  return check(resp)
472
558
  elif isawaitable(resp):
473
559
  async def check_await() -> dict:
474
560
  return check(await resp)
475
561
  return check_await()
476
- else:
477
- raise P115OSError(EIO, resp)
562
+ raise P115OSError(errno.EIO, resp)
478
563
 
479
564
 
480
565
  def normalize_attr_web(
@@ -1278,7 +1363,7 @@ class ClientRequestMixin:
1278
1363
 
1279
1364
  POST https://qrcodeapi.115.com/open/deviceCodeToToken
1280
1365
 
1281
- .. note::
1366
+ .. admonition:: Reference
1282
1367
  https://www.yuque.com/115yun/open/shtpzfhewv5nag11#QCCVQ
1283
1368
 
1284
1369
  :payload:
@@ -1329,7 +1414,7 @@ class ClientRequestMixin:
1329
1414
 
1330
1415
  POST https://qrcodeapi.115.com/open/refreshToken
1331
1416
 
1332
- .. note::
1417
+ .. admonition:: Reference
1333
1418
  https://www.yuque.com/115yun/open/shtpzfhewv5nag11#ve54x
1334
1419
 
1335
1420
  :payload:
@@ -1481,7 +1566,7 @@ class ClientRequestMixin:
1481
1566
 
1482
1567
  GET https://qrcodeapi.115.com/get/status/
1483
1568
 
1484
- .. note::
1569
+ .. admonition:: Reference
1485
1570
  https://www.yuque.com/115yun/open/shtpzfhewv5nag11#lAsp2
1486
1571
 
1487
1572
  :payload:
@@ -1567,9 +1652,10 @@ class ClientRequestMixin:
1567
1652
 
1568
1653
  POST https://qrcodeapi.115.com/open/authDeviceCode
1569
1654
 
1570
- .. note::
1655
+ .. admonition:: Reference
1571
1656
  https://www.yuque.com/115yun/open/shtpzfhewv5nag11#WzRhM
1572
1657
 
1658
+ .. note::
1573
1659
  code_challenge 默认用的字符串为 64 个 0,hash 算法为 md5
1574
1660
 
1575
1661
  :payload:
@@ -1767,11 +1853,11 @@ class ClientRequestMixin:
1767
1853
  print("[status=2] qrcode: signed in")
1768
1854
  break
1769
1855
  case -1:
1770
- raise LoginError(EIO, "[status=-1] qrcode: expired")
1856
+ raise LoginError(errno.EAUTH, "[status=-1] qrcode: expired")
1771
1857
  case -2:
1772
- raise LoginError(EIO, "[status=-2] qrcode: canceled")
1858
+ raise LoginError(errno.EAUTH, "[status=-2] qrcode: canceled")
1773
1859
  case _:
1774
- raise LoginError(EIO, f"qrcode: aborted with {resp!r}")
1860
+ raise LoginError(errno.EAUTH, f"qrcode: aborted with {resp!r}")
1775
1861
  if app:
1776
1862
  return cls.login_qrcode_scan_result(
1777
1863
  login_uid,
@@ -1865,11 +1951,11 @@ class ClientRequestMixin:
1865
1951
  print("[status=2] qrcode: signed in")
1866
1952
  break
1867
1953
  case -1:
1868
- raise LoginError(EIO, "[status=-1] qrcode: expired")
1954
+ raise LoginError(errno.EAUTH, "[status=-1] qrcode: expired")
1869
1955
  case -2:
1870
- raise LoginError(EIO, "[status=-2] qrcode: canceled")
1956
+ raise LoginError(errno.EAUTH, "[status=-2] qrcode: canceled")
1871
1957
  case _:
1872
- raise LoginError(EIO, f"qrcode: aborted with {resp!r}")
1958
+ raise LoginError(errno.EAUTH, f"qrcode: aborted with {resp!r}")
1873
1959
  return cls.login_qrcode_access_token_open(
1874
1960
  login_uid,
1875
1961
  async_=async_,
@@ -2377,7 +2463,7 @@ class ClientRequestMixin:
2377
2463
  class P115OpenClient(ClientRequestMixin):
2378
2464
  """115 的客户端对象
2379
2465
 
2380
- .. note::
2466
+ .. admonition:: Reference
2381
2467
  https://www.yuque.com/115yun/open
2382
2468
 
2383
2469
  :param app_id_or_refresh_token: 申请到的 AppID 或 refresh_token
@@ -2597,8 +2683,8 @@ class P115OpenClient(ClientRequestMixin):
2597
2683
  for fid, info in resp["data"].items():
2598
2684
  url = info["url"]
2599
2685
  if strict and not url:
2600
- raise IsADirectoryError(
2601
- EISDIR,
2686
+ raise P115IsADirectoryError(
2687
+ errno.EISDIR,
2602
2688
  f"{fid} is a directory, with response {resp}",
2603
2689
  )
2604
2690
  return P115URL(
@@ -2611,8 +2697,8 @@ class P115OpenClient(ClientRequestMixin):
2611
2697
  is_directory=not url,
2612
2698
  headers=resp["headers"],
2613
2699
  )
2614
- raise FileNotFoundError(
2615
- ENOENT,
2700
+ raise P115FileNotFoundError(
2701
+ errno.ENOENT,
2616
2702
  f"no such pickcode: {pickcode!r}, with response {resp}",
2617
2703
  )
2618
2704
  if async_:
@@ -2660,7 +2746,7 @@ class P115OpenClient(ClientRequestMixin):
2660
2746
  .. hint::
2661
2747
  相当于 `P115Client.download_url_app(app="chrome")`
2662
2748
 
2663
- .. note::
2749
+ .. admonition:: Reference
2664
2750
  https://www.yuque.com/115yun/open/um8whr91bxb5997o
2665
2751
 
2666
2752
  :payload:
@@ -2723,7 +2809,7 @@ class P115OpenClient(ClientRequestMixin):
2723
2809
 
2724
2810
  POST https://proapi.115.com/open/ufile/copy
2725
2811
 
2726
- .. note::
2812
+ .. admonition:: Reference
2727
2813
  https://www.yuque.com/115yun/open/lvas49ar94n47bbk
2728
2814
 
2729
2815
  :payload:
@@ -2779,7 +2865,7 @@ class P115OpenClient(ClientRequestMixin):
2779
2865
 
2780
2866
  POST https://proapi.115.com/open/ufile/delete
2781
2867
 
2782
- .. note::
2868
+ .. admonition:: Reference
2783
2869
  https://www.yuque.com/115yun/open/kt04fu8vcchd2fnb
2784
2870
 
2785
2871
  :payload:
@@ -2830,7 +2916,7 @@ class P115OpenClient(ClientRequestMixin):
2830
2916
  .. hint::
2831
2917
  相当于 `P115Client.fs_files_app`
2832
2918
 
2833
- .. note::
2919
+ .. admonition:: Reference
2834
2920
  https://www.yuque.com/115yun/open/kz9ft9a7s57ep868
2835
2921
 
2836
2922
  :payload:
@@ -2949,7 +3035,7 @@ class P115OpenClient(ClientRequestMixin):
2949
3035
  .. hint::
2950
3036
  相当于 `P115Client.fs_category_get_app`
2951
3037
 
2952
- .. note::
3038
+ .. admonition:: Reference
2953
3039
  https://www.yuque.com/115yun/open/rl8zrhe2nag21dfw
2954
3040
 
2955
3041
  :payload:
@@ -2998,7 +3084,7 @@ class P115OpenClient(ClientRequestMixin):
2998
3084
 
2999
3085
  POST https://proapi.115.com/open/folder/add
3000
3086
 
3001
- .. note::
3087
+ .. admonition:: Reference
3002
3088
  https://www.yuque.com/115yun/open/qur839kyx9cgxpxi
3003
3089
 
3004
3090
  :payload:
@@ -3050,7 +3136,7 @@ class P115OpenClient(ClientRequestMixin):
3050
3136
 
3051
3137
  POST https://proapi.115.com/open/ufile/move
3052
3138
 
3053
- .. note::
3139
+ .. admonition:: Reference
3054
3140
  https://www.yuque.com/115yun/open/vc6fhi2mrkenmav2
3055
3141
 
3056
3142
  :payload:
@@ -3108,7 +3194,7 @@ class P115OpenClient(ClientRequestMixin):
3108
3194
  .. hint::
3109
3195
  相当于 `P115Client.fs_search_app2`
3110
3196
 
3111
- .. note::
3197
+ .. admonition:: Reference
3112
3198
  https://www.yuque.com/115yun/open/ft2yelxzopusus38
3113
3199
 
3114
3200
  :payload:
@@ -3263,7 +3349,7 @@ class P115OpenClient(ClientRequestMixin):
3263
3349
  .. hint::
3264
3350
  即使文件已经被删除,也可以操作成功
3265
3351
 
3266
- .. note::
3352
+ .. admonition:: Reference
3267
3353
  https://www.yuque.com/115yun/open/gyrpw5a0zc4sengm
3268
3354
 
3269
3355
  :payload:
@@ -3361,7 +3447,7 @@ class P115OpenClient(ClientRequestMixin):
3361
3447
 
3362
3448
  GET https://proapi.115.com/open/rb/list
3363
3449
 
3364
- .. note::
3450
+ .. admonition:: Reference
3365
3451
  https://www.yuque.com/115yun/open/bg7l4328t98fwgex
3366
3452
 
3367
3453
  :payload:
@@ -3410,7 +3496,7 @@ class P115OpenClient(ClientRequestMixin):
3410
3496
 
3411
3497
  POST https://proapi.115.com/open/rb/revert
3412
3498
 
3413
- .. note::
3499
+ .. admonition:: Reference
3414
3500
  https://www.yuque.com/115yun/open/gq293z80a3kmxbaq
3415
3501
 
3416
3502
  :payload:
@@ -3455,7 +3541,7 @@ class P115OpenClient(ClientRequestMixin):
3455
3541
 
3456
3542
  GET https://proapi.115.com/open/upload/get_token
3457
3543
 
3458
- .. note::
3544
+ .. admonition:: Reference
3459
3545
  https://www.yuque.com/115yun/open/kzacvzl0g7aiyyn4
3460
3546
  """
3461
3547
  api = complete_proapi("/open/upload/get_token", base_url)
@@ -3496,7 +3582,7 @@ class P115OpenClient(ClientRequestMixin):
3496
3582
 
3497
3583
  POST https://proapi.115.com/open/upload/init
3498
3584
 
3499
- .. note::
3585
+ .. admonition:: Reference
3500
3586
  https://www.yuque.com/115yun/open/ul4mrauo5i2uza0q
3501
3587
 
3502
3588
  :payload:
@@ -3554,7 +3640,7 @@ class P115OpenClient(ClientRequestMixin):
3554
3640
 
3555
3641
  POST https://proapi.115.com/open/upload/resume
3556
3642
 
3557
- .. note::
3643
+ .. admonition:: Reference
3558
3644
  https://www.yuque.com/115yun/open/tzvi9sbcg59msddz
3559
3645
 
3560
3646
  :payload:
@@ -3977,7 +4063,7 @@ class P115OpenClient(ClientRequestMixin):
3977
4063
  case 1:
3978
4064
  bucket, object, callback = data["bucket"], data["object"], data["callback"]
3979
4065
  case _:
3980
- raise P115OSError(EINVAL, resp)
4066
+ raise OperationalError(errno.EINVAL, resp)
3981
4067
  url = self.upload_endpoint_url(bucket, object)
3982
4068
  token = self.upload_token
3983
4069
  if partsize <= 0:
@@ -4044,7 +4130,7 @@ class P115OpenClient(ClientRequestMixin):
4044
4130
 
4045
4131
  GET https://proapi.115.com/open/user/info
4046
4132
 
4047
- .. note::
4133
+ .. admonition:: Reference
4048
4134
  https://www.yuque.com/115yun/open/ot1litggzxa1czww
4049
4135
  """
4050
4136
  api = complete_proapi("/open/user/info", base_url)
@@ -6530,8 +6616,8 @@ class P115Client(P115OpenClient):
6530
6616
  for fid, info in resp["data"].items():
6531
6617
  url = info["url"]
6532
6618
  if strict and not url:
6533
- raise IsADirectoryError(
6534
- EISDIR,
6619
+ raise P115IsADirectoryError(
6620
+ errno.EISDIR,
6535
6621
  f"{fid} is a directory, with response {resp}",
6536
6622
  )
6537
6623
  return P115URL(
@@ -6544,8 +6630,8 @@ class P115Client(P115OpenClient):
6544
6630
  is_directory=not url,
6545
6631
  headers=resp["headers"],
6546
6632
  )
6547
- raise FileNotFoundError(
6548
- ENOENT,
6633
+ raise P115FileNotFoundError(
6634
+ errno.ENOENT,
6549
6635
  f"no such pickcode: {pickcode!r}, with response {resp}",
6550
6636
  )
6551
6637
  if async_:
@@ -17444,7 +17530,7 @@ class P115Client(P115OpenClient):
17444
17530
  else:
17445
17531
  payload = dict(payload)
17446
17532
  if url:
17447
- from .tool import share_extract_payload
17533
+ from .tool.util import share_extract_payload
17448
17534
  share_payload = share_extract_payload(url)
17449
17535
  payload["share_code"] = share_payload["share_code"]
17450
17536
  payload["receive_code"] = share_payload["receive_code"] or ""
@@ -17456,14 +17542,14 @@ class P115Client(P115OpenClient):
17456
17542
  info = check_response(resp)["data"]
17457
17543
  file_id = payload["file_id"]
17458
17544
  if not info:
17459
- raise FileNotFoundError(
17460
- ENOENT,
17545
+ raise P115FileNotFoundError(
17546
+ errno.ENOENT,
17461
17547
  f"no such id: {file_id!r}, with response {resp}",
17462
17548
  )
17463
17549
  url = info["url"]
17464
17550
  if strict and not url:
17465
- raise IsADirectoryError(
17466
- EISDIR,
17551
+ raise P115IsADirectoryError(
17552
+ errno.EISDIR,
17467
17553
  f"{file_id} is a directory, with response {resp}",
17468
17554
  )
17469
17555
  return P115URL(
@@ -18208,14 +18294,14 @@ class P115Client(P115OpenClient):
18208
18294
  info = check_response(resp)["data"]
18209
18295
  file_id = payload["file_id"]
18210
18296
  if not info:
18211
- raise FileNotFoundError(
18212
- ENOENT,
18297
+ raise P115FileNotFoundError(
18298
+ errno.ENOENT,
18213
18299
  f"no such id: {file_id!r}, with response {resp}",
18214
18300
  )
18215
18301
  url = info["url"]
18216
18302
  if strict and not url:
18217
- raise IsADirectoryError(
18218
- EISDIR,
18303
+ raise P115IsADirectoryError(
18304
+ errno.EISDIR,
18219
18305
  f"{file_id} is a directory, with response {resp}",
18220
18306
  )
18221
18307
  return P115URL(
@@ -19939,7 +20025,7 @@ class P115Client(P115OpenClient):
19939
20025
  elif status == 1 and statuscode == 0:
19940
20026
  bucket, object, callback = resp["bucket"], resp["object"], resp["callback"]
19941
20027
  else:
19942
- raise P115OSError(EINVAL, resp)
20028
+ raise OperationalError(errno.EINVAL, resp)
19943
20029
  url = self.upload_endpoint_url(bucket, object)
19944
20030
  token = self.upload_token
19945
20031
  if partsize <= 0: