Tobatsu 2.0 · 查实更正版

kamila_voice_over 到底怎么做口播

先认错:上一版我说"查无此名"是错的——我查的是 Tobatsu 1.0 和本地仓,而它活在 Tobatsu 2.0。它是真 workflow(v5),task 34413dfd 今天刚 done。真机制是:Fish TTS 出声 → Seedance Omni 出画 → 对口型,声音不是 Seedance 也不是 ElevenLabs。

真实存在 · v5
queue=material · engine=claude
声音 = Fish TTS
固定 K 宝音色 · 俄语
画面 = Seedance Omni
any2video · 走 unified API
对齐 = ref_audio + lipsync
嘴型锚到 Fish 音频
更正说明: 我之前那版结论("kamila_voice_over 不存在 / 口播不碰 seedance")是在 错的库(tobatsu1 tobatsu-api.fpai.io + 本地 repo)上查的,作废。这版全部来自 tobatsu2 控制面的真实数据:WORKFLOW.md 源 + 真机 task 产物。

它是什么

K宝口播 / Kamila voice-over。Mai 先生成并让用户确认一张 GPT-image 首帧,把 first_frame_url 交给 Tobatsu;Tobatsu 跑 Seedance Omni 5-15 秒的对口型窗口、对口型、再拼接成片。

tobatsu2 GET /workflows/kamila_voice_over/source (v5, len 8546)

注意它跟你之前问过的 voiceover_align、combo 那套(voiceover_digital_human_pip_subtitle_mai)是不同的工作流。这条是 Seedance Omni 对口型,不是 ElevenLabs 对齐源视频,也不是 OmniHuman 数字人。

一张图:音频优先的对口型链

① 配音(声音源) Fish TTS 整轨 K宝音色 · 俄语 ② 按词时间戳 切 5–15s 窗 scribe 词级时间戳 ③ 出画 · UNIFIED API Seedance Omni any2video · ref_audio=该窗Fish MAI 已确认首帧 first_frame_url(锁背景/机位/妆造) ④ 对口型 · FAL lipsync-align 嘴型锚到 Fish 音频 ⑤ 拼接成片 concat 1080×1920 · 30fps 声音权威 = Fish 整轨;Seedance 自带音轨只用于背景/嘴型时序,交付的声音永远是 Fish
这是"音频优先"的链:先定 Fish 配音的时间线,视频窗口按它切、按它生成、按它对口型。Seedance 出的画要去贴音频,不是反过来。

seedance 对齐口播,具体五步

打包 runner 是 kamila-hook-recipe/scripts/talking_head.py,WORKFLOW.md 里把音频路径写得很死:

真机印证(task 34413dfd)

Fish 整轨 17.74s → 切 2 窗(0–11.60s、11.60–17.74s)→ 各自 win*.seedance.mp4 + win*.fish.wav + win*.aligned.mp4 → 拼成 18.09s 成片。timestamp_source = scribe_word_timestamps,两窗 lipsync_ok=true。产物里能看到 media/full_tts.wavwin0.seedance.audio.wav(Seedance 自带音,被弃用)、win0.aligned.mp4(对口型后)。

tobatsu2 GET /tasks/34413dfd/outputs + outputs/runner/result.json

用的哪个 unified seedance 模型

用的是 Seedance Omni,请求类型 any2video,走 unified 生成 API(UNIFIED_GENERATION_API_URL = unified-generation-api.adtech-videogen.workers.dev/generate)。它就是用 ref_audio_url 这条任意输入能力,把音频喂进去驱动画面。

来源omni 模型 idtype
omni-handler skill(omni 专用,rev1)ultra-seedance-2-omniany2video
unified-media-generation skill 示例清单(rev2)funpub-seedance-2-omniany2video
两份 skill 文档对 omni 的 id 写得不一致(ultra-seedance-2-omni vs funpub-seedance-2-omni)。最终发哪个,字面烧在 runner 脚本 talking_head.py 里 —— 该 .py 在 skill package 内,API 的 GET /skills 只回 SKILL.md、取不到脚本,所以这里没法 100% 断言。omni 专用的 omni-handler 明写 ultra-seedance-2-omni,以它为准的可能性更大。要钉死就得读 package 里的 .py。

tobatsu2 GET /skills/omni-handler · /skills/unified-media-generation

有没有用 "unified-api" skill

用了。它不是叫 "unified-api",在 2.0 里就叫 unified-media-generation —— 这就是 unified 生成 API 的客户端/契约 skill,是 kamila_voice_over 八个 skill 之一。再加 omni-handler 专门教怎么写 Seedance Omni 的 any2video 引用 prompt(@audio1 映射 ref_audio_url 等)。

# unified-media-generation 里的请求形(any2video / omni)
{
  "type": "any2video",
  "models": ["…-seedance-2-omni"],     // 数组,不是单数 model
  "input": {
    "prompt": ...,
    "ref_image_url": [首帧],
    "ref_audio_url": [该窗 Fish 片段],  // ← 口播对齐的关键
    "duration": "11"
  }
}

runtime_env 要 UNIFIED_GENERATION_API_URL / _AUTH_KEY / _BILLING_EMAIL 三个(unified)+ FAL_KEY(lipsync-align 那一步走 FAL)。

直接回答你的三个问题

Q1 · kamila_voice_over 怎么做 seedance 对齐口播?

Fish TTS 出整条俄语配音 → 按词级时间戳切 5–15s 窗 → 每窗喂 Seedance Omni(any2video,该窗 Fish 当 ref_audio_url)生成对口型画面 → lipsync-align(FAL)把嘴型锚到 Fish → 拼接 1080×1920。声音=Fish,画面=Seedance,对齐靠 ref_audio + lipsync。

Q2 · 用哪个 unified seedance 模型?

Seedance Omni · any2video,经 unified 生成 API。模型 id:omni-handlerultra-seedance-2-omni,unified-media-generation 示例写 funpub-seedance-2-omni —— 两份文档不一致,最终字面在 runner 的 .py 里(API 取不到)。

Q3 · 有没有用 unified-api skill?

有 —— 2.0 里叫 unified-media-generation(unified API 客户端),配 omni-handler(omni prompt 规则)。runtime_env 依赖 UNIFIED_GENERATION_* 三件。lipsync 那步另走 FAL_KEY