Training Trailer Videos
Admin upload, replacement, clear, and playback contract for training trailers.
Training Trailer Videos
Training trailers follow the same contract shape as course trailers:
trailerRefis the canonical stored keytrailerUrlis the backend-resolved playback URL- clients should never derive playback URLs from
trailerRef
Admin routes
| Method | Path | Purpose |
|---|---|---|
POST | /api/admin/training/:id/trailer | Upload or replace a trailer via multipart file |
DELETE | /api/admin/training/:id/trailer | Clear trailer and delete prior managed object |
PATCH | /api/admin/training/:id | Alternate path to write trailerRef directly |
GET | /api/admin/training/:id | Read trailerRef plus resolved trailerUrl |
Upload and replace flow
- Admin uploads a trailer with
POST /api/admin/training/:id/trailer - Backend validates MP4 MIME,
.mp4extension, and size limit - Backend uploads the file with
UploadType.TRAILER - Backend persists the returned storage key into
training.trailerRef - If a previous managed trailer existed under
public/trailer/, backend deletes it after the new ref is saved - If persistence fails after storage upload, backend deletes the newly uploaded managed object before returning the error
Multipart request requirements
- field name:
file - content type:
multipart/form-data - accepted MIME:
video/mp4 - accepted extension:
.mp4 - size limit:
COURSE_TRAILER_MAX_FILE_SIZE_MB(default250)
Success example:
{
"message": "Training trailer uploaded successfully",
"data": {
"id": 101,
"trailerRef": "public/trailer/training/nepse-masterclass.mp4",
"trailerUrl": "https://cdn.example.com/public/trailer/training/nepse-masterclass.mp4"
}
}Clear flow
DELETE /api/admin/training/:id/trailer sets trailerRef to null and best-effort deletes the old managed object.
{
"message": "Training trailer cleared successfully",
"data": {
"id": 101,
"trailerRef": null,
"trailerUrl": null
}
}Alternate shared-upload path
If you already use the shared upload endpoint:
POST /api/upload?uploadType=trailer&optimize=false- Read
relativePathfrom the upload response PATCH /api/admin/training/:idwith{ "trailerRef": "<relativePath>" }
This remains supported, but it does not replace the dedicated trailer lifecycle endpoints when you want backend-managed replacement and cleanup.
Read surfaces
Admin detail
GET /api/admin/training/:id returns both the stored key and the resolved URL:
{
"id": 101,
"title": "NEPSE Trading Masterclass",
"trailerRef": "public/trailer/training/nepse-masterclass.mp4",
"trailerUrl": "https://cdn.example.com/public/trailer/training/nepse-masterclass.mp4"
}Discovery list and featured
GET /api/trainings and GET /api/trainings/featured expose preview metadata through TrainingListItemDto:
{
"id": 101,
"title": "NEPSE Trading Masterclass",
"thumbnailRef": "https://cdn.bullhouse.com/training/nepse-masterclass/thumbnail.jpg",
"trailerRef": "public/trailer/training/nepse-masterclass.mp4",
"trailerUrl": "https://cdn.example.com/public/trailer/training/nepse-masterclass.mp4"
}Use trailerUrl directly for preview playback. trailerRef exists so admin and backend integrations can round-trip the canonical storage reference.
Cleanup semantics
- Managed cleanup is limited to refs under
public/trailer/ - Legacy non-managed refs are left untouched
- Public preview playback should consume
trailerUrl, not a client-computed bucket path