qqmusic-api-python 0.7.3__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.7.3 → qqmusic_api_python-0.8.1}/.gitignore +1 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/PKG-INFO +2 -2
- qqmusic_api_python-0.8.1/docs/coding.md +560 -0
- qqmusic_api_python-0.8.1/docs/reference/model/sound_power.md +3 -0
- qqmusic_api_python-0.8.1/docs/reference/modules/sound_power.md +3 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/release-notes.md +76 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/client.md +75 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/credential.md +11 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/pagination.md +9 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/web.md +4 -4
- qqmusic_api_python-0.8.1/examples/sound_power.py +153 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/pyproject.toml +1 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/__init__.py +3 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/core/__init__.py +29 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/client.py +292 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/endpoint.py +363 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/engine.py +378 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/core/exceptions.py +8 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/executor.py +400 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/request.py +233 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/response.py +259 -0
- qqmusic_api_python-0.8.1/qqmusic_api/core/transport.py +568 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/core/versioning.py +39 -73
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/__init__.py +2 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/request.py +5 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/singer.py +21 -25
- qqmusic_api_python-0.8.1/qqmusic_api/models/sound_power.py +219 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/__init__.py +2 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/_base.py +92 -81
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/album.py +51 -31
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/comment.py +69 -42
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/login.py +54 -52
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/login_utils.py +2 -3
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/lyric.py +43 -36
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/mv.py +29 -17
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/recommend.py +59 -34
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/search.py +59 -43
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/singer.py +91 -69
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/song.py +161 -103
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/songlist.py +31 -18
- qqmusic_api_python-0.8.1/qqmusic_api/modules/sound_power.py +191 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/top.py +21 -13
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/user.py +150 -88
- qqmusic_api_python-0.8.1/qqmusic_api/py.typed +0 -0
- qqmusic_api_python-0.8.1/qqmusic_api/utils/android_session.py +190 -0
- qqmusic_api_python-0.8.1/qqmusic_api/utils/device.py +435 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/mqtt.py +2 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/qimei.py +130 -63
- qqmusic_api_python-0.8.1/tests/kernel_contract.py +184 -0
- qqmusic_api_python-0.8.1/tests/test_android_session.py +128 -0
- qqmusic_api_python-0.8.1/tests/test_client.py +244 -0
- qqmusic_api_python-0.8.1/tests/test_device.py +86 -0
- qqmusic_api_python-0.8.1/tests/test_endpoint.py +498 -0
- qqmusic_api_python-0.8.1/tests/test_engine.py +337 -0
- qqmusic_api_python-0.8.1/tests/test_executor.py +891 -0
- qqmusic_api_python-0.8.1/tests/test_login_engine.py +246 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_pagination.py +10 -3
- qqmusic_api_python-0.8.1/tests/test_qimei.py +173 -0
- qqmusic_api_python-0.8.1/tests/test_response.py +281 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_singer.py +9 -22
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_songlist.py +2 -1
- qqmusic_api_python-0.8.1/tests/test_sound_power.py +85 -0
- qqmusic_api_python-0.8.1/tests/test_transport.py +381 -0
- qqmusic_api_python-0.8.1/tests/typing/test_build_request.py +177 -0
- qqmusic_api_python-0.8.1/tests/typing/test_client_typing.py +173 -0
- qqmusic_api_python-0.8.1/tests/typing/test_endpoint_typing.py +252 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/uv.lock +1 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/README.md +1 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/config.example.toml +6 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/app.py +60 -60
- qqmusic_api_python-0.8.1/web/src/core/auth.py +92 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/cache.py +38 -1
- qqmusic_api_python-0.8.1/web/src/core/coalesce.py +100 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/config.py +4 -1
- qqmusic_api_python-0.8.1/web/src/core/credential_pool.py +249 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/credential_store.py +51 -22
- qqmusic_api_python-0.8.1/web/src/core/deps.py +83 -0
- qqmusic_api_python-0.8.1/web/src/core/error_mapping.py +64 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/response.py +8 -2
- qqmusic_api_python-0.8.1/web/src/modules/__init__.py +10 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/modules/comment.py +5 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/modules/login.py +8 -6
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/modules/mv.py +12 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/modules/singer.py +8 -1
- qqmusic_api_python-0.8.1/web/src/modules/song.py +174 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/modules/songlist.py +8 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/__init__.py +2 -0
- qqmusic_api_python-0.8.1/web/src/routes/_helpers.py +139 -0
- qqmusic_api_python-0.8.1/web/src/routes/album.py +15 -0
- qqmusic_api_python-0.8.1/web/src/routes/comment.py +42 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/login.py +3 -1
- qqmusic_api_python-0.8.1/web/src/routes/lyric.py +30 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/mv.py +3 -5
- qqmusic_api_python-0.8.1/web/src/routes/recommend.py +30 -0
- qqmusic_api_python-0.8.1/web/src/routes/search.py +39 -0
- qqmusic_api_python-0.8.1/web/src/routes/singer.py +75 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/song.py +16 -43
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/songlist.py +5 -14
- qqmusic_api_python-0.8.1/web/src/routes/sound_power.py +60 -0
- qqmusic_api_python-0.8.1/web/src/routes/top.py +15 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routes/user.py +17 -79
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/adapter_registry.py +1 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/docstrings.py +7 -9
- qqmusic_api_python-0.8.1/web/src/routing/executor.py +268 -0
- qqmusic_api_python-0.8.1/web/src/routing/modules.py +69 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/params.py +2 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/route_types.py +38 -3
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/router_factory.py +37 -34
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/test_web_core.py +43 -5
- qqmusic_api_python-0.8.1/web/tests/test_web_engine_services.py +699 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/test_web_enums.py +24 -1
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/test_web_route_validation.py +16 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/test_web_routes.py +128 -2
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/zensical.toml +2 -0
- qqmusic_api_python-0.7.3/docs/coding.md +0 -440
- qqmusic_api_python-0.7.3/examples/singer_name_special_display.py +0 -22
- qqmusic_api_python-0.7.3/qqmusic_api/core/api_context.py +0 -227
- qqmusic_api_python-0.7.3/qqmusic_api/core/client.py +0 -531
- qqmusic_api_python-0.7.3/qqmusic_api/core/request.py +0 -371
- qqmusic_api_python-0.7.3/qqmusic_api/utils/device.py +0 -200
- qqmusic_api_python-0.7.3/tests/test_api_context.py +0 -356
- qqmusic_api_python-0.7.3/tests/test_client.py +0 -335
- qqmusic_api_python-0.7.3/web/src/core/auth.py +0 -233
- qqmusic_api_python-0.7.3/web/src/core/deps.py +0 -66
- qqmusic_api_python-0.7.3/web/src/modules/__init__.py +0 -12
- qqmusic_api_python-0.7.3/web/src/modules/song.py +0 -195
- qqmusic_api_python-0.7.3/web/src/routes/_helpers.py +0 -159
- qqmusic_api_python-0.7.3/web/src/routes/album.py +0 -18
- qqmusic_api_python-0.7.3/web/src/routes/comment.py +0 -78
- qqmusic_api_python-0.7.3/web/src/routes/lyric.py +0 -48
- qqmusic_api_python-0.7.3/web/src/routes/recommend.py +0 -45
- qqmusic_api_python-0.7.3/web/src/routes/search.py +0 -46
- qqmusic_api_python-0.7.3/web/src/routes/singer.py +0 -113
- qqmusic_api_python-0.7.3/web/src/routes/top.py +0 -18
- qqmusic_api_python-0.7.3/web/src/routing/executor.py +0 -178
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.agents/skills/pydantic/SKILL.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.agents/skills/python-standards/SKILL.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.agents/skills/uv-package-manager/SKILL.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.dockerignore +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/renovate.json +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/workflows/checking.yaml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/workflows/docs.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/workflows/release.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.github/workflows/testing.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/.markdownlint-cli2.yaml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/AGENTS.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/CLAUDE.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/LICENSE +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/README.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/assets/qq-music.svg +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/cliff.toml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/contributing.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/index.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/core/client.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/core/exception.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/core/pagination.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/core/request.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/core/versioning.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/album.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/base.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/comment.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/helper.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/login.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/lyric.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/mv.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/private_message.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/recommend.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/request.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/search.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/singer.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/song.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/songlist.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/top.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/model/user.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/album.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/comment.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/helper.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/helper_utils.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/login.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/login_utils.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/lyric.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/mv.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/private_message.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/recommend.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/search.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/singer.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/song.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/songlist.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/top.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/reference/modules/user.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/download.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/error-handling.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/login.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/docs/tutorial/start.md +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/download_song.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/get_all_sheets.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/phone_login.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/private_message.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/qrcode_login.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/examples/upload_file.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/prek.toml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/__init__.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/sign.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/algorithms/tripledes.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/core/pagination.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/_validator.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/album.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/base.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/comment.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/helper.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/login.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/lyric.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/mv.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/private_message.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/recommend.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/search.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/song.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/songlist.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/top.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/models/user.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/helper.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/helper_utils.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/modules/private_message.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/scripts/ag-1.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_comment.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_login_utils.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_private_message.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_recommend.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_song.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/tests/test_user.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/.gitignore +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/Dockerfile +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/accounts.example.toml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/docker-compose.example.yml +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/requirements.txt +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/run.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/__init__.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/core/security.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/src/routing/__init__.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/conftest.py +0 -0
- {qqmusic_api_python-0.7.3 → qqmusic_api_python-0.8.1}/web/tests/test_web_docstrings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: qqmusic-api-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: QQ音乐API封装库
|
|
5
5
|
Project-URL: documentation, https://l-1124.github.io/QQMusicApi/
|
|
6
6
|
Project-URL: homepage, https://l-1124.github.io/QQMusicApi/
|
|
@@ -35,7 +35,7 @@ Requires-Dist: niquests[speedups]>=3.18.7
|
|
|
35
35
|
Requires-Dist: orjson>=3.10.15
|
|
36
36
|
Requires-Dist: paho-mqtt>=2.1.0
|
|
37
37
|
Requires-Dist: pydantic>=2.13.3
|
|
38
|
-
Requires-Dist: typing-extensions>=4.
|
|
38
|
+
Requires-Dist: typing-extensions>=4.16.0
|
|
39
39
|
Provides-Extra: upload
|
|
40
40
|
Requires-Dist: cos-python-sdk-v5>=1.9.44; extra == 'upload'
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
# API 编写指南
|
|
2
|
+
|
|
3
|
+
`qqmusic_api` 采用 `Client + ApiModule + Request` 的结构:
|
|
4
|
+
|
|
5
|
+
* `ApiModule` 通过 `@cgi_endpoint` / `@http_endpoint` 声明接口契约,方法体只负责返回 `CgiRequestData` / `HttpRequestData` 装载运行时参数。
|
|
6
|
+
* `Request`(`CgiRequest` / `HttpRequest` 及分页子类)是由上述声明装配出的惰性描述符,被 `await` 时才真正执行。
|
|
7
|
+
* `Client` 是使用者门面,持有默认凭证与平台,并把 `Request` 交由 `RequestEngine` 执行。
|
|
8
|
+
|
|
9
|
+
## 调用流程图
|
|
10
|
+
|
|
11
|
+
### 单请求
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
模块方法
|
|
15
|
+
-> 装配为 BaseRequest 描述符 (CgiRequest / HttpRequest 及分页子类)
|
|
16
|
+
-> await request: RequestExecutor 解析身份并交由 RequestEngine 分派
|
|
17
|
+
-> CgiExecutor / HttpExecutor 装配物理请求
|
|
18
|
+
-> Transport.request(prepared) 发送并释放响应
|
|
19
|
+
-> core/response.py 统一解析
|
|
20
|
+
-> 返回原始 dict 或 Pydantic 模型
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 批量并发请求
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
多个模块方法
|
|
27
|
+
-> 各自产出 BaseRequest 描述符
|
|
28
|
+
-> Client.gather(requests)
|
|
29
|
+
-> RequestEngine.gather(calls, batch_size=...)
|
|
30
|
+
-> 按请求类型 (CGI / HTTP) 分区并行执行
|
|
31
|
+
-> CGI 条目按快照身份自动分组, 每组按 batch_size 拆分后经 send_many 批量发送
|
|
32
|
+
-> 统一解包解析每个响应项, 逐项归属错误
|
|
33
|
+
-> 按输入顺序还原结果列表
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`gather` 的分组边界由执行器按 **快照身份** 计算 (生效平台, 完整凭证, 规范化公共参数, 覆盖模式与签名)。只有这些线上环境完全一致的请求才会安全地合并到同一个批量请求中。
|
|
37
|
+
|
|
38
|
+
服务端接入不经过 `Client`: 由 `RequestEngine` 配合 `ScopedRequestExecutor` 按请求绑定身份, 单请求流程一致。
|
|
39
|
+
|
|
40
|
+
## 编写新的 API
|
|
41
|
+
|
|
42
|
+
### 添加新模块
|
|
43
|
+
|
|
44
|
+
1. 在 `qqmusic_api/modules/` 下创建新文件,例如 `foo.py`。
|
|
45
|
+
2. 定义模块类,继承 `ApiModule`。
|
|
46
|
+
3. 在 `Client` 中注册为 `@cached_property`。
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
# qqmusic_api/modules/foo.py
|
|
50
|
+
from ._base import ApiModule
|
|
51
|
+
from ..core.endpoint import cgi_endpoint, CgiRequestData
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class FooApi(ApiModule):
|
|
55
|
+
"""Foo 相关 API."""
|
|
56
|
+
|
|
57
|
+
@cgi_endpoint(
|
|
58
|
+
key="foo.get_something",
|
|
59
|
+
module="music.foo.Svc",
|
|
60
|
+
method="GetSomething",
|
|
61
|
+
)
|
|
62
|
+
def get_something(self, id: int) -> CgiRequestData:
|
|
63
|
+
"""获取某项数据."""
|
|
64
|
+
return CgiRequestData(param={"id": id})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# qqmusic_api/core/client.py
|
|
69
|
+
from functools import cached_property
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Client:
|
|
73
|
+
@cached_property
|
|
74
|
+
def foo(self) -> "FooApi":
|
|
75
|
+
from ..modules.foo import FooApi
|
|
76
|
+
|
|
77
|
+
return FooApi(self)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 添加新的请求方法
|
|
81
|
+
|
|
82
|
+
API 方法返回 `BaseRequest` 描述符对象,并不立即发起请求。可以通过声明式装饰器(`@cgi_endpoint` / `@http_endpoint`)或原生构建方法(`self._build_cgi` / `self._build_http`)构建:
|
|
83
|
+
|
|
84
|
+
#### 1. CGI 接口 (RPC 风格)
|
|
85
|
+
|
|
86
|
+
对于标准 CGI 风格的 RPC 请求,可以使用 `@cgi_endpoint` 装饰器声明端点契约,并在方法体中返回 `CgiRequestData` 装载运行时参数:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from ..core.endpoint import cgi_endpoint, CgiRequestData
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@cgi_endpoint(
|
|
93
|
+
key="song.get_detail", # 端点唯一标识
|
|
94
|
+
module="music.songDetail", # 接口所属模块
|
|
95
|
+
method="GetDetail", # 方法名
|
|
96
|
+
)
|
|
97
|
+
def get_detail(self, song_id: int) -> CgiRequestData:
|
|
98
|
+
"""获取歌曲详情."""
|
|
99
|
+
return CgiRequestData(
|
|
100
|
+
param={"songid": song_id}, # 业务参数
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
也可以使用 `self._build_cgi(...)` 构建:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
def get_detail(self, song_id: int):
|
|
108
|
+
"""获取歌曲详情."""
|
|
109
|
+
return self._build_cgi(
|
|
110
|
+
module="music.songDetail", # 接口所属模块
|
|
111
|
+
method="GetDetail", # 方法名
|
|
112
|
+
param={"songid": song_id}, # 业务参数
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### 2. HTTP 接口 (标准 HTTP)
|
|
117
|
+
|
|
118
|
+
对于标准 HTTP 请求(如直接 GET 请求、获取网页或二维码),可以使用 `@http_endpoint` 装饰器声明,并在方法体中返回 `HttpRequestData`:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from ..core.endpoint import http_endpoint, HttpRequestData
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@http_endpoint(
|
|
125
|
+
key="search.quick_search",
|
|
126
|
+
method="GET",
|
|
127
|
+
url="https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg",
|
|
128
|
+
)
|
|
129
|
+
def quick_search(self, keyword: str) -> HttpRequestData:
|
|
130
|
+
"""快速搜索 (直接返回解析后的 JSON 数据)."""
|
|
131
|
+
return HttpRequestData(
|
|
132
|
+
params={"key": keyword},
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
也可以使用 `self._build_http(...)` 构建:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
async def quick_search(self, keyword: str) -> dict[str, Any]:
|
|
140
|
+
"""快速搜索 (直接返回解析后的 JSON 数据)."""
|
|
141
|
+
resp = await self._build_http(
|
|
142
|
+
"GET",
|
|
143
|
+
"https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg",
|
|
144
|
+
params={"key": keyword},
|
|
145
|
+
)
|
|
146
|
+
return resp["data"]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `@cgi_endpoint` 声明参数说明
|
|
150
|
+
|
|
151
|
+
`@cgi_endpoint` 装饰器用于在定义期静态声明端点的核心契约:
|
|
152
|
+
|
|
153
|
+
| 参数 | 类型 | 说明 |
|
|
154
|
+
| ------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `key` | `str` | 端点唯一标识符(如 `"song.get_detail"`) |
|
|
156
|
+
| `module` | `str` | 接口所属 CGI 模块名 |
|
|
157
|
+
| `method` | `str` | CGI 方法名 |
|
|
158
|
+
| `response_model` | `type[BaseModel]` 或 `None` | 响应模型,为 None 时返回原始 dict |
|
|
159
|
+
| `item_type` | `type` 或 `None` | (仅分页)数据项模型,声明后推导为 `ItemPaginatedCgiRequest` |
|
|
160
|
+
| `pager` | `bool` | (仅分页)是否为分页端点,若 `item_type` 已填则无需填此项 |
|
|
161
|
+
| `sign` | `bool` | 是否对请求进行签名 |
|
|
162
|
+
| `require_login` | `bool` | 是否在执行时强制校验用户登录态 |
|
|
163
|
+
| `platform` | `Platform` 或 `None` | 强制指定该接口使用的目标平台 |
|
|
164
|
+
|
|
165
|
+
### `CgiRequestData` 运行时参数说明
|
|
166
|
+
|
|
167
|
+
在被 `@cgi_endpoint` 装饰的函数体内返回,用于承载每次调用的动态参数:
|
|
168
|
+
|
|
169
|
+
| 参数 | 类型 | 说明 |
|
|
170
|
+
| ------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
171
|
+
| `param` | `dict` | 请求体的业务参数 `param` 字段 |
|
|
172
|
+
| `comm` | `dict` 或 `None` | 附加的公共参数 |
|
|
173
|
+
| `override_comm` | `bool` | 为 True 时 `comm` 完全替代自动生成的参数;为 False 时合并 |
|
|
174
|
+
| `credential` | `Credential` 或 `None` | 覆盖本次请求的凭证 |
|
|
175
|
+
| `platform` | `Platform` 或 `None` | 覆盖本次请求的平台 |
|
|
176
|
+
| `preserve_bool` | `bool` 或 `None` | 是否保留布尔值原样(默认转为 0/1 整型) |
|
|
177
|
+
| `pager_strategy` | `PagerStrategy` 或 `None` | 分页策略,必须与装饰器的分页声明匹配 |
|
|
178
|
+
| `items_extractor` | `Callable` 或 `None` | (仅声明了 item_type 时需要)从单页响应对象中提取目标列表的闭包 |
|
|
179
|
+
|
|
180
|
+
### `@http_endpoint` 声明参数说明
|
|
181
|
+
|
|
182
|
+
`@http_endpoint` 装饰器用于静态声明 HTTP 接口契约:
|
|
183
|
+
|
|
184
|
+
| 参数 | 类型 | 说明 |
|
|
185
|
+
| ---------------- | -------------------------------- | -------------------------------------------------------------------- |
|
|
186
|
+
| `key` | `str` | 端点唯一标识符(如 `"search.quick_search"`) |
|
|
187
|
+
| `method` | `str` | HTTP 方法,如 `"GET"`、`"POST"` |
|
|
188
|
+
| `url` | `str` | 请求地址模板,支持 `{param}` 占位符配合 `path_params` 渲染 |
|
|
189
|
+
| `response_model` | `type[ResponseModel]` 或 `None` | 响应模型类型 |
|
|
190
|
+
| `raw` | `bool` 或 `None` | 为 True 时返回 `RawPayload`(当模型为 `RawPayload` 时默认为 True) |
|
|
191
|
+
|
|
192
|
+
### `HttpRequestData` 运行时参数说明
|
|
193
|
+
|
|
194
|
+
在被 `@http_endpoint` 装饰的函数体内返回,用于承载每次 HTTP 调用的动态参数:
|
|
195
|
+
|
|
196
|
+
| 参数 | 类型 | 说明 |
|
|
197
|
+
| ------------- | --------------------------- | --------------------------------------------------------- |
|
|
198
|
+
| `path_params` | `dict[str, Any]` 或 `None` | URL 路径参数,用于格式化 `url` 中的占位符 |
|
|
199
|
+
| `params` | `Any` 或 `None` | URL 查询参数(Query string) |
|
|
200
|
+
| `headers` | `Any` 或 `None` | HTTP 请求头 |
|
|
201
|
+
| `cookies` | `Any` 或 `None` | HTTP 请求 Cookies |
|
|
202
|
+
| `json` | `Any` 或 `None` | 请求体 JSON 数据 |
|
|
203
|
+
| `data` | `Any` 或 `None` | 原始请求体数据 |
|
|
204
|
+
| `credential` | `Credential` 或 `None` | 覆盖本次请求的凭证 |
|
|
205
|
+
| `options` | `dict[str, Any]` | 透传给底层客户端的可选参数(如 `timeout`、`files` 等) |
|
|
206
|
+
|
|
207
|
+
### `_build_cgi` 参数说明
|
|
208
|
+
|
|
209
|
+
`_build_cgi` 用于构建 CGI 风格 RPC 请求描述符:
|
|
210
|
+
|
|
211
|
+
| 参数 | 类型 | 说明 |
|
|
212
|
+
| ------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
213
|
+
| `module` | `str` | 接口所属模块名 |
|
|
214
|
+
| `method` | `str` | 方法名 |
|
|
215
|
+
| `param` | `dict` | 业务参数 |
|
|
216
|
+
| `response_model` | `type[BaseModel]` 或 `None` | 响应模型,为 None 时返回原始 dict |
|
|
217
|
+
| `comm` | `dict` 或 `None` | 附加的公共参数 |
|
|
218
|
+
| `override_comm` | `bool` | 为 True 时 `comm` 完全替代自动生成的参数;为 False 时合并 |
|
|
219
|
+
| `credential` | `Credential` 或 `None` | 覆盖本次请求的凭证 |
|
|
220
|
+
| `platform` | `Platform` 或 `None` | 覆盖本次请求的平台 |
|
|
221
|
+
| `preserve_bool` | `bool` | 是否保留布尔值原样(默认转为 0/1 整型) |
|
|
222
|
+
| `sign` | `bool` | 是否对请求进行签名 |
|
|
223
|
+
| `require_login` | `bool` | 是否在执行时强制校验用户登录态 |
|
|
224
|
+
| `pager_strategy` | `PagerStrategy` 或 `None` | 分页策略,提供后返回 `PaginatedCgiRequest`;可链式调用 `.with_extractor()` 提升为 `ItemPaginatedCgiRequest` |
|
|
225
|
+
|
|
226
|
+
### `_build_http` 参数说明
|
|
227
|
+
|
|
228
|
+
`_build_http` 用于构建标准 HTTP 请求描述符,遵循 httpx 风格参数规范,自动装配凭证 Cookies 和平台 User-Agent:
|
|
229
|
+
|
|
230
|
+
| 参数 | 类型 | 说明 |
|
|
231
|
+
| ---------------- | -------------------------------- | ------------------------------------------------------------------- |
|
|
232
|
+
| `method` | `str` | HTTP 方法,如 `"GET"`、`"POST"` |
|
|
233
|
+
| `url` | `str` | 请求地址 |
|
|
234
|
+
| `params` | `Mapping` 或 `None` | URL 查询参数 |
|
|
235
|
+
| `json` | `Any` 或 `None` | 请求体 JSON 数据 |
|
|
236
|
+
| `data` | `bytes` / `str` / `None` | 原始请求体数据(非 JSON 场景) |
|
|
237
|
+
| `headers` | `Mapping` 或 `None` | HTTP 请求头 |
|
|
238
|
+
| `cookies` | `Mapping` 或 `None` | HTTP 请求 Cookies |
|
|
239
|
+
| `credential` | `Credential` 或 `None` | 覆盖本次请求的凭证,默认使用客户端凭证 |
|
|
240
|
+
| `response_model` | `type[ResponseModel]` 或 `None` | 响应模型类型 |
|
|
241
|
+
| `raw` | `bool` | 校验 HTTP 状态并返回 `RawPayload`(值语义,无需释放) |
|
|
242
|
+
| `**options` | | 透传给底层客户端的参数(`timeout`、`allow_redirects`、`files` 等) |
|
|
243
|
+
|
|
244
|
+
常见用法:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
# GET 请求
|
|
248
|
+
req = self._build_http("GET", "https://example.com/api", params={"key": "value"})
|
|
249
|
+
|
|
250
|
+
# POST JSON
|
|
251
|
+
req = self._build_http("POST", "https://example.com/api", json={"key": "value"})
|
|
252
|
+
|
|
253
|
+
# 覆盖凭证
|
|
254
|
+
req = self._build_http("GET", "https://example.com/api", credential=my_credential)
|
|
255
|
+
|
|
256
|
+
# 返回 RawPayload 而非解析 JSON
|
|
257
|
+
req = self._build_http("GET", "https://example.com/api", raw=True)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 响应模型
|
|
261
|
+
|
|
262
|
+
### 基础用法
|
|
263
|
+
|
|
264
|
+
每个响应模型都应继承 `models.request.Response`:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from pydantic import Field
|
|
268
|
+
|
|
269
|
+
from .request import Response
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class MyResponse(Response):
|
|
273
|
+
"""我的响应模型."""
|
|
274
|
+
|
|
275
|
+
name: str
|
|
276
|
+
count: int
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`Response` 基类配置了 `frozen=True`(不可变)和 `extra="ignore"`(忽略多余字段)。
|
|
280
|
+
|
|
281
|
+
!!! warning "Pydantic 默认值规范"
|
|
282
|
+
|
|
283
|
+
定义模型时应避免使用 `None` 作为隐式兜底默认值。如果字段可选或为空,应当使用显式的空标量,或通过 `Field(default_factory=...)` 声明:
|
|
284
|
+
```python
|
|
285
|
+
class Album(Response):
|
|
286
|
+
name: str = ""
|
|
287
|
+
publish_time: str = ""
|
|
288
|
+
# 列表必须使用 default_factory
|
|
289
|
+
singers: list[Singer] = Field(default_factory=list)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### JSONPath 字段映射
|
|
293
|
+
|
|
294
|
+
可以通过 `Field(json_schema_extra={"jsonpath": ...})` 声明字段的 JSONPath 映射路径,自动从嵌套响应中提取数据:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
class SonglistMeta(Response):
|
|
298
|
+
"""歌单元数据示例."""
|
|
299
|
+
|
|
300
|
+
id: int = Field(json_schema_extra={"jsonpath": "$.result.tid"})
|
|
301
|
+
dirid: int = Field(json_schema_extra={"jsonpath": "$.result.dirId"})
|
|
302
|
+
name: str = Field(json_schema_extra={"jsonpath": "$.result.dirName"})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
对于列表字段,使用 `[*]` 通配符:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
class CommentListResponse(Response):
|
|
309
|
+
"""评论列表响应."""
|
|
310
|
+
|
|
311
|
+
comments: list[Comment] = Field(
|
|
312
|
+
default_factory=list,
|
|
313
|
+
json_schema_extra={"jsonpath": "$.commentlist[*]"},
|
|
314
|
+
)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 字段别名
|
|
318
|
+
|
|
319
|
+
Pydantic 的 `validation_alias` 支持多别名兼容:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
class Singer(Response):
|
|
323
|
+
"""歌手信息."""
|
|
324
|
+
|
|
325
|
+
id: int = Field(
|
|
326
|
+
default=-1,
|
|
327
|
+
validation_alias=AliasChoices("id", "singerID", "singerId", "SingerID"),
|
|
328
|
+
)
|
|
329
|
+
mid: str = Field(
|
|
330
|
+
default="",
|
|
331
|
+
validation_alias=AliasChoices("mid", "singerMid", "singerMID"),
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 需登录接口
|
|
336
|
+
|
|
337
|
+
需要登录的接口通过 `@cgi_endpoint` 的 `require_login=True` 参数校验凭证:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
@cgi_endpoint(
|
|
341
|
+
key="user.get_vip_info",
|
|
342
|
+
module="VipLogin.VipLoginInter",
|
|
343
|
+
method="vip_login_base",
|
|
344
|
+
response_model=UserVipInfoResponse,
|
|
345
|
+
require_login=True,
|
|
346
|
+
)
|
|
347
|
+
def get_vip_info(self, *, credential: Credential | None = None) -> CgiRequestData:
|
|
348
|
+
"""获取 VIP 信息."""
|
|
349
|
+
return CgiRequestData(credential=credential)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
> 若接口需要凭证对象的属性(如 `musicid` 等)来构建请求内联参数,
|
|
353
|
+
> 仍可通过 `credential = credential or self._client.credential` 获取并显式验证其有效性。
|
|
354
|
+
|
|
355
|
+
## 连续翻页与批次刷新
|
|
356
|
+
|
|
357
|
+
### 连续翻页
|
|
358
|
+
|
|
359
|
+
通过返回的 `CgiRequestData(pager_strategy=...)` 声明连续翻页能力,建议为策略配合显式的 Generic 标注(形如 `OffsetStrategy[GetSonglistDetailResponse]`)以确保静态类型推断。
|
|
360
|
+
若要提取特定类型的数据条目流,请在 `@cgi_endpoint(item_type=...)` 中声明目标类型,并在 `CgiRequestData` 中同时提供 `items_extractor`:
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
from ..core.pagination import OffsetStrategy
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@cgi_endpoint(
|
|
367
|
+
key="songlist.get_detail",
|
|
368
|
+
module="music.srfDissInfo.DissInfo",
|
|
369
|
+
method="CgiGetDiss",
|
|
370
|
+
response_model=GetSonglistDetailResponse,
|
|
371
|
+
item_type=Song,
|
|
372
|
+
)
|
|
373
|
+
def get_detail(self, songlist_id: int, num: int = 10, page: int = 1) -> CgiRequestData:
|
|
374
|
+
"""获取歌单详情."""
|
|
375
|
+
return CgiRequestData(
|
|
376
|
+
param={
|
|
377
|
+
"disstid": songlist_id,
|
|
378
|
+
"song_begin": num * (page - 1),
|
|
379
|
+
"song_num": num,
|
|
380
|
+
},
|
|
381
|
+
pager_strategy=OffsetStrategy[GetSonglistDetailResponse](
|
|
382
|
+
offset_key="song_begin",
|
|
383
|
+
page_size_key="song_num",
|
|
384
|
+
has_more_extractor=lambda response: bool(response.hasmore),
|
|
385
|
+
total_extractor=lambda response: response.total,
|
|
386
|
+
count_extractor=lambda response: len(response.songs),
|
|
387
|
+
),
|
|
388
|
+
items_extractor=lambda response: response.songs,
|
|
389
|
+
)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### 批次刷新 (Batch Refresh)
|
|
393
|
+
|
|
394
|
+
批次刷新(Batch Refresh)是一种针对推荐或关联接口、支持游标复位与防循环重复游标的特殊游标分页,同样通过 `pager_strategy` 声明:
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
from ..core.pagination import BatchRefreshStrategy
|
|
398
|
+
from ..models.base import MV
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@cgi_endpoint(
|
|
402
|
+
key="song.get_related_mv",
|
|
403
|
+
module="MvService.MvInfoProServer",
|
|
404
|
+
method="GetSongRelatedMv",
|
|
405
|
+
response_model=GetRelatedMvResponse,
|
|
406
|
+
item_type=RelatedMv,
|
|
407
|
+
)
|
|
408
|
+
def get_related_mv(self, songid: int, last_mvid: str | None = None) -> CgiRequestData:
|
|
409
|
+
"""获取歌曲相关 MV."""
|
|
410
|
+
return CgiRequestData(
|
|
411
|
+
param={"songid": str(songid), "songtype": 1, "lastmvid": last_mvid or 0},
|
|
412
|
+
pager_strategy=BatchRefreshStrategy[GetRelatedMvResponse](
|
|
413
|
+
refresh_key="lastmvid",
|
|
414
|
+
cursor_extractor=lambda response: response.mv[-1].id if response.mv else None,
|
|
415
|
+
has_more_extractor=lambda response: bool(response.has_more),
|
|
416
|
+
),
|
|
417
|
+
items_extractor=lambda response: response.mv,
|
|
418
|
+
)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### 内置策略速查
|
|
422
|
+
|
|
423
|
+
| 策略 | 适用场景 | 关键参数 |
|
|
424
|
+
| ---------------------------------- | -------------: | --------------------------------- |
|
|
425
|
+
| `PageStrategy` | 页码递增 | `page_key` |
|
|
426
|
+
| `OffsetStrategy` | 偏移量滑窗 | `offset_key` + `page_size_key` |
|
|
427
|
+
| `CursorStrategy` | 响应游标回写 | `cursor_key` |
|
|
428
|
+
| `MultiFieldContinuationStrategy` | 多字段续翻 | 自定义 `build_next_params` 函数 |
|
|
429
|
+
| `BatchRefreshStrategy` | 批次刷新 | `refresh_key` |
|
|
430
|
+
|
|
431
|
+
## 请求签名
|
|
432
|
+
|
|
433
|
+
部分接口需要对请求体进行签名。通过 `@cgi_endpoint` 的 `sign=True` 启用:
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
@cgi_endpoint(
|
|
437
|
+
key="song.get_sheet",
|
|
438
|
+
module="music.mir.SheetMusicSvr",
|
|
439
|
+
method="GetMoreSheetMusic",
|
|
440
|
+
sign=True,
|
|
441
|
+
)
|
|
442
|
+
def get_sheet(self, mid: str) -> CgiRequestData:
|
|
443
|
+
"""获取曲谱."""
|
|
444
|
+
return CgiRequestData(
|
|
445
|
+
param={"songMid": mid},
|
|
446
|
+
)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
签名后请求会发送到 `musics.fcg` 而非 `musicu.fcg`,并在 URL 参数中附加 `_`(时间戳)和 `sign`。
|
|
450
|
+
|
|
451
|
+
## 公共参数 `comm`
|
|
452
|
+
|
|
453
|
+
默认情况下,`comm` 参数由 `VersionPolicy.build_comm()` 自动生成。可以通过 `comm` 附加额外参数:
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
# 合并到自动生成的 comm 中(默认行为)
|
|
457
|
+
CgiRequestData(
|
|
458
|
+
...,
|
|
459
|
+
comm={"extra_key": "value"},
|
|
460
|
+
)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
发送前,所有 `comm` 值都会转换为字符串。合并模式下可将值设为 `None` 或空字符串,删除自动生成的同名参数。
|
|
464
|
+
|
|
465
|
+
使用 `override_comm=True` 完全替代自动生成的参数:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
CgiRequestData(
|
|
469
|
+
...,
|
|
470
|
+
comm={
|
|
471
|
+
"g_tk": 5381,
|
|
472
|
+
"uin": "",
|
|
473
|
+
"format": "json",
|
|
474
|
+
"inCharset": "utf-8",
|
|
475
|
+
"outCharset": "utf-8",
|
|
476
|
+
"notice": 0,
|
|
477
|
+
"needNewCode": 1,
|
|
478
|
+
},
|
|
479
|
+
override_comm=True,
|
|
480
|
+
)
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## 异常处理
|
|
484
|
+
|
|
485
|
+
在抛出或处理异常时,应使用项目统一的基于领域驱动(DDD)风格的异常类(继承自 `BaseApiException` 或 `ApiException`
|
|
486
|
+
)。在包装底层异常时,必须使用原生异常链(`raise ... from exc`)保留堆栈追踪:
|
|
487
|
+
|
|
488
|
+
```python
|
|
489
|
+
from ..core.exceptions import ApiDataError
|
|
490
|
+
|
|
491
|
+
try:
|
|
492
|
+
...
|
|
493
|
+
except KeyError as e:
|
|
494
|
+
raise ApiDataError("无法解析歌曲信息") from e
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## 编写测试
|
|
498
|
+
|
|
499
|
+
测试文件放在 `tests/` 下,按模块命名(如 `test_song.py`)。
|
|
500
|
+
|
|
501
|
+
### 基本格式
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
"""歌曲模块测试."""
|
|
505
|
+
|
|
506
|
+
import pytest
|
|
507
|
+
|
|
508
|
+
from qqmusic_api import Client
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
async def test_query_song(client: Client) -> None:
|
|
512
|
+
"""测试根据 ID 查询歌曲."""
|
|
513
|
+
result = await client.song.query_song([SongQueryInfo(mid="003w2xz20QlUZt")])
|
|
514
|
+
assert result.tracks
|
|
515
|
+
assert result.tracks[0].name
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### 使用 parametrize
|
|
519
|
+
|
|
520
|
+
```python
|
|
521
|
+
@pytest.mark.parametrize("page", [1, 2])
|
|
522
|
+
async def test_general_search(client: Client, page: int) -> None:
|
|
523
|
+
"""测试综合搜索翻页逻辑."""
|
|
524
|
+
try:
|
|
525
|
+
result = await client.search.general_search("周杰伦", page=page)
|
|
526
|
+
except Exception as e:
|
|
527
|
+
# 示例:优雅处理网络风控或限流 (需根据实际异常类型调整)
|
|
528
|
+
if "limit" in str(e).lower() or "risk" in str(e).lower():
|
|
529
|
+
pytest.skip(f"Triggered rate limit or risk control: {e}")
|
|
530
|
+
raise
|
|
531
|
+
|
|
532
|
+
assert result.song.items is not None
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### 需要登录的测试
|
|
536
|
+
|
|
537
|
+
使用 `authenticated_client` fixture:
|
|
538
|
+
|
|
539
|
+
```python
|
|
540
|
+
async def test_get_vip_info(authenticated_client: Client) -> None:
|
|
541
|
+
"""测试获取 VIP 信息."""
|
|
542
|
+
result = await authenticated_client.user.get_vip_info()
|
|
543
|
+
assert result.vip_flag is not None
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### 测试分页
|
|
547
|
+
|
|
548
|
+
```python
|
|
549
|
+
async def test_search_paginate(client: Client) -> None:
|
|
550
|
+
"""测试搜索分页."""
|
|
551
|
+
pager = client.search.search_by_type("周杰伦", num=5).pager(limit=2)
|
|
552
|
+
|
|
553
|
+
assert pager.has_more() is True
|
|
554
|
+
first_page = await pager.next()
|
|
555
|
+
assert pager.has_more() is True
|
|
556
|
+
second_page = await pager.next()
|
|
557
|
+
|
|
558
|
+
assert first_page.song
|
|
559
|
+
assert second_page.song
|
|
560
|
+
```
|
|
@@ -1,4 +1,80 @@
|
|
|
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
|
+
|
|
62
|
+
## [[0.7.3](https://github.com/L-1124/QQMusicApi/compare/v0.7.2..v0.7.3)] - 2026-09-13
|
|
63
|
+
|
|
64
|
+
### Bug 修复
|
|
65
|
+
|
|
66
|
+
* **(song)** 恢复 get_song_urls 的 mid 数量上限校验 ([c358c77](https://github.com/L-1124/QQMusicApi/commit/c358c77783ef2209a83b734f86a928ba7c91b70b)) by [@L-1124](https://github.com/L-1124)
|
|
67
|
+
|
|
68
|
+
### 功能更新
|
|
69
|
+
|
|
70
|
+
* **(singer)** 新增歌手名称透明 PNG 接口及演示 ([3740170](https://github.com/L-1124/QQMusicApi/commit/374017047c39dc987418450b6d513bc216499a69)) by [@Lincb522](https://github.com/Lincb522) in [#302](https://github.com/L-1124/QQMusicApi/pull/302)
|
|
71
|
+
|
|
72
|
+
### 贡献者
|
|
73
|
+
|
|
74
|
+
* @L-1124
|
|
75
|
+
* @Lincb522 [#302](https://github.com/L-1124/QQMusicApi/pull/302)
|
|
76
|
+
* @github-actions[bot]
|
|
77
|
+
|
|
2
78
|
## [[0.7.2](https://github.com/L-1124/QQMusicApi/compare/v0.7.1..v0.7.2)] - 2026-08-05
|
|
3
79
|
|
|
4
80
|
### Bug 修复
|