Skip to content

Media Upload

The "Add content" upload sheet in PlayoutAdmin — file select → transcoding profile pick → TUS upload → processing status.

  • Component: PlayoutAdmin/src/components/media/sections/AddContentSheet.jsx
  • Backend reference: domain/media — full API schema, pipeline behavior, and category/passthrough rules live there, not here

API calls used

Call Used for Notes / limitation
GET /media/transcoding-profiles?category=<video\|image\|document> Populate the profile picker once a file is selected Category is derived client-side from the file extension (detectMediaType → categoryFor), not from the backend. audio/other never call this — no category exists for them. See domain/media § Passthrough for what the returned profiles mean.
POST /media/prepare Issue the upload token; also where tenant, folder, transcodingProfileId are sent tenant/folder are hardcoded constants in this component today, not derived from the logged-in user — see Limitations. transcodingProfileId is only sent if a real numeric profile id was picked; "Custom..." sends none.
TUS upload to /tusd/ The actual file bytes Suite-wide shared path, not app-namespaced — must match nginx's /tusd/ block and tusd's -base-path. Not a CoreAPI route.
GET /media/?sortBy=created_at&sortDir=desc&limit=5 Find the Media row created for this upload (TUS itself doesn't return one) Matches by fileName/originalName, most-recent-first, retried for ~30s. Racy if two uploads with a colliding name finish at the same moment.
GET /media/:id (polled every 2s) Track transcodingStage until done/failed Only meaningful when a real transcodingProfileId was sent — otherwise stays at whatever default the backend leaves it. Full pipeline stages/stage semantics: domain/media.

For request/response shapes, error codes, and everything about what a profile actually does (renditions vs. settings, passthrough, category rules) see domain/media and its OpenAPI spec.


Limitations

  • Tenant and folder are hardcoded (HARDCODED_TENANT = "jastv", HARDCODED_FOLDER = "marketing") — every upload lands in the same place regardless of who's logged in. Should come from the authenticated user's org instead — AuthAPI's access token already carries an orgs[] claim (org_id, org_slug, role, permissions) that's the natural source for tenant, it's just not decoded/used anywhere in this flow yet. Folder likely still needs a real picker.
  • "Custom..." in the profile picker isn't a real custom-config path — it just means "send no transcodingProfileId." There's no way to submit ad-hoc, unsaved transcode settings from this form; a real profile has to be created first via Media Transcoding Presets.
  • Frontend media-type detection is extension-based and independent of the backend's own MIME-based classifier (media.InferMediaType) — the two can disagree on an edge case; the backend's classification is the one that's actually persisted and drives behavior.
  • Media-record lookup after upload is a match-by-recency race, not an id returned directly from the upload response.
  • One file per trip through the wizard — no batch upload.

Full upload flow

sequenceDiagram
    participant User
    participant Sheet as AddContentSheet
    participant API as CoreAPI
    participant tusd

    User->>Sheet: pick file
    Sheet->>Sheet: detectMediaType(filename) → categoryFor()
    Sheet->>API: GET /media/transcoding-profiles?category=<detected>
    API-->>Sheet: profiles (+ "Custom..." appended client-side)
    User->>Sheet: name + pick profile → Start upload
    Sheet->>API: POST /media/prepare {tenant: HARDCODED, folder: HARDCODED, transcodingProfileId?}
    API-->>Sheet: {token}
    Sheet->>tusd: TUS upload to /tusd/ (metadata.uploadToken = token)
    tusd->>API: POST /hooks/tusd (server-side, not seen by Sheet)
    Sheet->>API: GET /media/?sortBy=created_at (find the new record)
    loop every 2s until done/failed
        Sheet->>API: GET /media/:id
        API-->>Sheet: transcodingStage
    end
  1. Type — user drops/selects a file. No network call.
  2. Configure — detectMediaType + categoryFor resolve a category; GET /media/transcoding-profiles populates the picker; user names the asset and picks a profile.
  3. Upload — POST /media/prepare issues a token, then a TUS upload streams the file directly to storage via /tusd/.
  4. Processing — the sheet locates the resulting Media row and polls its transcodingStage every 2s until done/failed. The pipeline itself (normalize/transcode/optimize/rasterize) runs entirely server-side — see domain/media for what actually happens at each stage.
  5. Done — local summary only, no further calls.