from __future__ import annotations import requests from ..models import PlaylistCandidate from .base import BaseCollector PLAYLIST_SQUARE_URL = "https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg" TOPLIST_URL = "https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg" def _extract_collected_song_count(entry: dict) -> int | None: for key in ("songnum", "song_num", "songCount", "song_count", "trackCount", "track_count"): value = entry.get(key) if isinstance(value, bool): continue if isinstance(value, (int, float)): return int(value) if isinstance(value, str) and value.strip().isdigit(): return int(value.strip()) return None def parse_playlist_square_payload(payload: dict) -> list[PlaylistCandidate]: items: list[PlaylistCandidate] = [] for entry in payload.get("data", {}).get("list", []) or []: remote_id = str(entry.get("dissid", "")).strip() if not remote_id: continue creator = entry.get("creator") or {} items.append( PlaylistCandidate( platform="qq", pool_kind="playlist_square", remote_id=remote_id, name=entry.get("dissname") or remote_id, url=f"https://y.qq.com/n/ryqq/playlist/{remote_id}", cover_url=entry.get("imgurl"), creator_name=creator.get("name"), play_count=entry.get("listennum"), collected_song_count=_extract_collected_song_count(entry), ) ) return items def parse_toplist_payload(payload: dict) -> list[PlaylistCandidate]: items: list[PlaylistCandidate] = [] for entry in payload.get("data", {}).get("topList", []) or []: remote_id = str(entry.get("id", "")).strip() if not remote_id: continue items.append( PlaylistCandidate( platform="qq", pool_kind="toplist", remote_id=remote_id, name=entry.get("topTitle") or remote_id, url=f"https://y.qq.com/n/ryqq/toplist/{remote_id}", cover_url=entry.get("picUrl"), play_count=entry.get("listenCount"), collected_song_count=_extract_collected_song_count(entry), parse_strategy="qq_toplist", ) ) return items class QQCollector(BaseCollector): def __init__(self, headers: dict[str, str] | None = None, session: requests.Session | None = None): super().__init__(headers=headers or {"User-Agent": "Mozilla/5.0"}, session=session or requests.Session()) self.headers.update({"Referer": "https://y.qq.com/", "Origin": "https://y.qq.com/"}) def collect_playlist_square( self, category_id: int = 10000000, sort_id: int = 5, page: int = 1, page_size: int = 30, ) -> list[PlaylistCandidate]: params = { "picmid": "1", "rnd": "0.1", "g_tk": "732560869", "loginUin": "0", "hostUin": "0", "format": "json", "inCharset": "utf8", "outCharset": "utf-8", "notice": "0", "platform": "yqq.json", "needNewCode": "0", "categoryId": str(category_id), "sortId": str(sort_id), "sin": str(max(page - 1, 0) * page_size), "ein": str(max(page, 1) * page_size - 1), } response = self.get(PLAYLIST_SQUARE_URL, params=params) return parse_playlist_square_payload(response.json()) def collect_toplist(self) -> list[PlaylistCandidate]: response = self.get(TOPLIST_URL, params={"format": "json"}) return parse_toplist_payload(response.json())