Build a static explainer site from a folder of course video files. Three-step pipeline — (1) transcribe videos locally with whisper.cpp, (2) generate blog posts from transcripts using parallel Claude Code agents, (3) assemble into a tabbed single-page site. Use when asked to create a course site, transcribe videos for a course, turn video lectures into blog posts, or build an explainer site from recordings. Triggers on "course site", "transcribe videos", "video to blog", "explainer site from videos".
Turn a folder of video files into a readable, tabbed static site where each video becomes a blog post written in the teacher's voice.
brew install ffmpegbrew install whisper-cpp (provides whisper-cli)~/whisper-models/ggml-large-v3-turbo.binRun the bundled transcription script:
python3 scripts/transcribe_videos.py /path/to/video/folder
This extracts audio, runs whisper.cpp, and produces .json, .srt, and .txt files in <folder>/transcripts/. Skips already-transcribed files.
For long-running transcription (many videos, ~1.5hrs each), run in background:
nohup caffeinate -i python3 scripts/transcribe_videos.py /path/to/folder > ~/transcribe.log 2>&1 &
Known issue: Dropbox paths with unicode characters (curly apostrophes like ') can break os.listdir. Use os.walk from a known ASCII parent to discover the real path.
Dispatch one agent per transcript to generate blog posts. Posts go in <folder>/site/posts/.
Important: Write posts to a local (non-Dropbox) staging directory if the source folder is in Dropbox, since Dropbox smart sync can dehydrate newly written files to 0 bytes. Copy to Dropbox after all posts are complete.
For each transcript, dispatch an agent with instructions like:
Read the transcript at
<folder>/transcripts/<name>.txt. Write a blog-style HTML post (no page wrapper — just content starting with<h2>) that:
- Is written in the teacher's voice (first person), not a summary
- Uses
<h3>sections to organize the teaching topics- Calls out student Q&A using
<blockquote>with the question in<strong>tags- Transcribes guided meditations directly with timestamps referencing the video, using
<div class="meditation-marker">for timestamp callouts- Separates teaching, Q&A, and meditation into distinct sections
- Save to
<staging>/posts/<name>.html
Also generate an overview.html that introduces the course.
After all posts are written, run the assembly script:
python3 scripts/assemble_site.py /path/to/video/folder
This reads all .html files from <folder>/site/posts/, creates tab navigation, and outputs a single <folder>/site/index.html with Medium-inspired typography.
Posts are HTML fragments (no <html>, <head>, or <body> tags). Structure:
<h2>Class Title</h2>
<h3>Topic Section</h3>
<p>Teaching content in the teacher's voice...</p>
<h3>Q&A</h3>
<blockquote>
<p><strong>Student question here?</strong></p>
<p>Teacher's answer...</p>
</blockquote>
<h3>Guided Meditation [starts at 1:23:45]</h3>
<div class="meditation-marker">
Guided meditation begins at 1:23:45 in the video
</div>
<blockquote>
<p>Direct transcription of meditation instructions...</p>
</blockquote>
The pipeline is idempotent — it skips completed work at each step:
.json outputTo process a new course, just point the scripts at a new folder of videos.