Skip to content

Latest commit

 

History

History
199 lines (175 loc) · 13 KB

File metadata and controls

199 lines (175 loc) · 13 KB

Lesson Studio: Project Handover

Written 2026-07-12, end of the session that scoped this project, for a new chat thread to pick up cleanly. Nothing has been built yet; this document is the full requirements/architecture record from the design conversation. No code exists in this repo yet.

Revised 2026-07-12 (spec review pass): storage model changed from record-direct-to-SMB to local working storage with NAS archive; output/recording format decisions added (resolution, audio codec path, target browser); PPTX speaker-notes import added; take management and caption chunking rules added; Mac prerequisites listed.

What it is

A self-hosted tool for recording narrated lesson videos, similar in spirit to Descript's core recording workflow but self-hosted and purpose-built for Dave's use: feed it slides (a PowerPoint deck or image files) and a script (what to read per slide), and it gives a teleprompter to read from while recording voice-only narration, then produces a finished MP4 by pairing each slide with its narration.

Core workflow

  1. Create a project: one per video being produced.
  2. Import slides: upload a .pptx deck (auto-split into one image per slide) or upload image files directly.
  3. Import the script: paste or upload text, mapped one block per slide/scene. When a .pptx is uploaded, offer to import the script from the deck's speaker notes (one note per slide maps naturally to one script block per scene); Dave can then edit from there instead of pasting from scratch.
  4. Edit before recording: reorder slides, replace a slide's image, delete a slide, add a new slide, and edit/add/delete/reorder script blocks to match, all before any recording happens. This is a full pre-production editing pass, not just an import step.
  5. Record scenes: teleprompter shows the current slide full-screen with the script auto-scrolling at a lesson-appropriate pace (default ~145 words/minute, adjustable). Record scenes back-to-back in one sitting, or jump to any single scene independently.
  6. Produce the video: slides plus narration combined into a single MP4.
  7. Replace a scene: re-record just one scene's audio (or swap its slide) and re-produce the video without touching anything else, after recording has already happened.

Functional requirements

  • Project management: each project keeps its slides, script, per-scene audio, and produced video together, browsable/re-openable later.
  • Slide ingestion: .pptx auto-converted to one image per slide; also accepts image files directly.
  • Pre-production editing: reorder, replace, delete and add slides; same for script blocks; keep slides and script blocks in sync as the deck changes.
  • Script handling: one script block per scene, editable after import.
  • Teleprompter: adjustable scroll speed with a lesson-pace default (~145 wpm), pause/resume, manual speed nudge during recording.
  • Recording: microphone-only, no webcam. This pairs a static slide with voice, not a talking-head overlay. Record in sequence or one scene at a time.
  • Take management: re-recording a scene must never destroy the previous take before the new one is safely written. Keep prior takes per scene (with a simple "active take" selector); storage is cheap, re-recording a good take by accident is expensive.
  • Production: mux each slide+audio pair into a segment, concatenate into the final video. Output MP4 (H.264/AAC), universally compatible with Moodle and other LMS platforms.
  • Output format: 1920x1080 (16:9) default. All slide images are normalised to the output resolution at production time (scale-to-fit with letterbox/pillarbox padding for odd aspect ratios), so directly-uploaded images of any size are safe and the concat demuxer always sees uniform streams.
  • Re-production: replacing one scene's audio/slide and re-running production must be fast and must not require re-recording anything untouched.
  • Captions: generate an SRT file per produced video, script-timed. Captions are built from the known script text, evenly spread across each scene's actual recorded length (decided over speech-accurate transcription, for v1 simplicity; can upgrade later if needed). Chunk the script into caption cues at sentence/clause boundaries, max ~2 lines of ~42 characters each (standard subtitle convention), rather than one giant cue per scene.

Non-functional requirements

  • UI: best-in-class, intuitive. This is an explicit design bar, not just a functional checkbox; budget real design effort, including the branding below.
  • Branding: needs a cool, modern logo/visual identity. Not yet designed; do this as part of the design phase, ideally previewed with the visualize tool before committing engineering time to it.
  • Runs in a browser, driven from Dave's MacBook Pro. Target browser: Chrome (or another Chromium browser). This pins MediaRecorder behaviour to one well-understood implementation (WebM/Opus output) instead of chasing Safari's quirks; the app should detect and warn on unsupported browsers.
  • Storage: source files and finished videos persist per-project, held centrally so they survive a Mac restart. See the revised storage model in the architecture section: local working directory, NAS as archive target, not record-direct-to-SMB.

Architecture decision (made this session, reasoning included)

Local-first on the Mac, not a NAS-hosted service. Dave flagged that his MacBook Pro likely outguns the NAS. Two concrete technical reasons this makes local-first clearly the right call, not just a preference:

  1. The browser's microphone API (MediaRecorder / getUserMedia) only works on secure origins: HTTPS or localhost. Running the app locally gets a secure origin for free, with zero certificate setup. Hosting it on the NAS over the LAN would have forced a TLS cert (self-signed or Let's Encrypt) onto every recording device.
  2. Apple Silicon has a hardware H.264 encoder ffmpeg can use directly (-c:v h264_videotoolbox), fast and efficient, versus software-encoding on a NAS that's already running Gitea, Keycloak, Moodle and a media stack (see [[project-strayanas]] / [[project-compass]] memory for what else lives on that box).

Resulting shape:

  • Local server + browser UI, launched from the Mac with one command, opened at http://localhost:PORT.
  • Tech stack (Dave delegated this choice): Node.js backend + React frontend, matching patterns already proven in the Compass codebase (~/Documents/Claude/Projects/compass), so conventions and any reusable pieces (e.g. file upload handling, project-based data model) can be borrowed rather than reinvented.
  • PPTX to slide images: LibreOffice headless (soffice --headless --convert-to pdf then pdftoppm), the same pipeline already used elsewhere in these projects (see the docx/pptx Claude skills, and the DHE Moodle build pipeline's slide generation).
  • Video production: ffmpeg. Per scene: loop the static slide image for the duration of its audio track, encode with h264_videotoolbox, AAC audio. Concatenate all scene MP4s into the final video via ffmpeg's concat demuxer. Note two implementation details decided in review: MediaRecorder in Chrome records WebM/Opus, so each scene's audio is transcoded to AAC during the per-scene mux (no extra pipeline step needed); and h264_videotoolbox ignores CRF-style quality flags, so use an explicit bitrate (e.g. -b:v 4000k is ample for static slides).
  • Storage (revised in spec review): the app works against a local working directory (e.g. ~/LessonStudio/projects/), so recording, editing and production all hit local disk. Recording live audio and running ffmpeg directly against an SMB mount was rejected: network latency during MediaRecorder flushes, SMB locking oddities, and a hard dependency on the share being mounted make it fragile exactly when it hurts most (mid-recording). The NAS remains the system of record via an explicit archive/sync step: a per-project "Archive to NAS" action (and an "everything not yet archived" indicator) copies the project to the NAS share. Restoring a project from the archive is the inverse. Exact NAS share/path still to be decided (open item below).
  • Code repository: this repo (lesson-studio), intended to live on the NAS Gitea instance (http://192.168.50.200:3000), matching how Compass, dev-health-astro and Dave's Website are set up. Not yet pushed to Gitea as of this handover, see Open items below.

gstack status (installed this session, not fully verified)

Dave asked that gstack (Garry Tan's open-source Claude Code skill suite, github.com/garrytan/gstack) be used to drive this project's design/build/QA/review cycle. Status:

  • Installed to ~/.claude/skills/gstack/ via git clone plus ./setup --host claude --no-team --no-plan-tune-hooks (standard mode, no team mode, no global Claude Code hooks added to ~/.claude/settings.json).
  • bun was installed via Homebrew as a prerequisite (brew install oven-sh/bun/bun, v1.3.14).
  • The gstack skill router is registered and its 23 skills' files are present (~/.claude/skills/gstack/); core planning/review/text-based skills should be usable.
  • The Playwright Chromium download (needed for gstack's browser-driven QA skill only) hung twice, both times showing the download reach 100% and then stalling indefinitely: elapsed wall-clock time far exceeding CPU time consumed (e.g. 44 minutes elapsed, 1.37 seconds CPU time), no active network connection, and an incomplete extraction (39 files / 428K instead of the ~2,000 files / ~350-400MB a real Chromium install produces). Killed both times; the second attempt was supervised with a 5-minute background timeout and still hung the same way. This looks like a genuine, reproducible hang in Playwright's post-download extraction/verification step on this machine, not a slow network.
  • Per Dave's instruction, proceeding without Chromium for now. Only gstack's browser-QA skill needs it; the design/planning/code-review/release skills do not. Chromium can be retried later (maybe at a different time, or investigate whether a specific Playwright/Chrome version pin, a manual curl download into the expected cache path, or PLAYWRIGHT_DOWNLOAD_HOST pointed at a mirror resolves the hang) whenever the browser-QA skill is actually needed.
  • Not yet done: actually invoking gstack (its router skill, or whichever of its 23 skills fits a "scope a new project" step) to run this requirements doc through its own design process. Dave asked to pause before that happened, so it's the first real task for the next session.

Decisions already made (don't re-litigate these)

  • Project name: Lesson Studio.
  • Recording is audio-only (no webcam/PIP).
  • Tech stack: Node.js + React (Dave delegated this choice).
  • Captions: script-timed SRT, not speech-accurate transcription, for v1.
  • Architecture: local-first on the Mac; NAS is storage, not compute.
  • gstack: use it for the design/build/QA/review cycle; proceed without its browser-QA component for now.
  • Storage model (spec review): local working directory for all recording/editing/production; NAS is an explicit archive target, not the live working store.
  • Output: 1920x1080 MP4 (H.264/AAC), slide images normalised with letterbox padding.
  • Target browser: Chrome/Chromium; audio recorded as WebM/Opus and transcoded to AAC at mux.
  • Take management: prior takes are kept per scene with an active-take selector.

Mac prerequisites (install via Homebrew where missing)

  • ffmpeg (with videotoolbox support, standard in the Homebrew build)
  • libreoffice (headless PPTX conversion)
  • poppler (pdftoppm for PDF to slide images)
  • Node.js LTS
  • The scaffold should include a startup dependency check that reports anything missing rather than failing obscurely mid-pipeline.

Open items for the next session

  1. Push this repo to NAS Gitea. Done: repo lives at http://192.168.50.200:3000/dkempson/lesson-studio.git.
  2. Invoke gstack properly: read its ARCHITECTURE.md / ETHOS.md / SKILL.md (in ~/.claude/skills/gstack/) and use its planning skill to turn this handover into a formal build plan, per Dave's explicit request to use gstack for this project.
  3. Decide the NAS archive share/path for the project archive step (working storage is now local, per the revised storage model); needs an SMB share confirmed on the NAS and mounted on the Mac.
  4. Design the logo/brand identity: Dave asked for something "cool" and "modern"; worth previewing concepts with the visualize tool before committing to one.
  5. Scaffold the Node.js + React app and get a minimal end-to-end path working: import one slide plus one script line, record it, produce a one-scene MP4. Prove the full pipeline before building out the editing/reordering UI on top of it.
  6. Then iterate: design, build, validate, review, using gstack's cycle, until the tool is finished, tested and high quality (Dave's stated bar, verbatim).