qqmusic-api-python 0.8.0__tar.gz → 0.8.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.8.0 → qqmusic_api_python-0.8.2}/PKG-INFO +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/release-notes.md +86 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/credential.md +2 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/web.md +4 -4
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/pyproject.toml +0 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/__init__.py +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/executor.py +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/transport.py +15 -3
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/request.py +4 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/singer.py +76 -14
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/singer.py +111 -16
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/user.py +1 -1
- qqmusic_api_python-0.8.2/qqmusic_api/py.typed +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_singer.py +44 -6
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/typing/test_client_typing.py +7 -3
- qqmusic_api_python-0.8.2/uv.lock +2243 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/README.md +1 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/config.example.toml +0 -12
- qqmusic_api_python-0.8.2/web/requirements.txt +1565 -0
- qqmusic_api_python-0.8.2/web/run.py +39 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/app.py +2 -30
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/auth.py +1 -3
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/config.py +1 -15
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/credential_pool.py +2 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/credential_store.py +5 -10
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/response.py +8 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/login.py +0 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/mv.py +0 -10
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/_helpers.py +1 -37
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/login.py +3 -1
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/mv.py +3 -5
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/singer.py +8 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/adapter_registry.py +0 -5
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/executor.py +1 -2
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/route_types.py +1 -3
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/router_factory.py +3 -5
- qqmusic_api_python-0.8.0/uv.lock +0 -2083
- qqmusic_api_python-0.8.0/web/requirements.txt +0 -1407
- qqmusic_api_python-0.8.0/web/run.py +0 -105
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.agents/skills/pydantic/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.agents/skills/python-standards/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.agents/skills/uv-package-manager/SKILL.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.dockerignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/renovate.json +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/workflows/checking.yaml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/workflows/docs.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/workflows/release.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.github/workflows/testing.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.gitignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/.markdownlint-cli2.yaml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/AGENTS.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/CLAUDE.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/LICENSE +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/README.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/assets/qq-music.svg +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/cliff.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/coding.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/contributing.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/index.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/core/client.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/core/exception.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/core/pagination.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/core/request.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/core/versioning.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/album.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/base.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/comment.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/helper.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/lyric.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/mv.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/private_message.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/recommend.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/request.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/search.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/singer.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/song.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/songlist.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/sound_power.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/top.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/model/user.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/album.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/comment.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/helper.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/helper_utils.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/login_utils.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/lyric.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/mv.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/private_message.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/recommend.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/search.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/singer.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/song.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/songlist.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/sound_power.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/top.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/reference/modules/user.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/client.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/download.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/error-handling.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/login.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/pagination.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/docs/tutorial/start.md +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/download_song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/get_all_sheets.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/phone_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/qrcode_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/examples/upload_file.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/prek.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/algorithms/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/algorithms/sign.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/algorithms/tripledes.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/client.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/endpoint.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/exceptions.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/pagination.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/request.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/response.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/core/versioning.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/_validator.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/base.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/helper.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/models/user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/_base.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/helper.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/helper_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/login_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/modules/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/android_session.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/device.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/mqtt.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/qqmusic_api/utils/qimei.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/scripts/ag-1.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/kernel_contract.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_android_session.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_client.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_device.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_endpoint.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_executor.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_login_engine.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_login_utils.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_pagination.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_private_message.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_qimei.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_response.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_transport.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/test_user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/typing/test_build_request.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/tests/typing/test_endpoint_typing.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/.gitignore +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/Dockerfile +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/accounts.example.toml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/docker-compose.example.yml +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/cache.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/coalesce.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/deps.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/error_mapping.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/core/security.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/singer.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/modules/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/album.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/comment.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/lyric.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/recommend.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/search.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/song.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/songlist.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/sound_power.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/top.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routes/user.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/__init__.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/docstrings.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/modules.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/src/routing/params.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/conftest.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_core.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_docstrings.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_engine_services.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_enums.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_route_validation.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/web/tests/test_web_routes.py +0 -0
- {qqmusic_api_python-0.8.0 → qqmusic_api_python-0.8.2}/zensical.toml +0 -0
|
@@ -1,4 +1,90 @@
|
|
|
1
1
|
|
|
2
|
+
## [[0.8.1](https://github.com/L-1124/QQMusicApi/compare/v0.8.0..v0.8.1)] - 2026-10-04
|
|
3
|
+
|
|
4
|
+
### Bug 修复
|
|
5
|
+
|
|
6
|
+
* **(core)** 为传输层增加默认请求超时 ([86a2716](https://github.com/L-1124/QQMusicApi/commit/86a2716eddde166edcacfe511d4b9f2090f6a2bd)) by [@L-1124](https://github.com/L-1124)
|
|
7
|
+
* **(web)** 手机验证码路由改为 POST 提交 ([b714255](https://github.com/L-1124/QQMusicApi/commit/b714255b2a9dbef535e709659fd029b49856bbf6)) by [@L-1124](https://github.com/L-1124)
|
|
8
|
+
* **(web)** 参数校验失败响应返回错误详情 ([dee7e18](https://github.com/L-1124/QQMusicApi/commit/dee7e187c26a45b973759f56c7445da635e04740)) by [@L-1124](https://github.com/L-1124)
|
|
9
|
+
|
|
10
|
+
### 功能更新
|
|
11
|
+
|
|
12
|
+
* **(core)** 新增 Credential.is_valid 并收编登录态判断 ([415b3ae](https://github.com/L-1124/QQMusicApi/commit/415b3aef6e3f3b7fc5092653f22c1ce0d5dc31d5)) by [@L-1124](https://github.com/L-1124)
|
|
13
|
+
* **(core)** 补充 py.typed 类型标记 ([2136321](https://github.com/L-1124/QQMusicApi/commit/2136321cf19796e0738f6b91243a650dd99f76a2)) by [@L-1124](https://github.com/L-1124)
|
|
14
|
+
|
|
15
|
+
### 功能重构
|
|
16
|
+
|
|
17
|
+
* **(web)** 复用 Credential.is_valid 移除重复登录态判断 ([f8ec18d](https://github.com/L-1124/QQMusicApi/commit/f8ec18dcf9a559988ff141972b704861a3a5ef5e)) by [@L-1124](https://github.com/L-1124)
|
|
18
|
+
|
|
19
|
+
### 文档更新
|
|
20
|
+
|
|
21
|
+
* 修正刷新凭证示例与 Web 服务启动说明 ([83e743b](https://github.com/L-1124/QQMusicApi/commit/83e743b52219471ddb399e21d6a003273f2c0bd2)) by [@L-1124](https://github.com/L-1124)
|
|
22
|
+
|
|
23
|
+
### 贡献者
|
|
24
|
+
|
|
25
|
+
* @L-1124
|
|
26
|
+
* @github-actions[bot]
|
|
27
|
+
|
|
28
|
+
## [[0.8.0](https://github.com/L-1124/QQMusicApi/compare/v0.7.3..v0.8.0)] - 2026-10-04
|
|
29
|
+
|
|
30
|
+
### Bug 修复
|
|
31
|
+
|
|
32
|
+
* **(core)** 修复请求分页提取器的参数克隆冲突 ([e74d00b](https://github.com/L-1124/QQMusicApi/commit/e74d00b6cc6274676adb9855902234c6a9a9fcb1)) by [@L-1124](https://github.com/L-1124)
|
|
33
|
+
* **(tests)** 修复 disable_parse 移除后的测试残留并清理装饰性框线 ([39fc259](https://github.com/L-1124/QQMusicApi/commit/39fc259bdeefb3fd9f740d678a380da59e98f8da)) by [@L-1124](https://github.com/L-1124)
|
|
34
|
+
* **(web)** 未捕获异常统一返回 JSON 形状的 500 响应 ([95e5ab4](https://github.com/L-1124/QQMusicApi/commit/95e5ab44c79e7503df969677e5848b5b376efafa)) by [@L-1124](https://github.com/L-1124)
|
|
35
|
+
* **(web)** 屏蔽上游 5xx 异常细节并修正凭证快照的阻塞调用 ([2d384b0](https://github.com/L-1124/QQMusicApi/commit/2d384b021d0082dc2715c84884b36d29801ca342)) by [@L-1124](https://github.com/L-1124)
|
|
36
|
+
* **(web)** 修复池凭证过期判定与刷新未同锁导致的重复登录 ([4426508](https://github.com/L-1124/QQMusicApi/commit/442650810e9fe43d0a646b4dfc8f76696dbb55e5)) by [@L-1124](https://github.com/L-1124)
|
|
37
|
+
* **(web)** 修复调用方 Cookie 凭证被刷新并写回共享池的问题 ([4aeab21](https://github.com/L-1124/QQMusicApi/commit/4aeab216a26b873707c817457321b225a8e9d169)) by [@L-1124](https://github.com/L-1124)
|
|
38
|
+
|
|
39
|
+
### 功能更新
|
|
40
|
+
|
|
41
|
+
* **(core)** Android comm 参数补充注入 traceid ([84cc613](https://github.com/L-1124/QQMusicApi/commit/84cc6131a1cd44939d63edcdde5b3b554211bc88)) by [@L-1124](https://github.com/L-1124)
|
|
42
|
+
* **(core)** 提升全局 Android 协议版本为 20.9.0.8 ([6ce5d7e](https://github.com/L-1124/QQMusicApi/commit/6ce5d7ef6c45cea392173c33e493d98f0f3d2913)) by [@L-1124](https://github.com/L-1124)
|
|
43
|
+
* **(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)
|
|
44
|
+
* **(core)** 优化 comm 参数与设备标识语义 ([072011a](https://github.com/L-1124/QQMusicApi/commit/072011a5f5d9d60181148d79981701862906d6dc)) by [@L-1124](https://github.com/L-1124)
|
|
45
|
+
* **(core)** 解耦设备缓存并完善并发控制 ([2eb66c4](https://github.com/L-1124/QQMusicApi/commit/2eb66c4c1651d6f525015c8c4bf6cb72e963c814)) by [@L-1124](https://github.com/L-1124)
|
|
46
|
+
* **(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)
|
|
47
|
+
* **(device)** 完善 Android 设备档案生成 ([4c67b50](https://github.com/L-1124/QQMusicApi/commit/4c67b50bfd4a03fde786e63b4fef0a587f3f4d12)) by [@L-1124](https://github.com/L-1124)
|
|
48
|
+
* **(device)** 优化设备生成与 QIMEI 参数 ([f207f82](https://github.com/L-1124/QQMusicApi/commit/f207f8281e63da2b4fac4391dd7dbe10c527d68d)) by [@L-1124](https://github.com/L-1124)
|
|
49
|
+
* **(sound_power)** 新增音响力与听歌榜模块及 Web 路由 ([514fc3f](https://github.com/L-1124/QQMusicApi/commit/514fc3f4f1357d0b73d95f6fd37f65ec9b6d86b7)) by [@L-1124](https://github.com/L-1124)
|
|
50
|
+
* **(web)** 缓存未命中引入请求合并与上游失败标记熔断 ([807421d](https://github.com/L-1124/QQMusicApi/commit/807421db284db78e034d3abb1948228462d7d6e5)) by [@L-1124](https://github.com/L-1124)
|
|
51
|
+
|
|
52
|
+
### 功能重构
|
|
53
|
+
|
|
54
|
+
* **(album)** 迁移 AlbumApi 端点至 @cgi_endpoint 并更新 Web 路由 ([7522cea](https://github.com/L-1124/QQMusicApi/commit/7522ceadc69c4be3a3aa188b45f9f3d752109797)) by [@L-1124](https://github.com/L-1124)
|
|
55
|
+
* **(comment)** 迁移 CommentApi 端点至 @cgi_endpoint 并更新 Web 路由 ([1565743](https://github.com/L-1124/QQMusicApi/commit/1565743030dc6bed7c2574ffc37ecb4aec93a919)) by [@L-1124](https://github.com/L-1124)
|
|
56
|
+
* **(core)** 让 CgiRequestData.param 默认为空字典并移除冗余空参数 ([55f7b01](https://github.com/L-1124/QQMusicApi/commit/55f7b014ea4b118d2520278db8ffda900a871bf2)) by [@L-1124](https://github.com/L-1124)
|
|
57
|
+
* **(core)** 彻底移除 disable_parse 选项并更新端点声明范式文档 ([5cf1a06](https://github.com/L-1124/QQMusicApi/commit/5cf1a0645f7fec6523ec05a725ab1ef161d577d1)) by [@L-1124](https://github.com/L-1124)
|
|
58
|
+
* **(core)** 彻底分离端点元数据与运行期覆盖契约 ([46a1a2e](https://github.com/L-1124/QQMusicApi/commit/46a1a2e143698bbf77e3c8b318ff334c491f4755)) by [@L-1124](https://github.com/L-1124)
|
|
59
|
+
* **(core)** 为端点装饰器增加分页类型重载与通用契约校验 ([f443e65](https://github.com/L-1124/QQMusicApi/commit/f443e6561ff5331d7023d2b538a9ace9dd1e8d6e)) by [@L-1124](https://github.com/L-1124)
|
|
60
|
+
* **(lyric)** 迁移 LyricApi 端点至 @cgi_endpoint 并更新 Web 路由 ([e12ae87](https://github.com/L-1124/QQMusicApi/commit/e12ae875b1170d3eb7fbcf7724a801439807166f)) by [@L-1124](https://github.com/L-1124)
|
|
61
|
+
* **(modules)** 为各模块端点声明 item_type 与 pager 契约并清理测试 ([8308052](https://github.com/L-1124/QQMusicApi/commit/8308052cfeee23c8bb78425d28d0bc6d997d8375)) by [@L-1124](https://github.com/L-1124)
|
|
62
|
+
* **(mv)** 迁移 MvApi 端点至 @cgi_endpoint 并更新 Web 路由 ([d2e383c](https://github.com/L-1124/QQMusicApi/commit/d2e383cbcb8e304931afc04bf76167cce365cc1d)) by [@L-1124](https://github.com/L-1124)
|
|
63
|
+
* **(recommend)** 迁移 RecommendApi 端点至 @cgi_endpoint 并更新 Web 路由 ([b97c043](https://github.com/L-1124/QQMusicApi/commit/b97c0436907e51a3ca489797e95f37de53cb1dde)) by [@L-1124](https://github.com/L-1124)
|
|
64
|
+
* **(search)** 迁移 SearchApi 端点至 @cgi_endpoint 并更新 Web 路由 ([25fe3eb](https://github.com/L-1124/QQMusicApi/commit/25fe3ebeac8f65119d7b5ab3bc3f3e681c4a1b01)) by [@L-1124](https://github.com/L-1124)
|
|
65
|
+
* **(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)
|
|
66
|
+
* **(singer)** 迁移 SingerApi 端点至 @cgi_endpoint 并更新 Web 路由 ([79af270](https://github.com/L-1124/QQMusicApi/commit/79af2704002f53f29024eb0aabd82d9917b798d4)) by [@L-1124](https://github.com/L-1124)
|
|
67
|
+
* **(song)** 迁移 SongApi 端点至 @cgi_endpoint 并更新 Web 路由 ([8c08868](https://github.com/L-1124/QQMusicApi/commit/8c08868cd72c633ac023635fcf4f28c58a25576d)) by [@L-1124](https://github.com/L-1124)
|
|
68
|
+
* **(songlist)** 迁移 SonglistApi 端点至 @cgi_endpoint 并更新 Web 路由 ([ba50d18](https://github.com/L-1124/QQMusicApi/commit/ba50d1864e69d91e6ce896cf47085745e93bed7a)) by [@L-1124](https://github.com/L-1124)
|
|
69
|
+
* **(top)** 迁移 TopApi 端点至 @cgi_endpoint 并更新 Web 路由 ([e2a1fee](https://github.com/L-1124/QQMusicApi/commit/e2a1fee9c7a3d5193a3b2a37b6f6b4a6bf5348de)) by [@L-1124](https://github.com/L-1124)
|
|
70
|
+
* **(user)** 迁移 UserApi 端点至 @cgi_endpoint 并更新 Web 路由 ([49921f6](https://github.com/L-1124/QQMusicApi/commit/49921f600cb9ede33990bffa1d85ef502c985896)) by [@L-1124](https://github.com/L-1124)
|
|
71
|
+
* **(web)** [**breaking**] 优化 web 端参数声明,减少冗余 ([ba95861](https://github.com/L-1124/QQMusicApi/commit/ba95861ee9391f5b5f60f89caa8d4de4af160c8b)) by [@L-1124](https://github.com/L-1124)
|
|
72
|
+
* **(web)** 在路由注册器中增加 Web 模块白名单校验 ([9b0bc65](https://github.com/L-1124/QQMusicApi/commit/9b0bc6590ab597db25b5da378298dfd2a6817881)) by [@L-1124](https://github.com/L-1124)
|
|
73
|
+
|
|
74
|
+
### 性能优化
|
|
75
|
+
|
|
76
|
+
* **(qimei)** 优化设备特征计算与静态参数缓存 ([e19fdf2](https://github.com/L-1124/QQMusicApi/commit/e19fdf2f2e8f25a2b3bb24c03b7db16b90a832f6)) by [@L-1124](https://github.com/L-1124)
|
|
77
|
+
|
|
78
|
+
### 文档更新
|
|
79
|
+
|
|
80
|
+
* **(coding)** 补充声明式端点撰写指南并更新架构概览与调用流程 ([866bffb](https://github.com/L-1124/QQMusicApi/commit/866bffb6b20ef37ba81b36c7476164f9a8b8e556)) by [@L-1124](https://github.com/L-1124)
|
|
81
|
+
* **(examples)** 新增音响力查询与听歌时长上报示例 ([a408059](https://github.com/L-1124/QQMusicApi/commit/a40805982fd53c2db45db1e878aa9ab6934141af)) by [@L-1124](https://github.com/L-1124)
|
|
82
|
+
|
|
83
|
+
### 贡献者
|
|
84
|
+
|
|
85
|
+
* @L-1124
|
|
86
|
+
* @github-actions[bot]
|
|
87
|
+
|
|
2
88
|
## [[0.7.3](https://github.com/L-1124/QQMusicApi/compare/v0.7.2..v0.7.3)] - 2026-09-13
|
|
3
89
|
|
|
4
90
|
### 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
|
"""请求项."""
|
|
@@ -234,6 +234,74 @@ class VideoBrief(MV):
|
|
|
234
234
|
icon_type: int = 0
|
|
235
235
|
|
|
236
236
|
|
|
237
|
+
class TabFilterItem(Response):
|
|
238
|
+
"""Tab 筛选条目 (专辑类型 / 视频标签).
|
|
239
|
+
|
|
240
|
+
Attributes:
|
|
241
|
+
id: 筛选项 ID.
|
|
242
|
+
title: 筛选项名称.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
id: int = Field(default=-1, validation_alias=AliasChoices("ID", "id"))
|
|
246
|
+
title: str = Field(default="", validation_alias=AliasChoices("Title", "name"))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class TabFilterList(Response):
|
|
250
|
+
"""服务端下发的 Tab 筛选列表.
|
|
251
|
+
|
|
252
|
+
Attributes:
|
|
253
|
+
default_id: 默认选中项 ID.
|
|
254
|
+
items: 筛选项列表.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
default_id: int = Field(default=0, validation_alias="DefaultID")
|
|
258
|
+
items: Annotated[list[TabFilterItem], NoneToEmptyList] = Field(default_factory=list, validation_alias="ItemList")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class SongTabInfo(Response):
|
|
262
|
+
"""歌曲 Tab 内容.
|
|
263
|
+
|
|
264
|
+
Attributes:
|
|
265
|
+
songs: 歌曲列表.
|
|
266
|
+
tag_info_list: 歌曲标签信息列表 (客户端原样透传).
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
songs: Annotated[list[Song], NoneToEmptyList] = Field(
|
|
270
|
+
default_factory=list, json_schema_extra={"jsonpath": "$.List[*]"}
|
|
271
|
+
)
|
|
272
|
+
tag_info_list: Annotated[list[dict[str, Any]], NoneToEmptyList] = Field(
|
|
273
|
+
default_factory=list, validation_alias="SongTagInfoList"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class AlbumTabInfo(Response):
|
|
278
|
+
"""专辑 Tab 内容.
|
|
279
|
+
|
|
280
|
+
Attributes:
|
|
281
|
+
type_list: 服务端下发的专辑分类筛选列表.
|
|
282
|
+
albums: 专辑列表.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
type_list: TabFilterList = Field(default_factory=TabFilterList, validation_alias="TypeList")
|
|
286
|
+
albums: Annotated[list[AlbumBrief], NoneToEmptyList] = Field(
|
|
287
|
+
default_factory=list, json_schema_extra={"jsonpath": "$.AlbumList[*]"}
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class VideoTabInfo(Response):
|
|
292
|
+
"""视频 Tab 内容.
|
|
293
|
+
|
|
294
|
+
Attributes:
|
|
295
|
+
videos: 视频列表.
|
|
296
|
+
tag_list: 服务端下发的视频标签筛选列表.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
videos: Annotated[list[VideoBrief], NoneToEmptyList] = Field(
|
|
300
|
+
default_factory=list, json_schema_extra={"jsonpath": "$.VideoList[*]"}
|
|
301
|
+
)
|
|
302
|
+
tag_list: Annotated[list[TabFilterItem], NoneToEmptyList] = Field(default_factory=list, validation_alias="TagList")
|
|
303
|
+
|
|
304
|
+
|
|
237
305
|
class HomepageTabDetailResponse(Response):
|
|
238
306
|
"""歌手主页标签详情响应.
|
|
239
307
|
|
|
@@ -241,12 +309,12 @@ class HomepageTabDetailResponse(Response):
|
|
|
241
309
|
tab_id: 当前标签页 ID.
|
|
242
310
|
has_more: 是否还有更多结果.
|
|
243
311
|
need_show_tab: 是否需要展示标签.
|
|
244
|
-
order:
|
|
312
|
+
order: 服务端归一化后的排序值.
|
|
245
313
|
tab_list: 标签页元信息列表.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
314
|
+
song_tab: 歌曲 Tab 内容.
|
|
315
|
+
album_tab: 专辑 Tab 内容.
|
|
316
|
+
video_tab: 视频 Tab 内容.
|
|
317
|
+
introduction_tab: 简介 Tab 内容.
|
|
250
318
|
"""
|
|
251
319
|
|
|
252
320
|
tab_id: str = Field(default="", validation_alias="TabID")
|
|
@@ -254,19 +322,13 @@ class HomepageTabDetailResponse(Response):
|
|
|
254
322
|
need_show_tab: int = Field(default=0, validation_alias="NeedShowTab")
|
|
255
323
|
order: int = Field(default=0, validation_alias="Order")
|
|
256
324
|
tab_list: Annotated[list[TabMeta], NoneToEmptyList] = Field(default_factory=list, validation_alias="TabList")
|
|
325
|
+
song_tab: SongTabInfo = Field(default_factory=SongTabInfo, validation_alias="SongTab")
|
|
326
|
+
album_tab: AlbumTabInfo = Field(default_factory=AlbumTabInfo, validation_alias="AlbumTab")
|
|
327
|
+
video_tab: VideoTabInfo = Field(default_factory=VideoTabInfo, validation_alias="VideoTab")
|
|
257
328
|
introduction_tab: Annotated[list[dict[str, Any]], NoneToEmptyList] = Field(
|
|
258
329
|
default_factory=list,
|
|
259
330
|
json_schema_extra={"jsonpath": "$.IntroductionTab.List"},
|
|
260
331
|
)
|
|
261
|
-
song_tab: Annotated[list[Song], NoneToEmptyList] = Field(
|
|
262
|
-
default_factory=list, json_schema_extra={"jsonpath": "$.SongTab.List[*]"}
|
|
263
|
-
)
|
|
264
|
-
album_tab: Annotated[list[AlbumBrief], NoneToEmptyList] = Field(
|
|
265
|
-
default_factory=list, json_schema_extra={"jsonpath": "$.AlbumTab.AlbumList[*]"}
|
|
266
|
-
)
|
|
267
|
-
video_tab: Annotated[list[VideoBrief], NoneToEmptyList] = Field(
|
|
268
|
-
default_factory=list, json_schema_extra={"jsonpath": "$.VideoTab.VideoList[*]"}
|
|
269
|
-
)
|
|
270
332
|
|
|
271
333
|
|
|
272
334
|
class HomepageHeaderResponse(Response):
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""歌手相关 API."""
|
|
2
2
|
|
|
3
3
|
from enum import Enum, IntEnum
|
|
4
|
-
from typing import cast
|
|
4
|
+
from typing import Any, Literal, TypedDict, cast, overload
|
|
5
5
|
|
|
6
|
-
from ..core import Platform
|
|
6
|
+
from ..core import PaginatedCgiRequest, Platform
|
|
7
7
|
from ..core.endpoint import CgiRequestData, cgi_endpoint
|
|
8
8
|
from ..core.pagination import (
|
|
9
9
|
MultiFieldContinuationStrategy,
|
|
@@ -91,6 +91,17 @@ class TabType(Enum):
|
|
|
91
91
|
self.tab_name = tab_name
|
|
92
92
|
|
|
93
93
|
|
|
94
|
+
class OrderType(IntEnum):
|
|
95
|
+
"""歌手主页条目排序类型.
|
|
96
|
+
|
|
97
|
+
+ LATEST: 最新 / 按时间排序 (0)
|
|
98
|
+
+ HOT: 最热 / 按热度排序 (1)
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
LATEST = 0
|
|
102
|
+
HOT = 1
|
|
103
|
+
|
|
104
|
+
|
|
94
105
|
class IndexType(IntEnum):
|
|
95
106
|
"""首字母索引枚举."""
|
|
96
107
|
|
|
@@ -124,6 +135,30 @@ class IndexType(IntEnum):
|
|
|
124
135
|
HASH = 27
|
|
125
136
|
|
|
126
137
|
|
|
138
|
+
class AlbumExtension(TypedDict):
|
|
139
|
+
"""专辑 Tab 扩展参数.
|
|
140
|
+
|
|
141
|
+
Attributes:
|
|
142
|
+
IsNeedFilterType: 是否下发分类筛选列表.
|
|
143
|
+
FilterType: 分类筛选值.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
IsNeedFilterType: int
|
|
147
|
+
FilterType: int
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class VideoExtension(TypedDict):
|
|
151
|
+
"""视频 Tab 扩展参数.
|
|
152
|
+
|
|
153
|
+
Attributes:
|
|
154
|
+
TagID: 视频标签筛选值.
|
|
155
|
+
IsNeedTagList: 是否下发标签列表.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
TagID: int
|
|
159
|
+
IsNeedTagList: int
|
|
160
|
+
|
|
161
|
+
|
|
127
162
|
class SingerApi(ApiModule):
|
|
128
163
|
"""歌手相关 API."""
|
|
129
164
|
|
|
@@ -224,6 +259,38 @@ class SingerApi(ApiModule):
|
|
|
224
259
|
param={"SingerMid": mid},
|
|
225
260
|
)
|
|
226
261
|
|
|
262
|
+
@overload
|
|
263
|
+
def get_tab_detail(
|
|
264
|
+
self,
|
|
265
|
+
mid: str,
|
|
266
|
+
tab_type: Literal[TabType.VIDEO],
|
|
267
|
+
page: int = 1,
|
|
268
|
+
num: int = 10,
|
|
269
|
+
order: OrderType | int = OrderType.LATEST,
|
|
270
|
+
extension: VideoExtension | None = None,
|
|
271
|
+
) -> PaginatedCgiRequest[HomepageTabDetailResponse]: ...
|
|
272
|
+
|
|
273
|
+
@overload
|
|
274
|
+
def get_tab_detail(
|
|
275
|
+
self,
|
|
276
|
+
mid: str,
|
|
277
|
+
tab_type: Literal[TabType.ALBUM],
|
|
278
|
+
page: int = 1,
|
|
279
|
+
num: int = 10,
|
|
280
|
+
order: OrderType | int = OrderType.LATEST,
|
|
281
|
+
extension: AlbumExtension | None = None,
|
|
282
|
+
) -> PaginatedCgiRequest[HomepageTabDetailResponse]: ...
|
|
283
|
+
|
|
284
|
+
@overload
|
|
285
|
+
def get_tab_detail(
|
|
286
|
+
self,
|
|
287
|
+
mid: str,
|
|
288
|
+
tab_type: TabType,
|
|
289
|
+
page: int = 1,
|
|
290
|
+
num: int = 10,
|
|
291
|
+
order: OrderType | int = OrderType.LATEST,
|
|
292
|
+
) -> PaginatedCgiRequest[HomepageTabDetailResponse]: ...
|
|
293
|
+
|
|
227
294
|
@cgi_endpoint(
|
|
228
295
|
key="singer.get_tab_detail",
|
|
229
296
|
module="music.UnifiedHomepage.UnifiedHomepageSrv",
|
|
@@ -237,6 +304,8 @@ class SingerApi(ApiModule):
|
|
|
237
304
|
tab_type: TabType,
|
|
238
305
|
page: int = 1,
|
|
239
306
|
num: int = 10,
|
|
307
|
+
order: OrderType | int = OrderType.LATEST,
|
|
308
|
+
extension: VideoExtension | AlbumExtension | None = None,
|
|
240
309
|
) -> CgiRequestData:
|
|
241
310
|
"""获取歌手主页特定 Tab 的详情原始数据.
|
|
242
311
|
|
|
@@ -245,16 +314,21 @@ class SingerApi(ApiModule):
|
|
|
245
314
|
tab_type: Tab 类型.
|
|
246
315
|
page: 页码.
|
|
247
316
|
num: 返回数量.
|
|
317
|
+
order: 排序方式 (最新 / 最热).
|
|
318
|
+
extension: Tab 请求扩展参数.
|
|
248
319
|
"""
|
|
320
|
+
param: dict[str, Any] = {
|
|
321
|
+
"SingerMid": mid,
|
|
322
|
+
"IsQueryTabDetail": 1,
|
|
323
|
+
"TabID": tab_type.tab_id,
|
|
324
|
+
"PageNum": page - 1,
|
|
325
|
+
"PageSize": num,
|
|
326
|
+
"Order": int(order),
|
|
327
|
+
}
|
|
328
|
+
if extension:
|
|
329
|
+
param[tab_type.value[1].replace("Tab", "") + "Extension"] = extension
|
|
249
330
|
return CgiRequestData(
|
|
250
|
-
param=
|
|
251
|
-
"SingerMid": mid,
|
|
252
|
-
"IsQueryTabDetail": 1,
|
|
253
|
-
"TabID": tab_type.tab_id,
|
|
254
|
-
"PageNum": page - 1,
|
|
255
|
-
"PageSize": num,
|
|
256
|
-
"Order": 0,
|
|
257
|
-
},
|
|
331
|
+
param=param,
|
|
258
332
|
pager_strategy=PageStrategy[HomepageTabDetailResponse](
|
|
259
333
|
page_key="PageNum",
|
|
260
334
|
page_size=num,
|
|
@@ -324,16 +398,23 @@ class SingerApi(ApiModule):
|
|
|
324
398
|
response_model=SingerSongListResponse,
|
|
325
399
|
item_type=Song,
|
|
326
400
|
)
|
|
327
|
-
def get_songs_list(
|
|
401
|
+
def get_songs_list(
|
|
402
|
+
self,
|
|
403
|
+
mid: str,
|
|
404
|
+
num: int = 10,
|
|
405
|
+
page: int = 1,
|
|
406
|
+
order: OrderType | int = OrderType.HOT,
|
|
407
|
+
) -> CgiRequestData:
|
|
328
408
|
"""获取歌手的歌曲列表.
|
|
329
409
|
|
|
330
410
|
Args:
|
|
331
411
|
mid: 歌手 MID.
|
|
332
412
|
num: 返回歌曲数量.
|
|
333
413
|
page: 分页页码.
|
|
414
|
+
order: 排序方式.
|
|
334
415
|
"""
|
|
335
416
|
return CgiRequestData(
|
|
336
|
-
param={"singerMid": mid, "order":
|
|
417
|
+
param={"singerMid": mid, "order": int(order), "number": num, "begin": (page - 1) * num},
|
|
337
418
|
pager_strategy=OffsetStrategy[SingerSongListResponse](
|
|
338
419
|
offset_key="begin",
|
|
339
420
|
page_size_key="number",
|
|
@@ -350,16 +431,23 @@ class SingerApi(ApiModule):
|
|
|
350
431
|
response_model=SingerAlbumListResponse,
|
|
351
432
|
item_type=AlbumBrief,
|
|
352
433
|
)
|
|
353
|
-
def get_album_list(
|
|
434
|
+
def get_album_list(
|
|
435
|
+
self,
|
|
436
|
+
mid: str,
|
|
437
|
+
num: int = 10,
|
|
438
|
+
page: int = 1,
|
|
439
|
+
order: OrderType | int = OrderType.HOT,
|
|
440
|
+
) -> CgiRequestData:
|
|
354
441
|
"""获取歌手的专辑列表.
|
|
355
442
|
|
|
356
443
|
Args:
|
|
357
444
|
mid: 歌手 MID.
|
|
358
445
|
num: 返回专辑数量.
|
|
359
446
|
page: 分页页码.
|
|
447
|
+
order: 排序方式.
|
|
360
448
|
"""
|
|
361
449
|
return CgiRequestData(
|
|
362
|
-
param={"singerMid": mid, "order":
|
|
450
|
+
param={"singerMid": mid, "order": int(order), "number": num, "begin": (page - 1) * num},
|
|
363
451
|
pager_strategy=OffsetStrategy[SingerAlbumListResponse](
|
|
364
452
|
offset_key="begin",
|
|
365
453
|
page_size_key="number",
|
|
@@ -376,16 +464,23 @@ class SingerApi(ApiModule):
|
|
|
376
464
|
response_model=SingerMvListResponse,
|
|
377
465
|
item_type=VideoBrief,
|
|
378
466
|
)
|
|
379
|
-
def get_mv_list(
|
|
467
|
+
def get_mv_list(
|
|
468
|
+
self,
|
|
469
|
+
mid: str,
|
|
470
|
+
num: int = 10,
|
|
471
|
+
page: int = 1,
|
|
472
|
+
order: OrderType | int = OrderType.HOT,
|
|
473
|
+
) -> CgiRequestData:
|
|
380
474
|
"""获取歌手 MV 列表数据.
|
|
381
475
|
|
|
382
476
|
Args:
|
|
383
477
|
mid: 歌手 MID.
|
|
384
478
|
num: 返回数量.
|
|
385
479
|
page: 分页页码.
|
|
480
|
+
order: 排序方式.
|
|
386
481
|
"""
|
|
387
482
|
return CgiRequestData(
|
|
388
|
-
param={"singermid": mid, "order":
|
|
483
|
+
param={"singermid": mid, "order": order, "count": num, "start": (page - 1) * num},
|
|
389
484
|
pager_strategy=OffsetStrategy[SingerMvListResponse](
|
|
390
485
|
offset_key="start",
|
|
391
486
|
page_size_key="count",
|
|
File without changes
|