Add httpx and route Bilibili and Telegram requests through pooled clients with keepalive and explicit timeouts. Align Bilibili headers with data/raw-request.txt: Chrome 155 UA, sec-ch-ua, space.bilibili.com origin, folder-specific referer, and priority header. Close replaced clients when credentials change. Add transport contract tests and live read-only validation for nav, folder sync, WBI search, and Telegram getUpdates.
43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""按账号角色提供 B 站客户端,并在凭据变化时自动重建。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Callable
|
|
|
|
from ..config import ConfigStore
|
|
from .client import BilibiliClient
|
|
|
|
|
|
class ClientProvider:
|
|
"""缓存客户端;仅当 ``mid`` / ``cookie`` / ``csrf`` 变化时重建。
|
|
|
|
``factory`` 负责固化该角色的限速与重试策略,因此重建不会丢失这些设置。
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
store: ConfigStore,
|
|
role: str,
|
|
factory: Callable[[Any], BilibiliClient],
|
|
) -> None:
|
|
if role not in {"read", "write"}:
|
|
raise ValueError(f"未知账号角色: {role}")
|
|
self.store = store
|
|
self.role = role
|
|
self.factory = factory
|
|
self._key: tuple[int, str, str] | None = None
|
|
self._client: BilibiliClient | None = None
|
|
|
|
def get(self) -> BilibiliClient:
|
|
credentials = getattr(self.store.get().bilibili, self.role)
|
|
key = (credentials.mid, credentials.cookie, credentials.csrf)
|
|
if self._client is None or key != self._key:
|
|
if self._client is not None:
|
|
close = getattr(self._client, "close", None)
|
|
if callable(close):
|
|
close()
|
|
self._client = self.factory(credentials)
|
|
self._key = key
|
|
return self._client
|