qqmusic-api-python 0.5.0__tar.gz → 0.5.2__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 (133) hide show
  1. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/workflows/checking.yaml +5 -1
  2. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/workflows/docs.yml +5 -5
  3. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/workflows/release.yml +7 -3
  4. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.gitignore +3 -0
  5. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/PKG-INFO +2 -4
  6. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/README.md +0 -3
  7. qqmusic_api_python-0.5.2/docs/coding.md +381 -0
  8. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/index.md +2 -5
  9. qqmusic_api_python-0.5.2/docs/reference/core/pagination.md +3 -0
  10. qqmusic_api_python-0.5.2/docs/reference/modules/login_utils.md +3 -0
  11. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/release-notes.md +290 -244
  12. qqmusic_api_python-0.5.2/docs/tutorial/pagination.md +112 -0
  13. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/examples/download_song.py +2 -1
  14. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/prek.toml +2 -2
  15. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/pyproject.toml +5 -4
  16. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/__init__.py +1 -1
  17. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/core/client.py +79 -21
  18. qqmusic_api_python-0.5.2/qqmusic_api/core/pagination.py +496 -0
  19. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/core/request.py +46 -1
  20. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/album.py +7 -1
  21. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/base.py +1 -1
  22. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/user.py +1 -1
  23. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/_base.py +137 -17
  24. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/album.py +5 -0
  25. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/comment.py +42 -0
  26. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/login.py +67 -24
  27. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/login_utils.py +5 -10
  28. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/recommend.py +55 -5
  29. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/search.py +24 -1
  30. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/singer.py +41 -0
  31. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/song.py +97 -46
  32. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/songlist.py +9 -0
  33. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/top.py +8 -0
  34. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/user.py +60 -6
  35. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/utils/__init__.py +0 -2
  36. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/utils/mqtt.py +138 -186
  37. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/conftest.py +3 -3
  38. qqmusic_api_python-0.5.2/tests/test_album.py +29 -0
  39. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_comment.py +27 -0
  40. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_login.py +23 -0
  41. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_search.py +17 -0
  42. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_song.py +24 -8
  43. qqmusic_api_python-0.5.2/uv.lock +1502 -0
  44. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/zensical.toml +7 -0
  45. qqmusic_api_python-0.5.0/docs/coding.md +0 -237
  46. qqmusic_api_python-0.5.0/qqmusic_api/utils/retry.py +0 -116
  47. qqmusic_api_python-0.5.0/tests/test_album.py +0 -54
  48. qqmusic_api_python-0.5.0/uv.lock +0 -1487
  49. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.agents/skills/pydantic/SKILL.md +0 -0
  50. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.agents/skills/python-standards/SKILL.md +0 -0
  51. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.agents/skills/tarsio/SKILL.md +0 -0
  52. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.agents/skills/tarsio/references/api-reference.md +0 -0
  53. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.agents/skills/uv-package-manager/SKILL.md +0 -0
  54. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  55. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  56. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  57. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  58. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/renovate.json +0 -0
  59. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.github/workflows/testing.yml +0 -0
  60. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/.markdownlint-cli2.yaml +0 -0
  61. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/AGENTS.md +0 -0
  62. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/LICENSE +0 -0
  63. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/assets/qq-music.svg +0 -0
  64. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/cliff.toml +0 -0
  65. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/contributing.md +0 -0
  66. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/core/client.md +0 -0
  67. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/core/exception.md +0 -0
  68. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/core/request.md +0 -0
  69. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/core/versioning.md +0 -0
  70. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/album.md +0 -0
  71. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/base.md +0 -0
  72. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/comment.md +0 -0
  73. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/login.md +0 -0
  74. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/lyric.md +0 -0
  75. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/mv.md +0 -0
  76. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/recommend.md +0 -0
  77. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/request.md +0 -0
  78. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/search.md +0 -0
  79. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/singer.md +0 -0
  80. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/song.md +0 -0
  81. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/songlist.md +0 -0
  82. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/top.md +0 -0
  83. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/model/user.md +0 -0
  84. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/album.md +0 -0
  85. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/comment.md +0 -0
  86. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/login.md +0 -0
  87. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/lyric.md +0 -0
  88. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/mv.md +0 -0
  89. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/recommend.md +0 -0
  90. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/search.md +0 -0
  91. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/singer.md +0 -0
  92. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/song.md +0 -0
  93. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/songlist.md +0 -0
  94. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/top.md +0 -0
  95. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/reference/modules/user.md +0 -0
  96. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/tutorial/client.md +0 -0
  97. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/tutorial/credential.md +0 -0
  98. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/docs/tutorial/start.md +0 -0
  99. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/examples/phone_login.py +0 -0
  100. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/examples/qrcode_login.py +0 -0
  101. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/algorithms/__init__.py +0 -0
  102. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/algorithms/sign.py +0 -0
  103. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/algorithms/tripledes.py +0 -0
  104. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/core/__init__.py +0 -0
  105. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/core/exceptions.py +0 -0
  106. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/core/versioning.py +0 -0
  107. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/__init__.py +0 -0
  108. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/comment.py +0 -0
  109. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/login.py +0 -0
  110. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/lyric.py +0 -0
  111. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/mv.py +0 -0
  112. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/recommend.py +0 -0
  113. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/request.py +0 -0
  114. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/search.py +0 -0
  115. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/singer.py +0 -0
  116. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/song.py +0 -0
  117. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/songlist.py +0 -0
  118. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/models/top.py +0 -0
  119. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/__init__.py +0 -0
  120. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/lyric.py +0 -0
  121. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/modules/mv.py +0 -0
  122. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/utils/common.py +0 -0
  123. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/utils/device.py +0 -0
  124. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/qqmusic_api/utils/qimei.py +0 -0
  125. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/scripts/ag-1.py +0 -0
  126. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_login_utils.py +0 -0
  127. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_lyric.py +0 -0
  128. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_mv.py +0 -0
  129. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_recommend.py +0 -0
  130. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_singer.py +0 -0
  131. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_songlist.py +0 -0
  132. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_top.py +0 -0
  133. {qqmusic_api_python-0.5.0 → qqmusic_api_python-0.5.2}/tests/test_user.py +0 -0
@@ -8,7 +8,11 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
10
  - uses: actions/checkout@v6
11
- - name: Run prek pre-commit hooks
11
+ - name: Setup uv
12
+ uses: astral-sh/setup-uv@v5
13
+ - name: Install dependencies
14
+ run: uv sync
15
+ - name: Run prek
12
16
  uses: j178/prek-action@v2
13
17
  with:
14
18
  extra-args: --all-files --hook-stage pre-push
@@ -22,10 +22,10 @@ jobs:
22
22
  uses: actions/checkout@v6
23
23
  with:
24
24
  fetch-depth: 0
25
- token: ${{ secrets.GITHUB_TOKEN }}
25
+ token: ${{ secrets.GH_TOKEN }}
26
26
  - name: Setup Pages
27
27
  id: pages
28
- uses: actions/configure-pages@v5
28
+ uses: actions/configure-pages@v6
29
29
  - name: Install uv
30
30
  uses: astral-sh/setup-uv@v7
31
31
  with:
@@ -49,11 +49,11 @@ jobs:
49
49
  set +e
50
50
  git add docs/release-notes.md
51
51
  git commit -m "🧹 chore(release-notes): 更新 release notes"
52
- git push https://${{ secrets.GH_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git main
52
+ git push origin main
53
53
  - name: Build with zensical
54
54
  run: uv run zensical build
55
55
  - name: Upload artifact
56
- uses: actions/upload-pages-artifact@v4
56
+ uses: actions/upload-pages-artifact@v5
57
57
  with:
58
58
  path: ./site
59
59
 
@@ -66,4 +66,4 @@ jobs:
66
66
  steps:
67
67
  - name: Deploy to GitHub Pages
68
68
  id: deployment
69
- uses: actions/deploy-pages@v4
69
+ uses: actions/deploy-pages@v5
@@ -18,7 +18,7 @@ jobs:
18
18
  - uses: actions/checkout@v6
19
19
  with:
20
20
  fetch-depth: 0
21
- token: ${{ secrets.GITHUB_TOKEN }}
21
+ token: ${{ secrets.GH_TOKEN }}
22
22
  - name: Generate release body
23
23
  id: git-cliff
24
24
  uses: orhun/git-cliff-action@v4
@@ -33,6 +33,8 @@ jobs:
33
33
  id-token: write
34
34
  steps:
35
35
  - uses: actions/checkout@v6
36
+ with:
37
+ token: ${{ secrets.GH_TOKEN }}
36
38
  - name: Install uv
37
39
  uses: astral-sh/setup-uv@v7
38
40
  with:
@@ -50,9 +52,11 @@ jobs:
50
52
  steps:
51
53
  - name: Checkout
52
54
  uses: actions/checkout@v6
55
+ with:
56
+ token: ${{ secrets.GH_TOKEN }}
53
57
  - name: Release
54
- uses: softprops/action-gh-release@v2
58
+ uses: softprops/action-gh-release@v3
55
59
  with:
56
60
  body: ${{ needs.generate-release-body.outputs.release_body }}
57
61
  tag_name: ${{ github.ref_name }}
58
- token: ${{ secrets.GITHUB_TOKEN }}
62
+ token: ${{ secrets.GH_TOKEN }}
@@ -13,6 +13,7 @@ device.json
13
13
 
14
14
  # AI
15
15
  .ccb/
16
+ docs/superpowers/
16
17
 
17
18
  # Distribution / packaging
18
19
  .Python
@@ -184,3 +185,5 @@ pyrightconfig.json
184
185
  # End of https://www.toptal.com/developers/gitignore/api/python
185
186
 
186
187
  .sisyphus/
188
+
189
+ .worktrees/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qqmusic-api-python
3
- Version: 0.5.0
3
+ Version: 0.5.2
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/
@@ -30,6 +30,7 @@ Classifier: Typing :: Typed
30
30
  Requires-Python: >=3.10
31
31
  Requires-Dist: anyio>=4.12.1
32
32
  Requires-Dist: cryptography>=46.0.3
33
+ Requires-Dist: httpx-retries>=0.4.6
33
34
  Requires-Dist: httpx[http2]>=0.27.0
34
35
  Requires-Dist: jsonpath-ng>=1.8.0
35
36
  Requires-Dist: orjson>=3.10.15
@@ -61,7 +62,6 @@ Description-Content-Type: text/markdown
61
62
  ---
62
63
 
63
64
  > [!IMPORTANT]
64
- > 请在使用前阅读仓库中的 `LICENSE` 文件, **并遵守相关平台条款与版权法律**。
65
65
  > **音乐平台不易, 请尊重版权, 支持正版**。
66
66
 
67
67
  ---
@@ -84,8 +84,6 @@ Description-Content-Type: text/markdown
84
84
 
85
85
  本项目当前采用 **[GNU General Public License v3.0 or later](https://github.com/l-1124/QQMusicApi/blob/main/LICENSE)**。
86
86
 
87
- `v0.5.0` 及后续版本均按 GPL 条款分发, 具体内容以仓库中的 `LICENSE` 文件为准。
88
-
89
87
  本项目仅用于对技术可行性的探索及研究,请勿将其用于任何商业用途或侵犯版权的行为。
90
88
 
91
89
  ## ⚠️ 免责声明
@@ -20,7 +20,6 @@
20
20
  ---
21
21
 
22
22
  > [!IMPORTANT]
23
- > 请在使用前阅读仓库中的 `LICENSE` 文件, **并遵守相关平台条款与版权法律**。
24
23
  > **音乐平台不易, 请尊重版权, 支持正版**。
25
24
 
26
25
  ---
@@ -43,8 +42,6 @@
43
42
 
44
43
  本项目当前采用 **[GNU General Public License v3.0 or later](https://github.com/l-1124/QQMusicApi/blob/main/LICENSE)**。
45
44
 
46
- `v0.5.0` 及后续版本均按 GPL 条款分发, 具体内容以仓库中的 `LICENSE` 文件为准。
47
-
48
45
  本项目仅用于对技术可行性的探索及研究,请勿将其用于任何商业用途或侵犯版权的行为。
49
46
 
50
47
  ## ⚠️ 免责声明
@@ -0,0 +1,381 @@
1
+ # API 编写指南
2
+
3
+ `qqmusic_api` 采用 `Client + ApiModule + Request` 的结构:
4
+
5
+ * `Client` 负责网络发送、平台信息、凭证和批量调度。
6
+ * `ApiModule` 负责声明接口参数,并返回可 `await` 的 `Request`。
7
+ * `RequestGroup` 用于批量执行多个 `Request`。
8
+
9
+ ## 调用流程图
10
+
11
+ ### 单请求
12
+
13
+ ```text
14
+ 模块方法
15
+ -> self._build_request(...)
16
+ -> Request
17
+ -> await request
18
+ -> Client.execute(request)
19
+ -> 根据 request.is_jce 分发:
20
+ -> Client.request_jce(...)
21
+ -> 或 Client.request_musicu(...)
22
+ -> Client._build_result(...)
23
+ -> 返回原始 dict / TarsDict 或 Pydantic 模型
24
+ ```
25
+
26
+ ### 批量请求
27
+
28
+ ```text
29
+ 多个模块方法
30
+ -> 多个 Request
31
+ -> Client.request_group()
32
+ -> RequestGroup.add(...) / extend(...)
33
+ -> 按 is_jce / platform / comm / credential 分组
34
+ -> 按 batch_size 分批
35
+ -> 并发发送批次
36
+ -> 两种消费方式:
37
+ -> execute_iter():
38
+ 返回无序流式 RequestGroupResult
39
+ 字段包括 index / success / data / error
40
+ -> execute():
41
+ 返回按添加顺序回填的 list[Any | Exception]
42
+ ```
43
+
44
+ ## 编写新的 API
45
+
46
+ API 按功能拆分在 `qqmusic_api/modules/` 下,添加新的 API 只需在对应的模块中添加请求方法即可。
47
+
48
+ ```python
49
+ from typing import Any
50
+
51
+
52
+ class SongApi(ApiModule):
53
+ """歌曲相关 API 模块."""
54
+
55
+ ...
56
+
57
+ def get_detail(self, song_id: int):
58
+ """获取歌曲详情."""
59
+ return self._build_request(
60
+ module="music.songDetail",
61
+ method="GetDetail",
62
+ param={"songid": song_id},
63
+ )
64
+
65
+ class SearchApi(ApiModule):
66
+ """搜索相关 API 模块."""
67
+
68
+ ...
69
+
70
+ async def quick_search(self, keyword: str) -> dict[str, Any]:
71
+ """快速搜索 (直接返回解析后的 JSON 数据).
72
+
73
+ Args:
74
+ keyword: 关键词.
75
+
76
+ Returns:
77
+ dict[str, Any]: 搜索结果字典.
78
+ """
79
+ resp = await self._client.fetch(
80
+ "GET",
81
+ "https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg",
82
+ params={"key": keyword},
83
+ )
84
+ resp.raise_for_status()
85
+ return resp.json()["data"]
86
+ ```
87
+
88
+ ### `Credential` 和 `Platform` 参数
89
+
90
+ `_build_request` 可以接受 `credential` 和 `platform` 参数,默认会继承当前 `Client` 的设置。
91
+ 通常情况下,模块方法不需要暴露这些参数,除非需要支持覆盖凭证或平台。
92
+ 不同的 `Platform` 会影响接口返回的数据内容和格式,是否需要登录。
93
+ 部分接口的 `Platform` 是固定的。
94
+
95
+ ### 响应模型 `response_model`
96
+
97
+ 每个响应模型都应继承 `.models.request.Response`。
98
+ 可以通过 `Field(json_schema_extra={"jsonpath": ...})` 声明字段的 JSONPath 映射路径,自动从嵌套响应中提取数据,以减少嵌套层级。
99
+
100
+ ```py
101
+ from pydantic import Field
102
+
103
+ from .request import Response
104
+
105
+
106
+ class SonglistMeta(Response):
107
+ """歌单元数据示例."""
108
+
109
+ id: int = Field(json_schema_extra={"jsonpath": "$.result.tid"})
110
+ dirid: int = Field(json_schema_extra={"jsonpath": "$.result.dirId"})
111
+ name: str = Field(json_schema_extra={"jsonpath": "$.result.dirName"})
112
+
113
+
114
+ class MyApi(ApiModule):
115
+ """带 JSONPath 响应模型的示例模块."""
116
+
117
+ def get_songlist_meta(self, disstid: int):
118
+ """获取歌单元数据."""
119
+ return self._build_request(
120
+ module="music.srfDissInfo.aiDissInfo",
121
+ method="uniform_get_Dissinfo",
122
+ param={"disstid": disstid},
123
+ response_model=SonglistMeta,
124
+ )
125
+ ```
126
+
127
+ ## 声明连续翻页与换一批能力
128
+
129
+ 当一个接口支持连续翻页时,应在模块方法中通过 `_build_request(..., pager_meta=...)` 显式声明连续翻页能力。声明后,该方法返回的请求对象才会暴露 `.paginate()`。
130
+
131
+ ```python
132
+ from ..core.pagination import OffsetStrategy, PagerMeta, ResponseAdapter
133
+
134
+
135
+ class SonglistApi(ApiModule):
136
+ """歌单相关 API."""
137
+
138
+ def get_detail(self, songlist_id: int, num: int = 10, page: int = 1):
139
+ """获取歌单详情."""
140
+ return self._build_request(
141
+ module="music.srfDissInfo.DissInfo",
142
+ method="CgiGetDiss",
143
+ param={
144
+ "disstid": songlist_id,
145
+ "song_begin": num * (page - 1),
146
+ "song_num": num,
147
+ },
148
+ response_model=GetSonglistDetailResponse,
149
+ pager_meta=PagerMeta(
150
+ strategy=OffsetStrategy(offset_key="song_begin", page_size_key="song_num"),
151
+ adapter=ResponseAdapter(
152
+ has_more_flag="hasmore",
153
+ total="total",
154
+ count=lambda response: len(response.songs),
155
+ ),
156
+ ),
157
+ )
158
+ ```
159
+
160
+ 当一个接口支持“换一批”时,应通过 `_build_request(..., refresh_meta=...)` 声明换一批能力。声明后,该方法返回的请求对象会暴露 `.refresh()`,并返回 `ResponseRefresher`。
161
+
162
+ ```python
163
+ from ..core.pagination import BatchRefreshStrategy, RefreshMeta, ResponseAdapter
164
+
165
+
166
+ class SongApi(ApiModule):
167
+ """歌曲相关 API."""
168
+
169
+ def get_related_mv(self, songid: int, last_mvid: str | None = None):
170
+ """获取歌曲相关 MV."""
171
+ return self._build_request(
172
+ module="MvService.MvInfoProServer",
173
+ method="GetSongRelatedMv",
174
+ param={"songid": str(songid), "songtype": 1, "lastmvid": last_mvid or 0},
175
+ response_model=GetRelatedMvResponse,
176
+ refresh_meta=RefreshMeta(
177
+ strategy=BatchRefreshStrategy(refresh_key="lastmvid"),
178
+ adapter=ResponseAdapter(
179
+ has_more_flag="has_more",
180
+ cursor=lambda response: response.mv[-1].id if response.mv else None,
181
+ ),
182
+ ),
183
+ )
184
+ ```
185
+
186
+ ### 内置连续翻页策略
187
+
188
+ #### `PageStrategy`
189
+
190
+ 适用于请求参数里有明确页码字段,且下一页只需要把该字段加一的接口。
191
+
192
+ ```python
193
+ from ..core.pagination import PageStrategy, PagerMeta, ResponseAdapter
194
+
195
+ pager_meta = PagerMeta(
196
+ strategy=PageStrategy(page_key="PageNum", page_size=num, start_page=page - 1),
197
+ adapter=ResponseAdapter(has_more_flag="has_more"),
198
+ )
199
+ ```
200
+
201
+ #### `OffsetStrategy`
202
+
203
+ 适用于请求参数里有 `offset`、`begin`、`song_begin` 这类偏移量字段的接口。
204
+
205
+ ```python
206
+ from ..core.pagination import OffsetStrategy, PagerMeta, ResponseAdapter
207
+
208
+ pager_meta = PagerMeta(
209
+ strategy=OffsetStrategy(offset_key="song_begin", page_size_key="song_num"),
210
+ adapter=ResponseAdapter(
211
+ has_more_flag="hasmore",
212
+ total="total",
213
+ count=lambda response: len(response.songs),
214
+ ),
215
+ )
216
+ ```
217
+
218
+ 如果上游尾页可能返回少量结果或重叠窗口,应优先提供 `count`。
219
+
220
+ #### `CursorStrategy`
221
+
222
+ 适用于响应里能直接拿到下一页游标,并且下一次请求只需要回写这一个字段的接口。
223
+
224
+ ```python
225
+ from ..core.pagination import CursorStrategy, PagerMeta, ResponseAdapter
226
+
227
+ pager_meta = PagerMeta(
228
+ strategy=CursorStrategy(cursor_key="lastmvid"),
229
+ adapter=ResponseAdapter(
230
+ has_more_flag="has_more",
231
+ cursor=lambda response: response.mv[-1].id if response.mv else None,
232
+ ),
233
+ )
234
+ ```
235
+
236
+ #### `MultiFieldContinuationStrategy`
237
+
238
+ 适用于下一页请求需要同时更新多个字段的接口,例如页码加额外上下文。
239
+
240
+ ```python
241
+ from ..core.pagination import MultiFieldContinuationStrategy, PagerMeta, ResponseAdapter
242
+
243
+ pager_meta = PagerMeta(
244
+ strategy=MultiFieldContinuationStrategy(
245
+ lambda params, response, adapter: {
246
+ **params,
247
+ "page_id": response.nextpage,
248
+ "page_start": adapter.get_cursor(response),
249
+ },
250
+ context_name="general_search",
251
+ ),
252
+ adapter=ResponseAdapter(
253
+ has_more_flag=lambda response: response.nextpage != -1,
254
+ cursor="nextpage_start",
255
+ ),
256
+ )
257
+ ```
258
+
259
+ #### `BatchRefreshStrategy`
260
+
261
+ 适用于“换一批”接口。它不会把结果视为同一个连续窗口,而是根据上一批响应提取新的刷新参数,再请求下一批候选结果。
262
+
263
+ ```python
264
+ from ..core.pagination import BatchRefreshStrategy, RefreshMeta, ResponseAdapter
265
+
266
+ refresh_meta = RefreshMeta(
267
+ strategy=BatchRefreshStrategy(refresh_key="vecPlaylist"),
268
+ adapter=ResponseAdapter(
269
+ has_more_flag="has_more",
270
+ cursor=lambda response: [playlist.id for playlist in response.songlist] if response.songlist else None,
271
+ ),
272
+ )
273
+ ```
274
+
275
+ ### `ResponseAdapter`
276
+
277
+ `ResponseAdapter` 用于从响应中提取分页决策所需信息。常见字段包括:
278
+
279
+ * `has_more_flag`: 显式是否还有下一页
280
+ * `total`: 总量
281
+ * `cursor`: 下一页游标
282
+ * `count`: 当前页实际返回数量
283
+
284
+ 对偏移量分页,优先提供 `count`,因为上游尾页可能返回少于请求数量的结果,甚至返回重叠窗口;仅依赖请求页大小会导致尾页重复获取。
285
+
286
+ `ResponseAdapter` 的每个字段都用于告诉分页器“应该从哪里读取分页信号”。常见写法如下。
287
+
288
+ #### 只依赖显式 `has_more`
289
+
290
+ ```python
291
+ adapter = ResponseAdapter(has_more_flag="has_more")
292
+ ```
293
+
294
+ #### 使用总量判断是否还有下一页
295
+
296
+ ```python
297
+ adapter = ResponseAdapter(total="total_num")
298
+ ```
299
+
300
+ #### 从响应中提取下一页游标
301
+
302
+ ```python
303
+ adapter = ResponseAdapter(cursor="nextpage_start")
304
+ ```
305
+
306
+ 也可以在字段需要转换时使用函数:
307
+
308
+ ```python
309
+ adapter = ResponseAdapter(
310
+ cursor=lambda response: response.mv[-1].id if response.mv else None,
311
+ )
312
+ ```
313
+
314
+ #### 为偏移量分页提供当前页实际数量
315
+
316
+ ```python
317
+ adapter = ResponseAdapter(
318
+ has_more_flag="hasmore",
319
+ total="total",
320
+ count=lambda response: len(response.songs),
321
+ )
322
+ ```
323
+
324
+ 如果接口需要多个信号,也可以组合使用:
325
+
326
+ ```python
327
+ adapter = ResponseAdapter(
328
+ has_more_flag="has_more",
329
+ total="total",
330
+ cursor="nextpage_start",
331
+ count=lambda response: len(response.items),
332
+ )
333
+ ```
334
+
335
+ ## 在 `Client` 中注册模块
336
+
337
+ 新增模块后,在 `Client` 中注册该模块属性:
338
+
339
+ ```python
340
+ class Client:
341
+ @property
342
+ def my_api(self) -> "MyApi":
343
+ from ..modules.my_api import MyApi
344
+
345
+ return MyApi(self)
346
+ ```
347
+
348
+ ## 批量请求 `RequestGroup`
349
+
350
+ 使用 `Client.request_group()` 可以批量提交请求。`RequestGroup` 会自动按 `platform`、`credential`、`comm` 和 `is_jce` 分组,并按 `batch_size` 分批发送。
351
+
352
+ `execute()` 会返回与添加顺序一致的完整结果列表:
353
+
354
+ ```python
355
+ from qqmusic_api import Client
356
+
357
+
358
+ async def batch_query(song_ids: list[int]):
359
+ async with Client() as client:
360
+ group = client.request_group()
361
+ for song_id in song_ids:
362
+ group.add(client.song.get_detail(song_id))
363
+
364
+ return await group.execute()
365
+ ```
366
+
367
+ `execute_iter()` 会按完成顺序流式返回 [`RequestGroupResult`][core.request.RequestGroupResult],不保证与添加顺序一致:
368
+
369
+ ```python
370
+ from qqmusic_api import Client
371
+
372
+
373
+ async def batch_query_stream(song_ids: list[int]):
374
+ async with Client() as client:
375
+ group = client.request_group(batch_size=1, max_inflight_batches=4)
376
+ for song_id in song_ids:
377
+ group.add(client.song.get_detail(song_id))
378
+
379
+ async for result in group.execute_iter():
380
+ print(result.index, result.module, result.method, result.success)
381
+ ```
@@ -27,7 +27,6 @@ hide: navigation
27
27
 
28
28
  !!! note 重要提示
29
29
 
30
- 请在使用前阅读仓库中的 `LICENSE` 文件, 并**遵守相关平台条款与版权法律**。
31
30
  **音乐平台不易, 请尊重版权, 支持正版**。
32
31
 
33
32
  ---
@@ -59,8 +58,8 @@ from qqmusic_api import Client
59
58
  async def main():
60
59
  """演示基础调用."""
61
60
  async with Client() as client:
62
- result = await client.search.search_by_type("周杰伦")
63
- print(f"搜索结果数量: {len(result.list)}")
61
+ result = await client.search.search_by_type(keyword="周杰伦", num=5)
62
+ print(f"单曲结果数量: {len(result.song)}")
64
63
 
65
64
 
66
65
  asyncio.run(main())
@@ -70,8 +69,6 @@ asyncio.run(main())
70
69
 
71
70
  本项目当前采用 **[GNU General Public License v3.0 or later](https://github.com/l-1124/QQMusicApi/blob/main/LICENSE)**。
72
71
 
73
- `v0.5.0` 及后续版本均按 GPL 条款分发, 具体内容以仓库中的 `LICENSE` 文件为准。
74
-
75
72
  本项目仅用于对技术可行性的探索及研究,请勿将其用于任何商业用途或侵犯版权的行为。
76
73
 
77
74
  ## ⚠️ 免责声明
@@ -0,0 +1,3 @@
1
+ # pagination
2
+
3
+ ::: core.pagination
@@ -0,0 +1,3 @@
1
+ # LoginUtils
2
+
3
+ ::: modules.login_utils