Ideas

parked-sketch-sandbox-autovideo-moneyprinter-2026-06-11

DARE.CO.UK · PARKED SKETCH · 2026-06-14

Mirrored from ~/.claude/.../memory/parked_sketch_sandbox_autovideo_moneyprinter_2026-06-11.md. This is a design sketch parked for future build — read for context, not as a current deliverable.

sandbox.gf.cx /autovideo slot idea — generate net-new short-form video from a topic via MoneyPrinterTurbo; needs a VM data-plane (can’t run on a Worker), parked pending POC


Idea (Dan, 2026-06-11): “sandbox /autovideo idea using https://github.com/harry0703/MoneyPrinterTurbo” — a new sandbox.gf.cx slot that generates short-form videos from a topic.

What MoneyPrinterTurbo (MPT) does: topic/keyword → LLM writes script → derives search terms → pulls stock clips (Pexels/Pixabay) → TTS voiceover (edge-tts / Azure) → auto-subtitles → MoviePy composites → 9:16 or 16:9 MP4 + BGM. Ships a Streamlit WebUI AND a FastAPI. Heavy Python + ffmpeg + MoviePy stack; Docker support.

THE ARCHITECTURAL FORK (decides everything): MPT cannot run on a Cloudflare Worker (edge has no Python/ffmpeg/MoviePy and renders take minutes). Unlike the /fal slot (pure API passthrough, [[project_sandbox_gf_cx_fal_slot_shipped_2026-06-07]]), autovideo needs a real compute host. Clean split: - sandbox Worker = control planePOST /api/autovideo/generate (X-Sandbox-Key gated like every slot), validate {topic, aspect, voice, duration}, enqueue job → R2 gf-cx-media/sandbox/autovideo/jobs/<id>.json status=queued. - gf-cx-singapore VM = data plane — runs MPT’s FastAPI (Python + ffmpeg already live there for the gvr-* pipelines), renders, uploads MP4 → gf-cx-media/sandbox/autovideo/out/<id>.mp4. - GET /api/autovideo/{status,result} + health; tier-2 in surfaces.json. sandbox.gf.cx is wrangler-direct (no git) — deploy npx wrangler deploy.

Distinct from [[pattern_happiness_video_clip_ingest_pipeline_2026-06-10]]: that ANNOTATES existing source video; autovideo GENERATES net-new short-form from a topic. No overlap.

Prereqs before a POC: - ✅ Pexels API key DONE (2026-06-11): op://Code Shared/Pexels/api_key (item “Pexels”, field api_key, 56 chars) — verified live: /videos/search → HTTP 200, total_results 8000 for “ocean”. The exact endpoint MPT uses is authorized. - LLM provider MPT supports — it does NOT take Anthropic natively (last checked), so either stand up an OpenAI-compatible shim pointing at Claude, or use a DeepSeek/Moonshot key. Decide at build time. - VM CPU/disk headroom + the home/VM upload ceiling (~8.5 MB/s anchor, [[feedback_home_upload_ceiling_is_the_real_mac_bottleneck_2026-06-07]]) for clip pull + result push. - Content/licence: Pexels licence permissive → fine for sandbox.

Gotchas to encode at build: MoviePy renders are minutes-long + CPU-heavy (job queue, not synchronous response) · edge-tts is free but rate-limited · MPT’s API is unauthenticated by default → keep it bound to the VM’s Tailscale iface, never public · vertical 9:16 default suits Shorts/Reels.

BUILD STATUS — control plane SHIPPED + E2E-verified (2026-06-11 eve)

Dan green-lit “build the autovideo POC … see how far we can go”. Built:

Control plane (sandbox Worker, version fd18d556) — LIVE + TESTED E2E. Endpoints in ~/Code/sandbox.gf.cx/src/index.ts: /api/autovideo/ health (queue depth) · generate (X-Sandbox-Key gated, 202, writes job→R2 sandbox/autovideo/jobs/<id>.json status=queued) · status?id= · result?id= (streams MP4 from R2, range-aware 206 + content-range) · claim (VM leases oldest queued, →rendering) · update (VM patches status/progress/error) · upload?id= (VM streams finished MP4 bytes → R2 through the Worker so the VM needs NO R2 S3 creds). Verified the full lifecycle generate→status→claim→update →upload→result with an ephemeral key (mint+bind+test in one shell, key never printed); gate returns 401 without key. R2 gotcha relearned: wrangler r2 object put defaults to the LOCAL sim bucket — MUST pass --remote (silent 404 on read otherwise).

Gate key: dedicated AUTOVIDEO_SANDBOX_KEY (Worker secret), VOIP_SANDBOX_KEY accepted as fallback. 1P service account is READ-ONLY on “Code Shared” (op item create(101) no permission) — same wall the voip POC hit — so the durable key must be created interactively by Dan.

LLM provider DECIDED + VERIFIED: Anthropic via OpenAI-compat. MPT openai provider → openai_base_url=https://api.anthropic.com/v1/, key op://Code Shared/anthropic api-key/credential, model claude-haiku-4-5. Live-tested 2026-06-11: POST /v1/chat/completions → HTTP 200, correct reply. No new vendor, reuses portfolio Anthropic spend, pennies/render.

Data plane (gf-cx-singapore = GCP gvr-ingest-singapore) — PROVISIONED + LIVE (2026-06-11 eve). ~/Code/sandbox.gf.cx/autovideo/: poller.py · provision.sh · vm_deploy.sh · README.md. vm_deploy.sh ran clean: apt ffmpeg/imagemagick, cloned MPT, venv+deps, wrote config.toml (provider=openai, model=claude-haiku-4-5, Pexels), started two tmux sessions mpt-api (FastAPI :8080) + autovideo-poller. MPT API contract: POST /api/v1/videos {video_subject, video_aspect, voice_name, video_source=pexels, video_clip_duration, video_count}, poll GET /api/v1/tasks/{id} until combined_videos/videos populated. FULL END-TO-END PROVEN 2026-06-11 ~21:38Z — job …-567ac96d “compounding habits” → Claude script ✓ Pexels ✓ edge-TTS ✓ MoviePy composite ✓ → 1080×1920 / 85s / 42 MB MP4 landed in R2 (sandbox/autovideo/out/<id>.mp4) and downloaded+played. NOTE: the two earlier renders MPT-composited fine but errored at the fetch-back step — do NOT trust an “MPT render finished” as end-to-end; only status=done + a populated result_key counts. Render wall-time ~13 min (50%→75% MoviePy is the slow stretch; flips to done ~14 min). Web UI LIVE at sandbox.gf.cx/autovideo/ (topic box, aspect, voice; key in localStorage). VM = gvr-ingest-singapore zone asia-southeast1-b proj ambient-odyssey-498416-h3 acct studio@audreylam.com; SSH via gcloud compute ssh. MPT API on 127.0.0.1:8080 (GCP firewall blocks 8080) — never public.

Both original blockers RESOLVED: gcloud re-authed; gate key SELF-GENERATED by vm_deploy.sh (1P SA can’t create items, AND op item create via the ! shell ALSO hits the read-only SA — so the durable key lives on the Worker secret + VM ~/autovideo.env only; mirror to 1P via the desktop app later if wanted). Grab the key on the VM: grep AUTOVIDEO_SANDBOX_KEY ~/autovideo.env | cut -d= -f2.

THE MUTE-OUTPUT BUG (found 2026-06-11 late, after Dan flagged the voice picker vs a silent MP4): every render up to job …-567ac96d shipped a SILENT video. Root cause: MPT’s task result exposes TWO file sets — videos = final-N.mp4 (the final mux: TTS voiceover + subtitles) and combined_videos = combined-N.mp4 (just the stitched Pexels clips, no audio). Confirmed in MPT app/services/task.py:374-375. The poller’s await_render did t.get("combined_videos") or t.get("videos") — preferring the SILENT intermediate. VM log was the tell: render complete → …/combined-1.mp4. FIX: flip to t.get("videos") or t.get("combined_videos"), redeploy poller to VM + restart tmux. PROVEN: fresh job …-c5c7f90f (UK Ryan) came back H.264 + AAC stereo (ffprobe codec_type=audio channels=2). LESSON: ffprobe must check for an AUDIO stream, not just that an MP4 exists — a playable file ≠ a correct file. The 42 MB 567ac96d clip delivered earlier is mute; re-render if it matters.

Gallery shipped (Worker 8f504e04): public GET /api/autovideo/list (newest-first job summaries, cap 60, reads R2 sandbox/autovideo/jobs/) + page dist/autovideo/gallery/index.html (responsive grid, inline aspect- correct <video> per done job, error msg shown, status placeholder for in-flight) linked from the main /autovideo/ page. Orphan gotcha: restarting the poller drops its IN-MEMORY render tracking, so a job mid-render (e.g. …-666932e8) is stranded at status=rendering forever — reset to queued or just fire fresh.

Two earlier bugs found + fixed live: (1) /claim on EMPTY queue returned 500 — a jsonResp(204, {body}) (HTTP 204 forbids a body → Workers runtime throws); fixed to 200 {job:null}. (2) Poller’s requests.get(video_uri) failed No scheme supplied because MPT returns the result as a RELATIVE path (/tasks/<id>/combined-1.mp4) when app.endpoint is unset; fixed — poller now reads from local disk (<MPT_DIR>/storage/<relpath>) when the uri is relative. Worker now version ab0a3f1c. GOTCHA that bit twice: editing poller.py on the Mac is NOT enough — the VM runs ~/MoneyPrinterTurbo/autovideo_poller.py in tmux autovideo-poller. Must scp the file to the VM AND restart that tmux session for the fix to take. The relative-path “fix” sat un-deployed through two failed renders because only the Mac copy had it. Redeploy poller w/o full provision: gcloud compute scp poller.py dansellars@gvr-ingest-singapore:~/ (SSH user is dansellars, NOT the @-laden account — --account=studio@audreylam.com handles auth; studio@…@instance confuses scp arg-parsing) → ssh: cp ~/poller.py ~/MoneyPrinterTurbo/autovideo_poller.py && tmux kill-session -t autovideo-poller then relaunch with venv + . ~/autovideo.env. Errored jobs do NOT auto-retry — fire a fresh /generate (do it from the VM so the gate key stays in ~/autovideo.env, never the transcript).

MPT upstream fix SHIPPED as PR #1026 (https://github.com/harry0703/MoneyPrinterTurbo/pull/1026, from xlab-nyc fork): generate_terms() returned empty search terms on fenced/multi-line JSON (the failed to generate video terms: Expecting value warning we hit) — added _strip_code_fence() + re.DOTALL to the array regex, matching the maintainer’s existing _parse_social_metadata handling. Branch fix/robust-json-parse-for-non-openai-llms, proven against real failure modes.

Sister sandbox slots for pattern reference: fal (passthrough), voip (webhook receivers, [[parked_sketch_voipms_advanced_tools_sandbox_poc_2026-06-09]]), one-pager (lead capture, [[project_sandbox_one_pager_lead_capture_slot_2026-06-11]]).

← /reportsSource: parked_sketch_sandbox_autovideo_moneyprinter_2026-06-11.md · Rendered 2026-06-15 12:17