酷我听书
This commit is contained in:
@@ -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
@@ -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": []}
|
||||
|
||||
Reference in New Issue
Block a user