105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
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())
|