fuo-qqmusic 1.0.5__py3-none-any.whl
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.
Potentially problematic release.
This version of fuo-qqmusic might be problematic. Click here for more details.
- fuo_qqmusic/__init__.py +25 -0
- fuo_qqmusic/api.py +694 -0
- fuo_qqmusic/assets/icon.svg +10 -0
- fuo_qqmusic/consts.py +6 -0
- fuo_qqmusic/excs.py +6 -0
- fuo_qqmusic/provider.py +447 -0
- fuo_qqmusic/provider_ui.py +102 -0
- fuo_qqmusic/schemas.py +359 -0
- fuo_qqmusic-1.0.5.dist-info/METADATA +19 -0
- fuo_qqmusic-1.0.5.dist-info/RECORD +13 -0
- fuo_qqmusic-1.0.5.dist-info/WHEEL +5 -0
- fuo_qqmusic-1.0.5.dist-info/entry_points.txt +2 -0
- fuo_qqmusic-1.0.5.dist-info/top_level.txt +1 -0
fuo_qqmusic/schemas.py
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from marshmallow import Schema, fields, post_load, EXCLUDE
|
|
4
|
+
from feeluown.library import (
|
|
5
|
+
SongModel,
|
|
6
|
+
BriefAlbumModel,
|
|
7
|
+
BriefArtistModel,
|
|
8
|
+
ArtistModel,
|
|
9
|
+
AlbumModel,
|
|
10
|
+
PlaylistModel,
|
|
11
|
+
BriefPlaylistModel,
|
|
12
|
+
UserModel,
|
|
13
|
+
VideoModel,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BaseSchema(Schema):
|
|
20
|
+
source = fields.Str(missing="qqmusic")
|
|
21
|
+
|
|
22
|
+
class Meta:
|
|
23
|
+
unknown = EXCLUDE
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
SOURCE = "qqmusic"
|
|
27
|
+
Schema = BaseSchema
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_cover(mid, type_):
|
|
31
|
+
"""获取专辑、歌手封面
|
|
32
|
+
|
|
33
|
+
:param type_: 专辑: 2,歌手:1
|
|
34
|
+
"""
|
|
35
|
+
return f'http://y.gtimg.cn/music/photo_new/T00{type_}R800x800M000{mid}.jpg'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_model(model_cls, data, fields_to_cache=None):
|
|
39
|
+
"""
|
|
40
|
+
maybe this function should be provided by feeluown
|
|
41
|
+
|
|
42
|
+
:param fields_to_cache: list of fields name to be cached
|
|
43
|
+
"""
|
|
44
|
+
if fields_to_cache is not None:
|
|
45
|
+
cache_data = {}
|
|
46
|
+
for field in fields_to_cache:
|
|
47
|
+
value = data.pop(field)
|
|
48
|
+
if value is not None:
|
|
49
|
+
cache_data[field] = value
|
|
50
|
+
model = model_cls(**data)
|
|
51
|
+
for field, value in cache_data.items():
|
|
52
|
+
model.cache_set(field, value)
|
|
53
|
+
else:
|
|
54
|
+
model = model_cls(**data)
|
|
55
|
+
return model
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def pop_album_from_data(data):
|
|
59
|
+
album_id = data.pop("albumid")
|
|
60
|
+
album_mid = data.pop("albummid")
|
|
61
|
+
album_name = data.pop("albumname")
|
|
62
|
+
album_data = {
|
|
63
|
+
"identifier": album_id,
|
|
64
|
+
"source": SOURCE,
|
|
65
|
+
"mid": album_mid,
|
|
66
|
+
"name": album_name,
|
|
67
|
+
}
|
|
68
|
+
return create_model(BriefAlbumModel, album_data, ["mid"])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _SongArtistSchema(Schema):
|
|
72
|
+
identifier = fields.Int(data_key="id", required=True)
|
|
73
|
+
name = fields.Str(data_key="name", required=True)
|
|
74
|
+
|
|
75
|
+
@post_load
|
|
76
|
+
def create_model(self, data, **kwargs):
|
|
77
|
+
return BriefArtistModel(**data)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _SongAlbumSchema(Schema):
|
|
81
|
+
identifier = fields.Int(data_key="id", required=True)
|
|
82
|
+
name = fields.Str(data_key="name", required=True)
|
|
83
|
+
|
|
84
|
+
@post_load
|
|
85
|
+
def create_model(self, data, **kwargs):
|
|
86
|
+
return BriefAlbumModel(**data)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class QQSongSchema(Schema):
|
|
90
|
+
identifier = fields.Int(data_key="id", required=True)
|
|
91
|
+
mid = fields.Str(data_key="mid", required=True)
|
|
92
|
+
duration = fields.Float(data_key="interval", required=True)
|
|
93
|
+
title = fields.Str(data_key="title", required=True)
|
|
94
|
+
artists = fields.List(fields.Nested("_SongArtistSchema"), data_key="singer")
|
|
95
|
+
album = fields.Nested("_SongAlbumSchema", required=True)
|
|
96
|
+
files = fields.Dict(data_key="file", missing={})
|
|
97
|
+
mv = fields.Dict(required=True)
|
|
98
|
+
|
|
99
|
+
@post_load
|
|
100
|
+
def create_model(self, data, **kwargs):
|
|
101
|
+
song = SongModel(
|
|
102
|
+
identifier=data["identifier"],
|
|
103
|
+
source=SOURCE,
|
|
104
|
+
duration=data["duration"] * 1000,
|
|
105
|
+
title=data["title"],
|
|
106
|
+
artists=data.get("artists", []),
|
|
107
|
+
album=data["album"],
|
|
108
|
+
)
|
|
109
|
+
song.cache_set("mid", data["mid"])
|
|
110
|
+
song.cache_set("media_id", data["files"]["media_mid"])
|
|
111
|
+
song.cache_set("mv_id", data["mv"].get("vid", 0))
|
|
112
|
+
# 记录有哪些资源文件, 没有权限的用户依然获取不到
|
|
113
|
+
quality_suffix = []
|
|
114
|
+
files = data["files"]
|
|
115
|
+
if files.get("size_flac"): # has key and value is not empty
|
|
116
|
+
quality_suffix.append(("shq", "F000", 800, "flac"))
|
|
117
|
+
elif files.get("size_ape"):
|
|
118
|
+
quality_suffix.append(("shq", "A000", 800, "ape"))
|
|
119
|
+
if files.get("size_320") or files.get("size_320mp3"):
|
|
120
|
+
quality_suffix.append(("hq", "M800", 320, "ape"))
|
|
121
|
+
if files.get("size_aac") or files.get("size_192aac"):
|
|
122
|
+
quality_suffix.append(("sq", "C600", 192, "m4a"))
|
|
123
|
+
if files.get("size_128") or files.get("size_128mp3"):
|
|
124
|
+
quality_suffix.append(("lq", "M500", 128, "mp3"))
|
|
125
|
+
song.cache_set("quality_suffix", quality_suffix)
|
|
126
|
+
return song
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class _ArtistSongSchema(Schema):
|
|
130
|
+
value = fields.Nested(QQSongSchema, data_key="songInfo")
|
|
131
|
+
|
|
132
|
+
@post_load
|
|
133
|
+
def create_model(self, data, **kwargs):
|
|
134
|
+
return data["value"]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class _BriefArtistSchema(Schema):
|
|
138
|
+
identifier = fields.Int(data_key="singerID", required=True)
|
|
139
|
+
mid = fields.Str(data_key="singerMID", required=True)
|
|
140
|
+
name = fields.Str(data_key="singerName", required=True)
|
|
141
|
+
|
|
142
|
+
@post_load
|
|
143
|
+
def create_model(self, data, **kwargs):
|
|
144
|
+
return create_model(BriefArtistModel, data, ["mid"])
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SearchArtistSchema(_BriefArtistSchema):
|
|
148
|
+
pic_url = fields.Str(data_key="singerPic", required=True)
|
|
149
|
+
|
|
150
|
+
@post_load
|
|
151
|
+
def create_model(self, data, **kwargs):
|
|
152
|
+
data['hot_songs'] = []
|
|
153
|
+
data['description'] = ''
|
|
154
|
+
data['aliases'] = []
|
|
155
|
+
return create_model(ArtistModel, data, ["mid"])
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class _BriefAlbumSchema(Schema):
|
|
159
|
+
identifier = fields.Int(data_key="albumID", required=True)
|
|
160
|
+
mid = fields.Str(data_key="albumMID", required=True)
|
|
161
|
+
name = fields.Str(data_key="albumName", required=True)
|
|
162
|
+
|
|
163
|
+
@post_load
|
|
164
|
+
def create_model(self, data, **kwargs):
|
|
165
|
+
return create_model(BriefAlbumModel, data, ["mid"])
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class SearchAlbumSchema(_BriefAlbumSchema):
|
|
169
|
+
cover = fields.Str(data_key="albumPic", required=True)
|
|
170
|
+
released = fields.Str(data_key="publicTime", required=True)
|
|
171
|
+
song_count = fields.Int(required=True)
|
|
172
|
+
artists = fields.List(fields.Nested(_SongArtistSchema),
|
|
173
|
+
data_key="singer_list",
|
|
174
|
+
required=True)
|
|
175
|
+
@post_load
|
|
176
|
+
def create_model(self, data, **kwargs):
|
|
177
|
+
data['description'] = ''
|
|
178
|
+
data['songs'] = []
|
|
179
|
+
return create_model(AlbumModel, data, ['mid'])
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class _BriefPlaylistSchema(Schema):
|
|
183
|
+
identifier = fields.Int(data_key="dissid", required=True)
|
|
184
|
+
name = fields.Str(data_key="dissname", required=True)
|
|
185
|
+
cover = fields.Str(data_key="imgurl", required=True)
|
|
186
|
+
|
|
187
|
+
@post_load
|
|
188
|
+
def create_model(self, data, **kwargs):
|
|
189
|
+
data['description'] = ''
|
|
190
|
+
return create_model(PlaylistModel, **data)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PlaylistUserSchema(Schema):
|
|
194
|
+
identifier = fields.Str(data_key="creator_uin", required=True)
|
|
195
|
+
mid = fields.Str(data_key="encrypt_uin", required=True)
|
|
196
|
+
name = fields.Str(required=True)
|
|
197
|
+
avatar_url = fields.Str(required=True, data_key="avatarUrl")
|
|
198
|
+
|
|
199
|
+
@post_load
|
|
200
|
+
def create_model(self, data, **kwargs):
|
|
201
|
+
return create_model(UserModel, data, ['mid'])
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class SearchPlaylistSchema(_BriefPlaylistSchema):
|
|
205
|
+
creator = fields.Nested("PlaylistUserSchema", required=True)
|
|
206
|
+
description = fields.Str(data_key="introduction", required=True)
|
|
207
|
+
play_count = fields.Int(data_key="listennum", required=True)
|
|
208
|
+
|
|
209
|
+
@post_load
|
|
210
|
+
def create_model(self, data, **kwargs):
|
|
211
|
+
return create_model(PlaylistModel, data)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class QQArtistSchema(Schema):
|
|
215
|
+
"""歌手详情 Schema、歌曲歌手简要信息 Schema"""
|
|
216
|
+
|
|
217
|
+
identifier = fields.Int(data_key="singer_id", required=True)
|
|
218
|
+
mid = fields.Str(data_key="singer_mid", required=True)
|
|
219
|
+
name = fields.Str(data_key="singer_name", required=True)
|
|
220
|
+
|
|
221
|
+
description = fields.Str(data_key="SingerDesc", missing="")
|
|
222
|
+
hot_songs = fields.List(
|
|
223
|
+
fields.Nested(_ArtistSongSchema), data_key="list", missing=list
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
@post_load
|
|
227
|
+
def create_model(self, data, **kwargs):
|
|
228
|
+
data["pic_url"] = get_cover(data['mid'], 1)
|
|
229
|
+
data["aliases"] = []
|
|
230
|
+
return create_model(ArtistModel, data, ["mid"])
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class QQAlbumSchema(Schema):
|
|
234
|
+
album_info = fields.Dict(data_key="getAlbumInfo", required=True)
|
|
235
|
+
album_desc = fields.Dict(data_key="getAlbumDesc", required=True)
|
|
236
|
+
# 非中文专辑会把专辑的中文翻译加进去, 为保持前后一致此外去掉翻译文字
|
|
237
|
+
artist_info = fields.Dict(data_key="getSingerInfo", required=True)
|
|
238
|
+
|
|
239
|
+
# 有的专辑歌曲列表为 null,比如:fuo://qqmusic/albums/8623
|
|
240
|
+
songs = fields.List(
|
|
241
|
+
fields.Nested(QQSongSchema), data_key="getSongInfo", allow_none=True
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
@post_load
|
|
245
|
+
def create_model(self, data, **kwargs):
|
|
246
|
+
singer_name = data["artist_info"]["Fsinger_name"]
|
|
247
|
+
artist = BriefArtistModel(
|
|
248
|
+
identifier=data["artist_info"]["Fsinger_id"],
|
|
249
|
+
source=SOURCE,
|
|
250
|
+
# split('/'):有的专辑有个多歌手,只有第一个才是正确的专辑艺人
|
|
251
|
+
# split('('):有的非中文歌手拥有别名在括号里
|
|
252
|
+
name=singer_name.split("/")[0].split("(")[0].strip(),
|
|
253
|
+
)
|
|
254
|
+
# 非中文专辑会把专辑的中文翻译加进去, 为保持前后一致此外去掉括号里的中文翻译
|
|
255
|
+
if data["songs"]:
|
|
256
|
+
album_name = data["songs"][0].album.name
|
|
257
|
+
else:
|
|
258
|
+
album_name = data["album_info"]["Falbum_name"]
|
|
259
|
+
mid = data["album_info"]["Falbum_mid"]
|
|
260
|
+
album = AlbumModel(
|
|
261
|
+
identifier=data["album_info"]["Falbum_id"],
|
|
262
|
+
source=SOURCE,
|
|
263
|
+
name=album_name,
|
|
264
|
+
description=data["album_desc"]["Falbum_desc"],
|
|
265
|
+
songs=data["songs"] or [],
|
|
266
|
+
artists=[artist],
|
|
267
|
+
cover=get_cover(mid, 2)
|
|
268
|
+
)
|
|
269
|
+
album.cache_set("mid", mid)
|
|
270
|
+
return album
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class QQPlaylistSchema(Schema):
|
|
274
|
+
identifier = fields.Int(required=True, data_key="dissid")
|
|
275
|
+
name = fields.Str(required=True, data_key="dissname")
|
|
276
|
+
cover = fields.Str(required=True, data_key="logo")
|
|
277
|
+
# songs field maybe null, though it can't be null in model
|
|
278
|
+
songs = fields.List(
|
|
279
|
+
fields.Nested(QQSongSchema), data_key="songlist", allow_none=True
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
@post_load
|
|
283
|
+
def create_model(self, data, **kwargs):
|
|
284
|
+
fields_to_cache = []
|
|
285
|
+
if data.get('songs') is not None:
|
|
286
|
+
fields_to_cache = ['songs']
|
|
287
|
+
data['description'] = ''
|
|
288
|
+
return create_model(PlaylistModel, data, fields_to_cache)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class _UserArtistSchema(Schema):
|
|
292
|
+
other_infos = fields.Dict(data_key="OtherInfo", required=True)
|
|
293
|
+
mid = fields.Str(data_key="MID", required=True)
|
|
294
|
+
name = fields.Str(data_key="Name", required=True)
|
|
295
|
+
|
|
296
|
+
@post_load
|
|
297
|
+
def create_model(self, data, **kwargs):
|
|
298
|
+
data["identifier"] = data.pop("other_infos")["SingerID"]
|
|
299
|
+
return create_model(BriefArtistModel, data, ['mid'])
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class _UserAlbumSchema(Schema):
|
|
303
|
+
identifier = fields.Int(data_key="albumid", required=True)
|
|
304
|
+
mid = fields.Str(data_key="albummid", required=True)
|
|
305
|
+
name = fields.Str(data_key="albumname", required=True)
|
|
306
|
+
|
|
307
|
+
@post_load
|
|
308
|
+
def create_model(self, data, **kwargs):
|
|
309
|
+
return create_model(BriefAlbumModel, data, ["mid"])
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class _UserPlaylistSchema(Schema):
|
|
313
|
+
identifier = fields.Int(data_key="dissid", required=True)
|
|
314
|
+
name = fields.Str(data_key="title", required=True)
|
|
315
|
+
|
|
316
|
+
@post_load
|
|
317
|
+
def create_model(self, data, **kwargs):
|
|
318
|
+
return create_model(BriefPlaylistModel, data)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class QQUserSchema(Schema):
|
|
322
|
+
creator = fields.Dict(required=True)
|
|
323
|
+
mydiss = fields.Dict(required=True)
|
|
324
|
+
|
|
325
|
+
@post_load
|
|
326
|
+
def create_model(self, data, **kwargs):
|
|
327
|
+
creator = data["creator"]
|
|
328
|
+
playlists_data = data["mydiss"]["list"]
|
|
329
|
+
schema = _UserPlaylistSchema()
|
|
330
|
+
playlists = []
|
|
331
|
+
for each in playlists_data:
|
|
332
|
+
playlist = schema.load(each)
|
|
333
|
+
playlists.append(playlist)
|
|
334
|
+
data = dict(
|
|
335
|
+
identifier=creator["uin"],
|
|
336
|
+
source=SOURCE,
|
|
337
|
+
mid=creator["encrypt_uin"],
|
|
338
|
+
name=creator["nick"],
|
|
339
|
+
fav_pid=creator["fav_pid"],
|
|
340
|
+
avatar_url=creator["headpic"],
|
|
341
|
+
playlists=playlists,
|
|
342
|
+
)
|
|
343
|
+
return create_model(UserModel, data, ['mid', 'fav_pid', 'playlists'])
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class SearchMVSchema(Schema):
|
|
347
|
+
# 使用 mv_id 字段的话,目前拿不到播放 url,用 v_id 比较合适
|
|
348
|
+
identifier = fields.Str(data_key="v_id", required=True)
|
|
349
|
+
title = fields.Str(data_key="mv_name", required=True)
|
|
350
|
+
artists = fields.List(fields.Nested("_SongArtistSchema"),
|
|
351
|
+
data_key="singer_list",
|
|
352
|
+
required=True)
|
|
353
|
+
duration = fields.Int(required=True)
|
|
354
|
+
cover = fields.Str(data_key="mv_pic_url", required=True)
|
|
355
|
+
play_count = fields.Int(required=True)
|
|
356
|
+
|
|
357
|
+
@post_load
|
|
358
|
+
def create_model(self, data, **kwargs):
|
|
359
|
+
return create_model(VideoModel, data)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: fuo_qqmusic
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: feeluown qqmusic plugin
|
|
5
|
+
Home-page: https://github.com/feeluown/feeluown-qqmusic
|
|
6
|
+
Author: Cosven
|
|
7
|
+
Author-email: yinshaowen241@gmail.com
|
|
8
|
+
Keywords: feeluown,plugin,qqmusic
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Requires-Dist: feeluown (>=4.1.3)
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
Requires-Dist: marshmallow (>=3.0)
|
|
19
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
fuo_qqmusic/__init__.py,sha256=YxZHAp5Qaye5B5V8GBPyxqnEUe-FcwUv6TiF_29qBEs,537
|
|
2
|
+
fuo_qqmusic/api.py,sha256=w_anRMxAqL9FaMJVRmZ3A_uV4rzr7_S_1xMjZf-uovY,23597
|
|
3
|
+
fuo_qqmusic/consts.py,sha256=9VFc9yE6z6c0IzK03PQTEO0JqgXi37RahEvZRP-A4Dk,185
|
|
4
|
+
fuo_qqmusic/excs.py,sha256=SBcER_wESjvui6a0Li1dX5utep4ouDS7HaTXqXPOQiA,160
|
|
5
|
+
fuo_qqmusic/provider.py,sha256=JmQtjPVPsaZ1n12phJDd_Ll3Y83RfaHb5S6i0d1xrjw,15669
|
|
6
|
+
fuo_qqmusic/provider_ui.py,sha256=UKmzK6SImyWZe_wtkjcfXH_gy2XXbcnnI4Er0fICM6I,3197
|
|
7
|
+
fuo_qqmusic/schemas.py,sha256=Sbka-YuK3UTn26CbfwOb0ZfB6XmiJHW-ibCLThj49VQ,12128
|
|
8
|
+
fuo_qqmusic/assets/icon.svg,sha256=o48VTiHroJU6S2g8LbxfnK25G2QW3d1XtQZKoL3ZMGA,1216
|
|
9
|
+
fuo_qqmusic-1.0.5.dist-info/METADATA,sha256=EgB-48xvBBqTnxxGYFAjK3rqBpaYK6hS5Csvdx5ubAM,675
|
|
10
|
+
fuo_qqmusic-1.0.5.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
11
|
+
fuo_qqmusic-1.0.5.dist-info/entry_points.txt,sha256=XUL7UwcsLZEuEbBJIcsFemC3BiYb7dSTiXB9r6SJ5zY,39
|
|
12
|
+
fuo_qqmusic-1.0.5.dist-info/top_level.txt,sha256=Qn_g3dRwtoVZgVPZ9uRehspXi62T-E1x9_kLIdYFic4,12
|
|
13
|
+
fuo_qqmusic-1.0.5.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fuo_qqmusic
|