qqmusic-api-python 0.8.0__tar.gz → 0.8.1__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.8.0 → qqmusic_api_python-0.8.1}/PKG-INFO +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/release-notes.md +60 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/credential.md +2 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/web.md +4 -4
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/__init__.py +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/executor.py +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/transport.py +15 -3
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/request.py +4 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/user.py +1 -1
- qqmusic_api_python-0.8.1/qqmusic_api/py.typed +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/app.py +2 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/auth.py +1 -3
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/config.py +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/credential_pool.py +2 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/credential_store.py +5 -10
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/response.py +8 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/login.py +3 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/executor.py +1 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.agents/skills/pydantic/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.agents/skills/python-standards/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.agents/skills/uv-package-manager/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.dockerignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/renovate.json +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/workflows/checking.yaml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/workflows/docs.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/workflows/release.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.github/workflows/testing.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.gitignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.markdownlint-cli2.yaml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/AGENTS.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/CLAUDE.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/LICENSE +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/README.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/assets/qq-music.svg +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/cliff.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/coding.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/contributing.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/index.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/core/client.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/core/exception.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/core/pagination.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/core/request.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/core/versioning.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/album.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/base.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/comment.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/helper.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/lyric.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/mv.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/private_message.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/recommend.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/request.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/search.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/singer.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/song.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/songlist.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/sound_power.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/top.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/user.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/album.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/comment.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/helper.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/helper_utils.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/login_utils.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/lyric.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/mv.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/private_message.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/recommend.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/search.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/singer.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/song.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/songlist.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/sound_power.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/top.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/user.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/client.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/download.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/error-handling.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/pagination.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/tutorial/start.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/download_song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/get_all_sheets.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/phone_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/qrcode_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/examples/upload_file.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/prek.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/pyproject.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/sign.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/tripledes.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/client.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/endpoint.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/exceptions.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/pagination.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/request.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/response.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/core/versioning.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/_validator.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/base.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/helper.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/models/user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/_base.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/helper.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/helper_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/login_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/android_session.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/device.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/mqtt.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/qimei.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/scripts/ag-1.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/kernel_contract.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_android_session.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_client.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_device.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_endpoint.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_executor.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_login_engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_login_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_pagination.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_qimei.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_response.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_transport.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/test_user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/typing/test_build_request.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/typing/test_client_typing.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/tests/typing/test_endpoint_typing.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/uv.lock +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/.gitignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/Dockerfile +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/README.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/accounts.example.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/config.example.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/docker-compose.example.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/requirements.txt +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/run.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/cache.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/coalesce.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/deps.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/error_mapping.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/core/security.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/modules/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/_helpers.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routes/user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/adapter_registry.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/docstrings.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/modules.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/params.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/route_types.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/src/routing/router_factory.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/conftest.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_core.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_docstrings.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_engine_services.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_enums.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_route_validation.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/web/tests/test_web_routes.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/zensical.toml +0 -0
|
@@ -1,4 +1,64 @@
|
|
|
1
1
|
|
|
2
|
+
## [[0.8.0](https://github.com/L-1124/QQMusicApi/compare/v0.7.3..v0.8.0)] - 2026-10-04
|
|
3
|
+
|
|
4
|
+
### Bug 修复
|
|
5
|
+
|
|
6
|
+
* **(core)** 修复请求分页提取器的参数克隆冲突 ([e74d00b](https://github.com/L-1124/QQMusicApi/commit/e74d00b6cc6274676adb9855902234c6a9a9fcb1)) by [@L-1124](https://github.com/L-1124)
|
|
7
|
+
* **(tests)** 修复 disable_parse 移除后的测试残留并清理装饰性框线 ([39fc259](https://github.com/L-1124/QQMusicApi/commit/39fc259bdeefb3fd9f740d678a380da59e98f8da)) by [@L-1124](https://github.com/L-1124)
|
|
8
|
+
* **(web)** 未捕获异常统一返回 JSON 形状的 500 响应 ([95e5ab4](https://github.com/L-1124/QQMusicApi/commit/95e5ab44c79e7503df969677e5848b5b376efafa)) by [@L-1124](https://github.com/L-1124)
|
|
9
|
+
* **(web)** 屏蔽上游 5xx 异常细节并修正凭证快照的阻塞调用 ([2d384b0](https://github.com/L-1124/QQMusicApi/commit/2d384b021d0082dc2715c84884b36d29801ca342)) by [@L-1124](https://github.com/L-1124)
|
|
10
|
+
* **(web)** 修复池凭证过期判定与刷新未同锁导致的重复登录 ([4426508](https://github.com/L-1124/QQMusicApi/commit/442650810e9fe43d0a646b4dfc8f76696dbb55e5)) by [@L-1124](https://github.com/L-1124)
|
|
11
|
+
* **(web)** 修复调用方 Cookie 凭证被刷新并写回共享池的问题 ([4aeab21](https://github.com/L-1124/QQMusicApi/commit/4aeab216a26b873707c817457321b225a8e9d169)) by [@L-1124](https://github.com/L-1124)
|
|
12
|
+
|
|
13
|
+
### 功能更新
|
|
14
|
+
|
|
15
|
+
* **(core)** Android comm 参数补充注入 traceid ([84cc613](https://github.com/L-1124/QQMusicApi/commit/84cc6131a1cd44939d63edcdde5b3b554211bc88)) by [@L-1124](https://github.com/L-1124)
|
|
16
|
+
* **(core)** 提升全局 Android 协议版本为 20.9.0.8 ([6ce5d7e](https://github.com/L-1124/QQMusicApi/commit/6ce5d7ef6c45cea392173c33e493d98f0f3d2913)) by [@L-1124](https://github.com/L-1124)
|
|
17
|
+
* **(core)** 引入声明式 Endpoint 与无客户端网关 ([116e7fd](https://github.com/L-1124/QQMusicApi/commit/116e7fdb701f6d261ac913c3636b56f188c17ced)) by [@L-1124](https://github.com/L-1124) in [#304](https://github.com/L-1124/QQMusicApi/pull/304)
|
|
18
|
+
* **(core)** 优化 comm 参数与设备标识语义 ([072011a](https://github.com/L-1124/QQMusicApi/commit/072011a5f5d9d60181148d79981701862906d6dc)) by [@L-1124](https://github.com/L-1124)
|
|
19
|
+
* **(core)** 解耦设备缓存并完善并发控制 ([2eb66c4](https://github.com/L-1124/QQMusicApi/commit/2eb66c4c1651d6f525015c8c4bf6cb72e963c814)) by [@L-1124](https://github.com/L-1124)
|
|
20
|
+
* **(core)** [**breaking**] 引入统一请求内核与显式流式读取 ([bc09d1c](https://github.com/L-1124/QQMusicApi/commit/bc09d1c60b1ffb093255938d63fa5a197af9939f)) by [@L-1124](https://github.com/L-1124) in [#303](https://github.com/L-1124/QQMusicApi/pull/303)
|
|
21
|
+
* **(device)** 完善 Android 设备档案生成 ([4c67b50](https://github.com/L-1124/QQMusicApi/commit/4c67b50bfd4a03fde786e63b4fef0a587f3f4d12)) by [@L-1124](https://github.com/L-1124)
|
|
22
|
+
* **(device)** 优化设备生成与 QIMEI 参数 ([f207f82](https://github.com/L-1124/QQMusicApi/commit/f207f8281e63da2b4fac4391dd7dbe10c527d68d)) by [@L-1124](https://github.com/L-1124)
|
|
23
|
+
* **(sound_power)** 新增音响力与听歌榜模块及 Web 路由 ([514fc3f](https://github.com/L-1124/QQMusicApi/commit/514fc3f4f1357d0b73d95f6fd37f65ec9b6d86b7)) by [@L-1124](https://github.com/L-1124)
|
|
24
|
+
* **(web)** 缓存未命中引入请求合并与上游失败标记熔断 ([807421d](https://github.com/L-1124/QQMusicApi/commit/807421db284db78e034d3abb1948228462d7d6e5)) by [@L-1124](https://github.com/L-1124)
|
|
25
|
+
|
|
26
|
+
### 功能重构
|
|
27
|
+
|
|
28
|
+
* **(album)** 迁移 AlbumApi 端点至 @cgi_endpoint 并更新 Web 路由 ([7522cea](https://github.com/L-1124/QQMusicApi/commit/7522ceadc69c4be3a3aa188b45f9f3d752109797)) by [@L-1124](https://github.com/L-1124)
|
|
29
|
+
* **(comment)** 迁移 CommentApi 端点至 @cgi_endpoint 并更新 Web 路由 ([1565743](https://github.com/L-1124/QQMusicApi/commit/1565743030dc6bed7c2574ffc37ecb4aec93a919)) by [@L-1124](https://github.com/L-1124)
|
|
30
|
+
* **(core)** 让 CgiRequestData.param 默认为空字典并移除冗余空参数 ([55f7b01](https://github.com/L-1124/QQMusicApi/commit/55f7b014ea4b118d2520278db8ffda900a871bf2)) by [@L-1124](https://github.com/L-1124)
|
|
31
|
+
* **(core)** 彻底移除 disable_parse 选项并更新端点声明范式文档 ([5cf1a06](https://github.com/L-1124/QQMusicApi/commit/5cf1a0645f7fec6523ec05a725ab1ef161d577d1)) by [@L-1124](https://github.com/L-1124)
|
|
32
|
+
* **(core)** 彻底分离端点元数据与运行期覆盖契约 ([46a1a2e](https://github.com/L-1124/QQMusicApi/commit/46a1a2e143698bbf77e3c8b318ff334c491f4755)) by [@L-1124](https://github.com/L-1124)
|
|
33
|
+
* **(core)** 为端点装饰器增加分页类型重载与通用契约校验 ([f443e65](https://github.com/L-1124/QQMusicApi/commit/f443e6561ff5331d7023d2b538a9ace9dd1e8d6e)) by [@L-1124](https://github.com/L-1124)
|
|
34
|
+
* **(lyric)** 迁移 LyricApi 端点至 @cgi_endpoint 并更新 Web 路由 ([e12ae87](https://github.com/L-1124/QQMusicApi/commit/e12ae875b1170d3eb7fbcf7724a801439807166f)) by [@L-1124](https://github.com/L-1124)
|
|
35
|
+
* **(modules)** 为各模块端点声明 item_type 与 pager 契约并清理测试 ([8308052](https://github.com/L-1124/QQMusicApi/commit/8308052cfeee23c8bb78425d28d0bc6d997d8375)) by [@L-1124](https://github.com/L-1124)
|
|
36
|
+
* **(mv)** 迁移 MvApi 端点至 @cgi_endpoint 并更新 Web 路由 ([d2e383c](https://github.com/L-1124/QQMusicApi/commit/d2e383cbcb8e304931afc04bf76167cce365cc1d)) by [@L-1124](https://github.com/L-1124)
|
|
37
|
+
* **(recommend)** 迁移 RecommendApi 端点至 @cgi_endpoint 并更新 Web 路由 ([b97c043](https://github.com/L-1124/QQMusicApi/commit/b97c0436907e51a3ca489797e95f37de53cb1dde)) by [@L-1124](https://github.com/L-1124)
|
|
38
|
+
* **(search)** 迁移 SearchApi 端点至 @cgi_endpoint 并更新 Web 路由 ([25fe3eb](https://github.com/L-1124/QQMusicApi/commit/25fe3ebeac8f65119d7b5ab3bc3f3e681c4a1b01)) by [@L-1124](https://github.com/L-1124)
|
|
39
|
+
* **(singer)** [**breaking**] 移除 get_name_special_display 并合并至 get_info ([e293aa3](https://github.com/L-1124/QQMusicApi/commit/e293aa320f316ae9e654f90503c8a4b9186b0b91)) by [@L-1124](https://github.com/L-1124)
|
|
40
|
+
* **(singer)** 迁移 SingerApi 端点至 @cgi_endpoint 并更新 Web 路由 ([79af270](https://github.com/L-1124/QQMusicApi/commit/79af2704002f53f29024eb0aabd82d9917b798d4)) by [@L-1124](https://github.com/L-1124)
|
|
41
|
+
* **(song)** 迁移 SongApi 端点至 @cgi_endpoint 并更新 Web 路由 ([8c08868](https://github.com/L-1124/QQMusicApi/commit/8c08868cd72c633ac023635fcf4f28c58a25576d)) by [@L-1124](https://github.com/L-1124)
|
|
42
|
+
* **(songlist)** 迁移 SonglistApi 端点至 @cgi_endpoint 并更新 Web 路由 ([ba50d18](https://github.com/L-1124/QQMusicApi/commit/ba50d1864e69d91e6ce896cf47085745e93bed7a)) by [@L-1124](https://github.com/L-1124)
|
|
43
|
+
* **(top)** 迁移 TopApi 端点至 @cgi_endpoint 并更新 Web 路由 ([e2a1fee](https://github.com/L-1124/QQMusicApi/commit/e2a1fee9c7a3d5193a3b2a37b6f6b4a6bf5348de)) by [@L-1124](https://github.com/L-1124)
|
|
44
|
+
* **(user)** 迁移 UserApi 端点至 @cgi_endpoint 并更新 Web 路由 ([49921f6](https://github.com/L-1124/QQMusicApi/commit/49921f600cb9ede33990bffa1d85ef502c985896)) by [@L-1124](https://github.com/L-1124)
|
|
45
|
+
* **(web)** [**breaking**] 优化 web 端参数声明,减少冗余 ([ba95861](https://github.com/L-1124/QQMusicApi/commit/ba95861ee9391f5b5f60f89caa8d4de4af160c8b)) by [@L-1124](https://github.com/L-1124)
|
|
46
|
+
* **(web)** 在路由注册器中增加 Web 模块白名单校验 ([9b0bc65](https://github.com/L-1124/QQMusicApi/commit/9b0bc6590ab597db25b5da378298dfd2a6817881)) by [@L-1124](https://github.com/L-1124)
|
|
47
|
+
|
|
48
|
+
### 性能优化
|
|
49
|
+
|
|
50
|
+
* **(qimei)** 优化设备特征计算与静态参数缓存 ([e19fdf2](https://github.com/L-1124/QQMusicApi/commit/e19fdf2f2e8f25a2b3bb24c03b7db16b90a832f6)) by [@L-1124](https://github.com/L-1124)
|
|
51
|
+
|
|
52
|
+
### 文档更新
|
|
53
|
+
|
|
54
|
+
* **(coding)** 补充声明式端点撰写指南并更新架构概览与调用流程 ([866bffb](https://github.com/L-1124/QQMusicApi/commit/866bffb6b20ef37ba81b36c7476164f9a8b8e556)) by [@L-1124](https://github.com/L-1124)
|
|
55
|
+
* **(examples)** 新增音响力查询与听歌时长上报示例 ([a408059](https://github.com/L-1124/QQMusicApi/commit/a40805982fd53c2db45db1e878aa9ab6934141af)) by [@L-1124](https://github.com/L-1124)
|
|
56
|
+
|
|
57
|
+
### 贡献者
|
|
58
|
+
|
|
59
|
+
* @L-1124
|
|
60
|
+
* @github-actions[bot]
|
|
61
|
+
|
|
2
62
|
## [[0.7.3](https://github.com/L-1124/QQMusicApi/compare/v0.7.2..v0.7.3)] - 2026-09-13
|
|
3
63
|
|
|
4
64
|
### Bug 修复
|
|
@@ -40,7 +40,7 @@ asyncio.run(main())
|
|
|
40
40
|
|
|
41
41
|
## 单次请求覆盖
|
|
42
42
|
|
|
43
|
-
如果你不想把凭证绑定到整个 `Client
|
|
43
|
+
如果你不想把凭证绑定到整个 `Client`,需要登录态的接口都支持使用 `credential` 参数进行单次请求覆盖。
|
|
44
44
|
|
|
45
45
|
```python
|
|
46
46
|
import asyncio
|
|
@@ -76,7 +76,7 @@ async def main() -> None:
|
|
|
76
76
|
refresh_token="xxx",
|
|
77
77
|
access_token="xxx",
|
|
78
78
|
)
|
|
79
|
-
await
|
|
79
|
+
credential = await client.login.refresh_credential(credential)
|
|
80
80
|
print(credential.musicid, credential.musickey)
|
|
81
81
|
|
|
82
82
|
asyncio.run(main())
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Web 服务
|
|
1
|
+
# QQMusic API Web 服务
|
|
2
2
|
|
|
3
3
|
Web 服务将 QQMusicApi 暴露为 HTTP API。
|
|
4
4
|
|
|
5
5
|
## 1. 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
git clone https://github.com/
|
|
8
|
+
git clone https://github.com/L-1124/QQMusicApi
|
|
9
9
|
cd QQMusicApi
|
|
10
10
|
uv sync --group web
|
|
11
11
|
```
|
|
@@ -15,12 +15,12 @@ uv sync --group web
|
|
|
15
15
|
## 2. 启动服务
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
uv run
|
|
18
|
+
uv run --no-sync web/run.py
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## 3. 查看 API 文档
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
默认监听 `127.0.0.1:8080`。打开 [http://localhost:8080/docs](http://localhost:8080/docs) 可以查看所有可用接口,Swagger UI 与 ReDoc 分别位于 `/swagger` 与 `/redoc`。
|
|
24
24
|
|
|
25
25
|
## 4. 认证方式
|
|
26
26
|
|
|
@@ -126,7 +126,7 @@ class CgiExecutor:
|
|
|
126
126
|
results[call.index] = exc
|
|
127
127
|
continue
|
|
128
128
|
raise
|
|
129
|
-
if request.require_login and not
|
|
129
|
+
if request.require_login and not call.scope.credential.is_valid():
|
|
130
130
|
exc = CredentialInvalidError("请求需要登录, 未提供有效的登录凭证")
|
|
131
131
|
if return_exceptions:
|
|
132
132
|
results[call.index] = exc
|
|
@@ -11,7 +11,13 @@ from niquests import AsyncSession, AsyncTokenBucketLimiter, RetryConfiguration
|
|
|
11
11
|
from niquests import PreparedRequest as NiquestsPreparedRequest
|
|
12
12
|
from niquests.exceptions import RequestException, Timeout
|
|
13
13
|
from niquests.models import Response
|
|
14
|
-
from niquests.typing import
|
|
14
|
+
from niquests.typing import (
|
|
15
|
+
AsyncHookType,
|
|
16
|
+
ProxyType,
|
|
17
|
+
TimeoutType,
|
|
18
|
+
TLSClientCertType,
|
|
19
|
+
TLSVerifyType,
|
|
20
|
+
)
|
|
15
21
|
|
|
16
22
|
from .exceptions import NetworkError, TimeoutNetworkError
|
|
17
23
|
|
|
@@ -321,6 +327,7 @@ class NiquestsTransport:
|
|
|
321
327
|
rate: float = 10,
|
|
322
328
|
capacity: float = 50,
|
|
323
329
|
connect_retries: int = 2,
|
|
330
|
+
timeout: TimeoutType = (10, 60),
|
|
324
331
|
proxies: ProxyType | None = None,
|
|
325
332
|
cert: TLSClientCertType | None = None,
|
|
326
333
|
verify: TLSVerifyType | None = None,
|
|
@@ -350,6 +357,7 @@ class NiquestsTransport:
|
|
|
350
357
|
self.cert = cert
|
|
351
358
|
self.verify = verify
|
|
352
359
|
self.hooks = hooks
|
|
360
|
+
self.timeout = timeout
|
|
353
361
|
self._max_concurrency = max_concurrency
|
|
354
362
|
self._capacity = _CapacityLimiter(max_concurrency)
|
|
355
363
|
self._closed = False
|
|
@@ -406,10 +414,12 @@ class NiquestsTransport:
|
|
|
406
414
|
try:
|
|
407
415
|
for position, request in enumerate(chunk):
|
|
408
416
|
try:
|
|
417
|
+
kwargs = dict(request.kwargs)
|
|
418
|
+
kwargs.setdefault("timeout", self.timeout)
|
|
409
419
|
response = await self._client.request(
|
|
410
420
|
request.method,
|
|
411
421
|
request.url,
|
|
412
|
-
**
|
|
422
|
+
**kwargs,
|
|
413
423
|
proxies=self.proxies,
|
|
414
424
|
hooks=self.hooks,
|
|
415
425
|
cert=self.cert,
|
|
@@ -480,10 +490,12 @@ class NiquestsTransport:
|
|
|
480
490
|
response: Response | None = None
|
|
481
491
|
try:
|
|
482
492
|
try:
|
|
493
|
+
stream_kwargs = dict(request.kwargs)
|
|
494
|
+
stream_kwargs.setdefault("timeout", self.timeout)
|
|
483
495
|
response = await self._client.request(
|
|
484
496
|
request.method,
|
|
485
497
|
request.url,
|
|
486
|
-
**
|
|
498
|
+
**stream_kwargs,
|
|
487
499
|
stream=True,
|
|
488
500
|
proxies=self.proxies,
|
|
489
501
|
hooks=self.hooks,
|
|
@@ -118,6 +118,10 @@ class Credential(BaseModel):
|
|
|
118
118
|
current_time = int(time.time())
|
|
119
119
|
return current_time >= self.musickey_create_time + self.key_expires_in
|
|
120
120
|
|
|
121
|
+
def is_valid(self) -> bool:
|
|
122
|
+
"""检查凭据是否包含可用登录态 (musicid 与 musickey 均有效)."""
|
|
123
|
+
return self.musicid > 0 and bool(self.musickey)
|
|
124
|
+
|
|
121
125
|
|
|
122
126
|
class RequestItem(TypedDict):
|
|
123
127
|
"""请求项."""
|
|
File without changes
|
|
@@ -8,6 +8,7 @@ from time import perf_counter
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
from fastapi import FastAPI, HTTPException, Request
|
|
11
|
+
from fastapi.encoders import jsonable_encoder
|
|
11
12
|
from fastapi.exceptions import RequestValidationError
|
|
12
13
|
from fastapi.middleware.cors import CORSMiddleware
|
|
13
14
|
from fastapi.responses import HTMLResponse, JSONResponse
|
|
@@ -241,6 +242,7 @@ def create_app() -> FastAPI:
|
|
|
241
242
|
return error_response(
|
|
242
243
|
status_code=422,
|
|
243
244
|
msg="请求参数校验失败",
|
|
245
|
+
data={"errors": jsonable_encoder(exc.errors())},
|
|
244
246
|
)
|
|
245
247
|
|
|
246
248
|
@app.get("/", include_in_schema=False)
|
|
@@ -9,7 +9,6 @@ from qqmusic_api import Credential, Platform
|
|
|
9
9
|
from qqmusic_api.core.engine import RequestEngine
|
|
10
10
|
|
|
11
11
|
from .credential_pool import CallerCredential, ResolvedCredential
|
|
12
|
-
from .credential_store import credential_has_login
|
|
13
12
|
from .deps import get_credential_config, get_credential_pool
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger(__name__)
|
|
@@ -34,10 +33,9 @@ async def configured_credential_for_api(
|
|
|
34
33
|
Returns:
|
|
35
34
|
调用方 Cookie 来源的凭证, 或共享凭证池来源的凭证; 只有后者允许写回共享池.
|
|
36
35
|
"""
|
|
37
|
-
if
|
|
36
|
+
if cookie_credential.is_valid():
|
|
38
37
|
logger.debug("API %s 使用 Cookie 凭证 (musicid: %s)", api_key, cookie_credential.musicid)
|
|
39
38
|
return CallerCredential(credential=cookie_credential)
|
|
40
|
-
|
|
41
39
|
credential_config = get_credential_config(request)
|
|
42
40
|
if credential_config is None or not credential_config.api_enabled(api_key):
|
|
43
41
|
logger.debug("API %s 未启用全局默认凭证或配置不存在", api_key)
|
|
@@ -149,7 +149,7 @@ class AccountConfig(BaseModel):
|
|
|
149
149
|
merged.update({key: value for key, value in data.items() if key not in {"credential", "credential_json"}})
|
|
150
150
|
return merged
|
|
151
151
|
|
|
152
|
-
def
|
|
152
|
+
def is_valid(self) -> bool:
|
|
153
153
|
"""判断是否包含可用登录凭证."""
|
|
154
154
|
return self.musicid > 0 and bool(self.musickey)
|
|
155
155
|
|
|
@@ -19,7 +19,7 @@ from qqmusic_api.core.engine import RequestEngine, RequestScope, ScopedRequestEx
|
|
|
19
19
|
from qqmusic_api.modules.login import LoginApi
|
|
20
20
|
|
|
21
21
|
from .config import AccountConfig
|
|
22
|
-
from .credential_store import CredentialStore,
|
|
22
|
+
from .credential_store import CredentialStore, credential_needs_refresh
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
@@ -192,7 +192,7 @@ class CredentialPool:
|
|
|
192
192
|
async def _check_one(musicid: int) -> None:
|
|
193
193
|
async with semaphore:
|
|
194
194
|
credential = await run_sync(self._store.get, musicid)
|
|
195
|
-
if credential is None or not
|
|
195
|
+
if credential is None or not credential.is_valid():
|
|
196
196
|
logger.warning("启动检查: 凭证 %s 不可用, 标记为无效", musicid)
|
|
197
197
|
await run_sync(self._store.mark_invalid_row, musicid)
|
|
198
198
|
return
|
|
@@ -58,7 +58,7 @@ class CredentialStore:
|
|
|
58
58
|
"""同步账号种子到状态库."""
|
|
59
59
|
with self._lock:
|
|
60
60
|
connection = self._connect()
|
|
61
|
-
valid_accounts = [account for account in accounts if account.
|
|
61
|
+
valid_accounts = [account for account in accounts if account.is_valid()]
|
|
62
62
|
toml_ids = {account.musicid for account in valid_accounts}
|
|
63
63
|
|
|
64
64
|
logger.info("同步账号种子: 总计 %d 个有效账号", len(valid_accounts))
|
|
@@ -93,7 +93,7 @@ class CredentialStore:
|
|
|
93
93
|
credentials: list[Credential] = []
|
|
94
94
|
for json_str in json_strings:
|
|
95
95
|
credential = _load_credential(json_str)
|
|
96
|
-
if credential is not None and
|
|
96
|
+
if credential is not None and credential.is_valid():
|
|
97
97
|
credentials.append(credential)
|
|
98
98
|
return credentials
|
|
99
99
|
|
|
@@ -112,7 +112,7 @@ class CredentialStore:
|
|
|
112
112
|
logger.debug("凭证不存在: musicid %s", musicid)
|
|
113
113
|
return None
|
|
114
114
|
credential = _load_credential(row[0])
|
|
115
|
-
if credential is None or not
|
|
115
|
+
if credential is None or not credential.is_valid():
|
|
116
116
|
logger.debug("凭证无效或缺少登录信息: musicid %s", musicid)
|
|
117
117
|
return None
|
|
118
118
|
logger.debug("凭证获取成功: musicid %s", musicid)
|
|
@@ -124,7 +124,7 @@ class CredentialStore:
|
|
|
124
124
|
Note:
|
|
125
125
|
该入口用于独立写入单行; ``sync_accounts`` 走单事务批量路径, 直接复用内部 ``_upsert``.
|
|
126
126
|
"""
|
|
127
|
-
if not
|
|
127
|
+
if not credential.is_valid():
|
|
128
128
|
raise ValueError("Credential 缺少 musicid 或 musickey")
|
|
129
129
|
logger.debug("写入账号种子: musicid %s", credential.musicid)
|
|
130
130
|
with self._lock:
|
|
@@ -138,7 +138,7 @@ class CredentialStore:
|
|
|
138
138
|
Returns:
|
|
139
139
|
是否命中已有行; 未命中时返回 False 且不产生任何写入.
|
|
140
140
|
"""
|
|
141
|
-
if not
|
|
141
|
+
if not credential.is_valid():
|
|
142
142
|
raise ValueError("Credential 缺少 musicid 或 musickey")
|
|
143
143
|
with self._lock:
|
|
144
144
|
connection = self._connect()
|
|
@@ -254,11 +254,6 @@ def credential_needs_refresh(credential: Credential) -> bool:
|
|
|
254
254
|
return False
|
|
255
255
|
|
|
256
256
|
|
|
257
|
-
def credential_has_login(credential: Credential) -> bool:
|
|
258
|
-
"""判断 Credential 是否包含可用登录凭证."""
|
|
259
|
-
return credential.musicid > 0 and bool(credential.musickey)
|
|
260
|
-
|
|
261
|
-
|
|
262
257
|
def _load_credential(value: str) -> Credential | None:
|
|
263
258
|
try:
|
|
264
259
|
return Credential.model_validate_json(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Web API 标准响应结构."""
|
|
2
2
|
|
|
3
|
-
from typing import Generic, TypeVar
|
|
3
|
+
from typing import Any, Generic, TypeVar
|
|
4
4
|
|
|
5
5
|
from fastapi.responses import JSONResponse
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
@@ -21,6 +21,7 @@ class ErrorResponse(BaseModel):
|
|
|
21
21
|
|
|
22
22
|
code: int = Field(default=-1, description="状态码, 错误时为 -1.")
|
|
23
23
|
msg: str = Field(description="错误说明.")
|
|
24
|
+
data: Any = Field(default=None, description="错误详情 (如参数校验失败明细), 无详情时为 null.")
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def success_response(data: T) -> ApiResponse[T]:
|
|
@@ -32,7 +33,12 @@ def error_response(
|
|
|
32
33
|
*,
|
|
33
34
|
status_code: int,
|
|
34
35
|
msg: str,
|
|
36
|
+
data: Any = None,
|
|
35
37
|
headers: dict[str, str] | None = None,
|
|
36
38
|
) -> JSONResponse:
|
|
37
39
|
"""构造标准错误响应."""
|
|
38
|
-
return JSONResponse(
|
|
40
|
+
return JSONResponse(
|
|
41
|
+
status_code=status_code,
|
|
42
|
+
content=ErrorResponse(msg=msg, data=data).model_dump(exclude_none=True),
|
|
43
|
+
headers=headers,
|
|
44
|
+
)
|
|
@@ -8,7 +8,7 @@ from ..modules.login import (
|
|
|
8
8
|
QRCodeStatusData,
|
|
9
9
|
WebQRLoginType,
|
|
10
10
|
)
|
|
11
|
-
from ..routing.route_types import AuthPolicy, WebRoute
|
|
11
|
+
from ..routing.route_types import AuthPolicy, HttpMethod, WebRoute
|
|
12
12
|
from ._helpers import P, Q, R
|
|
13
13
|
|
|
14
14
|
ROUTES: tuple[WebRoute, ...] = (
|
|
@@ -48,6 +48,7 @@ ROUTES: tuple[WebRoute, ...] = (
|
|
|
48
48
|
"phone_authcode",
|
|
49
49
|
"/login/phone/authcode",
|
|
50
50
|
PhoneAuthCodeData,
|
|
51
|
+
methods=(HttpMethod.POST,),
|
|
51
52
|
params=(
|
|
52
53
|
Q("phone", int | None, None, "明文手机号."),
|
|
53
54
|
Q("encrypted_phone", str | None, None, "加密手机号."),
|
|
@@ -59,6 +60,7 @@ ROUTES: tuple[WebRoute, ...] = (
|
|
|
59
60
|
"phone_authorize",
|
|
60
61
|
"/login/phone/authorize",
|
|
61
62
|
Credential,
|
|
63
|
+
methods=(HttpMethod.POST,),
|
|
62
64
|
params=(
|
|
63
65
|
Q("auth_code", str, description="短信验证码 (字符串, 保留前导零)."),
|
|
64
66
|
Q("phone", int | None, None, "明文手机号."),
|
|
@@ -24,7 +24,6 @@ from ..core.cache import (
|
|
|
24
24
|
)
|
|
25
25
|
from ..core.coalesce import Coalescer, Flight
|
|
26
26
|
from ..core.credential_pool import CredentialPool, PoolCredential, ResolvedCredential
|
|
27
|
-
from ..core.credential_store import credential_has_login
|
|
28
27
|
from ..core.deps import get_cache_config, get_coalescer, get_credential_pool
|
|
29
28
|
from ..core.error_mapping import HTTP_ERROR_MESSAGES, api_exception_status_code, is_upstream_failure
|
|
30
29
|
from ..core.response import ApiResponse, error_response, success_response
|
|
@@ -235,7 +234,7 @@ async def _resolve_credential(context: RouteContext, *, strict: bool = True) ->
|
|
|
235
234
|
cookie_credential,
|
|
236
235
|
platform=context.platform,
|
|
237
236
|
)
|
|
238
|
-
if not
|
|
237
|
+
if not resolved.credential.is_valid():
|
|
239
238
|
if strict:
|
|
240
239
|
logger.error("凭证解析失败: 无有效登录凭证")
|
|
241
240
|
raise HTTPException(status_code=401, detail="未提供有效的登录凭证")
|
|
File without changes
|
{qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.agents/skills/python-standards/SKILL.md
RENAMED
|
File without changes
|
{qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/.agents/skills/uv-package-manager/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/model/private_message.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/helper_utils.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.1}/docs/reference/modules/private_message.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|