Single-topic breaking news video for Douyin. Triggered manually when the user sends a breaking news alert from WeChat or webchat. Covers: deep search → scrip...
---
name: breaking-news
description: >-
Single-topic breaking news video for Douyin. Triggered manually when the user
sends a breaking news alert from WeChat or webchat. Covers: deep search → script
→ TTS → info card → ffmpeg assembly → upload → WeChat delivery → publish.
Vertical (9:16) format.
---
# Breaking News Video Pipeline
## Architecture
```
User Trigger (微信/webchat: "突发:xxx")
│
Phase 1: Spawn sub-agent (DeepSeek V4 Flash, thinking=high)
│ deep search (24h freshness) → data cleaning → output ~100 char script
▼
Phase 2: Main agent review (char count / date / relevance)
│ Pass → launch TTS (edge-tts, background)
▼
Phase 3: Exaggerated title + topic.json + info card PNG
│
Phase 4: Assembly + subtitle line-breaking (>10 chars wrap)
│ Output: final.mp4
▼
Phase 5: Browser Restart (强制,防 CDP 断连)
│
Phase 6: Douyin Upload (upload_douyin.py)
│
Phase 7: WeChat Delivery → user review → publish
wechat_send.py + wechat_screenshot.py
```
---
## Differences from bi-daily
| Dimension | bi-daily | breaking-news |
|-----------|----------|:--:|
| Topics | 10 | 1 |
| Trigger | cron | manual |
| Search | sub-agent pipeline | sub-agent (V4 Flash) |
| Script | 1900-2100 chars | ~100 chars |
| Info cards | 12 | 1 (metric) |
| Video duration | 6-7 min | 15-30 sec |
| Aspect ratio | 16:9 (1920x1080) | **9:16 (1080x1920)** |
| Freshness | 48h | **24h** |
| Subtitle wrap | none | **>10 chars → wrap** |
---
## Phase 1: Deep Search + Script (Sub-Agent)
Spawn a V4 Flash sub-agent to handle search and first-draft script writing.
```
sessions_spawn with:
task: <see prompt template below>
model: "volcengine-plan/deepseek-v4-flash"
mode: "run"
thinking: "high"
```
### Prompt Template
Replace `[keyword]` with the breaking event keyword from the user's message.
```
你是一个突发新闻编辑。请按以下步骤执行:
1. 使用 web_search 对关键词"[keyword]"进行深度搜索
- 强制要求:只浏览 24 小时内的报道(使用 date_after 参数)
- 至少搜索 3 次不同角度(如:事件本身、各方反应、影响分析)
- 阅读搜索结果获取详细内容
2. 清洗数据:
- 排除重复信息
- 排除未经验证的传言
- 保留关键事实:时间、地点、人物、数字、后果
3. 撰写一条 100 字左右的短视频口播文案(中文):
- 开头有冲击力,直击核心
- 不换行,正确使用中文标点(。,、)
- 涉及"台湾"一律使用"台湾省"
- 涉及中国领导人加职位称呼
- 仅输出文案本身,不要任何解释
只输出文案,不要输出其他任何内容。
```
**Output**: The sub-agent returns the script text directly. Save to `突发新闻/突发<date>/文案/script.txt`.
---
## Phase 2: Main Agent Review + TTS
### 2a. Review Gate (MANDATORY)
Before TTS, the main agent MUST verify:
- [ ] **字数审查**: 中文字符 80-120 字。超出 → 手动裁剪。不足 → 补充关键事实。
- [ ] **日期核查**: web_search 交叉验证事件日期,确认 ≤24h
- [ ] **关联度核查**: 文案内容与用户触发关键词紧密相关,无偏题
- [ ] **合规审查**: 无禁止类别内容(娱乐/影视/短剧/游戏/电商/采访/访谈/评论/宗教/迷信/超自然/新车上市/地方政务)
**If fails**: fix and re-save script.txt before proceeding.
### 2b. TTS + Subtitles
After review passes, launch edge-tts (can run in background, no dependency on Phase 3):
```bash
mkdir -p 突发新闻/突发<date>/音频 && \
cd ~/.openclaw/workspace && \
edge-tts --voice zh-CN-YunjianNeural --rate=+30% \
-f 脚本/<date>_breaking/script.txt \
--write-media 音频/<date>_breaking/audio.mp3 \
--write-subtitles 突发新闻/突发<date>/音频/out.vtt
```
**VTT post-processing** (de-punctuation + de-overlap):
```bash
python3 skills/breaking-news/scripts/check_vtt.py 突发新闻/突发<date>/音频/out.vtt
```
---
## Phase 3: Title + Info Card
### 3a. Exaggerated Title(夸张标题)
From the final script, create a clickbait-style title (≤30 chars for Douyin):
- 提取最具冲击力的关键词
- 使用感叹号、问号、省略号等吸引点击
- 示例: "突发!俄无人机撞入北约大楼" / "刚刚!美伊达成停火协议"
Save to `突发新闻/标题/标题<date>.txt`:
```
[夸张标题]
#新闻 #突发 #时事
```
### 3b. Info Card (topic.json + gen_cards.py)
Extract from the final script + search results:
```json
{
"category": "国际",
"title": "俄罗斯无人机袭击罗马尼亚公寓楼",
"big_value": "2人受伤",
"unit": "俄无人机坠入北约领土",
"note": "罗马尼亚驱逐俄外交官并关闭领事馆,北约启动第四条款紧急磋商",
"source": "NYT / NPR / euronews"
}
```
**字段约束**:
- `title` ≤17 中文字符,超长自动截断加省略号
- `big_value` ≤6 中文字符
- `note` ≤28 中文字符,超过 14 字自动换行
- `source` 列出主要信源,用 / 分隔
```bash
python3 skills/breaking-news/scripts/gen_cards.py \
突发新闻/突发<date>/信息卡/topic.json \
突发新闻/突发<date>/信息卡/
```
Output: `s00.png` (1080x1920).
---
## Phase 4: Assembly + Subtitle Wrapping
### 4a. Subtitle Line-Breaking
After VTT is ready, wrap any subtitle line >10 CJK characters:
```bash
python3 skills/breaking-news/scripts/wrap_vtt.py 突发新闻/突发<date>/音频/out.vtt 13
```
This inserts `\n` break points every 10 CJK characters. Only counts CJK codepoints — punctuation and numbers are ignored. Original file backed up as `.bak`.
### 4b. Video Assembly
Single card PNG looped for audio duration, with subtitles burned:
```bash
cd ~/.openclaw/workspace && bash 视频制作/scripts/build_breaking.sh <date>
```
build_breaking.sh:
1. Detect audio duration via ffprobe
2. Convert s00.png → s00.mp4 (loop for audio duration)
3. ffmpeg single-pass: card + audio + subtitles at 1080x1920
4. Output: `导出/final_<date>_breaking.mp4`
---
## Phase 5: Browser Restart
**强制** — 每次上传前必须重启浏览器,防 CDP 端口堆积 Worker 导致断连。
```bash
browser action=stop
sleep 3
browser action=start
```
---
## Phase 6: Douyin Upload
```bash
cd skills/breaking-news/scripts/upload
PYTHONUNBUFFERED=1 .venv/bin/python upload_douyin.py <date>
```
---
## Phase 7: WeChat Delivery → Publish
**Step 1 — Send**:
```bash
python3 skills/breaking-news/scripts/wechat_send.py <date> --breaking
```
**Step 2 — Screenshot verify**:
```bash
python3 skills/breaking-news/scripts/wechat_screenshot.py
```
**Step 3 — Wait for user confirmation**: "可以发布"
**Step 4 — Publish**: Browser tool (same as bi-daily Phase 9).
---
## Scripts
| Script | Purpose |
|--------|---------|
| `scripts/gen_cards.py` | Info card generation (vertical, single card) |
| `scripts/wrap_vtt.py` | VTT subtitle line-breaking (>13 CJK chars) |
| `scripts/check_vtt.py` | VTT auto-fix (de-punctuation + de-overlap) |
| `scripts/upload/upload_douyin.py` | Upload to Douyin |
| `scripts/wechat_send.py` | WeChat 3-piece delivery |
| `scripts/wechat_screenshot.py` | WeChat delivery verification |
| `scripts/cover_base.png` | Cover template base |
## External Scripts
| Script | Location | Purpose |
|--------|----------|---------|
| `build_breaking.sh` | `视频制作/scripts/` | Vertical ffmpeg assembly |
---
## Directory Structure
```
突发新闻/
├── 标题/
│ └── 标题<date>.txt
├── 封面/
│ └── cover.png
├── 突发<date>/ ← one folder per event
│ ├── 文案/script.txt
│ ├── 信息卡/
│ │ ├── topic.json
│ │ └── s00.png
│ ├── 音频/
│ │ ├── audio.mp3
│ │ └── out.vtt
│ └── 导出/final.mp4
└── 突发<date2>/
└── ...
```
don't have the plugin yet? install it then click "run inline in claude" again.