Media Domain
Manages media assets stored in SeaweedFS — video, image, audio, document. Full lifecycle: upload token → TUS upload → background processing (normalize/transcode/optimize/rasterize) → asset serving.
See also: Architecture (serving, storage layout, upload flow, status lifecycle, NATS events) and Transcoding (data model, pipelines, passthrough, seeded profiles).
Handles:
- TUS resumable upload (prepare → upload → post-finish webhook)
- Video normalize (AV trim, mdat defrag, EBU R128 loudnorm)
- Video transcode (CMAF ladder), image optimize (webp), document rasterize (per-page JPEG + slide metadata) — one shared, category-discriminated
TranscodingProfile - Passthrough per category (skip pipeline, serve as-is)
- Thumbnail generation (video/image/pdf/office)
- CRUD, soft delete with S3 purge (default on), storage audit
- NATS events on processing milestones
- Batch upload
- Cache purge on rename/delete/reprocess (cproxy purge not wired to media mutations)
- Real CDN/GSLB hostname (currently a bucket-qualified local URL)
Domain Structure
domain/media/
├── config.go env config
├── helper.go response shaping, media-type inference, ffprobe wrappers
├── models.go GORM models — Media, TranscodingProfile(+Version), MediaSlide, MediaPlaylist(+Item), UploadToken
├── validation.go ValidatePrepare
│
├── handlers/
│ ├── handler_media.go CRUD, asset serving/presign, delete+purge
│ ├── handler_upload.go prepare token, tusd post-finish webhook
│ ├── handler_process.go pipeline orchestration, thumbnails, dedup
│ ├── handler_normalize.go video normalize (remux + loudnorm)
│ ├── handler_image.go image optimize (webp)
│ ├── handler_document.go document rasterize + slides
│ ├── handler_transcode.go video CMAF ladder
│ ├── handler_transcoding_profiles.go profile/version CRUD
│ ├── handler_playlist.go media playlists (SMIL/CMAF)
│ ├── handler_query.go folders/tenants/tags/stats
│ ├── handler_nats.go NATS subscribers
│ ├── handler_storage_audit.go orphan/missing audit
│ └── handler_pdf.go legacy PDF path — see To-do, not part of the real pipeline
│
├── repositories/media_repository.go MediaRepository
└── routes/ wire.go (Setup) + routes.go
Mount point: media's own routes (/media/...) sit under /api/v1;
/assets/* and /hooks/tusd are mounted on the app root instead (see
Architecture).
Testing
An in-page upload form isn't practical here — this is a static docs site, and the upload path is TUS (a resumable-upload protocol), not a plain HTTP form Swagger's "try it" can drive.
- The embedded spec below can exercise every JSON endpoint directly
against a running server (
/media/prepare,/media/transcoding-profilesCRUD,/media/:id, etc. — see theservers:list in the spec). - The file upload step itself still needs a real TUS client — either drive it through PlayoutAdmin's own "Add content" UI, or see guide.md for a curl-based walkthrough per pipeline (profile lookup → prepare → verify).
To-do / known gaps
- Auth: media routes aren't gated by the RS256 JWT middleware at registration (unlike identity/tenancy) — confirm whether that's intentional before wiring tenant/org derivation from the token.
- Tenant/folder still hardcoded on the frontend — see the
Media Upload capability app
for the concrete to-do (derive from AuthAPI's
orgs[]claim). -
/hooks/tusdhas no signature/secret check — only the opaqueuploadTokenmetadata value gates it. Anyone who can reach CoreAPI can POST a fabricated post-finish payload. -
handler_pdf.go(GET /media/convert-pdf/:key) is a legacy, parallel PDF-rasterization path with hardcoded staging hostnames (stg.filer.castis.io,stg.api.castis.io) and its own local disk cache — unrelated to and duplicatinghandler_document.go's realrasterizeDocumentpipeline. Candidate for removal. - Dead code:
validation.go'sValidateDirectUpload,handler_playlist_helpers.go's two error constructors,handler_playlist.go'sconcatAndNormalize— none have any caller. -
/assets/*is bypassed in the local/Quickstart topology — nginx routes it straight to cproxy→SeaweedFS, not to CoreAPI's ownServeAsset/PresignAsset. Don't assume a browser client ever hits CoreAPI directly for asset bytes; see Architecture. - Batch upload, cache invalidation on mutation, real CDN hostname — as above.