p115client 0.0.5.8.4__tar.gz → 0.0.5.8.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/PKG-INFO +1 -1
  2. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/client.py +64 -2
  3. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/download.py +28 -25
  4. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/edit.py +50 -5
  5. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/iterdir.py +101 -6
  6. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/pyproject.toml +1 -1
  7. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/LICENSE +0 -0
  8. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/__init__.py +0 -0
  9. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/_upload.py +0 -0
  10. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/const.py +0 -0
  11. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/exception.py +0 -0
  12. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/py.typed +0 -0
  13. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/__init__.py +0 -0
  14. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/export_dir.py +0 -0
  15. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/fs_files.py +0 -0
  16. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/life.py +0 -0
  17. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/pool.py +0 -0
  18. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/request.py +0 -0
  19. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/upload.py +0 -0
  20. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/tool/xys.py +0 -0
  21. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/p115client/type.py +0 -0
  22. {p115client-0.0.5.8.4 → p115client-0.0.5.8.6}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p115client
3
- Version: 0.0.5.8.4
3
+ Version: 0.0.5.8.6
4
4
  Summary: Python 115 webdisk client.
5
5
  Home-page: https://github.com/ChenyangGao/p115client
6
6
  License: MIT
@@ -3139,6 +3139,64 @@ class P115OpenClient(ClientRequestMixin):
3139
3139
  }
3140
3140
  return self.request(url=api, params=payload, async_=async_, **request_kwargs)
3141
3141
 
3142
+ @overload
3143
+ def fs_star_set(
3144
+ self,
3145
+ payload: int | str | Iterable[int | str] | dict,
3146
+ /,
3147
+ star: bool = True,
3148
+ base_url: bool | str | Callable[[], str] = False,
3149
+ *,
3150
+ async_: Literal[False] = False,
3151
+ **request_kwargs,
3152
+ ) -> dict:
3153
+ ...
3154
+ @overload
3155
+ def fs_star_set(
3156
+ self,
3157
+ payload: int | str | Iterable[int | str] | dict,
3158
+ /,
3159
+ star: bool = True,
3160
+ base_url: bool | str | Callable[[], str] = False,
3161
+ *,
3162
+ async_: Literal[True],
3163
+ **request_kwargs,
3164
+ ) -> Coroutine[Any, Any, dict]:
3165
+ ...
3166
+ def fs_star_set(
3167
+ self,
3168
+ payload: int | str | Iterable[int | str] | dict,
3169
+ /,
3170
+ star: bool = True,
3171
+ base_url: bool | str | Callable[[], str] = False,
3172
+ *,
3173
+ async_: Literal[False, True] = False,
3174
+ **request_kwargs,
3175
+ ) -> dict | Coroutine[Any, Any, dict]:
3176
+ """为文件或目录设置或取消星标,此接口是对 `fs_update_open` 的封装
3177
+
3178
+ .. note::
3179
+ 即使其中任何一个 id 目前已经被删除,也可以操作成功
3180
+
3181
+ :payload:
3182
+ - file_id: int | str 💡 只能传入 1 个
3183
+ - file_id[0]: int | str 💡 如果有多个,则按顺序给出
3184
+ - file_id[1]: int | str
3185
+ - ...
3186
+ - star: 0 | 1 = 1
3187
+ """
3188
+ api = complete_webapi("/files/star", base_url=base_url)
3189
+ if isinstance(payload, (int, str)):
3190
+ payload = {"file_id": payload, "star": int(star)}
3191
+ elif not isinstance(payload, dict):
3192
+ payload = {f"file_id[{i}]": id for i, id in enumerate(payload)}
3193
+ if not payload:
3194
+ return {"state": False, "message": "no op"}
3195
+ payload["star"] = int(star)
3196
+ else:
3197
+ payload = {"star": int(star), **payload}
3198
+ return self.fs_update(payload, async_=async_, **request_kwargs)
3199
+
3142
3200
  @overload
3143
3201
  def fs_update(
3144
3202
  self,
@@ -3175,13 +3233,16 @@ class P115OpenClient(ClientRequestMixin):
3175
3233
  POST https://proapi.115.com/open/ufile/update
3176
3234
 
3177
3235
  .. hint::
3178
- 类似于 `P115Client.fs_edit_app`
3236
+ 即使文件已经被删除,也可以操作成功
3179
3237
 
3180
3238
  .. note::
3181
3239
  https://www.yuque.com/115yun/open/gyrpw5a0zc4sengm
3182
3240
 
3183
3241
  :payload:
3184
- - file_id: int | str
3242
+ - file_id: int | str 💡 只能传入 1 个
3243
+ - file_id[0]: int | str 💡 如果有多个,则按顺序给出
3244
+ - file_id[1]: int | str
3245
+ - ...
3185
3246
  - file_name: str = <default> 💡 文件名
3186
3247
  - star: 0 | 1 = <default> 💡 是否星标:0:取消星标 1:设置星标
3187
3248
  - ...
@@ -3970,6 +4031,7 @@ class P115OpenClient(ClientRequestMixin):
3970
4031
  fs_mkdir_open = fs_mkdir
3971
4032
  fs_move_open = fs_move
3972
4033
  fs_search_open = fs_search
4034
+ fs_star_set_open = fs_star_set
3973
4035
  fs_update_open = fs_update
3974
4036
  recyclebin_clean_open = recyclebin_clean
3975
4037
  recyclebin_list_open = recyclebin_list
@@ -25,6 +25,7 @@ from shutil import rmtree
25
25
  from threading import Lock
26
26
  from time import time
27
27
  from typing import cast, overload, Any, Final, Literal, TypedDict
28
+ from types import EllipsisType
28
29
  from urllib.parse import quote, urlsplit
29
30
  from urllib.request import urlopen, Request
30
31
  from uuid import uuid4
@@ -257,7 +258,7 @@ def iter_files_with_url(
257
258
  use_star: None | bool = False,
258
259
  escape: None | bool | Callable[[str], str] = True,
259
260
  normalize_attr: Callable[[dict], dict] = normalize_attr,
260
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
261
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
261
262
  app: str = "web",
262
263
  raise_for_changed_count: bool = False,
263
264
  user_agent: str = "",
@@ -278,7 +279,7 @@ def iter_files_with_url(
278
279
  use_star: None | bool = False,
279
280
  escape: None | bool | Callable[[str], str] = True,
280
281
  normalize_attr: Callable[[dict], dict] = normalize_attr,
281
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
282
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
282
283
  app: str = "web",
283
284
  raise_for_changed_count: bool = False,
284
285
  user_agent: str = "",
@@ -298,7 +299,7 @@ def iter_files_with_url(
298
299
  use_star: None | bool = False,
299
300
  escape: None | bool | Callable[[str], str] = True,
300
301
  normalize_attr: Callable[[dict], dict] = normalize_attr,
301
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
302
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
302
303
  app: str = "web",
303
304
  raise_for_changed_count: bool = False,
304
305
  user_agent: str = "",
@@ -423,7 +424,7 @@ def iter_images_with_url(
423
424
  use_star: None | bool = False,
424
425
  escape: None | bool | Callable[[str], str] = True,
425
426
  normalize_attr: Callable[[dict], dict] = normalize_attr,
426
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
427
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
427
428
  app: str = "web",
428
429
  raise_for_changed_count: bool = False,
429
430
  *,
@@ -442,7 +443,7 @@ def iter_images_with_url(
442
443
  use_star: None | bool = False,
443
444
  escape: None | bool | Callable[[str], str] = True,
444
445
  normalize_attr: Callable[[dict], dict] = normalize_attr,
445
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
446
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
446
447
  app: str = "web",
447
448
  raise_for_changed_count: bool = False,
448
449
  *,
@@ -460,7 +461,7 @@ def iter_images_with_url(
460
461
  use_star: None | bool = False,
461
462
  escape: None | bool | Callable[[str], str] = True,
462
463
  normalize_attr: Callable[[dict], dict] = normalize_attr,
463
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
464
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
464
465
  app: str = "web",
465
466
  raise_for_changed_count: bool = False,
466
467
  *,
@@ -578,7 +579,7 @@ def iter_subtitles_with_url(
578
579
  use_star: None | bool = False,
579
580
  escape: None | bool | Callable[[str], str] = True,
580
581
  normalize_attr: Callable[[dict], dict] = normalize_attr,
581
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
582
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
582
583
  app: str = "web",
583
584
  raise_for_changed_count: bool = False,
584
585
  *,
@@ -597,7 +598,7 @@ def iter_subtitles_with_url(
597
598
  use_star: None | bool = False,
598
599
  escape: None | bool | Callable[[str], str] = True,
599
600
  normalize_attr: Callable[[dict], dict] = normalize_attr,
600
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
601
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
601
602
  app: str = "web",
602
603
  raise_for_changed_count: bool = False,
603
604
  *,
@@ -615,7 +616,7 @@ def iter_subtitles_with_url(
615
616
  use_star: None | bool = False,
616
617
  escape: None | bool | Callable[[str], str] = True,
617
618
  normalize_attr: Callable[[dict], dict] = normalize_attr,
618
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
619
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
619
620
  app: str = "web",
620
621
  raise_for_changed_count: bool = False,
621
622
  *,
@@ -855,7 +856,7 @@ def make_strm(
855
856
  suffix: str = "",
856
857
  type: Literal[1, 2, 3, 4, 5, 6, 7, 99] = 4,
857
858
  max_workers: None | int = None,
858
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
859
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
859
860
  path_already: bool = False,
860
861
  app: str = "android",
861
862
  fs_files_cooldown: int | float = 0.5,
@@ -881,7 +882,7 @@ def make_strm(
881
882
  suffix: str = "",
882
883
  type: Literal[1, 2, 3, 4, 5, 6, 7, 99] = 4,
883
884
  max_workers: None | int = None,
884
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
885
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
885
886
  path_already: bool = False,
886
887
  app: str = "android",
887
888
  fs_files_cooldown: int | float = 0.5,
@@ -906,7 +907,7 @@ def make_strm(
906
907
  suffix: str = "",
907
908
  type: Literal[1, 2, 3, 4, 5, 6, 7, 99] = 4,
908
909
  max_workers: None | int = None,
909
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
910
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
910
911
  path_already: bool = False,
911
912
  app: str = "android",
912
913
  fs_files_cooldown: int | float = 0.5,
@@ -1170,40 +1171,36 @@ def iter_download_nodes(
1170
1171
  method = client.download_files
1171
1172
  else:
1172
1173
  method = client.download_folders
1174
+ get_nodes = partial(method, async_=async_, **{"base_url": get_base_url, **request_kwargs})
1173
1175
  if max_workers == 1:
1174
1176
  def gen_step(pickcode):
1175
1177
  if isinstance(pickcode, int):
1176
1178
  resp = yield client.fs_file_skim(pickcode, async_=async_, **request_kwargs)
1177
1179
  check_response(resp)
1178
1180
  pickcode = resp["data"][0]["pick_code"]
1179
- request_kwargs.setdefault("base_url", get_base_url)
1180
1181
  for i in count(1):
1181
1182
  payload = {"pickcode": pickcode, "page": i}
1182
- resp = yield method(payload, async_=async_, **request_kwargs)
1183
+ resp = yield get_nodes(payload)
1183
1184
  check_response(resp)
1184
1185
  data = resp["data"]
1185
1186
  yield YieldFrom(data["list"], identity=True)
1186
1187
  if not data["has_next_page"]:
1187
1188
  break
1188
1189
  else:
1190
+ max_page = 0
1189
1191
  get_next_page = count(1).__next__
1190
1192
  if async_:
1191
1193
  q: Any = AsyncQueue()
1192
1194
  else:
1193
1195
  q = SimpleQueue()
1194
1196
  get, put = q.get, q.put_nowait
1195
- max_page = 0
1196
1197
  def request(pickcode):
1197
1198
  nonlocal max_page
1198
1199
  while True:
1199
1200
  page = get_next_page()
1200
1201
  if max_page and page > max_page:
1201
1202
  return
1202
- resp: dict = yield method(
1203
- {"pickcode": pickcode, "page": page},
1204
- async_=async_, # type: ignore
1205
- **request_kwargs,
1206
- )
1203
+ resp: dict = yield get_nodes({"pickcode": pickcode, "page": page})
1207
1204
  try:
1208
1205
  check_response(resp)
1209
1206
  except BaseException as e:
@@ -1214,7 +1211,9 @@ def iter_download_nodes(
1214
1211
  if not data["has_next_page"]:
1215
1212
  max_page = page
1216
1213
  def gen_step(pickcode):
1217
- nonlocal max_workers
1214
+ nonlocal max_workers, max_page, get_next_page
1215
+ max_page = 0
1216
+ get_next_page = count(1).__next__
1218
1217
  if async_:
1219
1218
  if max_workers is None or max_workers <= 0:
1220
1219
  max_workers = 20
@@ -1239,7 +1238,6 @@ def iter_download_nodes(
1239
1238
  )
1240
1239
  check_response(resp)
1241
1240
  pickcode = resp["data"][0]["pick_code"]
1242
- request_kwargs.setdefault("base_url", cycle(("http://proapi.115.com", "https://proapi.115.com")).__next__)
1243
1241
  try:
1244
1242
  sentinel = object()
1245
1243
  countdown: Callable
@@ -1282,6 +1280,11 @@ def iter_download_nodes(
1282
1280
  )) as get_next:
1283
1281
  while True:
1284
1282
  attr = yield get_next
1283
+ if not files:
1284
+ yield Yield(
1285
+ {"fid": str(attr["id"]), "pid": "0", "fn": attr["name"]},
1286
+ identity=True,
1287
+ )
1285
1288
  yield YieldFrom(
1286
1289
  run_gen_step_iter(gen_step(attr["pickcode"]), async_=async_),
1287
1290
  identity=True,
@@ -1293,7 +1296,7 @@ def iter_download_nodes(
1293
1296
  def iter_download_files(
1294
1297
  client: str | P115Client,
1295
1298
  cid: int = 0,
1296
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
1299
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
1297
1300
  escape: None | bool | Callable[[str], str] = True,
1298
1301
  with_ancestors: bool = True,
1299
1302
  max_workers: None | int = None,
@@ -1307,7 +1310,7 @@ def iter_download_files(
1307
1310
  def iter_download_files(
1308
1311
  client: str | P115Client,
1309
1312
  cid: int = 0,
1310
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
1313
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
1311
1314
  escape: None | bool | Callable[[str], str] = True,
1312
1315
  with_ancestors: bool = True,
1313
1316
  max_workers: None | int = None,
@@ -1320,7 +1323,7 @@ def iter_download_files(
1320
1323
  def iter_download_files(
1321
1324
  client: str | P115Client,
1322
1325
  cid: int = 0,
1323
- id_to_dirnode: None | dict[int, tuple[str, int] | DirNode] = None,
1326
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
1324
1327
  escape: None | bool | Callable[[str], str] = True,
1325
1328
  with_ancestors: bool = True,
1326
1329
  max_workers: None | int = None,
@@ -103,6 +103,7 @@ def update_desc(
103
103
  desc: str = "",
104
104
  batch_size: int = 10_000,
105
105
  max_workers: None | int = None,
106
+ app: str = "web",
106
107
  *,
107
108
  async_: Literal[False] = False,
108
109
  **request_kwargs,
@@ -116,6 +117,7 @@ def update_desc(
116
117
  desc: str = "",
117
118
  batch_size: int = 10_000,
118
119
  max_workers: None | int = None,
120
+ app: str = "web",
119
121
  *,
120
122
  async_: Literal[True],
121
123
  **request_kwargs,
@@ -128,6 +130,7 @@ def update_desc(
128
130
  desc: str = "",
129
131
  batch_size: int = 10_000,
130
132
  max_workers: None | int = None,
133
+ app: str = "web",
131
134
  *,
132
135
  async_: Literal[False, True] = False,
133
136
  **request_kwargs,
@@ -139,13 +142,19 @@ def update_desc(
139
142
  :param desc: 备注文本
140
143
  :param batch_size: 批次大小,分批次,每次提交的 id 数
141
144
  :param max_workers: 并发工作数,如果为 None 或者 <= 0,则自动确定
145
+ :param app: 使用此设备的接口
142
146
  :param async_: 是否异步
143
147
  :param request_kwargs: 其它请求参数
144
148
  """
149
+ if app in ("", "web", "desktop", "harmony"):
150
+ method = "fs_desc_set"
151
+ else:
152
+ method = "fs_desc_set_app"
153
+ request_kwargs["app"] = app
145
154
  return update_abstract(
146
155
  client,
147
156
  ids, # type: ignore
148
- method="fs_desc_set",
157
+ method=method,
149
158
  value=desc,
150
159
  batch_size=batch_size,
151
160
  max_workers=max_workers,
@@ -162,6 +171,7 @@ def update_star(
162
171
  star: bool = True,
163
172
  batch_size: int = 10_000,
164
173
  max_workers: None | int = None,
174
+ app: str = "web",
165
175
  *,
166
176
  async_: Literal[False] = False,
167
177
  **request_kwargs,
@@ -175,6 +185,7 @@ def update_star(
175
185
  star: bool = True,
176
186
  batch_size: int = 10_000,
177
187
  max_workers: None | int = None,
188
+ app: str = "web",
178
189
  *,
179
190
  async_: Literal[True],
180
191
  **request_kwargs,
@@ -187,6 +198,7 @@ def update_star(
187
198
  star: bool = True,
188
199
  batch_size: int = 10_000,
189
200
  max_workers: None | int = None,
201
+ app: str = "web",
190
202
  *,
191
203
  async_: Literal[False, True] = False,
192
204
  **request_kwargs,
@@ -201,13 +213,23 @@ def update_star(
201
213
  :param star: 是否设置星标
202
214
  :param batch_size: 批次大小,分批次,每次提交的 id 数
203
215
  :param max_workers: 并发工作数,如果为 None 或者 <= 0,则自动确定
216
+ :param app: 使用此设备的接口
204
217
  :param async_: 是否异步
205
218
  :param request_kwargs: 其它请求参数
206
219
  """
220
+ if isinstance(client, str):
221
+ client = P115Client(client, check_for_relogin=True)
222
+ if not isinstance(client, P115Client) or app == "open":
223
+ method = "fs_star_set_open"
224
+ elif app in ("", "web", "desktop", "harmony"):
225
+ method = "fs_star_set"
226
+ else:
227
+ method = "fs_star_set_app"
228
+ request_kwargs["app"] = app
207
229
  return update_abstract(
208
230
  client,
209
231
  ids, # type: ignore
210
- method="fs_star_set",
232
+ method=method,
211
233
  value=star,
212
234
  batch_size=batch_size,
213
235
  max_workers=max_workers,
@@ -224,6 +246,7 @@ def update_label(
224
246
  label: int | str = 1,
225
247
  batch_size: int = 10_000,
226
248
  max_workers: None | int = None,
249
+ app: str = "web",
227
250
  *,
228
251
  async_: Literal[False] = False,
229
252
  **request_kwargs,
@@ -237,6 +260,7 @@ def update_label(
237
260
  label: int | str = 1,
238
261
  batch_size: int = 10_000,
239
262
  max_workers: None | int = None,
263
+ app: str = "web",
240
264
  *,
241
265
  async_: Literal[True],
242
266
  **request_kwargs,
@@ -249,6 +273,7 @@ def update_label(
249
273
  label: int | str = 1,
250
274
  batch_size: int = 10_000,
251
275
  max_workers: None | int = None,
276
+ app: str = "web",
252
277
  *,
253
278
  async_: Literal[False, True] = False,
254
279
  **request_kwargs,
@@ -260,13 +285,19 @@ def update_label(
260
285
  :param label: 标签 id,多个用逗号 "," 隔开,如果用一个根本不存在的 id,效果就是清空标签列表
261
286
  :param batch_size: 批次大小,分批次,每次提交的 id 数
262
287
  :param max_workers: 并发工作数,如果为 None 或者 <= 0,则自动确定
288
+ :param app: 使用此设备的接口
263
289
  :param async_: 是否异步
264
290
  :param request_kwargs: 其它请求参数
265
291
  """
292
+ if app in ("", "web", "desktop", "harmony"):
293
+ method = "fs_label_set"
294
+ else:
295
+ method = "fs_label_set_app"
296
+ request_kwargs["app"] = app
266
297
  return update_abstract(
267
298
  client,
268
299
  ids, # type: ignore
269
- method="fs_label_set",
300
+ method=method,
270
301
  value=label,
271
302
  batch_size=batch_size,
272
303
  max_workers=max_workers,
@@ -401,6 +432,7 @@ def update_show_play_long(
401
432
  show: bool = True,
402
433
  batch_size: int = 10_000,
403
434
  max_workers: None | int = None,
435
+ app: str = "web",
404
436
  *,
405
437
  async_: Literal[False] = False,
406
438
  **request_kwargs,
@@ -414,6 +446,7 @@ def update_show_play_long(
414
446
  show: bool = True,
415
447
  batch_size: int = 10_000,
416
448
  max_workers: None | int = None,
449
+ app: str = "web",
417
450
  *,
418
451
  async_: Literal[True],
419
452
  **request_kwargs,
@@ -426,6 +459,7 @@ def update_show_play_long(
426
459
  show: bool = True,
427
460
  batch_size: int = 10_000,
428
461
  max_workers: None | int = None,
462
+ app: str = "web",
429
463
  *,
430
464
  async_: Literal[False, True] = False,
431
465
  **request_kwargs,
@@ -437,13 +471,19 @@ def update_show_play_long(
437
471
  :param show: 是否显示时长
438
472
  :param batch_size: 批次大小,分批次,每次提交的 id 数
439
473
  :param max_workers: 并发工作数,如果为 None 或者 <= 0,则自动确定
474
+ :param app: 使用此设备的接口
440
475
  :param async_: 是否异步
441
476
  :param request_kwargs: 其它请求参数
442
477
  """
478
+ if app in ("", "web", "desktop", "harmony"):
479
+ method = "fs_show_play_long_set"
480
+ else:
481
+ method = "fs_show_play_long_set_app"
482
+ request_kwargs["app"] = app
443
483
  return update_abstract(
444
484
  client,
445
485
  ids, # type: ignore
446
- method="fs_show_play_long_set",
486
+ method=method,
447
487
  value=show,
448
488
  batch_size=batch_size,
449
489
  max_workers=max_workers,
@@ -518,6 +558,7 @@ def batch_unstar(
518
558
  batch_size: int = 10_000,
519
559
  ensure_file: None | bool = None,
520
560
  max_workers: None | int = None,
561
+ app: str = "web",
521
562
  *,
522
563
  async_: Literal[False] = False,
523
564
  **request_kwargs,
@@ -530,6 +571,7 @@ def batch_unstar(
530
571
  batch_size: int = 10_000,
531
572
  ensure_file: None | bool = None,
532
573
  max_workers: None | int = None,
574
+ app: str = "web",
533
575
  *,
534
576
  async_: Literal[True],
535
577
  **request_kwargs,
@@ -541,6 +583,7 @@ def batch_unstar(
541
583
  batch_size: int = 10_000,
542
584
  ensure_file: None | bool = None,
543
585
  max_workers: None | int = None,
586
+ app: str = "web",
544
587
  *,
545
588
  async_: Literal[False, True] = False,
546
589
  **request_kwargs,
@@ -556,6 +599,7 @@ def batch_unstar(
556
599
  - None: 可以是目录或文件
557
600
 
558
601
  :param max_workers: 并发工作数,如果为 None 或者 <= 0,则自动确定
602
+ :param app: 使用此设备的接口
559
603
  :param async_: 是否异步
560
604
  :param request_kwargs: 其它请求参数
561
605
  """
@@ -572,7 +616,7 @@ def batch_unstar(
572
616
  client,
573
617
  payload={"cid": 0, "count_folders": 1, "cur": 0, "fc_mix": 0, "offset": 0, "show_dir": 1, "star": 1},
574
618
  ensure_file=ensure_file,
575
- app="android",
619
+ app=app,
576
620
  cooldown=0.5,
577
621
  async_=async_,
578
622
  **request_kwargs,
@@ -587,6 +631,7 @@ def batch_unstar(
587
631
  star=False,
588
632
  batch_size=batch_size,
589
633
  max_workers=max_workers,
634
+ app=app,
590
635
  async_=async_, # type: ignore
591
636
  **request_kwargs,
592
637
  )
@@ -8,9 +8,9 @@ __all__ = [
8
8
  "get_ancestors_to_cid", "get_id_to_path", "get_id_to_sha1", "get_id_to_pickcode",
9
9
  "iter_nodes_skim", "iter_stared_dirs_raw", "iter_stared_dirs", "ensure_attr_path",
10
10
  "ensure_attr_path_by_category_get", "iterdir_raw", "iterdir", "iterdir_limited",
11
- "iter_files_raw", "iter_files", "traverse_files", "iter_dupfiles", "iter_image_files",
12
- "share_extract_payload", "share_iterdir", "share_iter_files", "iter_selected_nodes",
13
- "iter_selected_nodes_by_pickcode", "iter_selected_nodes_using_category_get",
11
+ "iter_files_raw", "iter_files", "traverse_files", "iter_dirs", "iter_dupfiles",
12
+ "iter_image_files", "share_extract_payload", "share_iterdir", "share_iter_files",
13
+ "iter_selected_nodes", "iter_selected_nodes_by_pickcode", "iter_selected_nodes_using_category_get",
14
14
  "iter_selected_nodes_using_edit", "iter_selected_nodes_using_star_event",
15
15
  "iter_selected_dirs_using_star", "iter_files_with_dirname", "iter_files_with_path",
16
16
  "iter_files_with_path_by_export_dir", "iter_parents_3_level", "iter_dir_nodes",
@@ -41,9 +41,9 @@ from weakref import WeakValueDictionary
41
41
  from asynctools import async_chain, async_filter, async_map, to_list
42
42
  from concurrenttools import run_as_thread, taskgroup_map, threadpool_map
43
43
  from iterutils import (
44
- bfs_gen, chunked, async_foreach, ensure_aiter, foreach, flatten,
45
- iter_unique, run_gen_step, run_gen_step_iter, through, async_through,
46
- with_iter_next, Yield, YieldFrom,
44
+ as_gen_step, bfs_gen, chunked, async_foreach, ensure_aiter, foreach,
45
+ flatten, iter_unique, run_gen_step, run_gen_step_iter, through,
46
+ async_through, with_iter_next, Yield, YieldFrom,
47
47
  )
48
48
  from iter_collect import iter_keyed_dups, SupportsLT
49
49
  from orjson import loads
@@ -2789,6 +2789,101 @@ def traverse_files(
2789
2789
  return run_gen_step_iter(gen_step, async_=async_)
2790
2790
 
2791
2791
 
2792
+ @overload
2793
+ def iter_dirs(
2794
+ client: str | P115Client,
2795
+ cid: int | str = 0,
2796
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
2797
+ with_pickcode: bool = False,
2798
+ max_workers: None | int = None,
2799
+ *,
2800
+ async_: Literal[False] = False,
2801
+ **request_kwargs,
2802
+ ) -> Iterator[dict]:
2803
+ ...
2804
+ @overload
2805
+ def iter_dirs(
2806
+ client: str | P115Client,
2807
+ cid: int | str = 0,
2808
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
2809
+ with_pickcode: bool = False,
2810
+ max_workers: None | int = None,
2811
+ *,
2812
+ async_: Literal[True],
2813
+ **request_kwargs,
2814
+ ) -> AsyncIterator[dict]:
2815
+ ...
2816
+ def iter_dirs(
2817
+ client: str | P115Client,
2818
+ cid: int | str = 0,
2819
+ id_to_dirnode: None | EllipsisType | dict[int, tuple[str, int] | DirNode] = None,
2820
+ with_pickcode: bool = False,
2821
+ max_workers: None | int = None,
2822
+ *,
2823
+ async_: Literal[False, True] = False,
2824
+ **request_kwargs,
2825
+ ) -> Iterator[dict] | AsyncIterator[dict]:
2826
+ """遍历目录树,获取目录信息
2827
+
2828
+ :param client: 115 客户端或 cookies
2829
+ :param cid: 目录 id(如果是 int) 或者 pickcode(如果是 str)
2830
+ :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
2831
+ :param with_pickcode: 是否需要包含提取码
2832
+ :param max_workers: 最大并发数
2833
+ :param async_: 是否异步
2834
+ :param request_kwargs: 其它请求参数
2835
+
2836
+ :return: 迭代器,返回此目录内的(仅目录)文件信息
2837
+ """
2838
+ from .download import iter_download_nodes
2839
+ if isinstance(client, str):
2840
+ client = P115Client(client, check_for_relogin=True)
2841
+ if id_to_dirnode is None:
2842
+ id_to_dirnode = ID_TO_DIRNODE_CACHE[client.user_id]
2843
+ it = iter_download_nodes(
2844
+ client,
2845
+ cid,
2846
+ files=False,
2847
+ max_workers=max_workers,
2848
+ async_=async_, # type: ignore
2849
+ **request_kwargs,
2850
+ )
2851
+ do_map: Callable = async_map if async_ else map
2852
+ def project(info: dict, /) -> dict:
2853
+ attr = {"id": int(info["fid"]), "parent_id": int(info["pid"]), "name": info["fn"]}
2854
+ if id_to_dirnode is not ...:
2855
+ id_to_dirnode[attr["id"]] = DirNode(attr["name"], attr["parent_id"])
2856
+ return attr
2857
+ it = do_map(project, it)
2858
+ if with_pickcode:
2859
+ file_skim = client.fs_file_skim
2860
+ @as_gen_step(async_=async_)
2861
+ def batch_load_pickcode(batch: Sequence[dict], /):
2862
+ resp = yield file_skim(
2863
+ (a["id"] for a in batch),
2864
+ method="POST",
2865
+ async_=async_,
2866
+ **request_kwargs,
2867
+ )
2868
+ check_response(resp)
2869
+ maps = {int(a["file_id"]): a["pick_code"] for a in resp["data"]}
2870
+ for attr in batch:
2871
+ attr["pickcode"] = maps[attr["id"]]
2872
+ return batch
2873
+ def gen_step(iterable):
2874
+ batch_map = taskgroup_map if async_ else threadpool_map
2875
+ with with_iter_next(batch_map(
2876
+ batch_load_pickcode,
2877
+ chunked(iterable, 3000),
2878
+ max_workers=max_workers,
2879
+ )) as get_next:
2880
+ while True:
2881
+ batch = yield get_next
2882
+ yield YieldFrom(batch, identity=True)
2883
+ it = run_gen_step_iter(gen_step(it), async_=async_)
2884
+ return it
2885
+
2886
+
2792
2887
  @overload
2793
2888
  def iter_dupfiles[K](
2794
2889
  client: str | P115Client,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "p115client"
3
- version = "0.0.5.8.4"
3
+ version = "0.0.5.8.6"
4
4
  description = "Python 115 webdisk client."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
File without changes
File without changes