Files
MediaCrawler/mac-agent-os-main/PLANS/STUDIO_REDESIGN.md
T
butubb 2112c1a870
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
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 带网关原话 / 正常返回)。
2026-10-10 17:12:25 +08:00

13 KiB
Raw Blame History

导演工作室 · 重新设计方案

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 的菜单重排,要不要现在就做?(零风险,改完立刻清爽)

附:本次盘查用到的命令(可复现)

# 视图规模
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()))"