fuo-qqmusic 1.0.1__tar.gz → 1.0.3__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.
Potentially problematic release.
This version of fuo-qqmusic might be problematic. Click here for more details.
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/PKG-INFO +1 -1
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/README.md +6 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/api.py +41 -12
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/provider.py +39 -4
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/schemas.py +1 -1
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/PKG-INFO +1 -1
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/requires.txt +1 -1
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/setup.py +2 -2
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/__init__.py +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/assets/icon.svg +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/consts.py +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/excs.py +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic/provider_ui.py +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/SOURCES.txt +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/dependency_links.txt +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/entry_points.txt +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/fuo_qqmusic.egg-info/top_level.txt +0 -0
- {fuo_qqmusic-1.0.1 → fuo_qqmusic-1.0.3}/setup.cfg +0 -0
|
@@ -20,6 +20,12 @@ pip3 install fuo-qqmusic
|
|
|
20
20
|
[操作示例](https://github.com/feeluown/feeluown-qqmusic/issues/6)。
|
|
21
21
|
|
|
22
22
|
## changelog
|
|
23
|
+
### 1.0.3 (2024-04-21)
|
|
24
|
+
- 适配 feeluown 4.1.3 的新主页功能
|
|
25
|
+
|
|
26
|
+
### 1.0.2 (2024-03-04)
|
|
27
|
+
- 修复获取歌单封面失败的问题 #21
|
|
28
|
+
- 修复获取歌曲播放链接失败的问题 #20
|
|
23
29
|
|
|
24
30
|
### 1.0 (2024-01-1)
|
|
25
31
|
- 使用 FeelUOwn 新接口
|
|
@@ -177,6 +177,25 @@ class API(object):
|
|
|
177
177
|
return None
|
|
178
178
|
return data_song
|
|
179
179
|
|
|
180
|
+
def batch_song_details(self, song_ids):
|
|
181
|
+
"""
|
|
182
|
+
song_ids should be a list of int
|
|
183
|
+
"""
|
|
184
|
+
payload = {
|
|
185
|
+
'comm': self.get_wkv17_common_params(),
|
|
186
|
+
'req_0': {
|
|
187
|
+
'module': 'music.trackInfo.UniformRuleCtrl',
|
|
188
|
+
'method': 'CgiGetTrackInfo',
|
|
189
|
+
'param': {
|
|
190
|
+
'ids': song_ids,
|
|
191
|
+
'types': [200]*len(song_ids),
|
|
192
|
+
'source': 'AiNoFree',
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
js = self.rpc(payload)
|
|
197
|
+
return js['req_0']['data']['tracks']
|
|
198
|
+
|
|
180
199
|
def song_similar(self, song_id):
|
|
181
200
|
payload = {
|
|
182
201
|
"simsongs": {
|
|
@@ -377,24 +396,24 @@ class API(object):
|
|
|
377
396
|
playlist = js['recomPlaylist']
|
|
378
397
|
return playlist['data']['v_hot']
|
|
379
398
|
|
|
380
|
-
def
|
|
399
|
+
def get_recommend_feed(self, page=1):
|
|
400
|
+
# APIs are found in https://y.qq.com/wk_v17/#/recommend
|
|
381
401
|
data = {
|
|
382
402
|
'req_0': {
|
|
383
403
|
'module': 'recommend.RecommendFeedServer',
|
|
384
404
|
'method': 'get_recommend_feed',
|
|
385
405
|
'param': {
|
|
386
406
|
'direction': 0,
|
|
387
|
-
'page':
|
|
407
|
+
'page': page,
|
|
388
408
|
'v_cache': [],
|
|
389
409
|
'v_uniq': [],
|
|
390
410
|
's_num': 0
|
|
391
411
|
}
|
|
392
412
|
},
|
|
413
|
+
'comm': self.get_wkv17_common_params(),
|
|
393
414
|
}
|
|
394
415
|
js = self.rpc(data)
|
|
395
|
-
|
|
396
|
-
for card in js['req_0']['data']['v_shelf'][index]['v_niche'][0]['v_card']]
|
|
397
|
-
return ids
|
|
416
|
+
return js['req_0']['data']
|
|
398
417
|
|
|
399
418
|
def get_lyric_by_songmid(self, songmid):
|
|
400
419
|
url = api_base_url + '/lyric/fcgi-bin/fcg_query_lyric_new.fcg'
|
|
@@ -426,6 +445,22 @@ class API(object):
|
|
|
426
445
|
CodeShouldBe0.check(js)
|
|
427
446
|
return js
|
|
428
447
|
|
|
448
|
+
def get_wkv17_common_params(self):
|
|
449
|
+
return {
|
|
450
|
+
# ct field is important, without this field,
|
|
451
|
+
# the req_0 result is completely different.
|
|
452
|
+
"ct": 20,
|
|
453
|
+
"cv": 1770,
|
|
454
|
+
'g_tk': 5381,
|
|
455
|
+
'uin': self._uin,
|
|
456
|
+
'format': 'json',
|
|
457
|
+
'inCharset': 'utf-8',
|
|
458
|
+
'outCharset': 'utf-8',
|
|
459
|
+
'platform': 'wk_v17',
|
|
460
|
+
'uid': '',
|
|
461
|
+
'guid': '',
|
|
462
|
+
}
|
|
463
|
+
|
|
429
464
|
def get_common_params(self):
|
|
430
465
|
return {
|
|
431
466
|
'loginUin': self._uin,
|
|
@@ -476,13 +511,7 @@ class API(object):
|
|
|
476
511
|
},
|
|
477
512
|
}
|
|
478
513
|
js = self.rpc(payload)
|
|
479
|
-
|
|
480
|
-
for track in js['songlist']['data']['tracks']:
|
|
481
|
-
track['songid'] = track.pop('id')
|
|
482
|
-
track['songmid'] = track.pop('mid')
|
|
483
|
-
track['songname'] = track.pop('name')
|
|
484
|
-
res_json.append(track)
|
|
485
|
-
return res_json
|
|
514
|
+
return js['songlist']['data']['tracks']
|
|
486
515
|
|
|
487
516
|
def get_song_url(self, song_mid):
|
|
488
517
|
uin = self._uin
|
|
@@ -3,6 +3,10 @@ from typing import List, Optional, Protocol
|
|
|
3
3
|
from feeluown.excs import ModelNotFound
|
|
4
4
|
from feeluown.library import (
|
|
5
5
|
AbstractProvider,
|
|
6
|
+
BriefSongModel,
|
|
7
|
+
PlaylistModel,
|
|
8
|
+
Collection,
|
|
9
|
+
CollectionType,
|
|
6
10
|
ProviderV2,
|
|
7
11
|
ProviderFlags as PF,
|
|
8
12
|
SupportsSongGet,
|
|
@@ -17,6 +21,7 @@ from feeluown.library import (
|
|
|
17
21
|
SupportsArtistGet,
|
|
18
22
|
SupportsPlaylistGet,
|
|
19
23
|
SupportsPlaylistSongsReader,
|
|
24
|
+
SupportsRecACollectionOfSongs,
|
|
20
25
|
SimpleSearchResult,
|
|
21
26
|
SearchType,
|
|
22
27
|
ModelType,
|
|
@@ -42,6 +47,7 @@ class Supports(
|
|
|
42
47
|
SupportsArtistGet,
|
|
43
48
|
SupportsPlaylistGet,
|
|
44
49
|
SupportsPlaylistSongsReader,
|
|
50
|
+
SupportsRecACollectionOfSongs,
|
|
45
51
|
Protocol,
|
|
46
52
|
):
|
|
47
53
|
pass
|
|
@@ -153,13 +159,13 @@ class QQProvider(AbstractProvider, ProviderV2):
|
|
|
153
159
|
:return: when quality is invalid, return None
|
|
154
160
|
"""
|
|
155
161
|
q_media_mapping = self._song_get_q_media_mapping(song)
|
|
156
|
-
quality_suffix =
|
|
157
|
-
mid =
|
|
158
|
-
media_id =
|
|
162
|
+
quality_suffix = self._model_cache_get_or_fetch(song, "quality_suffix")
|
|
163
|
+
mid = self._model_cache_get_or_fetch(song, "mid")
|
|
164
|
+
media_id = self._model_cache_get_or_fetch(song, "media_id")
|
|
159
165
|
media = q_media_mapping.get(quality)
|
|
160
166
|
if media is UNFETCHED_MEDIA:
|
|
161
167
|
for q, t, b, s in quality_suffix:
|
|
162
|
-
if quality == q:
|
|
168
|
+
if quality == Quality.Audio(q):
|
|
163
169
|
url = self.api.get_song_url_v2(mid, media_id, t)
|
|
164
170
|
if url:
|
|
165
171
|
media = Media(url, bitrate=b, format=s)
|
|
@@ -247,6 +253,35 @@ class QQProvider(AbstractProvider, ProviderV2):
|
|
|
247
253
|
pl["logo"] = pl["cover"]
|
|
248
254
|
return [_deserialize(playlist, QQPlaylistSchema) for playlist in playlists]
|
|
249
255
|
|
|
256
|
+
def rec_a_collection_of_songs(self):
|
|
257
|
+
# TODO: cache API result
|
|
258
|
+
feed = self.api.get_recommend_feed()
|
|
259
|
+
shelf = None
|
|
260
|
+
for shelf in feed['v_shelf']:
|
|
261
|
+
# I guess 10046 means 'song'.
|
|
262
|
+
if shelf['miscellany'].get('jumptype') == 10046:
|
|
263
|
+
shelf = shelf
|
|
264
|
+
if shelf is None:
|
|
265
|
+
return '', []
|
|
266
|
+
title = shelf['title_content'] or shelf['title_template']
|
|
267
|
+
song_ids = []
|
|
268
|
+
for batch in shelf['v_niche']:
|
|
269
|
+
for card in batch['v_card']:
|
|
270
|
+
if card['jumptype'] == 10046:
|
|
271
|
+
song_id = int(card['id'])
|
|
272
|
+
if song_id not in song_ids:
|
|
273
|
+
song_ids.append(song_id)
|
|
274
|
+
|
|
275
|
+
tracks = self.api.batch_song_details(song_ids)
|
|
276
|
+
return Collection(name=title,
|
|
277
|
+
type_=CollectionType.only_songs,
|
|
278
|
+
models=[_deserialize(track, QQSongSchema) for track in tracks],
|
|
279
|
+
description='')
|
|
280
|
+
|
|
281
|
+
def current_user_get_radio_songs(self):
|
|
282
|
+
songs_data = self.api.get_radio_music()
|
|
283
|
+
return [_deserialize(s, QQSongSchema) for s in songs_data]
|
|
284
|
+
|
|
250
285
|
def current_user_list_playlists(self):
|
|
251
286
|
user = self.get_current_user()
|
|
252
287
|
if user is None:
|
|
@@ -226,7 +226,7 @@ class QQAlbumSchema(Schema):
|
|
|
226
226
|
class QQPlaylistSchema(Schema):
|
|
227
227
|
identifier = fields.Int(required=True, data_key="dissid")
|
|
228
228
|
name = fields.Str(required=True, data_key="dissname")
|
|
229
|
-
cover = fields.
|
|
229
|
+
cover = fields.Str(required=True, data_key="logo")
|
|
230
230
|
# songs field maybe null, though it can't be null in model
|
|
231
231
|
songs = fields.List(
|
|
232
232
|
fields.Nested(QQSongSchema), data_key="songlist", allow_none=True
|
|
@@ -5,7 +5,7 @@ from setuptools import setup
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='fuo_qqmusic',
|
|
8
|
-
version='1.0.
|
|
8
|
+
version='1.0.3',
|
|
9
9
|
description='feeluown qqmusic plugin',
|
|
10
10
|
author='Cosven',
|
|
11
11
|
author_email='yinshaowen241@gmail.com',
|
|
@@ -27,7 +27,7 @@ setup(
|
|
|
27
27
|
'Programming Language :: Python :: 3 :: Only',
|
|
28
28
|
],
|
|
29
29
|
install_requires=[
|
|
30
|
-
'feeluown>=4.
|
|
30
|
+
'feeluown>=4.1.3',
|
|
31
31
|
'requests',
|
|
32
32
|
'marshmallow>=3.0'
|
|
33
33
|
],
|
|
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
|