Skip to content

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, no ended_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_videos table — (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/static endpoint generalizes lightly — accepts (owner_kind, owner_id, language) so the S3 prefix templates to platform/educational/{slug}/ instead of platform/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:

  • Nameeducational_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 | archived as 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 static exercise 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.