bmdwire 1.5.1__tar.gz → 1.5.3__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.5.1/bmdwire.egg-info → bmdwire-1.5.3}/PKG-INFO +2 -2
  2. {bmdwire-1.5.1 → bmdwire-1.5.3}/README.md +1 -1
  3. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/docs/MEDIA_LOCK.md +7 -0
  4. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/docs/SURFACE.md +19 -1
  5. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/protocol.py +25 -8
  6. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/transport.py +33 -21
  7. {bmdwire-1.5.1 → bmdwire-1.5.3/bmdwire.egg-info}/PKG-INFO +2 -2
  8. {bmdwire-1.5.1 → bmdwire-1.5.3}/pyproject.toml +1 -1
  9. {bmdwire-1.5.1 → bmdwire-1.5.3}/LICENSE +0 -0
  10. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/LICENSE +0 -0
  11. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/LICENSE-gpl3.txt +0 -0
  12. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/NOTICE +0 -0
  13. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/__init__.py +0 -0
  14. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/_socketqueue.py +0 -0
  15. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/_transfer.py +0 -0
  16. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/atem.py +0 -0
  17. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/connection.py +0 -0
  18. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/docs/MACRO_FORMAT.md +0 -0
  19. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/docs/PROFILE_FORMAT.md +0 -0
  20. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/docs/PROFILE_SAVE_RESTORE.md +0 -0
  21. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/helpers.py +0 -0
  22. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/imaging.py +0 -0
  23. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/__init__.py +0 -0
  24. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/_helpers.py +0 -0
  25. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/_io.py +0 -0
  26. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/downstream_keyer.py +0 -0
  27. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/fairlight.py +0 -0
  28. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/flow.py +0 -0
  29. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/hyperdeck.py +0 -0
  30. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/media.py +0 -0
  31. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/switching.py +0 -0
  32. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/macrotransfer/upstream_keyer.py +0 -0
  33. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/mediaconvertmodule.c +0 -0
  34. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/__init__.py +0 -0
  35. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/_dsl.py +0 -0
  36. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/audio_legacy.py +0 -0
  37. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/camera_control.py +0 -0
  38. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/color_generator.py +0 -0
  39. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/downstream_keyer.py +0 -0
  40. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/fade_to_black.py +0 -0
  41. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/fairlight.py +0 -0
  42. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/file_transfer.py +0 -0
  43. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/hyperdeck.py +0 -0
  44. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/input_video.py +0 -0
  45. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/lock.py +0 -0
  46. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/macros.py +0 -0
  47. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/manual.py +0 -0
  48. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/media.py +0 -0
  49. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/meters.py +0 -0
  50. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/multiviewer.py +0 -0
  51. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/recording.py +0 -0
  52. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/streaming.py +0 -0
  53. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/supersource.py +0 -0
  54. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/switching.py +0 -0
  55. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/system_info.py +0 -0
  56. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/tally.py +0 -0
  57. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/transition.py +0 -0
  58. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/messages/upstream_keyer.py +0 -0
  59. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/pool.py +0 -0
  60. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/probe.py +0 -0
  61. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/__init__.py +0 -0
  62. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/_common.py +0 -0
  63. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/_enums.py +0 -0
  64. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/_xml.py +0 -0
  65. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/apply.py +0 -0
  66. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/options.py +0 -0
  67. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/profile/save.py +0 -0
  68. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/py.typed +0 -0
  69. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/ready.py +0 -0
  70. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/state.py +0 -0
  71. {bmdwire-1.5.1 → bmdwire-1.5.3}/atemwire/testing.py +0 -0
  72. {bmdwire-1.5.1 → bmdwire-1.5.3}/bmdwire.egg-info/SOURCES.txt +0 -0
  73. {bmdwire-1.5.1 → bmdwire-1.5.3}/bmdwire.egg-info/dependency_links.txt +0 -0
  74. {bmdwire-1.5.1 → bmdwire-1.5.3}/bmdwire.egg-info/requires.txt +0 -0
  75. {bmdwire-1.5.1 → bmdwire-1.5.3}/bmdwire.egg-info/top_level.txt +0 -0
  76. {bmdwire-1.5.1 → bmdwire-1.5.3}/hyperdeckwire/LICENSE +0 -0
  77. {bmdwire-1.5.1 → bmdwire-1.5.3}/hyperdeckwire/__init__.py +0 -0
  78. {bmdwire-1.5.1 → bmdwire-1.5.3}/hyperdeckwire/client.py +0 -0
  79. {bmdwire-1.5.1 → bmdwire-1.5.3}/hyperdeckwire/setup.py +0 -0
  80. {bmdwire-1.5.1 → bmdwire-1.5.3}/hyperdeckwire/upload.py +0 -0
  81. {bmdwire-1.5.1 → bmdwire-1.5.3}/setup.cfg +0 -0
  82. {bmdwire-1.5.1 → bmdwire-1.5.3}/setup.py +0 -0
  83. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/LICENSE +0 -0
  84. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/__init__.py +0 -0
  85. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/_preamble.py +0 -0
  86. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/_protocol.py +0 -0
  87. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/network.py +0 -0
  88. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/profile/__init__.py +0 -0
  89. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/profile/_ranges.py +0 -0
  90. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/profile/archive.py +0 -0
  91. {bmdwire-1.5.1 → bmdwire-1.5.3}/ultimattewire/profile/restore.py +0 -0
  92. {bmdwire-1.5.1 → bmdwire-1.5.3}/videohubwire/LICENSE +0 -0
  93. {bmdwire-1.5.1 → bmdwire-1.5.3}/videohubwire/__init__.py +0 -0
  94. {bmdwire-1.5.1 → bmdwire-1.5.3}/videohubwire/client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bmdwire
3
- Version: 1.5.1
3
+ Version: 1.5.3
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.5.1`) 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.5.3`) 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.5.1`) 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.5.3`) so a later release cannot change your install under you.
19
19
 
20
20
  ## atemwire stands on pyatem
21
21
 
@@ -227,6 +227,13 @@ two of them captured on the ASC machine.
227
227
  thumbnails while the other client downloaded; the download paused 38 s
228
228
  for ASC's 20 and went on. Alone, 20 uploads take 14.2 s, against
229
229
  12.7 s with 1.5.0 and about 12 s for ASC.
230
+ * That has to follow every release, work queued or not (`1b30449`).
231
+ WebATEM's media pool watcher queues one still at a time, the next
232
+ after decoding the last, so nothing is queued at the release; 1.5.1
233
+ yielded only when something was, and the watcher's next request went
234
+ out a moment after the release, beside ASC's. With the yield after
235
+ every release, ASC loaded all 20 thumbnails while a client downloaded
236
+ as the watcher does.
230
237
  * atemwire 1.4.0 waited for its own release's `LKST` echo before asking
231
238
  again, so it waited out a client handed the store too, by accident.
232
239
  bmdctl, which mirrors this code, saw that as unexplained pauses of 2 to
@@ -918,7 +918,7 @@ Items 1, 2 and 4 were fixed after 1.2.0, in the commits named in each; they stil
918
918
  10, the split-mono bullet of item 8, the float and `sourceId` bullets of item 9, items 15, 16,
919
919
  18, 19 and 20, and part of item 17 were fixed after 1.3.0 and still apply to it. Items 21 to 25
920
920
  were fixed after 1.4.0 and still apply to it. Item 26 was fixed after 1.5.0 and still applies to
921
- it.
921
+ it; item 27 was fixed after 1.5.1.
922
922
 
923
923
  1. **Profile video mode does not round-trip at fractional rates** (1.2.0). Save writes
924
924
  `get_label()` (`1080p59.94`); apply only knows `1080p5994`-style names, so it skips the mode as
@@ -1089,6 +1089,24 @@ it.
1089
1089
  while another client is seen holding the store, and with no announcement waits while anyone
1090
1090
  is seen at work, asking after `LOCK_QUIET` (10 s) of quiet. `UdpProtocol.wake_at` wakes the
1091
1091
  protocol for it.
1092
+ 27. **A transfer queued after the release doesn't wait its turn** (1.5.1). Item 26's fix starts
1093
+ the wait only after a release with more work queued. A caller that queues its transfers one at
1094
+ a time (WebATEM's media pool watcher, the next still after decoding the last) has nothing
1095
+ queued at the release, and its next request goes out at once, beside another client's,
1096
+ aborting it as before. *Fixed in `1b30449`:* every per-frame release starts the wait, after an
1097
+ FTDE 6 too, and so does any client's announced release.
1098
+ 28. **The UDP thread can wait on itself for good** (1.5.0). `thread_queue` is a `SocketQueue`:
1099
+ each put writes a byte to a socketpair (278 fit on Linux) that only the UDP thread reads.
1100
+ `drain_queue` puts a whole upload budget, up to 768 chunks, at once, and the UDP thread queued
1101
+ its own packets too, the ACK for every reliable packet received among them. A packet arriving
1102
+ mid-budget (another session's lock or transfer broadcast, say) left both threads waiting in
1103
+ `SocketQueue.put` for good: the upload stops, and a caller's timeout never runs if its pump is
1104
+ the protocol thread. *Fixed in `81955c1`:* what the UDP thread sends goes to the socket at once.
1105
+ 29. **A caller's Wakeup runs the trigger in every yield** (1.5.2). `_service_yields` ran on
1106
+ every `Wakeup`, a caller's included, and triggered each store with work queued before its
1107
+ turn was due: two INFO lines per `Wakeup`, 100 a second under WebATEM's pump. *Fixed in
1108
+ `81955c1`:* the trigger runs once the turn is due; before it, the protocol only wakes for the
1109
+ due time again.
1092
1110
 
1093
1111
  ## Counts
1094
1112
 
@@ -397,11 +397,11 @@ class AtemProtocol:
397
397
  # and abort their transfer (TEST ATEM 2, 2026-10-15).
398
398
  y = self._yielding.get(contents.store)
399
399
  now = time.monotonic()
400
- if y is None and self.transfer_queue.get(contents.store) and self._can_wake():
400
+ if y is None and self._can_wake():
401
401
  self._yielding[contents.store] = [now, now]
402
- self._wake_at(now + LOCK_COURTESY)
403
402
  elif y is not None and y[1] is None:
404
403
  y[1] = now
404
+ if contents.store in self._yielding and self.transfer_queue.get(contents.store):
405
405
  self._wake_at(now + LOCK_COURTESY)
406
406
  # The lock is free: whatever request we had outstanding is
407
407
  # either about to be granted (a queued PLCK — the switcher
@@ -955,11 +955,16 @@ class AtemProtocol:
955
955
  self.send_commands([LockCommand(store, False)])
956
956
  except Exception:
957
957
  self.log.exception('per-frame unlock send failed')
958
+ if self._can_wake():
959
+ # Every release, whether or not more work is queued now: a
960
+ # caller that queues its transfers one at a time (WebATEM's
961
+ # media pool watcher queues the next still after decoding the
962
+ # last) asks again a moment after the release, with nothing
963
+ # queued at it, and that request must wait its turn too. The
964
+ # trigger defers it and asks to be woken.
965
+ self._yielding[store] = [time.monotonic(), None]
958
966
  if not request_next:
959
967
  return # the store's release broadcast continues the queue
960
- if self.transfer_queue.get(store) and self._can_wake():
961
- # The trigger below defers the request and asks to be woken.
962
- self._yielding[store] = [time.monotonic(), None]
963
968
  self._transfer_trigger(store)
964
969
 
965
970
  def _can_wake(self):
@@ -986,11 +991,23 @@ class AtemProtocol:
986
991
  y[0] = now
987
992
 
988
993
  def _service_yields(self):
989
- """Ask for each store we left free whose yield is over."""
994
+ """Ask for each store we left free whose yield is over; forget a
995
+ yield that is over with nothing waiting on it. A Wakeup comes from
996
+ callers too (WebATEM's pump sends one every 20 ms): a yield not yet
997
+ over is only woken for again, its time perhaps moved by someone at
998
+ work, so the trigger runs once per turn, not once per tick."""
999
+ now = time.monotonic()
990
1000
  for store in list(self._yielding):
1001
+ due = self._yield_due(store)
991
1002
  if not self.transfer_queue.get(store):
992
- del self._yielding[store]
993
- elif not self.transfer_requested and not self.locks.get(store):
1003
+ if now >= due:
1004
+ del self._yielding[store]
1005
+ elif (self.transfer_requested or self.locks.get(store)
1006
+ or store in self._foreign_held):
1007
+ continue # their release asks for us
1008
+ elif now < due:
1009
+ self._wake_at(due)
1010
+ else:
994
1011
  self._transfer_trigger(store)
995
1012
 
996
1013
  def _transfer_trigger(self, store, retry=False):
@@ -496,27 +496,12 @@ class UdpProtocol(BaseProtocol):
496
496
  elif queue is self.thread_queue:
497
497
  item = queue.get()
498
498
  if item is None:
499
- # Defensive: tolerate a stray wake sentinel on the
500
- # send queue (no current producer).
499
+ # wake_at's sentinel: select() returned, so the
500
+ # next one takes the new timeout.
501
501
  continue
502
502
  try:
503
- self._send_packet_low(item)
503
+ self._send_on_udp_thread(item)
504
504
  except OSError as e:
505
- if e.errno in TRANSIENT_SEND_ERRNOS:
506
- # A momentary network hiccup (VPN blip, ICMP
507
- # unreachable burst, full socket buffer) must
508
- # not kill the transport thread: a dead thread
509
- # leaves a zombie connection whose worker
510
- # blocks forever while is_connected stays True
511
- # and send() silently drops (2026-07-06).
512
- # Only an ACK or a handshake packet gets here:
513
- # a reliable one keeps its sequence number in
514
- # _send_packet_low and is resent. ACKs go out
515
- # again with the next packet.
516
- self.log.error(
517
- f"Transient send error (packet dropped, "
518
- f"thread stays up): {e}")
519
- continue
520
505
  self.log.error(e)
521
506
  # Queue a None to signal the socket died
522
507
  self.thread_recv_queue.put(None)
@@ -525,8 +510,34 @@ class UdpProtocol(BaseProtocol):
525
510
  self.thread_recv_queue.put(None)
526
511
  raise RuntimeError("Unexpected result from select()")
527
512
 
513
+ def _send_on_udp_thread(self, packet):
514
+ try:
515
+ self._send_packet_low(packet)
516
+ except OSError as e:
517
+ if e.errno not in TRANSIENT_SEND_ERRNOS:
518
+ raise
519
+ # A momentary network hiccup (VPN blip, ICMP unreachable
520
+ # burst, full socket buffer) must not kill the transport
521
+ # thread: a dead thread leaves a zombie connection whose
522
+ # worker blocks forever while is_connected stays True and
523
+ # send() silently drops (2026-07-06). Only an ACK or a
524
+ # handshake packet gets here: a reliable one keeps its
525
+ # sequence number in _send_packet_low and is resent. ACKs go
526
+ # out again with the next packet.
527
+ self.log.error(
528
+ f"Transient send error (packet dropped, thread stays up): {e}")
529
+
528
530
  def _send_packet(self, packet):
529
- self.thread_queue.put(packet)
531
+ if threading.current_thread() is self.thread:
532
+ # The UDP thread is thread_queue's only reader, and a put waits
533
+ # while the socketpair under it is full: a few hundred entries,
534
+ # fewer than an upload budget drain_queue puts at once. Its own
535
+ # packets (the ACK for each reliable packet received, a SYN
536
+ # after a timeout) queued behind a budget had it wait on itself
537
+ # for good, and the upload with it (2026-10-15). They go now.
538
+ self._send_on_udp_thread(packet)
539
+ else:
540
+ self.thread_queue.put(packet)
530
541
  self.packet_success += 1
531
542
 
532
543
  def close_session(self):
@@ -853,8 +864,9 @@ class UdpProtocol(BaseProtocol):
853
864
  protocol's timed work, which its packets alone can't drive (the
854
865
  switcher's pings never reach it). The earliest of several wins."""
855
866
  with self._wake_lock:
856
- if self._wake_due is None or when < self._wake_due:
857
- self._wake_due = when
867
+ if self._wake_due is not None and self._wake_due <= when:
868
+ return # select() wakes sooner anyway
869
+ self._wake_due = when
858
870
  self.thread_queue.put(None) # select() takes the new timeout
859
871
 
860
872
  def _fire_wake(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bmdwire
3
- Version: 1.5.1
3
+ Version: 1.5.3
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.5.1`) 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.5.3`) so a later release cannot change your install under you.
53
53
 
54
54
  ## atemwire stands on pyatem
55
55
 
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
13
13
 
14
14
  [project]
15
15
  name = "bmdwire"
16
- version = "1.5.1"
16
+ version = "1.5.3"
17
17
  description = "Python libraries for Blackmagic Design broadcast devices: ATEM switchers, HyperDeck recorders, Ultimatte keyers and Videohub routers"
18
18
  readme = "README.md"
19
19
  # atemwire is a fork of pyatem and stays LGPL-3.0-only; the other three were
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes