qqmusic-api-python 0.1.4__tar.gz → 0.1.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.1.4 → qqmusic_api_python-0.1.5}/PKG-INFO +2 -2
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/README.md +1 -1
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/pyproject.toml +6 -9
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/__init__.py +1 -1
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/album.py +15 -4
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/album.json +3 -1
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/login.py +28 -18
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/singer.py +0 -1
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/song.py +1 -1
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/network.py +2 -2
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/LICENSE +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/login.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/mv.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/search.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/singer.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/song.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/songlist.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/api/top.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/file_type.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/data/search_type.json +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/ApiException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/CredentialNoMusicidException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/CredentialNoMusickeyException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/CredentialNoRefreshkeyException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/LoginException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/ResponseCodeException.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/__init__.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/mv.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/search.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/songlist.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/top.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/__init__.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/credential.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/qimei.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/sync.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/utils/utils.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/__init__.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/conftest.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_album.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_login.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_mv.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_search.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_singer.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_song.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_songlist.py +0 -0
- {qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/tests/test_top.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qqmusic-api-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: QQ音乐API封装库
|
|
5
5
|
Keywords: music,api,qqmusic,tencentmusic
|
|
6
6
|
Home-page: https://github.com/luren-dc/QQMusicApi
|
|
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|
<a href="https://pdm-project.org">
|
|
40
40
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json" alt="pdm-managed">
|
|
41
41
|
</a>
|
|
42
|
-
<a href="https://github.com/luren-dc/QQMusicApi
|
|
42
|
+
<a href="https://github.com/luren-dc/QQMusicApi?tab=MIT-1-ov-file">
|
|
43
43
|
<img src="https://img.shields.io/github/license/luren-dc/PyQQMusicApi" alt="GitHub license">
|
|
44
44
|
</a>
|
|
45
45
|
<a href="https://github.com/luren-dc/QQMusicApi/stargazers">
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="https://pdm-project.org">
|
|
11
11
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json" alt="pdm-managed">
|
|
12
12
|
</a>
|
|
13
|
-
<a href="https://github.com/luren-dc/QQMusicApi
|
|
13
|
+
<a href="https://github.com/luren-dc/QQMusicApi?tab=MIT-1-ov-file">
|
|
14
14
|
<img src="https://img.shields.io/github/license/luren-dc/PyQQMusicApi" alt="GitHub license">
|
|
15
15
|
</a>
|
|
16
16
|
<a href="https://github.com/luren-dc/QQMusicApi/stargazers">
|
|
@@ -40,7 +40,7 @@ classifiers = [
|
|
|
40
40
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
41
41
|
]
|
|
42
42
|
dynamic = []
|
|
43
|
-
version = "0.1.
|
|
43
|
+
version = "0.1.5"
|
|
44
44
|
|
|
45
45
|
[project.license]
|
|
46
46
|
text = "MIT"
|
|
@@ -79,14 +79,6 @@ linting = [
|
|
|
79
79
|
"ruff>=0.5.4",
|
|
80
80
|
]
|
|
81
81
|
|
|
82
|
-
[tool.pdm.build]
|
|
83
|
-
source-includes = [
|
|
84
|
-
"qqmusic_api",
|
|
85
|
-
"README.md",
|
|
86
|
-
"LICENSE",
|
|
87
|
-
"tests",
|
|
88
|
-
]
|
|
89
|
-
|
|
90
82
|
[tool.pdm.scripts]
|
|
91
83
|
docs = "mkdocs serve"
|
|
92
84
|
|
|
@@ -129,6 +121,11 @@ convention = "google"
|
|
|
129
121
|
"tests/*" = [
|
|
130
122
|
"D",
|
|
131
123
|
]
|
|
124
|
+
"qqmusic_api/login.py" = [
|
|
125
|
+
"F405",
|
|
126
|
+
"F403",
|
|
127
|
+
"D102",
|
|
128
|
+
]
|
|
132
129
|
"qqmusic_api/__init__.py" = [
|
|
133
130
|
"F405",
|
|
134
131
|
"F403",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""专辑相关 API"""
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from .song import Song
|
|
4
6
|
from .utils.network import Api
|
|
5
7
|
from .utils.utils import get_api
|
|
@@ -14,16 +16,25 @@ class Album:
|
|
|
14
16
|
mid: 专辑 mid
|
|
15
17
|
"""
|
|
16
18
|
|
|
17
|
-
def __init__(
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
mid: Optional[str] = None,
|
|
22
|
+
id: Optional[int] = None,
|
|
23
|
+
):
|
|
18
24
|
"""初始化专辑类
|
|
19
25
|
|
|
20
26
|
Args:
|
|
21
27
|
mid: 专辑 mid
|
|
28
|
+
id: 专辑 id
|
|
22
29
|
"""
|
|
30
|
+
# ID 检查
|
|
31
|
+
if mid is None and id is None:
|
|
32
|
+
raise ValueError("mid or id must be provided")
|
|
23
33
|
self.mid = mid
|
|
34
|
+
self.id = id
|
|
24
35
|
|
|
25
36
|
def __repr__(self) -> str:
|
|
26
|
-
return f"Album(mid={self.mid})"
|
|
37
|
+
return f"Album(mid={self.mid}, id={self.id})"
|
|
27
38
|
|
|
28
39
|
async def get_detail(self) -> dict:
|
|
29
40
|
"""获取专辑详细信息
|
|
@@ -31,7 +42,7 @@ class Album:
|
|
|
31
42
|
Returns:
|
|
32
43
|
专辑详细信息
|
|
33
44
|
"""
|
|
34
|
-
return await Api(**API["detail"]).update_params(albumMid=self.mid).result
|
|
45
|
+
return await Api(**API["detail"]).update_params(albumMid=self.mid, albumId=self.id).result
|
|
35
46
|
|
|
36
47
|
async def get_song(self) -> list[Song]:
|
|
37
48
|
"""获取专辑歌曲
|
|
@@ -39,5 +50,5 @@ class Album:
|
|
|
39
50
|
Returns:
|
|
40
51
|
歌曲列表
|
|
41
52
|
"""
|
|
42
|
-
result = await Api(**API["song"]).update_params(albumMid=self.mid, begin=0, num=0).result
|
|
53
|
+
result = await Api(**API["song"]).update_params(albumMid=self.mid, albumId=self.id, begin=0, num=0).result
|
|
43
54
|
return [Song.from_dict(song["songInfo"]) for song in result["songList"]]
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"module": "music.musichallAlbum.AlbumInfoServer",
|
|
4
4
|
"method": "GetAlbumDetail",
|
|
5
5
|
"params": {
|
|
6
|
-
"albumMid": "str 专辑 mid"
|
|
6
|
+
"albumMid": "str 专辑 mid",
|
|
7
|
+
"albumId": "str 专辑 id"
|
|
7
8
|
},
|
|
8
9
|
"comment": "获取专辑信息"
|
|
9
10
|
},
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
"method": "GetAlbumSongList",
|
|
13
14
|
"params": {
|
|
14
15
|
"albumMid": "str 专辑 mid",
|
|
16
|
+
"albumId": "str 专辑 id",
|
|
15
17
|
"begin": "int 开启位置",
|
|
16
18
|
"num": "int 返回数量"
|
|
17
19
|
},
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import random
|
|
4
4
|
import re
|
|
5
|
+
import sys
|
|
5
6
|
import time
|
|
6
7
|
import uuid
|
|
7
8
|
from abc import ABC, abstractmethod
|
|
@@ -9,7 +10,11 @@ from enum import Enum
|
|
|
9
10
|
from typing import Optional
|
|
10
11
|
|
|
11
12
|
import aiohttp
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
if sys.version_info >= (3, 12):
|
|
15
|
+
from typing import override
|
|
16
|
+
else:
|
|
17
|
+
from typing_extensions import override
|
|
13
18
|
|
|
14
19
|
from .exceptions import LoginException, ResponseCodeException
|
|
15
20
|
from .utils.credential import Credential
|
|
@@ -51,7 +56,7 @@ class PhoneLoginEvents(Enum):
|
|
|
51
56
|
OTHER = 5
|
|
52
57
|
|
|
53
58
|
|
|
54
|
-
class Login:
|
|
59
|
+
class Login(ABC):
|
|
55
60
|
"""登录基类
|
|
56
61
|
|
|
57
62
|
Attributes:
|
|
@@ -64,7 +69,7 @@ class Login:
|
|
|
64
69
|
self.credential: Optional[Credential] = None
|
|
65
70
|
|
|
66
71
|
|
|
67
|
-
class QRCodeLogin(Login
|
|
72
|
+
class QRCodeLogin(Login):
|
|
68
73
|
"""二维码登录基类
|
|
69
74
|
|
|
70
75
|
Attributes:
|
|
@@ -76,14 +81,19 @@ class QRCodeLogin(Login, ABC):
|
|
|
76
81
|
self.musicid = ""
|
|
77
82
|
self._state: Optional[QrCodeLoginEvents] = None
|
|
78
83
|
self._qrcode_data: Optional[bytes] = None
|
|
84
|
+
self._session = aiohttp.ClientSession()
|
|
85
|
+
|
|
86
|
+
async def close(self):
|
|
87
|
+
"""关闭登录会话"""
|
|
88
|
+
if not self._session.closed:
|
|
89
|
+
await self._session.close()
|
|
79
90
|
|
|
80
91
|
async def __aenter__(self):
|
|
81
|
-
self.
|
|
92
|
+
await self._session.__aenter__()
|
|
82
93
|
return self
|
|
83
94
|
|
|
84
95
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
85
|
-
|
|
86
|
-
await self.session.close()
|
|
96
|
+
await self.close()
|
|
87
97
|
|
|
88
98
|
@abstractmethod
|
|
89
99
|
async def get_qrcode(self) -> bytes:
|
|
@@ -128,7 +138,7 @@ class QQLogin(QRCodeLogin):
|
|
|
128
138
|
QrCodeLoginEvents.DONE,
|
|
129
139
|
]:
|
|
130
140
|
return self._qrcode_data
|
|
131
|
-
async with self.
|
|
141
|
+
async with self._session.get(
|
|
132
142
|
"https://xui.ptlogin2.qq.com/cgi-bin/xlogin",
|
|
133
143
|
params={
|
|
134
144
|
"appid": "716027609",
|
|
@@ -146,7 +156,7 @@ class QQLogin(QRCodeLogin):
|
|
|
146
156
|
},
|
|
147
157
|
) as res:
|
|
148
158
|
self.sig = res.cookies.get("pt_login_sig").value
|
|
149
|
-
async with self.
|
|
159
|
+
async with self._session.get(
|
|
150
160
|
"https://ssl.ptlogin2.qq.com/ptqrshow",
|
|
151
161
|
params={
|
|
152
162
|
"appid": "716027609",
|
|
@@ -168,7 +178,7 @@ class QQLogin(QRCodeLogin):
|
|
|
168
178
|
async def get_qrcode_state(self):
|
|
169
179
|
if not self._qrcode_data:
|
|
170
180
|
raise LoginException("请先获取二维码")
|
|
171
|
-
async with self.
|
|
181
|
+
async with self._session.get(
|
|
172
182
|
"https://ssl.ptlogin2.qq.com/ptqrlogin",
|
|
173
183
|
params={
|
|
174
184
|
"u1": "https://graph.qq.com/oauth2.0/login_jump",
|
|
@@ -216,7 +226,7 @@ class QQLogin(QRCodeLogin):
|
|
|
216
226
|
if self._state != QrCodeLoginEvents.DONE:
|
|
217
227
|
raise LoginException("未完成二维码认证")
|
|
218
228
|
# TODO: 优化获取 p_skey
|
|
219
|
-
async with self.
|
|
229
|
+
async with self._session.get(self.auth_url, allow_redirects=False) as res: # type: ignore
|
|
220
230
|
from http import cookies
|
|
221
231
|
|
|
222
232
|
set_cookie_header = res.headers.getall("Set-Cookie", [])
|
|
@@ -225,10 +235,10 @@ class QQLogin(QRCodeLogin):
|
|
|
225
235
|
cookie.load(header)
|
|
226
236
|
for key, morsel in cookie.items():
|
|
227
237
|
if morsel.value:
|
|
228
|
-
self.
|
|
238
|
+
self._session.cookie_jar.update_cookies(cookie)
|
|
229
239
|
|
|
230
|
-
skey = self.
|
|
231
|
-
async with self.
|
|
240
|
+
skey = self._session.cookie_jar.filter_cookies(self.auth_url).get("p_skey").value # type: ignore
|
|
241
|
+
async with self._session.post(
|
|
232
242
|
"https://graph.qq.com/oauth2.0/authorize",
|
|
233
243
|
params={
|
|
234
244
|
"Referer": "https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id"
|
|
@@ -277,7 +287,7 @@ class WXLogin(QRCodeLogin):
|
|
|
277
287
|
QrCodeLoginEvents.DONE,
|
|
278
288
|
]:
|
|
279
289
|
return self._qrcode_data
|
|
280
|
-
async with self.
|
|
290
|
+
async with self._session.get(
|
|
281
291
|
"https://open.weixin.qq.com/connect/qrconnect",
|
|
282
292
|
params={
|
|
283
293
|
"appid": "wx48db31d50e334801",
|
|
@@ -289,7 +299,7 @@ class WXLogin(QRCodeLogin):
|
|
|
289
299
|
},
|
|
290
300
|
) as res:
|
|
291
301
|
self.__uuid = re.findall(r"uuid=(.+?)\"", await res.text())[0]
|
|
292
|
-
async with self.
|
|
302
|
+
async with self._session.get(
|
|
293
303
|
f"https://open.weixin.qq.com/connect/qrcode/{self.__uuid}",
|
|
294
304
|
headers={
|
|
295
305
|
"referer": "https://open.weixin.qq.com/connect/qrconnect?appid=wx48db31d50e334801"
|
|
@@ -308,7 +318,7 @@ class WXLogin(QRCodeLogin):
|
|
|
308
318
|
async def get_qrcode_state(self):
|
|
309
319
|
if not self._qrcode_data:
|
|
310
320
|
raise LoginException("请先获取二维码")
|
|
311
|
-
async with self.
|
|
321
|
+
async with self._session.get(
|
|
312
322
|
"https://lp.open.weixin.qq.com/connect/l/qrconnect",
|
|
313
323
|
headers={
|
|
314
324
|
"referer": "https://open.weixin.qq.com/",
|
|
@@ -338,7 +348,7 @@ class WXLogin(QRCodeLogin):
|
|
|
338
348
|
"https://y.qq.com/portal/wx_redirect.html?login_type=2"
|
|
339
349
|
f"&surl=https://y.qq.com/&code={self.musicid}&state=STATE"
|
|
340
350
|
)
|
|
341
|
-
await self.
|
|
351
|
+
await self._session.get(
|
|
342
352
|
"https://lp.open.weixin.qq.com/connect/l/qrconnect",
|
|
343
353
|
params={
|
|
344
354
|
"uuid": self.__uuid,
|
|
@@ -354,7 +364,7 @@ class WXLogin(QRCodeLogin):
|
|
|
354
364
|
return self.credential
|
|
355
365
|
if self._state != QrCodeLoginEvents.DONE:
|
|
356
366
|
raise LoginException("未完成二维码认证")
|
|
357
|
-
await self.
|
|
367
|
+
await self._session.get(self.auth_url, allow_redirects=False) # type: ignore
|
|
358
368
|
res = (
|
|
359
369
|
await Api(**API["WX_login"])
|
|
360
370
|
.update_params(strAppid="wx48db31d50e334801", code=self.musicid)
|
|
@@ -261,7 +261,7 @@ def __clean() -> None:
|
|
|
261
261
|
if s0 is not None and not s0.closed:
|
|
262
262
|
await s0.close()
|
|
263
263
|
|
|
264
|
-
if loop.is_closed():
|
|
264
|
+
if not loop.is_closed():
|
|
265
265
|
loop.run_until_complete(__clean_task())
|
|
266
266
|
else:
|
|
267
|
-
|
|
267
|
+
asyncio.run(__clean_task())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/ApiException.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qqmusic_api_python-0.1.4 → qqmusic_api_python-0.1.5}/qqmusic_api/exceptions/LoginException.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
|