goose-ads
Edit, resize, animate and study image ads with GooseWorks in this chat. New static or image ads go to the make-static-ads skill…
Stitch video segments with ffmpeg concat, xfade, overlay, audio mux, and export settings, and trim a source into named clips at exact windows (trim_clips.py). Ships montage.py, a free montage assembler (python3 + ffmpeg, no keys). It takes a JSON EDL of clips and stills,
Claude Code
$ npx -y skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg --agent claude-code
Codex
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a codex -y
Cursor
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a cursor -y
Gemini CLI
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a gemini-cli -y
GitHub Copilot
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a github-copilot -y
OpenCode
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a opencode -y
Windsurf
$ npx skills add gooseworks-ai/goose-skills --skill stitch-videos-ffmpeg -a windsurf -y
name: stitch-videos-ffmpeg description: Stitch video segments with ffmpeg concat, xfade, overlay, audio mux, and export settings, and trim a source into named clips at exact windows (trim_clips.py). Ships montage.py, a free montage assembler (python3 + ffmpeg, no keys). It takes a JSON EDL of clips and stills, normalizes them and hard-cuts them in order, burns captions from an SRT, a cue list or word timings, and lays a VO over a music bed that ducks under it, mastered to -14 LUFS. version: "1.3.1"
Stitch video segments with ffmpeg concat, xfade, overlay, audio mux, and export settings.
Implementation status: refactored from existing repository skills. The workflow consolidates behavior that previously lived across larger skills.
Sources: ad-studio, voiceover-product-ad, ugc-product-video, voiceless-music-transformation-reel, product-stopmotion-ad.
Extraction notes: assembly and composite scripts.
A free, deterministic way to cut a montage ad from finished clips. It needs `python3` and `ffmpeg`/`ffprobe` only (Pillow is optional, see Captions). It makes no network or provider calls and needs no keys, so re-cuts cost nothing.
| Step | What it does | |---|---| | `edl` | Checks a JSON spec and writes `edl.json`. Every file must exist and is probed with ffprobe; every cut must fit inside its clip. All problems are listed at once. | | `assemble` | Scales each cut to one size (default 1080×1920, `cover` crops to fill, `contain` pads), one frame rate (default 30), square pixels and yuv420p. Then it hard-cuts them in order with the `filter_complex` concat filter, never the `-f concat` demuxer. Each cut gets an exact frame count, so the output length equals the EDL total. | | `captions` | Burns an SRT, a JSON cue list, or word timings (one cue per `--per` words) into the video. The cue text is kept exactly as given (wrapping only turns spaces into line breaks). | | `mix` | Lays the VO over the video and adds an optional music bed that ducks under the VO (sidechain compression, 20:1). Then it masters to -14 LUFS / -1 dBTP with two-pass loudnorm. | | `run` | Runs all four steps from one spec and writes `manifest.json`. |
Each step prints a one-line JSON summary. Exit codes: `0` ok, `1` ffmpeg failed, `2` bad spec or arguments, `3` a tool is missing (ffmpeg, ffprobe, or a caption renderer).
After `gooseworks fetch stitch-videos-ffmpeg` (or as a dependency of a format package), the scripts are in `/tmp/gooseworks-scripts/stitch-videos-ffmpeg/scripts/`:
S=/tmp/gooseworks-scripts/stitch-videos-ffmpeg/scripts # or this folder's scripts/ in a checkout
python3 $S/montage.py run --spec montage.json --out edits/master.mp4 --workdir edits/work
# or one step at a time
python3 $S/montage.py edl --spec montage.json --out edits/edl.json
python3 $S/montage.py assemble --edl edits/edl.json --out edits/body.mp4
python3 $S/montage.py captions --video edits/body.mp4 --words audio/vo.words.json \
--respell '{"symbiotic": "synbiotic"}' --color "#FFE800" --out edits/captioned.mp4
python3 $S/montage.py mix --video edits/captioned.mp4 --vo audio/vo.mp3 \
--music audio/bed.mp3 --out edits/master.mp4Paths are relative to the spec file. Times are seconds or a timecode string (`"SS"`, `"MM:SS"`, `"HH:MM:SS"`, optional `.ms`).
{
"output": {"width": 1080, "height": 1920, "fps": 30, "fit": "cover"},
"words": "audio/vo.words.json",
"clips": [
{"file": "clips/scene-01.mp4", "label": "hook", "word_range": [0, 4]},
{"file": "clips/scene-02.mp4", "label": "feature", "in": 0.2, "out": 1.6},
{"file": "clips/scene-03.mp4", "label": "reaction", "in": "00:00.5", "duration": 0.9},
{"file": "clips/scene-04.mp4", "label": "b-roll", "t_in": 4.1, "t_out": 5.0},
{"file": "clips/scene-05.mp4"},
{"file": "overlays/landing-page.png", "label": "landing-page", "duration": 1.5,
"pan": {"from": {"x": 0.5, "y": 0.2, "zoom": 1.0}, "to": {"x": 0.5, "y": 0.7, "zoom": 1.6}}},
{"file": "brand/end-card.png", "label": "end-card", "duration": 2.0}
],
"clip_audio": "drop",
"captions": {"words": "audio/vo.words.json", "per": 1, "respell": {"symbiotic": "synbiotic"},
"style": {"color": "#FFE800", "y": 0.5}},
"audio": {"vo": "audio/vo.mp3", "music": "audio/bed.mp3", "music_start": 14.2}
}**How long each cut is** (first match wins):
used as the transcriber wrote it: a flat `[{text|word, start, end}]` list, `{words: [...]}` (OpenAI, ElevenLabs; `spacing` entries are skipped), Whisper's `{segments: [{words: [...]}]}` (what fal's Whisper returns), or fal's `{chunks: [{text, timestamp: [s, e]}]}`. Words with no time are skipped, and leading spaces are stripped. The cut runs from the start of word `i` to the start of word `j + 1`, or to the last word's end. The first cut starts at 0 so it covers the lead-in. Consecutive ranges tile the VO with no gaps.
that does not start where the previous cut ended is reported as a gap or overlap warning; `--strict` turns warnings into errors.
Every cut starts at `in` in its source (default 0). A still image (`.png`, `.jpg`, `.webp`) needs a length; `pan` zooms and pans across it. The still is first cover-cropped to the output aspect (9:16 by default), and `x`/`y` are the window centre as a fraction of that cropped image (`zoom` ≥ 1). A video clip may be up to 0.1 s short of its cut, and then its last frame is held. Anything shorter is an error.
`clip_audio` (`--clip-audio` on `assemble`): `drop` (default) or `keep`. `keep` keeps each clip's own sound and fills silence under stills
Put your AI agent on the growth team. Research customers and competitors, analyze what is working, create the next campaign, and learn from the result.
Edit, resize, animate and study image ads with GooseWorks in this chat. New static or image ads go to the make-static-ads skill…
Make product photos (studio, lifestyle or on a model) of a brand's real products with GooseWorks in this chat. Start brand work…
Make an approved GooseWorks video when an answer's next_step gives its command.
Make a video ad in this chat from a real reference ad, found or theirs; styles are the fallback. Follow every answer's next_step.
GooseWorks growth coworker and router for markets, competitors, audiences, creators, campaigns, ads, photos, graphics, video,…
Create a custom video with sourced product accuracy, explicit sound routing and exact-final audiovisual review.
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use…
Comprehensive SEO analysis for any website or business type. Full site audits, single-page analysis, technical SEO (crawlability,…
Turn the current project website into a short, polished, shareable launch video using Hyperframes. Use when someone says "/brag",…
Full website GEO+SEO audit with parallel subagent delegation. Orchestrates a comprehensive Generative Engine Optimization audit…