移植自 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 带网关原话 / 正常返回)。
6.3 KiB
6.3 KiB
声音 / 口型 完整方案(v1 草案)
建立:2026-10-03 · 状态:待用户最终确认后实施 关联:
composer/dubbing.py(配音引擎·已上线)、composer/lipsync.py(待改造为可灵版)
一、目标
让成片的声音完整、口型对得上、节奏跟得上:
- 主角出镜说台词的镜头 → 口型同步
- 旁白 / 空镜 → 正常配音(不做口型)
- 同一角色音色全片一致(不管画面怎么变)
- 台词时长决定镜头时长(画面跟着声音走)
二、用户已确认的决策
| # | 决策 | 说明 |
|---|---|---|
| 1 | 只对有台词的镜头做口型 | 成本最低;其余镜头不做 |
| 2 | 音色必须一致 | 一个角色 = 一个固定 voice_id;音色库管理 |
| 3 | 音频先行 | 先 TTS 得到真实音频时长 → 按它定镜头时长 → 再生成画面 |
| 4 | 旁白标记 = 混合(c) | 默认自动判定(无出场人物=旁白),分镜卡片可手动改 |
| 5 | 一句话拆两镜 | 按语义停顿切分(先自动分,需要时手动调),每段独立 TTS + 对口型 |
| 6 | 对口型失败兜底 | 用原片段(不影响出片) |
| 7 | 成本测试 | 改用 20 秒测试片:1 个空镜(素材库免费素材)+ 2 个 5 秒角色说话镜头(做口型)——"该省省,该花花" |
| 8 | 双人同框对策 = 参数 | 正反打/过肩镜头做成本可控制的参数(默认关,未来有能力再启用,不影响创作) |
二·五、待补充的基础能力(用户要求:补在基础层,其他流程可复用)
原则:补在基础能力层(
capabilities//composer/),而非某个流程专属实现 —— 这样分镜、口播、混剪等流程都能复用。
| # | 能力 | 用途 | 现状 |
|---|---|---|---|
| B1 | 首尾帧衔接 | 镜头间无缝续接 / 镜头内首尾帧控制 | 底层已具备(可灵 image_tail),上层未接通 |
| B2 | 多参考图 | 多角色同框各保人脸 | ❌ 可灵不支持;待专业方案(ComfyUI 多参考 / IP-Adapter) |
| B3 | 微表情 + 口型核对 | 生成后校验表情/口型是否符合预期(质量门) | ❌ 无(可结合视觉模型做检测) |
三、技术方案
3.1 声音分四轨
一条片子的声音 = 旁白轨 + 角色人声轨 + 环境音轨 + BGM轨
3.2 镜头按声音需求分三类
| 类型 | 特征 | 画面 | 声音 |
|---|---|---|---|
| A 角色说话 | 有台词 + 有出场人物 | 图生视频(绑角色,保人脸) | 角色音色 TTS + 对口型 |
| B 旁白解说 | 有台词、无出场人物(或手动标旁白) | 空镜 / 场景 | 旁白音色 TTS,不做口型 |
| C 无台词 | 空镜 / 氛围 | 场景 | 环境音铺底(或静音) |
3.3 工作流(音频先行)
① 分镜定稿(每镜:台词 + 出场人物 + 旁白标记)
↓
② 【音频先行】逐镜 TTS
· A 类 → 该角色音色(voice_id 固定)
· B 类 → 旁白音色
· C 类 → 无
→ 得到每段**真实音频时长**
↓
③ 【画面跟随】按音频时长定镜头时长 → 生成画面
· 绑角色 → 图生视频(保人脸一致性)← 2026-10-03 已修复
· 未绑 → 文生视频
↓
④ 【对口型】仅 A 类镜头:
镜头视频 + 台词音频 → 可灵 lip-sync → 替换片段(失败则用原片段)
↓
⑤ 【混流】人声轨 + 环境音 + BGM + 字幕
3.4 音色一致性机制
- 一个角色 = 一个固定 voice_id(演员表
CastMember.voice_id绑定) - 未指定 → 按角色代号 hash 从路人池稳定分配(同代号永远同音色)
- CosyVoice 同一 voice_id 合成结果音色确定(仅语气/语速随文本微变)
- 旁白与角色音色可以不同(解说员 vs 画面人物,观众接受且更自然)
3.5 可灵 lip-sync 接口(已实测探明,2026-10-03)
POST /v1/videos/lip-sync
{
"input": {
"mode": "audio2video", // 或 text2video(需 voice_language)
"video_url": "..." | "video_id": "...", // 二选一(video_id 可用我们生成的镜头)
"audio_type": "url",
"audio_url": "..."
}
}
GET /v1/videos/lip-sync ← 任务列表查询
✅ 用现有 kling key 即可,不需要 fal.ai(现有 lipsync.py 是 fal.ai 版 → 改造为可灵版)
⚠️ 单价未知(文档站为 SPA 抓不到)→ 按用户决策:先跑 1 个 5 秒镜头实测
四、实施步骤(建议顺序)
| 阶段 | 内容 | 依赖 |
|---|---|---|
| P1 | 音频先行:出片流程加「先 TTS → 按音频时长定镜头时长」(音频存盘供混流复用,不重复 TTS) | 无 |
| P2 | 演员表音色绑定 + 旁白标记(前端 UI + schema) | 无 |
| P3 | lipsync.py 改造为可灵版 + 接入出片流程(仅 A 类镜头,失败用原片段) |
P1 |
| P4 | 20 秒测试片:1 空镜(免费素材)+ 2 个 5 秒角色说话 → 实测 lip-sync 单价 → 用户决定是否全量 | P3 |
| P5 | 首尾帧衔接(接通 image_tail)→ 补进基础能力层(B1) |
独立 |
| P6 | 双人同框 → 正反打参数(默认关,用户可控;未来 B2 到位后可启用) | 独立 |
| B 系列 | 基础能力补充:B1 首尾帧 / B2 多参考 / B3 微表情口型核对(补在基础层,全流程复用) | 见二·五 |
五、成本估算(粗略,待实测)
| 项 | 单价 | 说明 |
|---|---|---|
| TTS(CosyVoice) | 极低(按字数) | 几十字台词 ≈ 几分钱 |
| 可灵 lip-sync | 待实测 | 参考 fal.ai 版注释 ¥0.1/秒 |
| 粗估 | 每部短片 2 |
月产 20 分钟 ≈ 十数量级 |
六、已知局限(诚实标注)
- 多角色同框:图生视频只能喂一张参考图 → 只保主角(替代:正反打 / 合成参考图 / 等专业能力)
- 跨镜头口型连贯:一句话拆两镜时,语气可能有轻微断裂(缓解:切分点落在语义停顿)
- 已生成素材:本方案只对新生成生效(旧镜头需重新出片)
七、待确认
- 用户确认"按默认"(第 2/3/4 点)→ 即可开工 P1
- 优先级:A 声音链先做(建议),B 一致性增强随后