While rebuilding natehaskins.com I started recording tutorial footage again. The obvious move was to ask a cloud model to caption each file — but a twenty-minute screen recording is expensive to process that way, and I would be paying again every time I re-exported.
Why local first
Deterministic work belongs on your machine. Transcription, ffmpeg filters, and file copies are repeatable scripts. Judgment calls — how to phrase a blog post, what to cut from a tutorial, how to frame a DevRel page — are where I still want a language model in the loop.
So I asked Cursor to scaffold a small Python tool in this repo: faster-whisper for speech-to-text, ASS for styled captions, ffmpeg for burn-in. The agent handled Dockerfile updates, gitignore rules for tmp/ and .mp4 files, and npm scripts to run it the same way every time.
What lives in the repo
tools/caption/caption.py does the heavy lifting. npm run caption -- path/to/video.mp4 transcribes, writes captions.ass, and outputs a new MP4 with readable bottom-center text sized for mobile. Work artifacts land in tmp/.caption-<name>/ — transcript JSON, ASS, listing draft, and thumbnail — all gitignored.
For YouTube, npm run youtube -- prepare runs transcription (or reuses it), builds youtube-listing.json from the transcript — title, description, chapter timestamps — and generates a simple thumbnail.jpg (blurred frame + title overlay). npm run youtube -- upload publishes the video and sets the thumbnail when the file is present.
Dev container matters
ffmpeg, Python, and Whisper dependencies do not belong on my host OS if I can avoid it. The .devcontainer Dockerfile installs what the caption tool needs; Cursor updates it as the scripts evolve. Rebuild the container, run the same npm command — that is the whole portability story.
Chapters
Select a chapter to jump the video above directly to that moment.
What comes next
Phase one was captions. Phase two is wiring the output into this site automatically — upload to R2, create the blog post, sync the Trello card. That is the same pattern as the rest of this rebuild: local tools for bytes, agents for structure, git for history.
If you are recording tutorials in Cursor and burning tokens on every export, try pushing the ffmpeg-shaped work local first. You only need to build the pipeline once.