Communitygithub.com

adityaleksono/walkthrough-video

生成带有AI旁白、字幕、可见光标及高亮指示圈的网页应用解说视频——通过运行应用、编写脚本、使用Playwright录制并借助ffmpeg合成MP4。

walkthrough-video 是什么?

该技能能够将“解释这个功能”的请求自动转化为一段专业的MP4演示视频,其中包含真实的应用界面、AI生成的语音解说、同步字幕、模拟人工操作的光标移动,以及围绕当前讲解控件的环形高亮。它首先要求用户明确目标和受众,然后基于实际应用界面验证操作流程,再编写叙事脚本等待用户审批——只有在脚本获得批准后,才会生成语音、录制屏幕并最终合成视频。该技能支持Playwright进行录制、ffmpeg进行合成,并兼容Murf、ElevenLabs或macOS内置TTS等多种语音引擎。其核心价值在于显著降低制作产品演示、功能导览或新手引导视频所需的工程投入,同时确保视频内容准确、节奏恰当且易于修改。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/adityaleksono/walkthrough-video-skill/tree/HEAD/skills/walkthrough-video

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

解说视频

将“解释这个功能”转化为一段简短的MP4:真实的应用画面、讲解的语音、字幕、像真人一样移动的光标,以及围绕当前讨论控件的环形高亮。用户负责简要说明和审核,你完成其余所有工作。

整个流程中有一个比任何其他环节都更重要的审核关卡:在录制任何内容之前,用户必须批准叙事脚本。 修改措辞在脚本上只需数秒,在视频上则需要数分钟;而一个错误的声明在纸面上发现远比在视频中纠正成本低得多。

所需条件

  • 应用的源代码,以及一个可以打开并登录的实例(本地或开发/预发环境)。切勿对着生产环境录制:每一次点击都会产生实际影响。
  • 应用项目中的Playwright(node_modules/playwright 或 @playwright/test)。大多数包含E2E测试的项目已经安装了它。如果没有:npm i -D playwright && npx playwright install chromium。
  • 系统PATH中可用的 ffmpeg 和 python3。
  • 用于最终语音的文本转语音密钥(Murf 或 ElevenLabs)。如果没有,--provider say 会使用macOS语音,这适用于草稿录制。参见 references/voices.md。

首先检查这些条件,并明确指出缺少什么。不要要求用户将API密钥粘贴到聊天中:请他们将其放入环境变量或git忽略的文件中,并告诉你位置。

工作流程

1. 接收简要说明

你需要四件事。仅询问请求中未明确提及的内容。

  • 目标和受众。“让CTO了解新的权限页面”制作出的视频与“教新成员如何发布应用”完全不同。受众决定了视频长度、词汇以及需要解释的程度。
  • **操作流程。**要展示的内容及顺序。如果用户给出了流程,请进行改进并说明更改内容和原因:更好的顺序、遗漏的步骤、或多余的步骤。用户要求的是视频,而不是他们列表的转述。
  • **时长。**默认长度为60-120秒,聚焦一个主题。如果流程需要更长时间,建议制作两个视频。
  • **语音。**如果用户有偏好,指定提供商和音色。否则使用默认设置。

2. 在动笔之前,先在真实应用中走通流程

运行应用,使用演示账号登录,然后使用Playwright或浏览器亲自执行流程中的每一步。你需要验证叙述中将要声明的每一个事实是否属实。

这是该技能提供大部分价值的地方。功能通常是根据记忆或规格说明来描述的,而实际应用可能略有不同。如果应用的行为与用户所说的不符,请停下来并告知用户你所观察到的实际情况。不要编写掩盖问题的叙述,也不要悄悄跳过该步骤。

同时记录下你看到的按钮和菜单上的确切标签。叙述中应使用屏幕上的实际文字。

3. 植入演示数据

在空账号或状态不完整的账号上录制解说视频,看起来就像一份bug报告。创建故事所需的数据:一个组织、几个不同角色的用户、具有可信名称的项目。仅使用虚构的名称(Acme、Northwind、example.com):这些视频可能会被转发。

记录你创建或修改的所有内容。在第9步中你需要恢复它们。

4. 撰写叙事脚本,然后暂停等待审核

将脚本写成表格形式,并保存在视频将要存放的位置旁边,文件名为 <nn>-<slug>.script.md:

| # | 屏幕上显示的内容 | 旁白 | ~秒 |
|---|---|---|---|
| 1 | 任务列表静止状态。环高亮列表。 | 这是团队任务列表。/团队中的每个人都能看到相同的任务。 | 5 |
| 2 | 光标移至 **添加任务**,表单打开。输入名称,点击 **保存任务**。 | 要添加一个任务,选择添加任务,/给它一个名称,/然后保存。 | 7 |

/ 符号用于将某个场景的旁白分割成字幕行。在撰写之前,请阅读 references/narrative.md:其中涵盖了节奏、每行长度、前后对比卡片以及容易让观众难以理解的常见错误。

向用户展示表格并等待。说明预期的总时长。如果你更改了他们的流程,请在此处说明。只有在用户批准后才能继续;如果他们要求修改,请再次发送修改后的表格。

5. 生成语音

将已批准的叙述写入 scenes.json,每个表格行对应一个条目,在 / 标记处分割行:

[{ "id": "02", "lines": ["要添加一个任务,选择添加任务,", "给它一个名称,", "然后保存。"] }]
python3 scripts/tts.py scenes.json --audio-dir work/audio --provider murf

这会为每个场景写入一个音频片段和 durations.json。语音生成特意放在录制之前:每个场景需要在屏幕上停留与音频片段相同的时间,因此必须首先确定片段长度。重新运行仅会重新生成文本发生变化的场景。

6. 编写录制脚本并进行空跑测试

复制 example/record.cjs 并根据当前流程重写其场景。scripts/walkthrough.cjs 提供了以下辅助功能:

辅助函数用途
scene(id, async () => {...})一个带有旁白的场景。显示字幕并保持画面直到语音结束。
clickAt(locator)将光标滑动到该元素,显示按下动作,然后点击。请使用此函数,不要用 locator.click(),否则操作会在没有任何可见原因的情况下发生。
moveTo(locator)指向某个元素但不点击。
typeInto(locator, text)点击输入框并以可读的速度输入文本。
ring(locator) / ring(null)高亮显示语音正在讨论的内容;当语音切换到下一部分时移除高亮。
step('第2步,共4步 · 保存')左下角的标签,告诉观众他们当前所处的位置。
card({ title, subtitle })全屏标题卡片,用于结束场景。
card({ before, after })前后对比卡片。card(null) 隐藏它。
login: async (page) => {...}record() 的可选参数。在镜头外完成登录,使视频从故事开始的地方开始。

优先使用角色和标签定位器(getByRole、getByLabel)而不是CSS选择器:它们能适应样式变更,并且这些名称就是旁白中使用的名称。在运行时从屏幕上读取名称,因为不同环境中的数据可能不同。

首先使用 dry 模式运行:

node record.cjs <app-repo> work/rec work/audio/durations.json dry

空跑测试不进行任何录制,也不等待语音,因此错误的选择器只会浪费几秒钟,而不是一次完整的录制。修复问题直到每个场景都能正常运行。

7. 录制并合成

node record.cjs <app-repo> work/rec work/audio/durations.json
python3 scripts/mux.py --rec work/rec --audio-dir work/audio --output walkthrough-videos/<nn>-<slug>.mp4

录制器会打印每个场景的片段长度,以及该场景操作所花费的时间。如果某个场景在语音结束后还有数秒的静默,说明该场景的操作过多,而语音内容过少:要么精简操作,要么让语音多说一些内容。

mux.py 会输出MP4文件、对应的 .srt 字幕文件以及 <name>.sheet.jpg 联系表。

8. 先自己检查,再交给用户

打开联系表:每个场景一帧。检查环形高亮是否指向了错误的元素或缺失、字幕是否覆盖了正在讨论的内容、场景是否出现在错误的屏幕上、是否出现了开发者工具徽标或错误提示框、以及是否暴露了真实的个人数据。如果联系表分辨率不够,使用ffmpeg提取单帧。

然后将视频交给用户,并附带说明:视频长度、与已批准叙述相比是否有任何更改(如果有),以及你无法验证的任何内容。

当用户要求修改时,首先回到叙事脚本。 修改表格,展示给用户,获得批准,然后仅重新生成发生变化的部分。大多数反馈(“太快了”、“我搞不清楚哪里变了”、“还要提一下滤镜”)其实是对脚本的反馈,仅仅在录制中修正会产生一段与脚本不再匹配的视频。

9. 恢复环境

撤销录制过程中所做的更改,使用第3步中的清单以及录制本身所点击的内容:切换过的设置、创建或删除的项目、演示账号的状态。然后告知用户哪些内容已恢复,以及你故意保留了什么(例如,可能用于下一个视频的种子数据)。如果某些内容无法恢复,请说明。

文件

交付物应集中存放在用户容易找到的同一个地方,默认在项目的 walkthrough-videos/ 目录下:

walkthrough-videos/
  03-add-a-task.mp4
  03-add-a-task.srt
  03-add-a-task.script.md

将工作文件(scenes.json、音频、原始录制文件、录制脚本)保存在代码库之外或git忽略的文件夹中。它们体积较大,且录制脚本中包含特定时刻应用的选择器。脚本和 .srt 文件值得保留:它们能使后续的修改变得非常容易。

可能出现的问题

  • 原生控件无法录制。 原生的 <select> 下拉框、文件选择器或浏览器对话框是由浏览器在页面外部绘制的。使用 moveTo 指向该控件,使用 selectOption / setInputFiles 设置值,并在旁白中说明所选的选项。
  • 导航后覆盖层消失。 覆盖层会在每次页面加载时重新创建,当前的步骤和字幕会被恢复。如果某个页面完全替换了 document.body,请在页面稳定后再次调用 step()。
  • 页面尚未就绪时场景就开始。 在调用 scene() 之前(而不是内部)等待场景所需的元素就绪(await locator.waitFor()),以免加载时间也被配上旁白。
  • 开头几秒是空白页面。 mux.py 会裁剪掉第一个场景之前的所有内容。请将导航和等待操作放在 scene('01') 之前完成。
  • 在镜头前进行登录。 请使用 login 选项。没有人需要观看输入密码的过程,而且凭证绝不能在共享视频中可见。

相关技能