FastAPI service in infra/media-worker implementing every MediaInferenceProvider operation behind HTTP API v1, plus capabilities. Bearer auth, MEDIA_ROOT realpath enforcement, strict request schemas, cancellation on disconnect, GPU/CPU gates with 503 overflow, input size/duration limits, lazy SHA256-pinned model downloads, fail-closed redaction with atomic output, offline pytest suite with fake backends, CPU/CUDA/ROCm Dockerfile and a license review of every model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138YqDj3ttfayRwuMKKrqVj
media-worker
Local inference sidecar for Media Studio (Python 3.11, FastAPI). It implements
every MediaInferenceProvider operation (packages/adapter-kit/src/media.ts)
behind HTTP API v1; the TypeScript client is packages/adapters/src/media-sidecar.ts.
API
GET /healthz→{"ok": true, "version": "..."}(no token)GET /v1/capabilities→MediaCapabilitiesPOST /v1/<operation>with the TypeScript request object (minussignal)- Errors:
{"error": {"code", "message", "operation"}}withbad_request/invalid_path400,unauthorized401,too_large413,unsupported501,busy503,internal500.
Every route but /healthz needs Authorization: Bearer $MEDIA_WORKER_TOKEN.
Paths must be absolute and resolve (symlinks included) inside MEDIA_ROOT.
A request stops when its client disconnects (between frames or chunks) or
after MEDIA_MAX_SECONDS.
Configuration
| Variable | Default | |
|---|---|---|
MEDIA_WORKER_TOKEN |
— | required, ≥ 32 chars (MEDIA_WORKER_INSECURE_DEV=1 lifts this for tests) |
MEDIA_ROOT |
— | required; mount it at the same absolute path as the API's DATA_DIR/media |
MEDIA_MODELS_DIR |
/models |
weight cache (volume) |
MEDIA_WORKER_HOST / MEDIA_WORKER_PORT |
127.0.0.1 / 8790 |
|
MEDIA_GPU_CONCURRENCY |
1 |
model operations running at once |
MEDIA_CPU_CONCURRENCY |
2 |
scenes, redact, audio, saliency running at once |
MEDIA_QUEUE_SIZE |
8 |
waiting requests per gate before 503 busy |
MEDIA_MAX_INPUT_BYTES |
20 GiB | larger inputs → 413 |
MEDIA_MAX_SECONDS |
14400 | longer inputs → 413; longer processing is cancelled |
MEDIA_MODEL_DOWNLOAD |
1 |
0 = offline; missing weights → 501 |
MEDIA_OPTIONAL_BACKENDS |
empty | opt-in for backends that are not clean for commercial use, e.g. faceIdentity |
MEDIA_ACCELERATOR |
build arg | cpu, cuda or rocm; falls back to cpu when no GPU is visible |
Models
Backends, licenses, URLs, SHA256 and sizes are pinned in app/models.py; the
license review is in LICENSES.md. Weights download on first use into
MEDIA_MODELS_DIR/<backend>/, are checked against the pinned SHA256 and size,
and are deleted on mismatch (the operation then reports 501). Warm the volume
ahead of time with python -m app prefetch.
synthesizeIdentity always answers 501: there is no commercially licensed
generator and swap pipeline, and the request schema rejects any extra field,
so a target-face image cannot be passed.
Redaction geometry
Boxes interpolate linearly between keyframes; a single keyframe is a static
region for the whole clip. paddingPct grows the box by that share of its
size, featherPct fades the effect outward over that share of the shorter
side (the region itself stays fully covered). Partial modes use the first two
landmarks as eyes and the third as the nose. Output is FFV1 for .mkv,
otherwise x264 CRF 12; audio is copied and metadata dropped. Any frame error is
a 500 and nothing is left at outPath.
Build and test
docker build --build-arg ACCELERATOR=cpu -t rakazo/media-worker infra/media-worker
docker build --target test -t rakazo/media-worker:test infra/media-worker
docker run --rm rakazo/media-worker:test # offline pytest, fake model backends
Without Docker: pip install -r requirements.txt -r requirements-dev.txt and
python -m pytest in this directory. Operations whose ML packages are missing
report available: false.
CUDA uses the torch cu126 wheels and onnxruntime-gpu. ROCm uses the torch
rocm7.1 wheels for SAM 2; ONNX Runtime and CTranslate2 have no ROCm wheels on
PyPI, so SigLIP, LaMa and Whisper run on the CPU there.