44# ruff: noqa: S311 S324
55import hashlib
66import json
7+ import random
78import time
89from base64 import b64decode , b64encode
910from datetime import datetime , timedelta , timezone
@@ -137,7 +138,8 @@ def request(
137138 "uuid" : "-" ,
138139 # "uuid": "15e772e1213bdd0718d0c1d10d64e06f",
139140 "mid" : mid ,
140- "dfid" : self .dfid ,
141+ # "dfid": self.dfid,
142+ "dfid" : "-" ,
141143 "clientver" : "11070" ,
142144 "platform" : "AndroidFilter" ,
143145 ** params ,
@@ -181,7 +183,11 @@ def search(self, keyword: str, search_type: SearchType, page: int = 1) -> APIRes
181183 "page" : page ,
182184 }
183185 url , module = SEARCH_TYPE_MAPPING [search_type ]
184- data = self .request (url , params , module , headers = {"x-router" : "complexsearch.kugou.com" })
186+ try :
187+ data = self .request (url , params , module , headers = {"x-router" : "complexsearch.kugou.com" })
188+ except APIRequestError :
189+ logger .exception ("kg API请求错误,尝试使用旧接口" )
190+ return self ._old_search (keyword , search_type , page )
185191
186192 if not data ["data" ]["lists" ]:
187193 return APIResultList (
@@ -202,7 +208,7 @@ def search(self, keyword: str, search_type: SearchType, page: int = 1) -> APIRes
202208 [
203209 SongInfo (
204210 source = self .source ,
205- id = info ["ID" ],
211+ id = str ( info ["ID" ]) ,
206212 hash = info ["FileHash" ],
207213 title = info ["SongName" ],
208214 subtitle = info ["Auxiliary" ],
@@ -232,7 +238,7 @@ def search(self, keyword: str, search_type: SearchType, page: int = 1) -> APIRes
232238 SongListInfo (
233239 source = self .source ,
234240 type = SongListType .ALBUM ,
235- id = info ["albumid" ],
241+ id = str ( info ["albumid" ]) ,
236242 title = info ["albumname" ],
237243 imgurl = info ["img" ],
238244 songcount = info ["songcount" ],
@@ -267,7 +273,7 @@ def search(self, keyword: str, search_type: SearchType, page: int = 1) -> APIRes
267273 SongListInfo (
268274 source = self .source ,
269275 type = SongListType .SONGLIST ,
270- id = info ["gid" ],
276+ id = str ( info ["gid" ]) ,
271277 title = info ["specialname" ],
272278 imgurl = info ["img" ],
273279 songcount = info ["song_count" ],
@@ -297,6 +303,155 @@ def search(self, keyword: str, search_type: SearchType, page: int = 1) -> APIRes
297303 case _:
298304 raise NotImplementedError
299305
306+ def _old_search (self , keyword : str , search_type : SearchType , page : int = 1 ) -> APIResultList [SongInfo ] | APIResultList [SongListInfo ]:
307+ """备用搜索API"""
308+ domain = random .choice (["mobiles.kugou.com" , "msearchcdn.kugou.com" , "mobilecdnbj.kugou.com" , "msearch.kugou.com" ])
309+ pagesize = 20
310+
311+ match search_type :
312+ case SearchType .SONG :
313+ url = f"http://{ domain } /api/v3/search/song"
314+ params = {
315+ "showtype" : "14" ,
316+ "highlight" : "" ,
317+ "pagesize" : "30" ,
318+ "tag_aggr" : "1" ,
319+ "plat" : "0" ,
320+ "sver" : "5" ,
321+ "keyword" : keyword ,
322+ "correct" : "1" ,
323+ "api_ver" : "1" ,
324+ "version" : "9108" ,
325+ "page" : page ,
326+ }
327+ case SearchType .SONGLIST :
328+ url = f"http://{ domain } /api/v3/search/special"
329+ params = {
330+ "version" : "9108" ,
331+ "highlight" : "" ,
332+ "keyword" : keyword ,
333+ "pagesize" : "20" ,
334+ "filter" : "0" ,
335+ "page" : page ,
336+ "sver" : "2" ,
337+ }
338+ case SearchType .ALBUM :
339+ url = f"http://{ domain } /api/v3/search/album"
340+ params = {
341+ "version" : "9108" ,
342+ "iscorrection" : "1" ,
343+ "highlight" : "" ,
344+ "plat" : "0" ,
345+ "keyword" : keyword ,
346+ "pagesize" : "20" ,
347+ "page" : page ,
348+ "sver" : "2" ,
349+ }
350+
351+ response = self .client .get (url , params = params , timeout = 3 )
352+ response .raise_for_status ()
353+ data = response .json ()
354+ start_index = (page - 1 ) * pagesize
355+ match search_type :
356+ case SearchType .SONG :
357+ return APIResultList (
358+ [
359+ SongInfo (
360+ source = self .source ,
361+ id = str (info ["album_audio_id" ]),
362+ hash = info ["hash" ],
363+ title = info ["songname" ],
364+ subtitle = info ["topic" ],
365+ artist = Artist (info ["singername" ].split ("、" )),
366+ album = info ["album_name" ],
367+ duration = info ["duration" ] * 1000 ,
368+ language = LANGUAGE_MAPPING .get (info ["trans_param" ].get ("language" ), Language .OTHER ),
369+ )
370+ for info in data ["data" ]["info" ]
371+ ],
372+ SearchInfo (
373+ source = self .source ,
374+ keyword = keyword ,
375+ search_type = search_type ,
376+ page = page ,
377+ ),
378+ (
379+ start_index ,
380+ start_index + len (data ["data" ]["info" ]) - 1 ,
381+ data ["data" ]["total" ] if len (data ["data" ]["info" ]) == pagesize else start_index + len (data ["data" ]["info" ]),
382+ ),
383+ )
384+ case SearchType .SONGLIST :
385+ return APIResultList (
386+ [
387+ SongListInfo (
388+ source = self .source ,
389+ type = SongListType .SONGLIST ,
390+ id = str (info ["specialid" ]),
391+ title = info ["specialname" ],
392+ imgurl = info ["imgurl" ],
393+ songcount = info ["songcount" ],
394+ publishtime = int (
395+ datetime .strptime (info ["publishtime" ], "%Y-%m-%d %H:%M:%S" )
396+ .replace (tzinfo = timezone (timedelta (hours = 8 )))
397+ .astimezone (timezone .utc )
398+ .timestamp (),
399+ )
400+ if info ["publishtime" ] != "0000-00-00 00:00:00"
401+ else None ,
402+ author = info ["nickname" ],
403+ )
404+ for info in data ["data" ]["info" ]
405+ ],
406+ SearchInfo (
407+ source = self .source ,
408+ keyword = keyword ,
409+ search_type = search_type ,
410+ page = page ,
411+ ),
412+ (
413+ start_index ,
414+ start_index + len (data ["data" ]["info" ]) - 1 ,
415+ data ["data" ]["total" ] if len (data ["data" ]["info" ]) == pagesize else start_index + len (data ["data" ]["info" ]),
416+ ),
417+ )
418+ case SearchType .ALBUM :
419+ return APIResultList (
420+ [
421+ SongListInfo (
422+ source = self .source ,
423+ type = SongListType .ALBUM ,
424+ id = str (info ["albumid" ]),
425+ title = info ["albumname" ],
426+ imgurl = info ["imgurl" ],
427+ songcount = info ["songcount" ],
428+ publishtime = int (
429+ datetime .strptime (info ["publishtime" ], "%Y-%m-%d %H:%M:%S" )
430+ .replace (tzinfo = timezone (timedelta (hours = 8 )))
431+ .astimezone (timezone .utc )
432+ .timestamp (),
433+ )
434+ if info ["publishtime" ] != "0000-00-00 00:00:00"
435+ else None ,
436+ author = info ["singer" ],
437+ )
438+ for info in data ["data" ]["info" ]
439+ ],
440+ SearchInfo (
441+ source = self .source ,
442+ keyword = keyword ,
443+ search_type = search_type ,
444+ page = page ,
445+ ),
446+ (
447+ start_index ,
448+ start_index + len (data ["data" ]["info" ]) - 1 ,
449+ data ["data" ]["total" ] if len (data ["data" ]["info" ]) == pagesize else start_index + len (data ["data" ]["info" ]),
450+ ),
451+ )
452+ case _:
453+ raise NotImplementedError
454+
300455 def get_songlist (self , songlist_info : SongListInfo ) -> APIResultList [SongInfo ]:
301456 match songlist_info .type :
302457 case SongListType .ALBUM :
@@ -402,7 +557,7 @@ def get_lyricslist(self, song_info: SongInfo) -> APIResultList[LyricInfo]:
402557 [
403558 LyricInfo (
404559 source = self .source ,
405- id = lyric ["id" ],
560+ id = str ( lyric ["id" ]) ,
406561 accesskey = lyric ["accesskey" ],
407562 creator = lyric ["nickname" ],
408563 duration = lyric ["duration" ],
0 commit comments