Skip to content
Santiago Isaza.

F1 Content Machine · Solo Engineer · 2026

Autonomous F1
content pipeline.

Autonomous Formula 1 content pipeline: Veo 3 + NotebookLM + Nano Banana 2 generate Latin-American Spanish reels, Remotion edits, and Meta/YouTube/TikTok publish — daily.

24/7
Autonomous loop
3
Platforms published
9:16
Vertical reel format
0
Humans in the loop

Last video published

01 · System overview

One orchestrator, six engines, three publishers.

The pipeline is a single Python orchestrator that wakes up on a schedule, talks to six generation engines through whichever interface they actually expose (browser, API, or local GPU), composes the results in Remotion, and posts the final reel to three platforms — emitting a structured telemetry event at every step.

F1 CONTENT MACHINE FastF1 Telemetry source OpenF1 API Live data SQLite State store events.jsonl Telemetry log Orchestrator DAILY LOOP · PYTHON DRIVEN BY CLAUDE CODE Veo 3 8-sec video · 16:9 via Playwright MCP NotebookLM Explainer narration via Playwright MCP Nano Banana 2 Still imagery via Playwright MCP Qwen3-TTS Spanish voice (eric) Local GPU · 6 GB matplotlib + plotly + seaborn Data plot rendering Polars DataFrames No Pandas Remotion VIDEO COMPOSITION · 9:16 BURNED-IN SUBTITLES Meta Graph API Instagram Reels YouTube Data v3 YouTube Shorts TikTok Posting TikTok content API

System architecture — every node emits telemetry events

The pipeline is not a creative tool. It is a constraint-solver wearing creative clothes — 90% engineering around platform limits, 10% aesthetics.

02 · Step 01 — Decide

The orchestrator picks the day's fact.

A daily run starts with the orchestrator pulling the schedule, deciding what is worth publishing today, and writing the script and prompt set for every downstream stage. Decisions are deterministic — same input data produces the same fact selection — so a failed run can be replayed without skewing the calendar.

01Decide

Read sources, score candidates, write the script.

The schedule lives in SQLite and tracks which facts have already been published, which are reserved for race weekends, and which platform-quota windows are open. The orchestrator queries FastF1 for historical race telemetry and the OpenF1 API for current-season data, scores the candidates against simple recency and engagement heuristics, and writes the chosen fact into a typed Python object that travels through the rest of the pipeline.

From the same fact, the orchestrator generates three artefacts that the downstream stages will consume: the Spanish narration script (used by Qwen3-TTS), the natural-language prompt for Veo 3, and the data-plot specification (channel, range, presenter overlay) for matplotlib. Each is captured in code so the agent never drifts between runs.

Source
FastF1
Historical Formula 1 telemetry library — official lap data, sector times, tyre stints, weather, pit stops.
Source
OpenF1 API
Live data feed for current-season races. Used for "this just happened" facts within a few hours of session end.
State
SQLite
Local single-file database. Tracks published facts, schedule, platform quotas, retry counters. No external DB to maintain.
Output
Typed fact object
Pydantic-style typed record carrying script, video prompt, data-plot spec. Travels through every downstream stage.

03 · Step 02 — Generate

Five engines, five interfaces, one record.

The most asymmetric part of the pipeline. Three Google products are driven through a browser via Playwright MCP because they have no public API. Voice synthesis runs locally on a 6 GB GPU because no commercial TTS produced acceptable Latin-American Spanish at the price point. Data plots are rendered with the standard Python plotting stack against real F1 telemetry.

02aVideo

Veo 3 — 8-second presenter clip.

Veo 3 is invoked through the Google AI Studio web UI under a real Chrome profile, driven by Playwright MCP. The prompt is written in natural language because that is the style Veo 3 actually obeys — JSON-schema prompts produce drift. The output is a 16:9 clip capped at 8 seconds per call by the free tier; the 9:16 reframe happens later in Remotion. The orchestrator polls Google's job queue until the render completes, downloads the MP4, and writes a `video_generated` event to telemetry.

Why this
Free-tier ceiling matches the product
8 seconds is exactly the duration of a single fact-style reel hook. The cap that would kill a different product becomes a fit-for-purpose constraint here.
Prompt style
Natural language only
Veo 3 ignores JSON-schema prompts. Captured prompt templates in prompts/veo3/ so style is consistent across daily runs.
02bAudio

NotebookLM — explainer narration.

NotebookLM generates the conversational explainer audio that sits under the data-plot section of the reel. It is driven through the same Playwright MCP + real Chrome path as Veo 3. The orchestrator uploads the relevant source documents, asks for a single-host narration in the matching topic, waits for the render, and downloads the resulting MP3. Output is mixed with the Qwen3-TTS Spanish voice in Remotion.

02cImagery

Nano Banana 2 — still imagery.

Nano Banana 2 produces the still backgrounds and presenter overlays. Prompts are written as JSON here because that is the format the model obeys — natural-language prompts produce drift in the opposite direction from Veo 3. The orchestrator submits the prompt, polls for completion, downloads the PNG, and tags it with the fact ID so Remotion can pick it up.

Prompt style
JSON-structured
Mirror image of Veo 3 — what works for one breaks the other. Capturing per-tool prompt rules in code is what stops the agent drifting.
Cadence
1–3 stills per reel
Cover image, mid-reel cut, end card. Generated in parallel with video to keep the daily run under its time budget.
02dVoice

Qwen3-TTS — Spanish voice, local GPU.

No commercial TTS produced acceptable Latin-American Spanish at the volume this pipeline needs. The fix was to run Qwen3-TTS locally on a 6 GB GPU, using the eric speaker with a Latin-American instruct prompt. Cost dropped to "free after electricity" and quality went up. The synthesis call streams the WAV directly to disk; the orchestrator writes a voice_synthesised event with duration and word count.

Why local
Cost + quality
Commercial Spanish TTS APIs hit acceptable quality only at premium tiers; local GPU sidesteps both the cost and the rate limits.
Model config
eric · Latin-American instruct
Specific speaker and instruct prompt that produces consistent Latin-American Spanish without European drift across daily runs.
02eData plots

Real telemetry, real plots.

The data overlays — race-pace deltas, tyre-degradation curves, sector comparisons — are rendered from real F1 telemetry pulled from FastF1 or the OpenF1 API. Plotting is done with the standard Python stack (matplotlib, plotly, seaborn) writing PNGs at the exact aspect ratio Remotion expects. All data work uses Polars — Pandas is explicitly forbidden in the codebase. The streaming Parquet path keeps the data layer fast and predictable as the catalogue grows.

04 · Step 03 — Assemble

Remotion stitches the reel.

Once the five generation engines have done their work, the orchestrator hands a folder of MP4s, PNGs and WAVs to Remotion. The editing logic itself is versioned in the repository — a React-style composition tree that turns asset bundles into final 9:16 vertical reels, deterministically.

03Assemble

Composition is code, not GUI.

The Remotion project lives inside the monorepo and is invoked via its CLI. A single composition consumes the asset bundle and produces a final MP4 with: the 16:9 → 9:16 reframe (presenter-centred crop), the Qwen3-TTS Spanish narration on the primary track, the NotebookLM bed on the secondary track, the burned-in TikTok-style subtitles (per-word emphasis on the punchlines), and the royalty-free outro sting. Because the composition is just code, the editing decisions are reviewable in pull requests and rerunnable across the entire back-catalogue if the template changes.

Output
9:16 vertical MP4
Single deliverable file per fact. Same file goes to all three publishers.
Subtitles
Burned-in, per-word
TikTok-style on-rhythm subtitle emphasis. Burning them in removes the platform-dependent caption rendering risk.
Audio
Two tracks · royalty-free
Qwen3-TTS narration on top, NotebookLM bed below, ducking applied automatically. Every audio source is royalty-free.
Branding
Consistent presenter
Same presenter template across every reel — that's what makes the channel feel like a channel rather than a series of one-offs.

05 · Step 04 — Publish

Same reel, three platforms.

The orchestrator hands the final MP4 to three publisher modules, each of which speaks to a single platform's public API. The schedule pre-computes the publish time per platform per timezone so that, for instance, an evening LATAM post happens in the local prime-time window without operator intervention.

04Publish

Three public APIs, one schedule.

Instagram
Meta Graph API
Reels container endpoint. Two-step submit: media upload + publish call, with status polling between them.
YouTube
YouTube Data API v3
Shorts upload. Resumable upload session for the MP4, metadata payload for the caption, then videos.insert.
TikTok
Content Posting API
Direct post or upload-only. Audited callback handles the asynchronous publish-state confirmations.
Scheduling
Per-platform prime time
Posts land in the LATAM evening window on each platform — the orchestrator pre-computes the local time per-publisher.

06 · Browser automation

A real Chrome profile, not a bundled bot.

Three of the five generation engines — Veo 3, NotebookLM and Nano Banana 2 — have no public API at the price point this pipeline runs at. The pipeline drives them through their web UI, but with a deliberate twist: it attaches to a real Chrome profile over the remote debugging port instead of Playwright's bundled Chromium, because Google fingerprints and blocks the latter as a bot.

BROWSER AUTOMATION Orchestrator Playwright MCP Real Chrome (CDP) Google Veo 3 UI drive(veo3_prompt) attach via 9222 type prompt · submit render queued · polling render complete · download URL MP4 + telemetry event
Why a real profile
Google blocks Playwright's bundled Chromium as a bot
Detection runs on canvas fingerprint, WebGL renderer, user-agent and a dozen other signals — they all flip under bundled Chromium.
Why MCP
Browser automation as a tool Claude Code can call
Playwright MCP exposes the browser as a set of MCP tools so the orchestrator can navigate, type, click, and read the page from within a conversation.
Remote port
Chrome started with --remote-debugging-port=9222
The pipeline attaches over CDP. Anything that the human's Chrome session sees, the pipeline sees — sessions, cookies, the lot.
Failure mode
Quota walls and CAPTCHA
Telemetry catches both. Quota walls trigger a backoff schedule; CAPTCHAs raise a manual-intervention event into the dashboard.

07 · Telemetry layer

Every step writes a typed event.

Every component in the pipeline writes structured events to data/telemetry/events.jsonl AND broadcasts the same events live over a WebSocket. The dashboard subscribes to the WebSocket; the post-mortem reads the JSONL. One vocabulary, two consumers, zero log archaeology.

// data/telemetry/events.jsonl — excerpt from a daily run
{"ts": "2026-05-13T03:00:01Z", "event": "run_started", "run_id": "r_2026-05-13"}
{"ts": "2026-05-13T03:00:04Z", "event": "fact_selected", "fact_id": "f_4831", "topic": "race_pace_delta", "source": "FastF1"}
{"ts": "2026-05-13T03:00:12Z", "event": "video_generation_queued", "engine": "veo3", "duration_target": 8}
{"ts": "2026-05-13T03:03:46Z", "event": "video_generated", "engine": "veo3", "duration_ms": 214000, "bytes": 2147483}
{"ts": "2026-05-13T03:04:02Z", "event": "voice_synthesised", "engine": "qwen3_tts", "speaker": "eric", "locale": "es-419", "words": 94}
{"ts": "2026-05-13T03:05:18Z", "event": "remotion_render_complete", "output_mp4": "out/f_4831.mp4", "frames": 630}
{"ts": "2026-05-13T20:15:00Z", "event": "published", "platform": "instagram", "status": "ok", "post_id": "ig_18024…"}
{"ts": "2026-05-13T20:15:18Z", "event": "published", "platform": "youtube", "status": "ok", "video_id": "yt_xK9p…"}
{"ts": "2026-05-13T20:16:02Z", "event": "publish_warning", "platform": "tiktok", "reason": "quota_near_limit", "remaining": 3}
{"ts": "2026-05-13T20:16:15Z", "event": "run_complete", "duration_ms": 62173000, "status": "ok"}
JSONL stream
Append-only, grep-friendly
One event per line — every component writes through a single telemetry client that guarantees the schema and the file lock.
WebSocket channel
Live event stream to the dashboard
The same event the JSONL file gets is broadcast immediately. Operators (or Claude Code) watch the dashboard during a run.
Severity tags
info · warning · error · manual
manual is the special tag for CAPTCHAs and auth walls — the dashboard surfaces it as a banner so a human can step in fast.
Replay value
Multi-hour debug session → 5 minutes
The first time the pipeline failed silently, the JSONL stream said exactly which engine refused which prompt. That paid for the entire telemetry layer.

08 · Daily run timeline

24 hours, one orchestrator.

The daily run is intentionally split across the calendar day. Generation happens in the early morning when the LATAM market is asleep; publishing happens in the evening prime-time window per platform. Quota resets are honoured at midnight UTC.

24H 00 03 06 09 12 15 18 21 24 DECIDE GENERATE ASSEMBLE PUBLISH · LATAM PRIME UTC quota reset IDLE · CACHE WARM ACTIVE STAGE LONG-RUNNING (POLLING) IDLE QUOTA RESET

09 · Full tech stack

Everything in the codebase.

A breakdown by category. Choices favour local execution where possible, typed contracts at every boundary, and append-only telemetry over rotating logs.

Generation

  • Veo 38-second video clip
  • NotebookLMExplainer narration
  • Nano Banana 2Still imagery
  • Qwen3-TTSSpanish voice (local)
  • matplotlib · plotly · seabornData plots

Data

  • FastF1Historic telemetry
  • OpenF1 APILive data
  • PolarsDataFrames · no Pandas
  • SQLiteState store
  • JSONLTelemetry stream

Runtime

  • PythonOrchestrator language
  • Claude CodeDevelopment partner
  • Playwright MCPBrowser automation
  • Real Chrome (CDP)Anti-bot detection
  • WebSocketLive dashboard channel

Output

  • RemotionVideo composition (CLI)
  • Meta Graph APIInstagram Reels
  • YouTube Data API v3YouTube Shorts
  • TikTok Content PostingTikTok publish
  • MP4 (9:16)Single deliverable

10 · Platform constraints

The architecture is shaped by limits.

Every architectural choice traces back to a hard cap. The system is not a creative tool — it is a constraint-solver wearing creative clothes.

Video duration
Veo 3 caps free-tier clips at 8 seconds
Matches a single-fact reel hook. The cap shapes the script length and the data-plot density.
Aspect ratio
Generation is 16:9 → reframed to 9:16 in Remotion
Veo 3 outputs 16:9 only. The reframe is a presenter-centred crop done deterministically in Remotion.
Voice market
Latin-American Spanish only — no European drift
Qwen3-TTS eric with a Latin-American instruct prompt. Reviewed weekly to catch any drift toward Iberian pronunciation.
Licensing
Royalty-free across audio, imagery and music
Every audio bed and visual asset has a documented licence in the manifest — monetisation requirement.
Platform quotas
Daily-limit and overflow signalled through telemetry
Per-platform per-day caps. Quota windows reset on a mix of UTC midnight and rolling 24h windows; the orchestrator tracks both.
Bot detection
Real Chrome + CDP — bundled Chromium gets blocked
Google fingerprints Playwright's default Chromium. Attaching to a real human Chrome profile over 9222 sidesteps the detection.
Quota resets
Bi-weekly quota windows on the generation side
Veo 3 and Nano Banana 2 reset on a bi-weekly cycle. The schedule honours both reset clocks so a run never burns half its budget on day one.
Prompt drift
Per-tool prompt rules captured as code
Natural language for Veo 3, JSON for Nano Banana 2, instruct prompts for Qwen3-TTS — each in its own templates folder.

11 · Cost economics

Cheap because most of the heavy lifting is local.

The architecture's defining cost choice is running TTS on a local 6 GB GPU instead of a commercial API. That single decision moves Spanish voice synthesis from a monthly bill to an electricity line, while delivering quality that the commercial APIs only hit at premium tiers.

Voice synthesis

~0 USD/run
Qwen3-TTS on a local 6 GB GPU. The marginal cost is the electricity for ~10 seconds of inference per fact. The commercial equivalent at the same volume would run into multi-hundred-USD monthly bills at acceptable quality tiers.

Video generation

0 USD/run
Veo 3 free-tier within the bi-weekly quota window. The 8-second cap that would kill a different product is exactly the duration this format needs, so the free tier is structurally sufficient.

Data / telemetry

0 USD/run
SQLite, FastF1, OpenF1 and the JSONL telemetry stream are all local. No managed database, no observability vendor, no per-event billing.

Publishing

0 USD/run
Meta Graph API, YouTube Data API v3 and TikTok Content Posting API are free at the volume this pipeline runs at. Per-call quotas are tracked by the orchestrator and surfaced through telemetry.

12 · Lessons learned

What this build taught me.

Lesson 01
Autonomous content pipelines are 90% platform-limit engineering, 10% creative. The Veo 3 cap, NotebookLM's prompt rules, the bi-weekly quota resets, the Latin-American/European Spanish distinction and the royalty-free requirement shaped the architecture far more than any aesthetic choice.
Lesson 02
Write prompts in the exact style each tool actually obeys — natural language for Veo 3, JSON for Nano Banana 2, instruct prompts for Qwen3-TTS — and capture those rules as code so the agent never drifts between runs. Per-tool templates beat one-size-fits-all every time.
Lesson 03
Stand up a structured telemetry layer before you need it. The first time the pipeline failed silently, the JSONL log and the WebSocket dashboard collapsed the debug loop from hours to minutes. Pay this cost up front and you pay it once.
Lesson 04
Choose the data layer once, choose it well. Polars from day one kept the pipeline fast and predictable as the catalogue grew. No retrofit, no rewrite, no Pandas fallback. The same call to pl.scan_parquet handles a single race and a full season.
Lesson 05
Run the cost-heavy step locally when you can. Qwen3-TTS on a 6 GB GPU beat every commercial Spanish TTS API on both cost and quality. The whole monthly bill turned into an electricity line.
Lesson 06
When the upstream product has no API, drive its UI through a real browser. Playwright MCP plus a real Chrome profile over the remote debugging port gets the orchestrator past every Google bot-detection check, without paying for an unofficial scraping service.