feat(monitor): 抖音 Web 接口客户端 —— 绕开爬虫子进程,直接发 HTTP
移植自 mac-agent-os 的 mediacrawler_adapter:不起子进程、不开页面,用浏览器里那份
登录态直接调抖音 Web 接口。产物键名照抄 store/douyin,所以 ingest 那条链路一个字不用改。
**目前能用的(真环境实测,非推断)**:
profile/other : 200, 7075 字节 —— 博主主页指标(粉丝/获赞/作品数/昵称)
aweme/detail : 200, 45425 字节 —— 单条作品详情(含点赞/评论/收藏/分享)
**目前不能用的:作品列表 `aweme/post`。** 两个互相独立的原因:
1. 这个接口被抖音单独升级成了真校验:不带 x-tt-argus 回 403「Uifid Not Found」,
带上 dummy 值回 200 + **空 body**。也就是说「头在不在」骗得过,「真校验」过不了。
同一套头打 profile/other 和 aweme/detail 都是通的 —— 抖音是挑着接口加保护的,
挑中的恰好是「批量拉作品列表」这个最敏感的动作。
2. 改走页面截获也不行:CDP 浏览器打开博主主页会落到「验证码中间页」(当天大量探测的
代价,过几小时要重测)。
所以现在的边界是:**已知作品的指标刷新能做,自动发现新作品做不了**。
**排查中控住变量后得到的两条事实**(都写进注释了):
· `Accept` / `Accept-Language` / `Referer` 才是主页接口能返回真数据的原因 —— 只有
UA+client hints+Cookie 时是 200 但仅 121 字节的空壳,补上这三个头变 7074 字节。
(我先前猜的 sec-ch-ua 不是关键。)
· 因此 UA 与 client hints 必须**成套地取自同一个浏览器**,所以 BrowserIdentity 一次
从 CDP 取齐 cookie + UA + hints,而不是各自写死。
「200 + 空 body 必须当场报错」也是刻意写死的:放过去它会在下游变成「这个博主没作品」,
把一次失败伪装成一条正常结果 —— 爬虫那条路正是这么栽的,还被翻译成「账号被封」。
测试 +11:cookie 解析、请求头成套性(含 uifid 缺失/回退)、产物键名与 store 对齐、
以及 _get 的三条失败路径(空 body / 403 带网关原话 / 正常返回)。
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
# 导演工作室 · 重新设计方案
|
||||
|
||||
> 2026-10-10 · 基于**实测盘查**(非推测)
|
||||
> 目标:止住"越改越乱",把现有能力重新收进一个**边界清晰**的结构里
|
||||
|
||||
---
|
||||
|
||||
## 一、现状诊断(全部实测数据)
|
||||
|
||||
### 1.1 规模
|
||||
|
||||
| 项 | 数量 | 说明 |
|
||||
|---|---|---|
|
||||
| 前端视图 | **85 个** | 视频工厂相关 44 个 |
|
||||
| 后端路由 | **7 文件 / 145 端点 / 7206 行** | ave(33) · ave_project(30) · ave_keyframe(25) · ave_assets(33) · ave_scene(5) · ave_creatives(16) · ave_docs(3) |
|
||||
| `ave-director.js` | **112 KB** | 单文件注册 **26 个 `window._dir*` 函数** |
|
||||
| 项目对象顶层键 | **19 个** | 其中 3 个是从没写过的死键 |
|
||||
| 运行目录 | **15 个** | 含 5 个测试残留 |
|
||||
|
||||
### 1.2 五类具体问题(都对齐到了文件)
|
||||
|
||||
**① 空壳页面 4 个**(内容是"功能开发中,敬请期待")
|
||||
```
|
||||
ave-script.js 409 B 脚本生成
|
||||
ave-render.js 409 B 渲染任务
|
||||
ave-templates.js 406 B 模板
|
||||
ave-materials.js 409 B 素材库
|
||||
```
|
||||
|
||||
**② 新旧两套流程并存 —— 6 个老流程页在菜单里,但跑不通**
|
||||
```
|
||||
ave-flow-short-drama.js 744 B ← 菜单「🎭 短剧创作」
|
||||
ave-flow-dub.js 647 B ← 菜单「🎙️ 基础口播」
|
||||
ave-flow-beat.js 579 B ← 菜单「🥁 卡点视频」
|
||||
ave-flow-narrative.js 668 B ← 菜单「📖 角色叙事」
|
||||
ave-flow-digital-human.js 611 B ← 菜单「👤 数字人口播」
|
||||
ave-flow-hybrid.js 564 B ← 菜单「🎞️ 口播+混剪」
|
||||
```
|
||||
它们自己的注释写着:**"TTS / 素材 / BGM / 字幕的执行实现仍在接入中(运行时会明确跳过)"**
|
||||
→ 而工作室那条链路**是通的** → 两套并存,用户不知道点哪个
|
||||
|
||||
**③ 工作室内部职责重叠(用户已经混了)**
|
||||
```
|
||||
🌍 设定 vs 📋 创意卡 —— 都在"想清楚这个故事"
|
||||
🖼 关键帧设计 vs 🖼 关键帧 —— 用户明确问过"这两个有什么区别"
|
||||
```
|
||||
实测:用户上次说的"关键帧工作台里面是空的",我查了两轮才确认他指的是哪个页面。
|
||||
|
||||
**④ 单文件职责超载**
|
||||
`ave-director.js`(112 KB)一个文件里塞了:
|
||||
```
|
||||
<!-- ② 全局设置 --> 导演系统 / 风格 / 演员表 / 场景 / seed
|
||||
<!-- 📐 全片设定 --> 空间/角色/镜头语言(原故事板并入)
|
||||
<!-- ③ 按场次生成 --> 段落列表 + 场记板 + 生成
|
||||
<!-- ④ 分镜表 --> 镜头卡片编辑
|
||||
<!-- 出片进度 --> 渲染任务轮询
|
||||
```
|
||||
|
||||
**⑤ 数据源分散 + 死键**
|
||||
```
|
||||
projects/{pid}/project.json ← 项目对象(19 键,其中 3 键为空)
|
||||
storyboards/{pid}.json ← 分镜(不在 project.json)
|
||||
keyframes/{pid}/{sid}/ ← 关键帧图
|
||||
keyframe_videos/{pid}/{sid}/ ← 视频片段
|
||||
```
|
||||
`project.json` 里的 `keyframes` / `videos` / `assets` **三个键从没被写过**(空)。
|
||||
实测 `_enrich()` 每次读项目时**临时合并**前三个 → 说明它们本该在项目对象里。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因(为什么会"越改越乱")
|
||||
|
||||
三条,都不是技术问题:
|
||||
|
||||
1. **加功能时"就地扩"** —— 没有"这一层该管什么"的边界。分镜页越塞越多(演员表、锚点、全片设定、出片设置),因为"顺手就放这了"。
|
||||
2. **新旧并存不删** —— 怕删坏,就留着旧的。结果路由/菜单/文档里同时存在两套,用户选错就在所难免。
|
||||
3. **数据"能跑就行"** —— 写在哪方便就写哪。分镜写 `storyboards/`、关键帧写 `keyframes/`,导致"上游带入"读不到(这是之前修过的真 bug)。
|
||||
|
||||
---
|
||||
|
||||
## 三、新架构
|
||||
|
||||
### 3.1 三条主线(先分清楚"这是哪一类东西")
|
||||
|
||||
```
|
||||
📦 原料 🎬 生产 📊 交付
|
||||
(可复用资产) (做一条片子) (看结果)
|
||||
角色/场景/道具 导演工作室 生产记录
|
||||
音色/BGM/音效 (唯一主流程) 费用统计
|
||||
产品/服装/素材 产物中心
|
||||
```
|
||||
|
||||
**判断规则**:
|
||||
- 能被**多条片子复用** → 进「原料」
|
||||
- 属于**某一条片子**的 → 进「生产」
|
||||
- 是**结果/统计** → 进「交付」
|
||||
|
||||
### 3.2 生产主线:工作室 9 阶段 → **7 阶段**
|
||||
|
||||
**合并的关键点**:`🖼 关键帧设计` + `🖼 关键帧` → **一个页面**(左设计右图)
|
||||
|
||||
```
|
||||
① 💡 创意 ┐
|
||||
② 🌍 设定 ├→ ③ 📋 创意卡 → ④ 📝 剧本 → ⑤ 🎬 分镜 → ⑥ 🖼 关键帧 → ⑦ 🎞 视频 → ⑧ 🎙 后期
|
||||
┘
|
||||
(素材层) (汇聚层) (执行层)
|
||||
```
|
||||
|
||||
**为什么保留 ①②(不合并)**:
|
||||
- `💡 创意` = **这个故事的种子**(候选 → 选一句)
|
||||
- `🌍 设定` = **世界的规则**(可跨故事复用,比如"肛肠科病房"这个世界的规则)
|
||||
- `📋 创意卡` = **两者合起来 → 这个故事的具体构想**
|
||||
- 职责不同,但**界面上必须写明各自管什么**(现在的毛病是没写)
|
||||
|
||||
**为什么合并 ⑥**:
|
||||
- 用户已经混了。而且"设计"(决定拆几张)和"生成"(出图)本来就是**同一步的两半** —— 设计完立刻要看图对不对。
|
||||
|
||||
### 3.3 每阶段一张「职责卡」(界面顶部固定显示)
|
||||
|
||||
这是**治乱的核心**:每个页面顶部明确写三行 —— **我从哪来 / 我产出什么 / 下一步用它做什么**。
|
||||
|
||||
| # | 阶段 | 输入(从哪来) | 产出 | 下一步用它做什么 |
|
||||
|---|---|---|---|---|
|
||||
| ① | 💡 创意 | 参数选型 | 选中的 1 句话 | 扩写成创意卡 |
|
||||
| ② | 🌍 设定 | — | 世界规则(可复用) | 创意卡 + 剧本的世界观依据 |
|
||||
| ③ | 📋 创意卡 | 创意 + 设定 | 故事卡(12 字段) | 剧本的**最高依据** |
|
||||
| ④ | 📝 剧本 | 创意卡 | 按**段落**的剧本 | 分镜按段落做 |
|
||||
| ⑤ | 🎬 分镜 | 剧本段落 + 导演/风格/演员 | 镜头(Shot) | 关键帧按镜头做 |
|
||||
| ⑥ | 🖼 关键帧 | 镜头 + 角色/场景资产 | 帧设计(A/B/C)+ 帧图 | 视频的首/尾帧 |
|
||||
| ⑦ | 🎞 视频 | 关键帧(选首尾)+ 引擎 | 视频片段(可多段) | 后期拼接 |
|
||||
| ⑧ | 🎙 后期 | 采用的片段 | 成片(拼接/字幕/混音) | 交付 |
|
||||
|
||||
### 3.4 菜单重排
|
||||
|
||||
**现状 → 目标**
|
||||
|
||||
```
|
||||
🎬 视频工厂 · 资源库(16 项) → 📦 原料(12 项)
|
||||
摘掉:素材库(空壳)
|
||||
保留:创意库 / 角色库 / 角色生成器 / 场景生成器 / 场景库 /
|
||||
真实场地 / 道具库 / 产品库 / 服装库 / 音色库 / BGM / 音效
|
||||
说明:环境音库、特效预设 暂藏在「更多」里(当前为空,用不上)
|
||||
|
||||
🎬 视频工厂 · 流程(12 项) → 🎬 生产(3 项)
|
||||
★ 导演工作室(唯一主流程)
|
||||
🔄 人物置换(独立工具,不属主流程)
|
||||
🔀 流程画布(高级功能,折叠在「高级」下)
|
||||
隐藏:6 个 ave-flow-*(老流程,跑不通)+ 新建流程(与流程画布重复)
|
||||
|
||||
🎬 视频工厂 · 能力中心(4 项) → 🧩 配置(4 项,不变)
|
||||
|
||||
🎬 视频工厂 · 运营(2+ 项) → 📊 交付(3 项)
|
||||
生产记录 / 费用统计 / 产物中心
|
||||
```
|
||||
|
||||
**净减**:`16+12+4+2 = 34 项` → `12+3+4+3 = 22 项`(-35%)
|
||||
|
||||
### 3.5 拆掉 112 KB 的巨物
|
||||
|
||||
`ave-director.js` 拆成 3 个视图(各管一件事):
|
||||
|
||||
```
|
||||
views/studio-shots-config.js ← 导演系统 / 风格 / 演员表 / 场景 / 锚点(配置)
|
||||
views/studio-shots-generate.js ← 段落列表(场记板)/ 生成 / 全片设定(动作)
|
||||
views/studio-shots-table.js ← 分镜表(结果,可编辑)
|
||||
```
|
||||
用户在一个页面里**从上到下**走,但代码是三块,各自可独立改。
|
||||
|
||||
---
|
||||
|
||||
## 四、数据模型(单一真相源)
|
||||
|
||||
### 4.1 原则
|
||||
> **一条片子的全部数据,只在一个文件里。** 媒体文件(图/视频/音频)仍各自落盘,但**路径登记在项目对象里**。
|
||||
|
||||
### 4.2 目标结构
|
||||
|
||||
**⚠️ 权衡说明(我先自我修正)**:
|
||||
我最初想把 `shots`(含关键帧和视频的全部版本记录)塞进 `project.json`。
|
||||
**但那样会让它膨胀到几百 KB** —— 20 个镜头 × 每镜 3 帧 × 每帧多版本,
|
||||
每次改一个字都要重写整个文件,性能和并发都会出问题。
|
||||
|
||||
**所以改成:单一真相源 = 单一目录,而不是单一文件。**
|
||||
|
||||
```
|
||||
projects/{pid}/ ← ★ 一个项目 = 一个目录(这就是"真相源")
|
||||
│
|
||||
├ project.json ← 轻量(< 20 KB):元数据 + 各阶段摘要
|
||||
│ { "project_id","title","stage","created_at","updated_at",
|
||||
│ "creative":{...}, // 💡 创意(选中的候选)
|
||||
│ "setting":{...}, // 🌍 设定
|
||||
│ "creative_card":{...}, // 📋 创意卡
|
||||
│ "script":{...}, // 📝 剧本(段落)
|
||||
│ "film_settings":{...}, // 📐 全片设定
|
||||
│ "post":{...}, // 🎙 后期(时间线 + 成片路径)
|
||||
│ "cast":[...], // 演员表
|
||||
│ "history":[...] } // 变更记录
|
||||
│
|
||||
├ shots.json ← 重数据:分镜 + 关键帧 + 视频(可单独读写)
|
||||
│ [ { "shot_id","paragraph_no","visual","duration","cast_slots", ...,
|
||||
│ "keyframes": { "plan":{A/B/C 提示词},
|
||||
│ "frames":[{seq,role,versions:[{v,file,adopted}]}] },
|
||||
│ "videos": [ {segment, versions:[{v,file,adopted,has_voice}]} ] } ]
|
||||
│
|
||||
└ media/ ← 媒体文件(或继续用现目录,只登记路径)
|
||||
keyframes/shot_01/v1.png
|
||||
videos/shot_01/s1_v1.mp4
|
||||
```
|
||||
|
||||
**为什么这样更好**:
|
||||
- `project.json` 保持轻量 → 改设定/剧本不会牵动分镜
|
||||
- `shots.json` 单独读写 → 生成一镜只改这个文件
|
||||
- **但对外只有"一个项目目录"** → 备份/复制/迁移 = 复制目录
|
||||
|
||||
**取消**:
|
||||
- `project.json` 里的 `keyframes` / `videos` / `assets` 三个死键 → 删除
|
||||
- `storyboards/{pid}.json` **作为独立位置** → 并入 `projects/{pid}/shots.json`
|
||||
(同级目录,不再是"另一个地方")
|
||||
- 前端不再需要 `_enrich()` 跨目录临时合并
|
||||
|
||||
### 4.3 收益
|
||||
- 「上游带入」天然正确(同一目录,不用跨目录捞)
|
||||
- 「这个项目的全部产物」一个请求拿到
|
||||
- 备份/迁移/复制项目 = 复制一个目录
|
||||
- 删项目 = 删一个目录(现在要删 4 个地方)
|
||||
|
||||
---
|
||||
|
||||
## 五、迁移路径(现有内容怎么落位)
|
||||
|
||||
**原则:不删数据,只搬家。每一步都可回滚(先备份)。**
|
||||
|
||||
| 现有 | 去向 |
|
||||
|---|---|
|
||||
| `storyboards/{pid}.json` 的 shots | 搬进 `project.json` 的 `shots[]` |
|
||||
| `keyframes/{pid}/{sid}/*.png` | 文件不动;路径登记到 `shots[i].keyframes` |
|
||||
| `keyframe_videos/{pid}/{sid}/*.mp4` | 文件不动;路径登记到 `shots[i].videos` |
|
||||
| `storyboard_paper` | 已是 `film_settings` 的来源(已迁移)→ 删键 |
|
||||
| 6 个 `ave-flow-*` 视图 | **文件保留**(不删仓库文件),仅从菜单摘掉 |
|
||||
| 4 个空壳视图 | 同上,从菜单摘掉(它们本来也不在菜单里) |
|
||||
| 运行目录里的测试残留 | 移到 `_trash/` |
|
||||
|
||||
---
|
||||
|
||||
## 六、实施顺序(4 批,每批独立可验证)
|
||||
|
||||
**批 1 · 止血(零风险,先让界面不乱)**
|
||||
- 菜单重排(摘掉老流程 + 空壳)
|
||||
- 每个阶段页顶部加「职责卡」(我产出什么 / 下一步用它做什么)
|
||||
- 合并 `🖼 关键帧设计` + `🖼 关键帧` 为一个页面
|
||||
|
||||
**批 2 · 数据归一(动后端,但只加不改)**
|
||||
- `shots` 搬进 `project.json`(写入时双写,读取时优先新位置)
|
||||
- 关键帧/视频的路径登记进 `shots[i]`
|
||||
- 前端改读新位置(保留旧路径兜底)
|
||||
|
||||
**批 3 · 拆巨物**
|
||||
- `ave-director.js` 拆 3 块(配置 / 生成 / 表)
|
||||
- 每块独立测试
|
||||
|
||||
**批 4 · 清理**
|
||||
- 删死键、废弃 `_enrich()`、测试残留移 `_trash/`
|
||||
- 更新文档
|
||||
|
||||
---
|
||||
|
||||
## 七、待你确认的 5 件事
|
||||
|
||||
1. **要不要合并「关键帧设计」和「关键帧」?**(我倾向合并 —— 你已经混过一次)
|
||||
2. **`ave-flow-*` 那 6 个老流程页,从菜单摘掉可以吗?**(文件保留)
|
||||
3. **「环境音库」当前是空的 —— 先藏起来,还是保留在菜单里?**
|
||||
4. **「流程画布」(`workflow`) 你还用吗?**(它是另一套编排方式,功能与工作室重叠)
|
||||
5. **批 1 的菜单重排,要不要现在就做?**(零风险,改完立刻清爽)
|
||||
|
||||
---
|
||||
|
||||
## 附:本次盘查用到的命令(可复现)
|
||||
|
||||
```bash
|
||||
# 视图规模
|
||||
ls -la frontend/src/views/*.js | awk '{print $5, $NF}' | sort -rn
|
||||
# 空壳识别
|
||||
for f in views/*.js; do [ $(wc -c < $f) -lt 1000 ] && echo "$f"; done
|
||||
# 后端端点分布
|
||||
for f in routes/ave*.py; do echo "$(basename $f) $(grep -cE '^@router\.' $f)"; done
|
||||
# 项目对象结构
|
||||
python3 -c "import json;d=json.load(open('.../project.json'));print(list(d.keys()))"
|
||||
```
|
||||
Reference in New Issue
Block a user