Communitygithub.com

andrewbowlds/edp-listing-video-maker

Create EDP Realty listing marketing videos using an AI presenter (Faith, the blonde agent, or the EDP News male anchor) composited into the property's real listing photos via Grok Imagine image-to-video, then assembled into a finished 16:9 and/or 9:16 video with ffmpeg. Use whenever an EDP Realty broker or agent wants a video for a listing — \"make a Faith video for 123 Main St\", \"create a listing video\", \"make a video for this property\", \"EDP News video\", \"make a reel for this listing\", \"new listing video\", \"make a video with the listing photos\", or a video for a specific agent/team. Gathers listing facts and photos, writes the per-scene script, drives Grok in the browser, and stitches the scenes. Always trigger for any EDP Realty property/listing video request even if the presenter isn't named.

What is edp-listing-video-maker?

edp-listing-video-maker is a Claude Code agent skill that create EDP Realty listing marketing videos using an AI presenter (Faith, the blonde agent, or the EDP News male anchor) composited into the property's real listing photos via Grok Imagine image-to-video, then assembled into a finished 16:9 and/or 9:16 video with ffmpeg. Use whenever an EDP Realty broker or agent wants a video for a listing — \"make a Faith video for 123 Main St\", \"create a listing video\", \"make a video for this property\", \"EDP News video\", \"make a reel for this listing\", \"new listing video\", \"make a video with the listing photos\", or a video for a specific agent/team. Gathers listing facts and photos, writes the per-scene script, drives Grok in the browser, and stitches the scenes. Always trigger for any EDP Realty property/listing video request even if the presenter isn't named.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/andrewbowlds/edp-listing-video-maker/tree/HEAD/edp-listing-video-maker

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

EDP Listing Video Maker

Produce EDP Realty listing videos where an AI presenter stands in front of the real listing photo and delivers a scripted line, one scene per photo.

Pipeline: gather listing facts + photos → review the photos → write scene scripts → build a Faith character-reference still → Grok image-to-video (two inputs per scene: room photo + character reference) → ffmpeg stitch with logo watermark and end card → QC → deliver.

The core technique: this is image-to-video, never text-to-video. You upload the actual property photo and Grok blends the presenter into that real scene. Text-to-video invents a house that isn't the listing — which is both off-brand and an advertising problem.


THE THREE RULES THAT WERE LEARNED THE HARD WAY

These three cost a full rebuild cycle on the first production listing. Apply them from the first generation, not after a reviewer catches them.

  1. Always send a character-reference image as a second input. Text alone does not hold Faith's wardrobe. Across five scenes she drifted through four different outfits — navy blazer, tan trousers, blush draped blouse, white collared shirt. See STEP 3.5.
  2. Lock the camera on exterior scenes. "Smooth camera motion, light pan and zoom" is fine indoors where walls bound the frame. Outdoors the camera pulls back into open yard and Grok invents whatever it reveals — a porch with columns, landscaped shrub beds, a reshaped neighbor's house. See STEP 4.
  3. The presenter must be in frame at frame 0. Grok routinely renders the first frame empty and pops her in, or starts her small and far away and zooms. Both read as a glitch. Demand her presence and her framing explicitly.

THE PRESENTERS

Two locked characters. Faith is the default — only use the anchor if the user asks for the news format or an investor/fixer-upper angle.

Faith — BLONDE-AGENT-01 (default)

Character ID: BLONDE-AGENT-01
Use the exact same on-screen presenter in every video.
Attractive blonde woman, early 30s, medium-length wavy hair, blue eyes, friendly
smile, light natural makeup.
Professional real estate style, blue suit, neutral colors.
Maintain identical facial structure, hair style, clothing style, and personality
in all videos.
This character must remain consistent across every separate video.

House wardrobe (use unless told otherwise): steel blue single-button suit blazer, ivory white collared shirt open at the collar, matching steel blue trousers. Name every piece explicitly in the prompt, including the trouser colour — Grok's most common drift is putting her in tan or khaki trousers.

Opening line template: "Hey, Faith here with EDP Realty. Check out our listing here at [ADDRESS]."

Tone: warm, upbeat, confident. She is a real estate agent, not a spokesmodel.

EDP News Anchor — MALE-ANCHOR-01 (investor / fixer-upper format)

Character ID: MALE-ANCHOR-01
Use the exact same on-screen presenter in every video.
Older male news anchor, early-mid 40s. Salt-and-pepper short hair, neatly
groomed. Light stubble or clean shave. Strong jawline, blue/gray eyes, confident
presence, natural newsroom-style makeup.
Wearing a dark navy or charcoal anchor blazer, white dress shirt, no tie.
Holding a black handheld microphone with a simple "EDP News" mic-flag (must stay
identical in every video).
Maintain identical face, hair, clothing, microphone, and personality across all
videos.

Anchor-specific rules: he stands outside or on the porch, never inside, and stands in place rather than walking into the home.

Outfit variants

Faith supports outfit overrides (e.g. "navy blue professional dress, pink hard hat, pink heels" for a construction / renovation listing). Append the outfit lines to her character block; leave the face and hair language untouched or she drifts. If you override the outfit, you must also rebuild the character-reference still to match, or the reference and the text will fight each other.


STEP 1 — GATHER THE LISTING FACTS

Ask for the property address if it wasn't given. You need: street address, status, list price, bedrooms, bathrooms, square footage, year built, MLS number, and the listing remarks. Use whatever listing source your session has connected (the brokerage's transaction/listing system, or an MLS sheet or listing link the user pastes in). If nothing is connected, ask the user to paste the MLS sheet or listing link — don't guess facts.

Expect duplicate records for the same address. Old or archived versions of a listing can sit next to the live one with different prices and missing bed/bath/sqft. Use the one that is active and has an MLS number, and tell the user which record you skipped so they know which price the video reflects.

Check status before you build. If it's archived, expired, sold, or pending, say so and confirm before making "FOR SALE" marketing for it.

STEP 2 — GET AND REVIEW THE PHOTOS

Get the listing photos into a local working folder <work>/photos/ with short names: front.jpg, living.jpg, kitchen.jpg, bed1.jpg, bed2.jpg, shower.jpg. If the photos come as links, download them with curl; if the user attaches them, copy them in. Original filenames usually end in a room label, which gives you the scene order for free. If outbound downloads are blocked, see the egress note in GOTCHAS.

Look at the photos before writing a word

Build a contact sheet and read it, so you can write lines about what's actually in each room and drop any that are dark, blurry, or duplicates:

montage front.jpg living.jpg kitchen.jpg bed1.jpg bed2.jpg shower.jpg \
  -tile 3x2 -geometry 640x480+6+6 -background '#111' contact_sheet.jpg

Pick 4–6 photos. Order: front exterior first (that's the opening scene), then living → kitchen → bedroom → bath, saving the single most striking room for last so the CTA lands on the best visual. Skip near-duplicate bedrooms.

STEP 3 — WRITE THE SCENE SCRIPTS

One short line per photo, ~2–4 seconds spoken (roughly 12–25 words). Show the user the full script as a table and get approval before generating anything — regenerating is the expensive step.

Ask these three things at the same time as script approval, because they all change the build and it's rude to ask one at a time:

  1. Any wording changes to the script.
  2. Which cuts — 16:9, 9:16, or both. (Often wide only is enough.)
  3. Who the video is for — house EDP branding, or a specific agent/team CTA on the end card. See the END CARD section in STEP 5.

Structure:

  • Scene 1 (front photo): the presenter's opening line + the address.
  • Middle scenes: one concrete benefit per room, drawn from the listing remarks and what you actually saw in the photo.
  • Final scene: the strongest room + CTA. The brokerage's preferred close is "Reach out and schedule your private tour today." — not "Call EDP Realty." Keep the spoken CTA generic; the specific agent, team, and phone number belong on the end card, never in Grok's mouth.

Write like a person talking, not like MLS remarks. "The kitchen's been updated and there's room for a real table" beats "featuring a spacious updated kitchen with dining area."

Keep the price out of the spoken lines. Grok garbles spoken numbers, and a wrong price in a listing video is a real problem. Price goes on the title card, where it renders cleanly. Same for square footage and any phone number.

Facts from the MLS record that no photo shows (garage, fenced yard, year built) are fine for Faith to mention — just don't imply a photo depicts them. Match the MLS wording for these: say detached garage, not "two-car garage," when that's what the property has.

Fair housing — non-negotiable

This is advertising for a brokerage. Describe the property, never the people.

  • Never reference or imply race, color, religion, national origin, sex, disability, or familial status. That includes soft proxies: "great family neighborhood," "perfect for a young couple," "safe area," "walk to church," "quiet, no kids around," "great schools," "exclusive."
  • Say "the third bedroom" — not "the kid's room."
  • Do not describe who would like the home. Describe the home.
  • Keep every stated fact true to the listing record. Don't round 858 sqft up to "900+," don't call an archived listing active, don't invent a year built.
  • The brokerage must be identified and the Equal Housing Opportunity mark must appear. The corner watermark, the end card's brokerage line, and the Equal Housing logo on the end card cover this — never ship without them.
  • If the user asks for wording that crosses this line, flag it and offer a compliant rewrite rather than generating it.

STEP 3.5 — BUILD THE FAITH CHARACTER REFERENCE

Do this before any generation. It is the single highest-leverage step for consistency.

Source a clean still of Faith in the house wardrobe. Either reuse a saved reference from a previous listing (preferred — it keeps her identical across listings), or pull a frame from a good clip:

ffmpeg -y -ss 7 -i good_clip.mp4 -frames:v 1 ref_full.png
# crop tight to her, drop the room behind her, pad to the viewport aspect
ffmpeg -y -i ref_full.png \
  -vf "crop=400:620:560:20,scale=-1:600,pad=1300:600:(ow-iw)/2:0:color=0x8a8a8a" \
  -q:v 3 faith_ref_wide.jpg

Crop tight so almost none of the original room survives — otherwise Grok blends that room into the new scene. Pad to roughly the browser viewport aspect on a neutral grey so the screenshot you feed Grok has no letterbox bars.

Look at the result before using it. It should read as: face, hair, blazer, shirt, and at least the top of the trousers.

STEP 4 — GENERATE THE CLIPS IN GROK

Drive the logged-in Chrome with the Claude-in-Chrome tools at grok.com/imagine. One generation per photo, Video mode, 720p, 10s, 16:9.

Getting images into Grok

file_upload rejects paths in the outputs folder — it only accepts files it considers shared with the session, and the outputs directory does not qualify. Do not waste calls on it. Two working routes:

Route A (preferred) — serve the working folder over localhost and screenshot. Grok's upload_image takes a screenshot imageId, so anything you can display in Chrome you can upload:

# on the user's Mac, from the working folder, backgrounded so the call returns
(nohup python3 -m http.server 8777 --bind 127.0.0.1 > /tmp/srv.log 2>&1 &)
sleep 2; curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 \
  http://127.0.0.1:8777/photos/front.jpg

Then per image: navigate to http://127.0.0.1:8777/photos/front.jpg, run this to strip Chrome's viewer chrome and any extension overlay, and screenshot it:

const img=document.querySelector('img');
document.body.querySelectorAll('*').forEach(e=>{if(e!==img&&!e.contains(img))e.remove()});
document.documentElement.style.cssText='margin:0;padding:0;overflow:hidden;background:#000';
document.body.style.cssText='margin:0;padding:0;overflow:hidden;background:#000';
img.style.cssText='position:fixed;top:0;left:0;width:100vw;height:100vh;object-fit:cover;margin:0';
'ok'

Kill the server when the run finishes: pkill -f "http.server 8777".

Route B — navigate directly to the photo's own URL and apply the same JS. Works, but the URLs are enormous and expire.

Either way you get a cover-cropped screenshot at the viewport aspect, which is what you want: Grok matches the source aspect, so a ~2.16:1 screenshot yields a ~2.16:1 clip that the build script crops cleanly to 16:9.

The page, verified 2026-08-11

  • Hidden file input — locate with find ("hidden file input for attaching an image"), then use upload_image with ref + imageId. Never click the attach button — that opens a native picker you can't drive.
  • Two inputs per scene: upload the room photo FIRST, then the character reference. They appear as two thumbnails above the composer. Refer to them in the prompt as IMAGE 1 and IMAGE 2 in that order.
  • Composer is .tiptap.ProseMirror contenteditable. Long prompts set more reliably via a synthetic paste event than by typing.
  • The composer defaults to Image mode. Switch to Video and confirm 720p / 10s / 16:9 with a screenshot. Mode persists across generations within a session but re-verify after any page reload.
  • Screenshot IDs expire. Recapture the room photo and the character reference immediately before each scene's upload — do not reuse an imageId from several steps earlier or the upload fails.

Prompt template — paste this, swapping the bracketed parts

IMAGE 1 is the PROPERTY. IMAGE 2 is the CHARACTER REFERENCE for the presenter.

Character ID: BLONDE-AGENT-01. Match IMAGE 2 exactly: same face, same blonde
wavy shoulder-length hair, same steel blue single-button suit blazer, same ivory
white collared shirt open at the collar, same matching steel blue trousers. The
wardrobe must be IDENTICAL to IMAGE 2 — do not change the blazer color, do not
change the shirt, and the trousers must be steel blue, NOT tan, NOT khaki.

IMPORTANT: Produce a VIDEO, not an image. Do NOT generate any still photos.

Create a real estate marketing VIDEO. IMAGE 1 is a REAL PHOTOGRAPH of a REAL
[room] and it must be preserved exactly. Use IMAGE 2 only for the presenter's
appearance and wardrobe — do NOT use any of IMAGE 2's background.

[CAMERA BLOCK — see below]

[PRESERVATION BLOCK — see below]

FRAMING: The presenter is fully visible, centered, and framed from roughly the
knees up in the VERY FIRST FRAME and stays that size for the whole video. She is
NOT small or far away. She is NOT cropped at the edge of frame. Do NOT start on
an empty room. Do NOT fade her in, materialize her, or have her walk into frame.
She is standing there talking from frame one.

She stands in [this room / in front of the property] and says exactly this, and
nothing else:
"[SCENE SCRIPT LINE]"

Presenter requirements:
- Confident, upbeat energy
- Natural movements, gestures, and expressions
- Blended naturally and realistically into the scene
- Stands in place; does not walk

Video style:
- 16:9 horizontal, bright, clean, modern real estate commercial
- Subtle, modern background music

CRITICAL:
- VIDEO only — absolutely no still images
- Presenter fully in frame from the first frame, no fade-in, no drop-in, no crop
- Wardrobe identical to IMAGE 2 in every frame
- Do NOT add any text, graphics, lower thirds, captions, or overlays

CAMERA BLOCK — exteriors (and any wide shot with open space):

CAMERA - MOST IMPORTANT RULE: The camera is LOCKED OFF and STATIC. It does not
move at all. Do NOT zoom out. Do NOT pull back. Do NOT dolly back. Do NOT pan
left or right. Do NOT crane or orbit. The framing at the end of the video is
exactly the framing at the start.

CAMERA BLOCK — interiors:

Very subtle camera motion only - a slow gentle push IN. Never pull back or zoom
out. Do NOT swing the camera around the room.

PRESERVATION BLOCK — exteriors:

DO NOT INVENT SCENERY: Everything visible in the video must already exist inside
IMAGE 1. Do NOT reveal or imagine anything beyond the edges of IMAGE 1. Do NOT
add a porch, carport, columns, breezeway, patio, fence, driveway, or any
additional structure. Do NOT add shrubs, bushes, hedges, flower beds, mulch
beds, or landscaping that is not already in IMAGE 1. Do NOT change the
neighboring houses. Do NOT change the brick, the roof, the porch railing, the
windows, the front door, or the trees.

PRESERVATION BLOCK — interiors. Inventory the room out loud in the prompt. Generic "don't change the room" is not enough; naming the objects is what makes it stick. The kitchen is the worst offender — it once lost its refrigerator and exterior door and became a different U-shaped kitchen. Example:

DO NOT REDESIGN THE KITCHEN. This is the single most important rule. The kitchen
in IMAGE 1 has, from left to right: a large stainless steel side-by-side
refrigerator with a water dispenser against the left wall, then a white exterior
door with a grid window, then white shaker cabinets with a stainless dishwasher
and a sink under a window, then a stainless electric range with a stainless
microwave above it. Every one of those elements must still be there, in the same
place, in every frame. Do NOT remove the refrigerator. Do NOT remove the
exterior door. Do NOT change the cabinet layout. Do NOT invent new doors,
windows, cabinets, or appliances.

POSITION note for busy rooms: if the presenter standing dead centre would hide a major feature, tell her where to stand — "The presenter stands at the RIGHT SIDE of the frame so she does not block the refrigerator or the door." Grok reconstructs whatever she occludes, so keeping her off the important stuff is what keeps the room honest.

Downloading each clip

Downloads require user activation — click the download icon in the results panel with a real click; a JS .click() won't fire it. The file lands in ~/Downloads/grok-video-<conversationId>.mp4.

The sandbox cannot read ~/Downloads, and it cannot reach assets.grok.com either. Move each file with a shell command on the user's Mac:

f=$(ls -t ~/Downloads/grok-video-*.mp4 | head -1)
mv "$f" "<work>/clips/01_front.mp4"

Verify every download. mv preserves mtime, so a stale file looks plausible in ls -la. Check the conversation id in the filename matches the scene you just generated, and md5 -q all clips at the end — five identical-looking files with a duplicate hash means a download silently didn't fire.

Consistency checks before you accept a clip

  • Same face, hair, blazer, shirt, and trouser colour as the other scenes.
  • The property must match the source photo. If Grok redecorated, removed an appliance, or invented structures, regenerate — that clip is unusable.
  • The anchor's mic flag should read EDP News. On wide shots an unreadable flag is acceptable, on close shots it is not.

Fixing a bad edge without regenerating

If a clip is good except for something at the frame edge (an invented structure, a neighbor's junk, a stray sliver), crop it rather than burning a generation. Ask the user to screenshot the frame and draw the box they want, then crop to it, keeping 16:9 and scaling back up:

# example: trim ~11% off the left of a 1280x720 clip
ffmpeg -y -i clips/01_front.mp4 \
  -filter_complex "[0:v:0]crop=1135:638:145:41,scale=1920:1080[v]" \
  -map "[v]" -map 0:a:0 -c:v libx264 -crf 18 -preset veryfast \
  -pix_fmt yuv420p -c:a aac -b:a 192k clips_cropped/01_front.mp4

Do this before the build so the watermark still lands at the right size and position. Show a before/after still and let the user confirm the framing.

STEP 5 — ASSEMBLE

Name the clips in scene order: 01_front.mp4, 02_living.mp4, 03_kitchen.mp4, 04_bedroom.mp4, 05_bath.mp4. The script takes them in filename sort order.

Branding assets

Two brand marks are needed, and both ship with this skill in its assets/ folder:

assets/edp_nobg.png   EDP Realty logo (transparent)   → corner watermark, all scenes
assets/eho.png        Equal Housing Opportunity logo  → end card, bottom left

Use these paths for edp_nobg.png / eho.png in the plate step below.

Both are black-on-transparent, which disappears over dark footage (tiled bathrooms, dark counters), so composite each onto a translucent white rounded plate. Same treatment for both so the marks read as a matched set:

from PIL import Image, ImageDraw

def plate(src_path, out_path):
    src = Image.open(src_path).convert('RGBA')
    src = src.crop(src.split()[3].getbbox())      # trim transparent border
    w, h = src.size
    pad = int(max(w, h) * 0.07)
    p = Image.new('RGBA', (w + pad*2, h + pad*2), (0, 0, 0, 0))
    ImageDraw.Draw(p).rounded_rectangle(
        [0, 0, p.size[0]-1, p.size[1]-1], radius=int(pad*1.5),
        fill=(255, 255, 255, 225))
    p.alpha_composite(src, (pad, pad))
    p.save(out_path)

plate('edp_nobg.png', 'edp_watermark.png')
plate('eho.png',      'eho_watermark.png')
  • EDP mark → bottom right of every scene (LOGO), and centered near the top of the end card.
  • Equal Housing mark → bottom left of the END CARD ONLY (EHO_LOGO). Not on the scenes — the brokerage compared both and chose end-card-only.

The end card

Three text lines plus the two logos. The text is env vars, so it changes per video. Ask who the video is for during script approval. Do not assume, and do not hardcode any agent as the default — EDP makes videos for several agents and teams.

Default — house branding (use when the user doesn't name an agent):

ENDLINE1="EDP Realty"
ENDLINE2="edprealty.com"
ENDLINE3=""

Agent / team CTA (use when the video is for a specific agent or team):

ENDLINE1="Call or Text the <Team Name>"
ENDLINE2="<(xxx) xxx-xxxx>"
ENDLINE3="EDP Realty"

Rules for the agent variant:

  • Line 3 keeps EDP Realty so the brokerage is named in text as well as by the logo — Indiana requires the brokerage identified in the advertising. Never let an agent CTA push it off the card.
  • The Equal Housing Opportunity mark replaces the old text line. Do not write "Equal Housing Opportunity" as a third line any more; pass EHO_LOGO and let the logo carry it.
  • Phone numbers go here, rendered by ffmpeg — never in a spoken line, because Grok garbles digits.
  • Confirm the exact team name and number with the user rather than reusing one from a previous video; agents and numbers change.

Always get the exact team name and phone number from the requesting agent; never reuse them from another video.

Then build:

TITLE="FOR SALE" \
SUBTITLE="123 Main St" \
FACTS="2 bd · 1 ba · 858 sqft · \$169,900" \
ENDLINE1="Call or Text the Example Team" \
ENDLINE2="(555) 555-0100" \
ENDLINE3="EDP Realty" \
LOGO=/path/to/edp_watermark.png \
EHO_LOGO=/path/to/eho_watermark.png \
bash scripts/build_listing_video.sh ./clips ./out/123-main-st_16x9.mp4 16x9

Re-run with 9x16 for the vertical cut if asked. Same clips, both aspects.

Keep the clip audio (KEEP_AUDIO=1, the default) — the presenter is speaking, and her voice is the whole point. The script runs speechnorm + loudnorm across the join so Grok's quiet, inconsistent per-clip mixes even out. Use KEEP_AUDIO=0 only for a silent b-roll cut.

STEP 6 — QC BEFORE DELIVERING

A coarse tile grid is not enough — sampling every ~110 frames hid both the wardrobe drift and the frame-0 pop on the first production listing. Run all five checks.

1. Frame 0 of every clip. This is where the pop-in and bad framing live:

for i in 1 2 3 4 5; do f=$(ls clips/0${i}_*.mp4)
  ffmpeg -y -v error -i "$f" -vf "select='lt(n\,3)',scale=520:-1,tile=3x1" \
    -vsync 0 -frames:v 1 f0_$i.png; done
ffmpeg -y -v error -i f0_1.png -i f0_2.png -i f0_3.png -i f0_4.png -i f0_5.png \
  -filter_complex "[0][1][2][3][4]vstack=inputs=5" frame0_all.png

Read it. She must be fully in frame, centered, at consistent size in all five.

2. Widest frame vs the source photo. Catches invented scenery. Pull a frame near the end of each clip (where the camera has drifted furthest) and stack it under the source photo:

ffmpeg -y -v error -ss 9.3 -i clips/01_front.mp4 -frames:v 1 -vf scale=900:-1 w1.png
ffmpeg -y -v error -i photos/front.jpg -vf "scale=900:-1,crop=900:410" p1.png
ffmpeg -y -v error -i p1.png -i w1.png -filter_complex "[0][1]vstack" cmp1.png

Compare object by object: structures, doors, appliances, landscaping, neighbors.

3. Wardrobe across scenes. Pull a mid-frame (-ss 7) from each clip at readable resolution and confirm blazer, shirt, and trousers match.

4. Read the end card and title card as stills. Confirm the agent name, phone number, brokerage line, and Equal Housing logo are all present and legible, and that nothing overlaps:

ffmpeg -y -v error -ss 1.8 -i out.mp4 -frames:v 1 qc_title.png
ffmpeg -y -v error -ss 54.5 -i out.mp4 -frames:v 1 qc_end.png

5. Final render grid + audio level.

ffmpeg -y -i out.mp4 -vf "select='not(mod(n\,110))',scale=340:-1,tile=5x3" -vsync 0 -frames:v 1 grid.png
ffmpeg -i out.mp4 -af volumedetect -f null /dev/null 2>&1 | grep -E "mean_volume|max_volume"

Confirm the watermark sits bottom-right and reads over both bright and dark scenes, and there's no garbled AI text anywhere. Peak should be under -1 dB.

Always tell the user the spoken lines are unverified. You cannot hear the audio in this environment, and Grok mangles words often enough that it matters — an ad once shipped with a garbled word in the spoken line. Give the user the scene timestamps to ear-check before posting, especially the address and the CTA.

STEP 7 — DELIVER

Save deliverables in a per-property folder (for example properties/<ADDRESS_SLUG>/) outside any git repository — never commit listing photos, clips, or client data to a public repo. Copy the final cut to the outputs folder so the user can open it. Present the file and give a one-line summary plus the unverified-audio note. Offer:

  • the other aspect ratio
  • an alternate end card for a different agent or team — the clips are reusable, so a second version costs one ffmpeg run, not new generations
  • a captioned cut for sound-off social feeds
  • posting to the listing's marketing channels

GOTCHAS (don't rediscover these)

  • Grok defaults to Image mode — verify Video before every send or you burn a generation on a still.
  • Grok sometimes returns two variants with a "Which video do you prefer?" prompt — click Skip, then download from the result page.
  • A "How satisfied are you with this video?" popup can cover a finished result — ignore it, the Download button still works.
  • Grok's layout and coordinates shift between sessions and the window can resize mid-run, silently moving the send arrow. Screenshot and re-locate the composer, toggles, send arrow, and download icon each time rather than trusting saved coordinates. If a send appears not to have fired, screenshot — the prompt is usually still sitting in the composer.
  • Grok mp4s carry a third mjpeg stream (thumbnail). -map 0:v grabs both video streams and ffmpeg dies with "Could not find tag for codec h264 in stream #1". Always -map 0:v:0 -map 0:a:0?.
  • Screenshot imageIds expire — recapture right before each upload.
  • file_upload rejects outputs-folder paths; use the localhost server + upload_image route.
  • New browser tabs outside the MCP tab group get closed; work in the single session tab, navigating it back and forth between the photo server and Grok.
  • navigate rejects about:blank, and prefixes bare file:// URLs with https:// — neither works. Use the localhost server.
  • Grok video generation needs a logged-in Grok account with video access, and there is a daily cap: if you hit it, stop, log which scenes are missing, and assemble what you have rather than stalling. Never purchase an upgrade.
  • The sandbox cannot delete files in a mounted folder and cannot remove the build scratch dir — do that from the Mac shell, or overwrite instead.
  • ffmpeg concat resolves relative paths against the list file's directory — always write absolute paths into the concat list.
  • -vsync 0, not -fps_mode — the sandbox ffmpeg is old enough to reject it.
  • If photo downloads are blocked (curl returns 000), the sandbox network allowlist was fixed at session start and adding domains mid-session won't help; start a new session. Test with a curl to pypi (allowed by default): 200 there plus 000 on the photo host means "allowlist not applied to this session," not "networking is broken." If blocked, write a handoff file (video-brief.md) with the listing facts, photo list, and approved script so the next session resumes at the Grok step.
  • Browsers are read-tier for computer-use: you can see them but not click. Use the Claude-in-Chrome tools for anything web.

APPENDIX — build_listing_video.sh

The assembly script ships with this skill at scripts/build_listing_video.sh (next to this file). Copy it to your working folder and call it per aspect ratio, as shown in STEP 5. Tested for 16:9 and 9:16, with and without logos, with mixed-aspect and silent source clips. Requires ffmpeg, ffprobe, and python3 on the machine that runs it.

Related Skills