qqmusic-api-python 0.6.8__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.dockerignore +0 -2
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/PKG-INFO +1 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/coding.md +1 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/release-notes.md +77 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/download.md +2 -2
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/__init__.py +1 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/client.py +2 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/album.py +49 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/base.py +5 -9
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/mv.py +33 -1
- qqmusic_api_python-0.7.0/qqmusic_api/modules/album.py +117 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/lyric.py +4 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/mv.py +30 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/recommend.py +7 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/song.py +43 -15
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/songlist.py +40 -0
- qqmusic_api_python-0.7.0/tests/test_album.py +59 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_mv.py +17 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_recommend.py +10 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_song.py +25 -6
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_songlist.py +19 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/Dockerfile +0 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/README.md +22 -1
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/config.example.toml +5 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/requirements.txt +61 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/run.py +0 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/app.py +3 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/auth.py +52 -42
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/cache.py +25 -12
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/config.py +18 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/credential_store.py +20 -25
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/response.py +1 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/security.py +26 -20
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/song.py +27 -18
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/songlist.py +10 -11
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/_helpers.py +1 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/search.py +3 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/song.py +22 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/songlist.py +2 -0
- qqmusic_api_python-0.7.0/web/src/routes/user.py +186 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/docstrings.py +1 -3
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/executor.py +42 -20
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/router_factory.py +9 -2
- qqmusic_api_python-0.7.0/web/tests/conftest.py +12 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/tests/test_web_enums.py +3 -12
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/tests/test_web_routes.py +33 -42
- qqmusic_api_python-0.6.8/qqmusic_api/modules/album.py +0 -58
- qqmusic_api_python-0.6.8/tests/test_album.py +0 -29
- qqmusic_api_python-0.6.8/web/src/routes/user.py +0 -109
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.agents/skills/pydantic/SKILL.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.agents/skills/python-standards/SKILL.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.agents/skills/tarsio/SKILL.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.agents/skills/tarsio/references/api-reference.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.agents/skills/uv-package-manager/SKILL.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/renovate.json +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/workflows/checking.yaml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/workflows/docs.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/workflows/release.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.github/workflows/testing.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.gitignore +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/.markdownlint-cli2.yaml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/AGENTS.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/CLAUDE.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/LICENSE +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/README.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/assets/qq-music.svg +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/cliff.toml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/contributing.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/index.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/core/client.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/core/exception.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/core/pagination.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/core/request.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/core/versioning.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/album.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/base.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/comment.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/login.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/lyric.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/mv.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/recommend.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/request.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/search.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/singer.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/song.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/songlist.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/top.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/model/user.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/album.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/comment.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/login.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/login_utils.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/lyric.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/mv.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/private_message.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/recommend.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/search.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/singer.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/song.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/songlist.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/top.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/reference/modules/user.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/client.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/credential.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/error-handling.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/login.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/pagination.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/start.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/docs/tutorial/web.md +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/download_song.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/get_all_sheets.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/phone_login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/private_message.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/qrcode_login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/examples/upload_file.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/prek.toml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/pyproject.toml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/algorithms/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/algorithms/sign.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/algorithms/tripledes.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/exceptions.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/pagination.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/request.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/core/versioning.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/_validator.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/comment.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/helper.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/lyric.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/private_message.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/recommend.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/request.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/search.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/singer.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/song.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/songlist.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/top.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/models/user.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/_base.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/comment.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/helper.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/helper_utils.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/login_utils.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/private_message.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/search.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/singer.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/top.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/modules/user.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/utils/device.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/utils/mqtt.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/qqmusic_api/utils/qimei.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/scripts/ag-1.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_comment.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_login_utils.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_private_message.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_singer.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/tests/test_user.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/uv.lock +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/.gitignore +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/accounts.example.toml +0 -0
- /qqmusic_api_python-0.6.8/web/docker-compose.yml → /qqmusic_api_python-0.7.0/web/docker-compose.example.yml +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/core/deps.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/comment.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/mv.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/modules/singer.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/album.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/comment.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/login.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/lyric.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/mv.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/recommend.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/singer.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routes/top.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/__init__.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/params.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/src/routing/route_types.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/tests/test_web_core.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/tests/test_web_docstrings.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/web/tests/test_web_route_validation.py +0 -0
- {qqmusic_api_python-0.6.8 → qqmusic_api_python-0.7.0}/zensical.toml +0 -0
|
@@ -388,7 +388,7 @@ from qqmusic_api import Client
|
|
|
388
388
|
|
|
389
389
|
async def test_query_song(client: Client) -> None:
|
|
390
390
|
"""测试根据 ID 查询歌曲."""
|
|
391
|
-
result = await client.song.query_song(["003w2xz20QlUZt"])
|
|
391
|
+
result = await client.song.query_song([SongQueryInfo(mid="003w2xz20QlUZt")])
|
|
392
392
|
assert result.tracks
|
|
393
393
|
assert result.tracks[0].name
|
|
394
394
|
```
|
|
@@ -1,4 +1,81 @@
|
|
|
1
1
|
|
|
2
|
+
## [[0.6.9](https://github.com/L-1124/QQMusicApi/compare/v0.6.8..v0.6.9)] - 2026-07-12
|
|
3
|
+
|
|
4
|
+
### Bug 修复
|
|
5
|
+
|
|
6
|
+
* **(web)** 修复凭据刷新异常捕获过宽与 ETag 缓存机制形同虚设的问题 ([305a063](https://github.com/L-1124/QQMusicApi/commit/305a0637f6261b3de3710c504dc8b22ec0ec52b8)) by [@L-1124](https://github.com/L-1124)
|
|
7
|
+
* **(web)** 修复配置账号由于重置逻辑缺陷被永久标记失效与流式响应并发槽位泄露 ([56f3034](https://github.com/L-1124/QQMusicApi/commit/56f30347a5182380e14febb1b6b1a80debc74ea3)) by [@L-1124](https://github.com/L-1124)
|
|
8
|
+
* **(web)** 避免将 credential 对象盲目混入参数导致 SDK 签名报错 ([fc8b32c](https://github.com/L-1124/QQMusicApi/commit/fc8b32ca1d8d20f16c52f37272798b3688f2848b)) by [@L-1124](https://github.com/L-1124)
|
|
9
|
+
* **(web)** 修复 KeyedLock finalizer 泄漏与 Redis 序列化 Pydantic 模型问题 ([5440a5e](https://github.com/L-1124/QQMusicApi/commit/5440a5e83b1d110d29f4e1b2eab74f5a8f509847)) by [@L-1124](https://github.com/L-1124)
|
|
10
|
+
* **(web)** 修正 songlist 创建与删除路由的 HTTP 方法 ([43c9ce6](https://github.com/L-1124/QQMusicApi/commit/43c9ce6fd0779895afe70a5c7062d7ead0d69957)) by [@L-1124](https://github.com/L-1124)
|
|
11
|
+
* **(web)** 修复凭据刷新锁与限流器的内存泄漏 ([c5ed6a3](https://github.com/L-1124/QQMusicApi/commit/c5ed6a315e7de680b60b63199350fca276781a46)) by [@L-1124](https://github.com/L-1124)
|
|
12
|
+
* **(web)** 修复 search 路由声明中 Any 导致的类型检查错误 ([d1aea06](https://github.com/L-1124/QQMusicApi/commit/d1aea0631aeee866cc8b09d1d6a086b07f24e7a9)) by [@L-1124](https://github.com/L-1124)
|
|
13
|
+
* **(web)** 修复返回 bool 类型时生成的文档和数据格式问题 ([af8550d](https://github.com/L-1124/QQMusicApi/commit/af8550d08038dc8f66be39e28c9d3a905abf7e63)) by [@L-1124](https://github.com/L-1124)
|
|
14
|
+
|
|
15
|
+
### 功能更新
|
|
16
|
+
|
|
17
|
+
* **(web)** 添加 UserApi 的DisLike和favor api ([b1aa67d](https://github.com/L-1124/QQMusicApi/commit/b1aa67d5e6608d722b337c53274b4d91963a008d)) by [@L-1124](https://github.com/L-1124)
|
|
18
|
+
* 新增新碟上架 / MV 分类 / 收藏专辑 / 收藏歌曲接口与推荐新歌地区筛选 ([520c92b](https://github.com/L-1124/QQMusicApi/commit/520c92bd4d3546a20358c0a6daf47f67af8813d8)) by [@jinzhongjia](https://github.com/jinzhongjia) in [#284](https://github.com/L-1124/QQMusicApi/pull/284)
|
|
19
|
+
|
|
20
|
+
### 功能重构
|
|
21
|
+
|
|
22
|
+
* **(web)** 收敛配置修改与硬编码路径到配置层 ([0e5950c](https://github.com/L-1124/QQMusicApi/commit/0e5950c9fec28783f081c7d37754de130dcd1950)) by [@L-1124](https://github.com/L-1124)
|
|
23
|
+
* **(web)** 抽取统一的 refresh_and_store 方法消除重复,并补全重试刷新失败时的无效标记逻辑 ([a4a0729](https://github.com/L-1124/QQMusicApi/commit/a4a072984f2dce212885bc240640c9a859d9b0ed)) by [@L-1124](https://github.com/L-1124)
|
|
24
|
+
* **(web)** 移除 songlist 的动态 getattr 分发以恢复类型安全与修复潜在凭据 KeyError ([c1030e1](https://github.com/L-1124/QQMusicApi/commit/c1030e1d867eed8e42957dbf797f548a754796e5)) by [@L-1124](https://github.com/L-1124)
|
|
25
|
+
|
|
26
|
+
### 性能优化
|
|
27
|
+
|
|
28
|
+
* **(web)** 启用 SQLite WAL 模式并延迟反序列化随机凭据 ([7d01994](https://github.com/L-1124/QQMusicApi/commit/7d019942ae3812fe286d90eb668dc52318ccabe7)) by [@L-1124](https://github.com/L-1124)
|
|
29
|
+
* **(web)** 缓存路由参数解析以避免重复的 SDK 签名内省 ([61cc5a5](https://github.com/L-1124/QQMusicApi/commit/61cc5a5494afb750149d6689e252cb429e9d109d)) by [@L-1124](https://github.com/L-1124)
|
|
30
|
+
* **(web)** 抽取 app 为会话级 fixture 以大幅降低测试耗时 ([6d13b50](https://github.com/L-1124/QQMusicApi/commit/6d13b50ad42c1bf8f56c596a2fd810cadc23208d)) by [@L-1124](https://github.com/L-1124)
|
|
31
|
+
* **(web)** 移除缓存层与响应中不必要的双重序列化 ([bbc865c](https://github.com/L-1124/QQMusicApi/commit/bbc865c8aa52da6a027064510e94c8fcf8d5ab76)) by [@L-1124](https://github.com/L-1124)
|
|
32
|
+
|
|
33
|
+
### 贡献者
|
|
34
|
+
|
|
35
|
+
* @L-1124
|
|
36
|
+
* @jinzhongjia [#284](https://github.com/L-1124/QQMusicApi/pull/284)
|
|
37
|
+
* @github-actions[bot]
|
|
38
|
+
|
|
39
|
+
## [[0.6.8](https://github.com/L-1124/QQMusicApi/compare/v0.6.7..v0.6.8)] - 2026-07-01
|
|
40
|
+
|
|
41
|
+
### Bug 修复
|
|
42
|
+
|
|
43
|
+
* **(web)** 禁用 FastAPI 默认按别名响应,使 JSON 输出与 Python 原生变量名保持一致 ([32459a5](https://github.com/L-1124/QQMusicApi/commit/32459a54c03d8a0a738f1917f9eb19fc6f9ac4ef)) by [@L-1124](https://github.com/L-1124)
|
|
44
|
+
|
|
45
|
+
### 功能更新
|
|
46
|
+
|
|
47
|
+
* **(core)** 新增 SignatureRequiredError 错误码 2000 处理 ([a73313a](https://github.com/L-1124/QQMusicApi/commit/a73313a91549a156358ad9311d0e20d7401b861b)) by [@L-1124](https://github.com/L-1124)
|
|
48
|
+
* **(core)** _ensure_session 添加 24h 保活机制 ([2290a32](https://github.com/L-1124/QQMusicApi/commit/2290a32304bcd9052365f60c67f2d4f6b00e4d7e)) by [@L-1124](https://github.com/L-1124)
|
|
49
|
+
* **(helper)** 新增辅助功能接口 ([eb0c40c](https://github.com/L-1124/QQMusicApi/commit/eb0c40c1461826955a1eb005afb723438730142a)) by [@L-1124](https://github.com/L-1124)
|
|
50
|
+
* **(login)** 支持登出API ([73f2a7e](https://github.com/L-1124/QQMusicApi/commit/73f2a7ee9ad1d48cbec2d6aa9dbf844f44b573c2)) by [@L-1124](https://github.com/L-1124)
|
|
51
|
+
* **(lyric)** [**breaking**] 彻底移除 crypt 字段,隐式处理加密状态 ([80edc40](https://github.com/L-1124/QQMusicApi/commit/80edc40e41331f6b5c11146d5daf6eccf344181f)) by [@L-1124](https://github.com/L-1124)
|
|
52
|
+
* **(lyric)** [**breaking**] 移除 decrypt 方法,改为默认解密 ([24949c4](https://github.com/L-1124/QQMusicApi/commit/24949c4df8c63b02b35dd69c30e2a8f40d234dfb)) by [@L-1124](https://github.com/L-1124)
|
|
53
|
+
* **(song)** 添加特殊歌曲文件类型 TRY_OGG_640 ([fef7e8c](https://github.com/L-1124/QQMusicApi/commit/fef7e8ceb220aa477ea7d8498f8ebfd240f72cc6)) by [@L-1124](https://github.com/L-1124)
|
|
54
|
+
* **(user)** 新增收藏/取消收藏歌单 API ([7865434](https://github.com/L-1124/QQMusicApi/commit/7865434782dd205815222fe716f52bac9f84aeec)) by [@jinzhongjia](https://github.com/jinzhongjia) in [#281](https://github.com/L-1124/QQMusicApi/pull/281)
|
|
55
|
+
* **(user)** 新增 FeedbackBlack 黑名单 API ([4340554](https://github.com/L-1124/QQMusicApi/commit/4340554aaaafaef175eead7ebced596538bc65e2)) by [@L-1124](https://github.com/L-1124)
|
|
56
|
+
* **(web)** 启用 FastAPI 内置 Swagger UI 和 ReDoc,更新文档地址说明 ([a3245ad](https://github.com/L-1124/QQMusicApi/commit/a3245ad3d3863c7f7eec94352d169a32ba4478c6)) by [@L-1124](https://github.com/L-1124)
|
|
57
|
+
* **(web)** 重构 Dockerfile 为多阶段构建,修复非 root 用户 uv 缓存权限问题 ([00e16a3](https://github.com/L-1124/QQMusicApi/commit/00e16a37b62e989de37232e8af7e77167a28e823)) by [@L-1124](https://github.com/L-1124)
|
|
58
|
+
|
|
59
|
+
### 功能重构
|
|
60
|
+
|
|
61
|
+
* **(core)** 移除 _session_cache 冗余缓存,简化会话验证逻辑 ([0b12b9d](https://github.com/L-1124/QQMusicApi/commit/0b12b9d041a8be24ad84d60bd2ceaed76b81fe92)) by [@L-1124](https://github.com/L-1124)
|
|
62
|
+
* **(models)** [**breaking**] 将所有模型中的 alias 统一替换为 validation_alias,隔离序列化字段名 ([c611b91](https://github.com/L-1124/QQMusicApi/commit/c611b91d607d36327662a80d0e126a6958b21bd5)) by [@L-1124](https://github.com/L-1124)
|
|
63
|
+
* **(modules)** 移除冗余的 _require_login 调用,统一使用 require_login 参数 ([1a666f4](https://github.com/L-1124/QQMusicApi/commit/1a666f45ae968c235066c07e8b3e82096c3fc668)) by [@L-1124](https://github.com/L-1124)
|
|
64
|
+
* **(modules)** 将 _require_login 收敛到 _build_request 的 require_login 参数中 ([d1a92ca](https://github.com/L-1124/QQMusicApi/commit/d1a92ca07d5a98f3f443453e5fc77b8f8c8dc44e)) by [@L-1124](https://github.com/L-1124)
|
|
65
|
+
|
|
66
|
+
### 文档更新
|
|
67
|
+
|
|
68
|
+
* **(coding)** 更新需登录接口的编码规范,使用 require_login 参数替代 _require_login ([9e56215](https://github.com/L-1124/QQMusicApi/commit/9e562152ec8039b3045ae6c51006401c93b36a36)) by [@L-1124](https://github.com/L-1124)
|
|
69
|
+
* **(dislike)** 更新不喜欢列表项和响应数据的文档说明 ([5dbf711](https://github.com/L-1124/QQMusicApi/commit/5dbf711e46a69f99940f048504446b9e074fd21f)) by [@L-1124](https://github.com/L-1124)
|
|
70
|
+
* **(login)** 更新二维码和手机验证码登录状态的文档注释 ([f31d4d0](https://github.com/L-1124/QQMusicApi/commit/f31d4d0132cb61a9935ebea92657fcfa6591926c)) by [@L-1124](https://github.com/L-1124)
|
|
71
|
+
* **(readme)** 添加 DeepWiki 相关链接 ([fcea758](https://github.com/L-1124/QQMusicApi/commit/fcea758b80a824cb513a8bdfc8b247da786f5a1e)) by [@L-1124](https://github.com/L-1124)
|
|
72
|
+
|
|
73
|
+
### 贡献者
|
|
74
|
+
|
|
75
|
+
* @L-1124
|
|
76
|
+
* @jinzhongjia [#281](https://github.com/L-1124/QQMusicApi/pull/281)
|
|
77
|
+
* @github-actions[bot]
|
|
78
|
+
|
|
2
79
|
## [[0.6.7](https://github.com/L-1124/QQMusicApi/compare/v0.6.6..v0.6.7)] - 2026-06-17
|
|
3
80
|
|
|
4
81
|
### 功能更新
|
|
@@ -34,7 +34,7 @@ asyncio.run(main())
|
|
|
34
34
|
通过 `query_song` 获取歌曲信息后,可从 `pay` 字段判断是否需要付费:
|
|
35
35
|
|
|
36
36
|
```python
|
|
37
|
-
song = await client.song.query_song(["003w2xz20QlUZt"])
|
|
37
|
+
song = await client.song.query_song([SongQueryInfo(mid="003w2xz20QlUZt")])
|
|
38
38
|
track = song.tracks[0]
|
|
39
39
|
|
|
40
40
|
# pay_play: 1=需要付费播放, 0=免费
|
|
@@ -72,7 +72,7 @@ for info in urls.data:
|
|
|
72
72
|
`query_song` 返回的 `file` 字段包含各音质的文件大小,值为 `0` 表示该音质不存在:
|
|
73
73
|
|
|
74
74
|
```python
|
|
75
|
-
song = await client.song.query_song(["003w2xz20QlUZt"])
|
|
75
|
+
song = await client.song.query_song([SongQueryInfo(mid="003w2xz20QlUZt")])
|
|
76
76
|
track = song.tracks[0]
|
|
77
77
|
|
|
78
78
|
file = track.file
|
|
@@ -7,12 +7,11 @@ from typing import TYPE_CHECKING, Any, Literal, cast, overload
|
|
|
7
7
|
|
|
8
8
|
import anyio
|
|
9
9
|
import orjson as json
|
|
10
|
-
from niquests import AsyncSession, AsyncTokenBucketLimiter, PreparedRequest
|
|
10
|
+
from niquests import AsyncSession, AsyncTokenBucketLimiter, PreparedRequest, RetryConfiguration
|
|
11
11
|
from niquests.exceptions import RequestException
|
|
12
12
|
from niquests.models import Response
|
|
13
13
|
from niquests.typing import AsyncHookType, ProxyType, TLSClientCertType, TLSVerifyType
|
|
14
14
|
from tarsio import TarsDict
|
|
15
|
-
from urllib3.util.retry import Retry
|
|
16
15
|
|
|
17
16
|
from ..algorithms import zzc_sign
|
|
18
17
|
from ..models.request import Credential, JceRequest, JceRequestItem, JceResponse, JceResponseItem, RequestItem
|
|
@@ -86,7 +85,7 @@ class Client:
|
|
|
86
85
|
multiplexed=True,
|
|
87
86
|
hooks=AsyncTokenBucketLimiter(rate=rate or 10, capacity=capacity or 50),
|
|
88
87
|
happy_eyeballs=True,
|
|
89
|
-
retries=
|
|
88
|
+
retries=RetryConfiguration(
|
|
90
89
|
total=connect_retries or 2,
|
|
91
90
|
connect=connect_retries or 2,
|
|
92
91
|
read=0,
|
|
@@ -70,3 +70,52 @@ class GetAlbumSongResponse(Response):
|
|
|
70
70
|
album_mid: str = Field(validation_alias="albumMid")
|
|
71
71
|
total_num: int = Field(validation_alias="totalNum")
|
|
72
72
|
song_list: list[Song] = Field(default_factory=list, json_schema_extra={"jsonpath": "$.songList[*].songInfo"})
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class NewAlbumItem(Album):
|
|
76
|
+
"""新碟上架列表中的单张专辑摘要.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
singers: 专辑署名歌手列表.
|
|
80
|
+
release_time: 发行日期, 格式通常为 YYYY-MM-DD.
|
|
81
|
+
type: 专辑类型.
|
|
82
|
+
area: 地区标识.
|
|
83
|
+
genre: 流派标识.
|
|
84
|
+
language: 语种标识.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
singers: list[Singer] = Field(default_factory=list)
|
|
88
|
+
release_time: str = ""
|
|
89
|
+
type: int = 0
|
|
90
|
+
area: int = 0
|
|
91
|
+
genre: int = 0
|
|
92
|
+
language: int = 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class GetNewAlbumResponse(Response):
|
|
96
|
+
"""新碟上架接口的响应体.
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
total: 该地区下新碟总数.
|
|
100
|
+
albums: 当前页新碟列表.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
total: int = 0
|
|
104
|
+
albums: list[NewAlbumItem] = Field(default_factory=list)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AlbumFavWriteResponse(Response):
|
|
108
|
+
"""收藏 / 取消收藏专辑的写操作响应.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
result: 操作结果码, 0 表示成功.
|
|
112
|
+
failed_album_id: 操作失败的专辑 ID 列表, 全部成功时为空.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
result: int = 0
|
|
116
|
+
failed_album_id: list[int] = Field(default_factory=list, validation_alias="v_failedAlbumId")
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def success(self) -> bool:
|
|
120
|
+
"""是否操作成功 (result 为 0 且无失败项)."""
|
|
121
|
+
return self.result == 0 and not self.failed_album_id
|
|
@@ -18,21 +18,17 @@ _PHOTO_NEW_SIZE_SEGMENTS: Final[dict[int, str]] = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _normalize_cover_size(size: CoverSize) -> str:
|
|
22
|
-
"""返回 `photo_new` 模板使用的尺寸片段."""
|
|
23
|
-
try:
|
|
24
|
-
return _PHOTO_NEW_SIZE_SEGMENTS[size]
|
|
25
|
-
except KeyError as exc:
|
|
26
|
-
raise ValueError("not supported size") from exc
|
|
27
|
-
|
|
28
|
-
|
|
29
21
|
def _build_photo_new_cover_url(kind: str, mid: str, size: CoverSize) -> str:
|
|
30
22
|
"""按 `photo_new` 规则拼接封面链接."""
|
|
31
23
|
normalized_mid = mid.strip()
|
|
32
24
|
if not normalized_mid:
|
|
33
25
|
return ""
|
|
34
26
|
|
|
35
|
-
|
|
27
|
+
try:
|
|
28
|
+
cover_size = _PHOTO_NEW_SIZE_SEGMENTS[size]
|
|
29
|
+
return f"https://y.gtimg.cn/music/photo_new/{kind}{cover_size}M000{normalized_mid}.jpg"
|
|
30
|
+
except KeyError as exc:
|
|
31
|
+
raise ValueError("not supported size") from exc
|
|
36
32
|
|
|
37
33
|
|
|
38
34
|
class Singer(Response):
|
|
@@ -4,7 +4,7 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
|
|
7
|
-
from .base import MV
|
|
7
|
+
from .base import MV, Singer
|
|
8
8
|
from .request import Response
|
|
9
9
|
|
|
10
10
|
|
|
@@ -116,3 +116,35 @@ class GetMvUrlsResponse(Response):
|
|
|
116
116
|
"""
|
|
117
117
|
|
|
118
118
|
data: dict[str, MvUrlSet] = Field(json_schema_extra={"jsonpath": "$"})
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class MvListItem(MV):
|
|
122
|
+
"""MV 分类列表中的单个 MV 摘要.
|
|
123
|
+
|
|
124
|
+
Attributes:
|
|
125
|
+
singers: 歌手列表.
|
|
126
|
+
subtitle: MV 副标题.
|
|
127
|
+
playcnt: 播放量.
|
|
128
|
+
pubdate: 发布时间戳.
|
|
129
|
+
duration: 时长 (秒).
|
|
130
|
+
picurl: 封面图片地址.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
singers: list[Singer] = Field(default_factory=list)
|
|
134
|
+
subtitle: str = ""
|
|
135
|
+
playcnt: int = 0
|
|
136
|
+
pubdate: int = 0
|
|
137
|
+
duration: int = 0
|
|
138
|
+
picurl: str = ""
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class GetMvListResponse(Response):
|
|
142
|
+
"""MV 分类列表接口的响应体.
|
|
143
|
+
|
|
144
|
+
Attributes:
|
|
145
|
+
total: 该分类条件下的 MV 总数.
|
|
146
|
+
items: 当前页 MV 列表.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
total: int = 0
|
|
150
|
+
items: list[MvListItem] = Field(default_factory=list, validation_alias="list")
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""专辑相关 API."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..core.pagination import OffsetStrategy, PagerMeta, ResponseAdapter
|
|
6
|
+
from ..models.album import (
|
|
7
|
+
AlbumFavWriteResponse,
|
|
8
|
+
GetAlbumDetailResponse,
|
|
9
|
+
GetAlbumSongResponse,
|
|
10
|
+
GetNewAlbumResponse,
|
|
11
|
+
)
|
|
12
|
+
from ..models.request import Credential
|
|
13
|
+
from ._base import ApiModule
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AlbumApi(ApiModule):
|
|
17
|
+
"""专辑相关 API."""
|
|
18
|
+
|
|
19
|
+
def get_detail(self, value: int | str):
|
|
20
|
+
"""获取专辑详细信息.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
value: 专辑 ID 或 MID.
|
|
24
|
+
"""
|
|
25
|
+
param: dict[str, Any] = {}
|
|
26
|
+
if isinstance(value, int) or (isinstance(value, str) and value.isdecimal()):
|
|
27
|
+
param["albumId"] = int(value)
|
|
28
|
+
else:
|
|
29
|
+
param["albumMId"] = value
|
|
30
|
+
|
|
31
|
+
return self._build_request(
|
|
32
|
+
module="music.musichallAlbum.AlbumInfoServer",
|
|
33
|
+
method="GetAlbumDetail",
|
|
34
|
+
param=param,
|
|
35
|
+
response_model=GetAlbumDetailResponse,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def get_song(self, value: int | str, num: int = 10, page: int = 1):
|
|
39
|
+
"""获取专辑歌曲列表.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
value: 专辑 ID 或 MID.
|
|
43
|
+
num: 返回结果数量.
|
|
44
|
+
page: 页码.
|
|
45
|
+
"""
|
|
46
|
+
param: dict[str, Any] = {
|
|
47
|
+
"begin": num * (page - 1),
|
|
48
|
+
"num": num,
|
|
49
|
+
}
|
|
50
|
+
if isinstance(value, int) or (isinstance(value, str) and value.isdecimal()):
|
|
51
|
+
param["albumId"] = int(value)
|
|
52
|
+
else:
|
|
53
|
+
param["albumMid"] = value
|
|
54
|
+
|
|
55
|
+
return self._build_request(
|
|
56
|
+
module="music.musichallAlbum.AlbumSongList",
|
|
57
|
+
method="GetAlbumSongList",
|
|
58
|
+
param=param,
|
|
59
|
+
response_model=GetAlbumSongResponse,
|
|
60
|
+
pager_meta=PagerMeta(
|
|
61
|
+
strategy=OffsetStrategy(offset_key="begin", page_size_key="num"),
|
|
62
|
+
adapter=ResponseAdapter(total="total_num", count=lambda response: len(response.song_list)),
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def get_new_album(self, area: int = 1, num: int = 20, page: int = 1):
|
|
67
|
+
"""获取新碟上架列表.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
area: 地区. 1=内地, 2=港台, 3=欧美, 4=韩国, 5=日本, 6=其他.
|
|
71
|
+
num: 每页返回的专辑数量.
|
|
72
|
+
page: 页码, 从 1 开始.
|
|
73
|
+
"""
|
|
74
|
+
return self._build_request(
|
|
75
|
+
module="newalbum.NewAlbumServer",
|
|
76
|
+
method="get_new_album_info",
|
|
77
|
+
param={"area": area, "num": num, "start": num * (page - 1)},
|
|
78
|
+
response_model=GetNewAlbumResponse,
|
|
79
|
+
pager_meta=PagerMeta(
|
|
80
|
+
strategy=OffsetStrategy(offset_key="start", page_size_key="num"),
|
|
81
|
+
adapter=ResponseAdapter(total="total", count=lambda response: len(response.albums)),
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def fav_album(self, album_id: int | list[int], *, credential: Credential | None = None):
|
|
86
|
+
"""收藏专辑到当前登录用户.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
album_id: 专辑 ID, 支持单个或列表.
|
|
90
|
+
credential: 登录凭证.
|
|
91
|
+
"""
|
|
92
|
+
ids = [album_id] if isinstance(album_id, int) else album_id
|
|
93
|
+
return self._build_request(
|
|
94
|
+
module="music.musicasset.AlbumFavWrite",
|
|
95
|
+
method="FavAlbum",
|
|
96
|
+
param={"v_albumId": ids},
|
|
97
|
+
credential=credential,
|
|
98
|
+
require_login=True,
|
|
99
|
+
response_model=AlbumFavWriteResponse,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def del_fav_album(self, album_id: int | list[int], *, credential: Credential | None = None):
|
|
103
|
+
"""取消收藏专辑.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
album_id: 专辑 ID, 支持单个或列表.
|
|
107
|
+
credential: 登录凭证.
|
|
108
|
+
"""
|
|
109
|
+
ids = [album_id] if isinstance(album_id, int) else album_id
|
|
110
|
+
return self._build_request(
|
|
111
|
+
module="music.musicasset.AlbumFavWrite",
|
|
112
|
+
method="CancelFavAlbum",
|
|
113
|
+
param={"v_albumId": ids},
|
|
114
|
+
credential=credential,
|
|
115
|
+
require_login=True,
|
|
116
|
+
response_model=AlbumFavWriteResponse,
|
|
117
|
+
)
|
|
@@ -16,6 +16,7 @@ class LyricApi(ApiModule):
|
|
|
16
16
|
qrc: bool = False,
|
|
17
17
|
trans: bool = False,
|
|
18
18
|
roma: bool = False,
|
|
19
|
+
song_type: int = 1,
|
|
19
20
|
):
|
|
20
21
|
"""获取歌词原始数据.
|
|
21
22
|
|
|
@@ -24,6 +25,7 @@ class LyricApi(ApiModule):
|
|
|
24
25
|
qrc: 是否获取逐字歌词 (逐字歌词可能需要特定权限).
|
|
25
26
|
trans: 是否获取翻译.
|
|
26
27
|
roma: 是否获取罗马音.
|
|
28
|
+
song_type: 歌曲类型.
|
|
27
29
|
"""
|
|
28
30
|
params: dict[str, Any] = {
|
|
29
31
|
"crypt": 1,
|
|
@@ -34,9 +36,10 @@ class LyricApi(ApiModule):
|
|
|
34
36
|
"roma_t": 0,
|
|
35
37
|
"trans": trans,
|
|
36
38
|
"trans_t": 0,
|
|
37
|
-
"type":
|
|
39
|
+
"type": song_type,
|
|
38
40
|
}
|
|
39
41
|
params.update(self._build_query_common_params())
|
|
42
|
+
|
|
40
43
|
if isinstance(value, int) or (isinstance(value, str) and value.isdecimal()):
|
|
41
44
|
params["songId"] = int(value)
|
|
42
45
|
else:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""MV 相关 API."""
|
|
2
2
|
|
|
3
|
-
from ..
|
|
3
|
+
from ..core.pagination import OffsetStrategy, PagerMeta, ResponseAdapter
|
|
4
|
+
from ..models.mv import GetMvDetailResponse, GetMvListResponse, GetMvUrlsResponse
|
|
4
5
|
from ..utils.common import get_guid
|
|
5
6
|
from ._base import ApiModule
|
|
6
7
|
|
|
@@ -68,3 +69,31 @@ class MvApi(ApiModule):
|
|
|
68
69
|
},
|
|
69
70
|
response_model=GetMvUrlsResponse,
|
|
70
71
|
)
|
|
72
|
+
|
|
73
|
+
def get_mv_list(
|
|
74
|
+
self,
|
|
75
|
+
area: int = 15,
|
|
76
|
+
version: int = 7,
|
|
77
|
+
order: int = 0,
|
|
78
|
+
num: int = 10,
|
|
79
|
+
page: int = 1,
|
|
80
|
+
):
|
|
81
|
+
"""获取 MV 分类列表.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
area: 地区 ID. 15=全部, 8=内地, 5=港台, 6=欧美, 7=韩国, 4=日本.
|
|
85
|
+
version: 类型 ID. 7=全部, 8=MV, 13=现场, 14=翻唱, 15=舞蹈, 16=影视, 17=综艺, 18=儿歌.
|
|
86
|
+
order: 排序方式. 0=最新, 1=最热.
|
|
87
|
+
num: 每页返回数量.
|
|
88
|
+
page: 页码, 从 1 开始.
|
|
89
|
+
"""
|
|
90
|
+
return self._build_request(
|
|
91
|
+
module="MvService.MvInfoProServer",
|
|
92
|
+
method="GetAllocMvInfo",
|
|
93
|
+
param={"area": area, "version": version, "order": order, "start": num * (page - 1), "size": num},
|
|
94
|
+
response_model=GetMvListResponse,
|
|
95
|
+
pager_meta=PagerMeta(
|
|
96
|
+
strategy=OffsetStrategy(offset_key="start", page_size_key="size"),
|
|
97
|
+
adapter=ResponseAdapter(total="total", count=lambda response: len(response.items)),
|
|
98
|
+
),
|
|
99
|
+
)
|
|
@@ -146,9 +146,13 @@ class RecommendApi(ApiModule):
|
|
|
146
146
|
),
|
|
147
147
|
)
|
|
148
148
|
|
|
149
|
-
def get_recommend_newsong(self):
|
|
150
|
-
"""获取推荐新歌.
|
|
151
|
-
|
|
149
|
+
def get_recommend_newsong(self, type: int = 5): # noqa: A002
|
|
150
|
+
"""获取推荐新歌.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
type: 地区/语种筛选. 1=内地, 2=欧美, 3=日本, 4=韩国, 5=最新, 6=港台.
|
|
154
|
+
"""
|
|
155
|
+
data = {"type": type}
|
|
152
156
|
return self._build_request(
|
|
153
157
|
"newsong.NewSongServer",
|
|
154
158
|
"get_new_song_info",
|
|
@@ -177,36 +177,64 @@ class SongFileInfo(NamedTuple):
|
|
|
177
177
|
media_mid: str | None = None
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
class SongQueryInfo(NamedTuple):
|
|
181
|
+
"""歌曲查询信息.
|
|
182
|
+
|
|
183
|
+
Attributes:
|
|
184
|
+
id: 歌曲 ID.
|
|
185
|
+
mid: 歌曲 MID.
|
|
186
|
+
song_type: 歌曲类型.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
id: int | None = None
|
|
190
|
+
mid: str | None = None
|
|
191
|
+
song_type: int | None = None
|
|
192
|
+
|
|
193
|
+
|
|
180
194
|
class SongApi(ApiModule):
|
|
181
195
|
"""歌曲相关 API 模块类."""
|
|
182
196
|
|
|
183
197
|
_GET_SONG_URLS_MAX_MID = 100
|
|
184
198
|
_SONG_URL_FALLBACK_DOMAIN = "https://isure.stream.qqmusic.qq.com/"
|
|
185
199
|
|
|
186
|
-
def query_song(
|
|
187
|
-
|
|
200
|
+
def query_song(
|
|
201
|
+
self,
|
|
202
|
+
song_info: list[SongQueryInfo],
|
|
203
|
+
):
|
|
204
|
+
"""批量获取歌曲信息.
|
|
188
205
|
|
|
189
206
|
Args:
|
|
190
|
-
|
|
207
|
+
song_info: SongQueryInfo 列表.
|
|
191
208
|
|
|
192
209
|
Raises:
|
|
193
|
-
ValueError: 如果 `
|
|
210
|
+
ValueError: 如果 `song_info` 为空, 或参数不匹配.
|
|
194
211
|
"""
|
|
195
|
-
if not
|
|
196
|
-
raise ValueError("
|
|
212
|
+
if not song_info:
|
|
213
|
+
raise ValueError("song_info 不能为空")
|
|
214
|
+
|
|
215
|
+
ids, mids, types = [], [], []
|
|
216
|
+
for item in song_info:
|
|
217
|
+
if (item.id is None) == (item.mid is None):
|
|
218
|
+
raise ValueError("SongQueryInfo 必须提供 id 或 mid 且不能同时提供")
|
|
219
|
+
|
|
220
|
+
if item.id is not None:
|
|
221
|
+
ids.append(item.id)
|
|
222
|
+
else:
|
|
223
|
+
mids.append(item.mid)
|
|
224
|
+
types.append(item.song_type or 0)
|
|
225
|
+
|
|
197
226
|
params: dict[str, Any] = {
|
|
198
|
-
"types": [0 for _ in range(len(value))],
|
|
199
|
-
"modify_stamp": [0 for _ in range(len(value))],
|
|
200
227
|
"ctx": 0,
|
|
201
228
|
"client": 1,
|
|
229
|
+
"types": types,
|
|
230
|
+
"modify_stamp": [0] * len(types),
|
|
202
231
|
}
|
|
203
|
-
|
|
204
|
-
if
|
|
205
|
-
params["ids"] =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
params["mids"] = [str(v) for v in value]
|
|
232
|
+
|
|
233
|
+
if ids:
|
|
234
|
+
params["ids"] = ids
|
|
235
|
+
if mids:
|
|
236
|
+
params["mids"] = mids
|
|
237
|
+
|
|
210
238
|
return self._build_request(
|
|
211
239
|
module="music.trackInfo.UniformRuleCtrl",
|
|
212
240
|
method="CgiGetTrackInfo",
|
|
@@ -175,3 +175,43 @@ class SonglistApi(ApiModule):
|
|
|
175
175
|
if e.code == 80092:
|
|
176
176
|
return False
|
|
177
177
|
raise
|
|
178
|
+
|
|
179
|
+
async def like_song(
|
|
180
|
+
self,
|
|
181
|
+
song_info: list[tuple[int, int]],
|
|
182
|
+
*,
|
|
183
|
+
credential: Credential | None = None,
|
|
184
|
+
) -> bool:
|
|
185
|
+
"""收藏歌曲到 "我喜欢" 歌单.
|
|
186
|
+
|
|
187
|
+
Note:
|
|
188
|
+
"我喜欢" 歌单的目录 ID 固定为 201, 本方法即以 dirid=201 调用 `add_songs`.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
song_info: 歌曲信息列表, 每项为 `(song_id, song_type)`.
|
|
192
|
+
credential: 登录凭证.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
操作成功与否 (歌曲已在 "我喜欢" 中也返回 True).
|
|
196
|
+
"""
|
|
197
|
+
return await self.add_songs(201, song_info, credential=credential)
|
|
198
|
+
|
|
199
|
+
async def unlike_song(
|
|
200
|
+
self,
|
|
201
|
+
song_info: list[tuple[int, int]],
|
|
202
|
+
*,
|
|
203
|
+
credential: Credential | None = None,
|
|
204
|
+
) -> bool:
|
|
205
|
+
"""从 "我喜欢" 歌单移除歌曲.
|
|
206
|
+
|
|
207
|
+
Note:
|
|
208
|
+
"我喜欢" 歌单的目录 ID 固定为 201, 本方法即以 dirid=201 调用 `del_songs`.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
song_info: 歌曲信息列表, 每项为 `(song_id, song_type)`.
|
|
212
|
+
credential: 登录凭证.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
操作成功与否 (歌曲不在 "我喜欢" 中也返回 True).
|
|
216
|
+
"""
|
|
217
|
+
return await self.del_songs(201, song_info, credential=credential)
|