feat(clips): async video clips with transcription [operator-action] (LC-496) #494
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/LC-496-video-clips"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
LC-496: Async video clips (Loom-style) with transcription [operator-action]
Record a short camera or screen clip in the browser, post it as a message, and have its audio auto-transcribed. Built to match the app's "browser captures, server only stores/tokenizes" posture: no server-side ffmpeg or native media deps.
What a user gets
<video controls>player in the timeline.VoiceTranscribedevent as voice messages).How it fits the codebase (reuse-first)
routes::uploads): newkind=clippath - sniff the container, canonicalize to avideo/*mime, store content-addressed like any non-image upload. Dedicated 64 MiB clip cap (CLIP_MAX_BYTES); the per-user storage quota still applies. The serve route now honors single-rangeRangerequests (206 +Accept-Ranges) so inline<video>seeking works (Safari MP4 needs it; WebM benefits too).file_uploads.transcriptfor every attachment, so clips reuse the existingmime_type+transcriptcolumns.Attachment::is_video()drives the new render branch.maybe_transcribe_voice_message, generalized): also transcribesvideo/*clips by forwarding the recorded container to the STT endpoint as-is. OpenAI-compatible engines demux the audio track themselves - hence no server ffmpeg. The STT filename mapping learns video containers (webm/mp4/mov).#lc-staged, hiddenfile_id,__lcClearAttach/__lcUploadBusy/__lcRefreshSend/__lcS), so clips are mutually exclusive with voice + file attachments and ride the normal form submit. Streams are released on stop and on htmx teardown. CSP already allowsmedia-src 'self' blob:- no change needed.Operator note
[operator-action]: whenLETS_CHAT_STT_*is configured, recorded video clips are now ALSO sent to the STT endpoint (extra load/cost for metered or third-party engines). An engine that rejects video containers simply yields no clip transcript; the clip still posts and plays. Documented in the STT env row in CLAUDE.md.Scope / follow-ups
v1 ships record + post + inline play + auto-transcribe (the core Loom value). LLM summary of a clip transcript is a natural additive follow-up (the existing summarize path targets call transcripts; pointing it at upload transcripts is a small wire-up). No camera/mic device picker for clips yet (uses defaults), matching the current voice-message behavior.
Tests
just check(clippy standalone + saas + desktop + fmt),just test,just test-saasall pass. New:parse_range+ clip-mime-allowlist unit tests, andvideo_clip_is_transcribed_and_attached(avideo/*upload with no waveform is transcribed and rendered as a clip).Manual smoke test (recommend before merge)
Browser capture can't be exercised by the Rust tests. Worth a quick check in two browsers: record a camera clip and a screen clip, confirm inline playback + seeking, and (with an STT endpoint configured) confirm the transcript appears.