feat(p0): add favorite folder sync and sqlite data foundation
Implement P0 of the reaction dashboard: config loading with hot reload, SQLite migrations with backups, single-writer queue, read-only Bilibili fav client, folder import/diff sync, invalid-resource handling, blacklist seed, scheduler, and JSON redacted logging. Includes offline unit and transport-level contract tests; all existing and new tests pass.
This commit is contained in:
commit
e2cdf997af
39 files changed
+8575
No files matched your search
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""脱敏后的真实接口样本(2026-10-31 只读抓取)。
|
||||
|
||||
样本只保留 P0 需要的字段结构,但包含真实接口的完整嵌套形状:
|
||||
``upper`` / ``cnt_info`` / ``attr`` / ``bv_id`` / ``bvid`` 等。
|
||||
不包含 Cookie、token 或任何凭据。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from typing import Any
|
||||
|
||||
# GET x/v3/fav/resource/ids?media_id=3700023351(两条有效 + 两条后来静默缺失)
|
||||
IDS_PAYLOAD: dict[str, Any] = {
|
||||
"code": 0,
|
||||
"message": "0",
|
||||
"ttl": 1,
|
||||
"data": [
|
||||
{"id": 117530286889638, "type": 2, "bv_id": "BV1xvxi6LEZD", "bvid": "BV1xvxi6LEZD"},
|
||||
{"id": 117014421051497, "type": 2, "bv_id": "BV17tGN6kETF", "bvid": "BV17tGN6kETF"},
|
||||
{"id": 116911710935631, "type": 2, "bv_id": "BV1JoNy66Exp", "bvid": "BV1JoNy66Exp"},
|
||||
{"id": 116769540805008, "type": 2, "bv_id": "BV183jw6hEpt", "bvid": "BV183jw6hEpt"},
|
||||
],
|
||||
}
|
||||
|
||||
# GET x/v3/fav/resource/infos 的真实有效条目
|
||||
VALID_INFO: dict[str, Any] = {
|
||||
"id": 117530286889638,
|
||||
"type": 2,
|
||||
"title": "【咻咻满充电专场①】百变音色机能怪,还有什么风格是你驾驭不了的?!",
|
||||
"cover": "http://i1.hdslb.com/bfs/archive/e5d3d4f25777b08c1dfae8bc9dc3d56b21d548f1.jpg",
|
||||
"intro": "视频唱见咻咻满充电专场,感谢宝宝的充电@推堡儿",
|
||||
"page": 2,
|
||||
"duration": 9504,
|
||||
"upper": {
|
||||
"mid": 434442072,
|
||||
"name": "_-抱抱-_",
|
||||
"face": "https://i0.hdslb.com/bfs/face/3176d3d551875a4dd7dac202963ec76d393ddd18.jpg",
|
||||
"jump_link": "",
|
||||
},
|
||||
"attr": 0,
|
||||
"cnt_info": {
|
||||
"collect": 43,
|
||||
"play": 799,
|
||||
"danmaku": 7,
|
||||
"vt": 0,
|
||||
"play_switch": 0,
|
||||
"reply": 0,
|
||||
"view_text_1": "799",
|
||||
},
|
||||
"link": "bilibili://video/117530286889638",
|
||||
"ctime": 1793370433,
|
||||
"pubtime": 1793370433,
|
||||
"fav_time": 0,
|
||||
"bv_id": "BV1xvxi6LEZD",
|
||||
"bvid": "BV1xvxi6LEZD",
|
||||
"season": None,
|
||||
"ogv": None,
|
||||
"ugc": {"first_cid": 43184885058},
|
||||
"media_list_link": "bilibili://music/playlist/playpage/3728825651?page_type=3&oid=117530286889638&otype=2",
|
||||
}
|
||||
|
||||
# attr=9 表示已失效视频(真实样本)
|
||||
INVALID_INFO: dict[str, Any] = {
|
||||
"id": 116769540805008,
|
||||
"type": 2,
|
||||
"title": "已失效视频",
|
||||
"cover": "http://i0.hdslb.com/bfs/archive/be27fd62c99036dce67efface486fb0a88ffed06.jpg",
|
||||
"intro": "-",
|
||||
"page": 1,
|
||||
"duration": 4504,
|
||||
"upper": {
|
||||
"mid": 3707011906669054,
|
||||
"name": "裤裤喝可乐",
|
||||
"face": "https://i1.hdslb.com/bfs/face/c1d2eb1c9e5c783b8b0aa6e6154892724dfe2812.jpg",
|
||||
"jump_link": "",
|
||||
},
|
||||
"attr": 9,
|
||||
"cnt_info": {
|
||||
"collect": 27,
|
||||
"play": 977,
|
||||
"danmaku": 0,
|
||||
"vt": 0,
|
||||
"play_switch": 0,
|
||||
"reply": 0,
|
||||
"view_text_1": "977",
|
||||
},
|
||||
"link": "bilibili://video/116769540805008",
|
||||
"ctime": 1781762147,
|
||||
"pubtime": 1781762147,
|
||||
"fav_time": 0,
|
||||
"bv_id": "BV183jw6hEpt",
|
||||
"bvid": "BV183jw6hEpt",
|
||||
"season": None,
|
||||
"ogv": None,
|
||||
"ugc": {"first_cid": 39207702089},
|
||||
"media_list_link": "bilibili://music/playlist/playpage/3728825651?page_type=3&oid=116769540805008&otype=2",
|
||||
}
|
||||
|
||||
# GET x/v3/fav/folder/created/list-all 的真实条目
|
||||
FOLDERS_PAYLOAD: dict[str, Any] = {
|
||||
"code": 0,
|
||||
"message": "0",
|
||||
"ttl": 1,
|
||||
"data": {
|
||||
"count": 2,
|
||||
"list": [
|
||||
{
|
||||
"id": 4451053851,
|
||||
"fid": 44510538,
|
||||
"mid": 28802351,
|
||||
"attr": 2,
|
||||
"title": "咻咻满-reaction-2025",
|
||||
"fav_state": 0,
|
||||
"media_count": 167,
|
||||
"is_kid_playlist": False,
|
||||
"kid_playlist_desc": "",
|
||||
},
|
||||
{
|
||||
"id": 3700023351,
|
||||
"fid": 37000233,
|
||||
"mid": 28802351,
|
||||
"attr": 54,
|
||||
"title": "咻咻满 -reaction-2026",
|
||||
"fav_state": 0,
|
||||
"media_count": 664,
|
||||
"is_kid_playlist": False,
|
||||
"kid_playlist_desc": "",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def info_for(
|
||||
aid: int,
|
||||
*,
|
||||
title: str,
|
||||
up_mid: int,
|
||||
up_name: str,
|
||||
pubdate: int = 1793370433,
|
||||
duration: int = 9504,
|
||||
attr: int = 0,
|
||||
bvid: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""基于真实样本复制一条 infos 条目,只改测试需要的字段。"""
|
||||
item = copy.deepcopy(VALID_INFO)
|
||||
item["id"] = aid
|
||||
item["title"] = title
|
||||
item["upper"]["mid"] = up_mid
|
||||
item["upper"]["name"] = up_name
|
||||
item["pubtime"] = pubdate
|
||||
item["ctime"] = pubdate
|
||||
item["duration"] = duration
|
||||
item["attr"] = attr
|
||||
item["bvid"] = bvid or f"BV{aid}"
|
||||
item["bv_id"] = item["bvid"]
|
||||
item["link"] = f"bilibili://video/{aid}"
|
||||
item["media_list_link"] = (
|
||||
f"bilibili://music/playlist/playpage/3728825651?page_type=3&oid={aid}&otype=2"
|
||||
)
|
||||
return item
|
||||
Reference in new issue
Block a user