酷我听书

This commit is contained in:
Harold
2026-04-24 17:59:03 +08:00
parent 94897aeb66
commit c82b47f4f2
2 changed files with 105 additions and 8 deletions
+44
View File
@@ -142,6 +142,50 @@ class TestKuWoTingShuSpider(unittest.TestCase):
self.assertEqual(vod["vod_play_url"], "1.第一集$free|MUSIC_1#2.💎第二集$vip|MUSIC_2")
self.assertEqual(vod["vod_remarks"], "会员 | 已完结 | 共2集 | 800播放")
@patch.object(Spider, "_search_get")
def test_detail_content_paginates_tracks_beyond_2000(self, mock_search_get):
first_page_tracks = [
{"name": f"{i}", "musicrid": f"MUSIC_{i}", "playcnt": "1", "payInfo": {"feeType": {"bookvip": "0"}}}
for i in range(1, 2001)
]
second_page_tracks = [
{"name": f"{i}", "musicrid": f"MUSIC_{i}", "playcnt": "1", "payInfo": {"feeType": {"bookvip": "0"}}}
for i in range(2001, 2006)
]
mock_search_get.side_effect = [
{
"name": "长篇评书",
"img": "cover.jpg",
"info": "超长连载",
"artist": "播音员",
"company": "酷我出品",
"pub": "2026-04-24",
"lang": "普通话",
"finished": "0",
"vip": "0",
"songnum": "2005",
"musiclist": first_page_tracks,
},
{
"name": "长篇评书",
"songnum": "2005",
"musiclist": second_page_tracks,
},
]
result = self.spider.detailContent(["999"])
vod = result["list"][0]
play_items = vod["vod_play_url"].split("#")
self.assertEqual(len(play_items), 2005)
self.assertEqual(play_items[0], "1.第1集$free|MUSIC_1")
self.assertEqual(play_items[-1], "2005.第2005集$free|MUSIC_2005")
self.assertEqual(vod["vod_remarks"], "免费 | 连载中 | 共2005集 | 2005播放")
self.assertEqual(mock_search_get.call_args_list[0].args[1]["pn"], 0)
self.assertEqual(mock_search_get.call_args_list[1].args[1]["pn"], 1)
self.assertEqual(mock_search_get.call_args_list[0].args[1]["rn"], 2000)
self.assertEqual(mock_search_get.call_args_list[1].args[1]["rn"], 2000)
@patch.object(Spider, "_api_get")
def test_player_content_resolves_free_track(self, mock_api_get):
mock_api_get.return_value = {"data": {"url": "https://audio.example/free.mp3"}}
+61 -8
View File
@@ -336,12 +336,8 @@ class Spider(BaseSpider):
return f"http://img3.sycdn.kuwo.cn/star/albumcover/240/{image}"
return image
def detailContent(self, ids):
album_id = str(ids[0] if isinstance(ids, list) and ids else ids).strip()
if not album_id:
return {"list": []}
payload = self._search_get(
def _fetch_album_detail_page(self, album_id, page, page_size=2000):
return self._search_get(
"/r.s",
{
"stype": "albuminfo",
@@ -355,14 +351,71 @@ class Spider(BaseSpider):
"bksource": "kwbook_ar_9.1.8.1_tvivo.apk",
"corp": "kuwo",
"albumid": album_id,
"pn": 0,
"rn": 2000,
"pn": page,
"rn": page_size,
"show_copyright_off": 1,
"vipver": "MUSIC_8.2.0.0_BCS17",
"mobi": 1,
"iskwbook": 1,
},
) or {}
def _fetch_album_detail(self, album_id, page_size=2000, max_pages=20):
merged_payload = {}
tracks = []
seen_track_ids = set()
total_expected = 0
for page in range(max_pages):
payload = self._fetch_album_detail_page(album_id, page, page_size)
page_tracks = payload.get("musiclist") or []
if not payload and page == 0:
return {}
if not merged_payload and payload:
merged_payload = dict(payload)
elif payload:
for key, value in payload.items():
if key == "musiclist":
continue
if merged_payload.get(key) in ("", None, [], {}) and value not in ("", None, [], {}):
merged_payload[key] = value
songnum = payload.get("songnum") or merged_payload.get("songnum")
try:
total_expected = int(songnum or 0)
except (TypeError, ValueError):
total_expected = 0
added = 0
for track in page_tracks:
track_id = str(track.get("musicrid") or "")
dedupe_key = track_id or f"{track.get('name', '')}|{len(tracks)}"
if dedupe_key in seen_track_ids:
continue
seen_track_ids.add(dedupe_key)
tracks.append(track)
added += 1
if not page_tracks or added == 0:
break
if total_expected and len(tracks) >= total_expected:
break
if len(page_tracks) < page_size:
break
if not tracks:
return {}
merged_payload["musiclist"] = tracks
if total_expected:
merged_payload["songnum"] = str(total_expected)
return merged_payload
def detailContent(self, ids):
album_id = str(ids[0] if isinstance(ids, list) and ids else ids).strip()
if not album_id:
return {"list": []}
payload = self._fetch_album_detail(album_id)
tracks = payload.get("musiclist") or []
if not tracks:
return {"list": []}