bmdwire 1.5.2__tar.gz → 1.5.4__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.
- {bmdwire-1.5.2/bmdwire.egg-info → bmdwire-1.5.4}/PKG-INFO +2 -2
- {bmdwire-1.5.2 → bmdwire-1.5.4}/README.md +1 -1
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/docs/SURFACE.md +18 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/protocol.py +13 -3
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/transport.py +40 -22
- {bmdwire-1.5.2 → bmdwire-1.5.4/bmdwire.egg-info}/PKG-INFO +2 -2
- {bmdwire-1.5.2 → bmdwire-1.5.4}/pyproject.toml +1 -1
- {bmdwire-1.5.2 → bmdwire-1.5.4}/LICENSE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/LICENSE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/LICENSE-gpl3.txt +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/NOTICE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/_socketqueue.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/_transfer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/atem.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/connection.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/docs/MACRO_FORMAT.md +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/docs/MEDIA_LOCK.md +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/docs/PROFILE_FORMAT.md +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/docs/PROFILE_SAVE_RESTORE.md +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/helpers.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/imaging.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/_helpers.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/_io.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/downstream_keyer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/fairlight.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/flow.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/hyperdeck.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/media.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/switching.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/macrotransfer/upstream_keyer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/mediaconvertmodule.c +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/_dsl.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/audio_legacy.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/camera_control.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/color_generator.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/downstream_keyer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/fade_to_black.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/fairlight.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/file_transfer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/hyperdeck.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/input_video.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/lock.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/macros.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/manual.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/media.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/meters.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/multiviewer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/recording.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/streaming.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/supersource.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/switching.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/system_info.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/tally.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/transition.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/messages/upstream_keyer.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/pool.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/probe.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/_common.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/_enums.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/_xml.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/apply.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/options.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/profile/save.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/py.typed +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/ready.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/state.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/atemwire/testing.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/bmdwire.egg-info/SOURCES.txt +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/bmdwire.egg-info/dependency_links.txt +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/bmdwire.egg-info/requires.txt +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/bmdwire.egg-info/top_level.txt +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/hyperdeckwire/LICENSE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/hyperdeckwire/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/hyperdeckwire/client.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/hyperdeckwire/setup.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/hyperdeckwire/upload.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/setup.cfg +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/setup.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/LICENSE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/_preamble.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/_protocol.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/network.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/profile/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/profile/_ranges.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/profile/archive.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/ultimattewire/profile/restore.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/videohubwire/LICENSE +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/videohubwire/__init__.py +0 -0
- {bmdwire-1.5.2 → bmdwire-1.5.4}/videohubwire/client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bmdwire
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
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.
|
|
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.4`) 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.
|
|
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.4`) so a later release cannot change your install under you.
|
|
19
19
|
|
|
20
20
|
## atemwire stands on pyatem
|
|
21
21
|
|
|
@@ -1095,6 +1095,24 @@ it; item 27 was fixed after 1.5.1.
|
|
|
1095
1095
|
queued at the release, and its next request goes out at once, beside another client's,
|
|
1096
1096
|
aborting it as before. *Fixed in `1b30449`:* every per-frame release starts the wait, after an
|
|
1097
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.
|
|
1110
|
+
30. **A receive buffer size the system refuses fails the connect** (1.5.3). `UdpProtocol` asked
|
|
1111
|
+
for a 16 MB `SO_RCVBUF` with a bare `setsockopt`. Linux caps the request silently and Windows
|
|
1112
|
+
takes it, but macOS 12 refuses anything above `kern.ipc.maxsockbuf` with errno 55
|
|
1113
|
+
(`ENOBUFS`): the constructor raised, and no switcher could be connected from that system.
|
|
1114
|
+
*Fixed in `c08f830`:* the request steps down 16, 8, 4, 1 MB and keeps the first size taken,
|
|
1115
|
+
or the system's default if it takes none.
|
|
1098
1116
|
|
|
1099
1117
|
## Counts
|
|
1100
1118
|
|
|
@@ -992,12 +992,22 @@ class AtemProtocol:
|
|
|
992
992
|
|
|
993
993
|
def _service_yields(self):
|
|
994
994
|
"""Ask for each store we left free whose yield is over; forget a
|
|
995
|
-
yield that is over with nothing waiting on it.
|
|
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()
|
|
996
1000
|
for store in list(self._yielding):
|
|
1001
|
+
due = self._yield_due(store)
|
|
997
1002
|
if not self.transfer_queue.get(store):
|
|
998
|
-
if
|
|
1003
|
+
if now >= due:
|
|
999
1004
|
del self._yielding[store]
|
|
1000
|
-
elif
|
|
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:
|
|
1001
1011
|
self._transfer_trigger(store)
|
|
1002
1012
|
|
|
1003
1013
|
def _transfer_trigger(self, store, retry=False):
|
|
@@ -312,7 +312,13 @@ class UdpProtocol(BaseProtocol):
|
|
|
312
312
|
|
|
313
313
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
314
314
|
self.sock.settimeout(self.timeout)
|
|
315
|
-
|
|
315
|
+
# macOS refuses more than kern.ipc.maxsockbuf with errno 55 (ENOBUFS) instead of capping.
|
|
316
|
+
for megabytes in (16, 8, 4, 1):
|
|
317
|
+
try:
|
|
318
|
+
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 1024 * megabytes)
|
|
319
|
+
break
|
|
320
|
+
except OSError:
|
|
321
|
+
pass
|
|
316
322
|
try:
|
|
317
323
|
# Room for an upload budget sent back to back (768 chunks of
|
|
318
324
|
# ~1.4 KB); the system may cap it lower, which is fine.
|
|
@@ -496,27 +502,12 @@ class UdpProtocol(BaseProtocol):
|
|
|
496
502
|
elif queue is self.thread_queue:
|
|
497
503
|
item = queue.get()
|
|
498
504
|
if item is None:
|
|
499
|
-
#
|
|
500
|
-
#
|
|
505
|
+
# wake_at's sentinel: select() returned, so the
|
|
506
|
+
# next one takes the new timeout.
|
|
501
507
|
continue
|
|
502
508
|
try:
|
|
503
|
-
self.
|
|
509
|
+
self._send_on_udp_thread(item)
|
|
504
510
|
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
511
|
self.log.error(e)
|
|
521
512
|
# Queue a None to signal the socket died
|
|
522
513
|
self.thread_recv_queue.put(None)
|
|
@@ -525,8 +516,34 @@ class UdpProtocol(BaseProtocol):
|
|
|
525
516
|
self.thread_recv_queue.put(None)
|
|
526
517
|
raise RuntimeError("Unexpected result from select()")
|
|
527
518
|
|
|
519
|
+
def _send_on_udp_thread(self, packet):
|
|
520
|
+
try:
|
|
521
|
+
self._send_packet_low(packet)
|
|
522
|
+
except OSError as e:
|
|
523
|
+
if e.errno not in TRANSIENT_SEND_ERRNOS:
|
|
524
|
+
raise
|
|
525
|
+
# A momentary network hiccup (VPN blip, ICMP unreachable
|
|
526
|
+
# burst, full socket buffer) must not kill the transport
|
|
527
|
+
# thread: a dead thread leaves a zombie connection whose
|
|
528
|
+
# worker blocks forever while is_connected stays True and
|
|
529
|
+
# send() silently drops (2026-07-06). Only an ACK or a
|
|
530
|
+
# handshake packet gets here: a reliable one keeps its
|
|
531
|
+
# sequence number in _send_packet_low and is resent. ACKs go
|
|
532
|
+
# out again with the next packet.
|
|
533
|
+
self.log.error(
|
|
534
|
+
f"Transient send error (packet dropped, thread stays up): {e}")
|
|
535
|
+
|
|
528
536
|
def _send_packet(self, packet):
|
|
529
|
-
|
|
537
|
+
if threading.current_thread() is self.thread:
|
|
538
|
+
# The UDP thread is thread_queue's only reader, and a put waits
|
|
539
|
+
# while the socketpair under it is full: a few hundred entries,
|
|
540
|
+
# fewer than an upload budget drain_queue puts at once. Its own
|
|
541
|
+
# packets (the ACK for each reliable packet received, a SYN
|
|
542
|
+
# after a timeout) queued behind a budget had it wait on itself
|
|
543
|
+
# for good, and the upload with it (2026-10-15). They go now.
|
|
544
|
+
self._send_on_udp_thread(packet)
|
|
545
|
+
else:
|
|
546
|
+
self.thread_queue.put(packet)
|
|
530
547
|
self.packet_success += 1
|
|
531
548
|
|
|
532
549
|
def close_session(self):
|
|
@@ -853,8 +870,9 @@ class UdpProtocol(BaseProtocol):
|
|
|
853
870
|
protocol's timed work, which its packets alone can't drive (the
|
|
854
871
|
switcher's pings never reach it). The earliest of several wins."""
|
|
855
872
|
with self._wake_lock:
|
|
856
|
-
if self._wake_due is None
|
|
857
|
-
|
|
873
|
+
if self._wake_due is not None and self._wake_due <= when:
|
|
874
|
+
return # select() wakes sooner anyway
|
|
875
|
+
self._wake_due = when
|
|
858
876
|
self.thread_queue.put(None) # select() takes the new timeout
|
|
859
877
|
|
|
860
878
|
def _fire_wake(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bmdwire
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
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.
|
|
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.4`) 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.
|
|
16
|
+
version = "1.5.4"
|
|
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
|
|
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
|
|
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
|
|
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
|