在上游 MediaCrawler 之上新增一层: - 监控层 api/monitor/ —— 多博主/多笔记的定时采集、指标快照差分、报表、 企业微信通知。每轮采集写入独立目录,差分才成立。 - WebUI 登录鉴权 api/auth.py —— PBKDF2 口令 + 服务端会话,/api 全接口防护。 WebSocket 单独加依赖:BaseHTTPMiddleware 对 ws 作用域直接放行,覆盖不到。 - 全局平台切换 + 能力矩阵 —— 如实区分「爬虫模块支持」与「监控层已接线」, 未接通的平台直接拒绝建任务,而不是静默跑空。 - 监控库改用 MySQL 5.7(可回退 SQLite 供测试):逐表强制 utf8mb4 (服务端与库默认都是 latin1),启动校验所连 schema 以防写错库, 连接池 recycle + pre_ping 应对 MySQL 的 8 小时空闲断连。 修复上游缺陷: - xhs/core.py: 主页抓取失败会跳掉整个博主,导致一条作品都抓不到, 而那份资料只喂给一个空函数。改为尽力而为,失败不中断。 - xhs/login.py: cookie 登录只注入 web_session,冷启动签名会失败。 新增 INJECT_ALL_COOKIES 开关(默认关闭,原有行为不变)。 - requirements.txt: 补上 websockets。它在上游 pyproject.toml 里有声明、 这里漏了,导致 uvicorn 没有 WebSocket 能力,实时日志流从未工作。 改动过的上游文件清单及合并方式见 UPSTREAM.md。 测试:492 passed(另有 1 个既有的 Windows/gbk 上游测试失败,与本改动无关)
130 lines
5.8 KiB
Markdown
130 lines
5.8 KiB
Markdown
# 与上游的差异管理
|
||
|
||
本仓库在 [NanmiCoder/MediaCrawler](https://github.com/NanmiCoder/MediaCrawler) 之上加了一层
|
||
监控/鉴权/多平台面板。这份文档记录**改了上游哪些文件、为什么**,以及**上游更新时怎么合并**。
|
||
|
||
---
|
||
|
||
## 一、改动分三类
|
||
|
||
冲突风险从低到高:
|
||
|
||
### 1. 纯新增文件(零冲突)
|
||
|
||
上游怎么改都不会碰到它们:
|
||
|
||
```
|
||
api/auth.py WebUI 登录鉴权
|
||
api/monitor/* 监控层整体(含 platforms.py 能力矩阵)
|
||
api/monitor/db.py MySQL 连接层(可回退 SQLite 供测试用)
|
||
api/monitor/migrate_from_sqlite.py SQLite → MySQL 一次性迁移脚本
|
||
api/routers/{auth,monitor,settings}.py
|
||
api/schemas/{auth,monitor,settings}.py
|
||
api/services/interpreter.py 解释器探测(uv / .venv / 当前解释器)
|
||
webui/src/components/{monitor,settings,auth}/ 新视图
|
||
webui/src/components/layout/{PlatformSwitcher,UnwiredPlatformNotice}.tsx
|
||
webui/src/{hooks/useMonitor.ts,hooks/usePlatform.ts,store/platformStore.ts,lib/monitorFormat.ts,types/monitor.ts}
|
||
docs/监控功能使用说明.md
|
||
tests/test_{auth,settings,platforms,monitor_*}.py
|
||
```
|
||
|
||
### 2. 加法改动(低冲突)
|
||
|
||
只在既有文件里**新增**内容,不改动原有行:
|
||
|
||
| 文件 | 加了什么 |
|
||
|---|---|
|
||
| `cmd_arg/arg.py` | typer 选项:`--enable_cdp_mode`、`--inject_all_cookies`、`--save_login_state`、`--cookies_file`、`--crawler_max_sleep_sec`,以及对应的 `config.*` 回写 |
|
||
| `api/schemas/crawler.py` | `CrawlerStartRequest` 的若干**可选**字段(默认 `None`,不传则不加对应 CLI 参数) |
|
||
| `config/base_config.py` | `INJECT_ALL_COOKIES = False` |
|
||
| `api/routers/__init__.py` | 导出新增的 router |
|
||
| `requirements.txt` | 补上 `websockets`(上游 `pyproject.toml` 里有、`requirements.txt` 里漏了) |
|
||
| `tests/conftest.py` | 新增 `_bypass_auth_for_non_auth_suites` fixture |
|
||
|
||
### 3. 接线改动(中冲突,需要人看)
|
||
|
||
| 文件 | 改了什么 | 上游若在此处变动 |
|
||
|---|---|---|
|
||
| `api/main.py` | 注册 4 个 router 并加 `Depends(require_auth)`;`lifespan` 里初始化监控库、启动调度器、跑设置键迁移;`load_dotenv`;CORS 可配;`docs/redoc/openapi` 关闭;监听地址改 env | **最需要人工合并的文件**。留意 router 注册块、lifespan、`__main__` |
|
||
| `api/routers/websocket.py` | 两个 WS 路由加 `dependencies=[Depends(require_ws_auth)]` | 上游若新增 WS 路由,**必须同样加上**,否则那条流是裸奔的 |
|
||
| `api/services/crawler_manager.py` | 解释器探测替换硬编码 `uv run`;`_build_command` 转发新增参数;新增 `is_busy()` / `run_and_wait()` 与完成事件 | 留意 `_build_command` 的参数拼装 |
|
||
| `media_platform/xhs/login.py` | `login_by_cookies` 在 `INJECT_ALL_COOKIES` 打开时注入**全部** cookie(默认关闭,行为不变) | 小改动,好合并 |
|
||
|
||
### 4. 上游 bug 修复(建议回馈上游)
|
||
|
||
| 文件 | 修的问题 |
|
||
|---|---|
|
||
| `media_platform/xhs/core.py` | 见下节 |
|
||
| `media_platform/xhs/login.py` | 同上(cookie 加固) |
|
||
|
||
---
|
||
|
||
## 二、应该给上游提 PR 的两个修复
|
||
|
||
这两处是**上游自身的缺陷**,提上去以后就不用自己背着:
|
||
|
||
### 1. 博主主页抓取失败会跳掉整个博主(`xhs/core.py`)
|
||
|
||
`get_creator_info()` 抓主页 HTML 解析 `window.__INITIAL_STATE__`,解析失败抛 `JSONDecodeError`——
|
||
它是 `ValueError` 的子类,被 `except ValueError` 误捕获,日志报成
|
||
"Failed to parse creator URL"(**误导**,URL 根本没解析错),然后 `continue` **跳过整个博主**。
|
||
|
||
而那份资料只喂给 `save_creator()`,它在教学版里是**空函数**。也就是说:
|
||
一个喂给空函数的抓取失败,让真正要抓的作品一条都没抓到,表现为"0 篇作品",
|
||
和"登录失效"长得一模一样。
|
||
|
||
修复:把资料抓取改成**尽力而为**,失败只警告、继续抓作品。
|
||
|
||
### 2. cookie 登录只注入 `web_session`(`xhs/login.py`)
|
||
|
||
`a1` / `webId` 等签名所需 cookie 只能靠持久化 profile 补,冷启动时签名会失败。
|
||
默认行为保持不变,用 `INJECT_ALL_COOKIES` 开关控制。
|
||
|
||
---
|
||
|
||
## 三、上游更新时怎么操作
|
||
|
||
### 日常流程
|
||
|
||
```bash
|
||
git stash # 或先 commit 到自己的分支(推荐)
|
||
git fetch origin main
|
||
git rebase origin/main # 冲突只会出现在上表第 3、4 类文件里
|
||
./.venv/Scripts/python.exe -m pytest tests/ -q # 486 个测试就是回归网
|
||
```
|
||
|
||
### 强烈建议:先把改动提交掉
|
||
|
||
当前状态是**未提交**的(25 个上游文件被改 + 31 个新文件)。在 `main` 分支上裸着工作区,
|
||
一次 `git checkout .` 就全没了,而且没法 rebase。
|
||
|
||
```bash
|
||
git checkout -b local/monitor-panel
|
||
git add -A && git commit -m "监控面板 / 鉴权 / 多平台"
|
||
```
|
||
|
||
### 如果改动持续增长:fork
|
||
|
||
把本仓库 fork 到自己名下,上游设为 remote:
|
||
|
||
```bash
|
||
git remote rename origin upstream
|
||
git remote add origin <你的 fork>
|
||
git push -u origin local/monitor-panel
|
||
```
|
||
|
||
之后同步上游用 `git fetch upstream && git rebase upstream/main`。
|
||
|
||
---
|
||
|
||
## 四、合并时最容易忘的三件事
|
||
|
||
1. **新增的 `/api` 路由必须带鉴权**。跑一下 `tests/test_auth.py`——
|
||
里面有个测试会遍历 `app.routes`,断言除豁免集外每个 `/api` 路由无凭据都返回 401。
|
||
上游新增接口忘了加鉴权,这个测试会直接失败。
|
||
2. **新增的 WebSocket 路由必须加 `require_ws_auth`**。
|
||
`BaseHTTPMiddleware` 对 WS 完全不生效(`scope["type"] != "http"` 直接放行),
|
||
只靠中间件会漏。同样有测试守着。
|
||
3. **上游若改动 `AsyncFileWriter` 的输出路径规则**,`api/monitor/ingest.py::find_run_files`
|
||
会跟着失效——它靠 glob `{out_dir}/{platform}/jsonl/*_contents_*.jsonl` 定位每轮的产物。
|