qqmusic-api-python 0.6.0__tar.gz → 0.6.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 (183) hide show
  1. qqmusic_api_python-0.6.2/CLAUDE.md +1 -0
  2. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/PKG-INFO +1 -1
  3. qqmusic_api_python-0.6.2/docs/reference/modules/private_message.md +3 -0
  4. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/release-notes.md +41 -0
  5. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/tutorial/client.md +12 -0
  6. qqmusic_api_python-0.6.2/examples/private_message.py +74 -0
  7. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/__init__.py +1 -1
  8. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/client.py +143 -64
  9. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/request.py +1 -0
  10. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/versioning.py +4 -0
  11. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/comment.py +20 -0
  12. qqmusic_api_python-0.6.2/qqmusic_api/models/private_message.py +350 -0
  13. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/recommend.py +2 -10
  14. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/request.py +3 -0
  15. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/__init__.py +2 -0
  16. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/comment.py +60 -1
  17. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/login.py +3 -1
  18. qqmusic_api_python-0.6.2/qqmusic_api/modules/private_message.py +518 -0
  19. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/recommend.py +8 -2
  20. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/device.py +4 -0
  21. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/qimei.py +33 -36
  22. qqmusic_api_python-0.6.2/tests/test_private_message.py +38 -0
  23. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/uv.lock +256 -256
  24. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/run.py +23 -10
  25. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/app.py +19 -10
  26. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/auth.py +30 -30
  27. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/cache.py +14 -14
  28. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/config.py +7 -4
  29. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/credential_store.py +24 -21
  30. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/security.py +2 -0
  31. qqmusic_api_python-0.6.2/web/src/modules/comment.py +24 -0
  32. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/login.py +28 -5
  33. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/mv.py +1 -1
  34. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/singer.py +1 -1
  35. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/song.py +15 -4
  36. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/songlist.py +1 -1
  37. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/_helpers.py +9 -4
  38. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/comment.py +33 -3
  39. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/recommend.py +8 -2
  40. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/params.py +27 -3
  41. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/route_types.py +1 -1
  42. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/router_factory.py +2 -1
  43. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/zensical.toml +1 -0
  44. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.agents/skills/pydantic/SKILL.md +0 -0
  45. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.agents/skills/python-standards/SKILL.md +0 -0
  46. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.agents/skills/tarsio/SKILL.md +0 -0
  47. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.agents/skills/tarsio/references/api-reference.md +0 -0
  48. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.agents/skills/uv-package-manager/SKILL.md +0 -0
  49. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.dockerignore +0 -0
  50. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  51. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  52. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  53. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  54. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/renovate.json +0 -0
  55. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/workflows/checking.yaml +0 -0
  56. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/workflows/docs.yml +0 -0
  57. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/workflows/release.yml +0 -0
  58. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.github/workflows/testing.yml +0 -0
  59. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.gitignore +0 -0
  60. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/.markdownlint-cli2.yaml +0 -0
  61. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/AGENTS.md +0 -0
  62. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/LICENSE +0 -0
  63. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/README.md +0 -0
  64. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/assets/qq-music.svg +0 -0
  65. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/cliff.toml +0 -0
  66. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/coding.md +0 -0
  67. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/contributing.md +0 -0
  68. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/index.md +0 -0
  69. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/core/client.md +0 -0
  70. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/core/exception.md +0 -0
  71. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/core/pagination.md +0 -0
  72. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/core/request.md +0 -0
  73. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/core/versioning.md +0 -0
  74. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/album.md +0 -0
  75. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/base.md +0 -0
  76. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/comment.md +0 -0
  77. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/login.md +0 -0
  78. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/lyric.md +0 -0
  79. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/mv.md +0 -0
  80. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/recommend.md +0 -0
  81. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/request.md +0 -0
  82. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/search.md +0 -0
  83. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/singer.md +0 -0
  84. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/song.md +0 -0
  85. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/songlist.md +0 -0
  86. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/top.md +0 -0
  87. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/model/user.md +0 -0
  88. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/album.md +0 -0
  89. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/comment.md +0 -0
  90. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/login.md +0 -0
  91. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/login_utils.md +0 -0
  92. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/lyric.md +0 -0
  93. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/mv.md +0 -0
  94. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/recommend.md +0 -0
  95. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/search.md +0 -0
  96. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/singer.md +0 -0
  97. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/song.md +0 -0
  98. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/songlist.md +0 -0
  99. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/top.md +0 -0
  100. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/reference/modules/user.md +0 -0
  101. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/tutorial/credential.md +0 -0
  102. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/tutorial/pagination.md +0 -0
  103. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/tutorial/start.md +0 -0
  104. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/docs/tutorial/web.md +0 -0
  105. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/examples/download_song.py +0 -0
  106. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/examples/phone_login.py +0 -0
  107. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/examples/qrcode_login.py +0 -0
  108. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/prek.toml +0 -0
  109. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/pyproject.toml +0 -0
  110. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/algorithms/__init__.py +0 -0
  111. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/algorithms/tripledes.py +0 -0
  112. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/__init__.py +0 -0
  113. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/exceptions.py +0 -0
  114. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/core/pagination.py +0 -0
  115. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/__init__.py +0 -0
  116. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/album.py +0 -0
  117. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/base.py +0 -0
  118. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/login.py +0 -0
  119. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/lyric.py +0 -0
  120. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/mv.py +0 -0
  121. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/search.py +0 -0
  122. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/singer.py +0 -0
  123. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/song.py +0 -0
  124. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/songlist.py +0 -0
  125. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/top.py +0 -0
  126. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/models/user.py +0 -0
  127. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/_base.py +0 -0
  128. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/album.py +0 -0
  129. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/login_utils.py +0 -0
  130. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/lyric.py +0 -0
  131. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/mv.py +0 -0
  132. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/search.py +0 -0
  133. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/singer.py +0 -0
  134. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/song.py +0 -0
  135. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/songlist.py +0 -0
  136. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/top.py +0 -0
  137. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/user.py +0 -0
  138. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/__init__.py +0 -0
  139. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/common.py +0 -0
  140. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/mqtt.py +0 -0
  141. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/scripts/ag-1.py +0 -0
  142. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/conftest.py +0 -0
  143. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_album.py +0 -0
  144. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_comment.py +0 -0
  145. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_login.py +0 -0
  146. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_login_utils.py +0 -0
  147. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_lyric.py +0 -0
  148. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_mv.py +0 -0
  149. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_recommend.py +0 -0
  150. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_search.py +0 -0
  151. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_singer.py +0 -0
  152. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_song.py +0 -0
  153. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_songlist.py +0 -0
  154. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_top.py +0 -0
  155. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/tests/test_user.py +0 -0
  156. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/.gitignore +0 -0
  157. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/Dockerfile +0 -0
  158. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/README.md +0 -0
  159. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/accounts.example.toml +0 -0
  160. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/config.example.toml +0 -0
  161. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/docker-compose.yml +0 -0
  162. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/__init__.py +0 -0
  163. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/deps.py +0 -0
  164. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/core/response.py +0 -0
  165. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/modules/__init__.py +0 -0
  166. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/__init__.py +0 -0
  167. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/album.py +0 -0
  168. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/login.py +0 -0
  169. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/lyric.py +0 -0
  170. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/mv.py +0 -0
  171. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/search.py +0 -0
  172. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/singer.py +0 -0
  173. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/song.py +0 -0
  174. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/songlist.py +0 -0
  175. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/top.py +0 -0
  176. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routes/user.py +0 -0
  177. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/__init__.py +0 -0
  178. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/docstrings.py +0 -0
  179. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/src/routing/executor.py +0 -0
  180. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/tests/test_web_docstrings.py +0 -0
  181. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/tests/test_web_enums.py +0 -0
  182. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/tests/test_web_route_validation.py +0 -0
  183. {qqmusic_api_python-0.6.0 → qqmusic_api_python-0.6.2}/web/tests/test_web_routes.py +0 -0
@@ -0,0 +1 @@
1
+ AGENTS.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qqmusic-api-python
3
- Version: 0.6.0
3
+ Version: 0.6.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/
@@ -0,0 +1,3 @@
1
+ # PrivateMessageApi
2
+
3
+ ::: modules.private_message.PrivateMessageApi
@@ -1,4 +1,45 @@
1
1
 
2
+ ## [[0.6.1](https://github.com/L-1124/QQMusicApi/compare/v0.6.0..v0.6.1)] - 2026-05-20
3
+
4
+ ### Bug 修复
5
+
6
+ * 传入的platform不生效 ([b19bec5](https://github.com/L-1124/QQMusicApi/commit/b19bec52f044d6508d9506f7111fea6eee5b42de)) by [@L-1124](https://github.com/L-1124)
7
+
8
+ ### 功能更新
9
+
10
+ * **(comment)** 支持添加评论和删除评论功能 ([dc5f568](https://github.com/L-1124/QQMusicApi/commit/dc5f5685d31f48b5dd8fc6bfcfdb6fb357884e7e)) by [@L-1124](https://github.com/L-1124)
11
+ * **(private-message)** 新增私信接口模块 ([0ce6c77](https://github.com/L-1124/QQMusicApi/commit/0ce6c77b423c01c684cf86ff7a7ee9ca9a70d9fa)) by [@L-1124](https://github.com/L-1124)
12
+
13
+ ### 贡献者
14
+
15
+ * @L-1124
16
+ * @github-actions[bot]
17
+
18
+ ## [[0.6.0](https://github.com/L-1124/QQMusicApi/compare/v0.5.3..v0.6.0)] - 2026-05-09
19
+
20
+ ### Bug 修复
21
+
22
+ * **(login)** 修改手机验证码鉴权参数类型为字符串 ([5cfebc6](https://github.com/L-1124/QQMusicApi/commit/5cfebc67cc816e0ca1930f76386fc599215496af)) by [@L-1124](https://github.com/L-1124)
23
+
24
+ ### 功能更新
25
+
26
+ * **(client)** 添加连接重试机制参数 ([38134a7](https://github.com/L-1124/QQMusicApi/commit/38134a74cfe9d3b623c46d74136687d9a2731848)) by [@L-1124](https://github.com/L-1124)
27
+ * **(core)** [**breaking**] 移除 `RequestGroup`,支持 `Client.gather` 批量并发请求,重构请求速率限制参数 ([632b6a8](https://github.com/L-1124/QQMusicApi/commit/632b6a85fe509bbbab7bbe6e7ae702e34ef49440)) by [@L-1124](https://github.com/L-1124)
28
+ * **(web)** 添加 Docker 支持 ([b6f4dfc](https://github.com/L-1124/QQMusicApi/commit/b6f4dfc824c45cdba784904fd937495ab15065cc)) by [@L-1124](https://github.com/L-1124)
29
+ * **(web)** 添加 Web 服务路由 ([154d714](https://github.com/L-1124/QQMusicApi/commit/154d714f4bd918ab6b23814f841b154e5b388c94)) by [@L-1124](https://github.com/L-1124) in [#247](https://github.com/L-1124/QQMusicApi/pull/247)
30
+ * [**breaking**] 不再支持配置 `Client` 部分参数并且修改并且初始化参数名 ([c4a7001](https://github.com/L-1124/QQMusicApi/commit/c4a7001a007128eba76b89004504b58be60c6f84)) by [@L-1124](https://github.com/L-1124)
31
+
32
+ ### 功能重构
33
+
34
+ * **(algorithms)** [**breaking**] 移除 Web 端请求签名模块 ([21b3179](https://github.com/L-1124/QQMusicApi/commit/21b31794d89efebbf58505a4310dc95ecd9ba7f0)) by [@L-1124](https://github.com/L-1124)
35
+ * **(core)** 重构 API 客户端, 迁移 httpx → niquests ([fc4f1b7](https://github.com/L-1124/QQMusicApi/commit/fc4f1b77f2f001331f38a178a2a16e84279a4549)) by [@L-1124](https://github.com/L-1124)
36
+ * **(exception)** [**breaking**] 重构异常体系 ([3f7ef32](https://github.com/L-1124/QQMusicApi/commit/3f7ef3239f90cbcf3bf8ba64ab1561e89683df52)) by [@L-1124](https://github.com/L-1124)
37
+
38
+ ### 贡献者
39
+
40
+ * @L-1124
41
+ * @github-actions[bot]
42
+
2
43
  ## [[0.5.3](https://github.com/L-1124/QQMusicApi/compare/v0.5.2..v0.5.3)] - 2026-05-01
3
44
 
4
45
  ### Bug 修复
@@ -94,3 +94,15 @@ async def main():
94
94
  asyncio.run(_main())
95
95
 
96
96
  ```
97
+
98
+ ## 设备信息
99
+
100
+ 可通过 `device_path` 参数指定设备信息文件的路径进行持久化存储:
101
+
102
+ ```python
103
+ client = Client(device_path="device.json")
104
+ ```
105
+
106
+ 不传 `device_path` 则仅在内存维护设备状态,重启后丢失。
107
+
108
+ `Client.credential` 更改时设备信息保持不变。
@@ -0,0 +1,74 @@
1
+ """私信只读接口示例."""
2
+
3
+ import asyncio
4
+
5
+ from qqmusic_api import Client, Credential
6
+ from qqmusic_api.models.private_message import PrivateMessageSession
7
+
8
+ MUSICID = 0
9
+ MUSICKEY = ""
10
+
11
+ credential = Credential(musicid=MUSICID, musickey=MUSICKEY)
12
+
13
+
14
+ def format_session_preview(index: int, session: PrivateMessageSession) -> str:
15
+ """格式化私信会话预览."""
16
+ user = session.user
17
+ latest_message = session.new_msg
18
+ metadata = latest_message.meta_data if latest_message else None
19
+ latest_text = metadata.content if metadata and metadata.content else latest_message.tips if latest_message else ""
20
+ return (
21
+ f"{index}. session_id={session.session_id}, "
22
+ f"uin={user.uin if user else ''}, "
23
+ f"nick={user.nick if user else ''}, "
24
+ f"unread={session.new_msg_cnt}, "
25
+ f"last={latest_text or '<无文本预览>'}"
26
+ )
27
+
28
+
29
+ async def main() -> None:
30
+ """运行私信只读接口示例."""
31
+ async with Client(credential) as client:
32
+ session_result = await client.private_message.get_sessions(size=10)
33
+ print(f"会话列表: has_more={session_result.has_more}, count={len(session_result.sessions)}")
34
+
35
+ if not session_result.sessions:
36
+ print("当前账号没有可展示的私信会话。")
37
+ return
38
+
39
+ print("可用会话:")
40
+ for index, session in enumerate(session_result.sessions, start=1):
41
+ print(format_session_preview(index, session))
42
+
43
+ first_session = session_result.sessions[0]
44
+ target_user = first_session.user
45
+ message_result = await client.private_message.get_messages(
46
+ session_id=first_session.session_id,
47
+ user_id=target_user.uin if target_user else "",
48
+ size=20,
49
+ )
50
+ print(f"首个会话消息: has_more={message_result.has_more}, count={len(message_result.messages)}")
51
+
52
+ entries_result = await client.private_message.get_chat_entries(
53
+ [1, 2],
54
+ from_user_type=0,
55
+ user_id=target_user.uin if target_user else None,
56
+ )
57
+ entry_count = sum(len(entries) for entries in entries_result.entries.values())
58
+ print(f"聊天入口: ret_code={entries_result.ret_code}, entry_count={entry_count}")
59
+
60
+ media_msg_ids = [
61
+ message.id for message in message_result.messages if message.id and message.msg_type in {2, 3, 5, 6}
62
+ ]
63
+ if not media_msg_ids:
64
+ print("当前会话没有可用于预览详情查询的图片或视频消息。")
65
+ return
66
+
67
+ detail_result = await client.private_message.get_media_message_details(
68
+ first_session.session_id,
69
+ media_msg_ids[:5],
70
+ )
71
+ print(f"媒体消息详情: count={len(detail_result.msg_ids)}")
72
+
73
+
74
+ asyncio.run(main())
@@ -18,7 +18,7 @@ from .core.exceptions import (
18
18
  from .core.versioning import Platform
19
19
  from .models.request import Credential
20
20
 
21
- __version__ = "0.6.0"
21
+ __version__ = "0.6.2"
22
22
 
23
23
  __all__ = [
24
24
  "ApiDataError",
@@ -1,11 +1,12 @@
1
1
  """API 客户端核心实现. 整合网络传输、鉴权与业务模块访问."""
2
2
 
3
- import uuid
4
3
  from collections import defaultdict
5
4
  from functools import cached_property
6
5
  from typing import TYPE_CHECKING, Any, Literal, cast, overload
7
6
 
7
+ import anyio
8
8
  from niquests import AsyncSession, AsyncTokenBucketLimiter, PreparedRequest
9
+ from niquests.exceptions import RequestException
9
10
  from niquests.models import Response
10
11
  from niquests.typing import AsyncHookType, ProxyType, TLSClientCertType, TLSVerifyType
11
12
  from tarsio import TarsDict
@@ -21,6 +22,7 @@ from .exceptions import (
21
22
  CredentialExpiredError,
22
23
  GlobalApiError,
23
24
  HTTPError,
25
+ NetworkError,
24
26
  RatelimitedError,
25
27
  )
26
28
  from .request import Request, RequestResultT, _build_result
@@ -32,6 +34,7 @@ if TYPE_CHECKING:
32
34
  from ..modules.login import LoginApi
33
35
  from ..modules.lyric import LyricApi
34
36
  from ..modules.mv import MvApi
37
+ from ..modules.private_message import PrivateMessageApi
35
38
  from ..modules.recommend import RecommendApi
36
39
  from ..modules.search import SearchApi
37
40
  from ..modules.singer import SingerApi
@@ -98,8 +101,9 @@ class Client:
98
101
 
99
102
  self._device_store = DeviceManager(device_path)
100
103
 
101
- self._guid = uuid.uuid4().hex
102
104
  self._version_policy: VersionPolicy = DEFAULT_VERSION_POLICY
105
+ self._session_lock = anyio.Lock()
106
+ self._session_initialized = False
103
107
  self._qimei_manager = QimeiManager(
104
108
  device_store=self._device_store,
105
109
  app_version=self._version_policy.get_qimei_app_version(),
@@ -107,6 +111,62 @@ class Client:
107
111
  session=self._session,
108
112
  )
109
113
 
114
+ async def _ensure_session(self) -> None:
115
+ async with self._session_lock:
116
+ if self._session_initialized:
117
+ return
118
+ device = await self._device_store.get_device()
119
+ if device.session_uid and device.session_sid:
120
+ self._session_initialized = True
121
+ return
122
+
123
+ finalcomm = self._version_policy.build_comm(
124
+ platform=Platform.ANDROID,
125
+ credential=self.credential,
126
+ device=device,
127
+ qimei=cast("dict[str, str]", await self._qimei_manager.get_cached()),
128
+ guid=device.open_udid,
129
+ )
130
+ payload: dict[str, Any] = {
131
+ "comm": finalcomm,
132
+ "req_0": {
133
+ "module": "music.getSession.session",
134
+ "method": "GetSession",
135
+ "param": {
136
+ "uid": device.session_uid or "",
137
+ "vkey": 0,
138
+ "caller": 0,
139
+ },
140
+ },
141
+ }
142
+ user_agent = await self._get_user_agent(Platform.ANDROID)
143
+ try:
144
+ resp = await self._session.post(
145
+ "https://u.y.qq.com/cgi-bin/musicu.fcg",
146
+ json=payload,
147
+ headers={"User-Agent": user_agent},
148
+ proxies=self.proxies,
149
+ hooks=self.hooks,
150
+ cert=self.cert,
151
+ verify=self.verify,
152
+ )
153
+ await self._session.gather(resp)
154
+ except RequestException as exc:
155
+ raise NetworkError(str(exc)) from exc
156
+ if resp.status_code != 200:
157
+ raise HTTPError(
158
+ f"HTTP 请求状态码异常: {resp.status_code}",
159
+ status_code=cast("int", resp.status_code),
160
+ )
161
+
162
+ resp_data = resp.json()
163
+ session_data = resp_data["req_0"]["data"]["session"]
164
+ device.session_uid = str(session_data["uid"])
165
+ device.session_sid = session_data["sid"]
166
+ device.session_vkey = session_data.get("vkey")
167
+ await self._device_store.save_device()
168
+ self._session_initialized = True
169
+
110
170
  @cached_property
111
171
  def comment(self) -> "CommentApi":
112
172
  """评论模块."""
@@ -114,6 +174,13 @@ class Client:
114
174
 
115
175
  return CommentApi(self)
116
176
 
177
+ @cached_property
178
+ def private_message(self) -> "PrivateMessageApi":
179
+ """私信模块."""
180
+ from ..modules.private_message import PrivateMessageApi
181
+
182
+ return PrivateMessageApi(self)
183
+
117
184
  @cached_property
118
185
  def recommend(self) -> "RecommendApi":
119
186
  """推荐模块."""
@@ -253,18 +320,21 @@ class Client:
253
320
  headers["User-Agent"] = await self._get_user_agent(platform)
254
321
  kwargs["headers"] = headers
255
322
 
256
- resp = await self._session.request(
257
- method,
258
- url,
259
- **kwargs,
260
- proxies=self.proxies,
261
- hooks=self.hooks,
262
- cert=self.cert,
263
- verify=self.verify,
264
- )
265
- if not lazy:
266
- await self._session.gather(resp)
267
- return resp
323
+ try:
324
+ resp = await self._session.request(
325
+ method,
326
+ url,
327
+ **kwargs,
328
+ proxies=self.proxies,
329
+ hooks=self.hooks,
330
+ cert=self.cert,
331
+ verify=self.verify,
332
+ )
333
+ if not lazy:
334
+ await self._session.gather(resp)
335
+ return resp
336
+ except RequestException as exc:
337
+ raise NetworkError(str(exc)) from exc
268
338
 
269
339
  async def request_api(
270
340
  self,
@@ -277,39 +347,68 @@ class Client:
277
347
  lazy: bool = False,
278
348
  ) -> Response:
279
349
  """发送 API 请求."""
280
- platform = Platform.ANDROID if is_jce else platform or self.platform
350
+ target_platform = Platform.ANDROID if is_jce else platform or self.platform
351
+ if target_platform == Platform.ANDROID:
352
+ await self._ensure_session()
353
+ device = await self._device_store.get_device()
281
354
  finalcomm = self._version_policy.build_comm(
282
- platform=platform,
355
+ platform=target_platform,
283
356
  credential=credential or self.credential,
284
- device=await self._device_store.get_device(),
357
+ device=device,
285
358
  qimei=cast("dict[str, str]", await self._qimei_manager.get_cached())
286
- if platform == Platform.ANDROID
359
+ if target_platform == Platform.ANDROID
287
360
  else None,
288
- guid=self._guid,
361
+ guid=device.open_udid,
289
362
  )
290
363
  if comm:
291
364
  finalcomm.update(comm)
292
365
 
293
- user_agent = await self._get_user_agent(platform)
366
+ user_agent = await self._get_user_agent(target_platform)
367
+
368
+ try:
369
+ if is_jce:
370
+ for k, v in finalcomm.items():
371
+ if not isinstance(v, str):
372
+ finalcomm[k] = str(v)
373
+ content = JceRequest(
374
+ finalcomm,
375
+ {
376
+ f"req_{idx}": JceRequestItem(
377
+ module=req["module"],
378
+ method=req["method"],
379
+ param=TarsDict(cast("dict[int, Any]", req["param"])),
380
+ )
381
+ for idx, req in enumerate(data)
382
+ },
383
+ ).encode()
384
+ resp = await self._session.post(
385
+ "http://u.y.qq.com/cgi-bin/musicw.fcg",
386
+ data=content,
387
+ headers={"User-Agent": user_agent},
388
+ proxies=self.proxies,
389
+ hooks=self.hooks,
390
+ cert=self.cert,
391
+ verify=self.verify,
392
+ )
393
+ if not lazy:
394
+ await self._session.gather(resp)
395
+ return resp
396
+
397
+ payload: dict[str, Any] = {
398
+ "comm": finalcomm,
399
+ }
400
+ params = {}
401
+ for idx, req in enumerate(data):
402
+ payload[f"req_{idx}"] = {
403
+ "module": req["module"],
404
+ "method": req["method"],
405
+ "param": req["param"] if req["preserve_bool"] else bool_to_int(req["param"]),
406
+ }
294
407
 
295
- if is_jce:
296
- for k, v in finalcomm.items():
297
- if not isinstance(v, str):
298
- finalcomm[k] = str(v)
299
- content = JceRequest(
300
- finalcomm,
301
- {
302
- f"req_{idx}": JceRequestItem(
303
- module=req["module"],
304
- method=req["method"],
305
- param=TarsDict(cast("dict[int, Any]", req["param"])),
306
- )
307
- for idx, req in enumerate(data)
308
- },
309
- ).encode()
310
408
  resp = await self._session.post(
311
- "http://u.y.qq.com/cgi-bin/musicw.fcg",
312
- data=content,
409
+ "https://u.y.qq.com/cgi-bin/musicu.fcg",
410
+ json=payload,
411
+ params=params,
313
412
  headers={"User-Agent": user_agent},
314
413
  proxies=self.proxies,
315
414
  hooks=self.hooks,
@@ -318,33 +417,10 @@ class Client:
318
417
  )
319
418
  if not lazy:
320
419
  await self._session.gather(resp)
321
- return resp
322
-
323
- payload: dict[str, Any] = {
324
- "comm": finalcomm,
325
- }
326
- params = {}
327
- for idx, req in enumerate(data):
328
- payload[f"req_{idx}"] = {
329
- "module": req["module"],
330
- "method": req["method"],
331
- "param": req["param"] if req["preserve_bool"] else bool_to_int(req["param"]),
332
- }
333
420
 
334
- resp = await self._session.post(
335
- "https://u.y.qq.com/cgi-bin/musicu.fcg",
336
- json=payload,
337
- params=params,
338
- headers={"User-Agent": user_agent},
339
- proxies=self.proxies,
340
- hooks=self.hooks,
341
- cert=self.cert,
342
- verify=self.verify,
343
- )
344
- if not lazy:
345
- await self._session.gather(resp)
346
-
347
- return resp
421
+ return resp
422
+ except RequestException as exc:
423
+ raise NetworkError(str(exc)) from exc
348
424
 
349
425
  @overload
350
426
  async def gather(
@@ -443,7 +519,10 @@ class Client:
443
519
  )
444
520
  batch_responses.append((batch_indices, response_task))
445
521
 
446
- await self._session.gather(*(resp for _, resp in batch_responses))
522
+ try:
523
+ await self._session.gather(*(resp for _, resp in batch_responses))
524
+ except RequestException as exc:
525
+ raise NetworkError(str(exc)) from exc
447
526
 
448
527
  results: list[Any] = [_SENTINEL] * len(requests)
449
528
 
@@ -63,6 +63,7 @@ def _build_result(
63
63
  return response_model.model_validate(raw)
64
64
  return raw
65
65
 
66
+
66
67
  @dataclass(kw_only=True)
67
68
  class Request(Generic[RequestResultT]):
68
69
  """请求描述符."""
@@ -88,6 +88,8 @@ class VersionPolicy:
88
88
  device.model,
89
89
  device.version.sdk,
90
90
  device.fingerprint,
91
+ device.session_uid,
92
+ device.session_sid,
91
93
  )
92
94
  if platform == Platform.ANDROID
93
95
  else (),
@@ -113,7 +115,9 @@ class VersionPolicy:
113
115
  QIMEI36=qimei["q36"] if qimei is not None else "",
114
116
  OpenUDID=guid,
115
117
  udid=guid,
118
+ uid=device.session_uid,
116
119
  OpenUDID2=guid,
120
+ sid=device.session_sid,
117
121
  aid=device.android_id,
118
122
  os_ver=device.version.release,
119
123
  phonetype=device.model,
@@ -203,3 +203,23 @@ class MomentCommentResponse(Response):
203
203
  hint: str = Field(alias="Hint")
204
204
  prev_list_loaded: int = Field(alias="PrevListLoaded")
205
205
  map_cm_ext: dict[str, dict[str, Any]] = Field(default_factory=dict, alias="MapCmExt")
206
+
207
+
208
+ class AddCommentResponse(Response):
209
+ """添加评论接口的响应体.
210
+
211
+ Attributes:
212
+ subcode: 响应子代码.
213
+ msg: 响应消息.
214
+ id: 新增评论 ID.
215
+ parent: 父评论 ID.
216
+ floor: 楼层号.
217
+ verify_url: 验证码 URL (如果需要).
218
+ """
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")
224
+ floor: int = Field(json_schema_extra={"jsonpath": "$.Floor.Num"})
225
+ verify_url: str = Field(alias="VerifyUrl")