qqmusic-api-python 0.6.1__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.
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/PKG-INFO +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/release-notes.md +16 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/tutorial/client.md +12 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/__init__.py +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/client.py +135 -64
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/request.py +1 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/versioning.py +4 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/recommend.py +2 -10
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/request.py +3 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/login.py +3 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/recommend.py +8 -2
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/device.py +4 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/qimei.py +33 -36
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/uv.lock +256 -256
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/run.py +23 -10
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/app.py +19 -10
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/auth.py +30 -30
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/cache.py +14 -14
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/config.py +7 -4
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/credential_store.py +24 -21
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/security.py +2 -0
- qqmusic_api_python-0.6.2/web/src/modules/comment.py +24 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/login.py +28 -5
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/mv.py +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/singer.py +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/song.py +15 -4
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/songlist.py +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/_helpers.py +9 -4
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/comment.py +33 -3
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/recommend.py +8 -2
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/params.py +27 -3
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/route_types.py +1 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/router_factory.py +2 -1
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.agents/skills/pydantic/SKILL.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.agents/skills/python-standards/SKILL.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.agents/skills/tarsio/SKILL.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.agents/skills/tarsio/references/api-reference.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.agents/skills/uv-package-manager/SKILL.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.dockerignore +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/renovate.json +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/workflows/checking.yaml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/workflows/docs.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/workflows/release.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.github/workflows/testing.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.gitignore +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/.markdownlint-cli2.yaml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/AGENTS.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/CLAUDE.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/LICENSE +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/README.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/assets/qq-music.svg +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/cliff.toml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/coding.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/contributing.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/index.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/core/client.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/core/exception.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/core/pagination.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/core/request.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/core/versioning.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/album.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/base.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/comment.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/login.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/lyric.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/mv.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/recommend.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/request.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/search.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/singer.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/song.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/songlist.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/top.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/model/user.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/album.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/comment.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/login.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/login_utils.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/lyric.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/mv.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/private_message.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/recommend.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/search.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/singer.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/song.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/songlist.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/top.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/reference/modules/user.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/tutorial/credential.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/tutorial/pagination.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/tutorial/start.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/docs/tutorial/web.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/examples/download_song.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/examples/phone_login.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/examples/private_message.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/examples/qrcode_login.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/prek.toml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/pyproject.toml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/algorithms/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/algorithms/tripledes.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/exceptions.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/core/pagination.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/album.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/base.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/comment.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/login.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/lyric.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/mv.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/private_message.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/search.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/singer.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/song.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/songlist.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/top.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/models/user.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/_base.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/album.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/comment.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/login_utils.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/lyric.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/mv.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/private_message.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/search.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/singer.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/song.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/songlist.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/top.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/modules/user.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/qqmusic_api/utils/mqtt.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/scripts/ag-1.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_comment.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_login_utils.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_private_message.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_recommend.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_singer.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_song.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_songlist.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/tests/test_user.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/.gitignore +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/Dockerfile +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/README.md +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/accounts.example.toml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/config.example.toml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/docker-compose.yml +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/deps.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/core/response.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/modules/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/album.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/login.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/lyric.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/mv.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/search.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/singer.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/song.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/songlist.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/top.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routes/user.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/__init__.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/docstrings.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/src/routing/executor.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/tests/test_web_docstrings.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/tests/test_web_enums.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/tests/test_web_route_validation.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/web/tests/test_web_routes.py +0 -0
- {qqmusic_api_python-0.6.1 → qqmusic_api_python-0.6.2}/zensical.toml +0 -0
|
@@ -1,4 +1,20 @@
|
|
|
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
|
+
|
|
2
18
|
## [[0.6.0](https://github.com/L-1124/QQMusicApi/compare/v0.5.3..v0.6.0)] - 2026-05-09
|
|
3
19
|
|
|
4
20
|
### 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` 更改时设备信息保持不变。
|
|
@@ -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
|
|
@@ -99,8 +101,9 @@ class Client:
|
|
|
99
101
|
|
|
100
102
|
self._device_store = DeviceManager(device_path)
|
|
101
103
|
|
|
102
|
-
self._guid = uuid.uuid4().hex
|
|
103
104
|
self._version_policy: VersionPolicy = DEFAULT_VERSION_POLICY
|
|
105
|
+
self._session_lock = anyio.Lock()
|
|
106
|
+
self._session_initialized = False
|
|
104
107
|
self._qimei_manager = QimeiManager(
|
|
105
108
|
device_store=self._device_store,
|
|
106
109
|
app_version=self._version_policy.get_qimei_app_version(),
|
|
@@ -108,6 +111,62 @@ class Client:
|
|
|
108
111
|
session=self._session,
|
|
109
112
|
)
|
|
110
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
|
+
|
|
111
170
|
@cached_property
|
|
112
171
|
def comment(self) -> "CommentApi":
|
|
113
172
|
"""评论模块."""
|
|
@@ -261,18 +320,21 @@ class Client:
|
|
|
261
320
|
headers["User-Agent"] = await self._get_user_agent(platform)
|
|
262
321
|
kwargs["headers"] = headers
|
|
263
322
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
|
276
338
|
|
|
277
339
|
async def request_api(
|
|
278
340
|
self,
|
|
@@ -285,39 +347,68 @@ class Client:
|
|
|
285
347
|
lazy: bool = False,
|
|
286
348
|
) -> Response:
|
|
287
349
|
"""发送 API 请求."""
|
|
288
|
-
|
|
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()
|
|
289
354
|
finalcomm = self._version_policy.build_comm(
|
|
290
|
-
platform=
|
|
355
|
+
platform=target_platform,
|
|
291
356
|
credential=credential or self.credential,
|
|
292
|
-
device=
|
|
357
|
+
device=device,
|
|
293
358
|
qimei=cast("dict[str, str]", await self._qimei_manager.get_cached())
|
|
294
|
-
if
|
|
359
|
+
if target_platform == Platform.ANDROID
|
|
295
360
|
else None,
|
|
296
|
-
guid=
|
|
361
|
+
guid=device.open_udid,
|
|
297
362
|
)
|
|
298
363
|
if comm:
|
|
299
364
|
finalcomm.update(comm)
|
|
300
365
|
|
|
301
|
-
user_agent = await self._get_user_agent(
|
|
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
|
+
}
|
|
302
407
|
|
|
303
|
-
if is_jce:
|
|
304
|
-
for k, v in finalcomm.items():
|
|
305
|
-
if not isinstance(v, str):
|
|
306
|
-
finalcomm[k] = str(v)
|
|
307
|
-
content = JceRequest(
|
|
308
|
-
finalcomm,
|
|
309
|
-
{
|
|
310
|
-
f"req_{idx}": JceRequestItem(
|
|
311
|
-
module=req["module"],
|
|
312
|
-
method=req["method"],
|
|
313
|
-
param=TarsDict(cast("dict[int, Any]", req["param"])),
|
|
314
|
-
)
|
|
315
|
-
for idx, req in enumerate(data)
|
|
316
|
-
},
|
|
317
|
-
).encode()
|
|
318
408
|
resp = await self._session.post(
|
|
319
|
-
"
|
|
320
|
-
|
|
409
|
+
"https://u.y.qq.com/cgi-bin/musicu.fcg",
|
|
410
|
+
json=payload,
|
|
411
|
+
params=params,
|
|
321
412
|
headers={"User-Agent": user_agent},
|
|
322
413
|
proxies=self.proxies,
|
|
323
414
|
hooks=self.hooks,
|
|
@@ -326,33 +417,10 @@ class Client:
|
|
|
326
417
|
)
|
|
327
418
|
if not lazy:
|
|
328
419
|
await self._session.gather(resp)
|
|
329
|
-
return resp
|
|
330
|
-
|
|
331
|
-
payload: dict[str, Any] = {
|
|
332
|
-
"comm": finalcomm,
|
|
333
|
-
}
|
|
334
|
-
params = {}
|
|
335
|
-
for idx, req in enumerate(data):
|
|
336
|
-
payload[f"req_{idx}"] = {
|
|
337
|
-
"module": req["module"],
|
|
338
|
-
"method": req["method"],
|
|
339
|
-
"param": req["param"] if req["preserve_bool"] else bool_to_int(req["param"]),
|
|
340
|
-
}
|
|
341
420
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
params=params,
|
|
346
|
-
headers={"User-Agent": user_agent},
|
|
347
|
-
proxies=self.proxies,
|
|
348
|
-
hooks=self.hooks,
|
|
349
|
-
cert=self.cert,
|
|
350
|
-
verify=self.verify,
|
|
351
|
-
)
|
|
352
|
-
if not lazy:
|
|
353
|
-
await self._session.gather(resp)
|
|
354
|
-
|
|
355
|
-
return resp
|
|
421
|
+
return resp
|
|
422
|
+
except RequestException as exc:
|
|
423
|
+
raise NetworkError(str(exc)) from exc
|
|
356
424
|
|
|
357
425
|
@overload
|
|
358
426
|
async def gather(
|
|
@@ -451,7 +519,10 @@ class Client:
|
|
|
451
519
|
)
|
|
452
520
|
batch_responses.append((batch_indices, response_task))
|
|
453
521
|
|
|
454
|
-
|
|
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
|
|
455
526
|
|
|
456
527
|
results: list[Any] = [_SENTINEL] * len(requests)
|
|
457
528
|
|
|
@@ -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,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from pydantic import AliasChoices, Field
|
|
5
|
+
from pydantic import AliasChoices, Field
|
|
6
6
|
|
|
7
7
|
from .base import Song, SongList
|
|
8
8
|
from .request import Response
|
|
@@ -69,15 +69,7 @@ class GuessRecommendResponse(Response):
|
|
|
69
69
|
songs: 推荐歌曲列表.
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
|
-
songs: list[Song] = Field(default_factory=list, alias="
|
|
73
|
-
|
|
74
|
-
@model_validator(mode="before")
|
|
75
|
-
@classmethod
|
|
76
|
-
def _normalize_tracks(cls, data: Any) -> Any:
|
|
77
|
-
"""将猜你喜欢响应规整为稳定的歌曲列表载荷."""
|
|
78
|
-
if isinstance(data, dict) and "Tracks" not in data:
|
|
79
|
-
return {"Tracks": []}
|
|
80
|
-
return data
|
|
72
|
+
songs: list[Song] = Field(default_factory=list, alias="tracks")
|
|
81
73
|
|
|
82
74
|
|
|
83
75
|
class RadarRecommendResponse(Response):
|
|
@@ -45,6 +45,8 @@ class CommonParams(BaseModel):
|
|
|
45
45
|
udid: str | None = Field(default=None)
|
|
46
46
|
aid: str | None = Field(default=None)
|
|
47
47
|
guid: str | None = Field(default=None)
|
|
48
|
+
uid: str | None = Field(default=None)
|
|
49
|
+
sid: str | None = Field(default=None)
|
|
48
50
|
os_ver: str | None = Field(default=None)
|
|
49
51
|
phonetype: str | None = Field(default=None)
|
|
50
52
|
devicelevel: str | None = Field(default=None)
|
|
@@ -102,6 +104,7 @@ class Credential(BaseModel):
|
|
|
102
104
|
if "loginType" in data or "login_type" in data:
|
|
103
105
|
return data
|
|
104
106
|
|
|
107
|
+
# TODO: 修复没有 musickey 时误判为 QQ 登录的问题
|
|
105
108
|
musickey = data.get("musickey", "")
|
|
106
109
|
inferred_login_type = 1 if isinstance(musickey, str) and musickey.startswith("W_X") else 2
|
|
107
110
|
return {**data, "loginType": inferred_login_type}
|
|
@@ -10,7 +10,7 @@ from typing import Any
|
|
|
10
10
|
from uuid import uuid4
|
|
11
11
|
|
|
12
12
|
import anyio
|
|
13
|
-
from niquests.exceptions import HTTPError, ReadTimeout
|
|
13
|
+
from niquests.exceptions import HTTPError, ReadTimeout, RequestException
|
|
14
14
|
|
|
15
15
|
from ..core import (
|
|
16
16
|
ApiDataError,
|
|
@@ -501,6 +501,8 @@ class LoginApi(ApiModule):
|
|
|
501
501
|
)
|
|
502
502
|
except ReadTimeout:
|
|
503
503
|
return QRLoginResult(event=QRCodeLoginEvents.SCAN)
|
|
504
|
+
except RequestException as exc:
|
|
505
|
+
raise NetworkError(str(exc)) from exc
|
|
504
506
|
|
|
505
507
|
match = _WX_STATUS_RE.search(response.text or "")
|
|
506
508
|
if not match:
|
|
@@ -17,6 +17,7 @@ from ..models.recommend import (
|
|
|
17
17
|
RecommendNewSongResponse,
|
|
18
18
|
RecommendSonglistResponse,
|
|
19
19
|
)
|
|
20
|
+
from ..models.request import Credential
|
|
20
21
|
from ._base import ApiModule
|
|
21
22
|
|
|
22
23
|
|
|
@@ -82,8 +83,12 @@ class RecommendApi(ApiModule):
|
|
|
82
83
|
),
|
|
83
84
|
)
|
|
84
85
|
|
|
85
|
-
def get_guess_recommend(self):
|
|
86
|
-
"""获取猜你喜欢推荐.
|
|
86
|
+
def get_guess_recommend(self, *, credential: Credential | None = None):
|
|
87
|
+
"""获取猜你喜欢推荐.
|
|
88
|
+
|
|
89
|
+
Tips:
|
|
90
|
+
请求平台非 `Platform.ANDROID` 时, 需要提供有效的 `Credential`.
|
|
91
|
+
"""
|
|
87
92
|
data = {
|
|
88
93
|
"id": 99,
|
|
89
94
|
"num": 5,
|
|
@@ -96,6 +101,7 @@ class RecommendApi(ApiModule):
|
|
|
96
101
|
"get_radio_track",
|
|
97
102
|
data,
|
|
98
103
|
response_model=GuessRecommendResponse,
|
|
104
|
+
credential=credential,
|
|
99
105
|
)
|
|
100
106
|
|
|
101
107
|
def get_radar_recommend(self, page: int = 1):
|
|
@@ -85,6 +85,10 @@ class Device:
|
|
|
85
85
|
vendor_os_name: str = "qmapi"
|
|
86
86
|
qimei: str | None = None
|
|
87
87
|
qimei36: str | None = None
|
|
88
|
+
session_uid: str | None = None
|
|
89
|
+
session_sid: str | None = None
|
|
90
|
+
session_vkey: str | None = None
|
|
91
|
+
open_udid: str = field(default_factory=lambda: uuid4().hex)
|
|
88
92
|
|
|
89
93
|
|
|
90
94
|
class DeviceManager:
|
|
@@ -15,7 +15,6 @@ from cryptography.hazmat.primitives import serialization
|
|
|
15
15
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
16
16
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
17
17
|
from niquests import AsyncSession
|
|
18
|
-
from niquests.exceptions import RequestException
|
|
19
18
|
|
|
20
19
|
from .common import calc_md5
|
|
21
20
|
from .device import Device, DeviceManager
|
|
@@ -30,7 +29,6 @@ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9qaIuS0qzfR8gWkrk
|
|
|
30
29
|
-----END PUBLIC KEY-----"""
|
|
31
30
|
SECRET = "ZdJqM15EeO2zWc08"
|
|
32
31
|
APP_KEY = "0AND0HD6FE4HY80F"
|
|
33
|
-
DEFAULT_QIMEI = "6c9d3cd110abca9b16311cee10001e717614"
|
|
34
32
|
CHANNEL_ID = "10003505"
|
|
35
33
|
PACKAGE_ID = "com.tencent.qqmusic"
|
|
36
34
|
HEX_CHARS = "0123456789abcdef"
|
|
@@ -71,17 +69,12 @@ class QimeiManager:
|
|
|
71
69
|
async with self._lock:
|
|
72
70
|
if self._cache is not None:
|
|
73
71
|
return self._cache
|
|
74
|
-
|
|
75
72
|
device = await self._device_store.get_device()
|
|
76
73
|
if device.qimei and device.qimei36:
|
|
77
74
|
self._cache = QimeiResult(q16=device.qimei, q36=device.qimei36)
|
|
78
75
|
return self._cache
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
self._cache = await self._request_qimei(device)
|
|
82
|
-
except Exception:
|
|
83
|
-
self._cache = QimeiResult(q16=DEFAULT_QIMEI, q36=DEFAULT_QIMEI)
|
|
84
|
-
|
|
77
|
+
self._cache = await self._request_qimei(device)
|
|
85
78
|
with contextlib.suppress(Exception):
|
|
86
79
|
await self._device_store.apply_qimei(
|
|
87
80
|
self._cache.get("q16") or "",
|
|
@@ -90,34 +83,38 @@ class QimeiManager:
|
|
|
90
83
|
return self._cache
|
|
91
84
|
|
|
92
85
|
async def _request_qimei(self, device: Device) -> QimeiResult:
|
|
93
|
-
"""请求新的 QIMEI 信息.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
86
|
+
"""请求新的 QIMEI 信息.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
RuntimeError: QIMEI 服务端返回空内容或缺少必要字段时.
|
|
90
|
+
RequestException: 网络请求失败时.
|
|
91
|
+
json.JSONDecodeError: 响应解析失败时.
|
|
92
|
+
"""
|
|
93
|
+
_, headers, request_json = await to_thread.run_sync(
|
|
94
|
+
_build_qimei_request,
|
|
95
|
+
device,
|
|
96
|
+
self._app_version,
|
|
97
|
+
self._sdk_version,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
client = self._session
|
|
101
|
+
res = await client.post(
|
|
102
|
+
"https://api.tencentmusic.com/tme/trpc/proxy",
|
|
103
|
+
headers=headers,
|
|
104
|
+
json=request_json,
|
|
105
|
+
)
|
|
106
|
+
await self._session.gather(res)
|
|
107
|
+
res.raise_for_status()
|
|
108
|
+
|
|
109
|
+
if res.content is None:
|
|
110
|
+
raise RuntimeError("QIMEI response content is empty")
|
|
111
|
+
|
|
112
|
+
qimei_data: dict[str, str] = json.loads(json.loads(res.content).get("data", "{}")).get("data", {})
|
|
113
|
+
|
|
114
|
+
if not qimei_data or "q36" not in qimei_data or "q16" not in qimei_data:
|
|
115
|
+
raise RuntimeError(f"QIMEI response missing required fields: {qimei_data}")
|
|
116
|
+
|
|
117
|
+
return QimeiResult(q16=qimei_data["q16"], q36=qimei_data["q36"])
|
|
121
118
|
|
|
122
119
|
|
|
123
120
|
def rsa_encrypt(content: bytes) -> bytes:
|