Files
Popiai-skill/social-media/hilo-promo/references/spec.md
T

179 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# hilo-promo · 规范速查与文件结构
详细技术规范与文件组织,供阶段六合成时参考。
---
## 关键规范速查
### 字幕规范
> 📸 视觉参考:`assets/subtitle_style_ref.jpg`(实拍样式图,收录于 skill assets
| 项目 | 规范 |
|------|------|
| **渲染方式** | PIL 生成全帧 RGBA 透明 MOV,再用 ffmpeg overlay 叠加(本环境无 `drawtext`/`ass` filter |
| **字体** | Arial Black,全大写(`text.upper()` |
| **颜色** | 白色填充 `(255,255,255)`,橙红色描边 `(210,70,10)``stroke_width=4` |
| **自适应字号** | 从 56pt 开始逐步缩小(52→48→44→40→36→32→28→24),直到文字宽度 ≤ **756px**(画面宽 70% |
| **左右边距** | 每侧留白 15-20%;最大文字宽度 = `1080 × 0.70 = 756px` |
| **竖向位置** | 文字**底边**在 `y = 1344`(画面高度 70%,位于数字人 PiP 上方) |
| **显示方式** | **逐句单行**:一次只显示一句话,句与句之间无重叠;长句拆成短语分时段显示 |
| **时间戳来源** | 先用 `audio_transcribe_lyrics` 获取句级时间戳,再按自然语义拆成 5-7 词的短语,时长按字符数比例分配 |
| **无背景条** | 文字直接叠加在视频画面上,不加半透明底条 |
**PIL 渲染核心代码:**
```python
font, tw, fs = fit_font(text, max_w=756) # 自适应字号
x = (1080 - tw) // 2 # 水平居中
y = 1344 - fs # 底边对齐 y=1344
draw.text((x, y), text.upper(), font=font,
fill=(255,255,255,255),
stroke_width=4, stroke_fill=(210,70,10,255))
```
### 数字人 PiP 规范
> ⚠️ **`pip_clean.mov` 易过度抠像(alpha 全零)**,阶段七合成时改用 `talking_head_green.mp4` + ffmpeg `chromakey` 实时抠像,更可靠。
| 项目 | 规范 |
|------|------|
| **抠像方式** | ffmpeg `chromakey` 实时抠绿幕(阶段七合成时内联,无需预处理) |
| **绿幕颜色** | 采样视频角落像素确定实际颜色(AI 生成绿幕通常为 RGB≈56,138,61 = `0x388A3D` |
| **⚠️ similarity** | **必须 ≤ 0.10**(深色皮肤角色 YUV 距离仅 0.25similarity=0.15 即开始抠掉人物;切勿使用 0.2 以上) |
| **blend** | `0.02`(边缘锐利,减少半透明残边) |
| **缩放** | `scale=400:-2`(宽 400px = 画面宽 37%,高度保持比例,约 541px) |
| **位置** | `overlay=x=0:y=H-h`(左对齐,底部与视频底边齐平) |
| **起始时间** | `enable='lte(t\,{voiceover_end})'`(第 0s 出场,口播结束即消失) |
| **图层顺序** | **数字人必须在最上层**(覆盖字幕层之上) |
| **幕色选择** | 默认绿幕 `#00FF00`;角色服装含绿色调时改用蓝幕 `#0000FF` |
**阶段七 filter_complex 片段:**
```
[2:v]chromakey=0x388A3D:0.10:0.02,scale=400:-2[pip_keyed];
[v_sub][pip_keyed]overlay=x=0:y=H-h:enable='lte(t\,13.735)':format=auto[v_out]
```
**similarity 诊断方法:**
```python
# 提取单帧检查抠像结果
ffmpeg -ss 3 -i talking_head_green.mp4 -frames:v 1 -vf "chromakey=COLOR:SIM:0.02" test.png
# 用 PIL 检查 non-zero alpha 像素数
# 正常:~55-65% 像素可见(人物占画面大部分区域)
# 过度抠像:< 5% 像素可见 → 降低 similarity
```
### 教程段规范
| 项目 | 规范 |
|------|------|
| tut_002 时长 | **1-1.5s,严禁超过 2s** |
| tut_004 | **不展示静态原图**;上传动画结束即为确认状态 |
| 教程 overlay | **必须预拼接为单一 tut_combined.mp4**,禁止分段 overlay |
| 教程结束衔接 | 教程段结束后立即切回写真照片,无缝衔接 |
### 音频规范
| 项目 | 规范 |
|------|------|
| 语音收尾 | **截断而非 fade out**:尾板开始时直接截断口播,**禁止 fade out** |
| BGM 音量 | `volume=0.15`,不可盖过口播 |
| voice_id | 根据角色性别,调用声音列表工具(如 `get_voice_id`)选择合适的声音 |
| 尾板音频 | 用 `amix` 混合,保留尾板自身声音 |
| **音效保留** | 原 skill 中的所有音效必须保留(如闪光灯音效、转场音效等) |
**口播超过 6s 时的画面循环处理**(数字人视频生成上限 ~5.88s):
```bash
ffmpeg -stream_loop N -i talking_head_green.mp4 -i voiceover.mp3 \
-map 0:v -map 1:a -c:v libx264 -c:a aac -shortest -t {实际音频时长} output.mp4
```
### 图片转场规范
- 写真图之间**必须使用 xfade 转场**(`slideleft` / `slideright` / `fadeblack`
- **禁止硬切**
---
## 文件组织总览
```
{project_name}/
├── fonts/
├── brief.md # 需求摘要
├── character.jpg # 角色形象图
├── character_prompt.txt # 生图提示词
├── digital_person/
│ ├── green_screen.jpg # 绿幕肖像(Step 1
│ ├── talking_head_green.mp4 # official 图音视频(口型同步,Step 2)
│ ├── pip_clean.mov # 抠像后透明视频(Step 3)
│ └── talking_head_preview.mp4 # 5s demo 预览
├── photos/ # 换脸写真
│ ├── photo_01.jpg
│ ├── photo_02.jpg
│ └── photo_03.jpg
├── voiceover.mp3 # 口播语音
├── voiceover_script.txt # 口播文案
├── voiceover_subtitle.json # 字幕时间戳
├── final.mp4 # 最终视频
└── final_4x5.mp4 # 4:5 裁切版
```
> ⚠️ **合成时音频处理**:数字人视频(talking_head_green.mp4**自带音频**,合成时直接使用该音频,不要分离音频再混合,避免口型错位。
---
## 导出剪映/CapCut
使用 clip-export skill 与 `scripts/export_to_jianying.py` 导出工程到剪映/CapCut 进行精细调整。
脚本已内置多路径自动查找 `jy_wrapper`(同级 `../clip-export/scripts/``~/.claude/skills/clip-export/scripts/` 等),无需手动指定路径。
### 使用方式
```bash
python scripts/export_to_jianying.py \
--project-name "my_promo" \
--photos photos/photo_01.jpg photos/photo_02.jpg photos/photo_03.jpg \
--digital-person digital_person/talking_head_green.mp4 \
--voiceover voiceover.mp3 \
--bgm bgm.mp3 \
--subtitles voiceover_subtitle.json
```
> 若找不到 `jy_wrapper`,脚本会打印安装提示;也可通过 `JY_WRAPPER_PATH` 环境变量手动指定 `clip-export/scripts/` 目录路径。
---
## 合成命令参数
### 动作模板合成
```bash
python scripts/composite.py action \
--dance ./dance_video.mp4 \
--tutorial ./tutorial_segment.mp4 \
--voiceover ./voiceover.mp3 \
--bgm ./bgm.mp3 \
--subtitle-json ./voiceover_subtitle.json \
--endcard ./assets/endcard_en.mp4 \
--output ./final.mp4
```
### 图片模板合成
```bash
python scripts/composite.py image \
--photos ./photos/photo_01.jpg ./photos/photo_02.jpg ./photos/photo_03.jpg \
--tutorial ./tut_combined.mp4 \
--pip ./digital_person/pip_clean.mov \
--voiceover ./voiceover.mp3 \
--bgm ./bgm.mp3 \
--subtitle-json ./voiceover_subtitle.json \
--endcard ./assets/endcard_en.mp4 \
--output ./final.mp4
```
> 所有路径均相对于 `{project_name}/` 项目目录。`--photos` 传入所有写真图,顺序即展示顺序;`--pip` 仅图片模板需要传入。