from __future__ import annotations import logging from yuxi.channel.extensions.bilibili.types import BilibiliAccountConfig class BilibiliClientManager: def __init__(self, account: BilibiliAccountConfig, logger: logging.Logger): self._account = account self._logger = logger self._credential = None self._img_key: str | None = None self._sub_key: str | None = None @property def credential(self): if self._credential is None: from bilibili_api import Credential self._credential = Credential( sessdata=self._account.sessdata, bili_jct=self._account.bili_jct, buvid3=self._account.buvid3 or "", dedeuserid=self._account.dedeuserid, ) return self._credential @property def img_key(self) -> str | None: return self._img_key @property def sub_key(self) -> str | None: return self._sub_key async def setup(self) -> None: try: from bilibili_api import select_client select_client("curl_cffi") try: from bilibili_api import request_settings request_settings.set("impersonate", "chrome131") except Exception: pass except Exception: self._logger.warning("curl_cffi 不可用,回退到 aiohttp") try: from bilibili_api import select_client select_client("aiohttp") except Exception: pass await self._fetch_wbi_keys() async def verify_credential(self) -> bool: try: from bilibili_api import user u = user.User(self.credential) info = await u.get_user_info() return info is not None except Exception as e: self._logger.error(f"Cookie 验证失败: {e}") return False async def _fetch_wbi_keys(self) -> None: try: import httpx async with httpx.AsyncClient() as client: resp = await client.get( "https://api.bilibili.com/x/web-interface/nav", cookies=self.credential.get_cookies(), ) data = resp.json() wbi_img = data.get("data", {}).get("wbi_img", {}) img_url = wbi_img.get("img_url", "") sub_url = wbi_img.get("sub_url", "") if img_url and sub_url: self._img_key = img_url.rsplit("/", 1)[-1].split(".")[0] self._sub_key = sub_url.rsplit("/", 1)[-1].split(".")[0] self._logger.debug("WBI keys 已获取") except Exception: self._logger.warning("WBI key 获取失败,部分 API 可能不可用")