# 咻咻满 Reaction 视频数据看板开发文档 > **状态:** P0(数据底座与手动入库)、P1(每日快照与粉丝追踪)、P2(只读本地看板)、P3(Telegram 发现与入库)已实施;项目已与 B 站收藏夹解耦 > > **技术基线:** 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 客户端 | `httpx` | 连接池、keepalive、明确的 connect/read 超时;B 站请求头与 UA 对齐 `data/raw-request.txt`。 | | 数据库 | SQLite(WAL) | 单用户、单机、低并发写入;备份和迁移成本低。 | | 调度 | `threading`、`queue.Queue`、标准库时间工具 | 单进程内运行发现、同步、快照和清理任务。 | | 前端 | 原生 HTML/CSS/JavaScript | 无 Node.js、无构建链;前端仅渲染 `/api/*` JSON。 | | 图表 | 仓库内置 ECharts | 离线静态资源,Apache-2.0 许可。 | | 通知 | Telegram Bot long polling + `httpx` | 具备按钮回调能力,开发和本机运行不需要公网 URL。 | | 容器化 | 可选的后续部署方式 | 本地开发、调试和日常运行均不要求 Docker。 | ### 1.1 选型原则 1. **复用现有能力。** 当前仓库的 `favsort.py` 已实现 B 站设备指纹 Cookie、扫码登录、收藏夹读取和排序逻辑;新功能应拆分复用,而不是另起技术栈。 2. **保持单体。** 当前规模约数百到数千视频,每日采集量约千级请求;单进程、SQLite 和内置调度足够。 3. **标准库优先,成熟依赖可用。** 当依赖能明确改善可靠性、性能或可维护性时允许引入;必须锁定在 `pyproject.toml` / `uv.lock`,隔离在适配层,并用契约测试覆盖。 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 # 扫码登录并原子更新指定账号 uv run python app.py auth --account read uv run python app.py auth --account write # 检查两套 B 站登录状态 uv run python app.py auth-check --account all # 执行当前无网络依赖的测试 uv run python tests/test_auth.py uv run python tests/test_moves.py ``` 未来看板服务实现后,统一使用: ```powershell # 建库、迁移与黑名单种子(离线,P0) uv run python app.py init # 手动入库:视频链接、BV 号、av 号或 b23.tv 短链(只写本地库) uv run python app.py ingest BV1x8x166EzY uv run python app.py ingest https://www.bilibili.com/video/BV1x8x166EzY # 按配置周期常驻运行发现与快照(P0/P3);--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 # 回填历史视频的 UP 粉丝数快照(第 9 轮;只补空值) uv run python app.py backfill-followers uv run python app.py backfill-followers --dry-run # 检查 Cookie 登录态(P1) uv run python app.py auth-check # 查看数据库状态(离线,P0) uv run python app.py status # P2 起启动本地看板服务 uv run python app.py serve uv run python app.py serve --config .\config.jsonc # 无子命令启动完整服务:只读 Web + 调度 + Telegram uv run python app.py # 生成 7 天预览库并启动预览 uv run python tools/seed_preview_db.py --days 7 uv run python app.py serve --preview --config data/preview/config.jsonc # 从 B 站粉丝历史 CSV 回填(先 dry-run;首次完整导入可用 --replace) uv run python tools/import_fans_csv.py --csv E:\Downloads\history.csv --dry-run uv run python tools/import_fans_csv.py --csv E:\Downloads\history.csv --replace ``` 不建议直接激活 `.venv`;优先使用 `uv run ...`,这样命令总是运行在项目锁定的解释器和环境中。 ### 2.4 依赖变更规范 若后续确实需要新增依赖,应使用 `uv` 修改项目元数据,而不是直接使用 `pip install`: ```powershell # 新增运行依赖(仅在标准库无法合理满足需求时) uv add # 新增仅用于开发的依赖 uv add --dev # 同步锁文件和环境 uv sync --all-groups ``` 提交依赖变更时,必须同时提交: - `pyproject.toml` - `uv.lock` 不得提交: - `.venv/` - `config.jsonc` / `config.json` - `secrets.json` / `secrets.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`; - 读操作使用短生命周期连接; - 网络请求期间不得持有数据库写事务; - 入库操作通过 `video` 存在性判断保证幂等; - 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.jsonc # 运行期业务配置(JSONC,不提交) ├── secrets.json # 运行期凭据(auth 回写目标,不提交) ├── config.example.jsonc # 脱敏业务配置样例(含注释) ├── secrets.example.json # 脱敏凭据样例 ├── config.docker.example.jsonc # 服务器业务配置样例 ├── DEVELOPMENT.md │ ├── reaction_board/ │ ├── config.py # 配置、ENV 覆盖、mtime 热加载 │ ├── analytics.py # 热度、增长、互动率、时长效率、EB 收缩与封面工具 │ ├── database.py # SQLite 连接、迁移、单写队列 │ ├── migrations.py # schema_version 与迁移 │ ├── scheduler.py # 内置任务调度 │ ├── logging_json.py # 脱敏 JSON 日志 │ ├── classify.py # 标题归一化、时长解析与打分 │ ├── client_ip.py # 来源 IP 提取、路由归一化、机器人判定(纯函数) │ ├── video_ref.py # BV/av/URL/b23.tv 引用解析(纯函数) │ ├── timeutil.py # 上海时区与每日时间 │ ├── bilibili/ │ │ ├── client.py # 只读 HTTP、重试与限速(含 view 详情、短链解析) │ │ ├── write_client.py # sort 写客户端(仅收藏夹重排序) │ │ ├── search.py # WBI 搜索与旧接口降级 │ │ ├── provider.py # 凭据变化重建客户端 │ │ ├── auth.py # Cookie、设备指纹与请求头工具 │ │ ├── auth_flow.py # app.py auth 的扫码与原子写回流程 │ │ └── models.py # 接口响应模型 │ ├── services/ │ │ ├── discovery.py # 搜索发现与候选池 │ │ ├── library.py # 入库(候选/手动)/ 忽略 / 拉黑 │ │ ├── telegram_poller.py # getUpdates、回调分发与私聊手动入库 │ │ ├── snapshot.py │ │ ├── up_profile.py # UP 粉丝快照采集与历史回填 │ │ └── maintenance.py │ ├── notify/telegram.py # Telegram 消息与按钮 │ └── web/ # P2 只读看板 │ ├── server.py # HTTP、路由、鉴权、静态资源 │ ├── queries.py # 看板 SQL 与数据整形 │ ├── visits.py # 访问统计内存缓冲与批量落库 │ └── static/ # 原生 JS/CSS、ECharts、预览页面 │ ├── deploy/ │ └── nginx/ # 反代示例:真实来源 IP(realip)与站点配置 │ ├── tools/ │ ├── seed_preview_db.py # 复制真实库并回填 7 天预览数据 │ └── import_fans_csv.py # 从 B 站粉丝历史 CSV 回填 up_stat │ ├── tests/ └── data/ # 本地运行期数据,默认不提交 ``` `favsort.py` 继续保持可独立运行,但只操作 `bilibili.write`;扫码授权统一由 `app.py auth --account read|write` 提供。Cookie、设备指纹与请求头逻辑必须共用 `reaction_board.bilibili.auth`。 --- ## 5. 配置与凭据 ### 5.1 运行期配置 业务参数放在 `config.jsonc`(JSONC:`//` / `/* */` 注释与尾逗号;实现为自研轻量剥离后再 `json.loads`,无额外依赖)。 敏感凭据放同目录 `secrets.json`(纯 JSON,供 `auth` 原子回写)。二者都必须被 Git 忽略,日志中不得输出敏感值。 默认路径优先 `config.jsonc`,否则回落 `config.json`;旧单体 `config.json`(凭据内嵌)仍可加载,若存在 `secrets.json` 则凭据以它为准。 `config.jsonc` 示例: ```jsonc { // bilibili / bot_token / auth_token → secrets.json "telegram": { "enabled": true, }, "web": { "host": "127.0.0.1", "port": 8080, // auth_token → secrets.json "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, "snapshot_at": "23:15", "seed_at": "00:00", "candidate_ttl_days": 7 }, "notify": { "min_score": 2, "max_items_per_message": 10 } } ``` - `folders` 仅作为历史来源与 `favsort.py` 重排序目标,系统不再自动同步或建夹; - `bilibili.read` / `bilibili.write` 写在 `secrets.json`(或旧单体主配置内);不兼容顶层扁平 `mid` / `csrf` / `cookie`; - `python app.py auth` 只原子回写 `secrets.json`(并保留 `.bak`),不改主 JSONC; - P0/P3 已实现的命令行:`python app.py init`、`python app.py ingest`、`python app.py status`、`python app.py run`。 ### 5.2 安全规则 - Windows 本地开发时应限制 `config.jsonc` / `secrets.json` 的文件访问权限;部署到 Linux 时权限必须为 `600`; - 主配置与凭据文件每 60 秒检查一次 mtime,凭据、分类规则和权重变更后无需重启; - Cookie / CSRF 变化时由 `ClientProvider` 重建 B 站请求客户端,并保留该角色的限速与重试策略; - `schedule.discover_minutes`、`schedule.snapshot_at` 与 `schedule.seed_at` 在启动时固化,修改后需重启 `run`; - Telegram 操作必须同时验证 `from.id` 和 `chat.id` 是否为配置的私聊用户; - 仅允许环境变量临时覆盖敏感配置,不能将真实凭据硬编码进源代码; - `GET` 路由绝不进行入库、忽略、拉黑等写操作。 --- ## 6. 数据模型 ### 6.1 核心状态 | 表 | 状态 | 含义 | 是否参与每日快照 | |---|---|---|---| | `video` | `active` | 在库追踪的视频 | 是 | | `video` | `unfaved` | 历史遗留状态(迁移 v5 已归一为 `active`) | 是 | | `video` | `removed` | 视频失效、删除或不可访问 | 否 | | `video` | `dropped` | 手动移出库;再次入库会恢复 `active` | 否 | | `candidate` | `NULL` | 待人工决策 | 否 | | `candidate` | `rejected` | 已忽略 / 所属 UP 已拉黑 | 否 | ### 6.2 建表原则 最终 DDL 由迁移模块维护,关键约束如下: - `snapshot` 主键为 `(stat_date, aid)`,保证同日幂等; - `up_stat` 主键为 `(stat_date, mid)`; - `visit` 访问统计表主键为 `(stat_date, ip, path)`,`hits` 累加;数据先落内存缓冲、批量 upsert,`PV = SUM(hits)`、`UV = COUNT(DISTINCT ip)`; - `folder` / `folder_resource` 保留为历史数据与重排序参考,**不再由服务写入**; - `up_blacklist` 只在发现任务中生效,不影响已入库视频; - `seen_aid` 永久保留搜索去重标记,候选过期删除后也不重复通知; - `fav_action` 保留为通用动作状态表(当前入库幂等由 `video` 存在性判断承担); - 迁移 v5 把历史 `unfaved` 行归一为 `active`;表结构不做破坏式删除。 ### 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 搜索失败时使用 | | 视频详情 | `x/web-interface/view?aid=` / `?bvid=` | 快照七项指标、手动入库元数据 | | 短链解析 | `b23.tv/` | 跟随重定向还原为视频 URL | | 粉丝数 | `x/relation/stat?vmid=` | 每日粉丝快照 / 入库时粉丝数 | | 收藏夹 ID | `x/v3/fav/resource/ids` | `favsort.py` 重排序计划 | | 收藏夹详情 | `x/v3/fav/resource/infos` | `favsort.py` 重排序计划 | | 收藏夹列表 | `x/v3/fav/folder/created/list-all` | 列出重排序目标夹 | | 收藏夹排序 | `x/v3/fav/resource/sort` | 收藏夹重排序(唯一写操作) | B 站请求统一使用 `httpx` 连接池,并按 `data/raw-request.txt` 对齐浏览器特征: - `user-agent`:Chrome 155 / Windows; - `sec-ch-ua`、`sec-ch-ua-mobile`、`sec-ch-ua-platform`; - `origin: https://space.bilibili.com`; - `referer`:收藏夹请求指向 `https://space.bilibili.com//favlist?fid=&ftype=create`; - `priority: u=1, i`; - 匿名请求仍剥离账号身份 Cookie,仅保留设备指纹。 请求头集中在 `bilibili/auth.py::make_headers`,不要在业务层散落自定义 Header。 ### 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:丢弃 ``` P3 实现说明: - WBI 密钥来自 `nav.data.wbi_img`,缓存 6 小时;WBI 失败自动降级旧版 `search/type`; - 关键词、页数和时间窗由 `discover` 配置控制; - 同一 aid 在多个关键词/页重复出现时,候选与通知都按 aid 去重; - 已通知但未决策的候选不会重复通知;未通知的候选会在后续发现轮次补发; - 候选确认入库后会删除同 aid 候选;私聊手动入库也可直接创建视频行。 ### 7.3 收藏夹解耦 项目**不再轮询收藏夹**,也不再根据收藏夹内容标记视频入库/出库: - 不再调用 `x/v3/fav/resource/ids` / `infos` 做差集同步; - 历史 `folder` / `folder_resource` / `video.folder_id` 数据保留,仅供历史展示与 `favsort.py` 重排序参考; - 迁移 v5 把历史 `unfaved` 状态归一为 `active`; - 收藏夹由人工维护;需要时用 `favsort.py`(唯一使用 `bilibili.write`)做低频重排序; - 入库只依赖只读接口(`view` / `relation/stat`)。 ### 7.4 每日快照与粉丝采集 每天 `23:15`(`Asia/Shanghai`)执行全量落盘;`00:00` 用上一完成日克隆当日壳(seed): - 对 `status IN ('active', 'unfaved')` 的视频逐条请求 `view?aid=`; - 成功写入播放、点赞、收藏、投币、转发、评论、弹幕七项数据,并把 `view` 的 `owner.face` 写入 `video.up_face`(UP 头像,供 UP 列表/详情展示); - `-404` / `62002` / `62012` 时标记 `removed`(62002/62012 为当前实测返回); - 其他错误最多重试 3 次,仍失败则写入 `ok=0` 与错误信息,不写伪造的零值; - 每条视频请求之间随机等待 1.0~1.6 秒; - 对 `track_fans` 中的每个 mid 请求一次 `relation/stat` 并写入 `up_stat`; - 可用 `tools/import_fans_csv.py` 从 B 站粉丝历史 CSV 回填 `up_stat`;只写 `up_stat`,默认跳过已有成功记录,`--force` 覆盖同日期,`--replace` 用完整历史覆盖重建(存在晚于 CSV 末尾的记录时默认拒绝,需显式 `--allow-lossy-replace`); - 任务结束向 Telegram 发送成功数、失败数、移除数和总耗时摘要。 服务启动后若当天快照尚未成功完成,应补跑当日任务;已成功的 `(stat_date, aid)` 不重复请求。 P1 实现说明: - 时区使用固定 UTC+8(中国无夏令时),避免 Windows 缺少 `tzdata`; - 午夜 `seed_at`(默认 `00:00`)把 `snapshot:last_completed` 的 ok=1 行克隆为当日壳,写入 `snapshot:seeded:=1` / `fans:seeded:=1`,**不**推进完成日; - 也可手动:`python app.py snapshot-seed [--date YYYY-MM-DD]`(Debian / `docker compose exec re-xxm python app.py snapshot-seed`); - 目标日已有 `snapshot:completed:` 时 seed 记 `already_completed` 成功(即使 `last_completed` 已等于当日),**不得**误判 `no_source`; - 若全站 `snapshot` 正在跑,或目标日未完成且 `last_completed` 暂不可用(`no_source`),seed **不记成功**,约 10 分钟后重试直到真正克隆成功; - 晚间 `snapshot_at`(默认 `23:15`)对 seeded 日 force 重采;成功后写 `snapshot:completed:=1`、推进 `last_completed` 并清除 seeded; - 当日全部视频与粉丝采集成功后在 `kv` 写入 `snapshot:completed:=1`;启动时未完成会补跑(纯 seed 日不误标完成); - 完成标记不是唯一门槛:每次检查 `pending_snapshot_aids(date)`,新入库或从 `dropped` 恢复的视频会立即补采当天快照; - `ok=0` 的当日记录会在下次补跑时重试;`ok=1` 直接跳过;粉丝同样跳过已成功的 `(stat_date, mid)`; - 开始采集前先写 `snapshot:completed:=0`,强制重采中断后不会残留旧的完成标记; - 每次采集后生成 `data/backup/app-YYYYMMDD.db` 并清理超过 `storage.backup_keep_days` 的日备份; - 采集完成与备份完成分开记录;备份失败时 `snapshot` 返回 1,下次执行只重试备份、不重复请求 B 站; - Cookie 登录态通过 `x/web-interface/nav` 检查,失效只告警,不阻塞匿名 `view`; - Telegram 摘要推迟到 P3,P1 先写结构化 JSON 日志和 `kv` 状态。 调度避让与补跑: - 常驻启动时日更任务(`snapshot_seed` / `snapshot` / `fans`)按墙钟下一档 `seed_at` / `snapshot_at` 排期(可参考上次成功/失败记录),**不得** `next_run=0` 重启即跑; - 仅到达当日 `snapshot_at` 后(或 CLI `--force`)才对 seed 壳 `force` 打穿;白天重启不因 `snapshot:seeded` 全量强采; - 全站 `snapshot` **不因**其它任务 running 而 skip;到期/强制必须执行; - 上海时区 **23:00–01:00** 为全站快照窗:`discovery` / `snapshot_recent` / `candidate_cleanup` 不执行,延后到 01:00 后;`fans` 短耗时,不避开任何任务; - 任务 skip(含静默窗延后)写入 `ok=false` + `skipped=true`,**不得**记成成功或推进「当日已完成」; - skip / fail 后短间隔补跑(约 5 分钟;`snapshot_seed` 避让/无源为约 10 分钟);日更成功才推到次日 `snapshot_at` / `seed_at`。 --- ## 8. Telegram 与入库操作 ### 8.1 通知动作 候选通知提供: - **入库**:写入本地 `video(status='active')` 并补当天快照,**不写 B 站收藏夹**; - **忽略**:候选标记 `rejected`,不再通知; - **拉黑**:写入 `up_blacklist`,同时拒绝该 UP 的其他待决策候选; - **打开视频**:外链到 B 站视频页。 私聊文本消息还会被解析为视频引用(链接 / BV 号 / av 号 / `b23.tv` 短链)并逐条入库。拉黑只影响未来搜索发现;不清理、不停止追踪已有视频。 ### 8.2 幂等与失败处理 Telegram `callback_data` 只包含短操作 ID,例如 `add:` / `ign:` / `blk:`;`fav:` 为旧按钮兼容。 入库流程: 1. 候选存在校验;若视频已在库则返回「已在库」; 2. 用只读客户端取一次 `view` 详情(失败时回退候选元数据); 3. 写 `video(status='active')`,删除同 aid 候选; 4. 用取到的 `view` 数据写一条当天快照(同日已有成功记录则跳过); 5. 采集该 UP 当前粉丝数并提升缓存版本。 私聊手动入库按消息中的每个引用重复上述步骤,错误逐条隔离,不中断其余引用。 GET 页面或链接预览只能展示信息,绝不能触发任何写操作。 P3 实现说明: - `callback_data` 使用 `add:` / `ign:` / `blk:`(`fav:` 兼容旧消息); - 入库只写本地库;`video` 的 `removed` / `dropped` / `unfaved` 状态会被重新激活; - 引用解析是纯函数 `video_ref.extract_video_refs`,`b23.tv` 短链由 `bilibili.client.resolve_short_link` 跟随跳转; - 手动入库不做「标题含咻咻满」等规则校验;单条消息最多处理 50 个引用,超出会在回执中提示分批发送;无效 BV/av 号(`-400`)逐条报错、不中断其余引用; - Telegram 只接受 `chat_id` 与 `from.id` 都等于配置私聊用户的回调与消息。 --- ## 9. Web API 与页面 ### 9.1 API 原则 - P2 看板**只读**:业务接口只注册 GET,写方法统一返回 `405`; - 仅显式放行两个 POST 端点:`POST /login`(建立会话)与 `POST /api/visit`(访问统计埋点);二者都不修改业务数据,其余 POST/PUT/PATCH/DELETE 仍统一 `405`; - 数据管理(入库、忽略、拉黑、移出库、移除黑名单)继续通过 Telegram、CLI 或数据库完成; - `/api/health` 和 `/api/session` 可匿名; - 首页、榜单、视频/UP、粉丝页面和接口访客可见; - 配置 `web.auth_token` 后,`/api/candidates`、`/api/system` 和 `/candidates`、`/system` 页面要求 Cookie 或 `Authorization: Bearer `,未登录访问页面会跳转 `/login?next=<原路径>`;「候选」「系统」入口放在顶栏右侧(未登录显示「登录」、登录后显示「候选 | 系统」),不占底部主导航,避免移动端底部拥挤; - 未配置 token 时视为无鉴权,全部页面可见; - 只读接口不需要 CSRF;登录 POST 只建立会话,不修改业务数据; - API 返回 JSON;前端不复制热度或状态机判断; - 不提供导出接口,结果只通过页面浏览; - 首页、榜单和粉丝的聚合结果使用内存 TTL 缓存:首页/榜单 1 小时,粉丝 24 小时; - 缓存版本存放在 `kv`(`cache:v:videos` / `cache:v:fans`);快照、发现、入库/忽略/拉黑、候选清理和粉丝 CSV 导入都会提升对应版本,使缓存立刻失效。 核心接口: | 方法 | 路径 | 用途 | |---|---|---| | `GET` | `/api/overview` | 首页动态、总览指标、容量、采集状态 | | `GET` | `/api/videos` | 视频分页、筛选和排序(`sort` / `within_days` / `year` / `q`;支持 added/published/heat/七项/互动/增长/内容效率;默认 `published`) | | `GET` | `/api/videos/{aid}` | 视频详情(含分 P 数、入库时粉丝数、内容/互动效率) | | `GET` | `/api/videos/{aid}/trend` | 七项趋势数据 | | `GET` | `/api/rankings` | 兼容旧链接;`scope=up` 时为 UP 聚合榜(`year` 可用) | | `GET` | `/api/ups` | UP 聚合榜单(含总时长、播放·小时、内容效率、UP 头像 `up_face`、最新粉丝数 `follower_latest`;`sort` / `within_days` / `year`;默认 `avg_views`) | | `GET` | `/api/ups/{mid}` | UP 详情、聚合趋势、头像和视频列表 | | `GET` | `/api/fans` | 粉丝曲线与增量 | | `GET` | `/api/candidates` | 候选池(只读) | | `GET` | `/api/system` | 最近任务、下次任务、备份、黑名单 | | `GET` | `/api/about` | 数据说明页当前口径(配置阈值、权重、覆盖度;无凭据) | | `POST` | `/api/visit` | 访问统计埋点(匿名):归一化路径后写内存缓冲,定期批量落库 | | `GET` | `/api/stats` | 访问统计(需登录):每日 PV/UV 序列、Top 页面,合并未落库缓冲 | ### 9.2 页面 | 页面 | 内容 | |---|---| | 首页 | 最新动态优先:标题下显示「收录视频 N」;顶部四个数据卡片为「今日入库→视频榜按入库排序 / 播放增量→视频榜按 1 日增长排序 / 7 日粉丝变化→粉丝页 / 数据日期」;下方为最近发布、增长动态、热度速览三个区块,统一使用竖排媒体卡(封面 + 两行截断标题 + 作者/数据行 + 高亮数据行)与响应式网格:移动端每行 2 个、PC 每行 3 个,每处各取前 6 条;采集状态等运行期信息只在登录后的「系统」页可见;有候选且已登录时,首页显示「N 条候选待确认」提示条(访客不显示)。 | | 视频(`/videos`) | 发布时间(默认)、内容效率、综合热度、入库时间、播放/投币/收藏/评论、互动率、播放增量、播放增速;工具条为「排序」下拉 + 时间下拉 + 右侧放大镜搜索框(一行展示),时间支持全部/7 天/30 天/90 天/1 年/年份。 | | UP主(`/ups`) | 独立入口,不再与视频混排;卡片显示 UP 头像、视频数与总时长、最新粉丝数,以及当前排序的主/副指标(「总播放」排序时副指标为「平均播放」);排序下拉含平均播放(默认)、内容效率、互动效率、内容量、总播放、视频数、总互动、最近发布;不提供按视频数筛选。 | | 数据说明(`/about`) | 数据来源与更新、发布时间/入库时间/快照日期、指标公式、时长与成熟窗口筛选、粉丝快照覆盖与已知局限;数值由 `/api/about` 动态读取。 | | 视频详情 | 元数据、分 P 数、七项指标、内容/互动效率、入库时粉丝数、单指标趋势、快照表、同 UP 对比。 | | UP 页 | 该 UP 的头像、首次观看与最近观看时间、全部视频(默认按发布时间排序)、聚合趋势、单视频对比、总时长与播放·小时/互动·小时、入库时与当前粉丝数。 | | 粉丝页 | 粉丝总数、日增、7 日净增、累计增长(标注起始日期);支持预设和自选日期,范围超过一年时按每月峰值日期聚合;最近记录默认 14 天并显示“较上一成功日”增量;不叠加视频数据。 | | 候选池 | 待确认候选、规则命中、剩余保留时间;操作指向 Telegram/CLI。 | | 系统状态 | 最近扫描、同步、快照、下次运行、备份、鉴权、黑名单与访问统计(近 30 天 PV/UV 与 Top 页面)。 | ### 9.3 预览模式 - `tools/seed_preview_db.py` 复制真实库并回填合成快照,真实库只读; - 合成快照写 `src='preview'`,预览库写 `kv preview:seeded=1`; - `python app.py serve --preview` 校验标记,缺失时拒绝启动; - 预览配置不包含真实凭据,默认端口 8081,Telegram 关闭; - 预览页面显示固定“预览数据 · 不影响真实数据库”横幅(不再有独立的“预览”入口); - 重新生成预览库前必须停止预览服务,避免 Windows 文件占用; - 第 9 轮起本地不再保留 `data/app.db` 真实副本;重新生成预览时用 `--source` 指向 `data/backup/` 中的真实库备份(如 `app-local-before-reseed-20261101.db`)。 ### 9.4 访问统计与真实来源 IP - 前端在整页加载与站内路由切换时通过 `navigator.sendBeacon("/api/visit")` 上报路径(`sort`/筛选等同路径重渲染不重复计数); - 服务端 `reaction_board/web/visits.py` 把上报按 `(日期, IP, 路由)` 聚合到内存缓冲,后台线程每 `web.track.flush_seconds`(默认 60 秒)批量 upsert 到 `visit` 表;`server_close()` 时做最后一次 flush; - `/api/stats` 合并未落库缓冲,因此系统页基本实时;`keep_days` 到期数据由后台按日清理; - 真实 IP 由 nginx 用 `realip` 模块按 CDN 网段归一化后写入 `X-Real-IP`;应用(`reaction_board/client_ip.py`)仅在 socket 对端属于 `web.track.trusted_proxies` 时采信请求头,否则回退对端,避免直连请求伪造 `CF-Connecting-IP`/`EO-Connecting-IP`/`X-Forwarded-For`; - 直连公网 EIP、Cloudflare、EdgeOne 三种来源的 nginx 配置与网段刷新脚本见 `deploy/nginx/`;配置项见 README 的 `web.track` 说明; - 进程被强杀时最多丢失一个刷新周期的上报(可接受)。 --- ## 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) ``` - 查询前一有效快照而不是填补缺失日期; - 缺失日期在图表中断开; - 增速榜要求播放基数至少 1000; - 互动率榜要求播放至少 100; - 样本不足两天的视频明确标注,不伪造增长数据。 ### 10.3 时长效率与 UP 内容效率(第 9 轮) ```text 内容小时 = duration / 3600 内容效率 = (view + κ_v·μ₀) / (内容小时 + κ_v) 互动效率 = (like + coin + favorite + κ_v·μ₀ᵢ) / (内容小时 + κ_v) UP 内容效率 = (Σview + κ·μ₀) / (Σ内容分钟 + κ) * 60 ``` - 分母只用总时长,不用分 P 数(B 站播放记在 bvid 上,`view/pages` 无统计含义); - 视频级 EB 收缩 `κ_v = analytics.video_shrink_minutes / 60`(默认 60 分钟),`μ₀` 为全库播放/小时;详情页展示原始播放/小时; - 效率榜与综合热度榜要求播放 ≥ `analytics.min_view_for_ranking`(默认 2000); - 效率榜要求 `duration >= analytics.min_duration_seconds`(默认 600 秒)且发布满 `analytics.maturity_days`(默认 2 天); - 不引入 `log(age)` 等无界年龄项,避免系统性惩罚老视频; - `duration` 缺失或非正数不参与效率排序; - UP 收缩强度 `κ` 取全库 UP 总内容分钟中位数,`analytics.shrink_minutes` 可覆盖; - 视频数与总时长并列展示、分别排序,不合成单一产出分。 --- ## 11. 测试与质量要求 在引入第三方测试框架前,测试继续使用可直接运行的 Python 脚本: ```powershell uv run python tests/test_auth.py uv run python tests/test_moves.py ``` 新增模块至少覆盖: - 标题归一化、时长解析、分类打分; - 视频引用解析(BV/av/URL/`b23.tv` 短链)与 `view?bvid=` 契约; - UP 头像与粉丝数列:`view`/`view_detail` 的 `owner.face` 解析、入库与快照写入 `video.up_face`、`/api/ups` 的 `up_face`/`follower_latest` 映射; - 黑名单只影响发现; - `infos` 静默缺失资源的识别; - `active`、`unfaved`、`removed`、`dropped` 状态迁移; - 数据库迁移与幂等写入; - 每日快照缺失处理; - 热度、增长和互动率计算; - 入库动作(候选确认 / 手动)与幂等; - `fav_action` 的重复点击、失败重试和过期处理; - Telegram 用户与 chat ID 授权校验; - 看板查询的筛选、分页、增长冷启动与状态过滤; - 只读路由、鉴权、静态资源路径穿越和预览标记校验; - 来源 IP 提取(直连 EIP/Cloudflare/EdgeOne、不可信对端伪造、IPv6、带端口、回退)与路由归一化、机器人判定; - 访问统计内存缓冲(聚合、跨日、单键上限、flush 幂等、写失败并回、保留期清理)与 `POST /api/visit`、`GET /api/stats` 契约; - 预览库生成的确定性、单调性、异常样本与真实库隔离; - 粉丝历史 CSV 解析、同日去重、缺口计数与幂等/替换回填; - UP 粉丝快照:首次写入不可覆盖、同轮去重、失败不中断、`live_since` 实时/回填标记、`backfill` 的 dry-run 与 limit; - 时长效率:`duration` 缺失/非正数、短于门槛、未满成熟窗口的排除,EB 收缩边界(除零、负先验)。 只有当标准库测试维护成本明显过高时,才考虑通过 `uv add --dev pytest` 引入 pytest。 --- ## 12. 实施阶段 ### P0:数据底座与收藏夹同步 ✅(收藏夹同步已于第 10 轮移除) - 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` 任务,启动时立即检查当日是否完成以支持补跑(早期同时注册收藏夹同步,已于第 10 轮移除); - 每次快照后生成日备份并清理超过 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`。 ### P1 补丁:数据完整性与热更新修复 ✅ - 新入库/恢复视频当天补采:`pending_snapshot_aids()` + 同步后立即触发快照; - 凭据热更新:`ClientProvider` 检测 `mid/cookie/csrf` 变化后重建客户端; - 协议防御:`ids` / `infos` 缺失 `data` 或 `data=null` 时中止,不再当作空结果; - 空远端保护:远端 0 条而本地非空时跳过差集,需 `--allow-empty-folder` 才执行清空; - 粉丝续跑:非强制模式跳过已成功的 `(stat_date, mid)`; - 强制重采:开始前先写未完成标记,中断后可续跑; - 备份恢复:采集完成与备份完成分离,备份失败返回 1 并支持无请求重试; - 跨夹主归属:迁移 v2 增加 `folder_resource.fav_time`,按有效关系重算 `video.folder_id`; - `dropped` 视频失效时保留原始标题; - `favsort.py`:`--verify-every` 必须为正数;`sort` 使用真实资源类型,不再硬编码 `2`; - 配置每 60 秒检查 mtime;调度参数变更需重启。 ### P3:Telegram 与一键加入 ✅ - long polling、候选通知、收藏/忽略/拉黑; - `deal`、`sort`、回读验证和失败重试; - 对真实写入操作使用独立测试视频完成授权验证后再启用。 完成情况(2026-10-31): - 已实现 `bilibili/search.py`:WBI 签名、密钥缓存 6 小时、旧版搜索降级; - 已实现 `classify.py`:标题归一化、时长解析与打分; - 已实现 `services/discovery.py`:关键词/页数/时间窗过滤、黑名单、候选池、通知批次; - 已实现 `notify/telegram.py` 与 `services/telegram_poller.py`:消息分片、Inline Keyboard、回调授权、offset 推进; - 已实现 `bilibili/write_client.py`、`fav_position.py`、`services/favorites.py`:目标夹选择、`deal`、`sort`、回读校验、失败保留候选; - `app.py` 新增 `discover` / `candidates`;`run` 注册发现与候选清理任务,并启动 Telegram poller; - 真实只读联调:4 次 WBI 搜索、168 条结果、1 条候选(score=6),Telegram 通知发送成功; - 真实 `deal` / `sort` 尚未在收藏夹执行,等待用户点击 Telegram 按钮或授权测试视频后验证; - 自动化测试见 `tests/test_classify.py`、`test_search.py`、`test_candidates.py`、`test_discovery.py`、`test_fav_position.py`、`test_favorites.py`、`test_telegram.py`、`test_httpx_transport.py`、`test_favsort_http.py`。 ### P2:只读本地看板 ✅ - 首页、榜单、视频/UP 详情、粉丝、候选和系统状态页面; - 原生前端和本地 vendored ECharts; - 本机默认绑定 `127.0.0.1:8080`; - Web 业务接口只读,写操作继续由 Telegram/CLI/数据库承担; - 首页默认“最新动态”,增长榜在不足 2 天时显示空态; - 视频趋势按单指标切换,缺失日断开、失败快照按缺失处理; - UP 榜默认平均播放,总播放/视频数排到末尾;单视频 UP 不显示单视频对比图; - 实现 `tools/seed_preview_db.py`:复制真实库并回填 7 天合成快照,支持 `--profile live|first-day`; - 实现 `tools/import_fans_csv.py`:从 B 站粉丝历史 CSV 回填 `up_stat`,支持 dry-run、`--force` 覆盖和 `--replace` 重建; - `serve --preview` 校验 `preview:seeded=1` 标记,预览页面显示“预览数据”横幅; - 自动化测试见 `tests/test_analytics.py`、`test_preview_seed.py`、`test_web_queries.py`、`test_web_server.py`。 ### 第 9 轮:时长效率榜、粉丝快照与导航拆分 ✅(2026-11-01) - 迁移 v4:`video.pages`、`video.up_follower` / `up_follower_at` / `up_follower_backfilled`、`up_profile`; - 收藏夹同步(已于第 10 轮移除)结束时采集缺失 UP 的粉丝数(`relation/stat`,匿名可用);`first_seen >= 运行开始 - 1h` 记为实时,更早的记为回填;第 10 轮起改为「入库时」采集; - 粉丝采集对每条 `relation/stat` 加 45s 硬超时看门狗(防 DNS/传输层挂起),每 25 条输出 `up_profile.progress` 进度;单条失败不阻塞整批; - `app.py backfill-followers`:一次性回填历史视频粉丝数,支持 `--limit` / `--dry-run`; - 新增内容效率/互动效率榜与 UP 内容效率(Gamma–Poisson EB 收缩),过滤 < 600 秒与未满 2 天的视频; - `analytics` 配置段:`min_duration_seconds`、`maturity_days`、`min_view_for_ranking`、`video_shrink_minutes`、`shrink_minutes`、`growth_rate_prior`; - 主导航拆分:`/videos`(视频)与 `/ups`(UP主)独立;旧 `/rank` 保留兼容; - 新增 `/about` 数据说明页与 `/api/about`; - 卡片精简:列表只保留主指标与互动率,明细移到详情页; - 本地只保留可再生的预览库 `data/preview/app.db`; - 自动化测试新增 `tests/test_up_profile.py`,并扩展 `test_analytics.py`、`test_repository.py`、`test_config.py`、`test_folder_sync.py`、`test_web_queries.py`、`test_web_server.py`、`test_migrations.py`、`test_database.py`、`test_cli.py`。 ### 第 10 轮:收藏夹解耦、手动入库与写鉴权收口 ✅(2026-11-01) - **入库只写本地库**:候选确认不再调用 `deal` / `sort` 写收藏夹,改为写 `video(status='active')` 并补当天快照; - **移除收藏夹追踪**:删除 `services/folder_sync.py`、`FavoriteActionService`、`app.py sync` 与调度中的 `folder_sync` 任务;`folder` / `folder_resource` / `video.folder_id` 仅保留为历史数据; - **迁移 v5**:把历史 `unfaved` 状态归一为 `active`(不再有「取消收藏」语义); - **手动入库**:新增 `video_ref.py` 纯函数解析 BV/av/URL/`b23.tv` 短链;`bilibili/client.py` 新增 `view?bvid=` 详情与短链跳转;`services/library.py` 统一候选/手动入库; - **Telegram**:`get_updates` 订阅 `callback_query` + `message`;私聊文本消息自动解析并入库;按钮改为「入库 / 忽略 / 拉黑」(`add:`,兼容旧 `fav:`); - **写鉴权收口**:删除 `write_client.deal`,`bilibili.write` 仅用于 `favsort.py` 收藏夹重排序; - **配置**:移除 `schedule.folder_sync_minutes` / `full_folder_refresh_hours`;`folders` 语义变为历史来源与重排序目标; - 看板:系统页与首页把「收藏夹同步/容量」改为「历史收藏夹」;候选页文案改为「入库」。 - 自动化测试:新增 `tests/test_video_ref.py`、`tests/test_library.py`,重写 `test_telegram.py`,删除 `test_folder_sync.py` / `test_fav_position.py` / `test_favorites.py`,并更新 `test_bilibili_client.py`、`test_cli.py`、`test_migrations.py`、`test_database.py`、`test_web_queries.py`、`test_web_server.py`、`web_fixtures.py`。 ### 第 11 轮:UP 头像与 UP 列表信息完善 ✅(2026-11-01) - 迁移 v6:`video.up_face`(来自 `view` 的 `owner.face`);入库与每日快照都会刷新该字段; - `ViewStat` / `VideoDetail` 增加 UP `mid/name/face` 解析; - UP 列表卡片由视频封面改为 UP 头像(无头像时显示首字母占位)、常驻显示最新粉丝数;「总播放」排序时副指标显示「平均播放」,避免与主指标重复; - UP 详情页补充头像; - UP 详情页移除已解耦的收藏夹徽标,改为显示「首RE时间」(该 UP 最早视频发布时间)与最近发布时间;`/api/ups/{mid}` 不再返回 `folders`; - 视频详情页同步移除已解耦的收藏夹徽标; - `/api/ups` 返回 `up_face` 与 `follower_latest`,`/api/ups/{mid}` 返回 `up_face`; - 自动化测试更新:`test_migrations.py`、`test_database.py`、`test_web_queries.py`、`test_bilibili_client.py`、`test_library.py`、`test_snapshot.py`、`web_fixtures.py`。 ### 第 12 轮:访问统计(PV/UV)✅(2026-11-01) - 迁移 v7:新增 `visit(stat_date, ip, path, hits, first_at, last_at)` 聚合表; - `reaction_board/client_ip.py`:来源 IP 提取、路由归一化与机器人判定纯函数(三种来源 + 伪造防护); - `reaction_board/web/visits.py`:访问上报先落内存缓冲,后台每分钟批量 upsert,支持单键上限与保留期清理,关闭时最后一次 flush; - 前端 `navigator.sendBeacon("/api/visit")` 埋点;Web 只读边界放宽为显式 POST 白名单(`/api/visit` + `/login`); - `config` 新增 `web.track`(`enabled`/`header_order`/`trusted_proxies`/`count_bots`/`flush_seconds`/`keep_days`/`max_hits_per_ip_path_per_day`); - 系统状态页展示近 30 天 PV/UV 与 Top 页面(`GET /api/stats`,需登录,合并未落库缓冲); - `deploy/nginx/` 提供直连 EIP / Cloudflare / EdgeOne 的 realip 与站点配置、网段刷新脚本和说明; - 自动化测试新增 `tests/test_client_ip.py`、`tests/test_visits.py`,并扩展 `test_config.py`、`test_web_queries.py`、`test_web_server.py`、`test_migrations.py` 等。 ### P4:可选部署 - 反向代理、TLS、远程访问鉴权; - 仓库已提供 `Dockerfile` / `compose.example.yml` / `config.docker.example.jsonc` / `secrets.example.json`;容器内执行 `python app.py --config /config/config.jsonc`,挂载 `/config` 与 `/data`; - Docker 不作为本机开发、联调或日常运行的前置条件。 --- ## 13. 明确非目标 第一期不实现: - 多用户、注册、角色权限; - 自动创建收藏夹、自动加入收藏夹或容量满后自动换夹; - AI 自动判定 reaction; - UP 白名单; - 视频历史统计回填; - Redis、消息队列、微服务、云数据库; - Node.js 前端工程; - Docker 作为本地开发依赖。