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.
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""B 站收藏夹写客户端(``deal`` / ``sort``)。
|
|
|
|
只应使用 ``bilibili.write`` 凭据构造。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import urllib.parse
|
|
from typing import Any, Mapping
|
|
|
|
from .client import (
|
|
BASE,
|
|
BilibiliClient,
|
|
BilibiliHTTPError,
|
|
BilibiliProtocolError,
|
|
)
|
|
|
|
|
|
class BilibiliWriteClient(BilibiliClient):
|
|
def post_form(
|
|
self, url: str, data: Mapping[str, Any], *, referer: str | None = None
|
|
) -> dict[str, Any]:
|
|
self.pacer.wait()
|
|
headers = self._headers(auth=True, referer=referer)
|
|
headers["content-type"] = "application/x-www-form-urlencoded"
|
|
body = urllib.parse.urlencode(data).encode("utf-8")
|
|
response = self.transport.request(
|
|
"POST", url, headers, data=body, timeout=self.timeout
|
|
)
|
|
if response.status >= 400:
|
|
raise BilibiliHTTPError(
|
|
response.status, response.body.decode("utf-8", "replace")[:200]
|
|
)
|
|
payload = response.json()
|
|
if not isinstance(payload, dict):
|
|
raise BilibiliProtocolError("写接口响应顶层不是 JSON 对象")
|
|
if payload.get("code") != 0:
|
|
raise self._api_error(payload)
|
|
return payload
|
|
|
|
def deal(self, aid: int, folder_id: int) -> dict[str, Any]:
|
|
return self.post_form(
|
|
f"{BASE}/x/v3/fav/resource/deal",
|
|
{
|
|
"rid": int(aid),
|
|
"type": 2,
|
|
"add_media_ids": int(folder_id),
|
|
"del_media_ids": "",
|
|
"csrf": self.csrf,
|
|
},
|
|
referer=(
|
|
f"https://space.bilibili.com/{self.mid}/favlist"
|
|
f"?fid={int(folder_id)}&ftype=create"
|
|
),
|
|
)
|
|
|
|
def sort(self, media_id: int, sort_spec: str) -> dict[str, Any]:
|
|
return self.post_form(
|
|
f"{BASE}/x/v3/fav/resource/sort",
|
|
{
|
|
"media_id": int(media_id),
|
|
"sort": sort_spec,
|
|
"csrf": self.csrf,
|
|
},
|
|
referer=(
|
|
f"https://space.bilibili.com/{self.mid}/favlist"
|
|
f"?fid={int(media_id)}&ftype=create"
|
|
),
|
|
)
|