Communitygithub.com

项目演示视频技能

为任何软件项目、黑客马拉松参赛作品或产品,以代码的形式制作带旁白和字幕的1080p演示视频(3分钟或更短),使用 Playwright 和 ffmpeg-static。

项目演示视频技能 是什么?

该技能允许 AI 代理通过代码方式构建一个完整的演示视频,将人工作业转变为完全自动化的、可重现的流水线。它通过 Playwright 录制真实的应用程序交互,使用 ffmpeg-static 进行编码,并为旁白生成字幕。视频会展示应用程序、终端输出、源代码和图表,确保演示 100% 真实无误。该技能提供了一种“以代码为视频”的方法,任何更改后都可以在几分钟内重新渲染视频,非常适合需要定期重新录制演示的正在进行的项目。它包含详细的阶段,指导代理和开发者完成从分析和录制到最终视频组装和 QA 的整个过程。该技能专为 Claude Code 等 AI 编码代理设计,可在安装了 Node.js 和 Playwright 的 Windows、macOS 或 Linux 系统上运行。

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

在你喜欢的 AI 中提问

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

文档

project-demo-video

将演示视频作为代码构建,以便在任何更改后几分钟内重新渲染。屏幕上的所有内容都是真实的:正在运行的应用程序、真实的命令输出、真实的源文件。没有任何模拟。

此技能编写成让能力较弱的编码代理可以逐步跟随。请按顺序执行各个阶段。每个阶段都有一个完成条件行:在满足条件之前,请勿继续。从 templates/ 复制文件,而不是重新编写它们。

安装:将整个文件夹(SKILL.md 和 templates/)复制到代理的技能文件夹中,例如 ~/.claude/skills/project-demo-video/,或者只需将代理指向 SKILL.md。

你产出的内容

输出备注
<name>-voiced.mp41920x1080,30 fps,H.264 + AAC,字幕烧录,带语音
<name>-captions-only.mp4相同画面,静音音轨(用户可以选择此版本或录制自己的声音)
<name>.srt字幕文件,文本内容与旁白相同
thumbnail.png1280x720
narration.md, shotlist.md带有真实时间戳的脚本;每个场景及其来源
frames/你实际查看过的 QA 帧
repo/已更改或新增的项目文件,在其仓库路径下镜像
COPY_LIST.md, README-snippet.md, REPORT.md要复制到哪里,一个“查看它如何工作”的 README 部分,已完成的内容以及存在风险的内容

0. 你必须遵守的规则

  1. 只读源文件。 切勿写入、提交或推送到用户的真实仓库。在副本中工作(robocopy/cp,排除 node_modules、.git、构建输出),然后在其中执行 npm ci。将值得保留的更改保留在输出文件夹下,镜像仓库路径,并附上 COPY_LIST.md。由其他工具或人员完成 Git 操作。
  2. 屏幕上要诚实。 任何模拟的内容(设备、连接器、付款、模型)都必须在视频中标记为“模拟的”。切勿暗示存在不存在的硬件、部署或用户。复制项目自己的“真实与模拟”表格;如果 README 有误,请在报告中说明,并且不要重复错误的主张。
  3. 屏幕上或你交付的文件中不得包含机密信息: 令牌、API 密钥、.env 内容、私钥、真实电子邮件地址。项目文档中提到的开发令牌(例如 Bearer dev-alice)是可以的。展示代码时,选择排除机密信息的行范围。
  4. 在帧、旁白、字幕或交付的文档中,不得提及编写代码的 AI 工具或其供应商,除非用户另有说明。作为产品配置一部分的运行时模型 ID 可以作为模型名称出现。在完成前使用 grep -ri 检查。
  5. 遵守竞赛规则。 阅读时长限制(通常“少于 3:00”;目标设定为 2:50)和所需技术规则。视频必须展示所需技术真正在运行,而不仅仅是提及它。
  6. 免费套餐,不部署,不购买。 请勿部署、创建云资源或花钱。如果某个步骤需要用户提供凭证或录制内容,请直接请求一次,然后继续处理所有其他内容。
  7. 仅向用户请求 凭证、他们自己的录制内容或真正属于他们的选择(例如使用哪种声音)。自行决定所有其他事项并告知你的决定。

1. 前提条件(检查,不要假设)

node --version          # 22+(推荐 24;.mjs 文件必须是纯 JavaScript,没有 TypeScript 转换)
  • 在项目副本中:必须存在 @playwright/test(安装了 Chromium)和 ffmpeg-static。检查:node -e "import('@playwright/test').then(async m=>{const b=await m.chromium.launch();console.log(b.version());await b.close()})" 和 node -e "import('ffmpeg-static').then(m=>console.log(m.default))"。如果缺少并且项目禁止添加新依赖,请将它们放在一个单独的工具文件夹中并指向它。
  • 语音:Windows PowerShell 使用 System.Speech(内置)。在 macOS 上使用 say,在 Linux 上使用 espeak-ng;然后你必须调整 templates/lib/tts.mjs(仅 synthAll 和 tts.ps1 是操作系统相关的)。
  • 没有 ffprobe 随 ffmpeg-static 提供:从 WAV 标头(templates/lib/tts.mjs 中的 wavSeconds)和录制器自己的计时来测量时长。
  • Windows shell 注意事项:对于任何包含反斜杠、正则表达式或 $ 的文件,最好使用写入和编辑工具。使用内联 python -c/heredoc 重写 JS 会静默地损坏转义字符。前台 sleep 在某些代理中被阻止:使用 until grep -q "done" log; do sleep 10; done 来等待。

2. 阶段

阶段 A:要求和证明映射

阅读规则页面、README、项目自己的文档以及任何故事情节说明。在你的笔记中编写一个表格:

评委必须相信的是视频中证明的地方场景
所需技术在代码中被调用真实调用可见:请求日志、终端、代码摘录...
体验有效应用程序在浏览器中运行...
什么是真实 vs 模拟项目自己的表格...

完成条件: 每一行都有对应的场景。

阶段 B:工作空间

  1. 将仓库复制到临时文件夹,然后安装:npm ci。
  2. 将 templates/ 复制到副本中的 video/(保持结构:video/lib、video/pages、video/scenes、video/final.mjs、video/narration.mjs,...)。将 "video:final": "node video/final.mjs" 添加到 package.json 的脚本中。不要添加依赖。
  3. 将 video/out/ 添加到副本的 .gitignore 中。

阶段 C:预运行真正的应用程序并查找错误(在录制任何内容之前执行此操作)

按照评委的方式启动应用程序(npm run demo 或 README 命令)在其文档记录的默认端口上,以便屏幕上显示的每个命令都是字面上的命令。使用一个小的 Playwright 脚本驱动它,在故事的每个步骤之后以 1920x1080 分辨率截取屏幕截图,并查看它们。检查:

  • 登录、空白面板、红色错误横幅、控制台错误;
  • 故事需要的每个状态是否实际渲染(参考项目的第一次运行显示了一个失败的登录、一个空的收件箱和一个缺失的关键视图,所有这些都被旧版本的草稿视频隐藏了);
  • 文本是否重复或截断。

在副本中修复真正的错误,最小化修复,运行项目自己的检查(npm run verify、测试、类型检查),并将每个修复记录在 COPY_LIST.md 中。如果应用程序无法显示网络流量,请添加一个小的可选钩子(请求日志),而不是伪造一个。 完成条件: 整个故事连续干净地运行两次,并且项目的检查通过。

阶段 D:故事板和旁白

  1. 挑选场景。参考故事(约 2:50):
场景时长展示内容
钩子12 秒一个更改波及多个承诺(动画卡片)
启动6 秒启动一切的一条命令,真实输出
模拟(应用程序)60 秒8-10 个节拍;在 2 个节拍上打开请求日志
证明终端17-30 秒针对同一服务器的官方客户端或 CLI;身份验证检查
代码导览16 秒3-4 个证明所需技术的真实代码摘录
架构、云堆栈、真实 vs 模拟30 秒图表、堆栈来源、诚实表格、开源卡片
结束12 秒标签线、仓库链接、构建致谢、“X 是模拟的”页脚
  1. 编写 video/narration.mjs:scenes = [{ id, title, segs: [{ id, text }] }]。每个 seg 是一句口头台词和一条字幕(text 按原样显示)。规则:大约每秒 2.4 个单词;总口语单词数 330-380;每行一个想法;任何屏幕上的卡片上不超过约 12 个单词;使用句子大小写;不要夸张。
  2. 在 say 表中仅为语音引擎添加发音提示(首字母缩略词拼写出来,“Alexa+” 作为 “Alexa plus”);切勿更改字幕文本。 完成条件: 旁白读起来像一个陌生人能理解的故事,并且证明映射中的每一行都已被讲述或显示。

阶段 E:调整模板

仅编辑项目特定的内容:

文件编辑内容
video/narration.mjs你的场景和台词
video/scenes/sim.mjs你的应用程序的步骤:要输入或点击的内容、选择器、相机缩放(lens(...)、zoomWire)
video/scenes/static.mjs终端命令(CMD)、codeBlocks(文件 + 第一行的正则表达式 + 行数 + 高亮正则表达式)、钩子/结束页面调用
video/proofs.mjs、video/proof-auth.mjs证明你的主张的真实命令;它们仅打印状态码和错误名称
video/pages/*.html文本、颜色、磁贴、图表框、表格行、链接
video/lib/overlay.mjs针对你的应用程序布局的 SIM_CSS 选择器(参见布局规则)、滚动条的数据源
video/final.mjsLIMIT 常量和场景顺序(顺序来自 narration.mjs)
video/thumbnail.mjs、video/docs.mjs标题和 SHOT 表格

除非某些内容损坏,否则不要编辑 lib/recorder.mjs、lib/director.mjs、lib/tts.*。

阶段 F:捕获真实的证明输出

node video/proofs.mjs 启动一个新的服务器,运行真实的命令(官方检查器或客户端 CLI、身份验证检查、安装/启动输出),并将它们打印的内容保存到 video/out/proofs.json 中。终端场景回放这个捕获的输出在一个 HTML 终端中,以可读的尺寸显示。规则:显示真实的命令文本(仅缩短包路径);明显截断非常长的输出(一个暗淡的 "... N lines ..." 行),并在芯片中放入 "Recorded run";切勿打印令牌;美化 JSON,如果你这样做,请说明。

阶段 G:旁白音频

默认(无需账户):node video/final.mjs --rate 2 使用内置的 Windows 语音(Microsoft Zira)为每行生成一个 WAV 并测量每个的长度。速率 1 对于参考脚本来说大约产生 150 秒的语音,速率 2 大约 139 秒,速率 3 太快。选择能保持整个视频低于限制的最慢速率。 可选的人性化声音:参见附录 A。以下所有内容在任一种情况下工作方式相同。

阶段 H:录制和组装

node video/final.mjs --rate 2                   # 所有步骤:TTS、证明、录制所有场景、组装两个版本
node video/final.mjs --rate 2 --reuse-proofs    # 跳过重新运行证明
node video/final.mjs --scenes sim,close --reuse-tts --reuse-proofs   # 仅重新录制某些场景
node video/final.mjs --assemble-only            # 仅重新封装(字幕、音频)而不重新录制
node video/final.mjs --tts-dir video/out/tts-human --name my_video --single    # 从另一个音频文件夹构建

完整运行需要 6-10 分钟;在后台运行并轮询日志。如果总时长达到 180 秒或更长,脚本会失败。目标设定为 170 秒。 重要提示:如果你更改旁白文本或语音,时长会改变,因此每个场景都必须重新录制(标记来自时长)。仅更改一个场景的视觉效果意味着 --scenes <id> --reuse-tts。

阶段 I:质量保证(绝不跳过,绝不相信日志)

  1. 从最终 MP4 的每个旁白行结束处提取帧:ffmpeg -ss <markEnd-0.3> -i final.mp4 -frames:v 1 frame.png(时间线在 video/out/timeline.json)。查看它们。检查 1080p 下的可读性、空白空间、裁剪的文本、字幕位置和大小、同步性(字幕的事件在屏幕上)、无机密、无供应商提及、存在诚实标签。
  2. ffmpeg -i final.mp4 -af volumedetect -f null -:平均值约为 -22 dB,最高值低于 0 dB。
  3. 两个版本中的时长均低于限制;两个文件都有视频和音频流。
  4. grep -ril "<vendor words>" video/pages video/narration.mjs <output docs> 返回空值,除了允许的模型 ID。
  5. 再次运行项目的检查(npm run verify;如果 UI 更改过,则运行 e2e 测试)。
  6. 修复并重新渲染直到全部通过。

阶段 J:交付物

使用 node video/docs.mjs <outdir> 生成 narration.md 和 shotlist.md(真实时间戳)。复制 MP4 文件、SRT 文件、缩略图(node video/thumbnail.mjs <png>)、QA 帧以及更改过的仓库文件(原始文件和你的副本之间所有不同的文件,在其仓库路径下)。编写 COPY_LIST.md(首先列出 Bug 修复,然后是新文件、建议的提交消息、“运行构建并在复制后验证”)、README-snippet.md(运行命令、入口文件:行、客户端文件:行、技能文件、视频链接占位符)和 REPORT.md(已完成的内容、用户必须做什么、风险)。明确说明你无法检查的内容(例如,你无法收听语音)。

3. 流水线的工作原理(在更改之前阅读)

录制器(lib/recorder.mjs)。Playwright 自己的 recordVideo 会限制比特率并使小文本模糊,因此此技能使用 Chromium 屏幕广播:JPEG 帧(质量 92)在到达时被打上时间戳、写入磁盘,并以恒定的 30 fps 使用 libx264 crf 17 进行编码。静态屏幕产生的帧很少;最后一帧会被保持。时间戳来自一个时钟(performance.timeOrigin + performance.now()),与导演使用的时钟相同。

导演(lib/director.mjs)。每个旁白行是一个片段。d.seg(id, actions, {tail}) 记录开始时间,运行屏幕上的操作,然后保持直到该行播放完毕再加上 tail * 0.45 秒(尾部会全局修剪以适应限制)。标记(id、开始、结束、文本)被保存到 video/out/<scene>.marks.json。由于音频后来在记录的标记处放置,因此画面和语音不会漂移。节拍的规则:在片段内执行操作,并确保操作在旁白结束前完成;如果行比操作短,则将其拆分为两行(这就是为什么一个故事节拍变成了 sim9 和 sim10)。

音频(final.mjs)。每个 WAV 使用 adelay 延迟到其全局开始时间,使用 amix=normalize=0 混合,使用 alimiter 限制,并使用 apad=whole_dur=<total> 填充(如果没有填充,-shortest 会切断视频末尾)。

字幕。 提示是旁白文本在句子末尾处拆分(诸如 "2.0" 之类的小数点会被保护)以及在超过 82 个字符的逗号处拆分,时间按长度比例分配。.srt 文件会被交付。烧录版本使用一个生成的 ASS 文件,其中包含 PlayResX: 1920, PlayResY: 1080 和字体大小 36。不要使用 force_style 烧录 SRT 文件:libass 会针对一个 288 像素高的画布进行缩放,导致文本大约变为四倍大。

覆盖层(lib/overlay.mjs),仅注入到录制的应用程序页面中(应用程序本身不被编辑)

相关技能