npx skills add ...
npx skills add starchild-ai-agent/official-skills --skill video
AI video generation: text-to-video, image-to-video, video-to-video, model selection.
npx skills add starchild-ai-agent/official-skills --skill video
Use this skill for all video-generation requests on Starchild.
Core principle: call the provided scripts. Do not re-implement proxy/billing/upload plumbing.
⚠️ Execution context — read this first. The code blocks below are Python, not shell commands. Starchild's
bashtool runs/bin/bash -c, which cannot parseexec(open(...))— pasting them directly into a bash command will fail withsyntax error near unexpected token 'open'. Also,exec(open(...))insidepython3 -cfails withNameError: __file__because the script uses__file__for path resolution.Use
python3 - <<'EOF'withfrom exports importwhen calling via the bash tool:The heredoc (
<<'EOF') preserves all quotes and newlines — no escaping needed. Note: video skill has noexports.py— import directly fromgenerate_video.
generate_video automatically: submits → polls → fetches result → downloads mp4 to output/videos/.
Never hand the user the raw video_url (e.g. https://*.fal.media/.../*.mp4). fal serves these files with Content-Security-Policy: sandbox; default-src 'none', which means:
<video> / <iframe> is blocked by CSP.Content-Disposition: attachment header, so the browser does not auto-download either.?download=1, etc.) cannot fix this — only a server-side header change would, and we don't control fal's CDN.The only reliable user-facing delivery path is the already-downloaded local file:
result["local_path"] (e.g. output/videos/xxx.mp4) — generate_video always downloads on success.output/videos/<filename> and is viewable in the workspace file panel / file browser.[video](output/videos/<filename>.mp4) — the workspace serves these directly with the right headers).send_to_telegram(file_path="output/videos/...", message_type="video") or send_to_wechat(file_path="output/videos/...", message_type="video").If the download somehow failed (local_path missing) — re-fetch with:
Then deliver the local path. Still do not give the user the raw fal URL as the primary deliverable.
fal.ai needs the reference asset as a public https URL. fal storage upload requires a Serverless permission your key currently does not have. The reliable path is to expose the asset via a published Starchild preview.
output/fal_assets/ using publish_asset.py.fal-assets is running and published (one-time setup, see §3).<preview_base>/<filename>.generate_video(... image_url=public_url).generate_video auto-rewrites the model path from */text-to-video to */image-to-video whenever image_url is provided. The same approach works for video-to-video models — pass an mp4 URL instead.
publish_asset.py).jpg .jpeg .png .webp .gif .bmp, max 10 MB.mp4 .mov .webm .mkv .m4v, max 100 MBfal-assets public preview setupRun this once per workspace. The preview keeps running across sessions.
After publish, the public base URL is reusable for every future image-to-video / video-to-video task. Files dropped into output/fal_assets/ become reachable as <base>/<filename> immediately — no re-publish needed.
Verify with:
If preview(action='serve') returns No available ports in pool, ask the user which existing preview can be stopped to free a port — never silently kill one.
| Tier | Model | Cost / 5s | Notes |
|---|---|---|---|
| budget | fal-ai/wan/v2.5/text-to-video | $0.25 | Fastest, cheapest; good for prompt iteration |
| balanced | alibaba/happy-horse/text-to-video | $0.70 | Default; best lip-sync, most use cases |
| premium | bytedance/seedance-2.0/fast/text-to-video | $1.20 | Best motion + camera direction |
| mini | bytedance/seedance-2.0/mini/text-to-video | $0.36 (480p) / $0.77 (720p) | Cheapest Seedance; resolution-tiered, no 1080p. Duration must be a string ("5", not 5 or "5s") — see gotcha below |
| premium-25 | bytedance/seedance-2.5/text-to-video | token-based | Supports text-to-video, image-to-video, and reference-to-video. Requires resolution (480p/720p), aspect_ratio (six supported ratios), and integer duration from 4–30 seconds. Estimate with estimate_cost(..., aspect_ratio=...). |
| — | xai/grok-imagine-video/v1.5/image-to-video | $0.41 (480p) / $0.71 (720p) per 5s | image-to-video ONLY (single required image_url, no image_urls); +$0.01 input-image surcharge included in estimate. ⚠️ resolution="1080p" is schema-valid upstream but has NO published price — the proxy rejects it 400 fail-closed |
| — | fal-ai/kling-video/v3/turbo/standard/text-to-video | $0.56 per 5s | Kling v3 Turbo Standard, flat $0.112/s; .../turbo/pro/... = $0.14/s ($0.70/5s); .../v3/4k/... = $0.42/s ($2.10/5s). i2v variants exist for all |
| — | alibaba/happy-horse/v1.1/text-to-video | $0.70 (720p) / $0.90 (1080p) per 5s | v1.1 has its own 1080p tier $0.18/s (NOT the v1.0 2× rule); also /image-to-video, /reference-to-video |
| — | fal-ai/minimax_h3/text-to-video | proxy pricing applies | Supports text-to-video, image-to-video, and reference-to-video. For reference-to-video pass image_urls=[...]; the payload is translated to upstream reference_image_urls. |
⚠️ Happy Horse default resolution is 1080p upstream (v1.0 and v1.1): omitting resolution bills the 1080p tier (v1.1 5s = $0.90; v1.0 ref2v 5s = $1.40). Pass resolution="720p" explicitly for the cheaper rate. Invalid resolution values are rejected 400 by the proxy.
Reference-to-video: pass image_urls=[...] (list of 1–9 public HTTP(S) URLs) — NOT the single image_url param. generate_video() validates count and URL scheme. Happy Horse and Seedance 2.5 submit the image_urls field; MiniMax H3 (fal-ai/minimax_h3/reference-to-video) submits upstream's reference_image_urls field.
Seedance 2.5 example:
Use an integer duration from 4–30 seconds. resolution must be 480p or 720p; aspect_ratio must be one of 21:9, 16:9, 4:3, 1:1, 3:4, 9:16. The proxy rejects auto values because they cannot be priced safely.
Override by passing the full model id to generate_video(model=...). Image-to-video variants are auto-derived by replacing text-to-video with image-to-video.
Pricing details and model registry live in generate_video.py::estimate_cost. For models not yet registered there, the legacy fallback is only a rough estimate and may differ from the proxy; do not use it for budgeting new endpoints.
Use this when an earlier generate_video call timed out or you only have a request_id.
generate_video.py — submit → poll → download. Handles text-to-video and image-to-video.publish_asset.py — copy local files (or download remote URLs) into output/fal_assets/ so they can be served by the fal-assets preview.poll_status.py — resume polling by request_id, downloads the result on completion.| Problem | Fix |
|---|---|
image_url must be a public HTTP(S) URL | Use publish_asset.py + fal-assets preview, then pass the public URL |
No available ports in pool (preview serve) | Ask the user which preview to stop; do not auto-kill |
downstream_service_error after COMPLETED | Reference asset host failed mid-render — re-encode/resize to 16:9, re-publish, retry |
HTTP 402 insufficient_credits | Top up balance; cost is pre-charged on submit |
HTTP 403 endpoint_not_allowed | sc-proxy only allows approved fal video endpoints; pick one from the model table |
Generation FAILED upstream | Shorten prompt, drop unusual tokens, retry once before changing model |
HTTP 422 literal_error on duration (Seedance Mini) | Mini requires duration as a string ("5", "10", "auto"), not an int and not "5s". generate_video() encodes this automatically when model contains seedance-2.0/mini — only hit this if you hand-build the request body. Other Seedance variants accept int/"5s" as before. |
Seedance 2.5 rejects auto or returns resolution_not_priceable / aspect_ratio_not_priceable | Pass explicit resolution="480p" or "720p", an explicit supported aspect_ratio, and integer duration from 4–30. Seedance 2.5 uses token-based pricing; call estimate_cost(model, duration, resolution, aspect_ratio) for a local estimate. |
| MiniMax H3 reference request returns a parameter error | Use image_urls=[...] with the fal-ai/minimax_h3/reference-to-video model. generate_video() translates it to upstream reference_image_urls; do not hand-send image_urls to upstream. |
Job stuck IN_PROGRESS >15 min | Save request_id, resume later with poll_status.py |
| User reports the fal.media link "shows nothing" / "blank page" | Expected — fal serves with CSP: sandbox; default-src 'none'. Deliver the local file at result["local_path"] instead of the raw URL (see §1). |
sc-proxy → queue.fal.run (and api.fal.ai) → fal model providersAuthorization: Key fake-falai-key-12345 (proxy injects the real FAL_KEY)403 endpoint_not_allowed.https://*.fal.media/... — public CDN, no auth needed for download.generate_video.py::estimate_cost and in transparent-proxy/apis/falai.py::_VIDEO_PRICING.FAL_KEY lacks Serverless permission. Keep using the preview-based approach until that changes.