Our review
Converts a video into visual notes as an Obsidian Canvas using a three-stage hybrid pipeline (audio transcription, visual detection, LLM fusion).
Strengths
- Combines audio transcription and visual analysis for comprehensive content coverage.
- Handles long videos by automatically splitting into 15-minute segments to reduce hallucinations.
- Produces a .canvas file directly usable in Obsidian with smart layout generation.
Limitations
- Requires external tools (FFmpeg, WhisperX, Gemini) and Python setup.
- Pipeline can be time-consuming depending on video length and compute power.
- Final quality depends on accuracy of transcription and visual detection models.
Best for converting training videos, lectures, or tutorials into structured visual notes.
Not recommended for very short videos (<1 minute) or videos with little to no relevant audio content.
Security analysis
CautionThe skill legitimately uses Bash to invoke a Python processing pipeline for video analysis, involving FFmpeg and possibly network APIs. While the skill itself seems well-intentioned, the combination of Bash with user-supplied file paths presents a risk of command injection if not properly sanitized by the agent. However, the skill is designed for local use and does not instruct destructive actions. Therefore, classify as caution.
- •Allows Bash execution that could be misused if video path is maliciously crafted, though double-quoting mitigates some risk.
- •Runs a Python script that may make network calls (Gemini API) and executes system commands (FFmpeg/ffprobe), which could lead to data exfiltration or unintended effects if the API keys are exposed.
Examples
/video-to-canvas video.mp4/video-to-canvas video.mp4 --depth=deep_dive/video-to-canvas video1.mp4 video2.mp4 video3.mp4name: video-to-canvas description: | 将视频转换为 Obsidian Canvas 可视化笔记。
三阶段混合管道 (社区验证最佳实践):
- Stage 1 (Ears): WhisperX/Gemini 音频转录
- Stage 2 (Eyes): Gemini 视觉检测 + FFmpeg 截图
- Stage 3 (Brain): LLM 双通道融合生成笔记
- Phase 4: Claude 智能布局生成 Canvas
用法:/video-to-canvas <视频路径> [选项]
user-invocable: true
allowed-tools:
- Read
- Write
- Edit
- Glob
- Grep
- Bash
- AskUserQuestion
- Task
- mcp__sequential-thinking__sequentialthinking
skills:
- obsidian-canvas
hooks: PreToolUse: - matcher: "Bash" hooks: - type: command command: "echo '[video-to-canvas] 执行系统命令...'"
Video to Canvas
将视频转换为 Obsidian Canvas 可视化笔记(三阶段混合管道)。
快速使用
/video-to-canvas <视频路径>
/video-to-canvas video.mp4 --depth=deep_dive
/video-to-canvas video.mp4 --no-transcribe # 跳过转录(旧模式)
/video-to-canvas video.mp4 --layout=mindmap
# 批量队列模式(多个视频顺序处理)
/video-to-canvas video1.mp4 video2.mp4 video3.mp4
完整工作流(三阶段混合管道)
📹 视频文件
│
├──────────────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Stage 1 (Ears) │ │ Stage 2 (Eyes) │
│ WhisperX/Gemini │ │ Gemini 视觉检测 │
│ 音频转录 │ │ + FFmpeg 截图 │
│ → 时间戳文本 │ │ → screenshots/ │
└──────────────────┘ └──────────────────┘
│ │
└──────────┬───────────┘
▼
┌────────────────────────────┐
│ Stage 3 (Brain) │
│ Gemini 2.5 Flash │
│ 转录文本 + 截图 → 笔记 │
│ 15 分钟分段避免幻觉 │
│ → 结构化 Markdown │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ Phase 4: Claude │
│ 语义理解 + 智能布局 │
│ → .canvas 文件 │
└────────────────────────────┘
│
▼
📊 Obsidian Canvas 可视化笔记
为什么需要三阶段?
| 问题 | 旧架构 | 三阶段管道 | |------|--------|-----------| | 音频内容丢失 | Phase 2 只发送截图,丢失口述内容 | Stage 1 转录音频,Stage 3 融合 | | 长视频幻觉 | Gemini >20min 严重幻觉 | 15 分钟分段处理 | | 信息不完整 | 只有屏幕变化,无讲解内容 | 双通道:视觉+音频 | | 截图覆盖缺口 | Gemini 只分析前半部分 | Stage 2.5 自动补充 + ffprobe 时长校正 | | 幻觉图片引用 | LLM 编造不存在的截图路径 | 提示词约束 + 后处理验证 |
防护机制(自动生效)
管道内置以下防护,无需手动干预:
| 机制 | 位置 | 解决的问题 |
|------|------|-----------|
| ffprobe 时长优先 | Stage 2.5, Stage 3 | Gemini Audio 转录时长可能幻觉(如 56min 视频报告 78min) |
| Stage 2.5 覆盖率检查 | Stage 2 之后 | Gemini 视觉检测只分析前半部分,后半段无截图 |
| 自动补充截图 | Stage 2.5 | 未覆盖区域每 30 秒自动截图填充 |
| 分段时长截断 | Stage 3 分段 | 超出实际视频时长的转录 chunks 被丢弃 |
| 提示词截图约束 | Stage 3 Prompt | 明确告知 LLM 只能引用列表中的截图文件 |
| 后处理图片验证 | 保存 MD 前 | 扫描所有  引用,移除指向不存在文件的 |
| 反引号修复 | 保存 MD 前 | 修复 Gemini 输出的 `` 格式问题 |
Stage 1-3: 三阶段管道 (Python 脚本)
执行步骤
-
后台启动三阶段管道(
--daemon自守护,无需 nohup):cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python video_to_md.py "<视频路径>" -o "<输出目录>" --depth balanced --srt-lang zh --daemon -
轮询进度(每 30-60 秒检查一次 progress.json):
cat "<输出目录>/progress.json"progress.json 字段说明:
status:running|completed|failedstage:stage1|stage1.5|stage2|stage3|donestage_detail: 当前阶段详细描述error: 失败时的错误信息
-
管道完成后(status=completed),获取输出文件:
<输出目录>/<视频名>.md- Markdown 笔记<输出目录>/screenshots/- 截图目录<输出目录>/<视频名>_transcript.json- 转录结果<输出目录>/<视频名>_changes.json- 变化点信息<输出目录>/<视频名>.srt- 英文字幕(精确时间戳,默认生成)<输出目录>/<视频名>.<lang>.srt- 翻译字幕(使用--srt-lang时生成)
恢复机制
管道支持断点恢复。如果中途失败,只需重新运行同一命令:
transcript.json已存在 → 跳过 Stage 1screenshots/+changes.json已存在 → 跳过 Stage 2chunks/chunk_N.json已存在 → Stage 3 跳过对应分段.srt文件已存在 → 跳过字幕生成
参数说明
| 参数 | 说明 | 默认值 |
|------|------|--------|
| --depth | 笔记深度 | balanced |
| --density | 检测密度 (sparse/normal/dense) | normal |
| --min-interval | 最小截图间隔(秒)| 2.0 |
| --fusion | 启用视觉双通道融合 | false |
| --backend | 转录后端 (auto/faster-whisper/gemini) | auto |
| --whisper-model | Whisper 模型大小 | large-v3 |
| --segment-minutes | 长视频分段时长(分钟)| 15 |
| --transcript | 已有转录文件路径 | - |
| --no-transcribe | 跳过音频转录(旧模式)| false |
| --no-srt | 不生成 SRT 字幕文件 | false |
| --srt-lang | SRT 翻译目标语言 (如 zh, ja, ko) | 不翻译 |
深度选项
short_hand: 极简模式,要点列表balanced: 平衡模式(推荐),段落+列表deep_dive: 深度模式,详尽解释
转录后端选择
| 后端 | 优势 | 要求 |
|------|------|------|
| faster-whisper (推荐) | 速度快、VAD 去幻觉、本地运行 | pip install faster-whisper |
| gemini (备选) | 无需本地模型、零配置 | GEMINI_API_KEY |
--backend auto 会自动检测:优先使用 faster-whisper,未安装则回退到 Gemini。
Phase 4: Canvas 智能生成
Phase 1-3 由 Python 脚本执行后,Phase 4 由 Claude 完成。
输入
读取 Phase 3 生成的 Markdown 笔记文件。
分析任务
-
结构识别
# 标题 → 画布标题(不创建节点) ## 一级章节 → group 节点(分组容器) ### 二级章节 → text 节点 #### 三级标题 → text 节点(在父节点内)  → file 节点 -
语义分析
- 并列关系 → 水平排列
- 层级关系 → 垂直排列
- 对比关系 → 左右对称
- 重要内容 → 颜色高亮
-
布局决策
- 教程类 → 流程图布局(从上到下)
- 概念类 → 思维导图(中心辐射)
- 对比类 → 表格布局(左右对称)
输出格式
生成 JSON Canvas 格式:
{
"nodes": [
{
"id": "group-章节名",
"type": "group",
"label": "一、章节标题",
"x": 0, "y": 0,
"width": 800, "height": 400,
"color": "4"
},
{
"id": "text-知识点",
"type": "text",
"text": "### 知识点标题\n\n内容描述...",
"x": 50, "y": 80,
"width": 350, "height": 150
},
{
"id": "img-00-36",
"type": "file",
"file": "<输出目录名>/screenshots/00-36.jpg",
"x": 50, "y": 250,
"width": 350, "height": 250
}
],
"edges": [
{
"id": "edge-1",
"fromNode": "text-知识点",
"toNode": "img-00-36",
"fromSide": "bottom",
"toSide": "top",
"label": "演示"
}
]
}
JSON Canvas 格式规范
节点类型
| 类型 | 用途 | 必需字段 |
|------|------|---------|
| text | Markdown 文本 | text |
| file | 图片/文件引用 | file |
| link | 外部 URL | url |
| group | 分组容器 | label (可选) |
通用节点属性
{
"id": "unique-16-char-hex",
"type": "text|file|link|group",
"x": 0,
"y": 0,
"width": 300,
"height": 150,
"color": "1" // 可选
}
颜色系统
| 值 | 颜色 | 建议用途 | |----|------|---------| | "1" | 红色 | 重要/警告 | | "2" | 橙色 | 提示/注意 | | "3" | 黄色 | 高亮重点 | | "4" | 绿色 | 正确/完成 | | "5" | 青色 | 引用/链接 | | "6" | 紫色 | 概念/定义 |
也支持 hex 格式:"#FF5733"
Edge 属性
{
"id": "edge-unique-id",
"fromNode": "source-node-id",
"toNode": "target-node-id",
"fromSide": "bottom", // top|right|bottom|left
"toSide": "top",
"toEnd": "arrow", // arrow|none
"label": "关系描述" // 可选
}
布局算法
尺寸建议
| 元素 | 宽度 | 高度 | |------|------|------| | 标题文本节点 | 300 | 80 | | 内容文本节点 | 400 | 150 | | 图片节点 | 350 | 250 | | 分组最小 | 自适应 | 自适应 | | 节点水平间距 | 100 | - | | 节点垂直间距 | 80 | - |
层级布局(默认)
[文档标题]
│
┌───────────────────┼───────────────────┐
│ │ │
[Group: 章节1] [Group: 章节2] [Group: 章节3]
│ │ │
┌───┴───┐ ┌─────┴─────┐ ┌───┴───┐
│ │ │ │ │ │
[知识点] [知识点] [知识点] [知识点] [知识点] [知识点]
│ │ │ │ │ │
[图片] [图片] [图片] [图片] [图片] [图片]
坐标计算规则
- 根节点:从 (0, 0) 开始
- Group 内部:padding 50px
- 同级节点:水平排列,间距 100px
- 子节点:垂直排列在父节点下方,间距 80px
- 图片节点:紧跟对应文本节点下方
避免重叠
- 计算每个 group 的实际宽高(基于内部节点)
- Group 之间保持 100px 间距
- 使用深度优先遍历计算坐标
图片路径处理
Markdown 中的图片

转换为 Canvas file 节点
{
"id": "img-00-36",
"type": "file",
"file": "<输出目录名>/screenshots/00-36.jpg",
"x": 50,
"y": 250,
"width": 350,
"height": 250
}
⚠️ 重要:Vault 相对路径
Obsidian Canvas 的 file 路径是相对于 vault 根目录的,不是相对于 .canvas 文件。
例如,输出目录为 lecture1/,用户将其复制到 vault 根目录后:
vault根目录/
├── lecture1/
│ ├── lecture.canvas
│ ├── lecture.md
│ └── screenshots/
│ └── 05-33.jpg
Canvas 中的路径应为:
"file": "lecture1/screenshots/05-33.jpg" // ✅ 正确 (vault 相对路径)
"file": "screenshots/05-33.jpg" // ❌ 错误 (canvas 相对路径)
Phase 4 生成规则:file 字段必须使用 <输出目录名>/screenshots/xx-xx.jpg 格式。
执行流程
当用户调用 /video-to-canvas <视频路径> 时:
Step 1: 参数解析
视频路径: <用户提供>
输出目录: 与视频同目录,或用户指定
深度: balanced (默认)
布局: hierarchical (默认)
Step 2: 后台启动三阶段管道
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 nohup uv run python video_to_md.py "<视频路径>" -o "<输出目录>" --depth <深度> --srt-lang zh > "<输出目录>/pipeline.log" 2>&1 &
Step 2.5: 轮询进度(每 30-60 秒)
cat "<输出目录>/progress.json"
等待 status 变为 completed(或 failed)。如果失败,查看 pipeline.log 和 error 字段。
完成后检查输出:
<输出目录>/<视频名>.md存在<输出目录>/screenshots/目录有图片<输出目录>/<视频名>_transcript.json存在(如启用转录)
Step 3: 读取 Markdown
使用 Read 工具读取生成的 .md 文件
Step 4: 分析并生成 Canvas
- 解析 Markdown 结构(标题、内容、图片)
- 确定语义关系和布局策略
- 计算每个节点的坐标
- 生成 JSON Canvas
- file 节点路径: 使用
<输出目录名>/screenshots/xx-xx.jpg(vault 相对路径) - text 节点内容: 确保 JSON 字符串中的双引号已转义 (
\") 或替换为全角引号
Step 5: 写入 Canvas 文件
使用 Write 工具写入 <视频名>.canvas
Step 6: 报告结果
✅ 三阶段混合管道完成!
输出文件:
├── <视频名>.md # Markdown 笔记(双通道融合)
├── <视频名>.canvas # Canvas 可视化
├── <视频名>.srt # 英文字幕(精确时间戳,默认生成)
├── <视频名>.<lang>.srt # 翻译字幕(--srt-lang 时生成)
├── <视频名>_transcript.json # 音频转录结果
├── <视频名>_changes.json # 变化点信息
└── screenshots/ # 截图目录
提示:将整个输出目录复制到 Obsidian vault 中,
然后打开 .canvas 文件查看可视化笔记。
SRT 字幕文件会被 Media Extended 插件自动检测(同名同目录)。
队列模式(批量处理多个视频)
当用户提供多个视频路径时,使用队列模式顺序处理。
队列流程
📹 video1.mp4 📹 video2.mp4 📹 video3.mp4
│ │ │
└───────────────┬───────────────┘
▼
┌─────────────────────┐
│ queue_processor.py │
│ add → run --daemon │
│ 顺序处理每个视频 │
└─────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
[Stage 1-3] [Stage 1-3] [Stage 1-3]
video1 ✓ video2 ◉ video3 ○
│ │ │
▼ ▼ ▼
[Phase 4] [Phase 4] [Phase 4]
Claude生成 Claude生成 Claude生成
Canvas Canvas Canvas
Step Q1: 添加视频到队列
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python queue_processor.py add "<视频1>" "<视频2>" "<视频3>" --depth balanced --srt-lang zh
Step Q2: 后台启动队列处理
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python queue_processor.py run --daemon
Step Q3: 轮询队列进度
cat ~/.claude/skills/video-to-canvas/queue.json
queue.json 字段说明:
status:idle|processing|completedtotal: 队列总数completed: 已完成数failed: 失败数current_index: 当前正在处理的项目索引items[].status:pending|processing|completed|failed
轮询策略:
- 每 60 秒检查一次 queue.json
- 当某个 item 的 status 变为
completed时,立即为其生成 Canvas(Phase 4) - 然后继续轮询等待下一个视频完成
- 当 queue.status 变为
completed时,所有视频处理完毕
Step Q4: 每个视频完成时生成 Canvas
对于队列中每个 completed 的视频:
- 读取其
<output>/<视频名>.md - 执行 Phase 4(分析 + 生成 Canvas)
- 写入
<output>/<视频名>.canvas
⚠️ 重要:Canvas 生成(Phase 4)在每个视频的管道完成后立即执行,不需要等待所有视频都完成。这样可以边处理边生成。
Step Q5: 处理中追加新视频
用户可以在队列处理过程中追加新视频:
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python queue_processor.py add "<新视频>"
队列处理器会在当前视频完成后自动热加载队列,开始处理新添加的视频。
Step Q6: 查看队列状态
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python queue_processor.py status
Step Q7: 清理队列
cd ~/.claude/skills/video-to-canvas/scripts && PYTHONUTF8=1 uv run python queue_processor.py clear --completed
队列模式判断规则
当用户调用 /video-to-canvas 时:
- 单个视频路径 → 使用原有单视频流程(Step 1-6)
- 多个视频路径 → 使用队列模式(Step Q1-Q7)
- 路径中包含通配符(如
*.mp4)→ 先 glob 展开,然后使用队列模式
错误处理
Python 脚本执行失败
- 检查 GEMINI_API_KEY 环境变量
- 检查视频文件路径是否正确
- 检查 FFmpeg 是否安装
转录失败
--backend auto: faster-whisper 未安装时自动回退到 Gemini- 安装 faster-whisper:
pip install faster-whisper - 或强制使用 Gemini:
--backend gemini - 使用已有转录:
--transcript existing_transcript.json - 跳过转录:
--no-transcribe
图片不显示
- 确保
screenshots/目录与.canvas文件在同一目录 - 检查图片路径是否正确(相对路径)
Canvas 节点重叠
- 增加
nodeSpacing参数 - 使用更简化的布局
高级选项
指定布局
/video-to-canvas video.mp4 --layout=mindmap
布局选项:
hierarchical: 层级布局(默认)mindmap: 思维导图flowchart: 流程图
指定深度
/video-to-canvas video.mp4 --depth=deep_dive
只生成 Canvas(已有 MD)
/video-to-canvas --canvas-only existing-notes.md
示例输出
输入 Markdown 片段
# Obsidian 核心功能
## 一、双向链接
双向链接让笔记形成网状结构,而非传统的树状层级。
### 什么是双向链接?
当笔记 A 链接到笔记 B 时,B 会自动显示反向链接。

### 如何创建?
1. 输入 `[[` 触发建议
2. 选择目标笔记
3. 按 Enter 确认

## 二、本地存储
所有文件以 Markdown 格式存储在本地。

输出 Canvas
{
"nodes": [
{
"id": "group-双向链接",
"type": "group",
"label": "一、双向链接",
"x": 0,
"y": 0,
"width": 850,
"height": 700,
"color": "4"
},
{
"id": "text-intro",
"type": "text",
"text": "双向链接让笔记形成网状结构,而非传统的树状层级。",
"x": 50,
"y": 50,
"width": 750,
"height": 60
},
{
"id": "text-what",
"type": "text",
"text": "### 什么是双向链接?\n\n当笔记 A 链接到笔记 B 时,B 会自动显示反向链接。",
"x": 50,
"y": 130,
"width": 350,
"height": 120
},
{
"id": "img-00-36",
"type": "file",
"file": "<输出目录名>/screenshots/00-36.jpg",
"x": 50,
"y": 270,
"width": 350,
"height": 220
},
{
"id": "text-how",
"type": "text",
"text": "### 如何创建?\n\n1. 输入 `[[` 触发建议\n2. 选择目标笔记\n3. 按 Enter 确认",
"x": 450,
"y": 130,
"width": 350,
"height": 120
},
{
"id": "img-00-48",
"type": "file",
"file": "<输出目录名>/screenshots/00-48.jpg",
"x": 450,
"y": 270,
"width": 350,
"height": 220
},
{
"id": "group-本地存储",
"type": "group",
"label": "二、本地存储",
"x": 950,
"y": 0,
"width": 500,
"height": 450,
"color": "5"
},
{
"id": "text-local",
"type": "text",
"text": "所有文件以 Markdown 格式存储在本地。",
"x": 1000,
"y": 50,
"width": 400,
"height": 60
},
{
"id": "img-01-16",
"type": "file",
"file": "<输出目录名>/screenshots/01-16.jpg",
"x": 1000,
"y": 130,
"width": 400,
"height": 280
}
],
"edges": [
{
"id": "edge-1",
"fromNode": "text-what",
"toNode": "img-00-36",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "edge-2",
"fromNode": "text-how",
"toNode": "img-00-48",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "edge-3",
"fromNode": "text-local",
"toNode": "img-01-16",
"fromSide": "bottom",
"toSide": "top"
}
]
}
验证清单
生成 Canvas 后检查:
- [ ] 所有节点 ID 唯一
- [ ] Edge 引用的节点存在
- [ ] 图片路径有效(相对路径)
- [ ] 无节点重叠
- [ ] JSON 格式正确
- [ ] Group 正确包含子节点
Content Repurposer
Content
Transforms a single piece of content into platform-adapted publications.
SEO Blog Post Writer
Content
Writes SEO-optimized blog posts with proper structure and keywords.
YouTube Script Writer
Content
Writes engaging YouTube scripts with hooks, structure, and retention.