Phase P5 of epic #323. Third content layer (after books #326, text #327). Largest phase — expect to split into sub-tickets once the design settles.
What: two video tracks surfaced in the shared playground shell:
Official videos — per lesson, produced by SpikerSoft (none exist yet; the model and player land first, content follows)
Community videos — per lesson block (TutorialPanel), submitted by users/students, visible only after admin/staff approval
Model (backend):
Video entity: lesson number + optional panel index, kind (official | community), submitter, status (pending | approved | rejected), title, duration, storage ref, rejection note
Submission endpoint ([Authorize] — submitting is a logged-in action) + moderation endpoints (staff/admin roles)
Approved-video read path on the anonymous-readable lesson detail endpoints (LessonsController)
Media pipeline: reuse the existing event-driven media processing pattern (SpikerSoft.EventHandlers.BlogMediaProcessor is prior art — transcode/thumbnail via a new or generalized handler per the worker-architecture conventions)
Storage/serving decision: same store the blog media pipeline targets; document size/length limits
Moderation (frontend admin):
Admin approval queue page mirroring /admin/blog-moderation (list pending, preview, approve/reject with note); add to the Management menu with a pending-count badge like the existing issue-badge pattern in menu-bar.component.html
Playground (frontend):
Videos tab/pane in LanguagePlaygroundShellComponent: official video for the lesson + approved community videos for the current block
Submit flow from the playground (logged-in users), with clear pending-review state for the submitter
Player consistent with existing media components; i18n chrome
Acceptance: an approved community video appears on its lesson block in all five playgrounds; unapproved videos are never publicly visible; staff can approve/reject with an audit trail (who, when).
Phase P5 of epic #323. Third content layer (after books #326, text #327). Largest phase — expect to split into sub-tickets once the design settles.
**What:** two video tracks surfaced in the shared playground shell:
1. **Official videos** — per lesson, produced by SpikerSoft (none exist yet; the model and player land first, content follows)
2. **Community videos** — per lesson **block** (`TutorialPanel`), submitted by users/students, visible only after admin/staff approval
**Model (backend):**
- [ ] Video entity: lesson number + optional panel index, kind (official | community), submitter, status (pending | approved | rejected), title, duration, storage ref, rejection note
- [ ] Submission endpoint (`[Authorize]` — submitting is a logged-in action) + moderation endpoints (staff/admin roles)
- [ ] Approved-video read path on the anonymous-readable lesson detail endpoints (`LessonsController`)
- [ ] Media pipeline: reuse the existing event-driven media processing pattern (`SpikerSoft.EventHandlers.BlogMediaProcessor` is prior art — transcode/thumbnail via a new or generalized handler per the worker-architecture conventions)
- [ ] Storage/serving decision: same store the blog media pipeline targets; document size/length limits
**Moderation (frontend admin):**
- [ ] Admin approval queue page mirroring `/admin/blog-moderation` (list pending, preview, approve/reject with note); add to the Management menu with a pending-count badge like the existing `issue-badge` pattern in `menu-bar.component.html`
**Playground (frontend):**
- [ ] **Videos** tab/pane in `LanguagePlaygroundShellComponent`: official video for the lesson + approved community videos for the current block
- [ ] Submit flow from the playground (logged-in users), with clear pending-review state for the submitter
- [ ] Player consistent with existing media components; i18n chrome
**Acceptance:** an approved community video appears on its lesson block in all five playgrounds; unapproved videos are never publicly visible; staff can approve/reject with an audit trail (who, when).
Acceptance met: approved community videos surface on their lesson block across all five playgrounds via the shared shell; unapproved videos are never publicly visible; staff approve/reject with an audit trail; uploaded raw video is scanned, transcoded to a web-safe MP4 with a poster, and served from the final store, with failures landing in a terminal state without exposure.
Follow-up (optional, non-blocking): the SPA submit flow currently posts a video URL (P5d); wiring the in-browser file upload to POST /api/Lessons/{n}/videos/upload is a small frontend follow-up.
P5 (Videos layer) complete — all four sub-tickets merged and closed:
- **#336** P5a — model + moderation/read endpoints (`LessonVideo`, `lessonVideos` collection)
- **#338** P5c — admin moderation queue + Management-menu pending badge
- **#339** P5d — playground Videos tab (official + approved community, submit flow, login CTA)
- **#337** P5b — media pipeline (upload → ClamAV scan → ffmpeg transcode + poster → move → static serving)
**Acceptance met:** approved community videos surface on their lesson block across all five playgrounds via the shared shell; unapproved videos are never publicly visible; staff approve/reject with an audit trail; uploaded raw video is scanned, transcoded to a web-safe MP4 with a poster, and served from the final store, with failures landing in a terminal state without exposure.
**Follow-up (optional, non-blocking):** the SPA submit flow currently posts a video URL (P5d); wiring the in-browser file upload to `POST /api/Lessons/{n}/videos/upload` is a small frontend follow-up.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Phase P5 of epic #323. Third content layer (after books #326, text #327). Largest phase — expect to split into sub-tickets once the design settles.
What: two video tracks surfaced in the shared playground shell:
TutorialPanel), submitted by users/students, visible only after admin/staff approvalModel (backend):
[Authorize]— submitting is a logged-in action) + moderation endpoints (staff/admin roles)LessonsController)SpikerSoft.EventHandlers.BlogMediaProcessoris prior art — transcode/thumbnail via a new or generalized handler per the worker-architecture conventions)Moderation (frontend admin):
/admin/blog-moderation(list pending, preview, approve/reject with note); add to the Management menu with a pending-count badge like the existingissue-badgepattern inmenu-bar.component.htmlPlayground (frontend):
LanguagePlaygroundShellComponent: official video for the lesson + approved community videos for the current blockAcceptance: an approved community video appears on its lesson block in all five playgrounds; unapproved videos are never publicly visible; staff can approve/reject with an audit trail (who, when).
Sub-ticket breakdown (PR series)
LessonVideomodel, submission + moderation endpoints, approved-read path (no pipeline)/admin/blog-moderation) + Management menu badgeOrder: #336 first (stable contract) → #337 pipeline + #338 moderation UI (parallel) → #339 playground UI. Starting on #336 now.
P5 (Videos layer) complete — all four sub-tickets merged and closed:
LessonVideo,lessonVideoscollection)Acceptance met: approved community videos surface on their lesson block across all five playgrounds via the shared shell; unapproved videos are never publicly visible; staff approve/reject with an audit trail; uploaded raw video is scanned, transcoded to a web-safe MP4 with a poster, and served from the final store, with failures landing in a terminal state without exposure.
Follow-up (optional, non-blocking): the SPA submit flow currently posts a video URL (P5d); wiring the in-browser file upload to
POST /api/Lessons/{n}/videos/uploadis a small frontend follow-up.