p115client 0.0.5.11.7.2__py3-none-any.whl → 0.0.5.11.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
p115client/client.py CHANGED
@@ -65,9 +65,9 @@ from yarl import URL
65
65
 
66
66
  from .const import CLASS_TO_TYPE, CLIENT_API_MAP, SSOENT_TO_APP, SUFFIX_TO_TYPE, errno
67
67
  from .exception import (
68
- AuthenticationError, BusyOSError, DataError, LoginError, OpenAppAuthLimitExceeded,
69
- NotSupportedError, P115OSError, OperationalError, P115Warning, P115FileExistsError,
70
- P115FileNotFoundError, P115IsADirectoryError,
68
+ AccessTokenError, AuthenticationError, BusyOSError, DataError, LoginError,
69
+ OpenAppAuthLimitExceeded, NotSupportedError, P115OSError, OperationalError,
70
+ P115Warning, P115FileExistsError, 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
@@ -220,6 +220,16 @@ def complete_lixian_api(
220
220
  return complete_api(path, base, base_url=base_url)
221
221
 
222
222
 
223
+ def try_parse_int(s: int | str, /):
224
+ if not isinstance(s, str):
225
+ return s
226
+ if s == "0":
227
+ return 0
228
+ if s.startswith("0") or s.strip(digits):
229
+ return s
230
+ return int(s)
231
+
232
+
223
233
  def json_loads(content: Buffer, /):
224
234
  try:
225
235
  if isinstance(content, (bytes, bytearray, memoryview)):
@@ -232,7 +242,7 @@ def json_loads(content: Buffer, /):
232
242
  raise DataError(errno.ENODATA, content) from e
233
243
 
234
244
 
235
- def default_parse(resp, content: Buffer, /):
245
+ def default_parse(_, content: Buffer, /):
236
246
  if not isinstance(content, (bytes, bytearray, memoryview)):
237
247
  content = memoryview(content)
238
248
  if content and content[0] + content[-1] not in (b"{}", b"[]", b'""'):
@@ -532,16 +542,16 @@ def check_response(resp: dict | Awaitable[dict], /) -> dict | Coroutine[Any, Any
532
542
  raise OpenAppAuthLimitExceeded(errno.EDQUOT, resp)
533
543
  # {"state": 0, "errno": 40140123, "error": "access_token 格式错误(防篡改)"}
534
544
  case 40140123:
535
- raise OperationalError(errno.EINVAL, resp)
545
+ raise AccessTokenError(errno.EINVAL, resp)
536
546
  # {"state": 0, "errno": 40140124, "error": "access_token 签名校验失败(防篡改)"}
537
547
  case 40140124:
538
- raise OperationalError(errno.EINVAL, resp)
548
+ raise AccessTokenError(errno.EINVAL, resp)
539
549
  # {"state": 0, "errno": 40140125, "error": "access_token 无效(已过期或者已解除授权)"}
540
550
  case 40140125:
541
- raise OperationalError(errno.EINVAL, resp)
551
+ raise AccessTokenError(errno.EINVAL, resp)
542
552
  # {"state": 0, "errno": 40140126, "error": "access_token 校验失败(防篡改)"}
543
553
  case 40140126:
544
- raise OperationalError(errno.EINVAL, resp)
554
+ raise AccessTokenError(errno.EINVAL, resp)
545
555
  # {"state": 0, "errno": 40140127, "error": "response_type 错误"}
546
556
  case 40140127:
547
557
  raise OperationalError(errno.EINVAL, resp)
@@ -685,40 +695,36 @@ def normalize_attr_web[D: dict[str, Any]](
685
695
  attr["has_desc"] = bool(val)
686
696
  for key, name in (
687
697
  ("aid", "area_id"),
698
+ ("audio_play_long", "audio_play_long"),
699
+ ("c", "violated"),
700
+ ("c", "is_collect"),
701
+ ("cc", "cover"),
702
+ ("class", "class"),
703
+ ("current_time", "current_time"),
704
+ #("d", "has_desc"),
705
+ ("dp", "dir_path"),
706
+ ("fl", "labels"),
688
707
  ("hdf", "hidden"),
689
708
  ("hdf", "is_private"),
690
- ("issct", "is_shortcut"),
691
- ("ispl", "show_play_long"),
692
709
  ("is_top", "is_top"),
710
+ ("ispl", "show_play_long"),
711
+ ("issct", "is_shortcut"),
693
712
  ("iv", "is_video"),
713
+ ("last_time", "last_time"),
694
714
  ("m", "star"),
695
715
  ("m", "is_mark"),
696
- ("c", "violated"),
697
- ("c", "is_collect"),
698
- ("sh", "is_share"),
699
- ("score", "score"),
700
- #("d", "has_desc"),
701
- #("p", "has_pass"),
702
- ):
703
- if key in info:
704
- attr[name] = int(info[key] or 0)
705
- for key, name in (
706
- ("fl", "labels"),
707
- ("dp", "dir_path"),
708
- ("style", "style"),
709
716
  ("ns", "name_show"),
710
- ("cc", "cover"),
711
- ("sta", "status"),
712
- ("class", "class"),
713
- ("u", "thumb"),
717
+ #("p", "has_pass"),
714
718
  ("play_long", "play_long"),
715
- ("audio_play_long", "audio_play_long"),
716
- ("current_time", "current_time"),
717
- ("last_time", "last_time"),
718
719
  ("played_end", "played_end"),
720
+ ("score", "score"),
721
+ ("sh", "is_share"),
722
+ ("sta", "status"),
723
+ ("style", "style"),
724
+ ("u", "thumb"),
719
725
  ):
720
726
  if key in info:
721
- attr[name] = info[key]
727
+ attr[name] = try_parse_int(info[key])
722
728
  if vdi := info.get("vdi"):
723
729
  attr["defination"] = vdi
724
730
  match vdi:
@@ -821,47 +827,43 @@ def normalize_attr_app[D: dict[str, Any]](
821
827
  if "uet" in info: # utime
822
828
  attr["utime"] = int(info["uet"])
823
829
  for key, name in (
824
- ("aid", "area_id"),
825
- ("fatr", "audio_play_long"),
826
- ("fta", "status"),
827
- ("ftype", "file_type"),
828
- ("ism", "star"),
829
- ("ism", "is_mark"),
830
- ("is_top", "is_top"),
831
- ("isp", "hidden"),
832
- ("isp", "is_private"),
833
- ("ispl", "show_play_long"),
834
- ("iss", "is_share"),
835
- ("isv", "is_video"),
836
- ("issct", "is_shortcut"),
837
- ("ic", "violated"),
838
- ("ic", "is_collect"),
839
- ("unzip_status", "unzip_status"),
840
- ):
841
- if key in info:
842
- attr[name] = int(info[key] or 0)
843
- for key, name in (
844
- ("def", "defination"),
845
- ("def2", "defination2"),
846
- ("fco", "cover"),
847
- ("fco", "folder_cover"),
848
- ("fdesc", "desc"),
849
- ("fl", "labels"),
850
- ("flabel", "fflabel"),
851
- ("multitrack", "multitrack"),
852
- ("play_long", "play_long"),
853
- ("muc", "cover"),
854
- ("muc", "music_cover"),
855
- ("d_img", "d_img"),
856
- ("v_img", "video_img_url"),
857
- ("audio_play_long", "audio_play_long"),
858
- ("current_time", "current_time"),
859
- ("last_time", "last_time"),
860
- ("played_end", "played_end"),
861
- ("uo", "source_url"),
830
+ ("aid", "area_id"), # 域 id,表示文件的状态:1:正常 7:删除(回收站) 120:彻底删除
831
+ ("audio_play_long", "audio_play_long"), # 音频长度
832
+ ("current_time", "current_time"), # 视频当前播放位置(从头开始到此为第 `current_time` 秒)
833
+ ("d_img", "d_img"), # 目录封面
834
+ ("def", "defination"), # 视频清晰度:1:标清 2:高清 3:超清 4:1080P 5:4k 100:原画
835
+ ("def2", "defination2"), # 视频清晰度:1:标清 2:高清 3:超清 4:1080P 5:4k 100:原画
836
+ ("fatr", "audio_play_long"), # 音频长度
837
+ ("fco", "cover"), # 文件夹封面
838
+ ("fco", "folder_cover"), # 文件夹封面
839
+ ("fdesc", "desc"), # 文件备注
840
+ ("fl", "labels"), # 文件标签,得到 1 个字典列表
841
+ ("flabel", "fflabel"), # 文件标签(一般为空)
842
+ ("fta", "status"), # 文件状态:0/2:未上传完成,1:已上传完成
843
+ ("ftype", "file_type"), # 文件类型代码
844
+ ("ic", "violated"), # 是否违规
845
+ ("ic", "is_collect"), # 是否违规
846
+ ("is_top", "is_top"), # 是否置顶
847
+ ("ism", "star"), # 是否星标
848
+ ("ism", "is_mark"), # 是否星标
849
+ ("isp", "hidden"), # 是否加密隐藏(隐藏模式中显示)
850
+ ("isp", "is_private"), # 是否加密隐藏(隐藏模式中显示)
851
+ ("ispl", "show_play_long"), # 是否统计文件夹下视频时长
852
+ ("iss", "is_share"), # 是否共享
853
+ ("issct", "is_shortcut"), # 是否在快捷入口
854
+ ("isv", "is_video"), # 是否为视频
855
+ ("last_time", "last_time"), # 视频上次播放时间戳(秒)
856
+ ("muc", "cover"), # 封面
857
+ ("muc", "music_cover"), # 音乐封面
858
+ ("multitrack", "multitrack"), # 音轨数量
859
+ ("play_long", "play_long"), # 音视频时长
860
+ ("played_end", "played_end"), # 是否播放完成
861
+ ("unzip_status", "unzip_status"), # 解压状态:0(或无值):未解压或已完成 1:解压中
862
+ ("uo", "source_url"), # 原图地址
863
+ ("v_img", "video_img_url"), # 图片封面
862
864
  ):
863
865
  if key in info:
864
- attr[name] = info[key]
866
+ attr[name] = try_parse_int(info[key])
865
867
  if is_directory:
866
868
  attr["type"] = 0
867
869
  elif (thumb := info.get("thumb")) and thumb.startswith("?"):
@@ -983,44 +985,60 @@ def normalize_attr_app2[D: dict[str, Any]](
983
985
  if "fl" in info:
984
986
  attr["labels"] = info["fl"]
985
987
  for key, name in (
986
- ("area_id", "area_id"),
987
- ("has_desc", "has_desc"),
988
- ("has_pass", "has_pass"),
988
+ ("is_collect", "violated"),
989
989
  ("is_mark", "star"),
990
- ("is_mark", "is_mark"),
991
- ("is_top", "is_top"),
992
990
  ("is_private", "hidden"),
993
- ("is_private", "is_private"),
994
- ("show_play_long", "show_play_long"),
995
- ("is_share", "is_share"),
996
- ("is_video", "is_video"),
997
- ("is_collect", "violated"),
998
- ("is_collect", "is_collect"),
999
- ("can_delete", "can_delete"),
1000
- ("file_category", "file_category"),
1001
991
  ):
1002
992
  if key in info:
1003
993
  attr[name] = int(info[key] or 0)
1004
994
  for name in (
1005
- "pick_time", "pick_expire", "file_status", "file_sort", "definition",
1006
- "definition2", "play_long", "current_time", "played_end",
1007
- "last_time", "cate_mark", "category_file_count", "category_order",
1008
- ):
1009
- if name in info:
1010
- attr[name] = int(info[name] or 0)
1011
- for name in (
1012
- "file_eda", "file_question", "file_answer", "password", "video_img_url",
1013
- "play_url", "d_img",
995
+ "area_id",
996
+ "can_delete",
997
+ "cate_mark",
998
+ "category_file_count",
999
+ "category_order",
1000
+ "current_time",
1001
+ "d_img",
1002
+ "definition",
1003
+ "definition2",
1004
+ "file_answer",
1005
+ "file_category",
1006
+ "file_eda",
1007
+ "file_question",
1008
+ "file_sort",
1009
+ "file_status",
1010
+ "has_desc",
1011
+ "has_pass",
1012
+ "is_collect",
1013
+ "is_mark",
1014
+ "is_private",
1015
+ "is_share",
1016
+ "is_top",
1017
+ "is_video",
1018
+ "last_time",
1019
+ "password",
1020
+ "pick_expire",
1021
+ "pick_time",
1022
+ "play_long",
1023
+ "play_url",
1024
+ "played_end",
1025
+ "show_play_long",
1026
+ "video_img_url",
1014
1027
  ):
1015
1028
  if name in info:
1016
- attr[name] = info[name]
1029
+ attr[name] = try_parse_int(info[name])
1017
1030
  if is_directory:
1018
1031
  attr["type"] = 0
1019
1032
  elif "thumb_url" in info:
1020
1033
  attr["type"] = 2
1021
1034
  elif "music_cover" in info or "play_url" in info:
1022
1035
  attr["type"] = 3
1023
- elif info.get("is_video") or "definition" in info or "definition2" in info or "video_img_url" in info:
1036
+ elif (
1037
+ info.get("is_video") or
1038
+ "definition" in info or
1039
+ "definition2" in info or
1040
+ "video_img_url" in info
1041
+ ):
1024
1042
  attr["type"] = 4
1025
1043
  elif type_ := SUFFIX_TO_TYPE.get(splitext(attr["name"])[1].lower()):
1026
1044
  attr["type"] = type_
@@ -2051,6 +2069,34 @@ class ClientRequestMixin:
2051
2069
  .. note::
2052
2070
  code_challenge 默认用的字符串为 64 个 0,hash 算法为 md5
2053
2071
 
2072
+ .. tip::
2073
+ 如果仅仅想要检查 AppID 是否有效,可以用如下的代码:
2074
+
2075
+ .. code:: python
2076
+
2077
+ from p115client import P115Client
2078
+
2079
+ app_id = 100195123
2080
+ response = P115Client.login_qrcode_token_open(app_id)
2081
+ if response["code"]:
2082
+ print("无效 AppID:", app_id, "因为:", response["error"])
2083
+ else:
2084
+ print("有效 AppID:", app_id)
2085
+
2086
+ .. tip::
2087
+ 如果想要罗列出所有可用的 AppID,可以用如下的代码:
2088
+
2089
+ .. code:: python
2090
+
2091
+ from itertools import count
2092
+ from p115client import P115Client
2093
+
2094
+ get_qrcode_token = P115Client.login_qrcode_token_open
2095
+ for app_id in count(100195123, 2):
2096
+ response = get_qrcode_token(app_id)
2097
+ if not response["code"]:
2098
+ print(app_id)
2099
+
2054
2100
  :payload:
2055
2101
  - client_id: int | str 💡 AppID
2056
2102
  - code_challenge: str = <default> 💡 PKCE 相关参数,计算方式如下
@@ -2329,7 +2375,7 @@ class ClientRequestMixin:
2329
2375
  def login_with_app_id(
2330
2376
  cls,
2331
2377
  /,
2332
- app_id: int | str,
2378
+ app_id: int | str = 100195123,
2333
2379
  console_qrcode: bool = True,
2334
2380
  base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
2335
2381
  *,
@@ -2342,7 +2388,7 @@ class ClientRequestMixin:
2342
2388
  def login_with_app_id(
2343
2389
  cls,
2344
2390
  /,
2345
- app_id: int | str,
2391
+ app_id: int | str = 100195123,
2346
2392
  console_qrcode: bool = True,
2347
2393
  base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
2348
2394
  *,
@@ -2354,7 +2400,7 @@ class ClientRequestMixin:
2354
2400
  def login_with_app_id(
2355
2401
  cls,
2356
2402
  /,
2357
- app_id: int | str,
2403
+ app_id: int | str = 100195123,
2358
2404
  console_qrcode: bool = True,
2359
2405
  base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
2360
2406
  *,
@@ -3054,7 +3100,7 @@ class P115OpenClient(ClientRequestMixin):
3054
3100
  /,
3055
3101
  async_: Literal[False] = False,
3056
3102
  **request_kwargs,
3057
- ) -> str:
3103
+ ) -> dict:
3058
3104
  ...
3059
3105
  @overload
3060
3106
  def refresh_access_token(
@@ -3062,27 +3108,36 @@ class P115OpenClient(ClientRequestMixin):
3062
3108
  /,
3063
3109
  async_: Literal[True],
3064
3110
  **request_kwargs,
3065
- ) -> Coroutine[Any, Any, str]:
3111
+ ) -> Coroutine[Any, Any, dict]:
3066
3112
  ...
3067
3113
  def refresh_access_token(
3068
3114
  self,
3069
3115
  /,
3070
3116
  async_: Literal[False, True] = False,
3071
3117
  **request_kwargs,
3072
- ) -> str | Coroutine[Any, Any, str]:
3118
+ ) -> dict | Coroutine[Any, Any, dict]:
3073
3119
  """更新 access_token 和 refresh_token (⚠️ 目前是 7200 秒内就要求刷新一次)
3074
3120
  """
3075
3121
  def gen_step():
3076
- resp = yield self.login_refresh_token_open(
3077
- self.refresh_token,
3078
- async_=async_,
3079
- **request_kwargs,
3080
- )
3122
+ if refresh_token := getattr(self, "refresh_token", ""):
3123
+ resp = yield self.login_refresh_token_open(
3124
+ refresh_token,
3125
+ async_=async_,
3126
+ **request_kwargs,
3127
+ )
3128
+ elif hasattr(self, "login_with_open") and (app_id := getattr(self, "app_id", 0)):
3129
+ resp = yield self.login_with_open(
3130
+ app_id,
3131
+ async_=async_,
3132
+ **request_kwargs,
3133
+ )
3134
+ else:
3135
+ raise RuntimeError("no `refresh_token` or `app_id` provided")
3081
3136
  check_response(resp)
3082
3137
  data = resp["data"]
3083
3138
  self.refresh_token = data["refresh_token"]
3084
- access_token = self.access_token = data["access_token"]
3085
- return access_token
3139
+ self.access_token = data["access_token"]
3140
+ return data
3086
3141
  return run_gen_step(gen_step, may_call=False, async_=async_)
3087
3142
 
3088
3143
  @overload
@@ -3388,7 +3443,7 @@ class P115OpenClient(ClientRequestMixin):
3388
3443
  https://www.yuque.com/115yun/open/kz9ft9a7s57ep868
3389
3444
 
3390
3445
  :payload:
3391
- - cid: int | str = 0 💡 目录 id
3446
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
3392
3447
  - limit: int = 32 💡 分页大小,最大值不一定,看数据量,7,000 应该总是安全的,10,000 有可能报错,但有时也可以 20,000 而成功
3393
3448
  - offset: int = 0 💡 分页开始的索引,索引从 0 开始计算
3394
3449
 
@@ -3397,7 +3452,12 @@ class P115OpenClient(ClientRequestMixin):
3397
3452
  - code: int | str = <default>
3398
3453
  - count_folders: 0 | 1 = 1 💡 统计文件数和目录数
3399
3454
  - cur: 0 | 1 = <default> 💡 是否只显示当前目录
3400
- - custom_order: 0 | 1 | 2 = <default> 💡 是否使用记忆排序。0:使用记忆排序(自定义排序失效) 1:使用自定义排序(不使用记忆排序) 2:自定义排序(非目录置顶)。如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 2
3455
+ - custom_order: 0 | 1 | 2 = <default> 💡 是否使用记忆排序。如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 2
3456
+
3457
+ - 0: 使用记忆排序(自定义排序失效)
3458
+ - 1: 使用自定义排序(不使用记忆排序)
3459
+ - 2: 自定义排序(非目录置顶)
3460
+
3401
3461
  - date: str = <default> 💡 筛选日期
3402
3462
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
3403
3463
  - fields: str = <default>
@@ -3423,12 +3483,12 @@ class P115OpenClient(ClientRequestMixin):
3423
3483
  - r_all: 0 | 1 = <default>
3424
3484
  - record_open_time: 0 | 1 = 1 💡 是否要记录目录的打开时间
3425
3485
  - scid: int | str = <default>
3426
- - show_dir: 0 | 1 = 1 💡 是否展示目录:1:展示 0:不展示
3486
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
3427
3487
  - snap: 0 | 1 = <default>
3428
3488
  - source: str = <default>
3429
3489
  - sys_dir: int | str = <default> 💡 系统通用目录
3430
3490
  - star: 0 | 1 = <default> 💡 是否星标文件
3431
- - stdir: 0 | 1 = <default>
3491
+ - stdir: 0 | 1 = <default> 💡 筛选文件时,是否显示文件夹:1:展示 0:不展示
3432
3492
  - suffix: str = <default> 💡 后缀名(优先级高于 `type`)
3433
3493
  - type: int = <default> 💡 文件类型
3434
3494
 
@@ -3500,8 +3560,11 @@ class P115OpenClient(ClientRequestMixin):
3500
3560
 
3501
3561
  GET https://proapi.115.com/open/folder/get_info
3502
3562
 
3563
+ .. note::
3564
+ 支持 GET 和 POST 方法。`file_id` 和 `path` 需必传一个
3565
+
3503
3566
  .. hint::
3504
- 相当于 `P115Client.fs_category_get_app`
3567
+ 部分相当于 `P115Client.fs_category_get_app`
3505
3568
 
3506
3569
  .. admonition:: Reference
3507
3570
 
@@ -3509,11 +3572,24 @@ class P115OpenClient(ClientRequestMixin):
3509
3572
 
3510
3573
  :payload:
3511
3574
  - file_id: int | str 💡 文件或目录的 id
3575
+ - path: str = <default> 💡 文件或目录的路径。分隔符支持 / 和 > 两种符号,最前面需分隔符开头,以分隔符分隔目录层级
3512
3576
  """
3513
3577
  api = complete_proapi("/open/folder/get_info", base_url)
3514
- if isinstance(payload, (int, str)):
3578
+ if isinstance(payload, int):
3515
3579
  payload = {"file_id": payload}
3516
- return self.request(url=api, params=payload, async_=async_, **request_kwargs)
3580
+ elif isinstance(payload, str):
3581
+ if payload.startswith("0") or payload.strip(digits):
3582
+ if not payload.startswith(("/", ">")):
3583
+ payload = "/" + payload
3584
+ payload = {"path": payload}
3585
+ else:
3586
+ payload = {"file_id": payload}
3587
+ method = request_kwargs.get("method")
3588
+ if method and method.upper() == "POST":
3589
+ request_kwargs["data"] = payload
3590
+ else:
3591
+ request_kwargs["params"] = payload
3592
+ return self.request(url=api, async_=async_, **request_kwargs)
3517
3593
 
3518
3594
  @overload
3519
3595
  def fs_mkdir(
@@ -3694,7 +3770,7 @@ class P115OpenClient(ClientRequestMixin):
3694
3770
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
3695
3771
  - pick_code: str = <default> 💡 是否查询提取码,如果该值为 1 则查询提取码为 `search_value` 的文件
3696
3772
  - search_value: str = "." 💡 搜索文本,可以是 sha1
3697
- - show_dir: 0 | 1 = 1
3773
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
3698
3774
  - source: str = <default>
3699
3775
  - star: 0 | 1 = <default>
3700
3776
  - suffix: str = <default>
@@ -5287,7 +5363,7 @@ class P115Client(P115OpenClient):
5287
5363
  """115 的客户端对象
5288
5364
 
5289
5365
  .. note::
5290
- 目前允许 1 个用户同时登录多个开放平台应用(用 AppID 区别),但如果多次登录同 1 个应用,则只有最近登录的有效
5366
+ 目前允许 1 个用户同时登录多个开放平台应用(用 AppID 区别),也允许多次登录同 1 个应用
5291
5367
 
5292
5368
  目前不允许短时间内再次用 `refresh_token` 刷新 `access_token`,但你可以用登录的方式再次授权登录以获取 `access_token`,即可不受频率限制
5293
5369
 
@@ -5953,11 +6029,60 @@ class P115Client(P115OpenClient):
5953
6029
  return uid
5954
6030
  return run_gen_step(gen_step, may_call=False, async_=async_)
5955
6031
 
6032
+ @overload
6033
+ def login_info_open(
6034
+ self,
6035
+ /,
6036
+ app_id: int | str = 100195123,
6037
+ *,
6038
+ async_: Literal[False] = False,
6039
+ **request_kwargs,
6040
+ ) -> dict:
6041
+ ...
6042
+ @overload
6043
+ def login_info_open(
6044
+ self,
6045
+ /,
6046
+ app_id: int | str = 100195123,
6047
+ *,
6048
+ async_: Literal[True],
6049
+ **request_kwargs,
6050
+ ) -> Coroutine[Any, Any, dict]:
6051
+ ...
6052
+ def login_info_open(
6053
+ self,
6054
+ /,
6055
+ app_id: int | str = 100195123,
6056
+ *,
6057
+ async_: Literal[False, True] = False,
6058
+ **request_kwargs,
6059
+ ) -> dict | Coroutine[Any, Any, dict]:
6060
+ """获取某个开放接口应用的信息(目前可获得名称和头像)
6061
+
6062
+ :param app_id: AppID
6063
+ :param async_: 是否异步
6064
+ :param request_kwargs: 其它请求参数
6065
+
6066
+ :return: 接口返回值
6067
+ """
6068
+ def gen_step():
6069
+ resp = yield self.login_qrcode_token_open(app_id, async_=async_, **request_kwargs)
6070
+ login_uid = check_response(resp)["data"]["uid"]
6071
+ resp = yield self.login_qrcode_scan(login_uid, async_=async_, **request_kwargs)
6072
+ check_response(resp)
6073
+ tip_txt = resp["data"]["tip_txt"]
6074
+ return {
6075
+ "app_id": app_id,
6076
+ "name": tip_txt[:-10].removeprefix("\ufeff"),
6077
+ "icon": resp["data"]["icon"],
6078
+ }
6079
+ return run_gen_step(gen_step, may_call=False, async_=async_)
6080
+
5956
6081
  @overload
5957
6082
  def login_with_open(
5958
6083
  self,
5959
6084
  /,
5960
- app_id: int | str,
6085
+ app_id: int | str = 100195123,
5961
6086
  *,
5962
6087
  show_warning: bool = False,
5963
6088
  async_: Literal[False] = False,
@@ -5968,7 +6093,7 @@ class P115Client(P115OpenClient):
5968
6093
  def login_with_open(
5969
6094
  self,
5970
6095
  /,
5971
- app_id: int | str,
6096
+ app_id: int | str = 100195123,
5972
6097
  *,
5973
6098
  show_warning: bool = False,
5974
6099
  async_: Literal[True],
@@ -5978,7 +6103,7 @@ class P115Client(P115OpenClient):
5978
6103
  def login_with_open(
5979
6104
  self,
5980
6105
  /,
5981
- app_id: int | str,
6106
+ app_id: int | str = 100195123,
5982
6107
  *,
5983
6108
  show_warning: bool = False,
5984
6109
  async_: Literal[False, True] = False,
@@ -6153,7 +6278,7 @@ class P115Client(P115OpenClient):
6153
6278
  def login_another_open(
6154
6279
  self,
6155
6280
  /,
6156
- app_id: int | str,
6281
+ app_id: int | str = 100195123,
6157
6282
  *,
6158
6283
  replace: Literal[True] | Self,
6159
6284
  show_warning: bool = False,
@@ -6165,7 +6290,7 @@ class P115Client(P115OpenClient):
6165
6290
  def login_another_open(
6166
6291
  self,
6167
6292
  /,
6168
- app_id: int | str,
6293
+ app_id: int | str = 100195123,
6169
6294
  *,
6170
6295
  replace: Literal[True] | Self,
6171
6296
  show_warning: bool = False,
@@ -6177,7 +6302,7 @@ class P115Client(P115OpenClient):
6177
6302
  def login_another_open(
6178
6303
  self,
6179
6304
  /,
6180
- app_id: int | str,
6305
+ app_id: int | str = 100195123,
6181
6306
  *,
6182
6307
  replace: Literal[False] = False,
6183
6308
  show_warning: bool = False,
@@ -6189,7 +6314,7 @@ class P115Client(P115OpenClient):
6189
6314
  def login_another_open(
6190
6315
  self,
6191
6316
  /,
6192
- app_id: int | str,
6317
+ app_id: int | str = 100195123,
6193
6318
  *,
6194
6319
  replace: Literal[False] = False,
6195
6320
  show_warning: bool = False,
@@ -6200,7 +6325,7 @@ class P115Client(P115OpenClient):
6200
6325
  def login_another_open(
6201
6326
  self,
6202
6327
  /,
6203
- app_id: int | str,
6328
+ app_id: int | str = 100195123,
6204
6329
  *,
6205
6330
  replace: bool | Self = False,
6206
6331
  show_warning: bool = False,
@@ -6571,8 +6696,16 @@ class P115Client(P115OpenClient):
6571
6696
  method=method,
6572
6697
  **request_kwargs,
6573
6698
  )
6699
+ if (
6700
+ is_open_api and
6701
+ need_to_check and
6702
+ isinstance(resp, dict) and
6703
+ resp.get("code") in (40140123, 40140124, 40140125, 40140126)
6704
+ ):
6705
+ check_response(resp)
6574
6706
  except BaseException as e:
6575
6707
  is_auth_error = isinstance(e, (AuthenticationError, LoginError))
6708
+ not_access_token_error = not isinstance(e, AccessTokenError)
6576
6709
  if (
6577
6710
  cert_headers is not None and
6578
6711
  revert_cert_headers is not None and
@@ -6582,26 +6715,25 @@ class P115Client(P115OpenClient):
6582
6715
  yield revert_cert_headers(cert_headers)
6583
6716
  if not need_to_check:
6584
6717
  raise
6585
- res = yield cast(Callable, check_for_relogin)(e)
6586
- if not res if isinstance(res, bool) else res != 405:
6587
- raise
6718
+ if not_access_token_error:
6719
+ res = yield cast(Callable, check_for_relogin)(e)
6720
+ if not res if isinstance(res, bool) else res != 405:
6721
+ raise
6588
6722
  if fetch_cert_headers is not None:
6589
6723
  cert_headers = yield fetch_cert_headers()
6590
6724
  headers.update(cert_headers)
6591
6725
  elif is_open_api:
6592
6726
  yield lock.acquire()
6593
6727
  try:
6594
- if cert != self.access_token:
6595
- continue
6596
- if i or is_auth_error:
6597
- raise
6598
- app_id = getattr(self, "app_id")
6599
- yield self.login_another_open(
6600
- app_id,
6601
- replace=True,
6602
- async_=async_, # type: ignore
6603
- )
6604
- warn(f"relogin to refresh token: {app_id=}", category=P115Warning)
6728
+ access_token = self.access_token
6729
+ if cert.capitalize().removeprefix("Bearer ") == access_token:
6730
+ if i or is_auth_error or not_access_token_error:
6731
+ raise
6732
+ warn(f"relogin to refresh token", category=P115Warning)
6733
+ yield self.refresh_access_token(async_=async_)
6734
+ cert = headers["authorization"] = "Bearer " + self.access_token
6735
+ else:
6736
+ cert = headers["authorization"] = "Bearer " + access_token
6605
6737
  finally:
6606
6738
  lock.release()
6607
6739
  else:
@@ -6625,8 +6757,9 @@ class P115Client(P115OpenClient):
6625
6757
  replace=True,
6626
6758
  async_=async_, # type: ignore
6627
6759
  )
6760
+ cert = headers["cookie"] = self.cookies_str
6628
6761
  else:
6629
- headers["cookie"] = cookies_new
6762
+ cert = headers["cookie"] = cookies_new
6630
6763
  finally:
6631
6764
  lock.release()
6632
6765
  else:
@@ -10222,7 +10355,7 @@ class P115Client(P115OpenClient):
10222
10355
  - 7: 其它
10223
10356
 
10224
10357
  :payload:
10225
- - cid: int | str = 0 💡 目录 id
10358
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
10226
10359
  - limit: int = 32 💡 分页大小,目前最大值是 1,150,以前是没限制的
10227
10360
  - offset: int = 0 💡 分页开始的索引,索引从 0 开始计算
10228
10361
 
@@ -10232,6 +10365,11 @@ class P115Client(P115OpenClient):
10232
10365
  - count_folders: 0 | 1 = 1 💡 统计文件数和目录数
10233
10366
  - cur: 0 | 1 = <default> 💡 是否只搜索当前目录
10234
10367
  - custom_order: 0 | 1 = <default> 💡 启用自定义排序,如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 1
10368
+
10369
+ - 0: 使用记忆排序(自定义排序失效)
10370
+ - 1: 使用自定义排序(不使用记忆排序)
10371
+ - 2: 自定义排序(非目录置顶)
10372
+
10235
10373
  - date: str = <default> 💡 筛选日期
10236
10374
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
10237
10375
  - fields: str = <default>
@@ -10256,12 +10394,12 @@ class P115Client(P115OpenClient):
10256
10394
  - r_all: 0 | 1 = <default>
10257
10395
  - record_open_time: 0 | 1 = 1 💡 是否要记录目录的打开时间
10258
10396
  - scid: int | str = <default>
10259
- - show_dir: 0 | 1 = 1
10397
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
10260
10398
  - snap: 0 | 1 = <default>
10261
10399
  - source: str = <default>
10262
10400
  - sys_dir: int | str = <default>
10263
10401
  - star: 0 | 1 = <default> 💡 是否星标文件
10264
- - stdir: 0 | 1 = <default>
10402
+ - stdir: 0 | 1 = <default> 💡 筛选文件时,是否显示文件夹:1:展示 0:不展示
10265
10403
  - suffix: str = <default> 💡 后缀名(优先级高于 `type`)
10266
10404
  - suffix_type: int = <default>
10267
10405
  - type: int = <default> 💡 文件类型
@@ -10358,7 +10496,7 @@ class P115Client(P115OpenClient):
10358
10496
  在根目录下且 fc_mix=0 且是特殊名字 ("我的接收", "手机相册", "云下载", "我的时光记录"),会在整个文件列表的最前面,这时可从返回信息的 "sys_count" 字段知道数目
10359
10497
 
10360
10498
  :payload:
10361
- - cid: int | str = 0 💡 目录 id
10499
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
10362
10500
  - limit: int = 32 💡 分页大小,最大值不一定,看数据量,7,000 应该总是安全的,10,000 有可能报错,但有时也可以 20,000 而成功
10363
10501
  - offset: int = 0 💡 分页开始的索引,索引从 0 开始计算
10364
10502
 
@@ -10367,7 +10505,12 @@ class P115Client(P115OpenClient):
10367
10505
  - code: int | str = <default>
10368
10506
  - count_folders: 0 | 1 = 1 💡 统计文件数和目录数
10369
10507
  - cur: 0 | 1 = <default> 💡 是否只显示当前目录
10370
- - custom_order: 0 | 1 | 2 = <default> 💡 是否使用记忆排序。0:使用记忆排序(自定义排序失效) 1:使用自定义排序(不使用记忆排序) 2:自定义排序(非目录置顶)。如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 2
10508
+ - custom_order: 0 | 1 | 2 = <default> 💡 是否使用记忆排序。如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 2
10509
+
10510
+ - 0: 使用记忆排序(自定义排序失效)
10511
+ - 1: 使用自定义排序(不使用记忆排序)
10512
+ - 2: 自定义排序(非目录置顶)
10513
+
10371
10514
  - date: str = <default> 💡 筛选日期
10372
10515
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
10373
10516
  - fields: str = <default>
@@ -10393,12 +10536,12 @@ class P115Client(P115OpenClient):
10393
10536
  - r_all: 0 | 1 = <default>
10394
10537
  - record_open_time: 0 | 1 = 1 💡 是否要记录目录的打开时间
10395
10538
  - scid: int | str = <default>
10396
- - show_dir: 0 | 1 = 1 💡 是否展示目录:1:展示 0:不展示
10539
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
10397
10540
  - snap: 0 | 1 = <default>
10398
10541
  - source: str = <default>
10399
10542
  - sys_dir: int | str = <default> 💡 系统通用目录
10400
10543
  - star: 0 | 1 = <default> 💡 是否星标文件
10401
- - stdir: 0 | 1 = <default>
10544
+ - stdir: 0 | 1 = <default> 💡 筛选文件时,是否显示文件夹:1:展示 0:不展示
10402
10545
  - suffix: str = <default> 💡 后缀名(优先级高于 `type`)
10403
10546
  - type: int = <default> 💡 文件类型
10404
10547
 
@@ -10483,7 +10626,7 @@ class P115Client(P115OpenClient):
10483
10626
  2. fc_mix 无论怎么设置,都和 fc_mix=0 的效果相同(即目录总是置顶),设置为 custom_order=2 也没用
10484
10627
 
10485
10628
  :payload:
10486
- - cid: int | str = 0 💡 目录 id
10629
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
10487
10630
  - limit: int = 32 💡 分页大小,最大值不一定,看数据量,7,000 应该总是安全的,10,000 有可能报错,但有时也可以 20,000 而成功
10488
10631
  - offset: int = 0 💡 分页开始的索引,索引从 0 开始计算
10489
10632
 
@@ -10493,6 +10636,11 @@ class P115Client(P115OpenClient):
10493
10636
  - count_folders: 0 | 1 = 1 💡 统计文件数和目录数
10494
10637
  - cur: 0 | 1 = <default> 💡 是否只搜索当前目录
10495
10638
  - custom_order: 0 | 1 | 2 = <default> 💡 启用自定义排序,如果指定了 "asc"、"fc_mix"、"o" 中其一,则此参数会被自动设置为 2
10639
+
10640
+ - 0: 使用记忆排序(自定义排序失效)
10641
+ - 1: 使用自定义排序(不使用记忆排序)
10642
+ - 2: 自定义排序(非目录置顶)
10643
+
10496
10644
  - date: str = <default> 💡 筛选日期
10497
10645
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
10498
10646
  - fields: str = <default>
@@ -10518,12 +10666,12 @@ class P115Client(P115OpenClient):
10518
10666
  - r_all: 0 | 1 = <default>
10519
10667
  - record_open_time: 0 | 1 = 1 💡 是否要记录目录的打开时间
10520
10668
  - scid: int | str = <default>
10521
- - show_dir: 0 | 1 = 1
10669
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
10522
10670
  - snap: 0 | 1 = <default>
10523
10671
  - source: str = <default>
10524
10672
  - sys_dir: int | str = <default>
10525
10673
  - star: 0 | 1 = <default> 💡 是否星标文件
10526
- - stdir: 0 | 1 = <default>
10674
+ - stdir: 0 | 1 = <default> 💡 筛选文件时,是否显示文件夹:1:展示 0:不展示
10527
10675
  - suffix: str = <default> 💡 后缀名(优先级高于 `type`)
10528
10676
  - type: int = <default> 💡 文件类型
10529
10677
 
@@ -10608,7 +10756,7 @@ class P115Client(P115OpenClient):
10608
10756
  不过在我看来,只要一个目录内的节点数超过 2,400 个,则大概就没必要使用此接口
10609
10757
 
10610
10758
  :payload:
10611
- - cid: int | str = 0 💡 目录 id
10759
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
10612
10760
  - limit: int = 32 💡 分页大小,最大值是 1,200
10613
10761
  - offset: int = 0 💡 分页开始的索引,索引从 0 开始计算
10614
10762
 
@@ -10618,6 +10766,11 @@ class P115Client(P115OpenClient):
10618
10766
  - count_folders: 0 | 1 = 1 💡 统计文件数和目录数
10619
10767
  - cur: 0 | 1 = <default> 💡 是否只搜索当前目录
10620
10768
  - custom_order: 0 | 1 = <default> 💡 启用自定义排序,如果指定了 "asc"、"fc_mix" 中其一,则此参数会被自动设置为 1
10769
+
10770
+ - 0: 使用记忆排序(自定义排序失效)
10771
+ - 1: 使用自定义排序(不使用记忆排序)
10772
+ - 2: 自定义排序(非目录置顶)
10773
+
10621
10774
  - date: str = <default> 💡 筛选日期
10622
10775
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
10623
10776
  - fields: str = <default>
@@ -10633,12 +10786,12 @@ class P115Client(P115OpenClient):
10633
10786
  - r_all: 0 | 1 = <default>
10634
10787
  - record_open_time: 0 | 1 = 1 💡 是否要记录目录的打开时间
10635
10788
  - scid: int | str = <default>
10636
- - show_dir: 0 | 1 = 1
10789
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
10637
10790
  - snap: 0 | 1 = <default>
10638
10791
  - source: str = <default>
10639
10792
  - sys_dir: int | str = <default>
10640
10793
  - star: 0 | 1 = <default> 💡 是否星标文件
10641
- - stdir: 0 | 1 = <default>
10794
+ - stdir: 0 | 1 = <default> 💡 筛选文件时,是否显示文件夹:1:展示 0:不展示
10642
10795
  - suffix: str = <default> 💡 后缀名(优先级高于 `type`)
10643
10796
  - type: int = <default> 💡 文件类型
10644
10797
 
@@ -10715,7 +10868,7 @@ class P115Client(P115OpenClient):
10715
10868
 
10716
10869
  :payload:
10717
10870
  - file_name: str 💡 文件名,不含后缀
10718
- - pid: int | str = 0 💡 目录 id
10871
+ - pid: int | str = 0 💡 目录 id,对应 parent_id
10719
10872
  - type: 1 | 2 | 3 = 1 💡 1:Word文档(.docx) 2:Excel表格(.xlsx) 3:PPT文稿(.pptx)
10720
10873
  """
10721
10874
  api = complete_webapi("/files/blank_document", base_url=base_url)
@@ -10860,7 +11013,7 @@ class P115Client(P115OpenClient):
10860
11013
  GET https://webapi.115.com/files/get_second_type
10861
11014
 
10862
11015
  :payload:
10863
- - cid: int | str = 0 💡 目录 id
11016
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
10864
11017
  - type: int = 1 💡 文件类型
10865
11018
 
10866
11019
  - 1: 文档
@@ -11921,7 +12074,7 @@ class P115Client(P115OpenClient):
11921
12074
  只能获取直属于 `cid` 所在目录的图片,不会遍历整个目录树
11922
12075
 
11923
12076
  :payload:
11924
- - cid: int | str 💡 目录 id
12077
+ - cid: int | str 💡 目录 id,对应 parent_id
11925
12078
  - file_id: int | str 💡 不能是 0,可以不同于 `cid`,必须是任何一个有效的 id(单纯是被检查一下)
11926
12079
  - limit: int = <default> 💡 最多返回数量
11927
12080
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
@@ -11984,7 +12137,7 @@ class P115Client(P115OpenClient):
11984
12137
  GET https://proapi.115.com/android/files/imglist
11985
12138
 
11986
12139
  :payload:
11987
- - cid: int | str = 0 💡 目录 id
12140
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
11988
12141
  - limit: int = 32 💡 一页大小,建议控制在 <= 9000,不然会报错
11989
12142
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
11990
12143
 
@@ -13890,7 +14043,7 @@ class P115Client(P115OpenClient):
13890
14043
  - "user_ptime": 创建时间
13891
14044
  - "user_otime": 上一次打开时间
13892
14045
 
13893
- - file_id: int | str = 0 💡 目录 id
14046
+ - file_id: int | str = 0 💡 目录 id,对应 parent_id
13894
14047
  - user_asc: 0 | 1 = <default> 💡 是否升序排列
13895
14048
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
13896
14049
  - module: str = <default> 💡 "label_search" 表示用于搜索的排序
@@ -13953,7 +14106,7 @@ class P115Client(P115OpenClient):
13953
14106
  - "user_ptime": 创建时间
13954
14107
  - "user_otime": 上一次打开时间
13955
14108
 
13956
- - file_id: int | str = 0 💡 目录 id
14109
+ - file_id: int | str = 0 💡 目录 id,对应 parent_id
13957
14110
  - user_asc: 0 | 1 = <default> 💡 是否升序排列
13958
14111
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
13959
14112
  - module: str = <default> 💡 "label_search" 表示用于搜索的排序
@@ -14338,7 +14491,7 @@ class P115Client(P115OpenClient):
14338
14491
  :payload:
14339
14492
  - aid: int | str = 1 💡 area_id。1:正常文件 7:回收站文件 12:瞬间文件 120:彻底删除文件、简历附件
14340
14493
  - asc: 0 | 1 = <default> 💡 是否升序排列
14341
- - cid: int | str = 0 💡 目录 id
14494
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
14342
14495
  - count_folders: 0 | 1 = <default> 💡 是否统计目录数,这样就会增加 "folder_count" 和 "file_count" 字段作为统计
14343
14496
  - date: str = <default> 💡 筛选日期,格式为 YYYY-MM-DD(或者 YYYY-MM 或 YYYY),具体可以看文件信息中的 "t" 字段的值
14344
14497
  - fc_mix: 0 | 1 = <default> 💡 是否目录和文件混合,如果为 0 则目录在前(目录置顶)
@@ -14450,7 +14603,7 @@ class P115Client(P115OpenClient):
14450
14603
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
14451
14604
  - pick_code: str = <default> 💡 是否查询提取码,如果该值为 1 则查询提取码为 `search_value` 的文件
14452
14605
  - search_value: str = "." 💡 搜索文本,可以是 sha1
14453
- - show_dir: 0 | 1 = 1
14606
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
14454
14607
  - source: str = <default>
14455
14608
  - star: 0 | 1 = <default>
14456
14609
  - suffix: str = <default>
@@ -14547,7 +14700,7 @@ class P115Client(P115OpenClient):
14547
14700
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
14548
14701
  - pick_code: str = <default> 💡 是否查询提取码,如果该值为 1 则查询提取码为 `search_value` 的文件
14549
14702
  - search_value: str = "." 💡 搜索文本,可以是 sha1
14550
- - show_dir: 0 | 1 = 1
14703
+ - show_dir: 0 | 1 = 1 💡 是否显示目录
14551
14704
  - source: str = <default>
14552
14705
  - star: 0 | 1 = <default>
14553
14706
  - suffix: str = <default>
@@ -19391,7 +19544,7 @@ class P115Client(P115OpenClient):
19391
19544
  :payload:
19392
19545
  - share_code: str 💡 分享码
19393
19546
  - receive_code: str 💡 接收码(即密码)
19394
- - cid: int | str = 0 💡 目录 id
19547
+ - cid: int | str = 0 💡 目录 id,对应 parent_id
19395
19548
  - limit: int = 32 💡 一页大小,意思就是 page_size
19396
19549
  - offset: int = 0 💡 索引偏移,索引从 0 开始计算
19397
19550
  - search_value: str = "." 💡 搜索文本,仅支持搜索文件名
@@ -20156,7 +20309,7 @@ class P115Client(P115OpenClient):
20156
20309
  POST https://aps.115.com/repeat/repeat.php
20157
20310
 
20158
20311
  :payload:
20159
- - folder_id: int | str 💡 目录 id
20312
+ - folder_id: int | str 💡 目录 id,对应 parent_id
20160
20313
  """
20161
20314
  api = complete_api("/repeat/repeat.php", "aps", base_url=base_url)
20162
20315
  if isinstance(payload, (int, str)):
p115client/exception.py CHANGED
@@ -2,10 +2,10 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __all__ = [
5
- "P115Warning", "P115OSError", "AuthenticationError", "BusyOSError", "DataError",
6
- "LoginError", "MultipartUploadAbort", "OpenAppAuthLimitExceeded",
7
- "NotSupportedError", "OperationalError", "P115FileExistsError",
8
- "P115FileNotFoundError", "P115IsADirectoryError",
5
+ "P115Warning", "P115OSError", "AccessTokenError", "AuthenticationError",
6
+ "BusyOSError", "DataError", "LoginError", "MultipartUploadAbort",
7
+ "OpenAppAuthLimitExceeded", "NotSupportedError", "OperationalError",
8
+ "P115FileExistsError", "P115FileNotFoundError", "P115IsADirectoryError",
9
9
  "P115NotADirectoryError", "P115PermissionError", "P115TimeoutError",
10
10
  ]
11
11
 
@@ -51,6 +51,11 @@ class P115OSError(OSError):
51
51
  return args[0]
52
52
 
53
53
 
54
+ class AccessTokenError(P115OSError, ValueError):
55
+ """access_token 错误或者无效
56
+ """
57
+
58
+
54
59
  class AuthenticationError(P115OSError):
55
60
  """当登录状态无效时抛出
56
61
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p115client
3
- Version: 0.0.5.11.7.2
3
+ Version: 0.0.5.11.8.1
4
4
  Summary: Python 115 webdisk client.
5
5
  Home-page: https://github.com/ChenyangGao/p115client
6
6
  License: MIT
@@ -1,9 +1,9 @@
1
1
  LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
2
  p115client/__init__.py,sha256=1mx7njuAlqcuEWONTjSiiGnXyyNyqOcJyNX1FMHqQ-4,214
3
3
  p115client/_upload.py,sha256=RTYpM_EDFuI3O7yf08f13NdeG0iD03o_V4H6Je6Nsr8,30754
4
- p115client/client.py,sha256=vRFeTy3SjoWu4uqmsZWkRl9xdXe0S1CrvoFXEACamvc,772745
4
+ p115client/client.py,sha256=ro8Qk1RiKMzMRyg19eRvVTBFF-9lgKZoOYq2FiDff5w,779734
5
5
  p115client/const.py,sha256=KqDGr9KsOnSkNVM3RIdQGptCMHbidMmZ_ffyFPiniww,7654
6
- p115client/exception.py,sha256=O4SlfjbtI9GPjzbzJrFaFUZENJFz2qA3VDEKZfdhHbc,3590
6
+ p115client/exception.py,sha256=4SZ8ubOLMRxtcqc0u1kNzXqH1a6wwXJFwGnRDURoEgQ,3708
7
7
  p115client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  p115client/tool/__init__.py,sha256=NjT9rroMhLwKI7UlpSCksSsnB3GexXzxvhfunNWzjY0,386
9
9
  p115client/tool/attr.py,sha256=RmNm5uar2PVv1Me7xOWyM0JtZr-JqoglIIpF0WHGues,2979
@@ -20,7 +20,7 @@ p115client/tool/upload.py,sha256=i6laoeG7GdNO7hvCDVBy8Yv2Is800wL7O5na6a3v70g,159
20
20
  p115client/tool/util.py,sha256=0o9TrXdoPcljgxDDRdxRon41bq1OjuUYCWsR0XLfmPo,3357
21
21
  p115client/tool/xys.py,sha256=vU28Px2yeQzIxxGkopJIpvV6TdOnWJ5xB6NPXpTgM0Y,10306
22
22
  p115client/type.py,sha256=7kOp98uLaYqcTTCgCrb3DRcl8ukMpn7ibsnVvtw2nG8,6250
23
- p115client-0.0.5.11.7.2.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
24
- p115client-0.0.5.11.7.2.dist-info/METADATA,sha256=Kdd0GJUqFKezZHveZwK3jj-z61N5Ou2o5QIjn4V2WNw,8194
25
- p115client-0.0.5.11.7.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
26
- p115client-0.0.5.11.7.2.dist-info/RECORD,,
23
+ p115client-0.0.5.11.8.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
24
+ p115client-0.0.5.11.8.1.dist-info/METADATA,sha256=e-F1jCsLHlXC-jCgUfDJcvKcjZpn7OwX8H9Hu7yu6nE,8194
25
+ p115client-0.0.5.11.8.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
26
+ p115client-0.0.5.11.8.1.dist-info/RECORD,,