p115client 0.0.5.10.3__py3-none-any.whl → 0.0.5.10.5__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.
@@ -8,8 +8,8 @@ __all__ = [
8
8
  "iter_nodes_skim", "iter_stared_dirs_raw", "iter_stared_dirs", "ensure_attr_path",
9
9
  "ensure_attr_path_by_category_get", "iterdir_raw", "iterdir", "iterdir_limited",
10
10
  "iter_files_raw", "iter_files", "traverse_files", "iter_dirs", "iter_dupfiles",
11
- "iter_image_files", "share_iterdir", "share_iter_files", "iter_selected_nodes",
12
- "iter_selected_nodes_by_pickcode", "iter_selected_nodes_using_category_get",
11
+ "iter_image_files", "share_iterdir", "share_iter_files", "share_get_id_to_path",
12
+ "iter_selected_nodes", "iter_selected_nodes_by_pickcode", "iter_selected_nodes_using_category_get",
13
13
  "iter_selected_nodes_using_edit", "iter_selected_nodes_using_star_event",
14
14
  "iter_selected_dirs_using_star", "iter_files_with_dirname", "iter_files_with_path",
15
15
  "iter_files_with_path_by_export_dir", "iter_parents_3_level", "iter_dir_nodes",
@@ -59,30 +59,6 @@ from .life import iter_life_behavior_once, life_show
59
59
  from .util import posix_escape_name, share_extract_payload, unescape_115_charref
60
60
 
61
61
 
62
- WEBAPI_BASE_URLS = (
63
- "http://webapi.115.com",
64
- "https://webapi.115.com",
65
- "http://webapi.115.com",
66
- "http://115cdn.com/webapi",
67
- "http://webapi.115.com",
68
- "http://115vod.com/webapi",
69
- )
70
- PROAPI_BASE_URLS = (
71
- "http://proapi.115.com",
72
- "https://proapi.115.com",
73
- "http://proapi.115.com",
74
- "https://proapi.115.com",
75
- )
76
- APS_BASE_URLS = (
77
- "http://115cdn.com/aps",
78
- "http://aps.115.com",
79
- "http://115vod.com/aps",
80
- )
81
-
82
- _n_get_ancestors = 0
83
- _n_get_count = 0
84
-
85
-
86
62
  class DirNode(NamedTuple):
87
63
  name: str
88
64
  parent_id: int
@@ -104,7 +80,7 @@ class OverviewAttr:
104
80
 
105
81
 
106
82
  #: 用于缓存每个用户(根据用户 id 区别)的每个目录 id 到所对应的 (名称, 父id) 的元组的字典的字典
107
- ID_TO_DIRNODE_CACHE: Final[defaultdict[int, dict[int, tuple[str, int] | DirNode]]] = defaultdict(dict)
83
+ ID_TO_DIRNODE_CACHE: Final[defaultdict[int | tuple[int, str], dict[int, tuple[str, int] | DirNode]]] = defaultdict(dict)
108
84
 
109
85
 
110
86
  def _overview_attr(info: Mapping, /) -> OverviewAttr:
@@ -117,8 +93,8 @@ def _overview_attr(info: Mapping, /) -> OverviewAttr:
117
93
  else:
118
94
  id = int(info["fid"])
119
95
  pid = int(info["cid"])
120
- ctime = int(info["tp"])
121
- mtime = int(info["te"])
96
+ ctime = int(info.get("tp") or info["t"])
97
+ mtime = int(info.get("te") or info["t"])
122
98
  elif "fn" in info:
123
99
  is_dir = info["fc"] == "0"
124
100
  name = info["fn"]
@@ -152,7 +128,7 @@ def get_path_to_cid(
152
128
  root_id: None | int = None,
153
129
  escape: None | bool | Callable[[str], str] = True,
154
130
  refresh: bool = False,
155
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
131
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
156
132
  app: str = "web",
157
133
  *,
158
134
  async_: Literal[False] = False,
@@ -166,7 +142,7 @@ def get_path_to_cid(
166
142
  root_id: None | int = None,
167
143
  escape: None | bool | Callable[[str], str] = True,
168
144
  refresh: bool = False,
169
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
145
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
170
146
  app: str = "web",
171
147
  *,
172
148
  async_: Literal[True],
@@ -179,7 +155,7 @@ def get_path_to_cid(
179
155
  root_id: None | int = None,
180
156
  escape: None | bool | Callable[[str], str] = True,
181
157
  refresh: bool = False,
182
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
158
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
183
159
  app: str = "web",
184
160
  *,
185
161
  async_: Literal[False, True] = False,
@@ -215,14 +191,31 @@ def get_path_to_cid(
215
191
  escape = cast(None | Callable[[str], str], escape)
216
192
  if id_to_dirnode is None:
217
193
  id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
194
+ elif id_to_dirnode is ...:
195
+ id_to_dirnode = {}
218
196
  def gen_step():
219
197
  nonlocal cid
220
198
  parts: list[str] = []
221
199
  if cid and (refresh or cid not in id_to_dirnode):
222
- if app in ("", "web", "desktop", "harmony"):
223
- resp = yield client.fs_files({"cid": cid, "limit": 1}, async_=async_, **request_kwargs)
200
+ if not isinstance(client, P115Client) or app == "open":
201
+ resp = yield client.fs_files_open(
202
+ {"cid": cid, "cur": 1, "nf": 1, "hide_data": 1},
203
+ async_=async_,
204
+ **request_kwargs,
205
+ )
206
+ elif app in ("", "web", "desktop", "harmony"):
207
+ resp = yield client.fs_files_aps(
208
+ {"cid": cid, "limit": 1, "nf": 1, "star": 1},
209
+ async_=async_,
210
+ **request_kwargs,
211
+ )
224
212
  else:
225
- resp = yield client.fs_files_app({"cid": cid, "hide_data": 1}, async_=async_, **request_kwargs)
213
+ resp = yield client.fs_files_app(
214
+ {"cid": cid, "cur": 1, "nf": 1, "hide_data": 1},
215
+ app=app,
216
+ async_=async_,
217
+ **request_kwargs,
218
+ )
226
219
  check_response(resp)
227
220
  if cid and int(resp["path"][-1]["cid"]) != cid:
228
221
  raise FileNotFoundError(ENOENT, cid)
@@ -252,6 +245,8 @@ def get_file_count(
252
245
  client: str | P115Client,
253
246
  cid: int = 0,
254
247
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
248
+ app: str = "web",
249
+ use_fs_files: bool = True,
255
250
  *,
256
251
  async_: Literal[False] = False,
257
252
  **request_kwargs,
@@ -262,6 +257,8 @@ def get_file_count(
262
257
  client: str | P115Client,
263
258
  cid: int = 0,
264
259
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
260
+ app: str = "web",
261
+ use_fs_files: bool = True,
265
262
  *,
266
263
  async_: Literal[True],
267
264
  **request_kwargs,
@@ -271,6 +268,8 @@ def get_file_count(
271
268
  client: str | P115Client,
272
269
  cid: int = 0,
273
270
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
271
+ app: str = "web",
272
+ use_fs_files: bool = True,
274
273
  *,
275
274
  async_: Literal[False, True] = False,
276
275
  **request_kwargs,
@@ -283,6 +282,8 @@ def get_file_count(
283
282
  :param client: 115 客户端或 cookies
284
283
  :param cid: 目录 id
285
284
  :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
285
+ :param app: 使用某个 app (设备)的接口
286
+ :param use_fs_files: 使用 `client.fs_files`,否则使用 `client.fs_category_get`
286
287
  :param async_: 是否异步
287
288
  :param request_kwargs: 其它请求参数
288
289
 
@@ -292,75 +293,78 @@ def get_file_count(
292
293
  client = P115Client(client, check_for_relogin=True)
293
294
  if id_to_dirnode is None:
294
295
  id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
295
- n_webapi = len(WEBAPI_BASE_URLS)
296
- n_proapi = len(PROAPI_BASE_URLS)
297
- n_apsapi = len(APS_BASE_URLS)
298
- n_api = n_webapi * 2 + n_proapi * 2 + n_apsapi
299
- def get_resp():
300
- global _n_get_count
301
- n = _n_get_count % n_api
302
- if n < n_webapi:
303
- _n_get_count += 1
296
+ def get_resp_of_fs_files(id: int, /):
297
+ if not isinstance(client, P115Client) or app == "open":
298
+ return client.fs_files_open(
299
+ {"cid": id, "hide_data": 1, "show_dir": 0},
300
+ async_=async_,
301
+ **request_kwargs,
302
+ )
303
+ elif app in ("", "web", "desktop", "harmony"):
304
304
  return client.fs_files(
305
- {"cid": cid, "limit": 1, "show_dir": 0},
306
- base_url=WEBAPI_BASE_URLS[n],
305
+ {"cid": id, "limit": 1, "show_dir": 0},
307
306
  async_=async_,
308
307
  **request_kwargs,
309
308
  )
310
- n -= n_webapi
311
- if n < n_proapi:
312
- _n_get_count += 1
309
+ elif app == "aps":
310
+ return client.fs_files_aps(
311
+ {"cid": id, "limit": 1, "show_dir": 0},
312
+ async_=async_,
313
+ **request_kwargs,
314
+ )
315
+ else:
313
316
  return client.fs_files_app(
314
- {"cid": cid, "hide_data": 1, "show_dir": 0},
315
- base_url=PROAPI_BASE_URLS[n],
317
+ {"cid": id, "hide_data": 1, "show_dir": 0},
318
+ app=app,
316
319
  async_=async_,
317
320
  **request_kwargs,
318
321
  )
319
- n -= n_proapi
320
- if n < n_apsapi:
321
- _n_get_count += 1
322
- return client.fs_files_aps(
323
- {"cid": cid, "limit": 1, "show_dir": 0},
324
- base_url=APS_BASE_URLS[n],
322
+ def get_resp_of_category_get(id: int, /):
323
+ if not isinstance(client, P115Client) or app == "open":
324
+ return client.fs_info_open(
325
+ id,
325
326
  async_=async_,
326
327
  **request_kwargs,
327
328
  )
328
- n -= n_apsapi
329
- if n < n_webapi:
330
- _n_get_count += 1
329
+ elif app in ("", "web", "desktop", "harmony", "aps"):
331
330
  return client.fs_category_get(
332
- cid,
333
- base_url=WEBAPI_BASE_URLS[n],
331
+ id,
332
+ async_=async_,
333
+ **request_kwargs,
334
+ )
335
+ else:
336
+ return client.fs_category_get_app(
337
+ id,
338
+ app=app,
334
339
  async_=async_,
335
340
  **request_kwargs,
336
341
  )
337
- n -= n_webapi
338
- _n_get_count += 1
339
- return client.fs_category_get_app(
340
- cid,
341
- base_url=PROAPI_BASE_URLS[n],
342
- async_=async_,
343
- **request_kwargs,
344
- )
345
342
  def gen_step():
346
- if cid == 0:
343
+ if not cid:
347
344
  resp = yield client.fs_space_summury(async_=async_, **request_kwargs)
348
345
  check_response(resp)
349
346
  return sum(v["count"] for k, v in resp["type_summury"].items() if k.isupper())
350
- resp = yield get_resp()
351
- if not resp:
352
- raise FileNotFoundError(ENOENT, cid)
353
- check_response(resp)
354
- resp["cid"] = cid
355
- if "path" in resp:
347
+ if use_fs_files:
348
+ resp = yield get_resp_of_fs_files(cid)
349
+ check_response(resp)
356
350
  if cid != int(resp["path"][-1]["cid"]):
351
+ resp["cid"] = cid
357
352
  raise NotADirectoryError(ENOTDIR, resp)
358
353
  if id_to_dirnode is not ...:
359
354
  for info in resp["path"][1:]:
360
355
  id_to_dirnode[int(info["cid"])] = DirNode(info["name"], int(info["pid"]))
361
356
  return int(resp["count"])
362
357
  else:
358
+ resp = yield get_resp_of_category_get(cid)
359
+ if not resp:
360
+ raise FileNotFoundError(ENOENT, cid)
361
+ if "paths" not in resp:
362
+ check_response(resp)
363
+ resp = resp["data"]
364
+ if not resp:
365
+ raise FileNotFoundError(ENOENT, cid)
363
366
  if int(resp["file_category"]):
367
+ resp["cid"] = cid
364
368
  raise NotADirectoryError(ENOTDIR, resp)
365
369
  if id_to_dirnode is not ...:
366
370
  pid = 0
@@ -374,8 +378,9 @@ def get_file_count(
374
378
  @overload
375
379
  def get_ancestors(
376
380
  client: str | P115Client,
377
- attr: dict,
381
+ attr: int | dict,
378
382
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
383
+ app: str = "web",
379
384
  *,
380
385
  async_: Literal[False] = False,
381
386
  **request_kwargs,
@@ -384,8 +389,9 @@ def get_ancestors(
384
389
  @overload
385
390
  def get_ancestors(
386
391
  client: str | P115Client,
387
- attr: dict,
392
+ attr: int | dict,
388
393
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
394
+ app: str = "web",
389
395
  *,
390
396
  async_: Literal[True],
391
397
  **request_kwargs,
@@ -393,8 +399,9 @@ def get_ancestors(
393
399
  ...
394
400
  def get_ancestors(
395
401
  client: str | P115Client,
396
- attr: dict,
402
+ attr: int | dict,
397
403
  id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
404
+ app: str = "web",
398
405
  *,
399
406
  async_: Literal[False, True] = False,
400
407
  **request_kwargs,
@@ -405,8 +412,9 @@ def get_ancestors(
405
412
  我通过一些经验,搭配了多个接口的占比和参数分布,可能不够合理,以后会根据实际情况调整
406
413
 
407
414
  :param client: 115 客户端或 cookies
408
- :param attr: 待查询节点的信息(必须有 id parent_id
415
+ :param attr: 待查询节点 `id` 或信息(必须有 `id`,可选有 `parent_id`)
409
416
  :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
417
+ :param app: 使用某个 app (设备)的接口
410
418
  :param async_: 是否异步
411
419
  :param request_kwargs: 其它请求参数
412
420
 
@@ -424,89 +432,168 @@ def get_ancestors(
424
432
  client = P115Client(client, check_for_relogin=True)
425
433
  if id_to_dirnode is None:
426
434
  id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
427
- n_webapi = len(WEBAPI_BASE_URLS)
428
- n_proapi = len(PROAPI_BASE_URLS)
429
- n_apsapi = len(APS_BASE_URLS)
430
- n_api = n_webapi * 2 + n_proapi * 2 + n_apsapi
431
- def get_resp():
432
- global _n_get_ancestors
433
- n = _n_get_ancestors % n_api
434
- if n < n_webapi:
435
- _n_get_ancestors += 1
435
+ def get_resp_of_fs_files(id: int, /):
436
+ if not isinstance(client, P115Client) or app == "open":
437
+ return client.fs_files_open(
438
+ {"cid": id, "cur": 1, "nf": 1, "hide_data": 1},
439
+ async_=async_,
440
+ **request_kwargs,
441
+ )
442
+ elif app in ("", "web", "desktop", "harmony"):
436
443
  return client.fs_files(
437
- {"cid": attr["parent_id"], "limit": 1},
438
- base_url=WEBAPI_BASE_URLS[n],
444
+ {"cid": id, "limit": 1, "nf": 1, "star": 1},
439
445
  async_=async_,
440
446
  **request_kwargs,
441
447
  )
442
- n -= n_webapi
443
- if n < n_proapi:
444
- _n_get_ancestors += 1
448
+ elif app == "aps":
449
+ return client.fs_files_aps(
450
+ {"cid": id, "limit": 1, "nf": 1, "star": 1},
451
+ async_=async_,
452
+ **request_kwargs,
453
+ )
454
+ else:
445
455
  return client.fs_files_app(
446
- {"cid": attr["parent_id"], "hide_data": 1},
447
- base_url=PROAPI_BASE_URLS[n],
456
+ {"cid": id, "cur": 1, "nf": 1, "hide_data": 1},
457
+ app=app,
448
458
  async_=async_,
449
459
  **request_kwargs,
450
460
  )
451
- n -= n_proapi
452
- if n < n_apsapi:
453
- _n_get_ancestors += 1
454
- return client.fs_files_aps(
455
- {"cid": attr["parent_id"], "limit": 1},
456
- base_url=APS_BASE_URLS[n],
461
+ def get_resp_of_category_get(id: int, /):
462
+ if not isinstance(client, P115Client) or app == "open":
463
+ return client.fs_info_open(
464
+ id,
457
465
  async_=async_,
458
466
  **request_kwargs,
459
467
  )
460
- if attr.get("is_dir", False) or attr.get("is_directory", False):
461
- _n_get_ancestors = 0
462
- return get_resp()
463
- n -= n_apsapi
464
- if n < n_webapi:
465
- _n_get_ancestors += 1
468
+ elif app in ("", "web", "desktop", "harmony", "aps"):
466
469
  return client.fs_category_get(
467
- attr["id"],
468
- base_url=WEBAPI_BASE_URLS[n],
470
+ id,
471
+ async_=async_,
472
+ **request_kwargs,
473
+ )
474
+ else:
475
+ return client.fs_category_get_app(
476
+ id,
477
+ app=app,
469
478
  async_=async_,
470
479
  **request_kwargs,
471
480
  )
472
- n -= n_webapi
473
- _n_get_ancestors += 1
474
- return client.fs_category_get_app(
475
- attr["id"],
476
- base_url=PROAPI_BASE_URLS[n],
477
- async_=async_,
478
- **request_kwargs,
479
- )
480
481
  def gen_step():
481
- if not attr["parent_id"]:
482
- return [{"id": 0, "parent_id": 0, "name": ""}]
483
- resp = yield get_resp()
484
- if not resp:
485
- raise FileNotFoundError(ENOENT, attr)
486
- check_response(resp)
487
- resp["attr"] = attr
488
482
  ancestors: list[dict] = [{"id": 0, "parent_id": 0, "name": ""}]
489
483
  add_ancestor = ancestors.append
490
484
  pid = 0
491
- if "path" in resp:
492
- if attr["parent_id"] != int(resp["path"][-1]["cid"]):
493
- raise FileNotFoundError(ENOENT, resp)
494
- for info in resp["path"][1:]:
495
- add_ancestor({
496
- "parent_id": pid,
497
- "id": (pid := int(info["cid"])),
498
- "name": info["name"],
499
- })
485
+ is_completed = False
486
+ if isinstance(attr, dict):
487
+ fid = cast(int, attr["id"])
488
+ if not fid:
489
+ return ancestors
490
+ is_dir: None | bool = attr.get("is_dir") or attr.get("is_directory")
491
+ if is_dir is None:
492
+ if "parent_id" in attr:
493
+ cid = cast(int, attr["parent_id"])
494
+ resp = yield get_resp_of_fs_files(cid)
495
+ if cid != int(resp["path"][-1]["cid"]):
496
+ resp["attr"] = attr
497
+ raise FileNotFoundError(ENOENT, resp)
498
+ for info in resp["path"][1:]:
499
+ add_ancestor({
500
+ "parent_id": pid,
501
+ "id": (pid := int(info["cid"])),
502
+ "name": info["name"],
503
+ })
504
+ if id_to_dirnode is not ...:
505
+ for ans in ancestors[1:]:
506
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
507
+ if "name" in attr:
508
+ name = attr["name"]
509
+ is_dir = bool(attr.get("is_dir"))
510
+ else:
511
+ resp = yield client.fs_file_skim(attr["id"], async_=async_, **request_kwargs)
512
+ check_response(resp)
513
+ name = unescape_115_charref(resp["data"]["file_name"])
514
+ is_dir = not resp["data"]["sha1"]
515
+ ans = {"id": fid, "parent_id": pid, "name": name}
516
+ add_ancestor(ans)
517
+ if is_dir and id_to_dirnode is not ...:
518
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
519
+ is_completed = True
520
+ elif is_dir:
521
+ resp = yield get_resp_of_fs_files(fid)
522
+ if fid != int(resp["path"][-1]["cid"]):
523
+ resp["attr"] = attr
524
+ raise FileNotFoundError(ENOENT, resp)
525
+ for info in resp["path"][1:]:
526
+ add_ancestor({
527
+ "parent_id": pid,
528
+ "id": (pid := int(info["cid"])),
529
+ "name": info["name"],
530
+ })
531
+ if id_to_dirnode is not ...:
532
+ for ans in ancestors[1:]:
533
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
534
+ is_completed = True
535
+ else:
536
+ resp = yield get_resp_of_category_get(fid)
537
+ if not resp:
538
+ raise FileNotFoundError(ENOENT, attr)
539
+ if "paths" not in resp:
540
+ check_response(resp)
541
+ resp = resp["data"]
542
+ if not resp:
543
+ raise FileNotFoundError(ENOENT, attr)
544
+ for info in resp["paths"]:
545
+ add_ancestor({
546
+ "parent_id": pid,
547
+ "id": (pid := int(info["file_id"])),
548
+ "name": info["file_name"],
549
+ })
550
+ if id_to_dirnode is not ...:
551
+ for ans in ancestors[1:]:
552
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
553
+ ans = {"id": fid, "parent_id": pid, "name": resp["file_name"]}
554
+ add_ancestor(ans)
555
+ if not resp.get("sha1") and id_to_dirnode is not ...:
556
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
557
+ is_completed = True
500
558
  else:
501
- for info in resp["paths"]:
502
- add_ancestor({
503
- "parent_id": pid,
504
- "id": (pid := int(info["file_id"])),
505
- "name": info["file_name"],
506
- })
507
- if id_to_dirnode is not ...:
508
- for ans in ancestors[1:]:
509
- id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
559
+ fid = attr
560
+ if not is_completed:
561
+ if not fid:
562
+ return ancestors
563
+ resp = yield get_resp_of_fs_files(fid)
564
+ check_response(resp)
565
+ if fid == int(resp["path"][-1]["cid"]):
566
+ for info in resp["path"][1:]:
567
+ add_ancestor({
568
+ "parent_id": pid,
569
+ "id": (pid := int(info["cid"])),
570
+ "name": info["name"],
571
+ })
572
+ if id_to_dirnode is not ...:
573
+ for ans in ancestors[1:]:
574
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
575
+ else:
576
+ resp = yield get_resp_of_category_get(fid)
577
+ if not resp:
578
+ raise FileNotFoundError(ENOENT, fid)
579
+ if "paths" not in resp:
580
+ check_response(resp)
581
+ resp = resp["data"]
582
+ if not resp:
583
+ raise FileNotFoundError(ENOENT, fid)
584
+ for info in resp["paths"]:
585
+ add_ancestor({
586
+ "parent_id": pid,
587
+ "id": (pid := int(info["file_id"])),
588
+ "name": info["file_name"],
589
+ })
590
+ if id_to_dirnode is not ...:
591
+ for ans in ancestors[1:]:
592
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
593
+ ans = {"id": fid, "parent_id": pid, "name": resp["file_name"]}
594
+ add_ancestor(ans)
595
+ if not resp.get("sha1") and id_to_dirnode is not ...:
596
+ id_to_dirnode[ans["id"]] = DirNode(ans["name"], ans["parent_id"])
510
597
  return ancestors
511
598
  return run_gen_step(gen_step, async_=async_)
512
599
 
@@ -516,7 +603,7 @@ def get_ancestors_to_cid(
516
603
  client: str | P115Client,
517
604
  cid: int = 0,
518
605
  refresh: bool = False,
519
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
606
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
520
607
  app: str = "web",
521
608
  *,
522
609
  async_: Literal[False] = False,
@@ -528,7 +615,7 @@ def get_ancestors_to_cid(
528
615
  client: str | P115Client,
529
616
  cid: int = 0,
530
617
  refresh: bool = False,
531
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
618
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
532
619
  app: str = "web",
533
620
  *,
534
621
  async_: Literal[True],
@@ -539,7 +626,7 @@ def get_ancestors_to_cid(
539
626
  client: str | P115Client,
540
627
  cid: int = 0,
541
628
  refresh: bool = False,
542
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
629
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
543
630
  app: str = "web",
544
631
  *,
545
632
  async_: Literal[False, True] = False,
@@ -569,14 +656,31 @@ def get_ancestors_to_cid(
569
656
  client = P115Client(client, check_for_relogin=True)
570
657
  if id_to_dirnode is None:
571
658
  id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
659
+ elif id_to_dirnode is ...:
660
+ id_to_dirnode = {}
572
661
  def gen_step():
573
662
  nonlocal cid
574
663
  parts: list[dict] = []
575
664
  if cid and (refresh or cid not in id_to_dirnode):
576
- if app in ("", "web", "desktop", "harmony"):
577
- resp = yield client.fs_files({"cid": cid, "limit": 1}, async_=async_, **request_kwargs)
665
+ if not isinstance(client, P115Client) or app == "open":
666
+ resp = yield client.fs_files_open(
667
+ {"cid": cid, "cur": 1, "nf": 1, "hide_data": 1},
668
+ async_=async_,
669
+ **request_kwargs,
670
+ )
671
+ elif app in ("", "web", "desktop", "harmony"):
672
+ resp = yield client.fs_files_aps(
673
+ {"cid": cid, "limit": 1, "nf": 1, "star": 1},
674
+ async_=async_,
675
+ **request_kwargs,
676
+ )
578
677
  else:
579
- resp = yield client.fs_files_app({"cid": cid, "hide_data": 1}, async_=async_, **request_kwargs)
678
+ resp = yield client.fs_files_app(
679
+ {"cid": cid, "cur": 1, "nf": 1, "hide_data": 1},
680
+ app=app,
681
+ async_=async_,
682
+ **request_kwargs,
683
+ )
580
684
  check_response(resp)
581
685
  if cid and int(resp["path"][-1]["cid"]) != cid:
582
686
  raise FileNotFoundError(ENOENT, cid)
@@ -596,18 +700,18 @@ def get_ancestors_to_cid(
596
700
  return run_gen_step(gen_step, async_=async_)
597
701
 
598
702
 
599
-
600
-
601
703
  # TODO: 使用 search 接口以在特定目录之下搜索某个名字,以便减少风控
602
704
  @overload
603
705
  def get_id_to_path(
604
706
  client: str | P115Client,
605
707
  path: str | Sequence[str],
708
+ parent_id: int = 0,
606
709
  ensure_file: None | bool = None,
607
710
  is_posixpath: bool = False,
608
711
  refresh: bool = False,
609
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
712
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
610
713
  app: str = "web",
714
+ dont_use_getid: bool = False,
611
715
  *,
612
716
  async_: Literal[False] = False,
613
717
  **request_kwargs,
@@ -617,11 +721,13 @@ def get_id_to_path(
617
721
  def get_id_to_path(
618
722
  client: str | P115Client,
619
723
  path: str | Sequence[str],
724
+ parent_id: int = 0,
620
725
  ensure_file: None | bool = None,
621
726
  is_posixpath: bool = False,
622
727
  refresh: bool = False,
623
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
728
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
624
729
  app: str = "web",
730
+ dont_use_getid: bool = False,
625
731
  *,
626
732
  async_: Literal[True],
627
733
  **request_kwargs,
@@ -630,11 +736,13 @@ def get_id_to_path(
630
736
  def get_id_to_path(
631
737
  client: str | P115Client,
632
738
  path: str | Sequence[str],
739
+ parent_id: int = 0,
633
740
  ensure_file: None | bool = None,
634
741
  is_posixpath: bool = False,
635
742
  refresh: bool = False,
636
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
743
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
637
744
  app: str = "web",
745
+ dont_use_getid: bool = False,
638
746
  *,
639
747
  async_: Literal[False, True] = False,
640
748
  **request_kwargs,
@@ -643,6 +751,7 @@ def get_id_to_path(
643
751
 
644
752
  :param client: 115 客户端或 cookies
645
753
  :param path: 路径
754
+ :param parent_id: 上级目录的 id
646
755
  :param ensure_file: 是否确保为文件
647
756
 
648
757
  - True: 必须是文件
@@ -653,6 +762,7 @@ def get_id_to_path(
653
762
  :param refresh: 是否刷新。如果为 True,则会执行网络请求以查询;如果为 False,则直接从 `id_to_dirnode` 中获取
654
763
  :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
655
764
  :param app: 使用某个 app (设备)的接口
765
+ :param dont_use_getid: 不要使用 `client.fs_dir_getid` 或 `client.fs_dir_getid_app`,以便 `id_to_dirnode` 有缓存
656
766
  :param async_: 是否异步
657
767
  :param request_kwargs: 其它请求参数
658
768
 
@@ -664,178 +774,124 @@ def get_id_to_path(
664
774
  id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
665
775
  error = FileNotFoundError(ENOENT, f"no such path: {path!r}")
666
776
  def gen_step():
667
- nonlocal client, ensure_file
668
- if not isinstance(path, str):
669
- patht = ["", *filter(None, path)]
670
- if len(patht) == 1:
671
- return 0
672
- if is_posixpath:
673
- for i in range(1, len(patht)):
674
- patht[i] = patht[i].replace("/", "|")
675
- elif path in (".", "..", "/"):
676
- if ensure_file:
677
- raise error
678
- return 0
679
- elif path.startswith("根目录 > "):
680
- patht = path.split(" > ")
681
- patht[0] = ""
682
- if is_posixpath:
683
- for i in range(1, len(patht)):
684
- patht[i] = patht[i].replace("/", "|")
685
- elif is_posixpath:
686
- if ensure_file is None and path.endswith("/"):
687
- ensure_file = False
688
- patht = ["", *filter(None, path.split("/"))]
777
+ nonlocal ensure_file, parent_id
778
+ if isinstance(path, str):
779
+ if path.startswith("/"):
780
+ parent_id = 0
781
+ if path in (".", "..", "/"):
782
+ if ensure_file:
783
+ raise error
784
+ return parent_id
785
+ elif path.startswith("根目录 > "):
786
+ parent_id = 0
787
+ patht = path.split(" > ")[1:]
788
+ elif is_posixpath:
789
+ if ensure_file is None and path.endswith("/"):
790
+ ensure_file = False
791
+ patht = [p for p in path.split("/") if p]
792
+ else:
793
+ if ensure_file is None and path_is_dir_form(path):
794
+ ensure_file = False
795
+ patht, _ = splits(path.lstrip("/"))
689
796
  else:
690
- if ensure_file is None and path_is_dir_form(path):
691
- ensure_file = False
692
- patht, _ = splits("/" + path)
693
- if len(patht) == 1:
797
+ if path and not path[0]:
798
+ parent_id = 0
799
+ patht = list(path[1:])
800
+ else:
801
+ patht = [p for p in path if p]
802
+ if not patht:
803
+ return parent_id
804
+ if not patht:
694
805
  if ensure_file:
695
806
  raise error
696
- return 0
697
- stop = len(patht) - bool(ensure_file)
698
- obj = "|" if is_posixpath else "/"
699
- for i in range(stop):
700
- if obj in patht[i]:
701
- break
702
- else:
703
- i += 1
704
- j = 1
705
- pid = 0
706
- if stop > 1 and not refresh and id_to_dirnode:
707
- if stop == 2:
708
- if is_posixpath:
709
- needle = (patht[1].replace("/", "|"), pid)
710
- else:
711
- needle = (patht[1], pid)
712
- for k, t in id_to_dirnode.items():
713
- if is_posixpath:
714
- t = (t[0].replace("/", "|"), t[1])
715
- if t == needle:
716
- pid = k
717
- j = 2
718
- else:
719
- if is_posixpath:
720
- table = {(n.replace("/", "|"), pid): k for k, (n, pid) in id_to_dirnode.items()}
807
+ return parent_id
808
+ i = 0
809
+ start_parent_id = parent_id
810
+ if not refresh and id_to_dirnode and id_to_dirnode is not ...:
811
+ if i := len(patht) - bool(ensure_file):
812
+ obj = "|" if is_posixpath else "/"
813
+ for i in range(i):
814
+ if obj in patht[i]:
815
+ break
721
816
  else:
722
- table = {cast(tuple[str, int], tuple(t)): k for k, t in id_to_dirnode.items()}
723
- try:
724
- for j in range(1, stop):
725
- if is_posixpath:
726
- needle = (patht[j].replace("/", "|"), pid)
727
- else:
728
- needle = (patht[j], pid)
729
- pid = table[needle]
730
- j += 1
731
- except KeyError:
732
- pass
733
- if j >= i:
734
- i = j
735
- cid = pid
736
- else:
737
- if ensure_file and len(patht) == i:
738
- i -= 1
739
- if app in ("", "web", "desktop", "harmony"):
740
- fs_dir_getid: Callable = client.fs_dir_getid
741
- else:
742
- fs_dir_getid = partial(client.fs_dir_getid_app, app=app)
743
- cid = 0
744
- while i > 1:
745
- dirname = "/".join(patht[:i])
746
- resp = yield fs_dir_getid(dirname, async_=async_, **request_kwargs)
747
- if not (resp["state"] and (cid := resp["id"])):
748
- if len(patht) == i and ensure_file is None:
749
- ensure_file = True
750
- i -= 1
751
- continue
752
- raise error
753
- cid = int(cid)
754
- if not refresh and cid not in id_to_dirnode:
755
- yield get_path_to_cid(
756
- client,
757
- cid,
758
- id_to_dirnode=id_to_dirnode,
759
- app=app,
760
- async_=async_,
761
- **request_kwargs,
762
- )
763
- break
764
- if len(patht) == i:
765
- return cid
766
- for name in patht[i:-1]:
767
- if async_:
768
- async def request():
769
- nonlocal cid
770
- async for info in iterdir_raw(
771
- client,
772
- cid,
773
- ensure_file=False,
774
- app=app,
775
- id_to_dirnode=id_to_dirnode,
776
- async_=True,
777
- **request_kwargs,
778
- ):
779
- attr = _overview_attr(info)
780
- if (attr.name.replace("/", "|") if is_posixpath else attr.name) == name:
781
- cid = attr.id
817
+ i += 1
818
+ if i:
819
+ for i in range(i):
820
+ needle = (patht[i], parent_id)
821
+ for fid, key in id_to_dirnode.items():
822
+ if needle == key:
823
+ parent_id = fid
782
824
  break
783
825
  else:
784
- raise error
785
- yield request
786
- else:
787
- for info in iterdir_raw(
788
- client,
789
- cid,
790
- ensure_file=False,
791
- app=app,
792
- id_to_dirnode=id_to_dirnode,
793
- **request_kwargs,
794
- ):
795
- attr = _overview_attr(info)
796
- if (attr.name.replace("/", "|") if is_posixpath else attr.name) == name:
797
- cid = attr.id
798
826
  break
799
827
  else:
800
- raise error
801
- name = patht[-1]
802
- if async_:
803
- async def request():
804
- async for info in iterdir_raw(
805
- client,
806
- cid,
807
- app=app,
808
- id_to_dirnode=id_to_dirnode,
809
- async_=True,
810
- **request_kwargs,
811
- ):
812
- attr = _overview_attr(info)
813
- if (attr.name.replace("/", "|") if is_posixpath else attr.name) == name:
814
- if ensure_file:
815
- if not attr.is_dir:
816
- return P115ID(attr.id, info, about="path")
817
- elif attr.is_dir:
818
- return P115ID(attr.id, info, about="path")
828
+ i += 1
829
+ if i == len(patht):
830
+ return parent_id
831
+ if not start_parent_id:
832
+ stop = 0
833
+ if j := len(patht) - bool(ensure_file):
834
+ for stop, part in enumerate(patht[:j]):
835
+ if "/" in part:
836
+ break
819
837
  else:
820
- raise error
821
- return (yield request)
822
- else:
823
- for info in iterdir_raw(
838
+ stop += 1
839
+ if not dont_use_getid:
840
+ while stop > i:
841
+ if app in ("", "web", "desktop", "harmony"):
842
+ fs_dir_getid: Callable = client.fs_dir_getid
843
+ else:
844
+ fs_dir_getid = partial(client.fs_dir_getid_app, app=app)
845
+ dirname = "/".join(patht[:stop])
846
+ resp = yield fs_dir_getid(dirname, async_=async_, **request_kwargs)
847
+ check_response(resp)
848
+ cid = int(resp["id"])
849
+ if not cid:
850
+ if stop == len(patht) and ensure_file is None:
851
+ stop -= 1
852
+ continue
853
+ raise error
854
+ parent_id = cid
855
+ i = stop
856
+ break
857
+ if i == len(patht):
858
+ return parent_id
859
+ for name in patht[i:-1]:
860
+ if is_posixpath:
861
+ name = name.replace("/", "|")
862
+ with with_iter_next(iterdir(
824
863
  client,
825
- cid,
864
+ parent_id,
865
+ ensure_file=False,
826
866
  app=app,
827
867
  id_to_dirnode=id_to_dirnode,
868
+ async_=async_,
828
869
  **request_kwargs,
829
- ):
830
- attr = _overview_attr(info)
831
- if (attr.name.replace("/", "|") if is_posixpath else attr.name) == name:
832
- if ensure_file:
833
- if not attr.is_dir:
834
- return P115ID(attr.id, info, about="path")
835
- elif attr.is_dir:
836
- return P115ID(attr.id, info, about="path")
837
- else:
838
- raise error
870
+ )) as get_next:
871
+ found = False
872
+ while not found:
873
+ attr = yield get_next
874
+ found = (attr["name"].replace("/", "|") if is_posixpath else attr["name"]) == name
875
+ parent_id = attr["id"]
876
+ if not found:
877
+ raise error
878
+ name = patht[-1]
879
+ if is_posixpath:
880
+ name = name.replace("/", "|")
881
+ with with_iter_next(iterdir(
882
+ client,
883
+ parent_id,
884
+ app=app,
885
+ id_to_dirnode=id_to_dirnode,
886
+ async_=async_,
887
+ **request_kwargs,
888
+ )) as get_next:
889
+ while True:
890
+ attr = yield get_next
891
+ if (attr["name"].replace("/", "|") if is_posixpath else attr["name"]) == name:
892
+ if ensure_file is None or ensure_file ^ attr["is_dir"]:
893
+ return P115ID(attr["id"], attr, about="path")
894
+ raise error
839
895
  return run_gen_step(gen_step, async_=async_)
840
896
 
841
897
 
@@ -1135,6 +1191,11 @@ def _iter_fs_files(
1135
1191
  pid, name = int(info["cid"]), info["name"]
1136
1192
  id_to_dirnode[pid] = DirNode(name, int(info["pid"]))
1137
1193
  if ensure_file is None:
1194
+ if id_to_dirnode is not ...:
1195
+ for info in resp["data"]:
1196
+ attr = _overview_attr(info)
1197
+ if attr.is_dir:
1198
+ id_to_dirnode[attr.id] = DirNode(attr.name, attr.parent_id)
1138
1199
  yield YieldFrom(resp["data"], identity=True)
1139
1200
  else:
1140
1201
  for info in resp["data"]:
@@ -3118,6 +3179,7 @@ def share_iterdir(
3118
3179
  order: Literal["file_name", "file_size", "file_type", "user_utime", "user_ptime", "user_otime"] = "user_ptime",
3119
3180
  asc: Literal[0, 1] = 1,
3120
3181
  normalize_attr: None | Callable[[dict], dict] = normalize_attr,
3182
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3121
3183
  *,
3122
3184
  async_: Literal[False] = False,
3123
3185
  **request_kwargs,
@@ -3133,6 +3195,7 @@ def share_iterdir(
3133
3195
  order: Literal["file_name", "file_size", "file_type", "user_utime", "user_ptime", "user_otime"] = "user_ptime",
3134
3196
  asc: Literal[0, 1] = 1,
3135
3197
  normalize_attr: None | Callable[[dict], dict] = normalize_attr,
3198
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3136
3199
  *,
3137
3200
  async_: Literal[True],
3138
3201
  **request_kwargs,
@@ -3147,6 +3210,7 @@ def share_iterdir(
3147
3210
  order: Literal["file_name", "file_size", "file_type", "user_utime", "user_ptime", "user_otime"] = "user_ptime",
3148
3211
  asc: Literal[0, 1] = 1,
3149
3212
  normalize_attr: None | Callable[[dict], dict] = normalize_attr,
3213
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3150
3214
  *,
3151
3215
  async_: Literal[False, True] = False,
3152
3216
  **request_kwargs,
@@ -3169,6 +3233,7 @@ def share_iterdir(
3169
3233
 
3170
3234
  :param asc: 升序排列。0: 否,1: 是
3171
3235
  :param normalize_attr: 把数据进行转换处理,使之便于阅读
3236
+ :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
3172
3237
  :param async_: 是否异步
3173
3238
  :param request_kwargs: 其它请求参数
3174
3239
 
@@ -3176,7 +3241,9 @@ def share_iterdir(
3176
3241
  """
3177
3242
  if isinstance(client, str):
3178
3243
  client = P115Client(client, check_for_relogin=True)
3179
- if page_size < 0:
3244
+ if id_to_dirnode is None:
3245
+ id_to_dirnode = ID_TO_DIRNODE_CACHE[(client.user_id, share_code)]
3246
+ if page_size <= 0:
3180
3247
  page_size = 10_000
3181
3248
  def gen_step():
3182
3249
  nonlocal receive_code
@@ -3202,6 +3269,10 @@ def share_iterdir(
3202
3269
  for attr in resp["data"]["list"]:
3203
3270
  attr["share_code"] = share_code
3204
3271
  attr["receive_code"] = receive_code
3272
+ if id_to_dirnode is not ...:
3273
+ oattr = _overview_attr(attr)
3274
+ if oattr.is_dir:
3275
+ id_to_dirnode[oattr.id] = DirNode(oattr.name, oattr.parent_id)
3205
3276
  if normalize_attr is not None:
3206
3277
  attr = normalize_attr(attr)
3207
3278
  yield Yield(attr, identity=True)
@@ -3216,6 +3287,7 @@ def share_iter_files(
3216
3287
  client: str | P115Client,
3217
3288
  share_link: str,
3218
3289
  receive_code: str = "",
3290
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3219
3291
  *,
3220
3292
  async_: Literal[False] = False,
3221
3293
  **request_kwargs,
@@ -3226,6 +3298,7 @@ def share_iter_files(
3226
3298
  client: str | P115Client,
3227
3299
  share_link: str,
3228
3300
  receive_code: str = "",
3301
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3229
3302
  *,
3230
3303
  async_: Literal[True],
3231
3304
  **request_kwargs,
@@ -3235,6 +3308,7 @@ def share_iter_files(
3235
3308
  client: str | P115Client,
3236
3309
  share_link: str,
3237
3310
  receive_code: str = "",
3311
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3238
3312
  *,
3239
3313
  async_: Literal[False, True] = False,
3240
3314
  **request_kwargs,
@@ -3274,6 +3348,7 @@ def share_iter_files(
3274
3348
  if isinstance(client, str):
3275
3349
  client = P115Client(client, check_for_relogin=True)
3276
3350
  def gen_step():
3351
+ nonlocal id_to_dirnode
3277
3352
  payload: dict = cast(dict, share_extract_payload(share_link))
3278
3353
  if receive_code:
3279
3354
  payload["receive_code"] = receive_code
@@ -3281,7 +3356,10 @@ def share_iter_files(
3281
3356
  resp = yield client.share_info(payload["share_code"], async_=async_, **request_kwargs)
3282
3357
  check_response(resp)
3283
3358
  payload["receive_code"] = resp["data"]["receive_code"]
3359
+ if id_to_dirnode is None:
3360
+ id_to_dirnode = ID_TO_DIRNODE_CACHE[(client.user_id, payload["share_code"])]
3284
3361
  payload["cid"] = 0
3362
+ payload["id_to_dirnode"] = id_to_dirnode
3285
3363
  it = share_iterdir(client, **payload, async_=async_, **request_kwargs)
3286
3364
  do_next: Callable = anext if async_ else next
3287
3365
  try:
@@ -3307,6 +3385,176 @@ def share_iter_files(
3307
3385
  return run_gen_step(gen_step, async_=async_)
3308
3386
 
3309
3387
 
3388
+ @overload
3389
+ def share_get_id_to_path(
3390
+ client: str | P115Client,
3391
+ share_code: str,
3392
+ receive_code: str = "",
3393
+ path: str | Sequence[str] = "",
3394
+ parent_id: int = 0,
3395
+ ensure_file: None | bool = None,
3396
+ is_posixpath: bool = False,
3397
+ refresh: bool = False,
3398
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3399
+ *,
3400
+ async_: Literal[False] = False,
3401
+ **request_kwargs,
3402
+ ) -> int:
3403
+ ...
3404
+ @overload
3405
+ def share_get_id_to_path(
3406
+ client: str | P115Client,
3407
+ share_code: str,
3408
+ receive_code: str = "",
3409
+ path: str | Sequence[str] = "",
3410
+ parent_id: int = 0,
3411
+ ensure_file: None | bool = None,
3412
+ is_posixpath: bool = False,
3413
+ refresh: bool = False,
3414
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3415
+ *,
3416
+ async_: Literal[True],
3417
+ **request_kwargs,
3418
+ ) -> Coroutine[Any, Any, int]:
3419
+ ...
3420
+ def share_get_id_to_path(
3421
+ client: str | P115Client,
3422
+ share_code: str,
3423
+ receive_code: str = "",
3424
+ path: str | Sequence[str] = "",
3425
+ parent_id: int = 0,
3426
+ ensure_file: None | bool = None,
3427
+ is_posixpath: bool = False,
3428
+ refresh: bool = False,
3429
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
3430
+ *,
3431
+ async_: Literal[False, True] = False,
3432
+ **request_kwargs,
3433
+ ) -> int | Coroutine[Any, Any, int]:
3434
+ """对分享链接,获取路径对应的 id
3435
+
3436
+ :param client: 115 客户端或 cookies
3437
+ :param share_code: 分享码
3438
+ :param receive_code: 密码
3439
+ :param path: 路径
3440
+ :param parent_id: 上级目录的 id
3441
+ :param ensure_file: 是否确保为文件
3442
+
3443
+ - True: 必须是文件
3444
+ - False: 必须是目录
3445
+ - None: 可以是目录或文件
3446
+
3447
+ :param is_posixpath: 使用 posixpath,会把 "/" 转换为 "|",因此解析的时候,会对 "|" 进行特别处理
3448
+ :param refresh: 是否刷新。如果为 True,则会执行网络请求以查询;如果为 False,则直接从 `id_to_dirnode` 中获取
3449
+ :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
3450
+ :param async_: 是否异步
3451
+ :param request_kwargs: 其它请求参数
3452
+
3453
+ :return: 文件或目录的 id
3454
+ """
3455
+ if isinstance(client, str):
3456
+ client = P115Client(client, check_for_relogin=True)
3457
+ if id_to_dirnode is None:
3458
+ id_to_dirnode = ID_TO_DIRNODE_CACHE[(client.user_id, share_code)]
3459
+ error = FileNotFoundError(ENOENT, f"no such path: {path!r}")
3460
+ def gen_step():
3461
+ nonlocal ensure_file, parent_id, receive_code
3462
+ if isinstance(path, str):
3463
+ if path.startswith("/"):
3464
+ parent_id = 0
3465
+ if path in (".", "..", "/"):
3466
+ if ensure_file:
3467
+ raise error
3468
+ return parent_id
3469
+ elif path.startswith("根目录 > "):
3470
+ parent_id = 0
3471
+ patht = path.split(" > ")[1:]
3472
+ elif is_posixpath:
3473
+ if ensure_file is None and path.endswith("/"):
3474
+ ensure_file = False
3475
+ patht = [p for p in path.split("/") if p]
3476
+ else:
3477
+ if ensure_file is None and path_is_dir_form(path):
3478
+ ensure_file = False
3479
+ patht, _ = splits(path.lstrip("/"))
3480
+ else:
3481
+ if path and not path[0]:
3482
+ parent_id = 0
3483
+ patht = list(path[1:])
3484
+ else:
3485
+ patht = [p for p in path if p]
3486
+ if not patht:
3487
+ return parent_id
3488
+ if not patht:
3489
+ if ensure_file:
3490
+ raise error
3491
+ return parent_id
3492
+ if not receive_code:
3493
+ resp = yield client.share_info(share_code, async_=async_, **request_kwargs)
3494
+ check_response(resp)
3495
+ receive_code = resp["data"]["receive_code"]
3496
+ i = 0
3497
+ if not refresh and id_to_dirnode and id_to_dirnode is not ...:
3498
+ if i := len(patht) - bool(ensure_file):
3499
+ obj = "|" if is_posixpath else "/"
3500
+ for i in range(i):
3501
+ if obj in patht[i]:
3502
+ break
3503
+ else:
3504
+ i += 1
3505
+ if i:
3506
+ for i in range(i):
3507
+ needle = (patht[i], parent_id)
3508
+ for fid, key in id_to_dirnode.items():
3509
+ if needle == key:
3510
+ parent_id = fid
3511
+ break
3512
+ else:
3513
+ break
3514
+ else:
3515
+ i += 1
3516
+ if i == len(patht):
3517
+ return parent_id
3518
+ for name in patht[i:-1]:
3519
+ if is_posixpath:
3520
+ name = name.replace("/", "|")
3521
+ with with_iter_next(share_iterdir(
3522
+ client,
3523
+ share_code,
3524
+ receive_code=receive_code,
3525
+ cid=parent_id,
3526
+ id_to_dirnode=id_to_dirnode,
3527
+ async_=async_,
3528
+ **request_kwargs,
3529
+ )) as get_next:
3530
+ found = False
3531
+ while not found:
3532
+ attr = yield get_next
3533
+ found = attr["is_dir"] and (attr["name"].replace("/", "|") if is_posixpath else attr["name"]) == name
3534
+ parent_id = attr["id"]
3535
+ if not found:
3536
+ raise error
3537
+ name = patht[-1]
3538
+ if is_posixpath:
3539
+ name = name.replace("/", "|")
3540
+ with with_iter_next(share_iterdir(
3541
+ client,
3542
+ share_code,
3543
+ receive_code=receive_code,
3544
+ cid=parent_id,
3545
+ id_to_dirnode=id_to_dirnode,
3546
+ async_=async_,
3547
+ **request_kwargs,
3548
+ )) as get_next:
3549
+ while True:
3550
+ attr = yield get_next
3551
+ if (attr["name"].replace("/", "|") if is_posixpath else attr["name"]) == name:
3552
+ if ensure_file is None or ensure_file ^ attr["is_dir"]:
3553
+ return P115ID(attr["id"], attr, about="path")
3554
+ raise error
3555
+ return run_gen_step(gen_step, async_=async_)
3556
+
3557
+
3310
3558
  @overload
3311
3559
  def iter_selected_nodes(
3312
3560
  client: str | P115Client,