Implement P1: seven-metric view snapshots, relation/stat fan snapshots, failure placeholders, resume by daily completion markers, Cookie login warnings, daily SQLite backups, Shanghai daily scheduling, and CLI snapshot/auth-check commands. Real read-only run captured 795/795 videos successfully over 1124s with a verified daily backup.
269 lines
7.4 KiB
Python
269 lines
7.4 KiB
Python
# -*- 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
|
||
|
||
|
||
# GET x/web-interface/view?aid=117530286889638(真实七项 stat)
|
||
VIEW_PAYLOAD: dict[str, Any] = {
|
||
"code": 0,
|
||
"message": "0",
|
||
"ttl": 1,
|
||
"data": {
|
||
"aid": 117530286889638,
|
||
"bvid": "BV1xvxi6LEZD",
|
||
"cid": 43184885058,
|
||
"title": "【咻咻满充电专场①】百变音色机能怪,还有什么风格是你驾驭不了的?!",
|
||
"pubdate": 1793370433,
|
||
"ctime": 1793370433,
|
||
"duration": 9504,
|
||
"owner": {"mid": 434442072, "name": "_-抱抱-_", "face": ""},
|
||
"stat": {
|
||
"aid": 117530286889638,
|
||
"view": 819,
|
||
"danmaku": 7,
|
||
"reply": 16,
|
||
"favorite": 43,
|
||
"coin": 9,
|
||
"share": 1,
|
||
"now_rank": 0,
|
||
"his_rank": 0,
|
||
"like": 44,
|
||
"dislike": 0,
|
||
"evaluation": "",
|
||
"vt": 0,
|
||
},
|
||
},
|
||
}
|
||
|
||
# 当前实测:收藏夹里已不可访问的 aid 返回 62012,而不是旧文档里的 -404
|
||
VIEW_UNAVAILABLE: dict[str, Any] = {
|
||
"code": 62012,
|
||
"message": "62012",
|
||
"ttl": 1,
|
||
"data": None,
|
||
}
|
||
|
||
# GET x/relation/stat?vmid=37754047
|
||
RELATION_PAYLOAD: dict[str, Any] = {
|
||
"code": 0,
|
||
"message": "0",
|
||
"ttl": 1,
|
||
"data": {
|
||
"mid": 37754047,
|
||
"following": 95,
|
||
"whisper": 0,
|
||
"black": 0,
|
||
"follower": 2767268,
|
||
"fans_medal_toast": None,
|
||
"fans_effect": None,
|
||
},
|
||
}
|
||
|
||
# GET x/web-interface/nav(已登录)
|
||
NAV_PAYLOAD: dict[str, Any] = {
|
||
"code": 0,
|
||
"message": "0",
|
||
"ttl": 1,
|
||
"data": {
|
||
"isLogin": True,
|
||
"mid": 28802351,
|
||
"uname": "冷萃编译器",
|
||
"face": "https://i0.hdslb.com/bfs/face/example.jpg",
|
||
"level_info": {"current_level": 5},
|
||
"vipStatus": 0,
|
||
"wbi_img": {
|
||
"img_url": "https://i0.hdslb.com/bfs/wbi/example.png",
|
||
"sub_url": "https://i0.hdslb.com/bfs/wbi/example-sub.png",
|
||
},
|
||
},
|
||
}
|
||
|
||
|
||
def view_for(
|
||
aid: int,
|
||
*,
|
||
view: int = 819,
|
||
like: int = 44,
|
||
coin: int = 9,
|
||
favorite: int = 43,
|
||
share: int = 1,
|
||
reply: int = 16,
|
||
danmaku: int = 7,
|
||
bvid: str | None = None,
|
||
) -> dict[str, Any]:
|
||
item = copy.deepcopy(VIEW_PAYLOAD)
|
||
data = item["data"]
|
||
data["aid"] = aid
|
||
data["bvid"] = bvid or f"BV{aid}"
|
||
data["stat"].update({
|
||
"aid": aid,
|
||
"view": view,
|
||
"like": like,
|
||
"coin": coin,
|
||
"favorite": favorite,
|
||
"share": share,
|
||
"reply": reply,
|
||
"danmaku": danmaku,
|
||
})
|
||
return item
|