Watch
mathiaschu/watch
Watch a video from YouTube, Instagram, X/Twitter, Vimeo, TikTok or any of ~1800 yt-dlp sites (or a local path).
AI Agent自动剪辑旅行Vlog的完整工作流。从原始素材到成品视频,系统级只需ffmpeg,其余在Python venv内完成。by nyx研究所 (GitHub @znyupup · B站/小红书 @nyx研究所)
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-edit --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
Claude Code skills documentation · loads skills from .claude/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .claude/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-edit --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .agents/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-edit --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .cursor/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-edit --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .gemini/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-editInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .github/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install znyupup/ai-video-editing-skill vlog-auto-edit --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "vlog-auto-edit" agent skill from https://github.com/znyupup/ai-video-editing-skill/tree/main into .opencode/skills/vlog-auto-edit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vlog-auto-edit", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
vlog-auto-editAI Agent自动剪辑旅行Vlog的完整工作流。从原始素材到成品视频,系统级只需ffmpeg,其余在Python venv内完成。by nyx研究所 (GitHub @znyupup · B站/小红书 @nyx研究所)
Vlog Auto Edit is an agent skill from znyupup/ai-video-editing-skill. AI Agent自动剪辑旅行Vlog的完整工作流。从原始素材到成品视频,系统级只需ffmpeg,其余在Python venv内完成。by nyx研究所 (GitHub @znyupup · B站/小红书 @nyx研究所)
Its SKILL.md is about 6.8k tokens, which your agent loads only when the skill is triggered. The skill folder holds 12 other files, including scripts (for example `README.md`, `examples/clip_analysis.json` and `examples/edit_plan.json`).
It sits in Media & Creative, covering Video production and Speech recognition and synthesis. It works with FFmpeg, Python, GitHub and PyTorch. The repository describes itself as: AI Agent Skill for automated vlog editing. Feed raw footage, get a finished video. Powered by ffmpeg + Whisper + Vision API. The licence is MIT.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit b6429ab. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 2 files in scripts/ (Python), which the agent can run.
Shell commands in SKILL.md call:
ffmpegpippython3ffprobebrewaptFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comspace.bilibili.comx.comopen.bigmodel.cnFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Vlog Auto Edit loads about 6.8k tokens when it runs. Until then it costs about 31 tokens; SKILL.md has 752 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from znyupup/ai-video-editing-skill at commit b6429ab, republished under its MIT licence (© znyupup). 752 words, ~6,779 tokens.
.claude/skills/vlog-auto-edit/SKILL.md (or your agent's skills folder). This skill also uses 9 other files; get the full folder from GitHub.Author: nyx研究所 · GitHub · B站 @nyx研究所 · 小红书 @nyx研究所 · X @znyupup_music
把一堆手机拍的旅行素材,用AI自动剪成一个完整vlog。 最小依赖:ffmpeg + Python(whisper或FunASR+Pillow) + 视觉API。系统级只装ffmpeg,其余pip装。
| 工具 | 用途 | 安装 |
|---|---|---|
| ffmpeg | 视频裁剪/编码/拼接/抽帧/音量检测 | brew install ffmpeg (macOS) / apt install ffmpeg (Linux) |
| Python 3.9+ | 脚本胶水 | macOS/Linux 系统自带 |
先检测系统是否已安装所需包,不要无脑创建 venv:
# 检测 whisper
python3 -c "import whisper; print('✅ whisper已安装:', whisper.__file__)"
# 检测 Pillow
python3 -c "from PIL import Image; print('✅ Pillow已安装')"⚠ 重要:不要用 2>/dev/null 吃掉错误!要看到实际报错才能判断是真没装还是PATH问题。
⚠ macOS 注意: 检查系统已安装的包再决定是否需要 venv。如果系统 python3 已经能 import whisper 和 from PIL import Image,直接用就行,不要盲目创建 venv 导致找不到已有的包。
仅在检测不通过时才安装:
# 方案A: 直接装到用户环境(推荐)
pip install openai-whisper Pillow
# 方案B: 如果用户环境有冲突,再用 venv
python3 -m venv .venv && source .venv/bin/activate
pip install openai-whisper Pillow⚠ 不要每个项目都新建 venv 重装一遍!whisper模型文件1.4GB,pip install也要几分钟。
⚠ ASR引擎二选一:Whisper 或 FunASR
本工作流支持两个ASR引擎,优先用 whisper,whisper 装不上就用 FunASR:
| Whisper (OpenAI) | FunASR (阿里达摩院) | |
|---|---|---|
| 安装 | pip install openai-whisper | pip install funasr modelscope torchaudio |
| 模型 | medium (1.4GB, 从GitHub下载) | paraformer-zh (1.05GB, 从ModelScope下载) |
| 附加模型 | 无 | VAD模型 + 标点模型 (首次运行自动下载, 共~50MB) |
| 中文效果 | 好 | 好(与whisper medium相当,细节略多) |
| 速度 | 14s音频 ≈ 5-10s (CPU) | 14s音频 ≈ 1.9s (CPU, RTF=0.132) ⚡ |
| 时间戳粒度 | 句级别 (每句话一个start/end) | 字级别 (每个字一个时间戳) |
| 模型下载源 | GitHub (国内不稳定,容易失败) | ModelScope (国内极快,~15MB/s) |
| 隐性依赖 | torch | torch + torchaudio (⚠ 必须额外装) |
检测已安装的ASR引擎:
python3 -c "import whisper; print('✅ whisper可用')" 2>/dev/null \
|| python3 -c "from funasr import AutoModel; print('✅ funasr可用')" 2>/dev/null \
|| echo "❌ 没有可用的ASR引擎,需要安装一个"⚠ FunASR 安装注意事项:
pip install funasr modelscope 不会自动装 torchaudio,import 时会报 No module named 'torchaudio'pip install funasr modelscope torchaudio~/.cache/modelscope/hub/models/iic/ 目录下标题卡方案自动选择:
ffmpeg -filters 2>&1 | grep drawtext
# 有drawtext → 直接用ffmpeg,不需要Pillow
# 没有 → 用Pillow生成透明PNG再overlay(macOS brew ffmpeg通常没编freetype)可选(参考分析阶段用):
which yt-dlp && echo "✅ yt-dlp已安装" || pip install yt-dlp
python3 -c "import scenedetect" 2>/dev/null && echo "✅ scenedetect已安装" || pip install scenedetect需要一个能理解图像内容的视觉模型API,用于分析素材画面。
要求:
推荐模型:
| 模型 | 费用 | 说明 |
|---|---|---|
| 智谱 GLM-4.6V-Flash | 免费 | 注册 https://open.bigmodel.cn 即用,中文理解好 |
| GPT-4o | 付费 | 效果最好 |
| Qwen-VL | 付费 | 阿里云,中文好 |
调用示例:
import base64, json, urllib.request
API_URL = 'YOUR_VISION_API_ENDPOINT' # 替换为你的视觉模型端点
API_KEY='***' # 替换为你的API Key
MODEL = 'YOUR_MODEL_NAME' # 替换为你的模型名
with open('frame.jpg', 'rb') as f:
img_b64 = base64.b64encode(f.read()).decode()
payload = {
'model': MODEL,
'messages': [{'role': 'user', 'content': [
{'type': 'image_url', 'image_url': {'url': f'data:image/jpeg;base64,{img_b64}'}},
{'type': 'text', 'text': '简洁描述画面内容,标注:镜头类型(远/全/中/近/特写)、拍摄手法(固定/手持/移动)、画面氛围。格式:内容|类型|手法|氛围'}
]}],
'max_tokens': 200
}
req = urllib.request.Request(API_URL, json.dumps(payload).encode(),
{'Content-Type': 'application/json', 'Authorization': f'Bearer {API_KEY}'})
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
print(result['choices'][0]['message']['content'])⚠ 视觉API调用注意事项:
data:image/jpeg;base64,{b64} 放入 image_url目标:了解你有什么素材。
# 批量获取素材信息
for f in footage/*.{MOV,mp4,MP4}; do
echo "=== $f ==="
ffprobe -v quiet -print_format json -show_format -show_streams "$f" \
| python3 -c "import json,sys; d=json.load(sys.stdin); \
s=d['streams'][0]; f=d['format']; \
print(f\" 时长: {float(f['duration']):.1f}s\"); \
print(f\" 分辨率: {s['width']}x{s['height']}\"); \
print(f\" 编码: {s['codec_name']}\")"
done输出一份素材清单:数量、总时长、分辨率分布、拍摄时间范围。
目标:建立"好vlog长什么样"的认知。不做这步直接剪,效果会很差。
步骤:
yt-dlp 下载720p视频 + 音频whisper 转录旁白(提取叙事结构)scenedetect 检测镜头切换(统计节奏)ffmpeg 抽关键帧 + 视觉API分析(理解画面构成)已验证的规律:
剪辑教程要点:
目标:让AI理解每条素材的内容。每条素材做三维分析:
a) 音频分析 — ASR转录(Whisper 或 FunASR)
# 提取音频(16kHz单声道WAV,两个引擎通用的最佳输入格式)
ffmpeg -i footage/INPUT.MOV -vn -acodec pcm_s16le -ar 16000 -ac 1 /tmp/audio.wav方案1: Whisper转录(优先使用)
import whisper, json
model = whisper.load_model('medium')
result = model.transcribe('/tmp/audio.wav', language='zh')
segments = [{"start": s['start'], "end": s['end'], "text": s['text']} for s in result['segments']]
# 输出: [{"start": 0.5, "end": 2.3, "text": "我们现在在中央大街"}, ...]方案2: FunASR转录(Whisper装不上时的替代)
import warnings
warnings.filterwarnings("ignore")
from funasr import AutoModel
model = AutoModel(
model="paraformer-zh", # Paraformer-large,中文ASR主模型(1.05GB)
vad_model="fsmn-vad", # 语音活动检测,自动切分静音段
punc_model="ct-punc", # 自动加标点
log_level="ERROR",
disable_update=True # 跳过版本检查,加快启动
)
result = model.generate(input="/tmp/audio.wav")
text = result[0]["text"] # 完整识别文本(已加标点)
timestamps = result[0].get("timestamp", []) # 字级别时间戳 [[start_ms, end_ms], ...]
# ⚠ FunASR时间戳是字级别(毫秒),需要聚合成句级别才能和whisper格式统一
# 聚合策略:按标点符号(。!?,)切分成句子
segments = []
if timestamps and text:
puncs = set("。!?,,.!?")
current_start = timestamps[0][0] / 1000.0
current_text = ""
chars = list(text)
for i, (char, ts) in enumerate(zip(chars, timestamps)):
current_text += char
if char in puncs or i == len(chars) - 1:
segments.append({
"start": round(current_start, 1),
"end": round(ts[1] / 1000.0, 1),
"text": current_text.strip()
})
if i < len(chars) - 1:
current_start = timestamps[i + 1][0] / 1000.0
current_text = ""
# 输出格式与whisper一致: [{"start": 0.6, "end": 2.9, "text": "我们现在在中央大街的入口,"}, ...]⚠ FunASR 输出差异注意:
"",timestamps 为空 []b) 音量检测 — ffmpeg
ffmpeg -i footage/INPUT.MOV -af volumedetect -f null /dev/null 2>&1 | grep volume
# mean_volume: 平均音量(dB) max_volume: 峰值音量(dB)
# mean < -40dB 基本无声 mean > -20dB 有明显声音/语音c) 视觉分析 — ffmpeg抽帧 + 视觉理解模型
# 抽帧策略(按时长分段)
# ≤20s → 3帧(首/中/尾)
# 20-60s → 5帧
# >60s → 每15s一帧
# 抽首/中/尾三帧示例(缩到720p节省带宽)
ffmpeg -i footage/INPUT.MOV -vf "select=eq(n\,0),scale=1280:-1" -frames:v 1 -q:v 2 frame_start.jpg
ffmpeg -i footage/INPUT.MOV -vf "select=eq(n\,MIDDLE),scale=1280:-1" -frames:v 1 -q:v 2 frame_mid.jpg
ffmpeg -i footage/INPUT.MOV -vf "reverse,scale=1280:-1" -frames:v 1 -q:v 2 frame_end.jpgd) 输出格式(每条素材一个JSON)
{
"filename": "clip_001.MOV",
"duration": 15.3,
"resolution": "1920x1080",
"visual": [
{"time": "0:00", "description": "...", "shot_type": "近景", "camera": "手持", "mood": "温馨"}
],
"audio": {
"has_speech": true,
"mean_volume_db": -20.5,
"max_volume_db": -3.2,
"transcript": [{"start": 0.5, "end": 2.3, "text": "..."}]
}
}目标:在给LLM之前,自动标注每条素材的"推荐有效区间",让LLM专注叙事编排。
为什么需要这步: 原始手机素材普遍存在:开头录制口令、同一句话重复多遍、前1-2s举手机晃动、说完话后拖很长的无意义画面。如果把这些原始数据直接给LLM,LLM输出的plan还需要逐条修补。
预处理做四件事:
RECORDING_CUES = [
"开始了", "好了开始", "开始录了", "好了 开始",
"走了", "好了", "来了", # 仅在前3s内出现时算口令
]
def detect_cues(transcript_segments, duration):
"""检测录制口令,返回skip zones"""
skip_zones = []
for seg in transcript_segments:
text = seg['text'].strip()
if seg['start'] < 3.0 and any(text.startswith(c) or text == c for c in RECORDING_CUES):
skip_zones.append({
'type': 'recording_cue',
'range': [seg['start'], seg['end']],
'text': text,
'action': 'skip'
})
if seg['end'] > duration - 3.0 and text in ["好了", "走了", "好了 走"]:
skip_zones.append({
'type': 'recording_cue',
'range': [seg['start'], seg['end']],
'text': text,
'action': 'skip'
})
return skip_zonesfrom difflib import SequenceMatcher
def detect_repeats(segments, threshold=0.6):
"""检测重复语音,标记建议跳过的重复段"""
repeats = []
for i, a in enumerate(segments):
for j, b in enumerate(segments):
if j <= i: continue
ratio = SequenceMatcher(None, a['text'], b['text']).ratio()
if ratio >= threshold and len(a['text']) > 4:
skip = a if len(a['text']) <= len(b['text']) else b
repeats.append({
'type': 'repeat',
'range': [skip['start'], skip['end']],
'text': skip['text'],
'kept': b['text'] if skip == a else a['text'],
'action': 'suggest_skip'
})
return repeatsdef detect_trim_points(segments, duration, mean_volume_db):
"""基于语音+音量推荐起止点"""
suggested_start = 0.0
suggested_end = duration
if segments:
first_speech = segments[0]['start']
last_speech_end = segments[-1]['end']
if first_speech > 2.0:
suggested_start = max(0, first_speech - 0.5)
if duration - last_speech_end > 3.0:
suggested_end = last_speech_end + 1.5
else:
if duration > 5.0:
suggested_start = 1.0
return suggested_start, suggested_end| 模式 | 纯画面上限 | 口令处理 | 重复处理 | 起手晃动 |
|---|---|---|---|---|
| strict | 8-10s | 全部去除 | 只留1遍 | 去1.5s |
| normal | 12-15s | 去除明确口令 | 去明显重复 | 去1.0s |
| loose | 20-25s | 仅去"开始了" | 保留大部分 | 去0.5s |
预处理结果追加到素材分析数据里:
━━━ clip_012.mp4 | 时长25.8s | 1920x1080 ━━━
【画面】...
【语音】...
【精修建议】模式: normal
原始区间: [0.0 - 25.8]
推荐区间: [2.4 - 25.8]
⊘ [0.0-2.4] 跳过: 录制口令 "开始了"
有效语音: [2.62-25.8]⚠ 重要原则:预处理只做标注和建议,不做硬裁剪。LLM保留最终决定权。
目标:把预处理后的素材数据交给LLM,生成剪辑方案。
输入:素材分析 + 精修建议(推荐区间) + 参考研究结论 输出:剪辑方案JSON(结构见下方schema)
{
"title": "视频标题",
"structure": [
{
"section": "段落名 — 副标题",
"description": "本段落内容概述",
"clips": [
{
"file": "素材文件名.mp4",
"start": 0.0,
"end": 12.0,
"note": "画面内容简述",
"subtitle": "保留的语音文字"
}
]
}
],
"bgm_suggestion": "BGM风格建议(含genre/mood/instruments/tempo/bpm)",
"editing_notes": "整体剪辑说明"
}约束:
详细prompt模板见 templates/edit_plan_prompt.md。
目标:把LLM输出的剪辑方案做成图文并茂的网页,方便用户直观Review。
Dashboard 包含两个面板:
使用 gen_dashboard.py 脚本:
# 最小用法(只生成素材面板)
python3 scripts/gen_dashboard.py \
--analysis clip_analysis.json \
--plan edit_plan.json \
--footage footage/ \
--out output/
# 完整两面板(含成品QC帧)
python3 scripts/gen_dashboard.py \
--analysis clip_analysis.json \
--plan edit_plan.json \
--footage footage/ \
--video output/final.mp4 \
--out output/支持两种分析数据格式(自动检测):
clip_analysis.json: {filename, duration, visual: [...], audio: {...}}clips_compact.json: {file, dur, visual: "string", speech: [...]}用户确认分镜方案后,再进入渲染阶段。
目标:自动校验LLM输出的plan,确保没有把任何语音从中间截断。
def validate_speech(plan_clips, speech_data):
"""检查每个clip的start/end是否截断了语音"""
issues = []
for clip in plan_clips:
f = clip['file']
cs, ce = clip['start'], clip['end']
if f not in speech_data: continue
for ss, se, txt in speech_data[f]:
if ss < ce and se > cs: # 语音与clip有交集
if ss < cs - 0.3: # 语音开始在clip之前
issues.append(f"截断开头: {f} clip从{cs}开始,但语音\"{txt}\"从{ss}开始")
if se > ce + 0.5: # 语音结束在clip之后
issues.append(f"截断结尾: {f} clip在{ce}结束,但语音\"{txt}\"到{se}结束")
return issues校验不通过时:自动修复start/end,不需要回LLM重新编排。
def fix_speech_cuts(plan, speech_data, margin=0.3):
"""
自动修复语音截断问题。直接修改plan中的start/end。
参数:
plan: edit_plan dict (会被原地修改)
speech_data: dict, {filename: [(start, end, text), ...]}
margin: float, 语音边界容差(秒)
返回:
fixes: list of str, 修复日志
"""
fixes = []
for sec in plan["structure"]:
for clip in sec["clips"]:
f = clip["file"]
cs = float(clip["start"])
ce = float(clip["end"])
if f not in speech_data:
continue
for ss, se, txt in speech_data[f]:
if ss >= ce or se <= cs:
continue
# 开头截断:clip.start > speech.start + margin
if ss < cs - margin and se > cs:
old_start = cs
new_start = max(0, ss - 0.1)
clip["start"] = round(new_start, 1)
fixes.append(
f" 修复开头: {f} [{old_start}→{new_start}] "
f"语音\"{txt[:20]}\"从{ss}s开始"
)
cs = new_start
# 结尾截断:clip.end < speech.end - margin
if se > ce + margin and ss < ce:
old_end = ce
new_end = se + 0.2
clip["end"] = round(new_end, 1)
fixes.append(
f" 修复结尾: {f} [{old_end}→{new_end}] "
f"语音\"{txt[:20]}\"到{se}s结束"
)
ce = new_end
return fixes
def validate_and_fix(plan, speech_data):
"""
端到端校验+修复入口。
用法:
import json
# 加载plan
plan = json.load(open('edit_plan.json'))
# 加载语音数据 (格式: {filename: [{start, end, text}, ...]})
raw = json.load(open('clip_analysis.json'))
speech_data = {}
for clip_info in raw:
fname = clip_info['filename']
if clip_info['audio']['has_speech']:
speech_data[fname] = [
(s['start'], s['end'], s['text'])
for s in clip_info['audio']['transcript']
]
# 校验
all_clips = [c for sec in plan['structure'] for c in sec['clips']]
issues = validate_speech(all_clips, speech_data)
print(f'发现 {len(issues)} 个截断问题')
# 修复
fixes = fix_speech_cuts(plan, speech_data)
print(f'自动修复 {len(fixes)} 处')
# 二次校验确认
all_clips2 = [c for sec in plan['structure'] for c in sec['clips']]
issues2 = validate_speech(all_clips2, speech_data)
assert len(issues2) == 0, f'仍有 {len(issues2)} 个问题未修复!'
print('✅ 二次校验通过')
# 保存修复后的plan
json.dump(plan, open('edit_plan_fixed.json', 'w', ensure_ascii=False), indent=2)
"""
all_clips = [c for sec in plan['structure'] for c in sec['clips']]
issues = validate_speech(all_clips, speech_data)
fixes = fix_speech_cuts(plan, speech_data)
return issues, fixes修复策略:
| 截断类型 | 检测条件 | 修复方式 |
|---|---|---|
| 开头截断 | clip.start > speech.start + margin | clip.start → speech.start - 0.1s |
| 结尾截断 | clip.end < speech.end - margin | clip.end → speech.end + 0.2s |
边界容差 (margin=0.3s): Whisper/FunASR 时间戳有±0.2-0.3s误差。修复后必须跑二次校验。
目标:把剪辑方案转成ffmpeg命令并执行。
# 单个片段裁剪+编码
ffmpeg -y -ss 00:00:02.400 -i footage/INPUT.mp4 -t 00:00:23.400 \
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1" \
-r 30 -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
-c:a aac -b:a 128k -ar 44100 -ac 2 \
-movflags +faststart segments/seg_001.mp4
# 拼接成品
ffmpeg -y -f concat -safe 0 -i concat.txt -c copy -movflags +faststart output.mp4# 2倍速(视频+音频同步)
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" -filter:a "atempo=2.0" out.mp4从各段落选最有视觉冲击力的镜头,快切拼接放在视频最前面。
选片原则:
import subprocess, os
highlights = [
('footage/clip_a.mp4', 1.5, 2.2),
('footage/clip_b.mp4', 2.0, 2.7),
# ... 每个0.7秒左右
]
for i, (f, start, end) in enumerate(highlights, 1):
dur = end - start
cmd = f'ffmpeg -y -ss {start} -i "{f}" -t {dur} ' \
f'-vf "scale=1920:1080:force_original_aspect_ratio=decrease,' \
f'pad=1920:1080:(ow-iw)/2:(oh-ih)/2" ' \
f'-c:v libx264 -preset ultrafast -crf 23 -pix_fmt yuv420p ' \
f'-an highlights/hl_{i}.mp4'
subprocess.run(cmd, shell=True)
# concat
with open('highlights/concat.txt', 'w') as f:
for i in range(1, len(highlights)+1):
f.write(f"file 'hl_{i}.mp4'\n")
subprocess.run('ffmpeg -y -f concat -safe 0 -i highlights/concat.txt '
'-c copy highlights/montage.mp4', shell=True)用 Pillow 生成透明RGBA的PNG,ffmpeg overlay到视频画面上。不要用黑底标题卡。
from PIL import Image, ImageDraw, ImageFont, ImageFilter
W, H = 1920, 1080
title = "段落标题"
img = Image.new("RGBA", (W, H), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# 字体:macOS用冬青黑体,Linux用Noto Sans CJK
# font = ImageFont.truetype("/System/Library/Fonts/Hiragino Sans GB.ttc", 60) # macOS
# font = ImageFont.truetype("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", 60) # Linux
font = ImageFont.truetype("YOUR_FONT_PATH", 60)
bbox = draw.textbbox((0, 0), title, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
x, y = (W - tw) // 2, (H - th) // 2
# 柔和阴影
shadow = Image.new("RGBA", (W, H), (0, 0, 0, 0))
sd = ImageDraw.Draw(shadow)
sd.text((x+2, y+2), title, fill=(0, 0, 0, 180), font=font)
shadow = shadow.filter(ImageFilter.GaussianBlur(radius=6))
img = Image.alpha_composite(img, shadow)
draw = ImageDraw.Draw(img)
draw.text((x, y), title, fill=(255, 255, 255, 240), font=font)
img.save("title_overlay.png")overlay到视频上:
# 标题显示前3秒
ffmpeg -y -i section.mp4 -i title.png \
-filter_complex "overlay=0:0:enable='between(t,0,3)'" \
-c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
-c:a aac -b:a 128k -movflags +faststart \
section_titled.mp4⚠ overlay关键Pitfall:
shortest=1 — PNG只有1帧,会导致视频流立刻截断overlay=0:0:enable='between(t,0,3)' 最简单可靠✅ 正确方案:逐段编码 → concat copy
echo "file 'final_sec_01.mp4'" > concat.txt
echo "file 'final_sec_02.mp4'" >> concat.txt
ffmpeg -y -f concat -safe 0 -i concat.txt -c copy -movflags +faststart output.mp4❌ 不要用xfade链式合并: 链式xfade会导致帧数累积丢失,后半段全黑。
段落间过渡:段落内硬切即可。如需渐变,每段首尾加fade比xfade更可靠。
BGM 可以手动添加,也可以用 AI 音乐生成工具(如 MiniMax music、Suno 等)。
风格映射参考(vlog常用):
| Vlog 风格 | genre | mood | instruments | tempo |
|---|---|---|---|---|
| 轻松日常 | indie pop | cheerful, carefree | ukulele, acoustic guitar | moderate |
| 文艺治愈 | folk, acoustic | warm, gentle | acoustic guitar, piano, strings | slow |
| 美食探店 | jazz, bossa nova | cozy, playful | piano, upright bass | moderate |
| 冰雪/冬季 | cinematic, ambient | serene, majestic | piano, celesta, strings | slow |
| 热带/海岛 | tropical house | sunny, relaxed | steel drums, marimba | upbeat |
| 城市探索 | lo-fi hip hop | chill, urban | keys, vinyl crackle | moderate |
# BGM 音量 10-15%(有语音的vlog要压低BGM)
ffmpeg -y -i video_no_bgm.mp4 -i bgm.m4a \
-filter_complex "[0:a]volume=1.0[orig];[1:a]volume=0.12[bgm];[orig][bgm]amix=inputs=2:duration=first:dropout_transition=2[aout]" \
-map 0:v -map "[aout]" -c:v copy -c:a aac -b:a 192k \
-movflags +faststart output/final.mp4BGM 音量建议:
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" -filter:a "atempo=2.0" out.mp4ffmpeg -i output.mp4 -f null - 检查实际frame数pip install funasr modelscope 不够,必须 pip install funasr modelscope torchaudio,否则 import 报 No module named 'torchaudio'# 验证
ffprobe -show_entries stream=duration -select_streams v -of csv=p=0 seg.mp4 # 视频时长
ffprobe -show_entries stream=duration -select_streams a -of csv=p=0 seg.mp4 # 音频时长
# 补齐
ffmpeg -y -i seg.mp4 -af "apad" -c:v copy -c:a aac -shortest seg_fixed.mp4# 每段提取音频(apad补齐到视频时长)
ffmpeg -y -i seg.mp4 -vn -af "apad" -ar 48000 -ac 2 -c:a pcm_s16le -t {视频时长} seg.wav
# concat filter拼接音频(不是demuxer)
ffmpeg -y -i a.wav -i b.wav -filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[aout]" -map "[aout]" -c:a aac audio.m4a
# 合并
ffmpeg -y -i video.mp4 -i audio.m4a -c:v copy -c:a copy output.mp4剪映兼容编码 — 导入剪映音画不同步的修复:-r 30 -vsync cfr(固定帧率) + -x264-params "bframes=0"(无B帧) + -ar 48000 -ac 2(统一音频)
crf值对录屏类内容要低 — 录屏文字密集,crf 22码率不够会糊。录屏用-crf 18或-b:v 3M。一般vlog素材crf 22即可
多次重编码画质累积损失 — 剪辑→拼接→加背景→叠进度条,每次编码都损失画质。尽量一次filter链搞定,减少编码次数
后台ffmpeg进程泄漏 — kill后台任务时可能只杀shell不杀ffmpeg子进程。每次kill后ps aux | grep ffmpeg确认,残留用kill -9 PID
开工前第一件事确认输出分辨率 — 全流程统一分辨率不要中途改。录屏类用1080p(文字密集对分辨率敏感),vlog素材用原始分辨率或1080p
⚠️ overlay视频时音频被覆盖/丢失 — 用filter_complex overlay叠加进度条/水印等视频时,ffmpeg可能自动选取overlay源(静音)的音频而不是原视频的音频。必须用-map明确指定音频来源:
# ❌ 不指定map,音频可能来自overlay源(静音)
ffmpeg -i main.mp4 -i overlay.mp4 -filter_complex "overlay=0:0" output.mp4
# ✅ 明确map:视频取filter输出,音频取原视频
ffmpeg -i main.mp4 -i overlay.mp4 \
-filter_complex "[0:v][1:v]overlay=0:0[vout]" \
-map "[vout]" -map 0:a \
-c:v libx264 -c:a copy output.mp4如果还不行(音频编解码问题),分步做更可靠:
# Step1: 只处理视频(-an去掉音频)
ffmpeg -i main.mp4 -i overlay.mp4 -filter_complex "overlay" -map "[vout]" -an video_only.mp4
# Step2: 把原始音频合回来
ffmpeg -i video_only.mp4 -i main.mp4 -map 0:v -map 1:a -c:v copy -c:a copy final.mp4-shortest会丢掉最后几秒的音频。优先用-t {精确时长}替代,或者确保音视频等长后不加-shortestmy-vlog-project/
├── footage/ # 原始素材(用户提供)
│ ├── clip_001.MOV
│ └── ...
│
├── analysis/ # 阶段3输出
│ └── clip_analysis.json
│
├── edit_plan.json # 阶段4输出
├── edit_plan_fixed.json # 阶段4.5输出
│
├── output/
│ ├── dashboard.html # Dashboard网页
│ ├── thumbnails/ # 素材缩略图
│ ├── qc_frames/ # 成品QC帧
│ ├── segments/ # 裁剪后的片段
│ ├── highlights/ # 片头高光蒙太奇
│ ├── titles/ # 段落标题PNG
│ ├── sections/ # 带标题的段落视频
│ ├── bgm/ # BGM文件
│ ├── video_no_bgm.mp4 # 无BGM的完整视频
│ └── final.mp4 # 🎬 成品视频
│
└── reference/ # 阶段2输出(可选)见 examples/ 目录下的示例文件。
素材文件夹 footage/
│
▼
阶段1: 素材盘点 → ffprobe批量扫描 → 清单
│
▼
阶段2: 参考研究 → 下载优质vlog → 分析节奏(可选)
│
▼
阶段3: 素材三维分析 → whisper/FunASR+音量+视觉 → clip_analysis.json
│
▼
阶段3.5: 精修预处理 → 口令/重复/晃动/纯画面标注
│
▼
阶段4: LLM叙事编排 → edit_plan.json
│
▼
阶段4+: Dashboard可视化 → 浏览器预览确认
│
▼
阶段4.5: 语音截断校验 → 自动修复
│
▼
阶段5: ffmpeg渲染 → 裁剪→标题→拼接→BGM → final.mp4 🎬© znyupup, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 9 other files (scripts) in the repository root of znyupup/ai-video-editing-skill.
Open the folder on GitHubat commit b6429ab
Vlog Auto Edit next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Vlog Auto Edit this skillznyupup/ai-video-editing-skill | 145 | — | ~6.8k | Automated safety check: Pass | MIT | |
| Watchmathiaschu/watch | 141 | — | ~4k | Automated safety check: Warn | MIT | |
| Render Myth Vs Factgooseworks-ai/goose-skills | 1.2k | — | ~2.2k | Automated safety check: Pass | MIT | |
| Webcode Local Windows Tts Installershuyu-labs/WebCode | 278 | — | ~787 | Automated safety check: Pass | Custom licence | |
| Cassette ModelCassette-Editor/oh-my-cassette | 157 | 1 repos | ~374 | Automated safety check: Pass | MIT | |
| Browser Video Recordingnirholas/three.ws | 227 | 1 repos | ~1.5k | Automated safety check: Pass | Apache-2.0 |
mathiaschu/watch
Watch a video from YouTube, Instagram, X/Twitter, Vimeo, TikTok or any of ~1800 yt-dlp sites (or a local path).
gooseworks-ai/goose-skills
Assemble a myth-vs-fact kinetic-typography explainer video ad (≈29.5s, 9:16) from N myth/fact pairs + hook / turn / punch copy + palette + a brand end-card PNG + a VO track — a hook, 3 red-strike…
shuyu-labs/WebCode
A skill your agent uses when building a local Windows WebCode installer from this repo for machine testing, especially when the package must bundle the Kokoro or sherpa-onnx Reply TTS service, model…
Cassette-Editor/oh-my-cassette
Show or change the Cassette editing model and thinking level for the current media session.
nirholas/three.ws
Create polished 60 fps 4:3 4K browser screen-recording style videos from Codex in-app browser captures, with browser-only crop, natural macOS cursor styling, deliberate click choreography…
LeoYeAI/openclaw-master-skills
Automated video editing skill for talk/vlog/standup videos. An agent skill from LeoYeAI/openclaw-master-skills.
Categories
AI Agent自动剪辑旅行Vlog的完整工作流。从原始素材到成品视频,系统级只需ffmpeg,其余在Python venv内完成。by nyx研究所 (GitHub @znyupup · B站/小红书 @nyx研究所). Vlog Auto Edit is an agent skill from znyupup/ai-video-editing-skill.
Vlog Auto Edit fits situations like: tasks that involve Video production; tasks that involve Speech recognition and synthesis.
Run `npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a claude-code`. Or copy the skill folder (the znyupup/ai-video-editing-skill repository) into .claude/skills/vlog-auto-edit in your project. Claude Code loads it when a task matches its description.
Run `npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a codex`. Or copy the skill folder (the znyupup/ai-video-editing-skill repository) into .agents/skills/vlog-auto-edit in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add znyupup/ai-video-editing-skill --skill vlog-auto-edit -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/vlog-auto-edit, .gemini/skills/vlog-auto-edit, .github/skills/vlog-auto-edit and .opencode/skills/vlog-auto-edit in your project.
Going by SKILL.md and its folder, Vlog Auto Edit needs Python for the scripts in its folder, the command-line tools its instructions call (ffmpeg, pip, python3, ffprobe, brew and apt) and credentials named API_KEY. Our summary lists: Python 3; A credential in API_KEY.
SKILL.md names 4 domains. As links in the text: github.com, space.bilibili.com, x.com and open.bigmodel.cn. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Vlog Auto Edit is published under the MIT licence (from the LICENSE file in the skill folder). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.8k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Vlog Auto Edit: Watch (mathiaschu/watch, 141 stars), Render Myth Vs Fact (gooseworks-ai/goose-skills, 1.2k stars), Webcode Local Windows Tts Installer (shuyu-labs/WebCode, 278 stars) and Cassette Model (Cassette-Editor/oh-my-cassette, 157 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
znyupup (a GitHub user) maintains it in znyupup/ai-video-editing-skill, which has 145 GitHub stars. The repository was last updated on April 27, 2026.
Source: znyupup/ai-video-editing-skill on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.