qqmusic-api-python 0.6.7__tar.gz → 0.6.9__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 (202) hide show
  1. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.dockerignore +0 -2
  2. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.gitignore +1 -0
  3. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/AGENTS.md +21 -0
  4. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/PKG-INFO +6 -1
  5. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/README.md +3 -0
  6. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/coding.md +6 -3
  7. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/release-notes.md +47 -1
  8. qqmusic_api_python-0.6.9/examples/upload_file.py +33 -0
  9. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/pyproject.toml +9 -0
  10. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/__init__.py +1 -1
  11. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/client.py +27 -8
  12. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/exceptions.py +8 -0
  13. qqmusic_api_python-0.6.9/qqmusic_api/models/album.py +121 -0
  14. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/base.py +5 -9
  15. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/comment.py +56 -56
  16. qqmusic_api_python-0.6.9/qqmusic_api/models/helper.py +177 -0
  17. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/login.py +14 -2
  18. qqmusic_api_python-0.6.9/qqmusic_api/models/lyric.py +34 -0
  19. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/mv.py +35 -3
  20. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/private_message.py +32 -30
  21. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/recommend.py +14 -14
  22. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/search.py +13 -13
  23. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/singer.py +51 -51
  24. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/song.py +49 -49
  25. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/songlist.py +4 -4
  26. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/top.py +29 -27
  27. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/user.py +133 -93
  28. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/__init__.py +2 -0
  29. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/_base.py +15 -0
  30. qqmusic_api_python-0.6.9/qqmusic_api/modules/album.py +117 -0
  31. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/comment.py +2 -2
  32. qqmusic_api_python-0.6.9/qqmusic_api/modules/helper.py +84 -0
  33. qqmusic_api_python-0.6.9/qqmusic_api/modules/helper_utils.py +280 -0
  34. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/login.py +15 -2
  35. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/mv.py +30 -1
  36. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/private_message.py +30 -30
  37. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/recommend.py +7 -3
  38. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/song.py +2 -0
  39. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/songlist.py +48 -8
  40. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/user.py +174 -14
  41. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/utils/device.py +2 -0
  42. qqmusic_api_python-0.6.9/tests/test_album.py +59 -0
  43. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_lyric.py +1 -7
  44. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_mv.py +17 -0
  45. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_recommend.py +10 -0
  46. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_songlist.py +19 -0
  47. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_user.py +25 -0
  48. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/uv.lock +112 -0
  49. qqmusic_api_python-0.6.9/web/Dockerfile +41 -0
  50. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/README.md +30 -2
  51. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/config.example.toml +5 -0
  52. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/requirements.txt +151 -198
  53. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/run.py +0 -3
  54. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/app.py +5 -5
  55. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/auth.py +52 -42
  56. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/cache.py +25 -12
  57. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/config.py +18 -0
  58. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/credential_store.py +20 -25
  59. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/response.py +1 -3
  60. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/security.py +26 -20
  61. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/songlist.py +10 -11
  62. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/search.py +3 -3
  63. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/songlist.py +2 -0
  64. qqmusic_api_python-0.6.9/web/src/routes/user.py +186 -0
  65. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/docstrings.py +1 -3
  66. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/executor.py +42 -20
  67. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/router_factory.py +10 -2
  68. qqmusic_api_python-0.6.9/web/tests/conftest.py +12 -0
  69. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/tests/test_web_routes.py +25 -28
  70. qqmusic_api_python-0.6.7/qqmusic_api/models/album.py +0 -72
  71. qqmusic_api_python-0.6.7/qqmusic_api/models/lyric.py +0 -41
  72. qqmusic_api_python-0.6.7/qqmusic_api/modules/album.py +0 -58
  73. qqmusic_api_python-0.6.7/tests/test_album.py +0 -29
  74. qqmusic_api_python-0.6.7/web/Dockerfile +0 -23
  75. qqmusic_api_python-0.6.7/web/src/routes/user.py +0 -109
  76. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.agents/skills/pydantic/SKILL.md +0 -0
  77. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.agents/skills/python-standards/SKILL.md +0 -0
  78. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.agents/skills/tarsio/SKILL.md +0 -0
  79. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.agents/skills/tarsio/references/api-reference.md +0 -0
  80. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.agents/skills/uv-package-manager/SKILL.md +0 -0
  81. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  82. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  83. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  84. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  85. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/renovate.json +0 -0
  86. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/workflows/checking.yaml +0 -0
  87. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/workflows/docs.yml +0 -0
  88. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/workflows/release.yml +0 -0
  89. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.github/workflows/testing.yml +0 -0
  90. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/.markdownlint-cli2.yaml +0 -0
  91. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/CLAUDE.md +0 -0
  92. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/LICENSE +0 -0
  93. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/assets/qq-music.svg +0 -0
  94. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/cliff.toml +0 -0
  95. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/contributing.md +0 -0
  96. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/index.md +0 -0
  97. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/core/client.md +0 -0
  98. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/core/exception.md +0 -0
  99. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/core/pagination.md +0 -0
  100. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/core/request.md +0 -0
  101. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/core/versioning.md +0 -0
  102. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/album.md +0 -0
  103. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/base.md +0 -0
  104. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/comment.md +0 -0
  105. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/login.md +0 -0
  106. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/lyric.md +0 -0
  107. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/mv.md +0 -0
  108. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/recommend.md +0 -0
  109. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/request.md +0 -0
  110. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/search.md +0 -0
  111. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/singer.md +0 -0
  112. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/song.md +0 -0
  113. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/songlist.md +0 -0
  114. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/top.md +0 -0
  115. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/model/user.md +0 -0
  116. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/album.md +0 -0
  117. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/comment.md +0 -0
  118. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/login.md +0 -0
  119. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/login_utils.md +0 -0
  120. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/lyric.md +0 -0
  121. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/mv.md +0 -0
  122. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/private_message.md +0 -0
  123. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/recommend.md +0 -0
  124. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/search.md +0 -0
  125. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/singer.md +0 -0
  126. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/song.md +0 -0
  127. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/songlist.md +0 -0
  128. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/top.md +0 -0
  129. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/reference/modules/user.md +0 -0
  130. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/client.md +0 -0
  131. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/credential.md +0 -0
  132. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/download.md +0 -0
  133. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/error-handling.md +0 -0
  134. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/login.md +0 -0
  135. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/pagination.md +0 -0
  136. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/start.md +0 -0
  137. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/docs/tutorial/web.md +0 -0
  138. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/examples/download_song.py +0 -0
  139. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/examples/get_all_sheets.py +0 -0
  140. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/examples/phone_login.py +0 -0
  141. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/examples/private_message.py +0 -0
  142. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/examples/qrcode_login.py +0 -0
  143. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/prek.toml +0 -0
  144. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/algorithms/__init__.py +0 -0
  145. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/algorithms/sign.py +0 -0
  146. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/algorithms/tripledes.py +0 -0
  147. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/__init__.py +0 -0
  148. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/pagination.py +0 -0
  149. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/request.py +0 -0
  150. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/core/versioning.py +0 -0
  151. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/__init__.py +0 -0
  152. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/_validator.py +0 -0
  153. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/models/request.py +0 -0
  154. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/login_utils.py +0 -0
  155. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/lyric.py +0 -0
  156. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/search.py +0 -0
  157. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/singer.py +0 -0
  158. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/modules/top.py +0 -0
  159. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/utils/__init__.py +0 -0
  160. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/utils/common.py +0 -0
  161. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/utils/mqtt.py +0 -0
  162. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/qqmusic_api/utils/qimei.py +0 -0
  163. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/scripts/ag-1.py +0 -0
  164. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/conftest.py +0 -0
  165. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_comment.py +0 -0
  166. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_login.py +0 -0
  167. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_login_utils.py +0 -0
  168. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_private_message.py +0 -0
  169. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_search.py +0 -0
  170. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_singer.py +0 -0
  171. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_song.py +0 -0
  172. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/tests/test_top.py +0 -0
  173. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/.gitignore +0 -0
  174. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/accounts.example.toml +0 -0
  175. /qqmusic_api_python-0.6.7/web/docker-compose.yml → /qqmusic_api_python-0.6.9/web/docker-compose.example.yml +0 -0
  176. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/__init__.py +0 -0
  177. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/core/deps.py +0 -0
  178. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/__init__.py +0 -0
  179. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/comment.py +0 -0
  180. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/login.py +0 -0
  181. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/mv.py +0 -0
  182. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/singer.py +0 -0
  183. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/modules/song.py +0 -0
  184. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/__init__.py +0 -0
  185. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/_helpers.py +0 -0
  186. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/album.py +0 -0
  187. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/comment.py +0 -0
  188. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/login.py +0 -0
  189. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/lyric.py +0 -0
  190. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/mv.py +0 -0
  191. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/recommend.py +0 -0
  192. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/singer.py +0 -0
  193. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/song.py +0 -0
  194. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routes/top.py +0 -0
  195. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/__init__.py +0 -0
  196. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/params.py +0 -0
  197. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/src/routing/route_types.py +0 -0
  198. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/tests/test_web_core.py +0 -0
  199. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/tests/test_web_docstrings.py +0 -0
  200. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/tests/test_web_enums.py +0 -0
  201. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/web/tests/test_web_route_validation.py +0 -0
  202. {qqmusic_api_python-0.6.7 → qqmusic_api_python-0.6.9}/zensical.toml +0 -0
@@ -22,5 +22,3 @@ dist
22
22
  .prek_cache
23
23
  *.toml
24
24
  !pyproject.toml
25
- !web/config.toml
26
- !web/accounts.toml
@@ -13,6 +13,7 @@ device.json
13
13
  # AI
14
14
  .ccb/
15
15
  docs/superpowers/
16
+ .omo/
16
17
 
17
18
  # Distribution / packaging
18
19
  .Python
@@ -47,6 +47,27 @@
47
47
  * 新增页面必须同步更新 `zensical.toml` 的 `nav`。
48
48
  * 文档构建工具实际使用 `zensical`,配置文件是 `zensical.toml`。
49
49
 
50
+ ## Release workflow
51
+
52
+ 1. 更新 `qqmusic_api/__init__.py` 中的 `__version__` 为待发布版本。
53
+ 2. 提交版本号变更:
54
+
55
+ ```bash
56
+ git add qqmusic_api/__init__.py
57
+ git commit -m "🧹 chore(release): x.x.x"
58
+ ```
59
+
60
+ 3. 在**该提交上**打 tag 并推送:
61
+
62
+ ```bash
63
+ git tag v0.x.x
64
+ git push origin v0.x.x
65
+ ```
66
+
67
+ 4. CI 自动执行:
68
+ * **release.yml**:`uv build` → `uv publish`(PyPI)→ 创建 GitHub Release(git-cliff 自动生成 release body)
69
+ * **docs.yml**(release published 后触发):更新 `docs/release-notes.md` → 导出 `web/requirements.txt` → commit → 构建文档站 → 部署 GitHub Pages
70
+
50
71
  ## Agent behavior
51
72
 
52
73
  * **核心规约**:遵循 `docs/contributing.md` 中的详细规约。**在执行任务前,必须完整阅读该指南以确保合规。**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qqmusic-api-python
3
- Version: 0.6.7
3
+ Version: 0.6.9
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/
@@ -37,6 +37,8 @@ Requires-Dist: paho-mqtt>=2.1.0
37
37
  Requires-Dist: pydantic>=2.13.3
38
38
  Requires-Dist: tarsio>=0.5.1
39
39
  Requires-Dist: typing-extensions>=4.12.2
40
+ Provides-Extra: upload
41
+ Requires-Dist: cos-python-sdk-v5>=1.9.44; extra == 'upload'
40
42
  Description-Content-Type: text/markdown
41
43
 
42
44
  <div align="center">
@@ -56,6 +58,9 @@ Description-Content-Type: text/markdown
56
58
  <a href="https://github.com/l-1124/QQMusicApi/actions/workflows/testing.yml">
57
59
  <img src="https://github.com/l-1124/QQMusicApi/actions/workflows/testing.yml/badge.svg?branch=main" alt="Testing">
58
60
  </a>
61
+ <a href="https://deepwiki.com/luren-dc/QQMusicApi">
62
+ <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
63
+ </a>
59
64
  </div>
60
65
 
61
66
  ---
@@ -15,6 +15,9 @@
15
15
  <a href="https://github.com/l-1124/QQMusicApi/actions/workflows/testing.yml">
16
16
  <img src="https://github.com/l-1124/QQMusicApi/actions/workflows/testing.yml/badge.svg?branch=main" alt="Testing">
17
17
  </a>
18
+ <a href="https://deepwiki.com/luren-dc/QQMusicApi">
19
+ <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
20
+ </a>
18
21
  </div>
19
22
 
20
23
  ---
@@ -221,21 +221,24 @@ class Singer(Response):
221
221
 
222
222
  ### 需登录的接口
223
223
 
224
- 需要登录的接口应通过 `_require_login` 校验凭证:
224
+ 需要登录的接口通过 `_build_request` 的 `require_login` 参数校验凭证:
225
225
 
226
226
  ```python
227
227
  def get_vip_info(self, *, credential: Credential | None = None):
228
228
  """获取 VIP 信息."""
229
- target_credential = self._require_login(credential)
230
229
  return self._build_request(
231
230
  module="VipLogin.VipLoginInter",
232
231
  method="vip_login_base",
233
232
  param={},
234
- credential=target_credential,
233
+ credential=credential,
235
234
  response_model=UserVipInfoResponse,
235
+ require_login=True,
236
236
  )
237
237
  ```
238
238
 
239
+ > 若接口需要凭证对象的字段来构建请求参数,
240
+ > 仍可显式调用 `_require_login` 获取凭证对象。
241
+
239
242
  ## 翻页与换一批
240
243
 
241
244
  ### 连续翻页
@@ -1,18 +1,64 @@
1
1
 
2
- ## What's Changed
2
+ ## [[0.6.8](https://github.com/L-1124/QQMusicApi/compare/v0.6.7..v0.6.8)] - 2026-07-01
3
+
4
+ ### Bug 修复
5
+
6
+ * **(web)** 禁用 FastAPI 默认按别名响应,使 JSON 输出与 Python 原生变量名保持一致 ([32459a5](https://github.com/L-1124/QQMusicApi/commit/32459a54c03d8a0a738f1917f9eb19fc6f9ac4ef)) by [@L-1124](https://github.com/L-1124)
7
+
8
+ ### 功能更新
9
+
10
+ * **(core)** 新增 SignatureRequiredError 错误码 2000 处理 ([a73313a](https://github.com/L-1124/QQMusicApi/commit/a73313a91549a156358ad9311d0e20d7401b861b)) by [@L-1124](https://github.com/L-1124)
11
+ * **(core)** _ensure_session 添加 24h 保活机制 ([2290a32](https://github.com/L-1124/QQMusicApi/commit/2290a32304bcd9052365f60c67f2d4f6b00e4d7e)) by [@L-1124](https://github.com/L-1124)
12
+ * **(helper)** 新增辅助功能接口 ([eb0c40c](https://github.com/L-1124/QQMusicApi/commit/eb0c40c1461826955a1eb005afb723438730142a)) by [@L-1124](https://github.com/L-1124)
13
+ * **(login)** 支持登出API ([73f2a7e](https://github.com/L-1124/QQMusicApi/commit/73f2a7ee9ad1d48cbec2d6aa9dbf844f44b573c2)) by [@L-1124](https://github.com/L-1124)
14
+ * **(lyric)** [**breaking**] 彻底移除 crypt 字段,隐式处理加密状态 ([80edc40](https://github.com/L-1124/QQMusicApi/commit/80edc40e41331f6b5c11146d5daf6eccf344181f)) by [@L-1124](https://github.com/L-1124)
15
+ * **(lyric)** [**breaking**] 移除 decrypt 方法,改为默认解密 ([24949c4](https://github.com/L-1124/QQMusicApi/commit/24949c4df8c63b02b35dd69c30e2a8f40d234dfb)) by [@L-1124](https://github.com/L-1124)
16
+ * **(song)** 添加特殊歌曲文件类型 TRY_OGG_640 ([fef7e8c](https://github.com/L-1124/QQMusicApi/commit/fef7e8ceb220aa477ea7d8498f8ebfd240f72cc6)) by [@L-1124](https://github.com/L-1124)
17
+ * **(user)** 新增收藏/取消收藏歌单 API ([7865434](https://github.com/L-1124/QQMusicApi/commit/7865434782dd205815222fe716f52bac9f84aeec)) by [@jinzhongjia](https://github.com/jinzhongjia) in [#281](https://github.com/L-1124/QQMusicApi/pull/281)
18
+ * **(user)** 新增 FeedbackBlack 黑名单 API ([4340554](https://github.com/L-1124/QQMusicApi/commit/4340554aaaafaef175eead7ebced596538bc65e2)) by [@L-1124](https://github.com/L-1124)
19
+ * **(web)** 启用 FastAPI 内置 Swagger UI 和 ReDoc,更新文档地址说明 ([a3245ad](https://github.com/L-1124/QQMusicApi/commit/a3245ad3d3863c7f7eec94352d169a32ba4478c6)) by [@L-1124](https://github.com/L-1124)
20
+ * **(web)** 重构 Dockerfile 为多阶段构建,修复非 root 用户 uv 缓存权限问题 ([00e16a3](https://github.com/L-1124/QQMusicApi/commit/00e16a37b62e989de37232e8af7e77167a28e823)) by [@L-1124](https://github.com/L-1124)
21
+
22
+ ### 功能重构
23
+
24
+ * **(core)** 移除 _session_cache 冗余缓存,简化会话验证逻辑 ([0b12b9d](https://github.com/L-1124/QQMusicApi/commit/0b12b9d041a8be24ad84d60bd2ceaed76b81fe92)) by [@L-1124](https://github.com/L-1124)
25
+ * **(models)** [**breaking**] 将所有模型中的 alias 统一替换为 validation_alias,隔离序列化字段名 ([c611b91](https://github.com/L-1124/QQMusicApi/commit/c611b91d607d36327662a80d0e126a6958b21bd5)) by [@L-1124](https://github.com/L-1124)
26
+ * **(modules)** 移除冗余的 _require_login 调用,统一使用 require_login 参数 ([1a666f4](https://github.com/L-1124/QQMusicApi/commit/1a666f45ae968c235066c07e8b3e82096c3fc668)) by [@L-1124](https://github.com/L-1124)
27
+ * **(modules)** 将 _require_login 收敛到 _build_request 的 require_login 参数中 ([d1a92ca](https://github.com/L-1124/QQMusicApi/commit/d1a92ca07d5a98f3f443453e5fc77b8f8c8dc44e)) by [@L-1124](https://github.com/L-1124)
28
+
29
+ ### 文档更新
30
+
31
+ * **(coding)** 更新需登录接口的编码规范,使用 require_login 参数替代 _require_login ([9e56215](https://github.com/L-1124/QQMusicApi/commit/9e562152ec8039b3045ae6c51006401c93b36a36)) by [@L-1124](https://github.com/L-1124)
32
+ * **(dislike)** 更新不喜欢列表项和响应数据的文档说明 ([5dbf711](https://github.com/L-1124/QQMusicApi/commit/5dbf711e46a69f99940f048504446b9e074fd21f)) by [@L-1124](https://github.com/L-1124)
33
+ * **(login)** 更新二维码和手机验证码登录状态的文档注释 ([f31d4d0](https://github.com/L-1124/QQMusicApi/commit/f31d4d0132cb61a9935ebea92657fcfa6591926c)) by [@L-1124](https://github.com/L-1124)
34
+ * **(readme)** 添加 DeepWiki 相关链接 ([fcea758](https://github.com/L-1124/QQMusicApi/commit/fcea758b80a824cb513a8bdfc8b247da786f5a1e)) by [@L-1124](https://github.com/L-1124)
35
+
36
+ ### 贡献者
37
+
38
+ * @L-1124
39
+ * @jinzhongjia [#281](https://github.com/L-1124/QQMusicApi/pull/281)
40
+ * @github-actions[bot]
41
+
42
+ ## [[0.6.7](https://github.com/L-1124/QQMusicApi/compare/v0.6.6..v0.6.7)] - 2026-06-17
3
43
 
4
44
  ### 功能更新
5
45
 
46
+ * **(login)** 新增登录异常子类和错误码映射 ([0ef9645](https://github.com/L-1124/QQMusicApi/commit/0ef9645806c9778cfa6214dd60fa56644568c276)) by [@jinzhongjia](https://github.com/jinzhongjia) in [#269](https://github.com/L-1124/QQMusicApi/pull/269)
47
+ * **(qimei)** 增加 24 小时过期重新获取逻辑 ([05aed29](https://github.com/L-1124/QQMusicApi/commit/05aed29096dff31b7d0cbb26ccc66ec50da38bdc)) by [@L-1124](https://github.com/L-1124)
6
48
  * **(web)** 添加 has_sheet 路由 ([9e7a143](https://github.com/L-1124/QQMusicApi/commit/9e7a1439ff597ae43a4ef99d88ec08ee32889b9a)) by [@L-1124](https://github.com/L-1124)
7
49
 
8
50
  ### 文档更新
9
51
 
52
+ * **(coding)** 重写 API 编写指南,补充开发细节 ([9db46de](https://github.com/L-1124/QQMusicApi/commit/9db46def73ec6c2d9826c4a15bc2eb7ba9ed0f37)) by [@L-1124](https://github.com/L-1124)
53
+ * 为固定请求平台的 API 补充 docstring 说明 ([aba712e](https://github.com/L-1124/QQMusicApi/commit/aba712e56a7485b4184941480c05f370cd53c548)) by [@L-1124](https://github.com/L-1124)
10
54
  * 完善快速开始文档并新增多篇核心教程 ([d6143cd](https://github.com/L-1124/QQMusicApi/commit/d6143cdc047ebeb4963b061680a21103ba3d58d4)) by [@L-1124](https://github.com/L-1124)
11
55
 
12
56
  ### 贡献者
13
57
 
14
58
  * @L-1124
59
+ * @jinzhongjia [#269](https://github.com/L-1124/QQMusicApi/pull/269)
15
60
  * @github-actions[bot]
61
+ * @renovate[bot]
16
62
 
17
63
  ## [[0.6.6](https://github.com/L-1124/QQMusicApi/compare/v0.6.5..v0.6.6)] - 2026-06-13
18
64
 
@@ -0,0 +1,33 @@
1
+ """辅助功能文件上传示例."""
2
+
3
+ import asyncio
4
+
5
+ from qqmusic_api import Client
6
+ from qqmusic_api.modules.helper_utils import UploadFileSession
7
+
8
+
9
+ async def main() -> None:
10
+ """文件上传示例."""
11
+ file_to_upload = ""
12
+
13
+ async with Client() as client:
14
+ session = UploadFileSession(
15
+ api=client.helper,
16
+ bus_id="songlist", # 歌单封面
17
+ max_concurrency=3,
18
+ )
19
+
20
+ object_infos = await session.upload([file_to_upload])
21
+ print(f"总计成功上传 {len(object_infos)} 个文件!")
22
+ for info in object_infos:
23
+ print(f"存储桶: {info.storage.bucket.name}")
24
+ print(f"区域: {info.storage.bucket.region}")
25
+ print(f"对象键: {info.storage.object_key}")
26
+ print(f"CDN 地址: {info.url.cdn_url}")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ try:
31
+ asyncio.run(main())
32
+ except KeyboardInterrupt:
33
+ print("\n用户取消操作")
@@ -49,6 +49,11 @@ documentation = "https://l-1124.github.io/QQMusicApi/"
49
49
  homepage = "https://l-1124.github.io/QQMusicApi/"
50
50
  repository = "https://github.com/l-1124/QQMusicApi"
51
51
 
52
+ [project.optional-dependencies]
53
+ upload = [
54
+ "cos-python-sdk-v5>=1.9.44",
55
+ ]
56
+
52
57
  [dependency-groups]
53
58
  dev = [
54
59
  "tarsio[cli]>=0.5.1",
@@ -56,6 +61,7 @@ dev = [
56
61
  { include-group = "docs" },
57
62
  { include-group = "linting" },
58
63
  { include-group = "testing" },
64
+ { include-group = "helper" },
59
65
  ]
60
66
  docs = [
61
67
  "docstring-inheritance>=3.0.0",
@@ -65,6 +71,9 @@ docs = [
65
71
  "mkdocstrings-python>=2.0.3",
66
72
  "zensical>=0.0.30",
67
73
  ]
74
+ helper = [
75
+ "cos-python-sdk-v5>=1.9.44",
76
+ ]
68
77
  linting = [
69
78
  "prek>=0.3.5",
70
79
  "pyrefly>=0.56.0",
@@ -22,7 +22,7 @@ from .core.exceptions import (
22
22
  from .core.versioning import Platform
23
23
  from .models.request import Credential
24
24
 
25
- __version__ = "0.6.7"
25
+ __version__ = "0.6.9"
26
26
 
27
27
  __all__ = [
28
28
  "ApiDataError",
@@ -17,7 +17,7 @@ from urllib3.util.retry import Retry
17
17
  from ..algorithms import zzc_sign
18
18
  from ..models.request import Credential, JceRequest, JceRequestItem, JceResponse, JceResponseItem, RequestItem
19
19
  from ..utils.common import bool_to_int
20
- from ..utils.device import DeviceManager
20
+ from ..utils.device import Device, DeviceManager
21
21
  from ..utils.qimei import QimeiManager
22
22
  from .exceptions import (
23
23
  ApiDataError,
@@ -27,6 +27,7 @@ from .exceptions import (
27
27
  HTTPError,
28
28
  NetworkError,
29
29
  RatelimitedError,
30
+ SignatureRequiredError,
30
31
  )
31
32
  from .request import Request, RequestResultT, _build_result
32
33
  from .versioning import DEFAULT_VERSION_POLICY, Platform, VersionPolicy
@@ -34,6 +35,7 @@ from .versioning import DEFAULT_VERSION_POLICY, Platform, VersionPolicy
34
35
  if TYPE_CHECKING:
35
36
  from ..modules.album import AlbumApi
36
37
  from ..modules.comment import CommentApi
38
+ from ..modules.helper import HelperApi
37
39
  from ..modules.login import LoginApi
38
40
  from ..modules.lyric import LyricApi
39
41
  from ..modules.mv import MvApi
@@ -107,7 +109,6 @@ class Client:
107
109
 
108
110
  self._version_policy: VersionPolicy = DEFAULT_VERSION_POLICY
109
111
  self._session_lock = anyio.Lock()
110
- self._session_initialized = False
111
112
  self._qimei_manager = QimeiManager(
112
113
  device_store=self._device_store,
113
114
  app_version=self._version_policy.get_qimei_app_version(),
@@ -116,12 +117,22 @@ class Client:
116
117
  )
117
118
 
118
119
  async def _ensure_session(self) -> None:
120
+ """获取 `Platform.ANDROID` 会话信息."""
121
+
122
+ def _is_session_valid(dev: "Device") -> bool:
123
+ return (
124
+ dev.session_save_time is not None
125
+ and (int(time.time()) - dev.session_save_time) < 86400
126
+ and bool(dev.session_uid and dev.session_sid)
127
+ )
128
+
129
+ device = await self._device_store.get_device()
130
+ if _is_session_valid(device):
131
+ return
132
+
119
133
  async with self._session_lock:
120
- if self._session_initialized:
121
- return
122
134
  device = await self._device_store.get_device()
123
- if device.session_uid and device.session_sid:
124
- self._session_initialized = True
135
+ if _is_session_valid(device):
125
136
  return
126
137
 
127
138
  finalcomm = self._version_policy.build_comm(
@@ -168,8 +179,15 @@ class Client:
168
179
  device.session_uid = str(session_data["uid"])
169
180
  device.session_sid = session_data["sid"]
170
181
  device.session_vkey = session_data.get("vkey")
182
+ device.session_save_time = int(time.time())
171
183
  await self._device_store.save_device()
172
- self._session_initialized = True
184
+
185
+ @cached_property
186
+ def helper(self) -> "HelperApi":
187
+ """辅助模块."""
188
+ from ..modules.helper import HelperApi
189
+
190
+ return HelperApi(self)
173
191
 
174
192
  @cached_property
175
193
  def comment(self) -> "CommentApi":
@@ -604,8 +622,9 @@ class Client:
604
622
  "RequestResultT",
605
623
  {"code": code, "data": data} if request.is_jce else item,
606
624
  )
607
-
608
625
  match code:
626
+ case 2000:
627
+ raise SignatureRequiredError(code=code, data=data)
609
628
  case 2001:
610
629
  raise RatelimitedError(code=code, data=data)
611
630
  case 1000 | 104401 | 104400:
@@ -19,6 +19,7 @@ __all__ = [
19
19
  "LoginRateLimitError",
20
20
  "NetworkError",
21
21
  "RatelimitedError",
22
+ "SignatureRequiredError",
22
23
  ]
23
24
 
24
25
 
@@ -202,3 +203,10 @@ class LoginRateLimitError(LoginError):
202
203
  data: Any = None,
203
204
  ):
204
205
  super().__init__(message, code=code, data=data)
206
+
207
+
208
+ class SignatureRequiredError(CgiApiException):
209
+ """请求需要签名 (code=2000)."""
210
+
211
+ def __init__(self, *, code: int = 2000, data: Any = None) -> None:
212
+ super().__init__("请求需要签名", code=code, data=data)
@@ -0,0 +1,121 @@
1
+ """Album API 返回模型定义."""
2
+
3
+ from pydantic import Field
4
+
5
+ from .base import Album, Singer, Song
6
+ from .request import Response
7
+
8
+
9
+ class AlbumDetail(Album):
10
+ """专辑详情页返回的核心专辑信息.
11
+
12
+ Attributes:
13
+ subtitle: 专辑副标题.
14
+ time_public: 发行日期.
15
+ desc: 专辑简介.
16
+ language: 专辑语种.
17
+ album_type: 专辑类型描述.
18
+ genre: 专辑流派文本.
19
+ wikiurl: 百科链接.
20
+ """
21
+
22
+ subtitle: str = ""
23
+ time_public: str = Field(default="", validation_alias="publishDate")
24
+ desc: str = ""
25
+ language: str = ""
26
+ album_type: str = Field(default="", validation_alias="albumType")
27
+ genre: str = ""
28
+ wikiurl: str = ""
29
+
30
+
31
+ class AlbumCompany(Response):
32
+ """专辑详情接口中的发行公司信息.
33
+
34
+ Attributes:
35
+ id: 公司 ID.
36
+ name: 公司名称.
37
+ is_show: 是否展示.
38
+ brief: 公司简介.
39
+ """
40
+
41
+ id: int = Field(validation_alias="ID")
42
+ name: str
43
+ is_show: int = Field(validation_alias="isShow")
44
+ brief: str = ""
45
+
46
+
47
+ class GetAlbumDetailResponse(Response):
48
+ """专辑详情接口聚合后的响应体.
49
+
50
+ Attributes:
51
+ album: 专辑基础信息与补充描述.
52
+ company: 发行公司信息.
53
+ singers: 专辑署名歌手列表.
54
+ """
55
+
56
+ album: AlbumDetail = Field(validation_alias="basicInfo")
57
+ company: AlbumCompany
58
+ singers: list[Singer] = Field(default_factory=list, json_schema_extra={"jsonpath": "$.singer.singerList"})
59
+
60
+
61
+ class GetAlbumSongResponse(Response):
62
+ """专辑歌曲列表接口返回的分页结果.
63
+
64
+ Attributes:
65
+ album_mid: 专辑 MID.
66
+ total_num: 歌曲总数.
67
+ song_list: 当前响应携带的专辑歌曲列表.
68
+ """
69
+
70
+ album_mid: str = Field(validation_alias="albumMid")
71
+ total_num: int = Field(validation_alias="totalNum")
72
+ song_list: list[Song] = Field(default_factory=list, json_schema_extra={"jsonpath": "$.songList[*].songInfo"})
73
+
74
+
75
+ class NewAlbumItem(Album):
76
+ """新碟上架列表中的单张专辑摘要.
77
+
78
+ Attributes:
79
+ singers: 专辑署名歌手列表.
80
+ release_time: 发行日期, 格式通常为 YYYY-MM-DD.
81
+ type: 专辑类型.
82
+ area: 地区标识.
83
+ genre: 流派标识.
84
+ language: 语种标识.
85
+ """
86
+
87
+ singers: list[Singer] = Field(default_factory=list)
88
+ release_time: str = ""
89
+ type: int = 0
90
+ area: int = 0
91
+ genre: int = 0
92
+ language: int = 0
93
+
94
+
95
+ class GetNewAlbumResponse(Response):
96
+ """新碟上架接口的响应体.
97
+
98
+ Attributes:
99
+ total: 该地区下新碟总数.
100
+ albums: 当前页新碟列表.
101
+ """
102
+
103
+ total: int = 0
104
+ albums: list[NewAlbumItem] = Field(default_factory=list)
105
+
106
+
107
+ class AlbumFavWriteResponse(Response):
108
+ """收藏 / 取消收藏专辑的写操作响应.
109
+
110
+ Attributes:
111
+ result: 操作结果码, 0 表示成功.
112
+ failed_album_id: 操作失败的专辑 ID 列表, 全部成功时为空.
113
+ """
114
+
115
+ result: int = 0
116
+ failed_album_id: list[int] = Field(default_factory=list, validation_alias="v_failedAlbumId")
117
+
118
+ @property
119
+ def success(self) -> bool:
120
+ """是否操作成功 (result 为 0 且无失败项)."""
121
+ return self.result == 0 and not self.failed_album_id
@@ -18,21 +18,17 @@ _PHOTO_NEW_SIZE_SEGMENTS: Final[dict[int, str]] = {
18
18
  }
19
19
 
20
20
 
21
- def _normalize_cover_size(size: CoverSize) -> str:
22
- """返回 `photo_new` 模板使用的尺寸片段."""
23
- try:
24
- return _PHOTO_NEW_SIZE_SEGMENTS[size]
25
- except KeyError as exc:
26
- raise ValueError("not supported size") from exc
27
-
28
-
29
21
  def _build_photo_new_cover_url(kind: str, mid: str, size: CoverSize) -> str:
30
22
  """按 `photo_new` 规则拼接封面链接."""
31
23
  normalized_mid = mid.strip()
32
24
  if not normalized_mid:
33
25
  return ""
34
26
 
35
- return f"https://y.gtimg.cn/music/photo_new/{kind}{_normalize_cover_size(size)}M000{normalized_mid}.jpg"
27
+ try:
28
+ cover_size = _PHOTO_NEW_SIZE_SEGMENTS[size]
29
+ return f"https://y.gtimg.cn/music/photo_new/{kind}{cover_size}M000{normalized_mid}.jpg"
30
+ except KeyError as exc:
31
+ raise ValueError("not supported size") from exc
36
32
 
37
33
 
38
34
  class Singer(Response):
@@ -85,29 +85,29 @@ class CommentItem(Response):
85
85
  sub_comments: 子评论列表.
86
86
  """
87
87
 
88
- cmid: str = Field(alias="CmId")
89
- seq_no: str = Field(alias="SeqNo")
90
- nick: str = Field(alias="Nick")
91
- avatar: str = Field(alias="Avatar")
92
- encrypt_uin: str = Field(alias="EncryptUin")
93
- content: str = Field(alias="Content")
94
- pub_time: int = Field(alias="PubTime")
95
- praise_num: int = Field(alias="PraiseNum")
96
- reply_cnt: int = Field(alias="ReplyCnt")
97
- is_praised: int = Field(alias="IsPraised")
98
- is_self: int = Field(alias="IsSelf")
99
- state: int = Field(alias="State")
100
- hot_score: str = Field(alias="HotScore")
101
- rec_score: str = Field(alias="RecScore")
102
- song_id: int = Field(alias="SongId")
103
- song_name: str = Field(alias="SongName")
104
- singer_names: str = Field(alias="SingerNames")
105
- song_ts_elems: list[dict[str, Any]] = Field(default_factory=list, alias="SongTsElems")
106
- hash_tag_list: list[dict[str, Any]] = Field(default_factory=list, alias="HashTagList")
107
- little_tails: list[dict[str, Any]] = Field(default_factory=list, alias="LittleTails")
108
- icon_list: list[dict[str, Any]] = Field(default_factory=list, alias="IconList")
109
- vip_ui: dict[str, Any] = Field(default_factory=dict, alias="VipUI")
110
- sub_comments: list[dict[str, Any]] = Field(default_factory=list, alias="SubComments")
88
+ cmid: str = Field(validation_alias="CmId")
89
+ seq_no: str = Field(validation_alias="SeqNo")
90
+ nick: str = Field(validation_alias="Nick")
91
+ avatar: str = Field(validation_alias="Avatar")
92
+ encrypt_uin: str = Field(validation_alias="EncryptUin")
93
+ content: str = Field(validation_alias="Content")
94
+ pub_time: int = Field(validation_alias="PubTime")
95
+ praise_num: int = Field(validation_alias="PraiseNum")
96
+ reply_cnt: int = Field(validation_alias="ReplyCnt")
97
+ is_praised: int = Field(validation_alias="IsPraised")
98
+ is_self: int = Field(validation_alias="IsSelf")
99
+ state: int = Field(validation_alias="State")
100
+ hot_score: str = Field(validation_alias="HotScore")
101
+ rec_score: str = Field(validation_alias="RecScore")
102
+ song_id: int = Field(validation_alias="SongId")
103
+ song_name: str = Field(validation_alias="SongName")
104
+ singer_names: str = Field(validation_alias="SingerNames")
105
+ song_ts_elems: list[dict[str, Any]] = Field(default_factory=list, validation_alias="SongTsElems")
106
+ hash_tag_list: list[dict[str, Any]] = Field(default_factory=list, validation_alias="HashTagList")
107
+ little_tails: list[dict[str, Any]] = Field(default_factory=list, validation_alias="LittleTails")
108
+ icon_list: list[dict[str, Any]] = Field(default_factory=list, validation_alias="IconList")
109
+ vip_ui: dict[str, Any] = Field(default_factory=dict, validation_alias="VipUI")
110
+ sub_comments: list[dict[str, Any]] = Field(default_factory=list, validation_alias="SubComments")
111
111
 
112
112
 
113
113
  class CommentListResponse(Response):
@@ -136,13 +136,13 @@ class CommentListResponse(Response):
136
136
  has_more: int = Field(json_schema_extra={"jsonpath": "$.CommentList.HasMore"})
137
137
  next_offset: int = Field(json_schema_extra={"jsonpath": "$.CommentList.NextOffset"})
138
138
  total: int = Field(json_schema_extra={"jsonpath": "$.CommentList.Total"})
139
- total_cm_num: int = Field(alias="TotalCmNum")
140
- comment_tip: str = Field(alias="CommentTip")
141
- comment_h5_page: str = Field(alias="CommentH5Page")
142
- has_ts_cm: int = Field(alias="HasTsCm")
143
- share_cnt: int = Field(alias="ShareCnt")
144
- msg: str = Field(alias="Msg")
145
- sub_code: int = Field(alias="SubCode")
139
+ total_cm_num: int = Field(validation_alias="TotalCmNum")
140
+ comment_tip: str = Field(validation_alias="CommentTip")
141
+ comment_h5_page: str = Field(validation_alias="CommentH5Page")
142
+ has_ts_cm: int = Field(validation_alias="HasTsCm")
143
+ share_cnt: int = Field(validation_alias="ShareCnt")
144
+ msg: str = Field(validation_alias="Msg")
145
+ sub_code: int = Field(validation_alias="SubCode")
146
146
 
147
147
 
148
148
  class MomentCommentItem(Response):
@@ -167,22 +167,22 @@ class MomentCommentItem(Response):
167
167
  little_tails: 尾巴挂件列表.
168
168
  """
169
169
 
170
- cmid: str = Field(alias="CmId")
171
- seq_no: str = Field(alias="SeqNo")
172
- content: str = Field(alias="Content")
173
- encrypt_uin: str = Field(alias="EncryptUin")
174
- pub_time: int = Field(alias="PubTime")
175
- praise_num: int = Field(alias="PraiseNum")
176
- reply_cnt: int = Field(alias="ReplyCnt")
177
- state: int = Field(alias="State")
178
- is_self: int = Field(alias="IsSelf")
179
- location: str = Field(alias="Location")
180
- phone_type: str = Field(alias="PhoneType")
181
- pic: str = Field(alias="Pic")
182
- pic_size: str = Field(alias="PicSize")
183
- song_ts_elems: list[dict[str, Any]] = Field(default_factory=list, alias="SongTsElems")
184
- hash_tag_list: list[dict[str, Any]] = Field(default_factory=list, alias="HashTagList")
185
- little_tails: list[dict[str, Any]] = Field(default_factory=list, alias="LittleTails")
170
+ cmid: str = Field(validation_alias="CmId")
171
+ seq_no: str = Field(validation_alias="SeqNo")
172
+ content: str = Field(validation_alias="Content")
173
+ encrypt_uin: str = Field(validation_alias="EncryptUin")
174
+ pub_time: int = Field(validation_alias="PubTime")
175
+ praise_num: int = Field(validation_alias="PraiseNum")
176
+ reply_cnt: int = Field(validation_alias="ReplyCnt")
177
+ state: int = Field(validation_alias="State")
178
+ is_self: int = Field(validation_alias="IsSelf")
179
+ location: str = Field(validation_alias="Location")
180
+ phone_type: str = Field(validation_alias="PhoneType")
181
+ pic: str = Field(validation_alias="Pic")
182
+ pic_size: str = Field(validation_alias="PicSize")
183
+ song_ts_elems: list[dict[str, Any]] = Field(default_factory=list, validation_alias="SongTsElems")
184
+ hash_tag_list: list[dict[str, Any]] = Field(default_factory=list, validation_alias="HashTagList")
185
+ little_tails: list[dict[str, Any]] = Field(default_factory=list, validation_alias="LittleTails")
186
186
 
187
187
 
188
188
  class MomentCommentResponse(Response):
@@ -198,11 +198,11 @@ class MomentCommentResponse(Response):
198
198
  """
199
199
 
200
200
  comments: list[MomentCommentItem] = Field(default_factory=list, json_schema_extra={"jsonpath": "$.CmList[*]"})
201
- has_more: int = Field(alias="HasMore")
202
- next_pos: str = Field(default="", alias="NextPos")
203
- hint: str = Field(alias="Hint")
204
- prev_list_loaded: int = Field(alias="PrevListLoaded")
205
- map_cm_ext: dict[str, dict[str, Any]] = Field(default_factory=dict, alias="MapCmExt")
201
+ has_more: int = Field(validation_alias="HasMore")
202
+ next_pos: str = Field(default="", validation_alias="NextPos")
203
+ hint: str = Field(validation_alias="Hint")
204
+ prev_list_loaded: int = Field(validation_alias="PrevListLoaded")
205
+ map_cm_ext: dict[str, dict[str, Any]] = Field(default_factory=dict, validation_alias="MapCmExt")
206
206
 
207
207
 
208
208
  class AddCommentResponse(Response):
@@ -217,9 +217,9 @@ class AddCommentResponse(Response):
217
217
  verify_url: 验证码 URL (如果需要).
218
218
  """
219
219
 
220
- subcode: int = Field(alias="SubCode")
221
- msg: str = Field(alias="Msg")
222
- id: str = Field(alias="AddedCmId")
223
- parent: str = Field(alias="ParentCmId")
220
+ subcode: int = Field(validation_alias="SubCode")
221
+ msg: str = Field(validation_alias="Msg")
222
+ id: str = Field(validation_alias="AddedCmId")
223
+ parent: str = Field(validation_alias="ParentCmId")
224
224
  floor: int = Field(json_schema_extra={"jsonpath": "$.Floor.Num"})
225
- verify_url: str = Field(alias="VerifyUrl")
225
+ verify_url: str = Field(validation_alias="VerifyUrl")