Where you are: docs → reference → api → clips Read this first: api.md See also: subsystems/clips.md · clips.md
TL;DR Eighteen endpoints manage the clip library (upload, CRUD, pin for lifecycle retention, import from recordings) and the four player slots (load, eject, play/pause/stop, seek, speed, loop). Uploads go through a transcode pipeline; progress is broadcast via ClipUploadProgress so the UI can show a status bar.
Purpose: list all stored clips.
Handler: control/api_clips.go → (*API).handleClipsList.
Response 200: []internal.ClipInfo.
Handler: (*API).handleClipGet. Response 200: internal.ClipInfo. Errors: 404 (clip.ErrNotFound).
Purpose: update clip metadata (name, loop setting).
Handler: (*API).handleClipUpdate.
Handler: (*API).handleClipDelete. Response 204.
Purpose: pin/unpin a clip. Pinned clips are not auto-evicted by the retention policy.
Handler: (*API).handleClipPin.
Request body: { "pinned": true }.
Purpose: upload a media file as a new clip. Accepts common container formats; the server transcodes to the internal pipeline format. Progress is streamed through ClipUploadProgress in the state broadcast.
Handler: (*API).handleClipUpload.
Request: multipart/form-data with a file field (up to 2 GB). Optional name field.
Response 202: internal.ClipInfo (metadata; transcode may still be in progress).
Errors: 400 (clip.ErrInvalidFormat, clip.ErrCorruptFile, clip.ErrOddDimensions, clip.ErrNoVideo, clip.ErrInvalidName), 409 (clip.ErrStorageFull, clip.ErrAlreadyExists), 413 (payload too large), 422 (clip.ErrTranscodeFailed).
Emits: state broadcast with progressing ClipUpload, then final Sources entry if the clip is a source.
Related: subsystems/clips.md
Purpose: list .ts files in the recording directory that can be imported as clips.
Handler: (*API).handleClipRecordings.
Response 200: [{ "filename": "...", "sizeBytes": ..., "modifiedMs": ... }, ...].
Purpose: stream a recording file back to the client.
Handler: (*API).handleRecordingDownload. Query: ?filename=<basename>.
Response 200: video/mp2t with Content-Disposition: attachment.
Errors: 400 (missing/invalid filename), 404.
Purpose: import a saved recording into the clip library (no transcode — the .ts file is referenced directly).
Handler: (*API).handleClipFromRecording.
Request body: { "filename": "recording-2026-04-17T12-30-00.ts", "name": "show-open" }.
Response 201: internal.ClipInfo.
Errors: 400 (invalid name / filename), 404 (file not in recording dir), 409 (already exists).
Four numbered player slots live at /api/clips/players/{n} (n = 1-4). Each slot loads one clip at a time.
Purpose: return state of all four players.
Handler: (*API).handleClipPlayersList.
Response 200: []internal.ClipPlayerInfo.
Purpose: load a clip into a player.
Handler: (*API).handleClipPlayerLoad.
Request body: { "clipId": "abc123" }.
Errors: 400 (clip.ErrInvalidPlayer, clip.ErrInvalidName), 404 (clip.ErrNotFound), 409 (clip.ErrPlayerBusy, clip.ErrPlayerFull).
Handler: (*API).handleClipPlayerEject. Unloads the clip.
Handler: (*API).handleClipPlayerPlay. Errors: 400 (clip.ErrPlayerEmpty).
Handler: (*API).handleClipPlayerPause.
Handler: (*API).handleClipPlayerStop. Resets position to 0 but keeps the clip loaded.
Purpose: seek to a normalized position (0.0-1.0).
Handler: (*API).handleClipPlayerSeek.
Request body: { "position": 0.5 }.
Errors: 400 (clip.ErrInvalidSeek).
Purpose: set playback speed.
Handler: (*API).handleClipPlayerSpeed.
Request body: { "speed": 1.5 }.
Errors: 400 (clip.ErrInvalidSpeed).
Purpose: enable/disable loop.
Handler: (*API).handleClipPlayerLoop.
Request body: { "loop": true }.
- Concepts: clips.md
- Reference: api.md · state-broadcast.md
- Subsystems: clips.md