Files
re-xxm/DEVELOPMENT.md
T
kairee 5c2ab7b187 feat(p1): add daily snapshots and fan tracking
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.
2026-10-31 19:19:24 +08:00

622 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 咻咻满 Reaction 视频数据看板开发文档
> **状态:** P0(数据底座与收藏夹同步)、P1(每日快照与粉丝追踪)已实施并通过只读联调;P2 待开始
>
> **技术基线:** Python 3.14、Python 标准库、SQLite、原生 HTML/CSS/JavaScript、`uv`
>
> **本地开发:** 不依赖 Docker;使用 `uv` 创建和管理隔离环境。
---
## 1. 技术选型
| 范围 | 选型 | 说明 |
|---|---|---|
| 开发语言 | Python 3.14 | 与现有 `favsort.py` 一致,复用 Cookie、扫码授权与收藏夹排序能力。 |
| 包与环境管理 | `uv` | 管理 Python 版本、`.venv`、依赖同步及统一命令。 |
| 后端服务 | Python 标准库 `ThreadingHTTPServer` | 个人单用户看板,接口与并发规模有限,不引入 Web 框架。 |
| HTTP 客户端 | `urllib.request` | 现有 B 站请求代码已使用;无需额外依赖。 |
| 数据库 | SQLite(WAL) | 单用户、单机、低并发写入;备份和迁移成本低。 |
| 调度 | `threading`、`queue.Queue`、标准库时间工具 | 单进程内运行发现、同步、快照和清理任务。 |
| 前端 | 原生 HTML/CSS/JavaScript | 无 Node.js、无构建链;前端仅渲染 `/api/*` JSON。 |
| 图表 | 仓库内置 ECharts | 离线静态资源,Apache-2.0 许可。 |
| 通知 | Telegram Bot long polling | 具备按钮回调能力,开发和本机运行不需要公网 URL。 |
| 容器化 | 可选的后续部署方式 | 本地开发、调试和日常运行均不要求 Docker。 |
### 1.1 选型原则
1. **复用现有能力。** 当前仓库的 `favsort.py` 已实现 B 站设备指纹 Cookie、扫码登录、收藏夹读取和排序逻辑;新功能应拆分复用,而不是另起技术栈。
2. **保持单体。** 当前规模约数百到数千视频,每日采集量约千级请求;单进程、SQLite 和内置调度足够。
3. **尽量标准库。** 当前没有第三方 Python 依赖,第一期不因便利性引入 Web 框架、ORM、任务队列或 Redis。
4. **本地优先。** 可在 Windows 上通过 `uv` 直接运行,不要求 WSL、Docker、数据库服务或宿主机 cron。
5. **未来可部署。** 服务保持无状态代码与可挂载数据目录分离,未来可选用 Docker 与 Caddy/Nginx 部署,但不是开发前置条件。
---
## 2. 使用 uv 的本地开发环境
### 2.1 前置条件
- 安装 [uv](https://docs.astral.sh/uv/);
- 网络联通以便 `uv` 首次下载 Python 3.14;
- Windows PowerShell、Windows Terminal 或任意可运行 Python 的终端;
- 不需要安装 Docker、Node.js、npm、SQLite 命令行工具或 PostgreSQL。
项目通过根目录的 `.python-version` 固定使用 Python 3.14。`uv` 会在需要时自动安装相应解释器,并在仓库根目录创建 `.venv`。
### 2.2 初始化与同步
在项目根目录执行:
```powershell
uv sync --all-groups
```
该命令会:
1. 读取 `pyproject.toml`;
2. 确保 Python 3.14 可用;
3. 创建或更新 `.venv`;
4. 安装项目本身及后续声明的依赖;
5. 根据 `uv.lock` 锁定可复现的环境。
目前项目功能仅依赖 Python 标准库,因此首次同步不会安装额外运行时包。这是预期行为,不表示 `uv` 没有生效。
### 2.3 常用命令
```powershell
# 查看 Python 与 uv 管理的虚拟环境是否可用
uv run python --version
# 查看现有收藏夹工具帮助
uv run bili-fav help
# 扫码登录并原子更新 config.json
uv run bili-fav auth
# 检查现有 B 站登录状态
uv run bili-fav auth --check
# 执行当前无网络依赖的测试
uv run python tests/test_auth.py
uv run python tests/test_moves.py
```
未来看板服务实现后,统一使用:
```powershell
# 建库、迁移与黑名单种子(离线,P0)
uv run python app.py init
# 收藏夹只读同步/首次导入(P0)
uv run python app.py sync
# 按配置周期常驻同步(P0);--once 只跑一次
uv run python app.py run
uv run python app.py run --once
# 每日七项快照与粉丝数(P1)
uv run python app.py snapshot
uv run python app.py snapshot --force
# 检查 Cookie 登录态(P1)
uv run python app.py auth-check
# 查看数据库状态(离线,P0)
uv run python app.py status
# P2 起启动本地看板服务
uv run python app.py
uv run python app.py --config .\config.json
```
不建议直接激活 `.venv`;优先使用 `uv run ...`,这样命令总是运行在项目锁定的解释器和环境中。
### 2.4 依赖变更规范
若后续确实需要新增依赖,应使用 `uv` 修改项目元数据,而不是直接使用 `pip install`:
```powershell
# 新增运行依赖(仅在标准库无法合理满足需求时)
uv add <package>
# 新增仅用于开发的依赖
uv add --dev <package>
# 同步锁文件和环境
uv sync --all-groups
```
提交依赖变更时,必须同时提交:
- `pyproject.toml`
- `uv.lock`
不得提交:
- `.venv/`
- `config.json`
- `config.json.bak`
- `data/` 中的运行期数据库、日志、计划和备份。
---
## 3. 总体架构
```text
浏览器 / 手机 Telegram 私聊
│ │
├──────────── HTTP / JSON ─────────────┤
│ │
┌─────▼──────────────────────────────────────▼─────┐
│ 单一 Python 进程 │
│ │
│ Web 线程:ThreadingHTTPServer :8080 │
│ 调度线程:发现 / 收藏夹同步 / 快照 / 清理 │
│ Telegram:getUpdates long polling │
│ 写入线程:SQLite 单写队列 │
│ │
│ BilibiliClient ── B 站 Web API │
│ SQLite (WAL) ── ./data/app.db 或配置的数据目录 │
└────────────────────────────────────────────────────┘
```
### 3.1 并发与事务原则
- Web 请求、调度任务和 Telegram 长轮询可以并发;
- 所有 SQLite 写操作经单写队列串行执行,避免 `database is locked`;
- 读操作使用短生命周期连接;
- 网络请求期间不得持有数据库写事务;
- 收藏操作通过 `fav_action` 状态锁保证幂等;
- SQLite 启用 WAL、外键和合理的 busy timeout。
```sql
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
PRAGMA synchronous = NORMAL;
```
---
## 4. 推荐代码结构
```text
.
├── app.py # 看板服务入口(P0 起新增)
├── favsort.py # 现有收藏夹排序与授权工具
├── pyproject.toml # uv / Python 项目元数据
├── uv.lock # uv 生成并提交的锁文件
├── .python-version # 固定 Python 3.14
├── config.json # 运行期私密配置,不提交
├── config.example.json # 脱敏配置样例
├── DEVELOPMENT.md
│
├── reaction_board/
│ ├── config.py # 配置、ENV 覆盖、mtime 热加载
│ ├── database.py # SQLite 连接、迁移、单写队列
│ ├── migrations.py # schema_version 与迁移
│ ├── scheduler.py # 内置任务调度
│ ├── logging_json.py # 脱敏 JSON 日志
│ ├── bilibili/
│ │ ├── client.py # HTTP、WBI、重试与限速
│ │ ├── auth.py # 从 favsort 抽取的扫码授权能力
│ │ └── favorite.py # 加入收藏夹与排序定位
│ ├── services/
│ │ ├── discovery.py
│ │ ├── folder_sync.py
│ │ ├── snapshot.py
│ │ ├── fans.py
│ │ ├── candidates.py
│ │ ├── rankings.py
│ │ └── maintenance.py
│ ├── notify/telegram.py
│ └── web/
│ ├── server.py
│ ├── api.py
│ └── static/
│ ├── index.html
│ ├── app.js
│ ├── styles.css
│ └── vendor/echarts.min.js
│
├── tests/
└── data/ # 本地运行期数据,默认不提交
```
`favsort.py` 继续保持可独立运行。P0 实施时优先抽取其通用的 Cookie、请求头、扫码授权和收藏夹 API 逻辑;不要直接复制出第二套不一致的身份与请求实现。
---
## 5. 配置与凭据
### 5.1 运行期配置
所有凭据和可变业务参数放在 `config.json` 中。文件必须被 Git 忽略,日志中不得输出其敏感值。
```jsonc
{
"bilibili": {
"mid": 28802351,
"csrf": "<bili_jct>",
"cookie": "SESSDATA=...; bili_jct=...; buvid3=..."
},
"telegram": {
"enabled": true,
"bot_token": "123456:ABC",
"chat_id": 123456789
},
"web": {
"host": "127.0.0.1",
"port": 8080,
"auth_token": "replace-with-a-long-random-token",
"public_base_url": ""
},
"storage": {
"database_path": "data/app.db",
"backup_dir": "data/backup",
"backup_keep_days": 14
},
"folders": [
{ "year": 2025, "media_id": 4451053851, "title": "咻咻满-reaction-2025" },
{ "year": 2026, "media_id": 3700023351, "title": "咻咻满 reaction 合集" }
],
"track_fans": [37754047],
"up_blacklist_seed": [261052419, 1908537643, 1701121088, 542262],
"schedule": {
"discover_minutes": 60,
"folder_sync_minutes": 60,
"full_folder_refresh_hours": 6,
"snapshot_at": "00:01",
"candidate_ttl_days": 7
},
"notify": { "min_score": 2, "max_items_per_message": 10 }
}
```
- `folders` 中的年度夹由人工创建,系统只读;
- 兼容现有 `favsort.py` 的扁平 `mid` / `csrf` / `cookie` 写法;存在 `bilibili` 嵌套对象时以嵌套值为准;
- P0 已实现的命令行:`python app.py init`、`python app.py sync [--full]`、`python app.py status`。
### 5.2 安全规则- Windows 本地开发时应限制 `config.json` 的文件访问权限;部署到 Linux 时权限必须为 `600`;
- 配置文件每 60 秒检查一次 mtime,收藏夹、分类规则和权重变更后无需重启;
- Cookie 或 CSRF 改变时重建 B 站请求客户端;
- Telegram 操作必须同时验证 `from.id` 和 `chat.id` 是否为配置的私聊用户;
- 仅允许环境变量临时覆盖敏感配置,不能将真实凭据硬编码进源代码;
- `GET` 路由绝不进行收藏、忽略、拉黑等写操作。
---
## 6. 数据模型
### 6.1 核心状态
| 表 | 状态 | 含义 | 是否参与每日快照 |
|---|---|---|---|
| `video` | `active` | 在库追踪的视频 | 是 |
| `video` | `unfaved` | 已从收藏夹移除,但保留历史追踪 | 是 |
| `video` | `removed` | 视频失效、删除或不可访问 | 否 |
| `video` | `dropped` | 在看板中手动移出库 | 否 |
| `candidate` | `NULL` | 待人工决策 | 否 |
| `candidate` | `rejected` | 已忽略 | 否 |
| `candidate` | `blacklisted` | 所属 UP 已拉黑 | 否 |
### 6.2 建表原则
最终 DDL 由迁移模块维护,关键约束如下:
- `snapshot` 主键为 `(stat_date, aid)`,保证同日幂等;
- `up_stat` 主键为 `(stat_date, mid)`;
- `folder_resource(media_id, aid)` 记录每个收藏夹实际拥有的资源,不能仅依赖 `video.folder_id`;
- `video.folder_id` 表示视频的主归属夹,用于新视频同 UP 归属和排序;
- `up_blacklist` 只在发现任务中生效,收藏夹同步不得用它过滤既有视频;
- `seen_aid` 永久保留搜索去重标记,候选过期删除后也不重复通知;
- `fav_action` 保存 Telegram 或签名链接的动作状态,防止重复点击造成重复写入。
### 6.3 迁移规范
- 使用 `schema_version(version, applied_at)`;
- 每个版本迁移必须包在单一 SQLite 事务;
- 迁移只能向前追加,不能用“删除数据库重建”替代升级;
- 开发和正式运行产生的数据均保留;
- 迁移前自动创建 SQLite 备份,失败时回滚事务并保留原库。
---
## 7. B 站接口与任务设计
### 7.1 接口职责
| 能力 | 接口 | 用途 |
|---|---|---|
| 发现 | `x/web-interface/wbi/search/type` | 每小时搜索候选 |
| 搜索降级 | `x/web-interface/search/type` | WBI 搜索失败时使用 |
| 收藏夹 ID | `x/v3/fav/resource/ids` | 计算新增和移除差集 |
| 收藏夹详情 | `x/v3/fav/resource/infos` | 元数据同步、失效识别 |
| 视频统计 | `x/web-interface/view?aid=` | 日更七项指标的唯一事实来源 |
| 粉丝数 | `x/relation/stat?vmid=` | 每日粉丝快照 |
| 收藏夹列表 | `x/v3/fav/folder/created/list-all` | 容量监控 |
| 加入收藏 | `x/v3/fav/resource/deal` | 确认候选后收藏 |
| 收藏夹排序 | `x/v3/fav/resource/sort` | 定位至同 UP 分组 |
### 7.2 发现任务
每小时扫描两个关键词、每个关键词前两页:
```text
关键词:咻咻满 reaction、咻咻满 充电
排序:pubdate
页面:1、2
```
处理规则:
1. 已在 `video` 或 `seen_aid` 的 aid 跳过;
2. 发布时间超过 14 天的结果跳过;
3. 标题不含“咻咻满”跳过;
4. 命中 `up_blacklist` 的 UP 跳过,不写候选池;
5. 通过分类规则计算分数;
6. 分数低于零丢弃;
7. 其余写入候选池;达到通知阈值的候选发送 Telegram 消息;
8. 请求之间随机等待 1.5~2.5 秒。
分类规则 v1:
```text
+2:标题含 reaction / re / 反应 / 看 / 听 / 初听 / 首次 / 点评 / 锐评 / 路人 / 震惊 / 双人
+2:标题含 充电 / 加急 / 专场
+2:时长大于 60 分钟
+1:时长大于 30 分钟且不超过 60 分钟
-5:标题含 歌切 / 切片 / 纯享 / 搬运 / 完整版
-3:UP 为咻咻满本人
score >= 2:发送通知
0 <= score < 2:仅候选池展示
score < 0:丢弃
```
### 7.3 收藏夹同步
每小时对所有已配置年度夹执行:
1. 请求 `resource/ids` 获取远端 aid 集合;
2. 与 `folder_resource` 的有效本地集合做差集;
3. 对新增 aid 每 200 条请求 `resource/infos` 并入库;
4. 对移除 aid 标记对应 `folder_resource.active=0`;若视频不在任何配置夹中,视频状态改为 `unfaved`;
5. 每 6 小时全量刷新 infos,刷新元数据、`attr` 和收藏夹容量;
6. `attr=9` 或请求集合中静默缺失的资源改为 `removed`;
7. 容量达到 950 条时发送预警;系统绝不自动换夹。
`infos` 只能作为元数据和失效同步来源。它缺少点赞、投币、转发,且评论字段不可靠,不能替代 `view` 日更。
### 7.4 每日快照与粉丝采集
每天 `00:01`(`Asia/Shanghai`)执行:
- 对 `status IN ('active', 'unfaved')` 的视频逐条请求 `view?aid=`;
- 成功写入播放、点赞、收藏、投币、转发、评论、弹幕七项数据;
- `-404` / `62002` / `62012` 时标记 `removed`(62002/62012 为当前实测返回);
- 其他错误最多重试 3 次,仍失败则写入 `ok=0` 与错误信息,不写伪造的零值;
- 每条视频请求之间随机等待 1.0~1.6 秒;
- 对 `track_fans` 中的每个 mid 请求一次 `relation/stat` 并写入 `up_stat`;
- 任务结束向 Telegram 发送成功数、失败数、移除数和总耗时摘要。
服务启动后若当天快照尚未成功完成,应补跑当日任务;已成功的 `(stat_date, aid)` 不重复请求。
P1 实现说明:
- 时区使用固定 UTC+8(中国无夏令时),避免 Windows 缺少 `tzdata`;
- 当日全部视频与粉丝采集成功后在 `kv` 写入 `snapshot:completed:<date>=1`;启动时未完成会补跑;
- `ok=0` 的当日记录会在下次补跑时重试;`ok=1` 直接跳过;
- 每次采集后生成 `data/backup/app-YYYYMMDD.db` 并清理超过 `storage.backup_keep_days` 的日备份;
- Cookie 登录态通过 `x/web-interface/nav` 检查,失效只告警,不阻塞匿名 `view`;
- Telegram 摘要推迟到 P3,P1 先写结构化 JSON 日志和 `kv` 状态。
---
## 8. Telegram 与收藏操作
### 8.1 通知动作
候选通知提供:
- **收藏**:加入目标收藏夹并入库;
- **忽略**:候选标记 `rejected`,不再通知;
- **拉黑**:写入 `up_blacklist`,同时拒绝该 UP 的其他待决策候选;
- **打开视频**:外链到 B 站视频页。
拉黑只影响未来搜索发现;不清理、不停止追踪已有视频,也不影响从收藏夹同步进来的视频。
### 8.2 幂等与失败处理
Telegram `callback_data` 只包含短操作 ID,例如 `fav:<id>`;真实 aid、目标夹、过期时间和状态保存在 `fav_action` 表。
收藏流程:
1. 将动作从 `pending` 原子切换为 `processing`;
2. 先读取目标夹 ids,若视频已存在则按成功处理;
3. 按“同 UP 最早视频所在夹优先,否则当年夹”的规则选择目标夹;
4. 调用 `deal` 加入收藏;
5. 调用 `sort` 将视频放进同 UP 连续分组;
6. 回读 ids 校验最终位置;
7. 成功:动作标记 `done`,视频标记 `active`,删除候选;
8. 失败:动作标记 `failed`,保留候选与原始错误,允许用户重试。
GET 页面或链接预览只能展示信息,绝不能触发任何写操作。
---
## 9. Web API 与页面
### 9.1 API 原则
- `/api/health` 可匿名,仅返回健康状态;
- 其他 `/api/*` 默认要求 `Authorization: Bearer <token>`;
- 写操作额外要求 CSRF 防护与 Origin/Referer 校验;
- API 返回 JSON;前端不复制热度、状态机或收藏夹归属判断;
- CSV 导出由后端根据当前筛选条件生成。
核心接口:
| 方法 | 路径 | 用途 |
|---|---|---|
| `GET` | `/api/overview` | 总览指标、容量、采集状态 |
| `GET` | `/api/videos` | 视频分页、筛选和排序 |
| `GET` | `/api/videos/{aid}` | 视频详情 |
| `GET` | `/api/videos/{aid}/trend` | 七项趋势数据 |
| `GET` | `/api/rankings` | 热度、增长、互动率与单项榜 |
| `GET` | `/api/ups` | UP 聚合榜单 |
| `GET` | `/api/fans` | 粉丝曲线与增量 |
| `GET` | `/api/candidates` | 候选池 |
| `GET` | `/api/system/status` | 最近任务、下次任务、错误日志 |
| `POST` | `/api/candidates/{aid}/favorite` | 收藏候选 |
| `POST` | `/api/candidates/{aid}/ignore` | 忽略候选 |
| `POST` | `/api/candidates/{aid}/blacklist` | 拉黑候选所属 UP |
| `DELETE` | `/api/blacklist/{mid}` | 移除黑名单 |
| `POST` | `/api/videos/{aid}/drop` | 手动停止追踪 |
### 9.2 页面
| 页面 | 内容 |
|---|---|
| 总览 | 库内数、UP 数、今日新增、增量、容量条、采集成功率、故障提示。 |
| 榜单 | 综合热度、七项单项、增长、增速、互动率、UP 聚合、筛选和 CSV。 |
| 视频详情 | 元数据、七项趋势、快照表、失败标记、同 UP 对比。 |
| UP 页 | UP 的全部视频、聚合趋势和平均互动率。 |
| 粉丝页 | 粉丝总数、日增、7 日净增、累计增长;不叠加视频数据。 |
| 候选池 | 待确认候选、规则命中、剩余保留时间和三个操作。 |
| 系统状态 | 最近扫描、同步、快照、下一次运行时间和错误。 |
---
## 10. 热度与趋势口径
### 10.1 综合热度
对每项最新有效快照值计算:
```text
x_i = log1p(metric_i)
z_i = (x_i - median(x)) / (IQR(x) / 1.349)
score = Σ weight_i * z_i
```
默认权重:
```text
view 0.30, like 0.20, coin 0.20, favorite 0.15,
share 0.10, reply 0.03, danmaku 0.02
```
权重从配置读取,改动后查询时重算,不存冗余结果。
### 10.2 增长与互动率
```text
d7 = (view_today - view_previous) / actual_day_gap
r7 = (view_today - view_previous) / max(view_previous, 1)
interaction_rate = (like + coin + favorite) / max(view, 1)
```
- 查询前一有效快照而不是填补缺失日期;
- 缺失日期在图表中断开;
- 增速榜要求播放基数至少 200;
- 互动率榜要求播放至少 100;
- 样本不足两天的视频明确标注,不伪造增长数据。
---
## 11. 测试与质量要求
在引入第三方测试框架前,测试继续使用可直接运行的 Python 脚本:
```powershell
uv run python tests/test_auth.py
uv run python tests/test_moves.py
```
新增模块至少覆盖:
- 标题归一化、时长解析、分类打分;
- 黑名单只影响发现;
- `infos` 静默缺失资源的识别;
- `active`、`unfaved`、`removed`、`dropped` 状态迁移;
- 数据库迁移与幂等写入;
- 每日快照缺失处理;
- 热度、增长和互动率计算;
- 收藏夹目标选择与排序锚点;
- `fav_action` 的重复点击、失败重试和过期处理;
- Telegram 用户与 chat ID 授权校验。
只有当标准库测试维护成本明显过高时,才考虑通过 `uv add --dev pytest` 引入 pytest。
---
## 12. 实施阶段
### P0:数据底座与收藏夹同步 ✅
- SQLite 迁移、配置加载、B 站只读客户端;
- 收藏夹首次导入与每小时差集同步;
- 全量 infos 元数据刷新、失效识别、黑名单种子;
- 在 Windows + `uv` 环境完成只读联调。
完成情况(2026-10-31):
- 已实现 `reaction_board/`:配置热加载、单写队列、`schema_version` 迁移与升级前备份;
- 已实现内置调度器,按 `schedule.folder_sync_minutes` 周期运行收藏夹同步;
- 已实现 `fav/resource/ids`、`fav/resource/infos`、`fav/folder/created/list-all` 只读客户端;
- 首次导入两个年度夹共 831 条资源:795 `active`、36 `removed`(20 条 `attr=9` + 16 条 `infos` 静默缺失),与探测结果一致;
- 增量同步在无变化时只发 2 次 `ids` 请求,不请求 `infos`;
- 黑名单 9 个 mid 已写入 `up_blacklist`,且不参与收藏夹同步过滤;
- 自动化测试见 `tests/test_config.py`、`test_migrations.py`、`test_database.py`、`test_repository.py`、`test_bilibili_client.py`、`test_folder_sync.py`、`test_scheduler.py`、`test_logging.py`、`test_cli.py`。
### P1:快照与粉丝追踪 ✅
- 每日七项视频快照;
- 粉丝快照;
- 失败占位、断点续跑、Cookie 失效告警、SQLite 备份。
完成情况(2026-10-31):
- 已实现 `services/snapshot.py`:`view` 七项指标、`relation/stat` 粉丝数、`ok=0` 失败占位、`ok=1` 断点跳过;
- 实测不可访问视频返回 `62012`,已和旧口径 `-404`、`62002` 一起按 `removed` 处理;
- `run` 调度器同时注册收藏夹同步与每日 `snapshot_at` 任务,启动时立即检查当日是否完成以支持补跑;
- 每次快照后生成日备份并清理超过 14 天的日备份;
- `auth-check` 与快照启动时检查 Cookie 登录态,失效写 `kv` 并告警;
- 真实全量联调:795 条视频全部 `ok=1`,0 失败、0 移除,耗时 1124 秒;粉丝 `follower=2767270`;日备份 `data/backup/app-20261031.db` 含 795 条快照;二次执行正确命中完成标记并跳过;
- 自动化测试见 `tests/test_snapshot.py`、`test_maintenance.py`、`test_timeutil.py`,并扩展了 `test_bilibili_client.py` 与 `test_scheduler.py`。
### P2:本地看板
- 总览、榜单、详情、UP、粉丝、候选和系统状态页面;
- 原生前端和 ECharts;
- 本机默认绑定 `127.0.0.1:8080`。
### P3:Telegram 与一键加入
- long polling、候选通知、收藏/忽略/拉黑;
- `deal`、`sort`、回读验证和失败重试;
- 对真实写入操作使用独立测试视频完成授权验证后再启用。
### P4:可选部署
- 反向代理、TLS、远程访问鉴权;
- 如有需要再提供 Dockerfile;
- Docker 不作为本机开发、联调或日常运行的前置条件。
---
## 13. 明确非目标
第一期不实现:
- 多用户、注册、角色权限;
- 自动创建收藏夹或容量满后自动换夹;
- AI 自动判定 reaction;
- UP 白名单;
- 视频历史统计回填;
- Redis、消息队列、微服务、云数据库;
- Node.js 前端工程;
- Docker 作为本地开发依赖。