p115client 0.0.5.10.6__tar.gz → 0.0.5.10.8__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 (25) hide show
  1. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/PKG-INFO +4 -4
  2. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/client.py +422 -115
  3. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/const.py +1 -1
  4. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/exception.py +8 -2
  5. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/iterdir.py +0 -6
  6. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/pyproject.toml +3 -3
  7. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/readme.md +1 -1
  8. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/LICENSE +0 -0
  9. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/__init__.py +0 -0
  10. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/_upload.py +0 -0
  11. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/py.typed +0 -0
  12. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/__init__.py +0 -0
  13. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/attr.py +0 -0
  14. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/download.py +0 -0
  15. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/edit.py +0 -0
  16. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/export_dir.py +0 -0
  17. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/fs_files.py +0 -0
  18. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/history.py +0 -0
  19. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/life.py +0 -0
  20. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/pool.py +0 -0
  21. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/request.py +0 -0
  22. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/upload.py +0 -0
  23. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/util.py +0 -0
  24. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/tool/xys.py +0 -0
  25. {p115client-0.0.5.10.6 → p115client-0.0.5.10.8}/p115client/type.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p115client
3
- Version: 0.0.5.10.6
3
+ Version: 0.0.5.10.8
4
4
  Summary: Python 115 webdisk client.
5
5
  Home-page: https://github.com/ChenyangGao/p115client
6
6
  License: MIT
@@ -23,8 +23,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Dist: aiofile
24
24
  Requires-Dist: ed2k (>=0.0.2.1)
25
25
  Requires-Dist: http_response (>=0.0.2.2)
26
- Requires-Dist: httpx
27
- Requires-Dist: httpx_request (>=0.1.3)
26
+ Requires-Dist: httpx (>=0.28)
27
+ Requires-Dist: httpx_request (>=0.1.4)
28
28
  Requires-Dist: iter_collect (>=0.0.5.1)
29
29
  Requires-Dist: multidict
30
30
  Requires-Dist: orjson
@@ -247,7 +247,7 @@ class MyCustom115Client(P115Client):
247
247
 
248
248
  ### 3. 检查响应
249
249
 
250
- 接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p115client.check_response` 执行检查。首先会查看其中名为 "state" 的键的对应值,如果为 True、1 或不存在,则原样返回被检查的数据;否则,"state" 的对应值大概是 False 或 0,说明有问题出现,会根据实际情况抛出一个异常,但都是 `OSError` 的实例,其中大部分还是 `p115client.P115OSError` 的实例。
250
+ 接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p115client.check_response` 执行检查。首先会查看其中名为 "state" 的键的对应值,如果为 True、1 或不存在,则原样返回被检查的数据;否则,"state" 的对应值大概是 False 或 0,说明有问题出现,会根据实际情况抛出一个异常,但都是 `p115client.P115OSError` 的实例。
251
251
 
252
252
  ```python
253
253
  from p115client import check_response
@@ -65,8 +65,8 @@ from yarl import URL
65
65
 
66
66
  from .const import CLASS_TO_TYPE, CLIENT_API_MAP, SSOENT_TO_APP, SUFFIX_TO_TYPE, errno
67
67
  from .exception import (
68
- AuthenticationError, BusyOSError, DataError, LoginError, NotSupportedError,
69
- P115OSError, OperationalError, P115Warning, P115FileExistsError,
68
+ AuthenticationError, BusyOSError, DataError, LoginError, OpenAppAuthLimitExceeded,
69
+ NotSupportedError, P115OSError, OperationalError, P115Warning, P115FileExistsError,
70
70
  P115FileNotFoundError, P115IsADirectoryError,
71
71
  )
72
72
  from .type import RequestKeywords, MultipartResumeData, P115Cookies, P115URL
@@ -339,6 +339,10 @@ def get_first(m: Mapping, /, *keys, default=None):
339
339
  return default
340
340
 
341
341
 
342
+ def contains_any(m: Mapping, /, *keys):
343
+ return any(k in m for k in keys)
344
+
345
+
342
346
  @overload
343
347
  def check_response(resp: dict, /) -> dict:
344
348
  ...
@@ -525,7 +529,7 @@ def check_response(resp: dict | Awaitable[dict], /) -> dict | Coroutine[Any, Any
525
529
  raise OperationalError(errno.EINVAL, resp)
526
530
  # {"state": 0, "errno": 40140122, "error": "超出授权应用个数上限"}
527
531
  case 40140122:
528
- raise OperationalError(errno.EINVAL, resp)
532
+ raise OpenAppAuthLimitExceeded(errno.EDQUOT, resp)
529
533
  # {"state": 0, "errno": 40140123, "error": "access_token 格式错误(防篡改)"}
530
534
  case 40140123:
531
535
  raise OperationalError(errno.EINVAL, resp)
@@ -1286,6 +1290,8 @@ class ClientRequestMixin:
1286
1290
  payload: str | dict,
1287
1291
  /,
1288
1292
  request: None | Callable = None,
1293
+ app: str = "web",
1294
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1289
1295
  *,
1290
1296
  async_: Literal[False] = False,
1291
1297
  **request_kwargs,
@@ -1297,6 +1303,8 @@ class ClientRequestMixin:
1297
1303
  payload: str | dict,
1298
1304
  /,
1299
1305
  request: None | Callable = None,
1306
+ app: str = "web",
1307
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1300
1308
  *,
1301
1309
  async_: Literal[True],
1302
1310
  **request_kwargs,
@@ -1307,6 +1315,8 @@ class ClientRequestMixin:
1307
1315
  payload: str | dict,
1308
1316
  /,
1309
1317
  request: None | Callable = None,
1318
+ app: str = "web",
1319
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1310
1320
  *,
1311
1321
  async_: Literal[False, True] = False,
1312
1322
  **request_kwargs,
@@ -1319,7 +1329,7 @@ class ClientRequestMixin:
1319
1329
 
1320
1330
  :return: 图片的二进制数据(PNG 图片)
1321
1331
  """
1322
- api = "https://qrcodeapi.115.com/api/1.0/web/1.0/qrcode"
1332
+ api = complete_api(f"/api/1.0/{app}/1.0/qrcode", base_url=base_url)
1323
1333
  if isinstance(payload, str):
1324
1334
  payload = {"uid": payload}
1325
1335
  request_kwargs.setdefault("parse", False)
@@ -1334,6 +1344,7 @@ class ClientRequestMixin:
1334
1344
  payload: str | dict,
1335
1345
  /,
1336
1346
  request: None | Callable = None,
1347
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1337
1348
  *,
1338
1349
  async_: Literal[False] = False,
1339
1350
  **request_kwargs,
@@ -1345,6 +1356,7 @@ class ClientRequestMixin:
1345
1356
  payload: str | dict,
1346
1357
  /,
1347
1358
  request: None | Callable = None,
1359
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1348
1360
  *,
1349
1361
  async_: Literal[True],
1350
1362
  **request_kwargs,
@@ -1355,6 +1367,7 @@ class ClientRequestMixin:
1355
1367
  payload: str | dict,
1356
1368
  /,
1357
1369
  request: None | Callable = None,
1370
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1358
1371
  *,
1359
1372
  async_: Literal[False, True] = False,
1360
1373
  **request_kwargs,
@@ -1371,7 +1384,7 @@ class ClientRequestMixin:
1371
1384
  - uid: str
1372
1385
  - code_verifier: str = <default> 💡 默认字符串是 64 个 "0"
1373
1386
  """
1374
- api = "https://qrcodeapi.115.com/open/deviceCodeToToken"
1387
+ api = complete_api("/open/deviceCodeToToken", base_url=base_url)
1375
1388
  if isinstance(payload, str):
1376
1389
  payload = {"uid": payload, "code_verifier": _default_code_verifier}
1377
1390
  request_kwargs.setdefault("parse", default_parse)
@@ -1386,6 +1399,7 @@ class ClientRequestMixin:
1386
1399
  payload: str | dict,
1387
1400
  /,
1388
1401
  request: None | Callable = None,
1402
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1389
1403
  *,
1390
1404
  async_: Literal[False] = False,
1391
1405
  **request_kwargs,
@@ -1397,6 +1411,7 @@ class ClientRequestMixin:
1397
1411
  payload: str | dict,
1398
1412
  /,
1399
1413
  request: None | Callable = None,
1414
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1400
1415
  *,
1401
1416
  async_: Literal[True],
1402
1417
  **request_kwargs,
@@ -1407,6 +1422,7 @@ class ClientRequestMixin:
1407
1422
  payload: str | dict,
1408
1423
  /,
1409
1424
  request: None | Callable = None,
1425
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1410
1426
  *,
1411
1427
  async_: Literal[False, True] = False,
1412
1428
  **request_kwargs,
@@ -1422,7 +1438,7 @@ class ClientRequestMixin:
1422
1438
  :payload:
1423
1439
  - refresh_token: str
1424
1440
  """
1425
- api = "https://qrcodeapi.115.com/open/refreshToken"
1441
+ api = complete_api("/open/refreshToken", base_url=base_url)
1426
1442
  if isinstance(payload, str):
1427
1443
  payload = {"refresh_token": payload}
1428
1444
  request_kwargs.setdefault("parse", default_parse)
@@ -1432,53 +1448,137 @@ class ClientRequestMixin:
1432
1448
  return request(url=api, method="POST", data=payload, **request_kwargs)
1433
1449
 
1434
1450
  @overload
1435
- @staticmethod
1451
+ def login_qrcode_scan(
1452
+ self,
1453
+ payload: str | dict,
1454
+ /,
1455
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1456
+ *,
1457
+ async_: Literal[False] = False,
1458
+ **request_kwargs,
1459
+ ) -> dict:
1460
+ ...
1461
+ @overload
1462
+ def login_qrcode_scan(
1463
+ self,
1464
+ payload: str | dict,
1465
+ /,
1466
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1467
+ *,
1468
+ async_: Literal[True],
1469
+ **request_kwargs,
1470
+ ) -> Coroutine[Any, Any, dict]:
1471
+ ...
1472
+ def login_qrcode_scan(
1473
+ self,
1474
+ payload: str | dict,
1475
+ /,
1476
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1477
+ *,
1478
+ async_: Literal[False, True] = False,
1479
+ **request_kwargs,
1480
+ ) -> dict | Coroutine[Any, Any, dict]:
1481
+ """扫描二维码,payload 数据取自 `login_qrcode_token` 接口响应
1482
+
1483
+ GET https://qrcodeapi.115.com/api/2.0/prompt.php
1484
+
1485
+ :payload:
1486
+ - uid: str
1487
+ """
1488
+ api = complete_api("/api/2.0/prompt.php", base_url=base_url)
1489
+ if isinstance(payload, str):
1490
+ payload = {"uid": payload}
1491
+ return self.request(url=api, params=payload, async_=async_, **request_kwargs)
1492
+
1493
+ @overload
1436
1494
  def login_qrcode_scan_cancel(
1495
+ self,
1437
1496
  payload: str | dict,
1438
1497
  /,
1439
- request: None | Callable = None,
1498
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1440
1499
  *,
1441
1500
  async_: Literal[False] = False,
1442
1501
  **request_kwargs,
1443
1502
  ) -> dict:
1444
1503
  ...
1445
1504
  @overload
1446
- @staticmethod
1447
1505
  def login_qrcode_scan_cancel(
1506
+ self,
1448
1507
  payload: str | dict,
1449
1508
  /,
1450
- request: None | Callable = None,
1509
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1451
1510
  *,
1452
1511
  async_: Literal[True],
1453
1512
  **request_kwargs,
1454
1513
  ) -> Coroutine[Any, Any, dict]:
1455
1514
  ...
1456
- @staticmethod
1457
1515
  def login_qrcode_scan_cancel(
1516
+ self,
1458
1517
  payload: str | dict,
1459
1518
  /,
1460
- request: None | Callable = None,
1519
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1461
1520
  *,
1462
1521
  async_: Literal[False, True] = False,
1463
1522
  **request_kwargs,
1464
1523
  ) -> dict | Coroutine[Any, Any, dict]:
1465
1524
  """取消扫描二维码,payload 数据取自 `login_qrcode_scan` 接口响应
1466
1525
 
1467
- GET https://hnqrcodeapi.115.com/api/2.0/cancel.php
1526
+ GET https://qrcodeapi.115.com/api/2.0/cancel.php
1468
1527
 
1469
1528
  :payload:
1470
1529
  - key: str
1471
1530
  - uid: str
1472
1531
  - client: int = 0
1473
1532
  """
1474
- api = "https://hnqrcodeapi.115.com/api/2.0/cancel.php"
1533
+ api = complete_api("/api/2.0/cancel.php", base_url=base_url)
1475
1534
  if isinstance(payload, str):
1476
1535
  payload = {"key": payload, "uid": payload, "client": 0}
1477
- request_kwargs.setdefault("parse", default_parse)
1478
- if request is None:
1479
- return get_default_request()(url=api, params=payload, async_=async_, **request_kwargs)
1480
- else:
1481
- return request(url=api, params=payload, **request_kwargs)
1536
+ return self.request(url=api, params=payload, async_=async_, **request_kwargs)
1537
+
1538
+ @overload
1539
+ def login_qrcode_scan_confirm(
1540
+ self,
1541
+ payload: str | dict,
1542
+ /,
1543
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1544
+ *,
1545
+ async_: Literal[False] = False,
1546
+ **request_kwargs,
1547
+ ) -> dict:
1548
+ ...
1549
+ @overload
1550
+ def login_qrcode_scan_confirm(
1551
+ self,
1552
+ payload: str | dict,
1553
+ /,
1554
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1555
+ *,
1556
+ async_: Literal[True],
1557
+ **request_kwargs,
1558
+ ) -> Coroutine[Any, Any, dict]:
1559
+ ...
1560
+ def login_qrcode_scan_confirm(
1561
+ self,
1562
+ payload: str | dict,
1563
+ /,
1564
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1565
+ *,
1566
+ async_: Literal[False, True] = False,
1567
+ **request_kwargs,
1568
+ ) -> dict | Coroutine[Any, Any, dict]:
1569
+ """确认扫描二维码,payload 数据取自 `login_qrcode_scan` 接口响应
1570
+
1571
+ GET https://qrcodeapi.115.com/api/2.0/slogin.php
1572
+
1573
+ :payload:
1574
+ - key: str
1575
+ - uid: str
1576
+ - client: int = 0
1577
+ """
1578
+ api = complete_api("/api/2.0/slogin.php", base_url=base_url)
1579
+ if isinstance(payload, str):
1580
+ payload = {"key": payload, "uid": payload, "client": 0}
1581
+ return self.request(url=api, params=payload, async_=async_, **request_kwargs)
1482
1582
 
1483
1583
  @overload
1484
1584
  @staticmethod
@@ -1486,6 +1586,7 @@ class ClientRequestMixin:
1486
1586
  uid: str,
1487
1587
  app: str = "alipaymini",
1488
1588
  request: None | Callable = None,
1589
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1489
1590
  *,
1490
1591
  async_: Literal[False] = False,
1491
1592
  **request_kwargs,
@@ -1497,6 +1598,7 @@ class ClientRequestMixin:
1497
1598
  uid: str,
1498
1599
  app: str = "alipaymini",
1499
1600
  request: None | Callable = None,
1601
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1500
1602
  *,
1501
1603
  async_: Literal[True],
1502
1604
  **request_kwargs,
@@ -1507,13 +1609,14 @@ class ClientRequestMixin:
1507
1609
  uid: str,
1508
1610
  app: str = "alipaymini",
1509
1611
  request: None | Callable = None,
1612
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1510
1613
  *,
1511
1614
  async_: Literal[False, True] = False,
1512
1615
  **request_kwargs,
1513
1616
  ) -> dict | Coroutine[Any, Any, dict]:
1514
1617
  """获取扫码登录的结果,包含 cookie
1515
1618
 
1516
- POST https://passportapi.115.com/app/1.0/{app}/1.0/login/qrcode/
1619
+ POST https://qrcodeapi.115.com/app/1.0/{app}/1.0/login/qrcode/
1517
1620
 
1518
1621
  :param uid: 扫码的 uid
1519
1622
  :param app: 绑定的 app
@@ -1525,7 +1628,7 @@ class ClientRequestMixin:
1525
1628
  """
1526
1629
  if app == "desktop":
1527
1630
  app = "web"
1528
- api = f"http://passportapi.115.com/app/1.0/{app}/1.0/login/qrcode/"
1631
+ api = complete_api(f"/app/1.0/{app}/1.0/login/qrcode/", base_url=base_url)
1529
1632
  payload = {"account": uid}
1530
1633
  request_kwargs.setdefault("parse", default_parse)
1531
1634
  if request is None:
@@ -1539,6 +1642,7 @@ class ClientRequestMixin:
1539
1642
  payload: dict,
1540
1643
  /,
1541
1644
  request: None | Callable = None,
1645
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1542
1646
  *,
1543
1647
  async_: Literal[False] = False,
1544
1648
  **request_kwargs,
@@ -1550,6 +1654,7 @@ class ClientRequestMixin:
1550
1654
  payload: dict,
1551
1655
  /,
1552
1656
  request: None | Callable = None,
1657
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1553
1658
  *,
1554
1659
  async_: Literal[True],
1555
1660
  **request_kwargs,
@@ -1560,6 +1665,7 @@ class ClientRequestMixin:
1560
1665
  payload: dict,
1561
1666
  /,
1562
1667
  request: None | Callable = None,
1668
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1563
1669
  *,
1564
1670
  async_: Literal[False, True] = False,
1565
1671
  **request_kwargs,
@@ -1577,7 +1683,7 @@ class ClientRequestMixin:
1577
1683
  - time: int
1578
1684
  - sign: str
1579
1685
  """
1580
- api = "https://qrcodeapi.115.com/get/status/"
1686
+ api = complete_api("/get/status/", base_url=base_url)
1581
1687
  request_kwargs.setdefault("parse", default_parse)
1582
1688
  if request is None:
1583
1689
  return get_default_request()(url=api, params=payload, async_=async_, **request_kwargs)
@@ -1588,6 +1694,8 @@ class ClientRequestMixin:
1588
1694
  @staticmethod
1589
1695
  def login_qrcode_token(
1590
1696
  request: None | Callable = None,
1697
+ app: str = "web",
1698
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1591
1699
  *,
1592
1700
  async_: Literal[False] = False,
1593
1701
  **request_kwargs,
@@ -1597,6 +1705,8 @@ class ClientRequestMixin:
1597
1705
  @staticmethod
1598
1706
  def login_qrcode_token(
1599
1707
  request: None | Callable = None,
1708
+ app: str = "web",
1709
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1600
1710
  *,
1601
1711
  async_: Literal[True],
1602
1712
  **request_kwargs,
@@ -1605,6 +1715,8 @@ class ClientRequestMixin:
1605
1715
  @staticmethod
1606
1716
  def login_qrcode_token(
1607
1717
  request: None | Callable = None,
1718
+ app: str = "web",
1719
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1608
1720
  *,
1609
1721
  async_: Literal[False, True] = False,
1610
1722
  **request_kwargs,
@@ -1613,7 +1725,7 @@ class ClientRequestMixin:
1613
1725
 
1614
1726
  GET https://qrcodeapi.115.com/api/1.0/web/1.0/token/
1615
1727
  """
1616
- api = "https://qrcodeapi.115.com/api/1.0/web/1.0/token/"
1728
+ api = complete_api(f"/api/1.0/{app}/1.0/token/", base_url=base_url)
1617
1729
  request_kwargs.setdefault("parse", default_parse)
1618
1730
  if request is None:
1619
1731
  return get_default_request()(url=api, async_=async_, **request_kwargs)
@@ -1626,6 +1738,7 @@ class ClientRequestMixin:
1626
1738
  payload: int | str | dict,
1627
1739
  /,
1628
1740
  request: None | Callable = None,
1741
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1629
1742
  *,
1630
1743
  async_: Literal[False] = False,
1631
1744
  **request_kwargs,
@@ -1637,6 +1750,7 @@ class ClientRequestMixin:
1637
1750
  payload: int | str | dict,
1638
1751
  /,
1639
1752
  request: None | Callable = None,
1753
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1640
1754
  *,
1641
1755
  async_: Literal[True],
1642
1756
  **request_kwargs,
@@ -1647,6 +1761,7 @@ class ClientRequestMixin:
1647
1761
  payload: int | str | dict,
1648
1762
  /,
1649
1763
  request: None | Callable = None,
1764
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1650
1765
  *,
1651
1766
  async_: Literal[False, True] = False,
1652
1767
  **request_kwargs,
@@ -1678,7 +1793,7 @@ class ClientRequestMixin:
1678
1793
 
1679
1794
  - code_challenge_method: str = <default> 💡 计算 `code_challenge` 的 hash 算法,支持 "md5", "sha1", "sha256"
1680
1795
  """
1681
- api = "https://qrcodeapi.115.com/open/authDeviceCode"
1796
+ api = complete_api("/open/authDeviceCode", base_url=base_url)
1682
1797
  if isinstance(payload, (int, str)):
1683
1798
  payload = {
1684
1799
  "client_id": payload,
@@ -1698,6 +1813,7 @@ class ClientRequestMixin:
1698
1813
  /,
1699
1814
  app: None | str = "",
1700
1815
  console_qrcode: bool = True,
1816
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1701
1817
  *,
1702
1818
  async_: Literal[False] = False,
1703
1819
  **request_kwargs,
@@ -1710,6 +1826,7 @@ class ClientRequestMixin:
1710
1826
  /,
1711
1827
  app: None | str = "",
1712
1828
  console_qrcode: bool = True,
1829
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1713
1830
  *,
1714
1831
  async_: Literal[True],
1715
1832
  **request_kwargs,
@@ -1721,6 +1838,7 @@ class ClientRequestMixin:
1721
1838
  /,
1722
1839
  app: None | str = "",
1723
1840
  console_qrcode: bool = True,
1841
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1724
1842
  *,
1725
1843
  async_: Literal[False, True] = False,
1726
1844
  **request_kwargs,
@@ -1821,6 +1939,7 @@ class ClientRequestMixin:
1821
1939
  def gen_step():
1822
1940
  resp = yield cls.login_qrcode_token(
1823
1941
  async_=async_,
1942
+ base_url=base_url,
1824
1943
  **request_kwargs,
1825
1944
  )
1826
1945
  qrcode_token = resp["data"]
@@ -1834,7 +1953,7 @@ class ClientRequestMixin:
1834
1953
  qr.add_data(qrcode)
1835
1954
  qr.print_ascii(tty=isatty(1))
1836
1955
  else:
1837
- url = "https://qrcodeapi.115.com/api/1.0/web/1.0/qrcode?uid=" + login_uid
1956
+ url = complete_api(f"/api/1.0/web/1.0/qrcode?uid={login_uid}", base_url=base_url)
1838
1957
  if async_:
1839
1958
  yield partial(startfile_async, url)
1840
1959
  else:
@@ -1843,6 +1962,7 @@ class ClientRequestMixin:
1843
1962
  try:
1844
1963
  resp = yield cls.login_qrcode_scan_status(
1845
1964
  qrcode_token,
1965
+ base_url=base_url,
1846
1966
  async_=async_,
1847
1967
  **request_kwargs,
1848
1968
  )
@@ -1866,6 +1986,7 @@ class ClientRequestMixin:
1866
1986
  return cls.login_qrcode_scan_result(
1867
1987
  login_uid,
1868
1988
  app,
1989
+ base_url=base_url,
1869
1990
  async_=async_,
1870
1991
  **request_kwargs,
1871
1992
  )
@@ -1880,6 +2001,7 @@ class ClientRequestMixin:
1880
2001
  /,
1881
2002
  app_id: int | str = 100195993,
1882
2003
  console_qrcode: bool = True,
2004
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1883
2005
  *,
1884
2006
  async_: Literal[False] = False,
1885
2007
  **request_kwargs,
@@ -1892,6 +2014,7 @@ class ClientRequestMixin:
1892
2014
  /,
1893
2015
  app_id: int | str = 100195993,
1894
2016
  console_qrcode: bool = True,
2017
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1895
2018
  *,
1896
2019
  async_: Literal[True],
1897
2020
  **request_kwargs,
@@ -1903,6 +2026,7 @@ class ClientRequestMixin:
1903
2026
  /,
1904
2027
  app_id: int | str = 100195993,
1905
2028
  console_qrcode: bool = True,
2029
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
1906
2030
  *,
1907
2031
  async_: Literal[False, True] = False,
1908
2032
  **request_kwargs,
@@ -1918,6 +2042,7 @@ class ClientRequestMixin:
1918
2042
  def gen_step():
1919
2043
  resp = yield cls.login_qrcode_token_open(
1920
2044
  app_id,
2045
+ base_url=base_url,
1921
2046
  async_=async_,
1922
2047
  **request_kwargs,
1923
2048
  )
@@ -1932,7 +2057,7 @@ class ClientRequestMixin:
1932
2057
  qr.add_data(qrcode)
1933
2058
  qr.print_ascii(tty=isatty(1))
1934
2059
  else:
1935
- url = "https://qrcodeapi.115.com/api/1.0/web/1.0/qrcode?uid=" + login_uid
2060
+ url = complete_api(f"/api/1.0/web/1.0/qrcode?uid={login_uid}", base_url=base_url)
1936
2061
  if async_:
1937
2062
  yield partial(startfile_async, url)
1938
2063
  else:
@@ -1941,6 +2066,7 @@ class ClientRequestMixin:
1941
2066
  try:
1942
2067
  resp = yield cls.login_qrcode_scan_status(
1943
2068
  qrcode_token,
2069
+ base_url=base_url,
1944
2070
  async_=async_,
1945
2071
  **request_kwargs,
1946
2072
  )
@@ -1962,11 +2088,14 @@ class ClientRequestMixin:
1962
2088
  raise LoginError(errno.EAUTH, f"qrcode: aborted with {resp!r}")
1963
2089
  return cls.login_qrcode_access_token_open(
1964
2090
  login_uid,
2091
+ base_url=base_url,
1965
2092
  async_=async_,
1966
2093
  **request_kwargs,
1967
2094
  )
1968
2095
  return run_gen_step(gen_step, async_=async_)
1969
2096
 
2097
+ ########## Upload API ##########
2098
+
1970
2099
  upload_endpoint = "http://oss-cn-shenzhen.aliyuncs.com"
1971
2100
 
1972
2101
  def upload_endpoint_url(
@@ -4360,6 +4489,10 @@ class P115Client(P115OpenClient):
4360
4489
  """
4361
4490
  self.cookies = None
4362
4491
 
4492
+ @locked_cacheproperty
4493
+ def request_kwargs(self, /) -> dict:
4494
+ return {}
4495
+
4363
4496
  @locked_cacheproperty
4364
4497
  def user_id(self, /) -> int:
4365
4498
  cookie_uid = self.cookies.get("UID")
@@ -4658,82 +4791,6 @@ class P115Client(P115OpenClient):
4658
4791
  return self
4659
4792
  return run_gen_step(gen_step, async_=async_)
4660
4793
 
4661
- @overload
4662
- def login_qrcode_scan(
4663
- self,
4664
- payload: str | dict,
4665
- /,
4666
- async_: Literal[False] = False,
4667
- **request_kwargs,
4668
- ) -> dict:
4669
- ...
4670
- @overload
4671
- def login_qrcode_scan(
4672
- self,
4673
- payload: str | dict,
4674
- /,
4675
- async_: Literal[True],
4676
- **request_kwargs,
4677
- ) -> Coroutine[Any, Any, dict]:
4678
- ...
4679
- def login_qrcode_scan(
4680
- self,
4681
- payload: str | dict,
4682
- /,
4683
- async_: Literal[False, True] = False,
4684
- **request_kwargs,
4685
- ) -> dict | Coroutine[Any, Any, dict]:
4686
- """扫描二维码,payload 数据取自 `login_qrcode_token` 接口响应
4687
-
4688
- GET https://qrcodeapi.115.com/api/2.0/prompt.php
4689
-
4690
- :payload:
4691
- - uid: str
4692
- """
4693
- api = "https://qrcodeapi.115.com/api/2.0/prompt.php"
4694
- if isinstance(payload, str):
4695
- payload = {"uid": payload}
4696
- return self.request(url=api, params=payload, async_=async_, **request_kwargs)
4697
-
4698
- @overload
4699
- def login_qrcode_scan_confirm(
4700
- self,
4701
- payload: str | dict,
4702
- /,
4703
- async_: Literal[False] = False,
4704
- **request_kwargs,
4705
- ) -> dict:
4706
- ...
4707
- @overload
4708
- def login_qrcode_scan_confirm(
4709
- self,
4710
- payload: str | dict,
4711
- /,
4712
- async_: Literal[True],
4713
- **request_kwargs,
4714
- ) -> Coroutine[Any, Any, dict]:
4715
- ...
4716
- def login_qrcode_scan_confirm(
4717
- self,
4718
- payload: str | dict,
4719
- /,
4720
- async_: Literal[False, True] = False,
4721
- **request_kwargs,
4722
- ) -> dict | Coroutine[Any, Any, dict]:
4723
- """确认扫描二维码,payload 数据取自 `login_qrcode_scan` 接口响应
4724
-
4725
- GET https://hnqrcodeapi.115.com/api/2.0/slogin.php
4726
-
4727
- :payload:
4728
- - key: str
4729
- - uid: str
4730
- - client: int = 0
4731
- """
4732
- api = "https://hnqrcodeapi.115.com/api/2.0/slogin.php"
4733
- if isinstance(payload, str):
4734
- payload = {"key": payload, "uid": payload, "client": 0}
4735
- return self.request(url=api, params=payload, async_=async_, **request_kwargs)
4736
-
4737
4794
  @overload
4738
4795
  def login_with_app(
4739
4796
  self,
@@ -5367,7 +5424,7 @@ class P115Client(P115OpenClient):
5367
5424
  return None
5368
5425
  return self.logout_by_ssoent(ssoent, async_=async_, **request_kwargs)
5369
5426
 
5370
- def request(
5427
+ def _request(
5371
5428
  self,
5372
5429
  /,
5373
5430
  url: str,
@@ -5609,6 +5666,90 @@ class P115Client(P115OpenClient):
5609
5666
  return resp
5610
5667
  return run_gen_step(gen_step, async_=async_)
5611
5668
 
5669
+ def request(
5670
+ self,
5671
+ /,
5672
+ url: str,
5673
+ method: str = "GET",
5674
+ params = None,
5675
+ data = None,
5676
+ *,
5677
+ async_: Literal[False, True] = False,
5678
+ **request_kwargs,
5679
+ ):
5680
+ """帮助函数:可执行同步和异步的网络请求
5681
+
5682
+ :param url: HTTP 的请求链接
5683
+ :param method: HTTP 的请求方法
5684
+ :param params: 查询参数
5685
+ :param check: 是否用 `check_response` 函数检查返回值
5686
+ :param ecdh_encrypt: 使用 ecdh 算法进行加密(返回值也要解密)
5687
+ :param fetch_cert_headers: 调用以获取认证信息头
5688
+ :param revert_cert_headers: 调用以退还认证信息头
5689
+ :param async_: 说明 `request` 是同步调用还是异步调用
5690
+ :param request: HTTP 请求调用,如果为 None,则默认用 httpx 执行请求
5691
+ 如果传入调用,则必须至少能接受以下几个关键词参数:
5692
+
5693
+ - url: HTTP 的请求链接
5694
+ - method: HTTP 的请求方法
5695
+ - headers: HTTP 的请求头
5696
+ - data: HTTP 的请求体
5697
+ - parse: 解析 HTTP 响应的方法,默认会构建一个 Callable,会把响应的字节数据视为 JSON 进行反序列化解析
5698
+
5699
+ - 如果为 None,则直接把响应对象返回
5700
+ - 如果为 ...(Ellipsis),则把响应对象关闭后将其返回
5701
+ - 如果为 True,则根据响应头来确定把响应得到的字节数据解析成何种格式(反序列化),请求也会被自动关闭
5702
+ - 如果为 False,则直接返回响应得到的字节数据,请求也会被自动关闭
5703
+ - 如果为 Callable,则使用此调用来解析数据,接受 1-2 个位置参数,并把解析结果返回给 `request` 的调用者,请求也会被自动关闭
5704
+ - 如果只接受 1 个位置参数,则把响应对象传给它
5705
+ - 如果能接受 2 个位置参数,则把响应对象和响应得到的字节数据(响应体)传给它
5706
+
5707
+ :param request_kwargs: 其余的请求参数,会被传给 `request`
5708
+
5709
+ :return: 直接返回 `request` 执行请求后的返回值
5710
+
5711
+ .. note::
5712
+ `request` 可以由不同的请求库来提供,下面是封装了一些模块
5713
+
5714
+ 1. `httpx_request <https://pypi.org/project/httpx_request/>`_,由 `httpx <https://pypi.org/project/httpx/>`_ 封装,支持同步和异步调用,本模块默认用的就是这个封装
5715
+
5716
+ .. code:: python
5717
+
5718
+ from httpx_request import request
5719
+
5720
+ 2. `python-urlopen <https://pypi.org/project/python-urlopen/>`_,由 `urllib.request.urlopen <https://docs.python.org/3/library/urllib.request.html#urllib.request.urlopen>`_ 封装,支持同步调用,性能相对最差
5721
+
5722
+ .. code:: python
5723
+
5724
+ from urlopen import request
5725
+
5726
+ 3. `urllib3_request <https://pypi.org/project/urllib3_request/>`_,由 `urllib3 <https://pypi.org/project/urllib3/>`_ 封装,支持同步调用,性能相对较好,推荐使用
5727
+
5728
+ .. code:: python
5729
+
5730
+ from urllib3_request import request
5731
+
5732
+ 4. `requests_request <https://pypi.org/project/requests_request/>`_,由 `requests <https://pypi.org/project/requests/>`_ 封装,支持同步调用
5733
+
5734
+ .. code:: python
5735
+
5736
+ from requests_request import request
5737
+
5738
+ 5. `aiohttp_client_request <https://pypi.org/project/aiohttp_client_request/>`_,由 `aiohttp <https://pypi.org/project/aiohttp/>`_ 封装,支持异步调用,异步并发能力最强,推荐使用
5739
+
5740
+ .. code:: python
5741
+
5742
+ from aiohttp_client_request import request
5743
+
5744
+ 6. `blacksheep_client_request <https://pypi.org/project/blacksheep_client_request/>`_,由 `blacksheep <https://pypi.org/project/blacksheep/>`_ 封装,支持异步调用
5745
+
5746
+ .. code:: python
5747
+
5748
+ from blacksheep_client_request import request
5749
+ """
5750
+ kwargs = {**self.request_kwargs, **request_kwargs}
5751
+ return self._request(url, method, params, data, async_=async_, **kwargs)
5752
+
5612
5753
  ########## Activity API ##########
5613
5754
 
5614
5755
  @overload
@@ -14960,10 +15101,143 @@ class P115Client(P115OpenClient):
14960
15101
  return device["icon"]
14961
15102
  return run_gen_step(gen_step, async_=async_)
14962
15103
 
15104
+ @overload
15105
+ def login_open_auth_detail(
15106
+ self,
15107
+ payload: int | str | dict,
15108
+ /,
15109
+ app: str = "web",
15110
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15111
+ *,
15112
+ async_: Literal[False] = False,
15113
+ **request_kwargs,
15114
+ ) -> dict:
15115
+ ...
15116
+ @overload
15117
+ def login_open_auth_detail(
15118
+ self,
15119
+ payload: int | str | dict,
15120
+ /,
15121
+ app: str = "web",
15122
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15123
+ *,
15124
+ async_: Literal[True],
15125
+ **request_kwargs,
15126
+ ) -> Coroutine[Any, Any, dict]:
15127
+ ...
15128
+ def login_open_auth_detail(
15129
+ self,
15130
+ payload: int | str | dict,
15131
+ /,
15132
+ app: str = "web",
15133
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15134
+ *,
15135
+ async_: Literal[False, True] = False,
15136
+ **request_kwargs,
15137
+ ) -> dict | Coroutine[Any, Any, dict]:
15138
+ """获取某个开放应用的授权信息
15139
+
15140
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/user/getAppAuthDetail
15141
+
15142
+ :payload:
15143
+ - auth_id: int | str 💡 授权 id
15144
+ """
15145
+ api = complete_api(f"/app/1.0/{app}/1.0/user/getAppAuthDetail", base_url=base_url)
15146
+ if isinstance(payload, (int, str)):
15147
+ payload = {"auth_id": payload}
15148
+ return self.request(url=api, params=payload, async_=async_, **request_kwargs)
15149
+
15150
+ @overload
15151
+ def login_open_auth_list(
15152
+ self,
15153
+ /,
15154
+ app: str = "web",
15155
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15156
+ *,
15157
+ async_: Literal[False] = False,
15158
+ **request_kwargs,
15159
+ ) -> dict:
15160
+ ...
15161
+ @overload
15162
+ def login_open_auth_list(
15163
+ self,
15164
+ /,
15165
+ app: str = "web",
15166
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15167
+ *,
15168
+ async_: Literal[True],
15169
+ **request_kwargs,
15170
+ ) -> Coroutine[Any, Any, dict]:
15171
+ ...
15172
+ def login_open_auth_list(
15173
+ self,
15174
+ /,
15175
+ app: str = "web",
15176
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15177
+ *,
15178
+ async_: Literal[False, True] = False,
15179
+ **request_kwargs,
15180
+ ) -> dict | Coroutine[Any, Any, dict]:
15181
+ """获取所有授权的开放应用的列表
15182
+
15183
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/user/getAppAuthList
15184
+ """
15185
+ api = complete_api(f"/app/1.0/{app}/1.0/user/getAppAuthList", base_url=base_url)
15186
+ return self.request(url=api, async_=async_, **request_kwargs)
15187
+
15188
+ @overload
15189
+ def login_open_deauth(
15190
+ self,
15191
+ payload: int | str | dict,
15192
+ /,
15193
+ app: str = "web",
15194
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15195
+ *,
15196
+ async_: Literal[False] = False,
15197
+ **request_kwargs,
15198
+ ) -> dict:
15199
+ ...
15200
+ @overload
15201
+ def login_open_deauth(
15202
+ self,
15203
+ payload: int | str | dict,
15204
+ /,
15205
+ app: str = "web",
15206
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15207
+ *,
15208
+ async_: Literal[True],
15209
+ **request_kwargs,
15210
+ ) -> Coroutine[Any, Any, dict]:
15211
+ ...
15212
+ def login_open_deauth(
15213
+ self,
15214
+ payload: int | str | dict,
15215
+ /,
15216
+ app: str = "web",
15217
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15218
+ *,
15219
+ async_: Literal[False, True] = False,
15220
+ **request_kwargs,
15221
+ ) -> dict | Coroutine[Any, Any, dict]:
15222
+ """取消某个开放应用的授权
15223
+
15224
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/user/deauthApp
15225
+
15226
+ :payload:
15227
+ - auth_id: int | str 💡 授权 id
15228
+ """
15229
+ api = complete_api(f"/app/1.0/{app}/1.0/user/deauthApp", base_url=base_url)
15230
+ if isinstance(payload, (int, str)):
15231
+ payload = {"auth_id": payload}
15232
+ return self.request(url=api, method="POST", data=payload, async_=async_, **request_kwargs)
15233
+
14963
15234
  @overload
14964
15235
  def login_check_sso(
14965
15236
  self,
14966
15237
  /,
15238
+ app: str = "web",
15239
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15240
+ *,
14967
15241
  async_: Literal[False] = False,
14968
15242
  **request_kwargs,
14969
15243
  ) -> dict:
@@ -14972,6 +15246,9 @@ class P115Client(P115OpenClient):
14972
15246
  def login_check_sso(
14973
15247
  self,
14974
15248
  /,
15249
+ app: str = "web",
15250
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15251
+ *,
14975
15252
  async_: Literal[True],
14976
15253
  **request_kwargs,
14977
15254
  ) -> Coroutine[Any, Any, dict]:
@@ -14979,14 +15256,17 @@ class P115Client(P115OpenClient):
14979
15256
  def login_check_sso(
14980
15257
  self,
14981
15258
  /,
15259
+ app: str = "web",
15260
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15261
+ *,
14982
15262
  async_: Literal[False, True] = False,
14983
15263
  **request_kwargs,
14984
15264
  ) -> dict | Coroutine[Any, Any, dict]:
14985
15265
  """检查当前 cookies 的登录状态信息,并且自最近一次登录的 60 秒后,使当前设备下除最近一次登录外的所有 cookies 失效
14986
15266
 
14987
- GET https://passportapi.115.com/app/1.0/web/1.0/check/sso
15267
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/check/sso
14988
15268
  """
14989
- api = "https://passportapi.115.com/app/1.0/web/1.0/check/sso"
15269
+ api = complete_api(f"/app/1.0/{app}/1.0/check/sso", base_url=base_url)
14990
15270
  return self.request(url=api, async_=async_, **request_kwargs)
14991
15271
 
14992
15272
  @overload
@@ -15025,6 +15305,9 @@ class P115Client(P115OpenClient):
15025
15305
  def login_devices(
15026
15306
  self,
15027
15307
  /,
15308
+ app: str = "web",
15309
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15310
+ *,
15028
15311
  async_: Literal[False] = False,
15029
15312
  **request_kwargs,
15030
15313
  ) -> dict:
@@ -15033,6 +15316,9 @@ class P115Client(P115OpenClient):
15033
15316
  def login_devices(
15034
15317
  self,
15035
15318
  /,
15319
+ app: str = "web",
15320
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15321
+ *,
15036
15322
  async_: Literal[True],
15037
15323
  **request_kwargs,
15038
15324
  ) -> Coroutine[Any, Any, dict]:
@@ -15040,14 +15326,17 @@ class P115Client(P115OpenClient):
15040
15326
  def login_devices(
15041
15327
  self,
15042
15328
  /,
15329
+ app: str = "web",
15330
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15331
+ *,
15043
15332
  async_: Literal[False, True] = False,
15044
15333
  **request_kwargs,
15045
15334
  ) -> dict | Coroutine[Any, Any, dict]:
15046
15335
  """获取所有的已登录设备的信息,不过当前的 cookies 必须是登录状态(未退出或未失效)
15047
15336
 
15048
- GET https://passportapi.115.com/app/1.0/web/1.0/login_log/login_devices
15337
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/login_log/login_devices
15049
15338
  """
15050
- api = "https://passportapi.115.com/app/1.0/web/1.0/login_log/login_devices"
15339
+ api = complete_api(f"/app/1.0/{app}/1.0/login_log/login_devices", base_url=base_url)
15051
15340
  return self.request(url=api, async_=async_, **request_kwargs)
15052
15341
 
15053
15342
  @overload
@@ -15093,6 +15382,8 @@ class P115Client(P115OpenClient):
15093
15382
  self,
15094
15383
  payload: dict = {},
15095
15384
  /,
15385
+ app: str = "web",
15386
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15096
15387
  *,
15097
15388
  async_: Literal[False] = False,
15098
15389
  **request_kwargs,
@@ -15103,6 +15394,8 @@ class P115Client(P115OpenClient):
15103
15394
  self,
15104
15395
  payload: dict = {},
15105
15396
  /,
15397
+ app: str = "web",
15398
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15106
15399
  *,
15107
15400
  async_: Literal[True],
15108
15401
  **request_kwargs,
@@ -15112,19 +15405,21 @@ class P115Client(P115OpenClient):
15112
15405
  self,
15113
15406
  payload: dict = {},
15114
15407
  /,
15408
+ app: str = "web",
15409
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15115
15410
  *,
15116
15411
  async_: Literal[False, True] = False,
15117
15412
  **request_kwargs,
15118
15413
  ) -> dict | Coroutine[Any, Any, dict]:
15119
15414
  """获取登录信息日志列表
15120
15415
 
15121
- GET https://passportapi.115.com/app/1.0/web/1.0/login_log/log
15416
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/login_log/log
15122
15417
 
15123
15418
  :payload:
15124
15419
  - start: int = 0
15125
15420
  - limit: int = 100
15126
15421
  """
15127
- api = "https://passportapi.115.com/app/1.0/web/1.0/login_log/log"
15422
+ api = complete_api(f"/app/1.0/{app}/1.0/login_log/log", base_url=base_url)
15128
15423
  payload = {"start": 0, "limit": 100, **payload}
15129
15424
  return self.request(url=api, params=payload, async_=async_, **request_kwargs)
15130
15425
 
@@ -15133,6 +15428,7 @@ class P115Client(P115OpenClient):
15133
15428
  self,
15134
15429
  /,
15135
15430
  app: str = "web",
15431
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15136
15432
  *,
15137
15433
  async_: Literal[False] = False,
15138
15434
  **request_kwargs,
@@ -15143,6 +15439,7 @@ class P115Client(P115OpenClient):
15143
15439
  self,
15144
15440
  /,
15145
15441
  app: str = "web",
15442
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15146
15443
  *,
15147
15444
  async_: Literal[True],
15148
15445
  **request_kwargs,
@@ -15152,15 +15449,16 @@ class P115Client(P115OpenClient):
15152
15449
  self,
15153
15450
  /,
15154
15451
  app: str = "web",
15452
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15155
15453
  *,
15156
15454
  async_: Literal[False, True] = False,
15157
15455
  **request_kwargs,
15158
15456
  ) -> dict | Coroutine[Any, Any, dict]:
15159
15457
  """当前登录的设备总数和最近登录的设备
15160
15458
 
15161
- GET https://passportapi.115.com/app/1.0/web/1.0/login_log/login_online
15459
+ GET https://qrcodeapi.115.com/app/1.0/web/1.0/login_log/login_online
15162
15460
  """
15163
- api = f"http://passportapi.115.com/app/1.0/{app}/1.0/login_log/login_online"
15461
+ api = complete_api(f"/app/1.0/{app}/1.0/login_log/login_online", base_url=base_url)
15164
15462
  return self.request(url=api, async_=async_, **request_kwargs)
15165
15463
 
15166
15464
  @overload
@@ -15222,6 +15520,7 @@ class P115Client(P115OpenClient):
15222
15520
  /,
15223
15521
  app: None | str = None,
15224
15522
  request: None | Callable = None,
15523
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15225
15524
  *,
15226
15525
  async_: Literal[False] = False,
15227
15526
  **request_kwargs,
@@ -15233,6 +15532,7 @@ class P115Client(P115OpenClient):
15233
15532
  /,
15234
15533
  app: None | str = None,
15235
15534
  request: None | Callable = None,
15535
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15236
15536
  *,
15237
15537
  async_: Literal[True],
15238
15538
  **request_kwargs,
@@ -15243,13 +15543,14 @@ class P115Client(P115OpenClient):
15243
15543
  /,
15244
15544
  app: None | str = None,
15245
15545
  request: None | Callable = None,
15546
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15246
15547
  *,
15247
15548
  async_: Literal[False, True] = False,
15248
15549
  **request_kwargs,
15249
15550
  ) -> None | Coroutine[Any, Any, None]:
15250
15551
  """退出登录状态(可以把某个客户端下线,所有已登录设备可从 `login_devices` 获取)
15251
15552
 
15252
- GET https://passportapi.115.com/app/1.0/{app}/1.0/logout/logout
15553
+ GET https://qrcodeapi.115.com/app/1.0/{app}/1.0/logout/logout
15253
15554
 
15254
15555
  :param app: 退出登录的 app
15255
15556
 
@@ -15315,7 +15616,7 @@ class P115Client(P115OpenClient):
15315
15616
  app = yield self.login_app(async_=async_)
15316
15617
  if app == "desktop":
15317
15618
  app = "web"
15318
- api = f"http://passportapi.115.com/app/1.0/{app}/1.0/logout/logout"
15619
+ api = complete_api(f"/app/1.0/{app}/1.0/logout/logout", base_url=base_url)
15319
15620
  request_kwargs["headers"] = {**(request_kwargs.get("headers") or {}), "Cookie": self.cookies_str}
15320
15621
  request_kwargs.setdefault("parse", ...)
15321
15622
  if request is None:
@@ -15329,6 +15630,8 @@ class P115Client(P115OpenClient):
15329
15630
  self,
15330
15631
  payload: None | str | dict = None,
15331
15632
  /,
15633
+ app: str = "web",
15634
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15332
15635
  *,
15333
15636
  async_: Literal[False] = False,
15334
15637
  **request_kwargs,
@@ -15339,6 +15642,8 @@ class P115Client(P115OpenClient):
15339
15642
  self,
15340
15643
  payload: None | str | dict = None,
15341
15644
  /,
15645
+ app: str = "web",
15646
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15342
15647
  *,
15343
15648
  async_: Literal[True],
15344
15649
  **request_kwargs,
@@ -15348,13 +15653,15 @@ class P115Client(P115OpenClient):
15348
15653
  self,
15349
15654
  payload: None | str | dict = None,
15350
15655
  /,
15656
+ app: str = "web",
15657
+ base_url: str | Callable[[], str] = "https://qrcodeapi.115.com",
15351
15658
  *,
15352
15659
  async_: Literal[False, True] = False,
15353
15660
  **request_kwargs,
15354
15661
  ) -> dict | Coroutine[Any, Any, dict]:
15355
15662
  """退出登录状态(可以把某个客户端下线,所有已登录设备可从 `login_devices` 获取)
15356
15663
 
15357
- POST https://passportapi.115.com/app/1.0/web/1.0/logout/mange
15664
+ POST https://qrcodeapi.115.com/app/1.0/web/1.0/logout/mange
15358
15665
 
15359
15666
  :payload:
15360
15667
  - ssoent: str
@@ -15415,7 +15722,7 @@ class P115Client(P115OpenClient):
15415
15722
  | 24 | S1 | harmony | 115(Harmony端) |
15416
15723
  +-------+----------+------------+-------------------------+
15417
15724
  """
15418
- api = "https://passportapi.115.com/app/1.0/web/1.0/logout/mange"
15725
+ api = complete_api(f"/app/1.0/{app}/1.0/logout/mange", base_url=base_url)
15419
15726
  if payload is None:
15420
15727
  payload = {"ssoent": self.login_ssoent or ""}
15421
15728
  elif isinstance(payload, str):
@@ -14,7 +14,7 @@ from typing import Final
14
14
 
15
15
 
16
16
  #: 可用的 AppID 列表
17
- AVAILABLE_APP_IDS = range(100195123, 100196837, 2)
17
+ AVAILABLE_APP_IDS = range(100195123, 100196845, 2)
18
18
 
19
19
 
20
20
  #: 目前可用的登录设备
@@ -3,8 +3,9 @@
3
3
 
4
4
  __all__ = [
5
5
  "P115Warning", "P115OSError", "AuthenticationError", "BusyOSError", "DataError",
6
- "LoginError", "MultipartUploadAbort", "NotSupportedError", "OperationalError",
7
- "P115FileExistsError", "P115FileNotFoundError", "P115IsADirectoryError",
6
+ "LoginError", "MultipartUploadAbort", "OpenAppAuthLimitExceeded",
7
+ "NotSupportedError", "OperationalError", "P115FileExistsError",
8
+ "P115FileNotFoundError", "P115IsADirectoryError",
8
9
  "P115NotADirectoryError", "P115PermissionError", "P115TimeoutError",
9
10
  ]
10
11
 
@@ -81,6 +82,11 @@ class MultipartUploadAbort(P115OSError):
81
82
  return f"{type(self).__module__}.{type(self).__qualname__}({self.ticket})"
82
83
 
83
84
 
85
+ class OpenAppAuthLimitExceeded(AuthenticationError):
86
+ """当授权应用数达到上限时抛出
87
+ """
88
+
89
+
84
90
  class NotSupportedError(P115OSError):
85
91
  """当调用不存在的接口或者接口不支持此操作时抛出
86
92
  """
@@ -276,9 +276,6 @@ def get_file_count(
276
276
  ) -> int | Coroutine[Any, Any, int]:
277
277
  """获取文件总数
278
278
 
279
- .. caution::
280
- 我通过一些经验,搭配了多个接口的占比和参数分布,可能不够合理,以后会根据实际情况调整
281
-
282
279
  :param client: 115 客户端或 cookies
283
280
  :param cid: 目录 id
284
281
  :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
@@ -408,9 +405,6 @@ def get_ancestors(
408
405
  ) -> list[dict] | Coroutine[Any, Any, list[dict]]:
409
406
  """获取某个节点对应的祖先节点列表(只有 id、parent_id 和 name 的信息)
410
407
 
411
- .. caution::
412
- 我通过一些经验,搭配了多个接口的占比和参数分布,可能不够合理,以后会根据实际情况调整
413
-
414
408
  :param client: 115 客户端或 cookies
415
409
  :param attr: 待查询节点 `id` 或信息(必须有 `id`,可选有 `parent_id`)
416
410
  :param id_to_dirnode: 字典,保存 id 到对应文件的 `DirNode(name, parent_id)` 命名元组的字典
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "p115client"
3
- version = "0.0.5.10.6"
3
+ version = "0.0.5.10.8"
4
4
  description = "Python 115 webdisk client."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
@@ -31,8 +31,8 @@ python = "^3.12"
31
31
  aiofile = "*"
32
32
  ed2k = ">=0.0.2.1"
33
33
  http_response = ">=0.0.2.2"
34
- httpx = "*"
35
- httpx_request = ">=0.1.3"
34
+ httpx = ">=0.28"
35
+ httpx_request = ">=0.1.4"
36
36
  iter_collect = ">=0.0.5.1"
37
37
  multidict = "*"
38
38
  orjson = "*"
@@ -195,7 +195,7 @@ class MyCustom115Client(P115Client):
195
195
 
196
196
  ### 3. 检查响应
197
197
 
198
- 接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p115client.check_response` 执行检查。首先会查看其中名为 "state" 的键的对应值,如果为 True、1 或不存在,则原样返回被检查的数据;否则,"state" 的对应值大概是 False 或 0,说明有问题出现,会根据实际情况抛出一个异常,但都是 `OSError` 的实例,其中大部分还是 `p115client.P115OSError` 的实例。
198
+ 接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p115client.check_response` 执行检查。首先会查看其中名为 "state" 的键的对应值,如果为 True、1 或不存在,则原样返回被检查的数据;否则,"state" 的对应值大概是 False 或 0,说明有问题出现,会根据实际情况抛出一个异常,但都是 `p115client.P115OSError` 的实例。
199
199
 
200
200
  ```python
201
201
  from p115client import check_response
File without changes