webatem 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webatem-0.2.0/LICENSE +27 -0
- webatem-0.2.0/PKG-INFO +335 -0
- webatem-0.2.0/README.md +298 -0
- webatem-0.2.0/atem_control/__init__.py +0 -0
- webatem-0.2.0/atem_control/activity.py +37 -0
- webatem-0.2.0/atem_control/apps.py +13 -0
- webatem-0.2.0/atem_control/control/__init__.py +9 -0
- webatem-0.2.0/atem_control/control/activity.py +194 -0
- webatem-0.2.0/atem_control/control/commands.py +469 -0
- webatem-0.2.0/atem_control/control/consumer.py +1245 -0
- webatem-0.2.0/atem_control/control/device_api.py +104 -0
- webatem-0.2.0/atem_control/control/logging.py +121 -0
- webatem-0.2.0/atem_control/control/views.py +222 -0
- webatem-0.2.0/atem_control/discovery.py +362 -0
- webatem-0.2.0/atem_control/hyperdeck/__init__.py +8 -0
- webatem-0.2.0/atem_control/hyperdeck/connection.py +146 -0
- webatem-0.2.0/atem_control/hyperdeck/views.py +197 -0
- webatem-0.2.0/atem_control/ip_upload_lock.py +32 -0
- webatem-0.2.0/atem_control/media_pool/__init__.py +36 -0
- webatem-0.2.0/atem_control/media_pool/broadcast.py +79 -0
- webatem-0.2.0/atem_control/media_pool/views.py +155 -0
- webatem-0.2.0/atem_control/media_pool/watcher.py +1348 -0
- webatem-0.2.0/atem_control/migrations/0001_initial.py +34 -0
- webatem-0.2.0/atem_control/migrations/0002_switcher.py +26 -0
- webatem-0.2.0/atem_control/migrations/0003_remove_atemcontrollog_atem_contro_user_id_3d6bb9_idx_and_more.py +29 -0
- webatem-0.2.0/atem_control/migrations/0004_delete_switcher.py +16 -0
- webatem-0.2.0/atem_control/migrations/__init__.py +0 -0
- webatem-0.2.0/atem_control/models.py +45 -0
- webatem-0.2.0/atem_control/netutil.py +36 -0
- webatem-0.2.0/atem_control/profile/__init__.py +5 -0
- webatem-0.2.0/atem_control/profile/dialog.py +521 -0
- webatem-0.2.0/atem_control/profile/export.py +406 -0
- webatem-0.2.0/atem_control/profile/views.py +444 -0
- webatem-0.2.0/atem_control/routing.py +11 -0
- webatem-0.2.0/atem_control/sightings.py +14 -0
- webatem-0.2.0/atem_control/static/css/atem_connect.css +34 -0
- webatem-0.2.0/atem_control/static/css/atem_control.css +872 -0
- webatem-0.2.0/atem_control/static/css/theme.css +620 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/OFL.txt +93 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Bold.ttf +0 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Italic.ttf +0 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Medium.ttf +0 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Regular.ttf +0 -0
- webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-SemiBold.ttf +0 -0
- webatem-0.2.0/atem_control/static/icon.png +0 -0
- webatem-0.2.0/atem_control/static/js/atem_connect.js +403 -0
- webatem-0.2.0/atem_control/static/js/atem_control.js +4151 -0
- webatem-0.2.0/atem_control/static/js/atem_control_page.js +864 -0
- webatem-0.2.0/atem_control/static/js/atem_discovery.js +150 -0
- webatem-0.2.0/atem_control/static/js/atem_hyperdeck.js +405 -0
- webatem-0.2.0/atem_control/static/js/atem_profile.js +569 -0
- webatem-0.2.0/atem_control/static/js/atem_realtime_slider.js +248 -0
- webatem-0.2.0/atem_control/static/js/custom-scrollbar.js +228 -0
- webatem-0.2.0/atem_control/static/js/pp-select.js +156 -0
- webatem-0.2.0/atem_control/static/vendor/README.md +30 -0
- webatem-0.2.0/atem_control/static/vendor/alpine.min.js +5 -0
- webatem-0.2.0/atem_control/static/vendor/alpinejs-collapse-3.14.3.min.js +1 -0
- webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
- webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- webatem-0.2.0/atem_control/static/vendor/webatem.css +1 -0
- webatem-0.2.0/atem_control/storage.py +23 -0
- webatem-0.2.0/atem_control/tally.py +141 -0
- webatem-0.2.0/atem_control/templates/connect.html +109 -0
- webatem-0.2.0/atem_control/templates/control/_atem_info.html +91 -0
- webatem-0.2.0/atem_control/templates/control/_fade_to_black.html +38 -0
- webatem-0.2.0/atem_control/templates/control/_fairlight.html +1378 -0
- webatem-0.2.0/atem_control/templates/control/_header.html +150 -0
- webatem-0.2.0/atem_control/templates/control/_hyperdeck_panel.html +180 -0
- webatem-0.2.0/atem_control/templates/control/_macros.html +41 -0
- webatem-0.2.0/atem_control/templates/control/_media_pool.html +360 -0
- webatem-0.2.0/atem_control/templates/control/_profile_modal.html +313 -0
- webatem-0.2.0/atem_control/templates/control/_settings_dsk.html +410 -0
- webatem-0.2.0/atem_control/templates/control/_settings_transition.html +854 -0
- webatem-0.2.0/atem_control/templates/control/_settings_usk.html +1366 -0
- webatem-0.2.0/atem_control/templates/control/_slider.html +63 -0
- webatem-0.2.0/atem_control/templates/control/_transition_controls.html +36 -0
- webatem-0.2.0/atem_control/templates/control/_transition_style.html +19 -0
- webatem-0.2.0/atem_control/templates/control/_usk_fly.html +131 -0
- webatem-0.2.0/atem_control/templates/control/_usk_keyframe.html +161 -0
- webatem-0.2.0/atem_control/templates/control/_usk_mask.html +123 -0
- webatem-0.2.0/atem_control/templates/control/_usk_slider.html +8 -0
- webatem-0.2.0/atem_control/templates/control.html +808 -0
- webatem-0.2.0/atem_control/templatetags/__init__.py +0 -0
- webatem-0.2.0/atem_control/templatetags/static_v.py +54 -0
- webatem-0.2.0/atem_control/uploader.py +800 -0
- webatem-0.2.0/atem_control/urls.py +32 -0
- webatem-0.2.0/pyproject.toml +62 -0
- webatem-0.2.0/setup.cfg +4 -0
- webatem-0.2.0/webatem/__init__.py +0 -0
- webatem-0.2.0/webatem/__main__.py +4 -0
- webatem-0.2.0/webatem/asgi.py +28 -0
- webatem-0.2.0/webatem/context.py +6 -0
- webatem-0.2.0/webatem/launcher.py +309 -0
- webatem-0.2.0/webatem/settings.py +142 -0
- webatem-0.2.0/webatem/templates/base.html +208 -0
- webatem-0.2.0/webatem/urls.py +7 -0
- webatem-0.2.0/webatem/websocket.py +133 -0
- webatem-0.2.0/webatem.egg-info/PKG-INFO +335 -0
- webatem-0.2.0/webatem.egg-info/SOURCES.txt +102 -0
- webatem-0.2.0/webatem.egg-info/dependency_links.txt +1 -0
- webatem-0.2.0/webatem.egg-info/entry_points.txt +2 -0
- webatem-0.2.0/webatem.egg-info/requires.txt +14 -0
- webatem-0.2.0/webatem.egg-info/top_level.txt +2 -0
webatem-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lucas Romanenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
NOTE: the `pyatem/` directory is a fork of the OpenAtem pyatem library and
|
|
26
|
+
is licensed LGPL-3.0-only, NOT MIT — see `pyatem/LICENSE` and
|
|
27
|
+
`pyatem/NOTICE.md`.
|
webatem-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: webatem
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Browser-based control for Blackmagic ATEM switchers: run it, open the browser, your switchers are there
|
|
5
|
+
Author: Lucas Romanenko
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/lucas-romanenko/webatem
|
|
8
|
+
Project-URL: Issues, https://github.com/lucas-romanenko/webatem/issues
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Classifier: Environment :: Web Environment
|
|
18
|
+
Classifier: Framework :: Django
|
|
19
|
+
Classifier: Topic :: Multimedia :: Video
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: Django==6.0.7
|
|
24
|
+
Requires-Dist: channels==4.3.2
|
|
25
|
+
Requires-Dist: uvicorn[standard]==0.52.0
|
|
26
|
+
Requires-Dist: pillow==12.3.0
|
|
27
|
+
Requires-Dist: whitenoise==6.12.0
|
|
28
|
+
Requires-Dist: zeroconf==0.147.2
|
|
29
|
+
Requires-Dist: pystray==0.19.5
|
|
30
|
+
Requires-Dist: atemwire==0.15.0.dev1
|
|
31
|
+
Requires-Dist: hyperdeckwire==0.1.0.dev1
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest==9.1.1; extra == "test"
|
|
34
|
+
Requires-Dist: pytest-django==4.12.0; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-timeout==2.4.0; extra == "test"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# WebATEM
|
|
39
|
+
|
|
40
|
+
**Control your Blackmagic ATEM switcher from any browser.** WebATEM finds the
|
|
41
|
+
ATEMs on your network and gives you a full control surface — switching,
|
|
42
|
+
keyers, audio, media pool, macros — at a URL, on any device.
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-windows-x64.exe"><img src="https://img.shields.io/badge/Download-Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white" alt="Download for Windows"></a>
|
|
46
|
+
|
|
47
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-arm64.dmg"><img src="https://img.shields.io/badge/Download-macOS-000000?style=for-the-badge&logo=apple&logoColor=white" alt="Download for macOS"></a>
|
|
48
|
+
|
|
49
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-linux-x64"><img src="https://img.shields.io/badge/Download-Linux-E95420?style=for-the-badge&logo=linux&logoColor=white" alt="Download for Linux"></a>
|
|
50
|
+
</p>
|
|
51
|
+
<p align="center"><sub>Download it, open it, and your switchers appear. · Intel Mac? <a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-intel.dmg">Intel build</a> · The Linux build runs on desktop <b>and</b> headless servers.</sub></p>
|
|
52
|
+
|
|
53
|
+
Three ways to run it, all simple:
|
|
54
|
+
|
|
55
|
+
- **Just you, right now** → **download it** for your Mac or Windows machine
|
|
56
|
+
and open it. It lives in your menu bar / system tray (with a *Start at
|
|
57
|
+
login* switch), opens your browser, and your switchers appear. Like ATEM
|
|
58
|
+
Software Control, but in your browser — and on every device on the network.
|
|
59
|
+
- **Your whole team, always on** → **host it** on a Linux box, VM or Raspberry
|
|
60
|
+
Pi with one Docker command; everyone opens a URL — no installs, no accounts.
|
|
61
|
+
- **Already have Python?** → `pipx install webatem`, then `webatem`.
|
|
62
|
+
|
|
63
|
+
Every way of running it **auto-discovers the ATEMs on your network**
|
|
64
|
+
(Bonjour/mDNS) and lists them by name, exactly like ATEM Software Control.
|
|
65
|
+
|
|
66
|
+
<p align="center">
|
|
67
|
+
<img src="docs/screenshot-control.png" alt="WebATEM control surface — program/preview buses, T-bar, keyers, transitions and macros" width="900">
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Get started
|
|
73
|
+
|
|
74
|
+
### Option A — Download it *(Mac, Windows)*
|
|
75
|
+
|
|
76
|
+
1. Click your platform's **Download** button at the top (or the
|
|
77
|
+
[Releases](https://github.com/lucas-romanenko/webatem/releases) page).
|
|
78
|
+
2. Open it. It starts a local server, opens your browser, and lists the ATEMs
|
|
79
|
+
on your network. Pick one and you're controlling it.
|
|
80
|
+
|
|
81
|
+
That's the whole setup — nothing to install alongside it, no Python, no Docker.
|
|
82
|
+
|
|
83
|
+
While it runs it sits in the **menu bar (Mac) / system tray (Windows)**: the
|
|
84
|
+
icon's menu shows the address other devices can use, opens the browser again,
|
|
85
|
+
and has a **Start at login** switch so it is back after every reboot. **Quit**
|
|
86
|
+
is there too. Like Bitfocus Companion: the server is the app, the browser is
|
|
87
|
+
the interface.
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary><b>First-launch security prompt</b> (the app isn't code-signed yet)</summary>
|
|
91
|
+
|
|
92
|
+
Because the downloads aren't signed with a paid developer certificate, the OS
|
|
93
|
+
warns you the first time. One-time, then it opens normally:
|
|
94
|
+
|
|
95
|
+
- **macOS:** double-click, let it get blocked, then **System Settings →
|
|
96
|
+
Privacy & Security → “Open Anyway”**. (On older macOS: right-click → Open.)
|
|
97
|
+
- **Windows:** **More info → Run anyway** on the SmartScreen prompt.
|
|
98
|
+
|
|
99
|
+
Removing the warning entirely requires paid Apple/Microsoft signing
|
|
100
|
+
certificates — planned, not done yet.
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
### Option B — Host it *(a whole team; always on)*
|
|
104
|
+
|
|
105
|
+
Run it on a **Linux box, VM or Raspberry Pi on the same network as your
|
|
106
|
+
ATEMs** (the ATEM-Software-Control-on-a-server model). The published image
|
|
107
|
+
is built for amd64 and arm64:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
docker run -d --name webatem --network host --restart unless-stopped \
|
|
111
|
+
-v webatem-data:/app/data ghcr.io/lucas-romanenko/webatem:latest
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Open `http://<that-box>:8000` from any device on the network. It comes back
|
|
115
|
+
automatically on reboot. No `.env` file needed — every setting has a working
|
|
116
|
+
default (SQLite DB + a generated secret key live in the `webatem-data`
|
|
117
|
+
volume); see [.env.example](.env.example) for knobs like `PORT`, `TIME_ZONE`,
|
|
118
|
+
`ALLOWED_HOSTS`. Prefer Compose? The repository's
|
|
119
|
+
[compose.yml](compose.yml) runs the same image: `docker compose up -d`.
|
|
120
|
+
|
|
121
|
+
Without Docker: the `webatem-linux-x64` binary from the Releases page runs
|
|
122
|
+
headless too — on a machine with no display it skips the browser and tray and
|
|
123
|
+
prints the address to open from another device:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
WebATEM is running.
|
|
127
|
+
On this machine: http://127.0.0.1:8000/atem/
|
|
128
|
+
From another device: http://192.168.1.50:8000/atem/
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Keep it up with a systemd unit, `tmux` or `nohup`.
|
|
132
|
+
|
|
133
|
+
> **Why Linux for hosting?** Discovery needs the app to see your LAN directly.
|
|
134
|
+
> That works natively on Linux (the container uses host networking). Docker
|
|
135
|
+
> Desktop on **Mac/Windows** runs containers in a VM that can’t see LAN
|
|
136
|
+
> discovery traffic — so on a Mac or PC, use the download (Option A) or pipx
|
|
137
|
+
> (Option C), not Docker.
|
|
138
|
+
|
|
139
|
+
### Option C — pipx *(you already have Python 3.10+)*
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pipx install webatem # or: uvx webatem, with no install at all
|
|
143
|
+
webatem
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Same behaviour as the download: a native process on your real network, the
|
|
147
|
+
browser opens, the tray icon appears where there is a desktop. Prebuilt wheels
|
|
148
|
+
for every platform mean no compiler is needed. Upgrade with
|
|
149
|
+
`pipx upgrade webatem`.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Finding your switchers
|
|
154
|
+
|
|
155
|
+
Three ways, in order of magic:
|
|
156
|
+
|
|
157
|
+
1. **Automatic (Bonjour/mDNS).** On the same network as your ATEMs, they show
|
|
158
|
+
up under **“On Your Network”** with their names, no typing. This is what
|
|
159
|
+
ATEM Software Control does.
|
|
160
|
+
2. **Scan subnet.** For ATEMs that don’t advertise over Bonjour, type your
|
|
161
|
+
subnet (e.g. `192.168.1`) and hit **Scan** — it finds them by IP.
|
|
162
|
+
3. **Manual IP.** Type an address and connect. Recent connections are
|
|
163
|
+
remembered for one-click reconnect.
|
|
164
|
+
|
|
165
|
+
> Automatic discovery is **same-network only** — Bonjour/mDNS is link-local
|
|
166
|
+
> and doesn’t cross a router or VPN (this is true of ASC too). Reaching your
|
|
167
|
+
> ATEMs over a **VPN**? Auto-discovery won’t see them, but **Scan** and
|
|
168
|
+
> **manual IP** work fine over the tunnel.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## WebATEM vs. ATEM Software Control
|
|
173
|
+
|
|
174
|
+
ATEM Software Control (ASC) is Blackmagic’s own free control app, and it’s
|
|
175
|
+
excellent — WebATEM isn’t trying to replace all of it. The difference is
|
|
176
|
+
*access*: ASC is a desktop app tied to one machine; WebATEM is a control
|
|
177
|
+
surface any device on your network can open (and you can still run it locally
|
|
178
|
+
like ASC if you want).
|
|
179
|
+
|
|
180
|
+
| | **WebATEM** | **ATEM Software Control** |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| **Runs on** | Any browser — Windows, macOS, **Linux**, ChromeOS, iPad, phone | Windows & macOS desktop only |
|
|
183
|
+
| **Client install** | None — just open a URL | Installed per machine |
|
|
184
|
+
| **Access from** | Any device on the network | The machine it’s installed on |
|
|
185
|
+
| **Multiple operators** | One shared switcher session, many operators | Each machine opens its own session |
|
|
186
|
+
| **Phones / tablets** | ✓ Responsive | ✗ |
|
|
187
|
+
| **How to run** | Download it, host one instance for everyone (Docker), or `pipx install webatem` | Install on each machine |
|
|
188
|
+
| **Price** | Free, open source | Free (proprietary) |
|
|
189
|
+
| **Feature breadth** | Core: switching, keyers, Fairlight audio, media pool, macros, profiles, HyperDeck | Everything, incl. camera control (CCU), streaming & recording, SuperSource |
|
|
190
|
+
| **Support** | Community / self-hosted | Official Blackmagic |
|
|
191
|
+
|
|
192
|
+
**Reach for ASC** when you need the full feature set on one operator’s machine
|
|
193
|
+
— camera control, streaming/recording, SuperSource, recording macros.
|
|
194
|
+
|
|
195
|
+
**Reach for WebATEM** when you want the core control surface available to
|
|
196
|
+
anyone on the network, on any device or OS — a second operator on an iPad, a
|
|
197
|
+
Linux box in the rack room, a phone at the camera position.
|
|
198
|
+
|
|
199
|
+
## Features
|
|
200
|
+
|
|
201
|
+
- **Full switcher control** — program/preview buses per M/E, cut/auto,
|
|
202
|
+
transition styles (mix, dip, wipe, DVE, stinger) with per-style settings,
|
|
203
|
+
fade to black, color generators, aux routing, source renaming that follows
|
|
204
|
+
live switcher labels.
|
|
205
|
+
- **Upstream & downstream keyers** — USK 1–4 (luma, chroma, pattern, DVE with
|
|
206
|
+
fly keyframes, masks), DSKs with tie/rate/clip/gain, on-air countdowns.
|
|
207
|
+
- **Fairlight audio** — per-strip faders, EQ, dynamics, master bus, and live
|
|
208
|
+
audio meters streamed over WebSocket (tab-gated so idle pages cost nothing).
|
|
209
|
+
- **Media pool** — live thumbnails of every still slot, and drag-and-drop
|
|
210
|
+
image upload straight onto a slot (validated and resized to 1080p
|
|
211
|
+
server-side).
|
|
212
|
+
- **Macros** — browse and run switcher macros.
|
|
213
|
+
- **Save/restore switcher state** — full switcher profile export/import as
|
|
214
|
+
XML, compatible with ATEM Software Control’s “Save Switcher State”,
|
|
215
|
+
including media pool images and macro bytecode.
|
|
216
|
+
- **HyperDeck transport** — clip browser and play/pause/stop/loop for decks
|
|
217
|
+
bound to the ATEM (TCP/9993), from the same page.
|
|
218
|
+
- **Multi-operator by design** — one pooled connection per switcher shared by
|
|
219
|
+
all operators; state fan-out over WebSockets with adaptive polling
|
|
220
|
+
(30 ms during transitions, relaxed when idle).
|
|
221
|
+
|
|
222
|
+
Deep per-switcher settings live in the Settings panel — color generators,
|
|
223
|
+
each upstream keyer (luma / chroma / pattern / DVE), downstream keyers,
|
|
224
|
+
transitions, video mode, input labels, outputs and HyperDecks:
|
|
225
|
+
|
|
226
|
+
<p align="center">
|
|
227
|
+
<img src="docs/screenshot-settings.png" alt="WebATEM settings panel — color generators and an upstream keyer expanded, showing HSL controls, fill-source dropdown and wipe patterns" width="900">
|
|
228
|
+
</p>
|
|
229
|
+
|
|
230
|
+
Full **Fairlight audio** — a per-strip mixer with input gain, EQ and dynamics
|
|
231
|
+
curves, faders, pan and live meters, plus a writable 6-band parametric EQ with
|
|
232
|
+
a live frequency-response graph:
|
|
233
|
+
|
|
234
|
+
<p align="center">
|
|
235
|
+
<img src="docs/screenshot-audio.png" alt="WebATEM Fairlight audio mixer — 14 strips with input, EQ, dynamics, faders, pan and meters" width="900">
|
|
236
|
+
</p>
|
|
237
|
+
<p align="center">
|
|
238
|
+
<img src="docs/screenshot-eq.png" alt="WebATEM 6-band parametric EQ — frequency-response curve with per-band type, frequency, gain and Q" width="900">
|
|
239
|
+
</p>
|
|
240
|
+
|
|
241
|
+
## Running it for real
|
|
242
|
+
|
|
243
|
+
- **There is no login — by design.** Like the hardware panel, anyone who can
|
|
244
|
+
reach the page can control your switchers. Keep it on the studio network. If
|
|
245
|
+
you must expose it further, put your reverse proxy’s auth (basic auth, SSO)
|
|
246
|
+
and TLS in front, and set `CSRF_TRUSTED_ORIGINS=https://your.host`.
|
|
247
|
+
- **Web pages can’t drive it cross-origin.** The control WebSocket only
|
|
248
|
+
accepts browser connections from the app’s own pages (same-origin), so a
|
|
249
|
+
malicious website open in an operator’s browser can’t reach the switchers.
|
|
250
|
+
If your reverse proxy rewrites `Host`, list the browser-facing host in
|
|
251
|
+
`WEBSOCKET_ALLOWED_ORIGINS`.
|
|
252
|
+
- It talks raw UDP to switchers on port 9910 (the ATEM protocol has no
|
|
253
|
+
authentication — that’s the hardware, not this app), so it needs to be on a
|
|
254
|
+
network that can reach them.
|
|
255
|
+
- **Single instance, by design.** The switcher connection pool, media-pool
|
|
256
|
+
watcher, and channel layer are process-local; one instance handles many
|
|
257
|
+
switchers and operators. Don’t run several against the same switchers.
|
|
258
|
+
- Stills upload/capture assume **1080p** switchers (all production use so far).
|
|
259
|
+
Non-1080p ATEMs connect and control fine; media features are untested there.
|
|
260
|
+
|
|
261
|
+
## Architecture
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Browser (Alpine.js + WebSockets)
|
|
265
|
+
│ ws/atem/ · HTTP
|
|
266
|
+
▼
|
|
267
|
+
Django + Channels (single ASGI worker, uvicorn)
|
|
268
|
+
│ pooled UDP session per switcher (atemwire)
|
|
269
|
+
▼
|
|
270
|
+
ATEM switchers (UDP 9910) · HyperDecks (TCP 9993 / FTP)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- **[atemwire](https://github.com/lucas-romanenko/bmdwire/tree/main/atemwire)** (PyPI) — the
|
|
274
|
+
ATEM protocol library, a substantially modified fork of Martijn Braam's
|
|
275
|
+
pyatem: declarative wire-format DSL, hardened UDP transport (in-order
|
|
276
|
+
delivery, retransmit serving, clean session close), ref-counted connection
|
|
277
|
+
pooling, native interleaved bulk transfers, macro bytecode transfer, and
|
|
278
|
+
ASC-compatible profile save/restore. A small C extension does YCbCr↔RGB
|
|
279
|
+
conversion. **[hyperdeckwire](https://github.com/lucas-romanenko/bmdwire/tree/main/hyperdeckwire)**
|
|
280
|
+
(PyPI) drives the HyperDecks. Both are pinned in `pyproject.toml`; a
|
|
281
|
+
library change is a release there and a pin bump here (Dependabot opens it,
|
|
282
|
+
CI runs, it merges itself).
|
|
283
|
+
- **`atem_control/`** — the Django app: the WebSocket consumer, a declarative
|
|
284
|
+
command dispatch table, the media-pool watcher, LAN discovery, and the UI.
|
|
285
|
+
- **`webatem/`** — the project package: settings, the ASGI entry, the
|
|
286
|
+
WebSocket origin guard, and `launcher.py` — the `webatem` command that runs
|
|
287
|
+
the same web app locally, opens a browser and lives in the tray. The
|
|
288
|
+
downloads are that launcher frozen per OS by PyInstaller
|
|
289
|
+
(`build/desktop/`); the PyPI package is the same code installed by pip.
|
|
290
|
+
- **One tag, three deliverables.** A `v*` tag publishes the Mac / Windows /
|
|
291
|
+
Linux downloads to the GitHub release, the Docker image to GHCR (amd64 +
|
|
292
|
+
arm64) and the `webatem` package to PyPI.
|
|
293
|
+
- **SQLite** for persistence (connection history) — no external database.
|
|
294
|
+
|
|
295
|
+
## Development
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
git clone https://github.com/lucas-romanenko/webatem.git
|
|
299
|
+
cd webatem
|
|
300
|
+
npm install && npm run build:css # the stylesheet — without it the app renders unstyled
|
|
301
|
+
pip install -e ".[test]" # Python 3.10+; the device libraries install as wheels
|
|
302
|
+
python -m webatem # the launcher: server + browser + tray
|
|
303
|
+
python -m pytest tests/ -q # the suite (no hardware needed)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Or entirely in Docker: `docker compose up -d --build` runs the app and
|
|
307
|
+
`docker compose exec webatem pytest tests/ -q` runs the suite in it. The suite
|
|
308
|
+
(the command dispatch table, the T-bar, the consumer's state-change and
|
|
309
|
+
session-cleanup rules, upload policy, tally safety, profile dialogs, the
|
|
310
|
+
media-pool thumbnail cache, LAN discovery identity, WebSocket origin checks)
|
|
311
|
+
runs against fake protocol layers. CI runs it on every push. The protocol
|
|
312
|
+
libraries carry their own suites in [bmdwire](https://github.com/lucas-romanenko/bmdwire).
|
|
313
|
+
|
|
314
|
+
`python manage.py runserver` works too for plain Django development. The
|
|
315
|
+
per-OS downloads are built by the **Desktop builds** workflow
|
|
316
|
+
(`pyinstaller build/desktop/webatem.spec`); the control page itself is
|
|
317
|
+
synced from its upstream application by `tools/sync_from_av_server.py`.
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
- Application code: [MIT](LICENSE).
|
|
322
|
+
- The ATEM protocol library, [atemwire](https://github.com/lucas-romanenko/bmdwire/tree/main/atemwire),
|
|
323
|
+
is a separate package (a fork of the [OpenAtem pyatem
|
|
324
|
+
library](https://git.sr.ht/~martijnbraam/pyatem)) and is **LGPL-3.0-only**;
|
|
325
|
+
WebATEM uses it as an installed dependency, unmodified.
|
|
326
|
+
[hyperdeckwire](https://github.com/lucas-romanenko/bmdwire/tree/main/hyperdeckwire) is MIT.
|
|
327
|
+
- Bundled frontend assets (Alpine.js and its collapse plugin, Bootstrap Icons,
|
|
328
|
+
Tailwind/daisyUI) are MIT and the Poppins font is OFL — notices in
|
|
329
|
+
[THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md).
|
|
330
|
+
|
|
331
|
+
Security posture and how to report vulnerabilities: [SECURITY.md](SECURITY.md).
|
|
332
|
+
|
|
333
|
+
Not affiliated with or endorsed by Blackmagic Design. ATEM and HyperDeck are
|
|
334
|
+
trademarks of Blackmagic Design Pty Ltd. Use against production hardware at
|
|
335
|
+
your own risk.
|
webatem-0.2.0/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# WebATEM
|
|
2
|
+
|
|
3
|
+
**Control your Blackmagic ATEM switcher from any browser.** WebATEM finds the
|
|
4
|
+
ATEMs on your network and gives you a full control surface — switching,
|
|
5
|
+
keyers, audio, media pool, macros — at a URL, on any device.
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-windows-x64.exe"><img src="https://img.shields.io/badge/Download-Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white" alt="Download for Windows"></a>
|
|
9
|
+
|
|
10
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-arm64.dmg"><img src="https://img.shields.io/badge/Download-macOS-000000?style=for-the-badge&logo=apple&logoColor=white" alt="Download for macOS"></a>
|
|
11
|
+
|
|
12
|
+
<a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-linux-x64"><img src="https://img.shields.io/badge/Download-Linux-E95420?style=for-the-badge&logo=linux&logoColor=white" alt="Download for Linux"></a>
|
|
13
|
+
</p>
|
|
14
|
+
<p align="center"><sub>Download it, open it, and your switchers appear. · Intel Mac? <a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-intel.dmg">Intel build</a> · The Linux build runs on desktop <b>and</b> headless servers.</sub></p>
|
|
15
|
+
|
|
16
|
+
Three ways to run it, all simple:
|
|
17
|
+
|
|
18
|
+
- **Just you, right now** → **download it** for your Mac or Windows machine
|
|
19
|
+
and open it. It lives in your menu bar / system tray (with a *Start at
|
|
20
|
+
login* switch), opens your browser, and your switchers appear. Like ATEM
|
|
21
|
+
Software Control, but in your browser — and on every device on the network.
|
|
22
|
+
- **Your whole team, always on** → **host it** on a Linux box, VM or Raspberry
|
|
23
|
+
Pi with one Docker command; everyone opens a URL — no installs, no accounts.
|
|
24
|
+
- **Already have Python?** → `pipx install webatem`, then `webatem`.
|
|
25
|
+
|
|
26
|
+
Every way of running it **auto-discovers the ATEMs on your network**
|
|
27
|
+
(Bonjour/mDNS) and lists them by name, exactly like ATEM Software Control.
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="docs/screenshot-control.png" alt="WebATEM control surface — program/preview buses, T-bar, keyers, transitions and macros" width="900">
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Get started
|
|
36
|
+
|
|
37
|
+
### Option A — Download it *(Mac, Windows)*
|
|
38
|
+
|
|
39
|
+
1. Click your platform's **Download** button at the top (or the
|
|
40
|
+
[Releases](https://github.com/lucas-romanenko/webatem/releases) page).
|
|
41
|
+
2. Open it. It starts a local server, opens your browser, and lists the ATEMs
|
|
42
|
+
on your network. Pick one and you're controlling it.
|
|
43
|
+
|
|
44
|
+
That's the whole setup — nothing to install alongside it, no Python, no Docker.
|
|
45
|
+
|
|
46
|
+
While it runs it sits in the **menu bar (Mac) / system tray (Windows)**: the
|
|
47
|
+
icon's menu shows the address other devices can use, opens the browser again,
|
|
48
|
+
and has a **Start at login** switch so it is back after every reboot. **Quit**
|
|
49
|
+
is there too. Like Bitfocus Companion: the server is the app, the browser is
|
|
50
|
+
the interface.
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary><b>First-launch security prompt</b> (the app isn't code-signed yet)</summary>
|
|
54
|
+
|
|
55
|
+
Because the downloads aren't signed with a paid developer certificate, the OS
|
|
56
|
+
warns you the first time. One-time, then it opens normally:
|
|
57
|
+
|
|
58
|
+
- **macOS:** double-click, let it get blocked, then **System Settings →
|
|
59
|
+
Privacy & Security → “Open Anyway”**. (On older macOS: right-click → Open.)
|
|
60
|
+
- **Windows:** **More info → Run anyway** on the SmartScreen prompt.
|
|
61
|
+
|
|
62
|
+
Removing the warning entirely requires paid Apple/Microsoft signing
|
|
63
|
+
certificates — planned, not done yet.
|
|
64
|
+
</details>
|
|
65
|
+
|
|
66
|
+
### Option B — Host it *(a whole team; always on)*
|
|
67
|
+
|
|
68
|
+
Run it on a **Linux box, VM or Raspberry Pi on the same network as your
|
|
69
|
+
ATEMs** (the ATEM-Software-Control-on-a-server model). The published image
|
|
70
|
+
is built for amd64 and arm64:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docker run -d --name webatem --network host --restart unless-stopped \
|
|
74
|
+
-v webatem-data:/app/data ghcr.io/lucas-romanenko/webatem:latest
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Open `http://<that-box>:8000` from any device on the network. It comes back
|
|
78
|
+
automatically on reboot. No `.env` file needed — every setting has a working
|
|
79
|
+
default (SQLite DB + a generated secret key live in the `webatem-data`
|
|
80
|
+
volume); see [.env.example](.env.example) for knobs like `PORT`, `TIME_ZONE`,
|
|
81
|
+
`ALLOWED_HOSTS`. Prefer Compose? The repository's
|
|
82
|
+
[compose.yml](compose.yml) runs the same image: `docker compose up -d`.
|
|
83
|
+
|
|
84
|
+
Without Docker: the `webatem-linux-x64` binary from the Releases page runs
|
|
85
|
+
headless too — on a machine with no display it skips the browser and tray and
|
|
86
|
+
prints the address to open from another device:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
WebATEM is running.
|
|
90
|
+
On this machine: http://127.0.0.1:8000/atem/
|
|
91
|
+
From another device: http://192.168.1.50:8000/atem/
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Keep it up with a systemd unit, `tmux` or `nohup`.
|
|
95
|
+
|
|
96
|
+
> **Why Linux for hosting?** Discovery needs the app to see your LAN directly.
|
|
97
|
+
> That works natively on Linux (the container uses host networking). Docker
|
|
98
|
+
> Desktop on **Mac/Windows** runs containers in a VM that can’t see LAN
|
|
99
|
+
> discovery traffic — so on a Mac or PC, use the download (Option A) or pipx
|
|
100
|
+
> (Option C), not Docker.
|
|
101
|
+
|
|
102
|
+
### Option C — pipx *(you already have Python 3.10+)*
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pipx install webatem # or: uvx webatem, with no install at all
|
|
106
|
+
webatem
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Same behaviour as the download: a native process on your real network, the
|
|
110
|
+
browser opens, the tray icon appears where there is a desktop. Prebuilt wheels
|
|
111
|
+
for every platform mean no compiler is needed. Upgrade with
|
|
112
|
+
`pipx upgrade webatem`.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Finding your switchers
|
|
117
|
+
|
|
118
|
+
Three ways, in order of magic:
|
|
119
|
+
|
|
120
|
+
1. **Automatic (Bonjour/mDNS).** On the same network as your ATEMs, they show
|
|
121
|
+
up under **“On Your Network”** with their names, no typing. This is what
|
|
122
|
+
ATEM Software Control does.
|
|
123
|
+
2. **Scan subnet.** For ATEMs that don’t advertise over Bonjour, type your
|
|
124
|
+
subnet (e.g. `192.168.1`) and hit **Scan** — it finds them by IP.
|
|
125
|
+
3. **Manual IP.** Type an address and connect. Recent connections are
|
|
126
|
+
remembered for one-click reconnect.
|
|
127
|
+
|
|
128
|
+
> Automatic discovery is **same-network only** — Bonjour/mDNS is link-local
|
|
129
|
+
> and doesn’t cross a router or VPN (this is true of ASC too). Reaching your
|
|
130
|
+
> ATEMs over a **VPN**? Auto-discovery won’t see them, but **Scan** and
|
|
131
|
+
> **manual IP** work fine over the tunnel.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## WebATEM vs. ATEM Software Control
|
|
136
|
+
|
|
137
|
+
ATEM Software Control (ASC) is Blackmagic’s own free control app, and it’s
|
|
138
|
+
excellent — WebATEM isn’t trying to replace all of it. The difference is
|
|
139
|
+
*access*: ASC is a desktop app tied to one machine; WebATEM is a control
|
|
140
|
+
surface any device on your network can open (and you can still run it locally
|
|
141
|
+
like ASC if you want).
|
|
142
|
+
|
|
143
|
+
| | **WebATEM** | **ATEM Software Control** |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| **Runs on** | Any browser — Windows, macOS, **Linux**, ChromeOS, iPad, phone | Windows & macOS desktop only |
|
|
146
|
+
| **Client install** | None — just open a URL | Installed per machine |
|
|
147
|
+
| **Access from** | Any device on the network | The machine it’s installed on |
|
|
148
|
+
| **Multiple operators** | One shared switcher session, many operators | Each machine opens its own session |
|
|
149
|
+
| **Phones / tablets** | ✓ Responsive | ✗ |
|
|
150
|
+
| **How to run** | Download it, host one instance for everyone (Docker), or `pipx install webatem` | Install on each machine |
|
|
151
|
+
| **Price** | Free, open source | Free (proprietary) |
|
|
152
|
+
| **Feature breadth** | Core: switching, keyers, Fairlight audio, media pool, macros, profiles, HyperDeck | Everything, incl. camera control (CCU), streaming & recording, SuperSource |
|
|
153
|
+
| **Support** | Community / self-hosted | Official Blackmagic |
|
|
154
|
+
|
|
155
|
+
**Reach for ASC** when you need the full feature set on one operator’s machine
|
|
156
|
+
— camera control, streaming/recording, SuperSource, recording macros.
|
|
157
|
+
|
|
158
|
+
**Reach for WebATEM** when you want the core control surface available to
|
|
159
|
+
anyone on the network, on any device or OS — a second operator on an iPad, a
|
|
160
|
+
Linux box in the rack room, a phone at the camera position.
|
|
161
|
+
|
|
162
|
+
## Features
|
|
163
|
+
|
|
164
|
+
- **Full switcher control** — program/preview buses per M/E, cut/auto,
|
|
165
|
+
transition styles (mix, dip, wipe, DVE, stinger) with per-style settings,
|
|
166
|
+
fade to black, color generators, aux routing, source renaming that follows
|
|
167
|
+
live switcher labels.
|
|
168
|
+
- **Upstream & downstream keyers** — USK 1–4 (luma, chroma, pattern, DVE with
|
|
169
|
+
fly keyframes, masks), DSKs with tie/rate/clip/gain, on-air countdowns.
|
|
170
|
+
- **Fairlight audio** — per-strip faders, EQ, dynamics, master bus, and live
|
|
171
|
+
audio meters streamed over WebSocket (tab-gated so idle pages cost nothing).
|
|
172
|
+
- **Media pool** — live thumbnails of every still slot, and drag-and-drop
|
|
173
|
+
image upload straight onto a slot (validated and resized to 1080p
|
|
174
|
+
server-side).
|
|
175
|
+
- **Macros** — browse and run switcher macros.
|
|
176
|
+
- **Save/restore switcher state** — full switcher profile export/import as
|
|
177
|
+
XML, compatible with ATEM Software Control’s “Save Switcher State”,
|
|
178
|
+
including media pool images and macro bytecode.
|
|
179
|
+
- **HyperDeck transport** — clip browser and play/pause/stop/loop for decks
|
|
180
|
+
bound to the ATEM (TCP/9993), from the same page.
|
|
181
|
+
- **Multi-operator by design** — one pooled connection per switcher shared by
|
|
182
|
+
all operators; state fan-out over WebSockets with adaptive polling
|
|
183
|
+
(30 ms during transitions, relaxed when idle).
|
|
184
|
+
|
|
185
|
+
Deep per-switcher settings live in the Settings panel — color generators,
|
|
186
|
+
each upstream keyer (luma / chroma / pattern / DVE), downstream keyers,
|
|
187
|
+
transitions, video mode, input labels, outputs and HyperDecks:
|
|
188
|
+
|
|
189
|
+
<p align="center">
|
|
190
|
+
<img src="docs/screenshot-settings.png" alt="WebATEM settings panel — color generators and an upstream keyer expanded, showing HSL controls, fill-source dropdown and wipe patterns" width="900">
|
|
191
|
+
</p>
|
|
192
|
+
|
|
193
|
+
Full **Fairlight audio** — a per-strip mixer with input gain, EQ and dynamics
|
|
194
|
+
curves, faders, pan and live meters, plus a writable 6-band parametric EQ with
|
|
195
|
+
a live frequency-response graph:
|
|
196
|
+
|
|
197
|
+
<p align="center">
|
|
198
|
+
<img src="docs/screenshot-audio.png" alt="WebATEM Fairlight audio mixer — 14 strips with input, EQ, dynamics, faders, pan and meters" width="900">
|
|
199
|
+
</p>
|
|
200
|
+
<p align="center">
|
|
201
|
+
<img src="docs/screenshot-eq.png" alt="WebATEM 6-band parametric EQ — frequency-response curve with per-band type, frequency, gain and Q" width="900">
|
|
202
|
+
</p>
|
|
203
|
+
|
|
204
|
+
## Running it for real
|
|
205
|
+
|
|
206
|
+
- **There is no login — by design.** Like the hardware panel, anyone who can
|
|
207
|
+
reach the page can control your switchers. Keep it on the studio network. If
|
|
208
|
+
you must expose it further, put your reverse proxy’s auth (basic auth, SSO)
|
|
209
|
+
and TLS in front, and set `CSRF_TRUSTED_ORIGINS=https://your.host`.
|
|
210
|
+
- **Web pages can’t drive it cross-origin.** The control WebSocket only
|
|
211
|
+
accepts browser connections from the app’s own pages (same-origin), so a
|
|
212
|
+
malicious website open in an operator’s browser can’t reach the switchers.
|
|
213
|
+
If your reverse proxy rewrites `Host`, list the browser-facing host in
|
|
214
|
+
`WEBSOCKET_ALLOWED_ORIGINS`.
|
|
215
|
+
- It talks raw UDP to switchers on port 9910 (the ATEM protocol has no
|
|
216
|
+
authentication — that’s the hardware, not this app), so it needs to be on a
|
|
217
|
+
network that can reach them.
|
|
218
|
+
- **Single instance, by design.** The switcher connection pool, media-pool
|
|
219
|
+
watcher, and channel layer are process-local; one instance handles many
|
|
220
|
+
switchers and operators. Don’t run several against the same switchers.
|
|
221
|
+
- Stills upload/capture assume **1080p** switchers (all production use so far).
|
|
222
|
+
Non-1080p ATEMs connect and control fine; media features are untested there.
|
|
223
|
+
|
|
224
|
+
## Architecture
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
Browser (Alpine.js + WebSockets)
|
|
228
|
+
│ ws/atem/ · HTTP
|
|
229
|
+
▼
|
|
230
|
+
Django + Channels (single ASGI worker, uvicorn)
|
|
231
|
+
│ pooled UDP session per switcher (atemwire)
|
|
232
|
+
▼
|
|
233
|
+
ATEM switchers (UDP 9910) · HyperDecks (TCP 9993 / FTP)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
- **[atemwire](https://github.com/lucas-romanenko/bmdwire/tree/main/atemwire)** (PyPI) — the
|
|
237
|
+
ATEM protocol library, a substantially modified fork of Martijn Braam's
|
|
238
|
+
pyatem: declarative wire-format DSL, hardened UDP transport (in-order
|
|
239
|
+
delivery, retransmit serving, clean session close), ref-counted connection
|
|
240
|
+
pooling, native interleaved bulk transfers, macro bytecode transfer, and
|
|
241
|
+
ASC-compatible profile save/restore. A small C extension does YCbCr↔RGB
|
|
242
|
+
conversion. **[hyperdeckwire](https://github.com/lucas-romanenko/bmdwire/tree/main/hyperdeckwire)**
|
|
243
|
+
(PyPI) drives the HyperDecks. Both are pinned in `pyproject.toml`; a
|
|
244
|
+
library change is a release there and a pin bump here (Dependabot opens it,
|
|
245
|
+
CI runs, it merges itself).
|
|
246
|
+
- **`atem_control/`** — the Django app: the WebSocket consumer, a declarative
|
|
247
|
+
command dispatch table, the media-pool watcher, LAN discovery, and the UI.
|
|
248
|
+
- **`webatem/`** — the project package: settings, the ASGI entry, the
|
|
249
|
+
WebSocket origin guard, and `launcher.py` — the `webatem` command that runs
|
|
250
|
+
the same web app locally, opens a browser and lives in the tray. The
|
|
251
|
+
downloads are that launcher frozen per OS by PyInstaller
|
|
252
|
+
(`build/desktop/`); the PyPI package is the same code installed by pip.
|
|
253
|
+
- **One tag, three deliverables.** A `v*` tag publishes the Mac / Windows /
|
|
254
|
+
Linux downloads to the GitHub release, the Docker image to GHCR (amd64 +
|
|
255
|
+
arm64) and the `webatem` package to PyPI.
|
|
256
|
+
- **SQLite** for persistence (connection history) — no external database.
|
|
257
|
+
|
|
258
|
+
## Development
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
git clone https://github.com/lucas-romanenko/webatem.git
|
|
262
|
+
cd webatem
|
|
263
|
+
npm install && npm run build:css # the stylesheet — without it the app renders unstyled
|
|
264
|
+
pip install -e ".[test]" # Python 3.10+; the device libraries install as wheels
|
|
265
|
+
python -m webatem # the launcher: server + browser + tray
|
|
266
|
+
python -m pytest tests/ -q # the suite (no hardware needed)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Or entirely in Docker: `docker compose up -d --build` runs the app and
|
|
270
|
+
`docker compose exec webatem pytest tests/ -q` runs the suite in it. The suite
|
|
271
|
+
(the command dispatch table, the T-bar, the consumer's state-change and
|
|
272
|
+
session-cleanup rules, upload policy, tally safety, profile dialogs, the
|
|
273
|
+
media-pool thumbnail cache, LAN discovery identity, WebSocket origin checks)
|
|
274
|
+
runs against fake protocol layers. CI runs it on every push. The protocol
|
|
275
|
+
libraries carry their own suites in [bmdwire](https://github.com/lucas-romanenko/bmdwire).
|
|
276
|
+
|
|
277
|
+
`python manage.py runserver` works too for plain Django development. The
|
|
278
|
+
per-OS downloads are built by the **Desktop builds** workflow
|
|
279
|
+
(`pyinstaller build/desktop/webatem.spec`); the control page itself is
|
|
280
|
+
synced from its upstream application by `tools/sync_from_av_server.py`.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
- Application code: [MIT](LICENSE).
|
|
285
|
+
- The ATEM protocol library, [atemwire](https://github.com/lucas-romanenko/bmdwire/tree/main/atemwire),
|
|
286
|
+
is a separate package (a fork of the [OpenAtem pyatem
|
|
287
|
+
library](https://git.sr.ht/~martijnbraam/pyatem)) and is **LGPL-3.0-only**;
|
|
288
|
+
WebATEM uses it as an installed dependency, unmodified.
|
|
289
|
+
[hyperdeckwire](https://github.com/lucas-romanenko/bmdwire/tree/main/hyperdeckwire) is MIT.
|
|
290
|
+
- Bundled frontend assets (Alpine.js and its collapse plugin, Bootstrap Icons,
|
|
291
|
+
Tailwind/daisyUI) are MIT and the Poppins font is OFL — notices in
|
|
292
|
+
[THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md).
|
|
293
|
+
|
|
294
|
+
Security posture and how to report vulnerabilities: [SECURITY.md](SECURITY.md).
|
|
295
|
+
|
|
296
|
+
Not affiliated with or endorsed by Blackmagic Design. ATEM and HyperDeck are
|
|
297
|
+
trademarks of Blackmagic Design Pty Ltd. Use against production hardware at
|
|
298
|
+
your own risk.
|
|
File without changes
|