Breaking News

Other

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.

Install

openclaw skills install breaking-news

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

Dimensionbi-dailybreaking-news
Topics101
Triggercronmanual
Searchsub-agent pipelinesub-agent (V4 Flash)
Script1900-2100 chars~100 chars
Info cards121 (metric)
Video duration6-7 min15-30 sec
Aspect ratio16:9 (1920x1080)9:16 (1080x1920)
Freshness48h24h
Subtitle wrapnone>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):

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):

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:

{
  "category": "国际",
  "title": "俄罗斯无人机袭击罗马尼亚公寓楼",
  "big_value": "2人受伤",
  "unit": "俄无人机坠入北约领土",
  "note": "罗马尼亚驱逐俄外交官并关闭领事馆,北约启动第四条款紧急磋商",
  "source": "NYT / NPR / euronews"
}

字段约束

  • title ≤17 中文字符,超长自动截断加省略号
  • big_value ≤6 中文字符
  • note ≤28 中文字符,超过 14 字自动换行
  • source 列出主要信源,用 / 分隔
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:

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:

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 导致断连。

browser action=stop
sleep 3
browser action=start

Phase 6: Douyin Upload

cd skills/breaking-news/scripts/upload
PYTHONUNBUFFERED=1 .venv/bin/python upload_douyin.py <date>

Phase 7: WeChat Delivery → Publish

Step 1 — Send:

python3 skills/breaking-news/scripts/wechat_send.py <date> --breaking

Step 2 — Screenshot verify:

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

ScriptPurpose
scripts/gen_cards.pyInfo card generation (vertical, single card)
scripts/wrap_vtt.pyVTT subtitle line-breaking (>13 CJK chars)
scripts/check_vtt.pyVTT auto-fix (de-punctuation + de-overlap)
scripts/upload/upload_douyin.pyUpload to Douyin
scripts/wechat_send.pyWeChat 3-piece delivery
scripts/wechat_screenshot.pyWeChat delivery verification
scripts/cover_base.pngCover template base

External Scripts

ScriptLocationPurpose
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>/
    └── ...