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,146 @@
|
||||
# 声音 / 口型 完整方案(v1 草案)
|
||||
|
||||
> 建立:2026-10-03 · 状态:**待用户最终确认后实施**
|
||||
> 关联:`composer/dubbing.py`(配音引擎·已上线)、`composer/lipsync.py`(待改造为可灵版)
|
||||
|
||||
---
|
||||
|
||||
## 一、目标
|
||||
|
||||
让成片的**声音完整、口型对得上、节奏跟得上**:
|
||||
|
||||
1. 主角出镜说台词的镜头 → **口型同步**
|
||||
2. 旁白 / 空镜 → 正常配音(不做口型)
|
||||
3. 同一角色**音色全片一致**(不管画面怎么变)
|
||||
4. 台词时长决定镜头时长(**画面跟着声音走**)
|
||||
|
||||
---
|
||||
|
||||
## 二、用户已确认的决策
|
||||
|
||||
| # | 决策 | 说明 |
|
||||
|---|---|---|
|
||||
| 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~3 个台词镜头 × 5 秒 ≈ **¥1~2/部** | 月产 20 分钟 ≈ 十数量级 |
|
||||
|
||||
---
|
||||
|
||||
## 六、已知局限(诚实标注)
|
||||
|
||||
1. **多角色同框**:图生视频只能喂一张参考图 → 只保主角(替代:正反打 / 合成参考图 / 等专业能力)
|
||||
2. **跨镜头口型连贯**:一句话拆两镜时,语气可能有轻微断裂(缓解:切分点落在语义停顿)
|
||||
3. **已生成素材**:本方案只对新生成生效(旧镜头需重新出片)
|
||||
|
||||
---
|
||||
|
||||
## 七、待确认
|
||||
|
||||
- [ ] 用户确认"按默认"(第 2/3/4 点)→ 即可开工 P1
|
||||
- [ ] 优先级:**A 声音链先做**(建议),B 一致性增强随后
|
||||
Reference in New Issue
Block a user