p123client 0.0.6.8__py3-none-any.whl → 0.0.6.9.4__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/const.py CHANGED
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env python3
2
2
  # encoding: utf-8
3
3
 
4
- #__all__ = []
4
+ __all__ = ["CLIENT_METHOD_API_MAP", "CLIENT_API_METHODS_MAP"]
5
+
6
+ from typing import Final
7
+
8
+
9
+ #: 所有已封装的方法名和对应的 123 接口
10
+ CLIENT_METHOD_API_MAP: Final[dict[str, str]] = {}
11
+
12
+ #: 所有已封装的 123 接口和对应的方法名
13
+ CLIENT_API_METHODS_MAP: Final[dict[str, list[str]]] = {}
p123client/exception.py CHANGED
@@ -2,8 +2,11 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __all__ = [
5
- "P123Warning", "P123OSError", "P123BrokenUpload",
6
- "P123AccessTokenError", "P123AuthenticationError",
5
+ "P123Warning", "P123OSError", "P123AuthenticationError", "P123AccessTokenError",
6
+ "P123BrokenUpload", "P123DataError", "P123LoginError", "P123NotSupportedError",
7
+ "P123OperationalError", "P123FileExistsError", "P123FileNotFoundError",
8
+ "P123IsADirectoryError", "P123NotADirectoryError", "P123PermissionError",
9
+ "P123TimeoutError",
7
10
  ]
8
11
 
9
12
  import warnings
@@ -19,6 +22,7 @@ setattr(warnings, "formatwarning", lambda message, category, filename, lineno, l
19
22
  f"\r\x1b[K\x1b[1;31;43m{category.__qualname__}\x1b[0m(\x1b[32m{_getid()}\x1b[0m) @ \x1b[3;4;34m{filename}\x1b[0m:\x1b[36m{lineno}\x1b[0m \x1b[5;31m➜\x1b[0m \x1b[1m{message}\x1b[0m\n"
20
23
  )
21
24
 
25
+
22
26
  class P123Warning(UserWarning):
23
27
  """本模块的最基础警示类
24
28
  """
@@ -47,14 +51,67 @@ class P123OSError(OSError):
47
51
  return args[0]
48
52
 
49
53
 
54
+ class P123AuthenticationError(P123OSError):
55
+ """当登录状态无效时抛出
56
+ """
57
+
58
+
59
+ class P123AccessTokenError(P123AuthenticationError):
60
+ """access_token 错误或者无效
61
+ """
62
+
63
+
50
64
  class P123BrokenUpload(P123OSError):
51
- pass
65
+ """当上传文件中被打断时抛出
66
+ """
52
67
 
53
68
 
54
- class P123AccessTokenError(P123OSError):
55
- pass
69
+ class P123DataError(P123OSError):
70
+ """当响应数据解析失败时抛出
71
+ """
56
72
 
57
73
 
58
- class P123AuthenticationError(P123OSError):
59
- pass
74
+ class P123LoginError(P123AuthenticationError):
75
+ """当登录失败时抛出
76
+ """
77
+
78
+
79
+ class P123NotSupportedError(P123OSError):
80
+ """当调用不存在的接口或者接口不支持此操作时抛出
81
+ """
82
+
83
+
84
+ class P123OperationalError(P123OSError):
85
+ """当接口使用方法错误时抛出,例如参数错误、空间不足、超出允许数量范围等
86
+ """
87
+
88
+
89
+ class P123FileExistsError(P123OSError, FileExistsError):
90
+ """扩展 FileExistsError,同时是 P123OSError 的子类
91
+ """
92
+
93
+
94
+ class P123FileNotFoundError(P123OSError, FileNotFoundError):
95
+ """扩展 FileNotFoundError,同时是 P123OSError 的子类
96
+ """
97
+
98
+
99
+ class P123IsADirectoryError(P123OSError, IsADirectoryError):
100
+ """扩展 IsADirectoryError,同时是 P123OSError 的子类
101
+ """
102
+
103
+
104
+ class P123NotADirectoryError(P123OSError, NotADirectoryError):
105
+ """扩展 NotADirectoryError,同时是 P123OSError 的子类
106
+ """
107
+
108
+
109
+ class P123PermissionError(P123OSError, PermissionError):
110
+ """扩展 PermissionError,同时是 P123OSError 的子类
111
+ """
112
+
113
+
114
+ class P123TimeoutError(P123OSError, TimeoutError):
115
+ """扩展 TimeoutError,同时是 P123OSError 的子类
116
+ """
60
117
 
@@ -6,14 +6,14 @@ __all__ = ["make_uri", "upload_uri", "get_downurl", "iterdir", "share_iterdir"]
6
6
 
7
7
  from asyncio import sleep as async_sleep
8
8
  from collections import deque
9
- from collections.abc import AsyncIterator, Callable, Coroutine, Iterator
9
+ from collections.abc import AsyncIterator, Callable, Coroutine, Iterable, Iterator, Mapping
10
10
  from errno import EISDIR, ENOENT
11
11
  from functools import partial
12
12
  from itertools import count
13
13
  from time import sleep, time
14
14
  from typing import Literal
15
15
  from typing import overload, Any, Literal
16
- from urllib.parse import unquote
16
+ from urllib.parse import unquote, urlsplit
17
17
 
18
18
  from encode_uri import encode_uri_component_loose
19
19
  from iterutils import run_gen_step, run_gen_step_iter, Yield
@@ -216,6 +216,9 @@ def get_downurl(
216
216
  return run_gen_step(gen_step, async_)
217
217
 
218
218
 
219
+ # TODO: _iterdir 支持广度优先遍历
220
+ # TODO: 失败时,报错信息支持返回已经成功和未成功的列表,并且形式上也要利于断点重试
221
+ # TODO: 支持传入其它自定义的查询参数
219
222
  @overload
220
223
  def _iterdir(
221
224
  fs_files: Callable,
@@ -224,7 +227,9 @@ def _iterdir(
224
227
  min_depth: int = 1,
225
228
  max_depth: int = 1,
226
229
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
227
- interval: int | float = 0,
230
+ cooldown: int | float = 0,
231
+ base_url: None | str | Callable[[], str] = None,
232
+ extra_data: None | Mapping | Iterable[tuple[str, Any]] = None,
228
233
  *,
229
234
  async_: Literal[False] = False,
230
235
  **request_kwargs,
@@ -238,7 +243,9 @@ def _iterdir(
238
243
  min_depth: int = 1,
239
244
  max_depth: int = 1,
240
245
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
241
- interval: int | float = 0,
246
+ cooldown: int | float = 0,
247
+ base_url: None | str | Callable[[], str] = None,
248
+ extra_data: None | Mapping | Iterable[tuple[str, Any]] = None,
242
249
  *,
243
250
  async_: Literal[True],
244
251
  **request_kwargs,
@@ -251,7 +258,9 @@ def _iterdir(
251
258
  min_depth: int = 1,
252
259
  max_depth: int = 1,
253
260
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
254
- interval: int | float = 0,
261
+ cooldown: int | float = 0,
262
+ base_url: None | str | Callable[[], str] = None,
263
+ extra_data: None | Mapping | Iterable[tuple[str, Any]] = None,
255
264
  *,
256
265
  async_: Literal[False, True] = False,
257
266
  **request_kwargs,
@@ -270,7 +279,9 @@ def _iterdir(
270
279
  - 如果返回值是 False,则跳过此节点(但依然会继续处理位于此节点之下的节点)
271
280
  - 如果返回值是 True,则输出此节点
272
281
 
273
- :param interval: 两次调用之间,休息的时间
282
+ :param cooldown: 两次调用之间,冷却的时间(用两次调用开始时的时间差,而不是一次完成到下一次开始的时间差)
283
+ :param base_url: 基地址,如果为空,则用默认
284
+ :param extra_data: 附加数据
274
285
  :param async_: 是否异步
275
286
  :param request_kwargs: 其它请求参数
276
287
 
@@ -278,6 +289,8 @@ def _iterdir(
278
289
  """
279
290
  default_payload = payload
280
291
  page_size = int(payload.setdefault("limit", 100))
292
+ if base_url:
293
+ request_kwargs["base_url"] = base_url
281
294
  def gen_step():
282
295
  nonlocal parent_id
283
296
  dq: deque[tuple[int, int, str]] = deque()
@@ -290,13 +303,13 @@ def _iterdir(
290
303
  payload = {**default_payload, "parentFileId": parent_id}
291
304
  for i in count(1):
292
305
  payload["Page"] = i
293
- if last_ts and interval > 0 and (remains := last_ts + interval - time()) > 0:
306
+ if last_ts and cooldown > 0 and (remains := last_ts + cooldown - time()) > 0:
294
307
  if async_:
295
308
  yield async_sleep(remains)
296
309
  else:
297
310
  sleep(remains)
298
311
  resp = yield fs_files(payload, async_=async_, **request_kwargs)
299
- if interval > 0:
312
+ if cooldown > 0:
300
313
  last_ts = time()
301
314
  check_response(resp)
302
315
  info_list = resp["data"]["InfoList"]
@@ -320,6 +333,8 @@ def _iterdir(
320
333
  continue
321
334
  elif pred:
322
335
  if depth >= min_depth:
336
+ if extra_data:
337
+ info = dict(extra_data, **info)
323
338
  yield Yield(info)
324
339
  if pred is 1:
325
340
  continue
@@ -343,7 +358,8 @@ def iterdir(
343
358
  min_depth: int = 1,
344
359
  max_depth: int = 1,
345
360
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
346
- interval: int | float = 0,
361
+ cooldown: int | float = 0,
362
+ base_url: None | str | Callable[[], str] = None,
347
363
  use_list_new: bool = False,
348
364
  *,
349
365
  async_: Literal[False] = False,
@@ -357,7 +373,8 @@ def iterdir(
357
373
  min_depth: int = 1,
358
374
  max_depth: int = 1,
359
375
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
360
- interval: int | float = 0,
376
+ cooldown: int | float = 0,
377
+ base_url: None | str | Callable[[], str] = None,
361
378
  use_list_new: bool = False,
362
379
  *,
363
380
  async_: Literal[True],
@@ -370,7 +387,8 @@ def iterdir(
370
387
  min_depth: int = 1,
371
388
  max_depth: int = 1,
372
389
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
373
- interval: int | float = 0,
390
+ cooldown: int | float = 0,
391
+ base_url: None | str | Callable[[], str] = None,
374
392
  use_list_new: bool = False,
375
393
  *,
376
394
  async_: Literal[False, True] = False,
@@ -389,7 +407,8 @@ def iterdir(
389
407
  - 如果返回值是 False,则跳过此节点(但依然会继续处理位于此节点之下的节点)
390
408
  - 如果返回值是 True,则输出此节点
391
409
 
392
- :param interval: 两次调用之间,休息的时间
410
+ :param cooldown: 两次调用之间,冷却的时间(用两次调用开始时的时间差,而不是一次完成到下一次开始的时间差)
411
+ :param base_url: 基地址,如果为空,则用默认
393
412
  :param use_list_new: 使用 `P123Client.fs_list_new` 而不是 `P123Client.fs_list`
394
413
  :param async_: 是否异步
395
414
  :param request_kwargs: 其它请求参数
@@ -402,7 +421,8 @@ def iterdir(
402
421
  min_depth=min_depth,
403
422
  max_depth=max_depth,
404
423
  predicate=predicate,
405
- interval=interval,
424
+ cooldown=cooldown,
425
+ base_url=base_url,
406
426
  async_=async_,
407
427
  **request_kwargs,
408
428
  )
@@ -416,7 +436,8 @@ def share_iterdir(
416
436
  min_depth: int = 1,
417
437
  max_depth: int = 1,
418
438
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
419
- interval: int | float = 0,
439
+ cooldown: int | float = 0,
440
+ base_url: None | str | Callable[[], str] = None,
420
441
  *,
421
442
  async_: Literal[False] = False,
422
443
  **request_kwargs,
@@ -430,7 +451,8 @@ def share_iterdir(
430
451
  min_depth: int = 1,
431
452
  max_depth: int = 1,
432
453
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
433
- interval: int | float = 0,
454
+ cooldown: int | float = 0,
455
+ base_url: None | str | Callable[[], str] = None,
434
456
  *,
435
457
  async_: Literal[True],
436
458
  **request_kwargs,
@@ -443,15 +465,24 @@ def share_iterdir(
443
465
  min_depth: int = 1,
444
466
  max_depth: int = 1,
445
467
  predicate: None | Callable[[dict], Literal[None, 0, 1, False, True]] = None,
446
- interval: int | float = 0,
468
+ cooldown: int | float = 0,
469
+ base_url: None | str | Callable[[], str] = None,
447
470
  *,
448
471
  async_: Literal[False, True] = False,
449
472
  **request_kwargs,
450
473
  ) -> Iterator[dict] | AsyncIterator[dict]:
451
474
  """遍历分享的文件列表
452
475
 
453
- :param share_key: 分享码,在分享链接中的位置形如 "https://www.123pan.com/s/{share_key}"
454
- :param share_pwd: 密码,如果没有就不传
476
+ :param share_key: 分享码或者分享链接(可以携带提取码)
477
+
478
+ .. note::
479
+ 在分享链接中的位置形如 f"https://www.123pan.com/s/{share_key}"
480
+
481
+ 如果携带提取码,要写成 f"https://www.123pan.com/s/{share_key}?提取码:{share_pwd}"
482
+
483
+ 上面的基地址不必是 "https://www.123pan.com"
484
+
485
+ :param share_pwd: 提取码(4个文字),可以为空
455
486
  :param parent_id: 父目录 id,默认是根目录
456
487
  :param min_depth: 最小深度,小于此深度的不会输出
457
488
  :param max_depth: 最大深度,大于此深度的不会输出,如果小于 0 则无限
@@ -462,12 +493,22 @@ def share_iterdir(
462
493
  - 如果返回值是 False,则跳过此节点(但依然会继续处理位于此节点之下的节点)
463
494
  - 如果返回值是 True,则输出此节点
464
495
 
465
- :param interval: 两次调用之间,休息的时间
496
+ :param cooldown: 两次调用之间,冷却的时间(用两次调用开始时的时间差,而不是一次完成到下一次开始的时间差)
497
+ :param base_url: 基地址,如果为空,则用默认
466
498
  :param async_: 是否异步
467
499
  :param request_kwargs: 其它请求参数
468
500
 
469
501
  :return: 迭代器,产生文件或目录的信息
470
502
  """
503
+ if share_key.startswith(("http://", "https://")):
504
+ urlp = urlsplit(share_key)
505
+ # if not base_url:
506
+ # base_url = f"{urlp.scheme}://{urlp.netloc}"
507
+ share_key = urlp.path.rsplit("/", 1)[-1]
508
+ if not share_pwd:
509
+ share_pwd = urlp.query.rpartition(":")[-1]
510
+ if len(share_pwd) != 4:
511
+ share_pwd = ""
471
512
  return _iterdir(
472
513
  P123Client.share_fs_list,
473
514
  {"ShareKey": share_key, "SharePwd": share_pwd},
@@ -475,7 +516,9 @@ def share_iterdir(
475
516
  min_depth=min_depth,
476
517
  max_depth=max_depth,
477
518
  predicate=predicate,
478
- interval=interval,
519
+ cooldown=cooldown,
520
+ base_url=base_url,
521
+ extra_data={"ShareKey": share_key, "SharePwd": share_pwd},
479
522
  async_=async_,
480
523
  **request_kwargs,
481
524
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p123client
3
- Version: 0.0.6.8
3
+ Version: 0.0.6.9.4
4
4
  Summary: Python 123 webdisk client.
5
5
  Home-page: https://github.com/ChenyangGao/p123client
6
6
  License: MIT
@@ -33,6 +33,7 @@ Requires-Dist: python-http_request (>=0.0.7)
33
33
  Requires-Dist: python-httpfile (>=0.0.5)
34
34
  Requires-Dist: python-iterutils (>=0.2.4.1)
35
35
  Requires-Dist: python-property (>=0.0.3)
36
+ Requires-Dist: qrcode
36
37
  Requires-Dist: yarl
37
38
  Project-URL: Repository, https://github.com/ChenyangGao/p123client
38
39
  Description-Content-Type: text/markdown
@@ -45,7 +46,7 @@ Description-Content-Type: text/markdown
45
46
  ![PyPI - Format](https://img.shields.io/pypi/format/p123client)
46
47
  ![PyPI - Status](https://img.shields.io/pypi/status/p123client)
47
48
 
48
- ## 安装
49
+ ## 0. 安装
49
50
 
50
51
  你可以从 [pypi](https://pypi.org/project/p123client/) 安装最新版本
51
52
 
@@ -53,9 +54,7 @@ Description-Content-Type: text/markdown
53
54
  pip install -U p123client
54
55
  ```
55
56
 
56
- ## 入门介绍
57
-
58
- ### 1. 导入模块和创建实例
57
+ ## 1. 导入模块和创建实例
59
58
 
60
59
  导入模块
61
60
 
@@ -63,13 +62,21 @@ pip install -U p123client
63
62
  from p123client import P123Client
64
63
  ```
65
64
 
66
- 创建客户端对象,需要传入 JWT <kbd>token</kbd>
65
+ 创建客户端对象,需要传入 JWT <kbd>token</kbd>,也就是 `access_token`
67
66
 
68
67
  ```python
69
68
  token = "..."
70
69
  client = P123Client(token=token)
71
70
  ```
72
71
 
72
+ 不过我更推荐把 <kbd>token</kbd> 写入一个文件中,例如在 `~/123-token.txt`
73
+
74
+ ```python
75
+ from pathlib import Path
76
+
77
+ client = P123Client(token=Path("~/123-token.txt").expanduser())
78
+ ```
79
+
73
80
  你也可以用账户和密码登录
74
81
 
75
82
  ```python
@@ -78,7 +85,26 @@ password = "..." # 密码
78
85
  client = P123Client(passport, password)
79
86
  ```
80
87
 
81
- ### 2. 接口调用
88
+ 它的这个 <kbd>token</kbd>,就包含了有关信息
89
+
90
+ ```python
91
+ from base64 import b64decode
92
+ from json import loads
93
+
94
+ token = client.token
95
+
96
+ method, user_info, sign = token.split(".", 2)
97
+ print("JWT 算法:", loads(b64decode(method)))
98
+ print("用户信息:", loads(b64decode(user_info+"==")))
99
+ ```
100
+
101
+ 当你需要刷新此 <kbd>token</kbd> 时:
102
+
103
+ - 要么利用另一个 `refresh_token`
104
+ - 要么直接用这个 <kbd>token</kbd> 授权一次扫码,即可获得新的 <kbd>token</kbd>
105
+ - 要么重新用账号和密码登录
106
+
107
+ ## 2. 接口调用
82
108
 
83
109
  所有需要直接或间接执行 HTTP 请求的接口,都有同步和异步的调用方式,且默认是采用 POST 发送 JSON 请求数据
84
110
 
@@ -177,7 +203,7 @@ class MyCustom123Client(P123Client):
177
203
  )
178
204
  ```
179
205
 
180
- ### 3. 检查响应
206
+ ## 3. 检查响应
181
207
 
182
208
  接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p123client.check_response` 执行检查。首先会查看其中名为 "code" 的键的对应值,如果为 0 或 200 或者不存在,则原样返回被检查的数据;否则,抛出一个 `p123client.P123OSError` 的实例。
183
209
 
@@ -190,7 +216,7 @@ data = check_response(client.method(payload))
190
216
  data = check_response(await client.method(payload, async_=True))
191
217
  ```
192
218
 
193
- ### 4. 辅助工具
219
+ ## 4. 辅助工具
194
220
 
195
221
  一些简单的封装工具可能是必要的,特别是那种实现起来代码量比较少,可以封装成单个函数的。我把平常使用过程中,积累的一些经验具体化为一组工具函数。这些工具函数分别有着不同的功能,如果组合起来使用,或许能解决很多问题。
196
222
 
@@ -198,7 +224,9 @@ data = check_response(await client.method(payload, async_=True))
198
224
  from p123client import tool
199
225
  ```
200
226
 
201
- #### 1. 创建自定义 uri
227
+ ## 5. 学习案例
228
+
229
+ ### 1. 创建自定义 uri
202
230
 
203
231
  ```python
204
232
  from p123client import P123Client
@@ -212,7 +240,7 @@ file_id = 15688945
212
240
  print(make_uri(client, file_id))
213
241
  ```
214
242
 
215
- #### 2. 由自定义 uri 转存文件到你的网盘
243
+ ### 2. 由自定义 uri 转存文件到你的网盘
216
244
 
217
245
  ```python
218
246
  from p123client import P123Client
@@ -225,7 +253,7 @@ uri = "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd"
225
253
  print(upload_uri(client, uri, duplicate=1))
226
254
  ```
227
255
 
228
- #### 3. 由自定义 uri 获取下载直链
256
+ ### 3. 由自定义 uri 获取下载直链
229
257
 
230
258
  ```python
231
259
  from p123client import P123Client
@@ -240,7 +268,7 @@ print(get_downurl(client, "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512
240
268
  print(get_downurl(client, "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd"))
241
269
  ```
242
270
 
243
- #### 4. 直链服务
271
+ ### 4. 直链服务
244
272
 
245
273
  需要先安装 [fastapi](https://pypi.org/project/fastapi/)
246
274
 
@@ -289,9 +317,11 @@ if __name__ == "__main__":
289
317
  run(app, host="0.0.0.0", port=8123)
290
318
  ```
291
319
 
292
- #### 5. 遍历文件列表
320
+ ### 5. 遍历文件列表
293
321
 
294
- **遍历网盘中的文件列表**
322
+ 导出的文件信息中,有个 `"uri"`,表示文件的自定义链接,前面以 `123://` 开头,你可以替换成 302 服务的地址,例如 `http://localhost:8123/`
323
+
324
+ #### 1. 遍历网盘中的文件列表
295
325
 
296
326
  ```python
297
327
  from p123client import P123Client
@@ -300,21 +330,87 @@ from p123client.tool import iterdir
300
330
  # TODO: 改成你自己的账户和密码
301
331
  client = P123Client(passport="手机号或邮箱", password="登录密码")
302
332
 
303
- for info in iterdir(client, parent_id=0, max_depth=-1, predicate=lambda a: not a["is_dir"]):
333
+ for info in iterdir(client, parent_id=0, max_depth=-1):
304
334
  print(info)
305
335
  ```
306
336
 
307
- **遍历分享中的文件列表(无需登录)**
337
+ #### 2. 遍历分享中的文件列表,不含目录
308
338
 
309
339
  ```python
310
340
  from p123client.tool import share_iterdir
311
341
 
312
- # TODO: 分享码
313
- share_key = "g0n0Vv-2sbI"
314
- # TODO: 密码
315
- share_pwd = ""
316
-
317
- for info in share_iterdir(share_key, share_pwd, parent_id=0, max_depth=-1, predicate=lambda a: not a["is_dir"]):
342
+ # NOTE: 无需登录
343
+ for info in share_iterdir(
344
+ "https://www.123684.com/s/oec7Vv-CIYWh?提取码:ZY4K",
345
+ max_depth=-1,
346
+ predicate=lambda a: not a["is_dir"],
347
+ ):
318
348
  print(info)
319
349
  ```
320
350
 
351
+ #### 3. 导出分享中的文件列表到本地 .json 文件
352
+
353
+ ```python
354
+ from orjson import dumps
355
+ from p123client.tool import share_iterdir
356
+
357
+ def export_share_files_json(
358
+ link: str,
359
+ path: str = "",
360
+ cooldown: float = 0,
361
+ ):
362
+ """把分享链接中的文件列表导出到 json 文件
363
+
364
+ :param link: 分享链接,可以包含提取码
365
+ :param path: 保存的路径,如果不提供则自动确定
366
+ :param cooldown: 两次调用之间,冷却的时间(用两次调用开始时的时间差,而不是一次完成到下一次开始的时间差)
367
+ """
368
+ print("< 将拉取:", link)
369
+ ls: list[dict] = []
370
+ for i, a in enumerate(share_iterdir(link, max_depth=-1, cooldown=cooldown), 1):
371
+ ls.append(a)
372
+ print(i, a)
373
+ if ls:
374
+ info = ls[0]
375
+ if not path:
376
+ suffix = f"@{info['ShareKey']},{info['SharePwd']}.json"
377
+ path = f"{info['name'][:255-len(suffix)]}{suffix}"
378
+ open(path, "wb").write(dumps(ls))
379
+ print()
380
+ print("> 已拉取:", link)
381
+ print("> 已保存:", path)
382
+
383
+ export_share_files_json("https://www.123684.com/s/oec7Vv-CIYWh?提取码:ZY4K")
384
+ ```
385
+
386
+ ### 6. 最新的操作记录
387
+
388
+ 在网盘中,你可以按更新时间逆序查询,即可得到最新上传的文件列表
389
+
390
+ ```python
391
+ client.fs_list_new({
392
+ "orderBy": "update_time",
393
+ "orderDirection": "desc",
394
+ "SearchData": ".",
395
+ })
396
+ ```
397
+
398
+ 更一般的,你可以在[同步空间](https://www.123pan.com/SynchronousSpace/main)中执行文件操作。
399
+
400
+ 而在拉取文件列表时,需要指定
401
+
402
+ ```python
403
+ client.fs_list_new({
404
+ "operateType": "SyncSpacePage",
405
+ "event": "syncFileList",
406
+ "RequestSource": 1,
407
+ })
408
+ ```
409
+
410
+ 同步空间中的增删改操作都有[操作记录](https://www.123pan.com/SynchronousSpace/record),你可以用接口
411
+
412
+ ```python
413
+ client.fs_sync_log()
414
+ ```
415
+
416
+
@@ -0,0 +1,12 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ p123client/__init__.py,sha256=gfUum-q3f_XuXOk2HpArDAIxAlscZm8Fau1kiNkNFpg,214
3
+ p123client/client.py,sha256=dD7I0mmsJ6qkW28avOTjoomX9QXf-ImrShLPDMqZxrI,311381
4
+ p123client/const.py,sha256=UYfBrgciCz7RJJPqCX6zwWlMoWiaTUwUU8bpFJyh0sM,348
5
+ p123client/exception.py,sha256=Vn2UlJulY63z3cF7bnTjMlZ6og7gCw1pAA-dswFwKpQ,3174
6
+ p123client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ p123client/tool/__init__.py,sha256=YAIdp-xJ-oSXu6yDtOZGbUEvlE0eW0PV8h8fqlFMkiQ,18819
8
+ p123client/type.py,sha256=T17OzPQrnIG6w_Hzjc8TF_fFMKa-hQMSn1gff8pVcBc,56
9
+ p123client-0.0.6.9.4.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
10
+ p123client-0.0.6.9.4.dist-info/METADATA,sha256=m-kFfpuYOPS62Bm7USaUl_k7o4IlsuFGmlPfoPaFv2g,11619
11
+ p123client-0.0.6.9.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
12
+ p123client-0.0.6.9.4.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
- p123client/__init__.py,sha256=gfUum-q3f_XuXOk2HpArDAIxAlscZm8Fau1kiNkNFpg,214
3
- p123client/client.py,sha256=vK7kdjvd7I3iVUroiC89E1rt-HG-A1YhwECAaGr8aqk,258512
4
- p123client/const.py,sha256=T17OzPQrnIG6w_Hzjc8TF_fFMKa-hQMSn1gff8pVcBc,56
5
- p123client/exception.py,sha256=020xGo8WQmGCJz1UzNg9oFzpEvToQcgTye0s6lkFASQ,1540
6
- p123client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- p123client/tool/__init__.py,sha256=MFMue0hh3czAtfTf3yInzBgcNIWVVjO-8tHrPka4TGA,16454
8
- p123client/type.py,sha256=T17OzPQrnIG6w_Hzjc8TF_fFMKa-hQMSn1gff8pVcBc,56
9
- p123client-0.0.6.8.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
10
- p123client-0.0.6.8.dist-info/METADATA,sha256=kQ9PI-wnjaB8NQe1vL8J0dzlZsLqinejvRpxB615KH0,8859
11
- p123client-0.0.6.8.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
12
- p123client-0.0.6.8.dist-info/RECORD,,