Educational Videos
Direction noted — NOT decided, NOT built
This doc captures a design direction discussed 2026-05-28 after the weighted-saw (Fierastraul) prep + an example 47-minute full-session video (8Rk5wZofAv.mp4) surfaced the question. The shape below is what we're leaning toward, but the decision is deliberately left open until we actually have a need to build it. Don't ship code against this without re-confirming the architecture.
What this would be for
A category of platform content that's a single video the patient watches passively, distinct from:
- Exercises (the prescription building block —
reps_based/duration_based/static). See features/exercise-library/rendering-strategy.md. - Sessions (clinician-built playlists of exercises, with the session-run lifecycle, session player overlays, TV companion phone pairing, pose tracking, pain ingest).
The validating example is a 47-minute pre-recorded full session video. Initially proposed modelling that as a new "single-video session" shape — but that would have pulled all the session-engagement machinery (cues, overlays, TV companion, exercise events) into a passive-watching use case and required every future session change to carry a "disable for this kind" branch. The point of educational_videos as a separate type is to avoid exactly that cross-cutting tax.
What it explicitly is NOT
An educational_video deliberately does not touch:
- ❌
sessions/session_runs— no run lifecycle, noended_naturally/ended_explicit/auto_closed. - ❌ Session player overlays — no cues, no rep counter, no set badge, no between-card, no countdowns.
- ❌ TV companion — no phone pairing, no companion-mode messaging. If these need a TV surface, it's a flat-video screen, not a companion-controlled session.
- ❌ Exercises /
exercise_renders/ exercise catalog / exercise prescriptions. - ❌ Pose tracking config, dose dialog, capability snapshot.
- ❌
session_exercise_events,session_pain_events.
If any of those does turn out to be wanted at build time (e.g. lightweight watch-progress for stats), it's a deliberate add — not a feature-flag through the session pipeline.
Sketch (intent only)
- DB: an
educational_videostable —(id, slug, name, description, language, video_id, playback_hls_url, tv_mp4_urls, duration_seconds, thumbnail_url, status, created_*, updated_*). Per-language rows or(slug, language)UNIQUE — pick at build time. - Media: reuses the static-ingest pipeline (Bunny Stream upload + TV Constrained Baseline rungs + Bunny auto-thumb). The existing
POST /v1/exercises/staticendpoint generalizes lightly — accepts(owner_kind, owner_id, language)so the S3 prefix templates toplatform/educational/{slug}/instead ofplatform/exercises/{slug}/. All baking + Bunny logic stays unchanged. - API: its own thin domain — list / get / publish / archive. Standard CRUD; no session-domain coupling.
- Patient UI: a separate "Watch a video" / "Library" surface, distinct from "Start session" and "Do an exercise." Plain HTML
<video>element bound to the Bunny URL. - TV (if needed): parallel "Videos" surface that reads the baseline rungs directly — no companion.
Open questions when we revisit
These are deliberately not answered now:
- Name —
educational_videos,library_videos,videos,content_videos? Pick at build time. ("Educational" matches how the team described them initially but constrains semantically.) - Categorization / taxonomy — do these have categories the patient browses? Body region tagging? Or just a flat searchable list?
- Assignability — clinician says "patient should watch this," or purely patient-driven discovery? Both?
- Stats — do they count toward anything (watch progress, completion, time-spent)? If yes, a lightweight watch-event ingest path — but not via
session_runs. - Tenancy — platform-curated only, or clinics can upload their own (which raises org-private storage + entitlement questions)?
- Lifecycle status enum — same
draft | published | archivedas exercises, or different? - Static-ingest endpoint generalization — exact wire shape of the
(owner_kind, owner_id, language)parameters when we get there. Or split into per-owner endpoints — implementation detail.
Placeholder example
.platform/synced/8Rk5wZofAv.mp4 — 47 minutes, 312 MB, 720p H.264 + AAC. Currently not processed, not uploaded to S3. Stays as a local placeholder until we revisit this design and build for real.
See also
- features/exercise-library/rendering-strategy.md → Render models — the
staticexercise kind wraps a single MP4 as an exercise (prescribable, browsable in the exercise catalog). Full-session pre-recorded videos that need neither the exercise wrapper nor the session-run lifecycle should land here instead. - The architectural lesson is recorded under "Why this is its own type, not a session shape" above — preserve that reasoning when revisiting.