qqmusic-api-python 0.5.2__tar.gz → 0.5.3__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 (130) hide show
  1. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/PKG-INFO +1 -1
  2. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/release-notes.md +11 -1
  3. qqmusic_api_python-0.5.3/qqmusic_api/__init__.py +52 -0
  4. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/__init__.py +20 -0
  5. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/exceptions.py +78 -2
  6. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/versioning.py +1 -1
  7. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/base.py +3 -3
  8. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/request.py +5 -1
  9. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/login.py +253 -103
  10. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/login_utils.py +3 -7
  11. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/recommend.py +29 -9
  12. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/search.py +22 -10
  13. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/singer.py +18 -18
  14. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/song.py +4 -3
  15. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/utils/common.py +2 -1
  16. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_login.py +54 -4
  17. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_login_utils.py +1 -1
  18. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_recommend.py +2 -2
  19. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_search.py +1 -1
  20. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_singer.py +9 -9
  21. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_song.py +2 -2
  22. qqmusic_api_python-0.5.2/qqmusic_api/__init__.py +0 -32
  23. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.agents/skills/pydantic/SKILL.md +0 -0
  24. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.agents/skills/python-standards/SKILL.md +0 -0
  25. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.agents/skills/tarsio/SKILL.md +0 -0
  26. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.agents/skills/tarsio/references/api-reference.md +0 -0
  27. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.agents/skills/uv-package-manager/SKILL.md +0 -0
  28. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  29. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  30. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  31. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  32. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/renovate.json +0 -0
  33. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/workflows/checking.yaml +0 -0
  34. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/workflows/docs.yml +0 -0
  35. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/workflows/release.yml +0 -0
  36. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.github/workflows/testing.yml +0 -0
  37. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.gitignore +0 -0
  38. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/.markdownlint-cli2.yaml +0 -0
  39. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/AGENTS.md +0 -0
  40. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/LICENSE +0 -0
  41. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/README.md +0 -0
  42. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/assets/qq-music.svg +0 -0
  43. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/cliff.toml +0 -0
  44. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/coding.md +0 -0
  45. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/contributing.md +0 -0
  46. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/index.md +0 -0
  47. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/core/client.md +0 -0
  48. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/core/exception.md +0 -0
  49. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/core/pagination.md +0 -0
  50. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/core/request.md +0 -0
  51. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/core/versioning.md +0 -0
  52. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/album.md +0 -0
  53. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/base.md +0 -0
  54. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/comment.md +0 -0
  55. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/login.md +0 -0
  56. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/lyric.md +0 -0
  57. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/mv.md +0 -0
  58. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/recommend.md +0 -0
  59. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/request.md +0 -0
  60. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/search.md +0 -0
  61. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/singer.md +0 -0
  62. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/song.md +0 -0
  63. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/songlist.md +0 -0
  64. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/top.md +0 -0
  65. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/model/user.md +0 -0
  66. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/album.md +0 -0
  67. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/comment.md +0 -0
  68. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/login.md +0 -0
  69. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/login_utils.md +0 -0
  70. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/lyric.md +0 -0
  71. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/mv.md +0 -0
  72. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/recommend.md +0 -0
  73. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/search.md +0 -0
  74. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/singer.md +0 -0
  75. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/song.md +0 -0
  76. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/songlist.md +0 -0
  77. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/top.md +0 -0
  78. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/reference/modules/user.md +0 -0
  79. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/tutorial/client.md +0 -0
  80. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/tutorial/credential.md +0 -0
  81. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/tutorial/pagination.md +0 -0
  82. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/docs/tutorial/start.md +0 -0
  83. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/examples/download_song.py +0 -0
  84. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/examples/phone_login.py +0 -0
  85. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/examples/qrcode_login.py +0 -0
  86. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/prek.toml +0 -0
  87. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/pyproject.toml +0 -0
  88. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/algorithms/__init__.py +0 -0
  89. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/algorithms/sign.py +0 -0
  90. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/algorithms/tripledes.py +0 -0
  91. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/client.py +0 -0
  92. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/pagination.py +0 -0
  93. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/core/request.py +0 -0
  94. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/__init__.py +0 -0
  95. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/album.py +0 -0
  96. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/comment.py +0 -0
  97. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/login.py +0 -0
  98. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/lyric.py +0 -0
  99. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/mv.py +0 -0
  100. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/recommend.py +0 -0
  101. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/search.py +0 -0
  102. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/singer.py +0 -0
  103. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/song.py +0 -0
  104. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/songlist.py +0 -0
  105. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/top.py +0 -0
  106. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/models/user.py +0 -0
  107. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/__init__.py +0 -0
  108. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/_base.py +0 -0
  109. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/album.py +0 -0
  110. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/comment.py +0 -0
  111. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/lyric.py +0 -0
  112. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/mv.py +0 -0
  113. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/songlist.py +0 -0
  114. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/top.py +0 -0
  115. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/modules/user.py +0 -0
  116. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/utils/__init__.py +0 -0
  117. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/utils/device.py +0 -0
  118. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/utils/mqtt.py +0 -0
  119. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/qqmusic_api/utils/qimei.py +0 -0
  120. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/scripts/ag-1.py +0 -0
  121. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/conftest.py +0 -0
  122. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_album.py +0 -0
  123. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_comment.py +0 -0
  124. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_lyric.py +0 -0
  125. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_mv.py +0 -0
  126. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_songlist.py +0 -0
  127. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_top.py +0 -0
  128. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/tests/test_user.py +0 -0
  129. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/uv.lock +0 -0
  130. {qqmusic_api_python-0.5.2 → qqmusic_api_python-0.5.3}/zensical.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qqmusic-api-python
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: QQ音乐API封装库
5
5
  Project-URL: documentation, https://l-1124.github.io/QQMusicApi/
6
6
  Project-URL: homepage, https://l-1124.github.io/QQMusicApi/
@@ -1,5 +1,14 @@
1
1
 
2
- ## What's Changed
2
+ ## [[0.5.2](https://github.com/L-1124/QQMusicApi/compare/v0.5.1..v0.5.2)] - 2026-04-18
3
+
4
+ ### Bug 修复
5
+
6
+ * `UserFavSonglistResponse`jsonpath错误 ([2c54ebb](https://github.com/L-1124/QQMusicApi/commit/2c54ebb8d21a8ba02bb5b865a7ff0aa9aadcbc0d)) by [@L-1124](https://github.com/L-1124)
7
+ * `GetAlbumSongResponse`上游返回单个歌曲信息报错 ([adbdc40](https://github.com/L-1124/QQMusicApi/commit/adbdc4069f964125ca2228d36014f64893f3e69a)) by [@L-1124](https://github.com/L-1124)
8
+
9
+ ### 功能更新
10
+
11
+ * **(core)** 为 ResponsePager 添加 next 与 has_more ([0a63a26](https://github.com/L-1124/QQMusicApi/commit/0a63a264433bf37b5854064aa8ef95edba97adab)) by [@L-1124](https://github.com/L-1124)
3
12
 
4
13
  ### 文档更新
5
14
 
@@ -8,6 +17,7 @@
8
17
  ### 贡献者
9
18
 
10
19
  * @L-1124
20
+ * @renovate[bot] [#240](https://github.com/L-1124/QQMusicApi/pull/240)
11
21
  * @github-actions[bot]
12
22
 
13
23
  ## [[0.5.1](https://github.com/L-1124/QQMusicApi/compare/v0.5.0..v0.5.1)] - 2026-04-13
@@ -0,0 +1,52 @@
1
+ """QQMusic API 公开入口."""
2
+
3
+ from .core.client import Client
4
+ from .core.exceptions import (
5
+ ApiError,
6
+ HTTPError,
7
+ LoginAccountBannedError,
8
+ LoginAccountRestrictedError,
9
+ LoginApiError,
10
+ LoginAuthCodeError,
11
+ LoginAuthFailedError,
12
+ LoginBindRequiredError,
13
+ LoginCredentialExpiredError,
14
+ LoginDeviceLimitError,
15
+ LoginError,
16
+ LoginExpiredError,
17
+ LoginRateLimitedError,
18
+ LoginSecurityRequiredError,
19
+ NetworkError,
20
+ NotLoginError,
21
+ RatelimitedError,
22
+ SignInvalidError,
23
+ )
24
+ from .core.versioning import Platform
25
+ from .models.request import Credential
26
+
27
+ __version__ = "0.5.3"
28
+
29
+ __all__ = [
30
+ "ApiError",
31
+ "Client",
32
+ "Credential",
33
+ "HTTPError",
34
+ "LoginAccountBannedError",
35
+ "LoginAccountRestrictedError",
36
+ "LoginApiError",
37
+ "LoginAuthCodeError",
38
+ "LoginAuthFailedError",
39
+ "LoginBindRequiredError",
40
+ "LoginCredentialExpiredError",
41
+ "LoginDeviceLimitError",
42
+ "LoginError",
43
+ "LoginExpiredError",
44
+ "LoginRateLimitedError",
45
+ "LoginSecurityRequiredError",
46
+ "NetworkError",
47
+ "NotLoginError",
48
+ "Platform",
49
+ "RatelimitedError",
50
+ "SignInvalidError",
51
+ "__version__",
52
+ ]
@@ -7,8 +7,18 @@ from .exceptions import (
7
7
  BaseError,
8
8
  CredentialError,
9
9
  HTTPError,
10
+ LoginAccountBannedError,
11
+ LoginAccountRestrictedError,
12
+ LoginApiError,
13
+ LoginAuthCodeError,
14
+ LoginAuthFailedError,
15
+ LoginBindRequiredError,
16
+ LoginCredentialExpiredError,
17
+ LoginDeviceLimitError,
10
18
  LoginError,
11
19
  LoginExpiredError,
20
+ LoginRateLimitedError,
21
+ LoginSecurityRequiredError,
12
22
  NetworkError,
13
23
  NotLoginError,
14
24
  RatelimitedError,
@@ -29,8 +39,18 @@ __all__ = [
29
39
  "ClientConfig",
30
40
  "CredentialError",
31
41
  "HTTPError",
42
+ "LoginAccountBannedError",
43
+ "LoginAccountRestrictedError",
44
+ "LoginApiError",
45
+ "LoginAuthCodeError",
46
+ "LoginAuthFailedError",
47
+ "LoginBindRequiredError",
48
+ "LoginCredentialExpiredError",
49
+ "LoginDeviceLimitError",
32
50
  "LoginError",
33
51
  "LoginExpiredError",
52
+ "LoginRateLimitedError",
53
+ "LoginSecurityRequiredError",
34
54
  "NetworkError",
35
55
  "NotLoginError",
36
56
  "Platform",
@@ -8,8 +8,18 @@ __all__ = [
8
8
  "BaseError",
9
9
  "CredentialError",
10
10
  "HTTPError",
11
+ "LoginAccountBannedError",
12
+ "LoginAccountRestrictedError",
13
+ "LoginApiError",
14
+ "LoginAuthCodeError",
15
+ "LoginAuthFailedError",
16
+ "LoginBindRequiredError",
17
+ "LoginCredentialExpiredError",
18
+ "LoginDeviceLimitError",
11
19
  "LoginError",
12
20
  "LoginExpiredError",
21
+ "LoginRateLimitedError",
22
+ "LoginSecurityRequiredError",
13
23
  "NetworkError",
14
24
  "NotLoginError",
15
25
  "RatelimitedError",
@@ -166,8 +176,74 @@ class LoginError(BaseError):
166
176
  通常在扫码登录流程中断、超时或网络失败时抛出。
167
177
  """
168
178
 
169
- def __init__(self, message: str = "登录失败", cause: BaseException | None = None):
170
- super().__init__(message, cause=cause)
179
+ def __init__(
180
+ self,
181
+ message: str = "登录失败",
182
+ cause: BaseException | None = None,
183
+ context: dict[str, Any] | None = None,
184
+ ):
185
+ super().__init__(message, context=context, cause=cause)
186
+
187
+
188
+ class LoginApiError(LoginError):
189
+ """登录接口返回的业务错误基类."""
190
+
191
+ def __init__(
192
+ self,
193
+ message: str,
194
+ *,
195
+ code: int,
196
+ data: dict[str, Any] | None = None,
197
+ action_url: str = "",
198
+ cause: BaseException | None = None,
199
+ context: dict[str, Any] | None = None,
200
+ ):
201
+ merged_context = dict(context or {})
202
+ merged_context["code"] = code
203
+ if data:
204
+ merged_context["data"] = data
205
+ if action_url:
206
+ merged_context["url"] = action_url
207
+ super().__init__(message, context=merged_context, cause=cause)
208
+ self.code = code
209
+ self.data = data or {}
210
+ self.action_url = action_url
211
+
212
+
213
+ class LoginCredentialExpiredError(LoginApiError):
214
+ """登录刷新凭证不可用或登录态过期."""
215
+
216
+
217
+ class LoginAuthFailedError(LoginApiError):
218
+ """登录认证失败或上游票据不可用."""
219
+
220
+
221
+ class LoginSecurityRequiredError(LoginApiError):
222
+ """登录需要安全验证或人工交互."""
223
+
224
+
225
+ class LoginAccountRestrictedError(LoginApiError):
226
+ """账号登录受限."""
227
+
228
+
229
+ class LoginAccountBannedError(LoginApiError):
230
+ """账号被封禁导致无法登录."""
231
+
232
+
233
+ class LoginBindRequiredError(LoginApiError):
234
+ """登录需要完成账号绑定或设备确认."""
235
+
236
+
237
+ class LoginDeviceLimitError(LoginApiError):
238
+ """登录设备数量超限."""
239
+
240
+
241
+ class LoginAuthCodeError(LoginApiError):
242
+ """手机验证码错误或已失效."""
243
+
244
+
245
+ class LoginRateLimitedError(LoginApiError):
246
+ """登录操作触发频率限制."""
171
247
 
172
248
 
173
249
  class RequestGroupResultMissingError(ApiError):
@@ -30,7 +30,7 @@ class VersionProfile:
30
30
  qimei_sdk_version: str | None = None
31
31
 
32
32
 
33
- @dataclass(frozen=True, slots=True)
33
+ @dataclass(slots=True)
34
34
  class VersionPolicy:
35
35
  """请求版本策略."""
36
36
 
@@ -19,7 +19,7 @@ _PHOTO_NEW_SIZE_SEGMENTS: Final[dict[int, str]] = {
19
19
 
20
20
 
21
21
  def _normalize_cover_size(size: CoverSize) -> str:
22
- """返回 APK `photo_new` 模板使用的尺寸片段."""
22
+ """返回 `photo_new` 模板使用的尺寸片段."""
23
23
  try:
24
24
  return _PHOTO_NEW_SIZE_SEGMENTS[size]
25
25
  except KeyError as exc:
@@ -27,7 +27,7 @@ def _normalize_cover_size(size: CoverSize) -> str:
27
27
 
28
28
 
29
29
  def _build_photo_new_cover_url(kind: str, mid: str, size: CoverSize) -> str:
30
- """按 APK `photo_new` 规则拼接封面链接."""
30
+ """按 `photo_new` 规则拼接封面链接."""
31
31
  normalized_mid = mid.strip()
32
32
  if not normalized_mid:
33
33
  return ""
@@ -200,7 +200,7 @@ class SongList(Response):
200
200
  picurl: str = Field(default="", validation_alias=AliasChoices("picurl", "cover", "logo", "picUrl"))
201
201
  desc: str = Field(default="", validation_alias=AliasChoices("desc", "description"))
202
202
  songnum: int = Field(default=0, validation_alias=AliasChoices("songnum", "songNum", "song_cnt"))
203
- listennum: int | str = Field(default=0, validation_alias=AliasChoices("listennum", "playCnt", "play_cnt"))
203
+ listennum: int = Field(default=0, validation_alias=AliasChoices("listennum", "playCnt", "play_cnt"))
204
204
 
205
205
 
206
206
  class Song(Response):
@@ -53,6 +53,10 @@ class CommonParams(BaseModel):
53
53
  format: str | None = Field(default=None)
54
54
  in_charset: str | None = Field(default=None, alias="inCharset")
55
55
  out_charset: str | None = Field(default=None, alias="outCharset")
56
+ # [Web] 通知开关
57
+ notice: int | None = Field(default=None)
58
+ # [Web] 新设备码开关
59
+ need_new_code: int | None = Field(default=None)
56
60
 
57
61
 
58
62
  class Credential(BaseModel):
@@ -95,7 +99,7 @@ class Credential(BaseModel):
95
99
  if not isinstance(data, dict):
96
100
  return data
97
101
 
98
- if data.get("loginType") or data.get("login_type"):
102
+ if "loginType" in data or "login_type" in data:
99
103
  return data
100
104
 
101
105
  musickey = data.get("musickey", "")