@remixmate/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/argv.d.ts +15 -0
- package/dist/argv.js +51 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +83 -0
- package/dist/handlers/gen-image.d.ts +13 -0
- package/dist/handlers/gen-image.js +161 -0
- package/dist/handlers/gen-voice.d.ts +16 -0
- package/dist/handlers/gen-voice.js +110 -0
- package/dist/handlers/index.d.ts +13 -0
- package/dist/handlers/index.js +14 -0
- package/dist/handlers/template-bind.d.ts +15 -0
- package/dist/handlers/template-bind.js +149 -0
- package/dist/http.d.ts +52 -0
- package/dist/http.js +110 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/manifest.json +260 -0
- package/dist/progress.d.ts +23 -0
- package/dist/progress.js +15 -0
- package/dist/registry.d.ts +33 -0
- package/dist/registry.js +72 -0
- package/dist/runner.d.ts +17 -0
- package/dist/runner.js +63 -0
- package/package.json +54 -0
- package/skills/export-jianying/SKILL.md +251 -0
- package/skills/export-jianying/scripts/gen_jianying_draft.py +1099 -0
- package/skills/export-jianying/skill.json +24 -0
- package/skills/export-jianying/version.json +7 -0
- package/skills/gen-digital-human/SKILL.md +211 -0
- package/skills/gen-digital-human/scripts/gen_digital_human_video.py +574 -0
- package/skills/gen-digital-human/skill.json +23 -0
- package/skills/gen-digital-human/version.json +7 -0
- package/skills/gen-image/SKILL.md +130 -0
- package/skills/gen-image/skill.json +22 -0
- package/skills/gen-image/version.json +7 -0
- package/skills/gen-script/SKILL.md +283 -0
- package/skills/gen-script/scripts/gen_script.py +1208 -0
- package/skills/gen-script/skill.json +41 -0
- package/skills/gen-script/version.json +7 -0
- package/skills/gen-video/SKILL.md +170 -0
- package/skills/gen-video/scripts/gen_video.py +591 -0
- package/skills/gen-video/skill.json +24 -0
- package/skills/gen-video/version.json +7 -0
- package/skills/gen-voice/SKILL.md +104 -0
- package/skills/gen-voice/skill.json +21 -0
- package/skills/gen-voice/version.json +7 -0
- package/skills/prepare-video-assets/SKILL.md +192 -0
- package/skills/prepare-video-assets/scripts/prepare_video_assets.py +57 -0
- package/skills/prepare-video-assets/skill.json +23 -0
- package/skills/prepare-video-assets/version.json +7 -0
- package/skills/render-video/SKILL.md +187 -0
- package/skills/render-video/scripts/_chrome_vendor.py +305 -0
- package/skills/render-video/scripts/_video_probe.py +174 -0
- package/skills/render-video/scripts/_vod_polling.py +86 -0
- package/skills/render-video/scripts/remote_renderer_client.py +236 -0
- package/skills/render-video/scripts/render_video.py +2288 -0
- package/skills/render-video/scripts/upload_video.py +193 -0
- package/skills/render-video/skill.json +21 -0
- package/skills/render-video/version.json +7 -0
- package/skills/template-bind/README.md +63 -0
- package/skills/template-bind/SKILL.md +234 -0
- package/skills/template-bind/scripts/check_contracts.py +196 -0
- package/skills/template-bind/scripts/match_template.py +215 -0
- package/skills/template-bind/scripts/registry_loader.py +379 -0
- package/skills/template-bind/scripts/render_job_client.py +162 -0
- package/skills/template-bind/scripts/sync_registry.py +44 -0
- package/skills/template-bind/scripts/template_paths.py +72 -0
- package/skills/template-bind/skill.json +20 -0
- package/skills/template-bind/version.json +7 -0
- package/skills/template-bind/video_dsl/README.md +97 -0
- package/skills/template-bind/video_dsl/runtime/__init__.py +11 -0
- package/skills/template-bind/video_dsl/runtime/dsl_validator.py +495 -0
- package/skills/template-bind/video_dsl/runtime/prompt_enhancer.py +274 -0
- package/skills/template-bind/video_dsl/runtime/template_binder.py +87 -0
- package/skills/template-bind/video_dsl/runtime/timeline_compiler.py +312 -0
- package/skills/template-bind/video_dsl/schema/render-plan-v1alpha1.json +193 -0
- package/skills/template-bind/video_dsl/schema/template-binding-v1alpha1.json +122 -0
- package/skills/template-bind/video_dsl/schema/template-definition-v1alpha1.json +247 -0
- package/skills/template-bind/video_dsl/schema/video-dsl-v1alpha1.json +378 -0
- package/skills/video-parser/SKILL.md +238 -0
- package/skills/video-parser/scripts/analyze_video.py +1038 -0
- package/skills/video-parser/scripts/deconstruct_video.py +602 -0
- package/skills/video-parser/scripts/parse_via_render.py +148 -0
- package/skills/video-parser/skill.json +21 -0
- package/skills/video-parser/version.json +7 -0
- package/skills/web-capture/SKILL.md +183 -0
- package/skills/web-capture/scripts/web_capture.py +461 -0
- package/skills/web-capture/skill.json +29 -0
- package/skills/web-capture/version.json +7 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
视频解构脚本 — 通过 ab-render HTTP 接口实现,无需本地 ffmpeg。
|
|
4
|
+
|
|
5
|
+
调用链:
|
|
6
|
+
POST $RENDER_API_URL/parse → { taskId }
|
|
7
|
+
POST $RENDER_API_URL/parseStatus → { status, progress, result?, error? }
|
|
8
|
+
|
|
9
|
+
必要环境变量(由 ab-agent 自动注入):
|
|
10
|
+
RENDER_API_URL ab-render 服务地址,例如 http://localhost:3000
|
|
11
|
+
PRIV_TOKEN ab-api 私有 token
|
|
12
|
+
|
|
13
|
+
可选环境变量:
|
|
14
|
+
CONVERSATION_ID 对话 ID,用于文件关联
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import time
|
|
22
|
+
import urllib.error
|
|
23
|
+
import urllib.request
|
|
24
|
+
|
|
25
|
+
# ─── 环境变量 ─────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
RENDER_API_URL = os.environ.get("RENDER_API_URL", "").rstrip("/")
|
|
28
|
+
PRIV_TOKEN = os.environ.get("PRIV_TOKEN", "")
|
|
29
|
+
CONVERSATION_ID = os.environ.get("CONVERSATION_ID", "")
|
|
30
|
+
|
|
31
|
+
# ─── HTTP 工具 ────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
def api_post(path: str, body: dict, timeout: int = 30) -> dict:
|
|
34
|
+
"""向 ab-render 发送 POST 请求,返回解析后的 JSON。"""
|
|
35
|
+
url = f"{RENDER_API_URL}{path}"
|
|
36
|
+
data = json.dumps(body).encode()
|
|
37
|
+
|
|
38
|
+
headers: dict[str, str] = {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"X-Priv-Token": PRIV_TOKEN,
|
|
41
|
+
}
|
|
42
|
+
if CONVERSATION_ID:
|
|
43
|
+
headers["x-conversation-id"] = CONVERSATION_ID
|
|
44
|
+
|
|
45
|
+
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
|
|
46
|
+
try:
|
|
47
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
48
|
+
return json.loads(resp.read())
|
|
49
|
+
except urllib.error.HTTPError as e:
|
|
50
|
+
body_text = e.read().decode(errors="replace")
|
|
51
|
+
raise RuntimeError(f"HTTP {e.code}: {body_text}") from e
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def log(msg: str, silent: bool = False) -> None:
|
|
55
|
+
if not silent:
|
|
56
|
+
print(msg, flush=True)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ─── 主逻辑 ───────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def main() -> None:
|
|
62
|
+
parser = argparse.ArgumentParser(
|
|
63
|
+
description="Video deconstruction — calls the ab-render /parse endpoint (no local ffmpeg dependency)"
|
|
64
|
+
)
|
|
65
|
+
parser.add_argument("--url", required=True, help="Remote video URL")
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--scene-threshold", type=float, default=0.3,
|
|
68
|
+
help="Scene-change sensitivity 0.0-1.0 (default 0.3)",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument("--skip-asr", action="store_true", help="Skip the ASR step")
|
|
71
|
+
parser.add_argument("--skip-keyframes", action="store_true", help="Skip keyframe extraction")
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"--json-output", action="store_true",
|
|
74
|
+
help="Pipeline mode: emit only the final JSON to stdout",
|
|
75
|
+
)
|
|
76
|
+
args = parser.parse_args()
|
|
77
|
+
|
|
78
|
+
# ── Pre-flight check ─────────────────────────────────────────────────────
|
|
79
|
+
if not RENDER_API_URL:
|
|
80
|
+
print(
|
|
81
|
+
"❌ RENDER_API_URL env var is not configured.\n"
|
|
82
|
+
" Add render_api_url: \"http://<ab-render-host>:<port>\" to Nacos ab-agent.yaml",
|
|
83
|
+
file=sys.stderr,
|
|
84
|
+
)
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
# ── 1. Submit the deconstruction task ───────────────────────────────────
|
|
88
|
+
log(f"[video-parser] Submitting deconstruction task: {args.url[:80]}", args.json_output)
|
|
89
|
+
|
|
90
|
+
resp = api_post("/parse", {
|
|
91
|
+
"videoUrl": args.url,
|
|
92
|
+
"sceneThreshold": args.scene_threshold,
|
|
93
|
+
"skipAsr": args.skip_asr,
|
|
94
|
+
"skipKeyframes": args.skip_keyframes,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
if resp.get("code") != 0:
|
|
98
|
+
print(f"❌ submission failed: {resp.get('msg', 'unknown error')}", file=sys.stderr)
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
task_id: str = resp["data"]["taskId"]
|
|
102
|
+
log(f"[video-parser] task accepted, taskId={task_id}", args.json_output)
|
|
103
|
+
|
|
104
|
+
# ── 2. Poll status until completion ─────────────────────────────────────
|
|
105
|
+
max_wait_sec = 600 # Wait up to 10 minutes
|
|
106
|
+
poll_interval = 3 # Poll every 3 seconds
|
|
107
|
+
elapsed = 0
|
|
108
|
+
|
|
109
|
+
while elapsed < max_wait_sec:
|
|
110
|
+
time.sleep(poll_interval)
|
|
111
|
+
elapsed += poll_interval
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
status_resp = api_post("/parseStatus", {"taskId": task_id})
|
|
115
|
+
except Exception as e: # noqa: BLE001
|
|
116
|
+
log(f"[video-parser] polling error (will retry): {e}", args.json_output)
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
if status_resp.get("code") != 0:
|
|
120
|
+
log(f"[video-parser] polling returned an error code (will retry): {status_resp.get('msg')}", args.json_output)
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
task = status_resp["data"]
|
|
124
|
+
status = task.get("status", "unknown")
|
|
125
|
+
progress = task.get("progress", 0)
|
|
126
|
+
|
|
127
|
+
log(f"[video-parser] status={status} progress={int(progress * 100)}% elapsed={elapsed}s", args.json_output)
|
|
128
|
+
|
|
129
|
+
if status == "succeeded":
|
|
130
|
+
result = task.get("result")
|
|
131
|
+
if args.json_output:
|
|
132
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
133
|
+
else:
|
|
134
|
+
print("\n✅ Deconstruction complete")
|
|
135
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
if status == "failed":
|
|
139
|
+
error = task.get("error", "unknown error")
|
|
140
|
+
print(f"❌ deconstruction failed: {error}", file=sys.stderr)
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|
|
143
|
+
print(f"❌ deconstruction timed out (over {max_wait_sec}s)", file=sys.stderr)
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "video-parser",
|
|
3
|
+
"toolName": "video_parser",
|
|
4
|
+
"tier": "tool",
|
|
5
|
+
"title": "Video Deconstruction",
|
|
6
|
+
"description": "Video deconstruction: split a video into reusable assets (audio extraction, ASR transcript, keyframe extraction, scene segmentation).",
|
|
7
|
+
"envVars": ["PRIV_TOKEN", "MM_API_BASE_URL", "AGENT_NAME", "RENDER_API_URL"],
|
|
8
|
+
"scriptPath": "scripts/parse_via_render.py",
|
|
9
|
+
"parameters": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"url": { "type": "string", "description": "Remote video URL (or use local)" },
|
|
13
|
+
"local": { "type": "string", "description": "Local video path (or use url)" },
|
|
14
|
+
"scene_threshold": { "type": "number", "description": "Scene-cut detection threshold 0.0-1.0 (default 0.3)" },
|
|
15
|
+
"skip_asr": { "type": "boolean", "description": "Skip the ASR step" },
|
|
16
|
+
"skip_keyframes": { "type": "boolean", "description": "Skip the keyframe-extraction step" },
|
|
17
|
+
"json_output": { "type": "boolean", "description": "Pipeline mode — emit JSON only on stdout" }
|
|
18
|
+
},
|
|
19
|
+
"required": []
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skillName": "video-parser",
|
|
3
|
+
"repoName": "agent-skill-media-maker",
|
|
4
|
+
"skillId": "553",
|
|
5
|
+
"version": "V1",
|
|
6
|
+
"skillDescription": "视频解构技能,将视频拆解为可复用的内容资产(音频、ASR 文本、关键帧、场景分段)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 视频解构、视频拆解、视频分析、解析视频\n- 提取关键帧、提取视频文案、视频转文字\n- 视频内容资产、视频素材提取\n\n即使用户没有明确说「解构」,只要他们想要从视频中提取文案、关键帧或结构化信息,也要使用本 skill。"
|
|
7
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-capture
|
|
3
|
+
description: |
|
|
4
|
+
Web page capture skill. Drives a headless browser (Playwright) to turn a URL into reusable visual assets — viewport / full-page screenshots, or a smooth top-to-bottom scrolling video (MP4). These assets are the upstream material for the `screen-walkthrough` template.
|
|
5
|
+
|
|
6
|
+
Use this skill as soon as the user mentions any of these intents:
|
|
7
|
+
- Screenshot a web page / website / a specific URL, full-page screenshot
|
|
8
|
+
- Record a page scrolling top-to-bottom into a video, "scroll video" of a site
|
|
9
|
+
- Produce screen-recording / walkthrough footage of a GitHub repo, landing page, or product page
|
|
10
|
+
|
|
11
|
+
Even when the user does not say "Playwright" or "browser", use this skill whenever they want to turn a web address into a screenshot or a scrolling demo video.
|
|
12
|
+
triggers:
|
|
13
|
+
- Screenshot a web page / website / a specific URL, full-page screenshot
|
|
14
|
+
- Record a page scrolling top-to-bottom into a video, "scroll video" of a site
|
|
15
|
+
- Produce screen-recording / walkthrough footage of a GitHub repo, landing page, or product page
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Web Page Capture Skill
|
|
19
|
+
|
|
20
|
+
Drives a **headless browser via Playwright (Python)** to capture a web page as reusable visual assets:
|
|
21
|
+
|
|
22
|
+
- **Screenshots** — viewport, full scrollable page, or a single element.
|
|
23
|
+
- **Scroll video** — opens the page and smoothly scrolls top-to-bottom, capturing frames and assembling a deterministic MP4 with ffmpeg.
|
|
24
|
+
- **Record video** — Playwright's native context recording (webm), transcoded to MP4.
|
|
25
|
+
|
|
26
|
+
The MP4 / PNG output is designed to feed the **`screen-walkthrough`** template's screen stage.
|
|
27
|
+
|
|
28
|
+
## Dependencies
|
|
29
|
+
|
|
30
|
+
Aligned with method 1 of the design — the runtime provides the browser, the skill does not bundle it.
|
|
31
|
+
|
|
32
|
+
- **Python 3** with **Playwright**: `pip install playwright` then `playwright install chromium` (one-time; downloads the browser binary).
|
|
33
|
+
- **ffmpeg**: must be installed locally (`brew install ffmpeg`) — used to assemble the scroll-video frames and transcode native recordings to MP4. Same dependency the `video-parser` skill relies on.
|
|
34
|
+
|
|
35
|
+
The script checks for both at startup and prints an actionable install hint if either is missing (`__progress__` phase `web-capture:preflight`).
|
|
36
|
+
|
|
37
|
+
## Multi-user isolation & security
|
|
38
|
+
|
|
39
|
+
ab-agent spawns one fresh `python3` process per tool call (`spawn('python3', ...)`), and this script launches its own Playwright browser instance per run and closes it on exit. So **concurrent users are isolated at the process + browser-instance level** — no cookie/session bleed between users. The skill hardens a few defaults on top of that:
|
|
40
|
+
|
|
41
|
+
- **Unique output dir**: when `--output-dir` is omitted, output goes to a unique system temp dir (`tempfile.mkdtemp`), never the script's cwd. Concurrent calls can't collide or pollute `skills/`.
|
|
42
|
+
- **Temp cleanup**: per-frame images (`_frames/`) and native-recording webm (`_rec/`) are removed in a `finally` block, so failures don't leak temp data.
|
|
43
|
+
- **SSRF guard**: the user-supplied `url` is validated before any browser launch — only `http`/`https`, and the resolved IP must not be private / loopback / link-local / reserved / cloud-metadata (`169.254.169.254`). Rejection exits with code 5.
|
|
44
|
+
- **Sandbox on by default**: Chromium keeps its sandbox; `--no-sandbox` is only added when `WEB_CAPTURE_NO_SANDBOX=1`.
|
|
45
|
+
|
|
46
|
+
Not handled in-skill (orchestration concern): **concurrency limits**. Each browser is ~150–300MB; many simultaneous `scroll-video` calls can exhaust host memory. Gate concurrency at the ab-agent layer or via host resources.
|
|
47
|
+
|
|
48
|
+
### Environment variables
|
|
49
|
+
|
|
50
|
+
| Env var | Description | Default |
|
|
51
|
+
|---------|-------------|---------|
|
|
52
|
+
| `WEB_CAPTURE_BROWSER` | Default browser engine (`--browser` overrides). | `chromium` |
|
|
53
|
+
| `WEB_CAPTURE_OUTPUT_BASE` | Base dir for the auto-created temp output dir. | system tempdir |
|
|
54
|
+
| `WEB_CAPTURE_NO_SANDBOX` | Set `1` to add `--no-sandbox` (only for containers that can't use user namespaces). | unset (sandbox on) |
|
|
55
|
+
| `WEB_CAPTURE_ALLOW_PRIVATE_HOSTS` | Set `1` to disable the private/internal-IP SSRF block (single-tenant / trusted intranet only). | unset (block on) |
|
|
56
|
+
| `PLAYWRIGHT_BROWSERS_PATH` | Playwright browser binary location (provided by the runtime). | Playwright default |
|
|
57
|
+
|
|
58
|
+
## Operations
|
|
59
|
+
|
|
60
|
+
1. **Script path**: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>` and always use `<SkillDir>/scripts/web_capture.py`. Never hard-code paths.
|
|
61
|
+
2. **Pick the mode**: `screenshot` (default), `scroll-video`, or `record-video`.
|
|
62
|
+
3. **Run the capture** and wait for it to finish.
|
|
63
|
+
4. **Inspect the result**: a `capture.json` manifest plus the produced `*.png` / `*.mp4` under `--output-dir`.
|
|
64
|
+
|
|
65
|
+
### Viewport screenshot
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
69
|
+
--url "https://github.com/microsoft/playwright" \
|
|
70
|
+
--mode screenshot
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Full-page screenshot (whole scrollable page)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
77
|
+
--url "https://example.com" \
|
|
78
|
+
--mode screenshot \
|
|
79
|
+
--full-page
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Element-only screenshot
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
86
|
+
--url "https://example.com" \
|
|
87
|
+
--mode screenshot \
|
|
88
|
+
--selector "#readme"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Scrolling video (top-to-bottom)
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
95
|
+
--url "https://github.com/microsoft/playwright" \
|
|
96
|
+
--mode scroll-video \
|
|
97
|
+
--width 1280 --height 720 \
|
|
98
|
+
--fps 30 \
|
|
99
|
+
--scroll-duration 8
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Produces a deterministic `scroll.mp4` — fps, resolution and scroll speed are all controllable, which keeps the output aligned with the `screen-walkthrough` timeline.
|
|
103
|
+
|
|
104
|
+
### Native recording (webm → mp4)
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
108
|
+
--url "https://example.com" \
|
|
109
|
+
--mode record-video \
|
|
110
|
+
--scroll-duration 6
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Simpler than frame-capture but the frame rate is not precisely controllable; prefer `scroll-video` when timing matters.
|
|
114
|
+
|
|
115
|
+
### JSON pipeline mode
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python3 <SkillDir>/scripts/web_capture.py \
|
|
119
|
+
--url "https://example.com" \
|
|
120
|
+
--mode screenshot \
|
|
121
|
+
--json-output
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Prints only the final `capture.json` payload to stdout — no progress lines — suitable for piping into downstream skills.
|
|
125
|
+
|
|
126
|
+
See every option: `python3 <SkillDir>/scripts/web_capture.py --help`.
|
|
127
|
+
|
|
128
|
+
## Common CLI flags
|
|
129
|
+
|
|
130
|
+
| Flag | Description | Default |
|
|
131
|
+
|------|-------------|---------|
|
|
132
|
+
| `--url` | Target page URL (http/https). | required |
|
|
133
|
+
| `--mode` | `screenshot` / `scroll-video` / `record-video`. | `screenshot` |
|
|
134
|
+
| `-o` / `--output-dir` | Output directory. | unique system temp dir |
|
|
135
|
+
| `--width` | Viewport width (px). | `1280` |
|
|
136
|
+
| `--height` | Viewport height (px). | `720` |
|
|
137
|
+
| `--device-scale-factor` | Device pixel ratio (crisp / retina). | `2` |
|
|
138
|
+
| `--full-page` | Screenshot mode: capture the whole scrollable page. | off |
|
|
139
|
+
| `--selector` | Screenshot mode: capture only this CSS element. | none |
|
|
140
|
+
| `--fps` | Scroll / record video frame rate. | `30` |
|
|
141
|
+
| `--scroll-duration` | Seconds for the full top-to-bottom scroll. | `8` |
|
|
142
|
+
| `--browser` | `chromium` / `firefox` / `webkit`. | `chromium` |
|
|
143
|
+
| `--wait-until` | `load` / `domcontentloaded` / `networkidle`. | `networkidle` |
|
|
144
|
+
| `--wait-ms` | Extra wait after load before capture (ms). | `1500` |
|
|
145
|
+
| `--json-output` | Pipeline mode — JSON-only stdout. | off |
|
|
146
|
+
|
|
147
|
+
## Output layout
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
<output-dir>/
|
|
151
|
+
├── capture.json # asset manifest (mode, url, viewport, produced files)
|
|
152
|
+
├── screenshot.png # screenshot mode
|
|
153
|
+
└── scroll.mp4 # scroll-video / record-video mode
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `capture.json` shape
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"source": { "url": "https://example.com", "finalUrl": "https://example.com/" },
|
|
161
|
+
"mode": "scroll-video",
|
|
162
|
+
"viewport": { "width": 1280, "height": 720, "deviceScaleFactor": 2 },
|
|
163
|
+
"outputs": [
|
|
164
|
+
{ "type": "video", "localPath": "<output-dir>/scroll.mp4", "fps": 30, "durationSec": 8.0, "frames": 240 }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For `screenshot` mode each entry is `{ "type": "image", "localPath": "...", "fullPage": false }`.
|
|
170
|
+
|
|
171
|
+
## Notes on the deprecated `microsoft/playwright-cli`
|
|
172
|
+
|
|
173
|
+
The original `microsoft/playwright-cli` repository was **archived (read-only) in January 2021** and is deprecated; its functionality was folded into the main `playwright` package. This skill therefore uses the maintained **Playwright Python** library directly. Pure screenshots are also available via `npx playwright screenshot`, but scrolling-video capture requires script-driven scrolling + frame assembly, which is what this skill provides.
|
|
174
|
+
|
|
175
|
+
## Error handling
|
|
176
|
+
|
|
177
|
+
- **Playwright not installed**: the script detects it and prints `pip install playwright && playwright install chromium`.
|
|
178
|
+
- **Browser binary missing** (`Executable doesn't exist`): run `playwright install chromium` (or the chosen browser).
|
|
179
|
+
- **ffmpeg not installed** (video modes): the script prompts `brew install ffmpeg`.
|
|
180
|
+
- **URL rejected (exit 5)**: non-http(s) scheme, or the host resolves to a private/internal/metadata address. Use a public URL, or set `WEB_CAPTURE_ALLOW_PRIVATE_HOSTS=1` for a trusted intranet target.
|
|
181
|
+
- **Navigation timeout**: the page took too long; try `--wait-until load` or a smaller `--wait-ms`.
|
|
182
|
+
- **Blank / partial capture**: increase `--wait-ms`, or some sites block headless browsers.
|
|
183
|
+
- **Network**: verify the URL is reachable from the runtime host.
|