qqmusic-api-python 0.3.3__tar.gz → 0.3.5__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.3.3 → qqmusic_api_python-0.3.5}/PKG-INFO +2 -6
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/README.md +1 -5
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/pyproject.toml +1 -1
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/__init__.py +3 -2
- qqmusic_api_python-0.3.5/qqmusic_api/comment.py +91 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/login.py +10 -4
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/song.py +4 -1
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/songlist.py +4 -4
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/user.py +1 -1
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/credential.py +2 -4
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/network.py +8 -9
- qqmusic_api_python-0.3.5/tests/test_comments.py +15 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_song.py +1 -1
- qqmusic_api_python-0.3.5/web/README.md +46 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/.gitignore +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/LICENSE +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/album.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/exceptions/__init__.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/exceptions/api_exception.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/lyric.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/mv.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/search.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/singer.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/top.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/common.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/device.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/qimei.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/session.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/sign.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/utils/tripledes.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_lyric.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_qimei.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_session.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_sign.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_singer.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_songlist.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_top.py +0 -0
- {qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/tests/test_user.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qqmusic-api-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: QQ音乐API封装库
|
|
5
5
|
Project-URL: homepage, https://luren-dc.github.io/QQMusicApi/
|
|
6
6
|
Project-URL: repository, https://github.com/luren-dc/QQMusicApi
|
|
@@ -98,11 +98,7 @@ async def main():
|
|
|
98
98
|
asyncio.run(main())
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
## Web API
|
|
102
|
-
```
|
|
103
|
-
docker build . -t qq-music-api
|
|
104
|
-
docker run -d -p 8000:8000 qq-music-api
|
|
105
|
-
```
|
|
101
|
+
## [Web API](./web/README.md)
|
|
106
102
|
|
|
107
103
|
## Licence
|
|
108
104
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from . import album, login, lyric, mv, search, singer, song, songlist, top, user
|
|
3
|
+
from . import album, comment, login, lyric, mv, search, singer, song, songlist, top, user
|
|
4
4
|
from .utils.credential import Credential
|
|
5
5
|
from .utils.session import Session, get_session, set_session
|
|
6
6
|
|
|
7
|
-
__version__ = "0.3.
|
|
7
|
+
__version__ = "0.3.5"
|
|
8
8
|
|
|
9
9
|
logger = logging.getLogger("qqmusicapi")
|
|
10
10
|
|
|
@@ -13,6 +13,7 @@ __all__ = [
|
|
|
13
13
|
"Credential",
|
|
14
14
|
"Session",
|
|
15
15
|
"album",
|
|
16
|
+
"comment",
|
|
16
17
|
"get_session",
|
|
17
18
|
"login",
|
|
18
19
|
"lyric",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""评论 API"""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from qqmusic_api.utils.network import api_request
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@api_request("music.globalComment.CommentRead", "GetHotCommentList")
|
|
9
|
+
async def get_hot_comments(
|
|
10
|
+
biz_id: str,
|
|
11
|
+
page_num: int = 1,
|
|
12
|
+
page_size: int = 15,
|
|
13
|
+
last_comment_seq_no: str = "",
|
|
14
|
+
):
|
|
15
|
+
"""获取歌曲热评
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
biz_id: 歌曲 ID
|
|
19
|
+
page_num: 页码
|
|
20
|
+
page_size: 每页数量
|
|
21
|
+
last_comment_seq_no: 上一页最后一条评论 ID(可选)
|
|
22
|
+
"""
|
|
23
|
+
params = {
|
|
24
|
+
"BizType": 1,
|
|
25
|
+
"BizId": biz_id,
|
|
26
|
+
"LastCommentSeqNo": last_comment_seq_no,
|
|
27
|
+
"PageSize": page_size,
|
|
28
|
+
"PageNum": page_num,
|
|
29
|
+
"HotType": 1,
|
|
30
|
+
"WithAirborne": 0,
|
|
31
|
+
"PicEnable": 1,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def _processor(data: dict[str, Any]):
|
|
35
|
+
"""处理并返回结构化评论数据:
|
|
36
|
+
|
|
37
|
+
返回结构:
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
"Avatar": str, # 用户头像 URL
|
|
41
|
+
"CmId": str, # 评论 ID (后续需要获取全部子评论时需用到)
|
|
42
|
+
"PraiseNum": int, # 点赞数
|
|
43
|
+
"Nick": str, # 昵称
|
|
44
|
+
"Pic": str, # 评论配图 (可能为空)
|
|
45
|
+
"Content": str, # 评论内容
|
|
46
|
+
"SeqNo": str, # 评论序号 ID 可以用于传递给 参数: last_comment_seq_no
|
|
47
|
+
"SubComments": [ # 子评论列表
|
|
48
|
+
{
|
|
49
|
+
"Avatar": str,
|
|
50
|
+
"Nick": str,
|
|
51
|
+
"Content": str,
|
|
52
|
+
"Pic": str,
|
|
53
|
+
"PraiseNum": int,
|
|
54
|
+
"SeqNo": str
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
...
|
|
59
|
+
]
|
|
60
|
+
"""
|
|
61
|
+
comments = data.get("CommentList", {}).get("Comments", [])
|
|
62
|
+
result = []
|
|
63
|
+
|
|
64
|
+
for comment in comments:
|
|
65
|
+
item = {
|
|
66
|
+
"Avatar": comment.get("Avatar"),
|
|
67
|
+
"CmId": comment.get("CmId"),
|
|
68
|
+
"PraiseNum": comment.get("PraiseNum"),
|
|
69
|
+
"Nick": comment.get("Nick"),
|
|
70
|
+
"Pic": comment.get("Pic"),
|
|
71
|
+
"Content": comment.get("Content"),
|
|
72
|
+
"SeqNo": comment.get("SeqNo"),
|
|
73
|
+
"SubComments": [],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for sub in comment.get("SubComments", []):
|
|
77
|
+
sub_item = {
|
|
78
|
+
"Avatar": sub.get("Avatar"),
|
|
79
|
+
"Nick": sub.get("Nick"),
|
|
80
|
+
"Content": sub.get("Content"),
|
|
81
|
+
"Pic": sub.get("Pic"),
|
|
82
|
+
"PraiseNum": sub.get("PraiseNum"),
|
|
83
|
+
"SeqNo": sub.get("SeqNo"),
|
|
84
|
+
}
|
|
85
|
+
item["SubComments"].append(sub_item)
|
|
86
|
+
|
|
87
|
+
result.append(item)
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
return params, _processor
|
|
@@ -12,7 +12,7 @@ from uuid import uuid4
|
|
|
12
12
|
|
|
13
13
|
import httpx
|
|
14
14
|
|
|
15
|
-
from .exceptions.api_exception import CredentialExpiredError, LoginError
|
|
15
|
+
from .exceptions.api_exception import CredentialExpiredError, LoginError, ResponseCodeError
|
|
16
16
|
from .utils.common import hash33
|
|
17
17
|
from .utils.credential import Credential
|
|
18
18
|
from .utils.network import ApiRequest
|
|
@@ -339,7 +339,7 @@ async def _authorize_qq_qr(uin: str, sigx: str) -> Credential:
|
|
|
339
339
|
data={
|
|
340
340
|
"response_type": "code",
|
|
341
341
|
"client_id": "100497308",
|
|
342
|
-
"redirect_uri": "https://y.qq.com/portal/wx_redirect.html?login_type=1&surl=https
|
|
342
|
+
"redirect_uri": "https://y.qq.com/portal/wx_redirect.html?login_type=1&surl=https://y.qq.com/",
|
|
343
343
|
"scope": "get_user_info,get_app_friends",
|
|
344
344
|
"state": "state",
|
|
345
345
|
"switch": "",
|
|
@@ -359,8 +359,8 @@ async def _authorize_qq_qr(uin: str, sigx: str) -> Credential:
|
|
|
359
359
|
raise LoginError("[QQLogin] 获取 code 失败")
|
|
360
360
|
try:
|
|
361
361
|
api = ApiRequest[[], dict[str, Any]](
|
|
362
|
-
"
|
|
363
|
-
"
|
|
362
|
+
"QQConnectLogin.LoginServer",
|
|
363
|
+
"QQLogin",
|
|
364
364
|
common={"tmeLoginType": "2"},
|
|
365
365
|
params={"code": code},
|
|
366
366
|
cacheable=False,
|
|
@@ -368,6 +368,10 @@ async def _authorize_qq_qr(uin: str, sigx: str) -> Credential:
|
|
|
368
368
|
return Credential.from_cookies_dict(await api())
|
|
369
369
|
except CredentialExpiredError:
|
|
370
370
|
raise LoginError("[QQLogin] 无法重复鉴权")
|
|
371
|
+
except ResponseCodeError as e:
|
|
372
|
+
if e.code == 20274:
|
|
373
|
+
raise LoginError("[QQLogin] 设备数量限制")
|
|
374
|
+
raise LoginError(f"[QQLogin] 未知错误: {e.code} - {e.message}")
|
|
371
375
|
|
|
372
376
|
|
|
373
377
|
async def _authorize_wx_qr(code: str) -> Credential:
|
|
@@ -431,6 +435,8 @@ async def phone_authorize(phone: int, auth_code: int, country_code: int = 86) ->
|
|
|
431
435
|
cacheable=False,
|
|
432
436
|
)()
|
|
433
437
|
match resp["code"]:
|
|
438
|
+
case 20274:
|
|
439
|
+
raise LoginError("[PhoneLogin] 设备数量限制")
|
|
434
440
|
case 20271:
|
|
435
441
|
raise LoginError("[PhoneLogin] 验证码错误或已鉴权")
|
|
436
442
|
case 0:
|
|
@@ -16,7 +16,7 @@ def _get_extract_func(key: str):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@api_request("music.trackInfo.UniformRuleCtrl", "CgiGetTrackInfo")
|
|
19
|
-
async def query_song(value: list[
|
|
19
|
+
async def query_song(value: list[int] | list[str]):
|
|
20
20
|
"""根据 id 或 mid 获取歌曲信息
|
|
21
21
|
|
|
22
22
|
Args:
|
|
@@ -114,6 +114,7 @@ class EncryptedSongFileType(BaseSongFileType):
|
|
|
114
114
|
async def get_song_urls(
|
|
115
115
|
mid: list[str],
|
|
116
116
|
file_type: SongFileType = SongFileType.MP3_128,
|
|
117
|
+
*,
|
|
117
118
|
credential: Credential | None = None,
|
|
118
119
|
) -> dict[str, str]: ...
|
|
119
120
|
|
|
@@ -122,6 +123,7 @@ async def get_song_urls(
|
|
|
122
123
|
async def get_song_urls(
|
|
123
124
|
mid: list[str],
|
|
124
125
|
file_type: EncryptedSongFileType,
|
|
126
|
+
*,
|
|
125
127
|
credential: Credential | None = None,
|
|
126
128
|
) -> dict[str, tuple[str, str]]: ...
|
|
127
129
|
|
|
@@ -129,6 +131,7 @@ async def get_song_urls(
|
|
|
129
131
|
async def get_song_urls(
|
|
130
132
|
mid: list[str],
|
|
131
133
|
file_type: SongFileType | EncryptedSongFileType = SongFileType.MP3_128,
|
|
134
|
+
*,
|
|
132
135
|
credential: Credential | None = None,
|
|
133
136
|
) -> dict[str, str] | dict[str, tuple[str, str]]:
|
|
134
137
|
"""获取歌曲文件链接
|
|
@@ -79,7 +79,7 @@ async def get_songlist(
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
@api_request("music.musicasset.PlaylistBaseWrite", "AddPlaylist", verify=True, cacheable=False)
|
|
82
|
-
async def create(dirname: str, credential: Credential | None = None):
|
|
82
|
+
async def create(dirname: str, *, credential: Credential | None = None):
|
|
83
83
|
"""添加歌单, 重名会在名称后面添加时间戳
|
|
84
84
|
|
|
85
85
|
Args:
|
|
@@ -95,7 +95,7 @@ async def create(dirname: str, credential: Credential | None = None):
|
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
@api_request("music.musicasset.PlaylistBaseWrite", "DelPlaylist", verify=True, cacheable=False)
|
|
98
|
-
async def delete(dirid: int, credential: Credential | None = None):
|
|
98
|
+
async def delete(dirid: int, *, credential: Credential | None = None):
|
|
99
99
|
"""删除歌单
|
|
100
100
|
|
|
101
101
|
Args:
|
|
@@ -111,7 +111,7 @@ async def delete(dirid: int, credential: Credential | None = None):
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
@api_request("music.musicasset.PlaylistDetailWrite", "AddSonglist", verify=True, cacheable=False)
|
|
114
|
-
async def add_songs(dirid: int = 1, song_ids: list[int] = [], credential: Credential | None = None):
|
|
114
|
+
async def add_songs(dirid: int = 1, song_ids: list[int] = [], *, credential: Credential | None = None):
|
|
115
115
|
"""添加歌曲到歌单
|
|
116
116
|
|
|
117
117
|
Args:
|
|
@@ -129,7 +129,7 @@ async def add_songs(dirid: int = 1, song_ids: list[int] = [], credential: Creden
|
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
@api_request("music.musicasset.PlaylistDetailWrite", "DelSonglist", verify=True, cacheable=False)
|
|
132
|
-
async def del_songs(dirid: int = 1, song_ids: list[int] = [], credential: Credential | None = None):
|
|
132
|
+
async def del_songs(dirid: int = 1, song_ids: list[int] = [], *, credential: Credential | None = None):
|
|
133
133
|
"""删除歌单歌曲
|
|
134
134
|
|
|
135
135
|
Args:
|
|
@@ -93,7 +93,7 @@ async def get_friend(page: int = 1, num: int = 10, *, credential: Credential | N
|
|
|
93
93
|
return {
|
|
94
94
|
"PageSize": num,
|
|
95
95
|
"Page": page - 1,
|
|
96
|
-
}, lambda data: {"total": data.get("
|
|
96
|
+
}, lambda data: {"total": len(data.get("Friends", [])), "list": data.get("Friends", [])}
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
@api_request("music.concern.RelationList", "GetFollowUserList", verify=True, cacheable=False)
|
|
@@ -84,16 +84,14 @@ class Credential:
|
|
|
84
84
|
if not self.has_musicid() or not self.has_musickey():
|
|
85
85
|
return False
|
|
86
86
|
if await self.is_expired():
|
|
87
|
-
return bool(self.refresh_key)
|
|
87
|
+
return bool(self.refresh_key)
|
|
88
88
|
return True
|
|
89
89
|
|
|
90
90
|
async def is_expired(self) -> bool:
|
|
91
91
|
"""判断 credential 是否过期"""
|
|
92
92
|
if "musickeyCreateTime" in self.extra_fields and "keyExpiresIn" in self.extra_fields:
|
|
93
93
|
expired_time_stamp = self.extra_fields["musickeyCreateTime"] + self.extra_fields["keyExpiresIn"]
|
|
94
|
-
|
|
95
|
-
return True
|
|
96
|
-
return False
|
|
94
|
+
return expired_time_stamp <= time()
|
|
97
95
|
|
|
98
96
|
from ..login import check_expired
|
|
99
97
|
|
|
@@ -38,8 +38,8 @@ def api_request(
|
|
|
38
38
|
process_bool: bool = True,
|
|
39
39
|
cache_ttl: int | None = None,
|
|
40
40
|
cacheable: bool = True,
|
|
41
|
-
exclude_params: list[str] =
|
|
42
|
-
catch_error_code: list[int] =
|
|
41
|
+
exclude_params: list[str] | None = None,
|
|
42
|
+
catch_error_code: list[int] | None = None,
|
|
43
43
|
):
|
|
44
44
|
"""API请求"""
|
|
45
45
|
|
|
@@ -111,7 +111,7 @@ class BaseRequest(ABC):
|
|
|
111
111
|
return common
|
|
112
112
|
|
|
113
113
|
@common.setter
|
|
114
|
-
def
|
|
114
|
+
def common(self, value: dict[str, Any]):
|
|
115
115
|
"""设置公共参数"""
|
|
116
116
|
self._common = value
|
|
117
117
|
|
|
@@ -194,8 +194,8 @@ class ApiRequest(BaseRequest, Generic[_P, _R]):
|
|
|
194
194
|
process_bool: bool = True,
|
|
195
195
|
cache_ttl: int | None = None,
|
|
196
196
|
cacheable: bool = True,
|
|
197
|
-
exclude_params: list[str] =
|
|
198
|
-
catch_error_code: list[int] =
|
|
197
|
+
exclude_params: list[str] | None = None,
|
|
198
|
+
catch_error_code: list[int] | None = None,
|
|
199
199
|
) -> None:
|
|
200
200
|
super().__init__(common, credential, verify, ignore_code)
|
|
201
201
|
self.module = module
|
|
@@ -206,8 +206,8 @@ class ApiRequest(BaseRequest, Generic[_P, _R]):
|
|
|
206
206
|
self.processor: Callable[[dict[str, Any]], Any] = NO_PROCESSOR
|
|
207
207
|
self.cacheable = cacheable
|
|
208
208
|
self.cache_ttl = cache_ttl
|
|
209
|
-
self.exclude_params = exclude_params
|
|
210
|
-
self.catch_error_code = catch_error_code
|
|
209
|
+
self.exclude_params = exclude_params or []
|
|
210
|
+
self.catch_error_code = catch_error_code or []
|
|
211
211
|
|
|
212
212
|
def copy(self) -> "ApiRequest[_P, _R]":
|
|
213
213
|
"""创建当前 ApiRequest 实例的副本"""
|
|
@@ -353,7 +353,7 @@ class RequestGroup(BaseRequest):
|
|
|
353
353
|
|
|
354
354
|
self._requests.append(
|
|
355
355
|
RequestItem(
|
|
356
|
-
id=len(self._requests)
|
|
356
|
+
id=len(self._requests),
|
|
357
357
|
key=unique_key,
|
|
358
358
|
request=request,
|
|
359
359
|
args=args,
|
|
@@ -422,7 +422,6 @@ class RequestGroup(BaseRequest):
|
|
|
422
422
|
|
|
423
423
|
if not self._requests:
|
|
424
424
|
return self._results
|
|
425
|
-
|
|
426
425
|
resp = await self.request()
|
|
427
426
|
await self._process_response(resp)
|
|
428
427
|
return self._results
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from qqmusic_api.comment import get_hot_comments
|
|
4
|
+
|
|
5
|
+
pytestmark = pytest.mark.asyncio(loop_scope="session")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def test_get_comment():
|
|
9
|
+
comment = await get_hot_comments(
|
|
10
|
+
"542574330",
|
|
11
|
+
1,
|
|
12
|
+
10,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
assert comment[0]["Content"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Web Port 使用说明
|
|
2
|
+
|
|
3
|
+
## 1. 安装与运行
|
|
4
|
+
|
|
5
|
+
### 克隆仓库
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/luren-dc/QQMusicApi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### 依赖安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv sync --group web
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 启动服务
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run uvicorn web.app:app --host 0.0.0.0 --port 8000 --reload
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Docker
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
docker build . -t qq-music-api
|
|
27
|
+
docker run -d -p 8000:8000 qq-music-api
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. API Endpoint
|
|
31
|
+
|
|
32
|
+
- **请求格式**: `GET /{module}/{func}`
|
|
33
|
+
- **示例**:
|
|
34
|
+
`GET /song/get_detail?id=12345`
|
|
35
|
+
|
|
36
|
+
## 3. 请求参数规则
|
|
37
|
+
|
|
38
|
+
### 类型转换规则
|
|
39
|
+
|
|
40
|
+
| 参数类型 | 示例值 | 说明 |
|
|
41
|
+
| ----------- | ------------------------------- | ------------------------------------ |
|
|
42
|
+
| `int` | `count=5` | 整数 |
|
|
43
|
+
| `bool` | `is_vip=true` | `true`/`1`/`yes` 或 `false`/`0`/`no` |
|
|
44
|
+
| `datetime` | `date=2023-10-01T12:34` | ISO 8601 格式 |
|
|
45
|
+
| `list[int]` | `id=1,2,3` | 逗号分隔的字符串 |
|
|
46
|
+
| `Enum` | `type=SongType.HIT`or`type=HIT` | 枚举名或值(见具体模块定义) |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.3.3 → qqmusic_api_python-0.3.5}/qqmusic_api/exceptions/api_exception.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|