Metadata-Version: 2.4
Name: stapel-video
Version: 0.5.0
Summary: Video calls (rooms, lobby admission, recording-egress seam) for the Stapel framework
License: MIT
Project-URL: Homepage, https://github.com/usestapel/stapel-video
Project-URL: Repository, https://github.com/usestapel/stapel-video
Project-URL: Documentation, https://github.com/usestapel/stapel-video#readme
Project-URL: Changelog, https://github.com/usestapel/stapel-video/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/usestapel/stapel-video/issues
Keywords: django,stapel,video,livekit,webrtc
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: stapel-core<1.0,>=0.27.0
Provides-Extra: livekit
Requires-Dist: livekit-api<1,>=0.7; extra == "livekit"
Requires-Dist: requests<3,>=2.28; extra == "livekit"
Provides-Extra: channels
Requires-Dist: stapel-core[channels]; extra == "channels"
Provides-Extra: all
Requires-Dist: livekit-api<1,>=0.7; extra == "all"
Requires-Dist: requests<3,>=2.28; extra == "all"
Requires-Dist: stapel-core[channels]; extra == "all"
Dynamic: license-file

<!-- Generated by stapel-readme from docs/readme.md + docs/*.json. Do not edit this file; edit docs/readme.md and re-run `make readme`. -->

# stapel-video

[![CI](https://img.shields.io/github/actions/workflow/status/usestapel/stapel-video/ci.yml?branch=main&logo=github&label=CI)](https://github.com/usestapel/stapel-video/actions/workflows/ci.yml?query=branch%3Amain)
[![coverage](https://img.shields.io/codecov/c/github/usestapel/stapel-video?branch=main&logo=codecov&label=coverage)](https://app.codecov.io/gh/usestapel/stapel-video)
[![pypi](https://img.shields.io/pypi/v/stapel-video?logo=pypi&logoColor=white&label=pypi)](https://pypi.org/project/stapel-video/)
[![downloads](https://static.pepy.tech/badge/stapel-video/month)](https://pepy.tech/project/stapel-video)
[![python](https://img.shields.io/pypi/pyversions/stapel-video?logo=python&logoColor=white)](https://pypi.org/project/stapel-video/)
[![license](https://img.shields.io/github/license/usestapel/stapel-video)](https://github.com/usestapel/stapel-video/blob/main/LICENSE)
[![llms.txt](https://img.shields.io/badge/llms.txt-blue)](https://github.com/usestapel/stapel-video/blob/main/docs/llms.txt)

> Video calls: rooms with shareable join codes, an access-level admission model (public / scope-trusted / restricted lobby) with a realtime waiting room over WebSockets, host admit/deny controls, join-token minting through a pluggable video-provider seam (LiveKit by default), and a recording-egress seam (start/stop + a video.egress_ended event) that integrates with stapel-recordings by event, never by import.

Part of the [Stapel framework](https://github.com/usestapel) — composable Django apps that deploy as a monolith or as microservices without changing module code.

## Install

```bash
pip install stapel-video
```

## At a glance

| Fact | Value |
|---|---|
| Version | `0.5.0` |
| Python | `>=3.11` (3.11, 3.12, 3.13, 3.14) |
| HTTP operations | 7 |
| Config axes | 3 |
| Usage surface | 11 |
| Extension points | 4 |
| Error codes | 49 |
| Fleet dependencies | [`stapel-auth`](https://github.com/usestapel/stapel-auth) (optional) · [`stapel-core`](https://github.com/usestapel/stapel-core) · [`stapel-profiles`](https://github.com/usestapel/stapel-profiles) (optional) · [`stapel-recordings`](https://github.com/usestapel/stapel-recordings) (optional) |

## Documentation

[OpenAPI](https://github.com/usestapel/stapel-video/blob/main/docs/schema.json) · [capabilities.json](https://github.com/usestapel/stapel-video/blob/main/docs/capabilities.json) · [llms.txt (for agents)](https://github.com/usestapel/stapel-video/blob/main/docs/llms.txt)

## What this is

- **Rooms** with human-shareable join codes (`abc-defg-hij`).
- **Admission model** — `public` (anyone with the code joins), `scope_trusted`
  (members of the room's scope join instantly, others wait), `restricted`
  (everyone but the host waits in a lobby).
- **Realtime lobby** over WebSockets (Channels) — `waiting` / `admitted` /
  `denied`, authenticated by the same Stapel JWT stack HTTP uses.
- **Host controls** — admit / deny waiting guests.
- **Provider seam** — one `VideoProvider` ABC (mint join token, create room,
  start/stop recording egress, verify webhook). Swap vendors without forking.
- **Recording** is a *seam*, not a pipeline: `start`/`stop_egress` proxy the
  provider and a `video.egress_ended` comm event carries the storage key to
  [stapel-recordings](https://github.com/usestapel/stapel-recordings) — by
  event, never by import.

Alpha. See [MODULE.md](https://github.com/usestapel/stapel-video/blob/main/MODULE.md) for the agent-facing map of seams.

## Quick start

```bash
pip install stapel-video            # core library
pip install 'stapel-video[livekit]' # + the default LiveKit backend
pip install 'stapel-video[channels]'# + the realtime lobby (WebSockets)
```

```python
# urls.py
path("video/", include("stapel_video.urls"))

# asgi.py (realtime lobby)
from channels.routing import ProtocolTypeRouter, URLRouter
from stapel_core.django.jwt.channels import JWTAuthMiddlewareStack
from stapel_video.routing import websocket_urlpatterns

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": JWTAuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
})
```

## API

| Method | Path | What |
|---|---|---|
| POST | `/video/api/rooms` | Create a room (creator auto-admitted host, with a token) |
| GET | `/video/api/rooms/{join_code}` | Room info |
| POST | `/video/api/rooms/{join_code}/join` | Join → admitted / waiting / denied |
| GET | `/video/api/rooms/{join_code}/participants` | Participants (anchor-paginated) |
| POST | `/video/api/rooms/{join_code}/lobby/admit` | Admit a waiting guest (host-only) |
| POST | `/video/api/rooms/{join_code}/lobby/deny` | Deny a waiting guest (host-only) |
| POST | `/video/api/webhook` | Provider webhook ingress (signed, unauthenticated) |

## Configuration (`STAPEL_VIDEO`)

| Key | Default | What |
|---|---|---|
| `VIDEO_PROVIDER` | `…livekit.LiveKitProvider` | Video backend (dotted path) |
| `SCOPE_PROVIDER` | `…scope.DefaultScopeProvider` | scope_key resolution + membership |
| `DEFAULT_ACCESS_LEVEL` | `restricted` | Access level for a room created without one |
| `DEFAULT_ADMIT_REQUIRED` | `True` | Whether new rooms start with the lobby on |
| `LIVEKIT_URL` / `LIVEKIT_API_KEY` / `LIVEKIT_API_SECRET` | `""` | Default-provider credentials |

`VIDEO_PROVIDER`, `DEFAULT_ACCESS_LEVEL` and `DEFAULT_ADMIT_REQUIRED` are the
three CTO-facing config axes surfaced in `docs/capabilities.json`.

## License

MIT — see [LICENSE](https://github.com/usestapel/stapel-video/blob/main/LICENSE).

---

<sub>This page is assembled by `stapel-readme` from `docs/readme.md` plus the contract artifacts in `docs/`. Edit the prose in `docs/readme.md`; the badges, facts and links above and below it are generated — do not hand-edit `README.md`.</sub>
