Initial import: Music_Server, MusicFree, catalog-sync
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
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())
|
||||
Reference in New Issue
Block a user