bmdwire 1.2.0__tar.gz → 1.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.
Files changed (94) hide show
  1. {bmdwire-1.2.0/bmdwire.egg-info → bmdwire-1.3.0}/PKG-INFO +2 -2
  2. {bmdwire-1.2.0 → bmdwire-1.3.0}/README.md +1 -1
  3. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/atem.py +4 -1
  4. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/connection.py +36 -1
  5. bmdwire-1.3.0/atemwire/docs/SURFACE.md +995 -0
  6. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/_helpers.py +13 -0
  7. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/switching.py +4 -17
  8. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/input_video.py +19 -0
  9. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/upstream_keyer.py +11 -1
  10. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/probe.py +9 -2
  11. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/apply.py +51 -4
  12. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/save.py +11 -4
  13. {bmdwire-1.2.0 → bmdwire-1.3.0/bmdwire.egg-info}/PKG-INFO +2 -2
  14. {bmdwire-1.2.0 → bmdwire-1.3.0}/bmdwire.egg-info/SOURCES.txt +1 -0
  15. {bmdwire-1.2.0 → bmdwire-1.3.0}/pyproject.toml +1 -1
  16. {bmdwire-1.2.0 → bmdwire-1.3.0}/LICENSE +0 -0
  17. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/LICENSE +0 -0
  18. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/LICENSE-gpl3.txt +0 -0
  19. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/NOTICE +0 -0
  20. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/__init__.py +0 -0
  21. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/_socketqueue.py +0 -0
  22. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/_transfer.py +0 -0
  23. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/docs/MACRO_FORMAT.md +0 -0
  24. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/docs/MEDIA_LOCK.md +0 -0
  25. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/docs/PROFILE_FORMAT.md +0 -0
  26. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/docs/PROFILE_SAVE_RESTORE.md +0 -0
  27. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/helpers.py +0 -0
  28. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/imaging.py +0 -0
  29. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/__init__.py +0 -0
  30. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/_io.py +0 -0
  31. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/downstream_keyer.py +0 -0
  32. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/fairlight.py +0 -0
  33. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/flow.py +0 -0
  34. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/hyperdeck.py +0 -0
  35. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/media.py +0 -0
  36. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/macrotransfer/upstream_keyer.py +0 -0
  37. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/mediaconvertmodule.c +0 -0
  38. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/__init__.py +0 -0
  39. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/_dsl.py +0 -0
  40. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/audio_legacy.py +0 -0
  41. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/camera_control.py +0 -0
  42. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/color_generator.py +0 -0
  43. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/downstream_keyer.py +0 -0
  44. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/fade_to_black.py +0 -0
  45. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/fairlight.py +0 -0
  46. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/file_transfer.py +0 -0
  47. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/hyperdeck.py +0 -0
  48. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/lock.py +0 -0
  49. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/macros.py +0 -0
  50. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/manual.py +0 -0
  51. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/media.py +0 -0
  52. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/meters.py +0 -0
  53. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/multiviewer.py +0 -0
  54. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/recording.py +0 -0
  55. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/streaming.py +0 -0
  56. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/supersource.py +0 -0
  57. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/switching.py +0 -0
  58. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/system_info.py +0 -0
  59. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/tally.py +0 -0
  60. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/messages/transition.py +0 -0
  61. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/pool.py +0 -0
  62. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/__init__.py +0 -0
  63. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/_common.py +0 -0
  64. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/_enums.py +0 -0
  65. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/_xml.py +0 -0
  66. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/profile/options.py +0 -0
  67. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/protocol.py +0 -0
  68. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/py.typed +0 -0
  69. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/ready.py +0 -0
  70. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/state.py +0 -0
  71. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/testing.py +0 -0
  72. {bmdwire-1.2.0 → bmdwire-1.3.0}/atemwire/transport.py +0 -0
  73. {bmdwire-1.2.0 → bmdwire-1.3.0}/bmdwire.egg-info/dependency_links.txt +0 -0
  74. {bmdwire-1.2.0 → bmdwire-1.3.0}/bmdwire.egg-info/requires.txt +0 -0
  75. {bmdwire-1.2.0 → bmdwire-1.3.0}/bmdwire.egg-info/top_level.txt +0 -0
  76. {bmdwire-1.2.0 → bmdwire-1.3.0}/hyperdeckwire/LICENSE +0 -0
  77. {bmdwire-1.2.0 → bmdwire-1.3.0}/hyperdeckwire/__init__.py +0 -0
  78. {bmdwire-1.2.0 → bmdwire-1.3.0}/hyperdeckwire/client.py +0 -0
  79. {bmdwire-1.2.0 → bmdwire-1.3.0}/hyperdeckwire/setup.py +0 -0
  80. {bmdwire-1.2.0 → bmdwire-1.3.0}/hyperdeckwire/upload.py +0 -0
  81. {bmdwire-1.2.0 → bmdwire-1.3.0}/setup.cfg +0 -0
  82. {bmdwire-1.2.0 → bmdwire-1.3.0}/setup.py +0 -0
  83. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/LICENSE +0 -0
  84. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/__init__.py +0 -0
  85. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/_preamble.py +0 -0
  86. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/_protocol.py +0 -0
  87. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/network.py +0 -0
  88. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/profile/__init__.py +0 -0
  89. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/profile/_ranges.py +0 -0
  90. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/profile/archive.py +0 -0
  91. {bmdwire-1.2.0 → bmdwire-1.3.0}/ultimattewire/profile/restore.py +0 -0
  92. {bmdwire-1.2.0 → bmdwire-1.3.0}/videohubwire/LICENSE +0 -0
  93. {bmdwire-1.2.0 → bmdwire-1.3.0}/videohubwire/__init__.py +0 -0
  94. {bmdwire-1.2.0 → bmdwire-1.3.0}/videohubwire/client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bmdwire
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Python libraries for Blackmagic Design broadcast devices: ATEM switchers, HyperDeck recorders, Ultimatte keyers and Videohub routers
5
5
  Author: Lucas Romanenko, Martijn Braam and the OpenAtem contributors
6
6
  License-Expression: LGPL-3.0-only AND MIT
@@ -49,7 +49,7 @@ pip install bmdwire
49
49
  | **`ultimattewire`** | Ultimatte 12 keyers: archive and restore over the native TCP protocol, Smart Remote 4 compatible zips | MIT | [ultimattewire/README.md](ultimattewire/README.md) |
50
50
  | **`videohubwire`** | Videohub routers: state snapshot, crosspoint routing, labels over TCP 9990 | MIT | [videohubwire/README.md](videohubwire/README.md) |
51
51
 
52
- All four are pure standard library except atemwire, whose small C extension ships prebuilt for Linux, macOS and Windows on CPython 3.10 to 3.14, so installing needs no compiler. Pin the version in a requirements file (`bmdwire==1.2.0`) so a later release cannot change your install under you.
52
+ All four are pure standard library except atemwire, whose small C extension ships prebuilt for Linux, macOS and Windows on CPython 3.10 to 3.14, so installing needs no compiler. Pin the version in a requirements file (`bmdwire==1.3.0`) so a later release cannot change your install under you.
53
53
 
54
54
  ## atemwire stands on pyatem
55
55
 
@@ -15,7 +15,7 @@ pip install bmdwire
15
15
  | **`ultimattewire`** | Ultimatte 12 keyers: archive and restore over the native TCP protocol, Smart Remote 4 compatible zips | MIT | [ultimattewire/README.md](ultimattewire/README.md) |
16
16
  | **`videohubwire`** | Videohub routers: state snapshot, crosspoint routing, labels over TCP 9990 | MIT | [videohubwire/README.md](videohubwire/README.md) |
17
17
 
18
- All four are pure standard library except atemwire, whose small C extension ships prebuilt for Linux, macOS and Windows on CPython 3.10 to 3.14, so installing needs no compiler. Pin the version in a requirements file (`bmdwire==1.2.0`) so a later release cannot change your install under you.
18
+ All four are pure standard library except atemwire, whose small C extension ships prebuilt for Linux, macOS and Windows on CPython 3.10 to 3.14, so installing needs no compiler. Pin the version in a requirements file (`bmdwire==1.3.0`) so a later release cannot change your install under you.
19
19
 
20
20
  ## atemwire stands on pyatem
21
21
 
@@ -221,8 +221,11 @@ class ATEM:
221
221
 
222
222
  @property
223
223
  def connected(self) -> bool:
224
+ """True while the session is up AND its state is current — False
225
+ during a transport re-handshake, when the state is empty (see
226
+ ``ATEMConnection.is_ready``)."""
224
227
  conn = self._conn
225
- return bool(conn and conn.is_connected)
228
+ return bool(conn and conn.is_ready)
226
229
 
227
230
  @property
228
231
  def video_mode(self) -> str:
@@ -131,6 +131,12 @@ class ATEMConnection:
131
131
  # immediately reflects the post-disconnect state.
132
132
  self._handshake_done: bool = False
133
133
 
134
+ # ``_session_ready`` follows the protocol's 'connected' /
135
+ # 'disconnected' events: False from the moment a transport
136
+ # re-handshake empties mixerstate until the new state dump is
137
+ # complete. Read through ``is_ready``.
138
+ self._session_ready: bool = False
139
+
134
140
  self._lifecycle_lock = threading.Lock() # serializes connect/disconnect calls
135
141
  self._transfer_serial_lock = threading.Lock() # one native transfer at a time
136
142
 
@@ -168,6 +174,19 @@ class ATEMConnection:
168
174
  worker = self._worker
169
175
  return worker is not None and worker.is_alive()
170
176
 
177
+ @property
178
+ def is_ready(self) -> bool:
179
+ """True iff ``is_connected`` AND the switcher's state is current.
180
+
181
+ After ~5 s without packets the transport re-handshakes on its own:
182
+ the protocol raises 'disconnected' and empties ``mixerstate``, the
183
+ same worker carries on, and 'connected' fires again once the new
184
+ state dump is complete. ``is_connected`` stays True through that
185
+ gap (the worker is alive and recovering, so the pool must not evict
186
+ it); ``is_ready`` is False for its whole length.
187
+ """
188
+ return self._session_ready and self.is_connected
189
+
171
190
  @property
172
191
  def mixerstate(self) -> dict:
173
192
  """Live state dict from atemwire. Safe to read from any thread — CPython
@@ -405,6 +424,7 @@ class ATEMConnection:
405
424
  self._ready_event.clear()
406
425
  self._connect_error = None
407
426
  self._handshake_done = False
427
+ self._session_ready = False
408
428
  self._died_signalled = False
409
429
  self.ip_address = ip_address
410
430
 
@@ -478,12 +498,27 @@ class ATEMConnection:
478
498
  # bare (non-indexed) field so the event name is 'change:macro-play-status'.
479
499
  def _on_macro_play(contents):
480
500
  running = bool(getattr(contents, 'running', False))
481
- idx = int(getattr(contents, 'index', 0xFFFF) or 0xFFFF)
501
+ # Slot 0 is a real macro; only 0xFFFF (or no index) is idle.
502
+ idx = getattr(contents, 'index', None)
503
+ idx = 0xFFFF if idx is None else int(idx)
482
504
  if running and idx != 0xFFFF and idx >= 0:
483
505
  self.last_run_macro_index = idx
484
506
 
485
507
  self._protocol.on('change:macro-play-status', _on_macro_play)
486
508
 
509
+ # Session readiness (``is_ready``): 'disconnected' is raised when
510
+ # the transport gives up on the session and mixerstate is
511
+ # emptied; 'connected' when a state dump is complete, at the
512
+ # first connect and again after every re-handshake.
513
+ def _on_connected():
514
+ self._session_ready = True
515
+
516
+ def _on_disconnected():
517
+ self._session_ready = False
518
+
519
+ self._protocol.on('connected', _on_connected)
520
+ self._protocol.on('disconnected', _on_disconnected)
521
+
487
522
  try:
488
523
  self._protocol.connect()
489
524
  except Exception as e: