webatem 2.2.1__tar.gz → 2.3.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-2.2.1 → webatem-2.3.0}/PKG-INFO +1 -1
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/consumer.py +27 -2
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_control.js +23 -1
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/webatem.css +1 -1
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control.html +27 -3
- {webatem-2.2.1 → webatem-2.3.0}/pyproject.toml +1 -1
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/PKG-INFO +1 -1
- {webatem-2.2.1 → webatem-2.3.0}/LICENSE +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/README.md +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/activity.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/apps.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/activity.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/commands.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/device_api.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/logging.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/control/views.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/discovery.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/hooks.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/hyperdeck/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/hyperdeck/connection.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/hyperdeck/views.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/ip_upload_lock.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/media_pool/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/media_pool/broadcast.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/media_pool/still_cache.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/media_pool/views.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/media_pool/watcher.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/migrations/0001_initial.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/migrations/0002_legacy_log.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/migrations/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/models.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/netutil.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/profile/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/profile/dialog.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/profile/export.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/profile/views.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/routing.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/sightings.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/apple-touch-icon.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/brand.css +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/favicon-32.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/favicon.ico +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-400-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-400-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-500-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-500-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-600-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-600-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-800-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/Archivo-800-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-400-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-500-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-600-latin-ext.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/IBMPlexMono-600-latin.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/OFL-Archivo.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/fonts/OFL-IBMPlexMono.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/icon-192.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/icon-512.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/icon-maskable-512.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/manifest.webmanifest +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/mark.svg +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/tray/linux-24.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/tray/mac-22.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/tray/mac-44.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/tray/win-16.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/brand/tray/win-32.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/css/atem_connect.css +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/css/atem_control.css +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/icon.png +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_connect.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_control_page.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_discovery.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_hyperdeck.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_profile.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/atem_realtime_slider.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/custom-scrollbar.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/recent_label.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/js/wa-select.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/README.md +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/alpine.min.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/alpinejs-collapse-3.14.3.min.js +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/bootstrap-icons/bootstrap-icons.min.css +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/storage.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/tally.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/connect.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_atem_info.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_fade_to_black.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_fairlight.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_header.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_hyperdeck_panel.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_macros.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_media_pool.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_profile_modal.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_settings_dsk.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_settings_transition.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_settings_usk.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_slider.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_transition_controls.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_transition_style.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_usk_fly.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_usk_keyframe.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_usk_mask.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templates/control/_usk_slider.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templatetags/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/templatetags/static_v.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/uploader.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/atem_control/urls.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/setup.cfg +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/__init__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/__main__.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/applog.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/asgi.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/context.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/launcher.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/server.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/settings.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/templates/base.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/templates/brand/_mark.svg +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/templates/launcher.html +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/uninstall.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/updates.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/urls.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/views.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem/websocket.py +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/SOURCES.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/dependency_links.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/entry_points.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/requires.txt +0 -0
- {webatem-2.2.1 → webatem-2.3.0}/webatem.egg-info/top_level.txt +0 -0
|
@@ -199,6 +199,18 @@ class ATEMConsumer(ATEMConnectionLoggingMixin, ATEMStateMixin, AsyncWebsocketCon
|
|
|
199
199
|
def is_connected(self) -> bool:
|
|
200
200
|
return bool(self.connection and self.connection.is_connected)
|
|
201
201
|
|
|
202
|
+
@property
|
|
203
|
+
def is_ready(self) -> bool:
|
|
204
|
+
"""The switcher's state is current. Through a transport re-handshake
|
|
205
|
+
``is_connected`` stays True while this is False: what is sent then
|
|
206
|
+
goes out under the session being replaced, and is lost or lands
|
|
207
|
+
late (#38, #39). bmdwire before 1.3 has no ``is_ready``; there it
|
|
208
|
+
is ``is_connected``. The poll loop stays on ``is_connected``: it
|
|
209
|
+
must keep polling through the gap to see the switcher go and
|
|
210
|
+
come back."""
|
|
211
|
+
conn = self.connection
|
|
212
|
+
return bool(conn and getattr(conn, 'is_ready', conn.is_connected))
|
|
213
|
+
|
|
202
214
|
# =========================================================================
|
|
203
215
|
# WebSocket Lifecycle
|
|
204
216
|
# =========================================================================
|
|
@@ -336,8 +348,12 @@ class ATEMConsumer(ATEMConnectionLoggingMixin, ATEMStateMixin, AsyncWebsocketCon
|
|
|
336
348
|
drags (20 Hz × 30 KB = ~600 KB/s purely from per-command pushes,
|
|
337
349
|
on top of the polling loop). That saturates the WebSocket write
|
|
338
350
|
buffer and queues audio meter / other small messages behind it.
|
|
351
|
+
|
|
352
|
+
Refused while the switcher is away (``is_ready``), not only once
|
|
353
|
+
the worker is gone: a command sent into a re-handshake is lost,
|
|
354
|
+
or lands after the operator has moved on (#38, #39).
|
|
339
355
|
"""
|
|
340
|
-
if not self.
|
|
356
|
+
if not self.is_ready:
|
|
341
357
|
await self.send_json({'type': 'error', 'message': 'ATEM not connected'})
|
|
342
358
|
return
|
|
343
359
|
|
|
@@ -722,9 +738,16 @@ class ATEMConsumer(ATEMConnectionLoggingMixin, ATEMStateMixin, AsyncWebsocketCon
|
|
|
722
738
|
if was_atem_reachable and not atem_reachable:
|
|
723
739
|
logger.info("ATEM dropped mid-session; notifying frontend")
|
|
724
740
|
try:
|
|
741
|
+
# ``reconnecting``: bmdwire re-handshakes in
|
|
742
|
+
# place until the switcher answers, and the
|
|
743
|
+
# branch below says so. The page stays and
|
|
744
|
+
# waits (#38); the other connected=False
|
|
745
|
+
# notices (a failed connect, a disconnect)
|
|
746
|
+
# send it to the connect page.
|
|
725
747
|
await self.send_json({
|
|
726
748
|
'type': 'connection_status',
|
|
727
749
|
'connected': False,
|
|
750
|
+
'reconnecting': True,
|
|
728
751
|
'message': 'ATEM connection lost',
|
|
729
752
|
})
|
|
730
753
|
except Exception as e: # noqa: BLE001
|
|
@@ -1341,7 +1364,9 @@ class ATEMConsumer(ATEMConnectionLoggingMixin, ATEMStateMixin, AsyncWebsocketCon
|
|
|
1341
1364
|
if not isinstance(slot, int) or slot < 0 or slot >= 32:
|
|
1342
1365
|
await self.send_json({'type': 'error', 'message': f'Invalid slot: {slot}'})
|
|
1343
1366
|
return
|
|
1344
|
-
|
|
1367
|
+
# is_ready, as for every command (#39): bmdwire's clear_still
|
|
1368
|
+
# refuses a re-handshake too (bmdwire#8), but as a failed delete.
|
|
1369
|
+
if not self.is_ready:
|
|
1345
1370
|
await self.send_json({'type': 'error', 'message': 'ATEM not connected'})
|
|
1346
1371
|
return
|
|
1347
1372
|
try:
|
|
@@ -885,6 +885,14 @@
|
|
|
885
885
|
window.chromaSampleBox = chromaSampleBox;
|
|
886
886
|
|
|
887
887
|
// ../../atem_control/js/runtime.js
|
|
888
|
+
var SESSION_VERBS = /* @__PURE__ */ new Set([
|
|
889
|
+
"connect",
|
|
890
|
+
"disconnect",
|
|
891
|
+
"refresh",
|
|
892
|
+
"subscribe_audio_meters",
|
|
893
|
+
"unsubscribe_audio_meters",
|
|
894
|
+
"media_pool_refresh"
|
|
895
|
+
]);
|
|
888
896
|
window.ATEMControl = {
|
|
889
897
|
ws: null,
|
|
890
898
|
// ---- Declared-state-keys guard ------------------------------------
|
|
@@ -1088,7 +1096,9 @@
|
|
|
1088
1096
|
// global verbs ignore it. Explicit params.me wins over the stamp.
|
|
1089
1097
|
cmd(command, params = {}) {
|
|
1090
1098
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
1091
|
-
const
|
|
1099
|
+
const store = window.Alpine?.store?.("atem");
|
|
1100
|
+
if (store?.switcherAway && !SESSION_VERBS.has(command)) return;
|
|
1101
|
+
const me = store?.activeMe ?? 0;
|
|
1092
1102
|
this.ws.send(JSON.stringify({ command, me, ...params }));
|
|
1093
1103
|
},
|
|
1094
1104
|
// Audio per-strip command helper. Bundles ``source`` and ``channel``
|
|
@@ -1431,9 +1441,15 @@
|
|
|
1431
1441
|
const alpineInstance = Alpine.store("atem");
|
|
1432
1442
|
switch (data.type) {
|
|
1433
1443
|
case "connection_status":
|
|
1444
|
+
if (!data.connected && data.reconnecting && alpineInstance.stateReady) {
|
|
1445
|
+
alpineInstance.switcherAway = true;
|
|
1446
|
+
alpineInstance.statusText = data.message;
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1434
1449
|
alpineInstance.connected = data.connected;
|
|
1435
1450
|
alpineInstance.statusText = data.message;
|
|
1436
1451
|
if (data.connected) {
|
|
1452
|
+
alpineInstance.switcherAway = false;
|
|
1437
1453
|
alpineInstance.armAudioMixerWait();
|
|
1438
1454
|
this.isReconnecting = false;
|
|
1439
1455
|
this.connectingIP = null;
|
|
@@ -2440,6 +2456,12 @@
|
|
|
2440
2456
|
Alpine.store("atem", {
|
|
2441
2457
|
// Connection state
|
|
2442
2458
|
connected: false,
|
|
2459
|
+
// True from a mid-session "connection lost" (``reconnecting``) to
|
|
2460
|
+
// "restored": the switcher went quiet and bmdwire is
|
|
2461
|
+
// re-handshaking. ``connected`` stays true, so the surface keeps
|
|
2462
|
+
// the last state on screen; it goes inert under a banner, and
|
|
2463
|
+
// cmd() sends the switcher nothing (#38).
|
|
2464
|
+
switcherAway: false,
|
|
2443
2465
|
// ``stateReady`` flips true on the FIRST atem_state arrival
|
|
2444
2466
|
// (see updateState). Templates that don't want to render against
|
|
2445
2467
|
// the initial default values gate their x-show on
|