URL2Video(ZCode 内置浏览器录 WebM → ffmpeg 转 MP4)
把 URL 变成可交付、可继续作为 video2code 输入的 MP4。浏览、截图、交互和录像都在同一个
ZCode 内置 WebView 中完成;不要安装/启动 Playwright、Chrome 或其它外部浏览器。
录制环节只能产出 WebM(ZCode 录制链路的原生格式),所以本 skill 分两段:
内置浏览器录 .webm → ffmpeg 转 .mp4。最终交付物和下游 video2code 流程都走 MP4。
前提与边界
- 必须同时使用 ZCode 官方
control-browserskill,并完整遵守其 bootstrap、backend 选择、 tab 恢复和页面安全规则。若iabdescriptor 或BrowserRecordingAPI不可用,明确报告版本 不匹配,不要回退到 shell browser。 - 每个
node_replJavaScript 调用都是新 kernel;每次都按control-browser重新 bootstrap, 再用await agent.browsers.get("iab")。 - 录像只接受 Browser Use SDK 的受限 action DSL,不执行任意页面脚本。
recording.status的outputPath必须是工作区内相对路径且以.webm结尾,例如recordings/source-home.webm;ZCode 只落 WebM,传.mp4会被拒绝 —— 转 MP4 是录制之后 由 ffmpeg 单独做的一步,不要试图让 ZCode 直接输出 MP4。- 转码必须真的调 ffmpeg 重编码成 H.264 MP4。禁止只把
.webm改名成.mp4伪装格式 —— 容器和编码不匹配,下游ingest_video/clip_video会解码失败或抽出错帧。 - 原始
.webm转码后保留,作为原始录制证据;.mp4是交付物。两个文件同名不同扩展。 - 因此 ffmpeg 是本 skill 的硬依赖(不再是可选后处理)。若 ffmpeg 不可用,先按
env-setup装好再继续;不要跳过转码直接交 WebM。
状态与时序
URL
│ tabs.new / goto / waitForLoadState
▼
同一 IAB WebView ── domSnapshot / screenshot 踩点
│
│ tab.recording.start({ actions, ... }) → recordingId
▼
preparing → capturing → finalizing → completed
│
└─ recording.status(id, { outputPath })
→ 工作区 recordings/<name>.webm
│ ffmpeg -c:v libx264
▼
recordings/<name>.mp4 ← 交付物
工作流
1. 踩点
- 用 IAB 打开 URL,等待
domcontentloaded。 - 用
domSnapshot()识别可交互元素;视觉布局、canvas/WebGL、图标含义再用 screenshot。 - 分屏滚动检查长页,记录稳定选择器。优先:
#id、[data-testid="..."]、稳定属性, 再考虑短 class 或文本 selector。禁止:nth-child()和靠猜的选择器。 - 为每个控件记账:
click、hover-only(导航/付费/外部副作用)、skip(原因)。
2. 编排录像
tab.recording.start(options) 支持:
viewport:{width,height},默认建议1280×800fps:1..60,建议25maxDurationMs:1000..90000settleMs: 开始录制后、动作前的静置时间showCursor: 是否叠加可见录制光标actions: 最多 500 个受限动作
动作格式:
[
{ type: "wait", durationMs: 800 },
{ type: "move", x: 240, y: 300, durationMs: 500 },
{ type: "hover", selector: "[data-testid=hero-card]", durationMs: 500, delayAfterMs: 1000 },
{ type: "click", selector: "#theme-toggle", delayAfterMs: 1500 },
{ type: "type", selector: "#search", text: "motion", delayAfterMs: 500 },
{ type: "scrollTo", selector: "#features", durationMs: 900, delayAfterMs: 1000 },
{ type: "scroll", deltaY: 700, durationMs: 700 },
{ type: "wheel", deltaY: 240, times: 3, intervalMs: 120 },
{ type: "drag", path: [{x:100,y:300},{x:300,y:260},{x:500,y:320}], durationMs: 900 },
{ type: "waitFor", selector: "#result", state: "visible", timeoutMs: 3000 }
]
编排规则:
- 开头留 800–1500ms,保证入口动效完整入镜。
- 点击后留 1500–2500ms,让响应和最终状态都可读。
- pointer-coupled 效果要包含宽幅
move、静止和再次移动;hover 不是 click 的替代品。 - 长页按章节
scrollTo,每步不超过一个 viewport;sticky/parallax 再接 2–3 个渐进scroll。 - canvas 创作工具用坐标
move/drag,每种工具/颜色/线宽留下不重叠样本。 - 不点击会跳出页面、花费账户余额、发消息/下单/注册或产生其它外部副作用的控件。
maxDurationMs是 runaway guard;总动作时长必须低于它,并留出加载余量。
3. 开始并轮询
在已核验的目标 tab 上开始:
const job = await tab.recording.start({
viewport: { width: 1280, height: 800 },
fps: 25,
maxDurationMs: 60000,
settleMs: 1200,
showCursor: true,
actions,
});
job;
保存 job.id。在后续 fresh JS 调用中先恢复并核验同一个 tab,然后轮询;只有最后一次
status 传 outputPath,Host 才会把 main 临时产物落到当前 local/remote workspace:
const job = await tab.recording.status(recordingId, {
outputPath: "recordings/source-home.webm",
});
job;
running: 根据phase(preparing/capturing/finalizing)继续轮询。completed: 必须同时看到artifact.path、durationMs、frameCount;该 WebM 是待转码的原始 录制产物(不是最终交付物),并记下durationMs/frameCount供转码后核对。failed: 修正失败原因后最多重录两次。cancelled: 不可作证据;需要时调用tab.recording.cancel(recordingId)。
4. ffmpeg 转 MP4
录制拿到 .webm 后,用 shell 跑一次 ffmpeg 转成 H.264 MP4;这一步是主流程的必经环节,
不是可选后处理:
ffmpeg -y -i recordings/source-home.webm \
-c:v libx264 -preset veryfast -crf 20 \
-pix_fmt yuv420p -r 25 \
-movflags +faststart -an \
recordings/source-home.mp4
各参数为什么是这样(改之前先想清楚):
-c:v libx264+-pix_fmt yuv420p: 下游ingest_video/clip_video与各家模型的视频输入 对 H.264/yuv420p 支持最稳;VP8 与 yuv444 都可能被拒或解错。-r 25: 必须与录制时的fps一致,作用是把输出固定成 CFR。录制产物的时间戳可能不均匀 (VFR);下游是按秒定位取帧(still_crops(video,[t])、trace 里的@t), VFR 会让 seek 落到邻近帧上、时间轴与实际动作错位。输出侧给-r就够了,不要加-vsync cfr(ffmpeg 6.1+ 已废弃并告警),也不要用-fps_mode cfr(ffmpeg 4.x 不认这个参数)。-an: 录制无音轨,显式丢弃避免容器里留空音频流。-movflags +faststart: moov 前置,边下边解,供审片工具快速 seek。-crf 20: 视觉近无损。动效/渐变出现明显 banding 时可降到 18;不要为省体积上调到 28 以上, 复刻要靠这段视频判断颜色和缓动。
转完必须核对一次,别把坏文件交下去:
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,width,height,nb_frames,r_frame_rate \
-show_entries format=duration -of default=noprint_wrappers=1 \
recordings/source-home.mp4
codec_name=h264,duration与录制回执的durationMs相差应在 ~0.5s 内。- ffmpeg 非零退出、输出文件不存在或时长为 0 → 转码失败:报告 ffmpeg 的 stderr,不要交 WebM 冒充 MP4,也不要改扩展名蒙过去。
.webm保留在原地,作为原始录制证据与转码失败时的回退。
5. 审片与交接
用 video MCP 的 ingest_video/clip_video 审查转好的 MP4(不是 WebM):入口、滚动、
hover/click、pointer/canvas 动作是否清晰完整。交付 recordings/<name>.mp4 作为源视频;
若还要复刻,就以这个 MP4 连同 video2code + web-replicate 走原流程。
审片发现录像本身不合格(漏动效、时长不够、动作被截断)→ 回到第 2 步调整 action DSL 重录, 不要靠调转码参数补救,也不要安装 Playwright 作为旁路。若 MP4 能播但 video MCP 读不了, 先按上面 ffprobe 的输出定位是转码问题还是工具限制,如实报告。