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.
Files changed (104) hide show
  1. webatem-0.2.0/LICENSE +27 -0
  2. webatem-0.2.0/PKG-INFO +335 -0
  3. webatem-0.2.0/README.md +298 -0
  4. webatem-0.2.0/atem_control/__init__.py +0 -0
  5. webatem-0.2.0/atem_control/activity.py +37 -0
  6. webatem-0.2.0/atem_control/apps.py +13 -0
  7. webatem-0.2.0/atem_control/control/__init__.py +9 -0
  8. webatem-0.2.0/atem_control/control/activity.py +194 -0
  9. webatem-0.2.0/atem_control/control/commands.py +469 -0
  10. webatem-0.2.0/atem_control/control/consumer.py +1245 -0
  11. webatem-0.2.0/atem_control/control/device_api.py +104 -0
  12. webatem-0.2.0/atem_control/control/logging.py +121 -0
  13. webatem-0.2.0/atem_control/control/views.py +222 -0
  14. webatem-0.2.0/atem_control/discovery.py +362 -0
  15. webatem-0.2.0/atem_control/hyperdeck/__init__.py +8 -0
  16. webatem-0.2.0/atem_control/hyperdeck/connection.py +146 -0
  17. webatem-0.2.0/atem_control/hyperdeck/views.py +197 -0
  18. webatem-0.2.0/atem_control/ip_upload_lock.py +32 -0
  19. webatem-0.2.0/atem_control/media_pool/__init__.py +36 -0
  20. webatem-0.2.0/atem_control/media_pool/broadcast.py +79 -0
  21. webatem-0.2.0/atem_control/media_pool/views.py +155 -0
  22. webatem-0.2.0/atem_control/media_pool/watcher.py +1348 -0
  23. webatem-0.2.0/atem_control/migrations/0001_initial.py +34 -0
  24. webatem-0.2.0/atem_control/migrations/0002_switcher.py +26 -0
  25. webatem-0.2.0/atem_control/migrations/0003_remove_atemcontrollog_atem_contro_user_id_3d6bb9_idx_and_more.py +29 -0
  26. webatem-0.2.0/atem_control/migrations/0004_delete_switcher.py +16 -0
  27. webatem-0.2.0/atem_control/migrations/__init__.py +0 -0
  28. webatem-0.2.0/atem_control/models.py +45 -0
  29. webatem-0.2.0/atem_control/netutil.py +36 -0
  30. webatem-0.2.0/atem_control/profile/__init__.py +5 -0
  31. webatem-0.2.0/atem_control/profile/dialog.py +521 -0
  32. webatem-0.2.0/atem_control/profile/export.py +406 -0
  33. webatem-0.2.0/atem_control/profile/views.py +444 -0
  34. webatem-0.2.0/atem_control/routing.py +11 -0
  35. webatem-0.2.0/atem_control/sightings.py +14 -0
  36. webatem-0.2.0/atem_control/static/css/atem_connect.css +34 -0
  37. webatem-0.2.0/atem_control/static/css/atem_control.css +872 -0
  38. webatem-0.2.0/atem_control/static/css/theme.css +620 -0
  39. webatem-0.2.0/atem_control/static/fonts/poppins/OFL.txt +93 -0
  40. webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Bold.ttf +0 -0
  41. webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Italic.ttf +0 -0
  42. webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Medium.ttf +0 -0
  43. webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-Regular.ttf +0 -0
  44. webatem-0.2.0/atem_control/static/fonts/poppins/Poppins-SemiBold.ttf +0 -0
  45. webatem-0.2.0/atem_control/static/icon.png +0 -0
  46. webatem-0.2.0/atem_control/static/js/atem_connect.js +403 -0
  47. webatem-0.2.0/atem_control/static/js/atem_control.js +4151 -0
  48. webatem-0.2.0/atem_control/static/js/atem_control_page.js +864 -0
  49. webatem-0.2.0/atem_control/static/js/atem_discovery.js +150 -0
  50. webatem-0.2.0/atem_control/static/js/atem_hyperdeck.js +405 -0
  51. webatem-0.2.0/atem_control/static/js/atem_profile.js +569 -0
  52. webatem-0.2.0/atem_control/static/js/atem_realtime_slider.js +248 -0
  53. webatem-0.2.0/atem_control/static/js/custom-scrollbar.js +228 -0
  54. webatem-0.2.0/atem_control/static/js/pp-select.js +156 -0
  55. webatem-0.2.0/atem_control/static/vendor/README.md +30 -0
  56. webatem-0.2.0/atem_control/static/vendor/alpine.min.js +5 -0
  57. webatem-0.2.0/atem_control/static/vendor/alpinejs-collapse-3.14.3.min.js +1 -0
  58. webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
  59. webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  60. webatem-0.2.0/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  61. webatem-0.2.0/atem_control/static/vendor/webatem.css +1 -0
  62. webatem-0.2.0/atem_control/storage.py +23 -0
  63. webatem-0.2.0/atem_control/tally.py +141 -0
  64. webatem-0.2.0/atem_control/templates/connect.html +109 -0
  65. webatem-0.2.0/atem_control/templates/control/_atem_info.html +91 -0
  66. webatem-0.2.0/atem_control/templates/control/_fade_to_black.html +38 -0
  67. webatem-0.2.0/atem_control/templates/control/_fairlight.html +1378 -0
  68. webatem-0.2.0/atem_control/templates/control/_header.html +150 -0
  69. webatem-0.2.0/atem_control/templates/control/_hyperdeck_panel.html +180 -0
  70. webatem-0.2.0/atem_control/templates/control/_macros.html +41 -0
  71. webatem-0.2.0/atem_control/templates/control/_media_pool.html +360 -0
  72. webatem-0.2.0/atem_control/templates/control/_profile_modal.html +313 -0
  73. webatem-0.2.0/atem_control/templates/control/_settings_dsk.html +410 -0
  74. webatem-0.2.0/atem_control/templates/control/_settings_transition.html +854 -0
  75. webatem-0.2.0/atem_control/templates/control/_settings_usk.html +1366 -0
  76. webatem-0.2.0/atem_control/templates/control/_slider.html +63 -0
  77. webatem-0.2.0/atem_control/templates/control/_transition_controls.html +36 -0
  78. webatem-0.2.0/atem_control/templates/control/_transition_style.html +19 -0
  79. webatem-0.2.0/atem_control/templates/control/_usk_fly.html +131 -0
  80. webatem-0.2.0/atem_control/templates/control/_usk_keyframe.html +161 -0
  81. webatem-0.2.0/atem_control/templates/control/_usk_mask.html +123 -0
  82. webatem-0.2.0/atem_control/templates/control/_usk_slider.html +8 -0
  83. webatem-0.2.0/atem_control/templates/control.html +808 -0
  84. webatem-0.2.0/atem_control/templatetags/__init__.py +0 -0
  85. webatem-0.2.0/atem_control/templatetags/static_v.py +54 -0
  86. webatem-0.2.0/atem_control/uploader.py +800 -0
  87. webatem-0.2.0/atem_control/urls.py +32 -0
  88. webatem-0.2.0/pyproject.toml +62 -0
  89. webatem-0.2.0/setup.cfg +4 -0
  90. webatem-0.2.0/webatem/__init__.py +0 -0
  91. webatem-0.2.0/webatem/__main__.py +4 -0
  92. webatem-0.2.0/webatem/asgi.py +28 -0
  93. webatem-0.2.0/webatem/context.py +6 -0
  94. webatem-0.2.0/webatem/launcher.py +309 -0
  95. webatem-0.2.0/webatem/settings.py +142 -0
  96. webatem-0.2.0/webatem/templates/base.html +208 -0
  97. webatem-0.2.0/webatem/urls.py +7 -0
  98. webatem-0.2.0/webatem/websocket.py +133 -0
  99. webatem-0.2.0/webatem.egg-info/PKG-INFO +335 -0
  100. webatem-0.2.0/webatem.egg-info/SOURCES.txt +102 -0
  101. webatem-0.2.0/webatem.egg-info/dependency_links.txt +1 -0
  102. webatem-0.2.0/webatem.egg-info/entry_points.txt +2 -0
  103. webatem-0.2.0/webatem.egg-info/requires.txt +14 -0
  104. 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
+ &nbsp;
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
+ &nbsp;
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. &nbsp;·&nbsp; Intel Mac? <a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-intel.dmg">Intel build</a> &nbsp;·&nbsp; 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.
@@ -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
+ &nbsp;
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
+ &nbsp;
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. &nbsp;·&nbsp; Intel Mac? <a href="https://github.com/lucas-romanenko/webatem/releases/latest/download/webatem-macos-intel.dmg">Intel build</a> &nbsp;·&nbsp; 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