Query and update server snapshots for the operator UI, Companion variables, and the media library.
Implementation: src/api/routes-state.js · src/api/get-state.js · src/api/routes-media.js · src/api/media-catalog.js
Project persistence: project.md.
| Route | Caspar required? |
|---|---|
GET /api/variables, /variables/batch, /variables/custom |
No |
POST /api/variables/batch, /variables/custom |
No |
POST /api/selection |
No |
GET /api/project, /media, POST /api/media/cinf (ffprobe fallback) |
No (cinf prefers AMCP when up) |
GET /api/state, /channels, /templates, /fonts, /server/*, /help |
Yes |
Thumbnails via Caspar THUMBNAIL RETRIEVE |
Yes (local PNG cache may work without) |
GET /api/stateFull UI bootstrap snapshot (requires Caspar).
curl -s http://127.0.0.1:4200/api/state | jq 'keys'
Query: ?full_cinf=1 — enrich all media entries with CINF (can be heavy).
| Key | Description |
|---|---|
variables |
Companion/UI variable map |
channels |
Channel id list |
channelStatus |
Status lines per channel |
media |
Media catalog (id, label, duration, resolution, …) |
templates |
CG template list |
caspar |
{ connected, host, port } |
channelMap |
PGM/PRV routing resolved for UI |
scene |
live, deck, programLayerBankByChannel, globalBorders |
timeline |
list, playback |
playback.matrix |
Layer play tracker |
screenDestinations |
Output routing metadata |
osc |
OSC snapshot when enabled |
WebSocket may send slim catalog first (catalogDeferred: true) then chunk media separately.
GET /api/variablesAll variables. Query ?prefix=ui_ filters keys.
GET /api/variables/batch?categories=ui,channelReturns variables whose keys start with category_ (adds _ if missing).
POST /api/variables/batch{ "keys": ["ui_selection_context", "channel_1_framerate"] }
Max 2000 keys — 400 if exceeded. Returns only keys that exist.
GET /api/variables/custom{ "labels": { "my_var": "Friendly name" } }
POST /api/variables/custom{ "labels": { "my_var": "New label", "old_var": null } }
null or "" removes a label. Persisted on disk.
POST /api/selectionSync UI inspector selection into variables (no Caspar).
{
"context": "scene_layer",
"lookId": "sc_abc",
"layerIndex": 2,
"previewChannel": 2,
"casparLayer": 10
}
Drives mixer UI integration (ui_selection_* keys). Debounced ~100 ms in the client.
200: { "ok": true }
| Method | Path | Description |
|---|---|---|
| GET | /api/channels |
ids, status, channelXml |
| GET | /api/channels/{n} |
INFO channel |
| GET | /api/channels/{n}/delay |
INFO delay |
| GET | /api/templates |
Template catalog |
| GET | /api/config |
Caspar INFO CONFIG XML (text/xml) |
| GET | /api/fonts |
FLS font list |
| GET | /api/server |
Server info |
| GET | /api/server/queues |
Queue stats |
| GET | /api/server/threads |
Thread stats |
| GET | /api/server/gl |
OpenGL info |
| GET | /api/help |
AMCP HELP |
| GET | /api/help/{command} |
HELP for one command |
GET /api/mediaMedia index (CLS + disk scan). Works without Caspar.
curl -s 'http://127.0.0.1:4200/api/media?full_cinf=1' | jq '.[0]'
Entries typically include id, label, optional durationMs, resolution, fps after enrichment.
POST /api/media/cinfDuration/metadata for one file (timeline drop):
{ "id": "media/clip.mov" }
Also accepts filename. Uses Caspar CINF when connected; ffprobe fallback on disk when AMCP is down.
200:
{
"ok": true,
"id": "media/clip.mov",
"durationMs": 120000,
"cinf": "…",
"source": "cinf"
}
POST /api/media/refreshTriggers CLS/TLS media scan cycle.
POST /api/media/delete{ "id": "media/old.mov" }
Prefer over DELETE /api/local-media/... when paths contain slashes.
POST /api/media/mkdir · POST /api/media/move{ "path": "folder/sub" }
{ "sourceId": "a.mov", "targetId": "folder/a.mov" }
GET /api/local-media/{path}Serve file from configured local media root.
DELETE /api/local-media/{path}Delete by URL path (encoding pitfalls — prefer POST /api/media/delete).
| Method | Path | Description |
|---|---|---|
| GET | /api/thumbnails |
Caspar thumbnail index |
| GET | /api/thumbnail?filename= |
One image (query) |
| GET | /api/thumbnail/{filename} |
One image (path) |
| GET | /api/thumbnail/live/{channel} |
Cached PRINT still |
| POST | /api/thumbnails/generate |
Generate one |
| POST | /api/thumbnails/generate-all |
Generate all |
| POST | /api/thumbnail/live/capture |
Capture from PRINT |
| POST | /api/thumbnail/live/upload |
Upload still |
Thumbnail GET tries local ffmpeg cache first (hq=1 default); Caspar retrieve with ?fallback=1.
Query params: w (max width), t (seek seconds), hq, fallback.
# Bootstrap UI (Caspar up)
curl -s http://127.0.0.1:4200/api/state | jq '{caspar, scene: .scene.live, timelines: .timeline.list | length}'
# Media without Caspar
curl -s http://127.0.0.1:4200/api/media | jq 'length'
# Clip duration for timeline
curl -s -X POST http://127.0.0.1:4200/api/media/cinf \
-H 'Content-Type: application/json' \
-d '{"id":"AMB"}' | jq .
# Companion variable batch
curl -s -X POST http://127.0.0.1:4200/api/variables/batch \
-H 'Content-Type: application/json' \
-d '{"keys":["channel_1_framerate","ui_selection_context"]}'
POST/GET /api/data/store|retrieve|list|remove → 410 with message to use project save/load.
openapi.yaml — StateSnapshot, VariablesBatchRequest, MediaCinfRequest, SelectionPayload.