feat(downloads): gate library row actions by media_kind (E7 S5b)

Surface the S5a media_kind discriminator in the library UI. A generic
uploaded file (media_kind='file') is download+share only — no player and
no timeline to trim, so Play/Edit/Edit-details are media-only; it shows a
neutral file glyph instead of an (always-empty) thumbnail and no duration
chip (container/size still read). The "Shared with me" tab's file row is
gated the same way. Re-download is now hidden for edit clips and uploads
too (neither has a re-fetchable source — the backend already rejects both
with 409), removing a latent click-to-409 on edit rows.

FE-only: the serializer already emits media_kind (S5a). Adds media_kind to
the DownloadAsset type and "upload" to the job_kind union; DOM tests cover
the file-row presentation (glyph, no img, no duration).
This commit is contained in:
2026-08-29 18:38:21 +02:00
parent fe0b34cd00
commit b4ad59274b
5 changed files with 150 additions and 83 deletions
+7 -2
View File
@@ -35,14 +35,19 @@ interface DownloadAsset {
width: number | null;
height: number | null;
container: string | null;
// "video" | "audio" | "file" — the stored discriminator that gates library row actions: media
// (video/audio) gets play/edit; a generic uploaded file is download+share only, no player. Null
// while still in flight (set at finalize). See backend downloads/mediakind.py.
media_kind: "video" | "audio" | "file" | null;
thumbnail_url: string | null;
expires_at: string | null;
}
export type DownloadStatus = "queued" | "running" | "paused" | "done" | "error" | "canceled";
// "download" (default) or "edit" (a per-user trim/crop derivative cut from another job).
// "download" (default), "edit" (a per-user trim/crop derivative cut from another job), or "upload"
// (a locally-uploaded file — no re-fetchable source, so re-download is rejected backend-side).
// Local (not exported): consumers compare `job.job_kind` against literals, never name the type.
type DownloadJobKind = "download" | "edit";
type DownloadJobKind = "download" | "edit" | "upload";
// Phase-2 editor recipe. A single `trim` (one output file) or a `segments` cut-list joined into
// one file. `accurate` re-encodes for a frame-accurate cut (a crop always re-encodes).