python-mpv-jsonipc 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl
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.
- {python_mpv_jsonipc-1.4.0.dist-info → python_mpv_jsonipc-1.5.0.dist-info}/METADATA +5 -1
- python_mpv_jsonipc-1.5.0.dist-info/RECORD +6 -0
- python_mpv_jsonipc.py +254 -27
- python_mpv_jsonipc-1.4.0.dist-info/RECORD +0 -6
- {python_mpv_jsonipc-1.4.0.dist-info → python_mpv_jsonipc-1.5.0.dist-info}/WHEEL +0 -0
- {python_mpv_jsonipc-1.4.0.dist-info → python_mpv_jsonipc-1.5.0.dist-info}/licenses/LICENSE.md +0 -0
- {python_mpv_jsonipc-1.4.0.dist-info → python_mpv_jsonipc-1.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-mpv-jsonipc
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Python API to MPV using JSON IPC
|
|
5
5
|
Author-email: Izzie Walton <izzie@iwalton.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -55,6 +55,10 @@ mpv.volume = 20
|
|
|
55
55
|
# You can also send commands.
|
|
56
56
|
mpv.command_name(arg1, arg2)
|
|
57
57
|
|
|
58
|
+
# Or send one without waiting for it. Returns a concurrent.futures.Future.
|
|
59
|
+
mpv.command_async("sub-add", "https://example.com/slow.srt",
|
|
60
|
+
callback=lambda error, result: print(error or "loaded"))
|
|
61
|
+
|
|
58
62
|
# Bind to key press events with a decorator
|
|
59
63
|
@mpv.on_key_press("space")
|
|
60
64
|
def space_handler():
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
python_mpv_jsonipc.py,sha256=6xQ45aP3vLNxLy6OdyReB-MPnQvmOJ00CzzNQ13iGS4,61266
|
|
2
|
+
python_mpv_jsonipc-1.5.0.dist-info/licenses/LICENSE.md,sha256=7GQBUOA3dNG1Jk-yaPOCncNnRVURCRMev23QpZ1ew-s,10495
|
|
3
|
+
python_mpv_jsonipc-1.5.0.dist-info/METADATA,sha256=axbDuWhKzIPoR1rvoF-b-MXVJx78LqF2MzFK6CGbmP8,2489
|
|
4
|
+
python_mpv_jsonipc-1.5.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
5
|
+
python_mpv_jsonipc-1.5.0.dist-info/top_level.txt,sha256=nGk4yjpVHKmIrL39XdGD2PhCeWF0oMjwey0nsNOd2TM,19
|
|
6
|
+
python_mpv_jsonipc-1.5.0.dist-info/RECORD,,
|
python_mpv_jsonipc.py
CHANGED
|
@@ -8,6 +8,7 @@ import subprocess
|
|
|
8
8
|
import random
|
|
9
9
|
import queue
|
|
10
10
|
import logging
|
|
11
|
+
from concurrent.futures import Future
|
|
11
12
|
|
|
12
13
|
log = logging.getLogger('mpv-jsonipc')
|
|
13
14
|
|
|
@@ -420,8 +421,13 @@ class WindowsSocket(threading.Thread):
|
|
|
420
421
|
# finally below, only once its own read has returned.
|
|
421
422
|
nread = self._transfer(_kernel32.ReadFile, self._read_event,
|
|
422
423
|
buf, len(buf))
|
|
423
|
-
if
|
|
424
|
+
if nread is None: # the peer is gone, or stop()
|
|
424
425
|
break
|
|
426
|
+
if nread == 0:
|
|
427
|
+
# A zero-length message, not the end: mpv's pipe is
|
|
428
|
+
# message-mode and it answers an async command with an
|
|
429
|
+
# empty write. Treating it as EOF closed the connection.
|
|
430
|
+
continue
|
|
425
431
|
|
|
426
432
|
data += buf.raw[:nread]
|
|
427
433
|
if data[-1] != 10:
|
|
@@ -699,6 +705,27 @@ class MPVProcess:
|
|
|
699
705
|
except FileNotFoundError:
|
|
700
706
|
pass # The other thread got there first, which is all we wanted.
|
|
701
707
|
|
|
708
|
+
#: Stands in a sync command's result slot when the connection closed first.
|
|
709
|
+
_CLOSED = object()
|
|
710
|
+
|
|
711
|
+
def _call_user(what, func, *args):
|
|
712
|
+
"""Call user code from a thread this library owns, logging anything it
|
|
713
|
+
raises -- SystemExit included: a caller's bug must not end the reader,
|
|
714
|
+
the close sweep or the drain. Not for EventHandler.run, which a
|
|
715
|
+
BaseException still ends (see there)."""
|
|
716
|
+
try:
|
|
717
|
+
func(*args)
|
|
718
|
+
except BaseException:
|
|
719
|
+
log.error("%s raised.", what, exc_info=1)
|
|
720
|
+
|
|
721
|
+
def _reply_outcome(data):
|
|
722
|
+
"""``(error, result)`` for one IPC reply."""
|
|
723
|
+
if data["error"] == "success":
|
|
724
|
+
return None, data.get("data")
|
|
725
|
+
if data["error"] == "property unavailable":
|
|
726
|
+
return None, None
|
|
727
|
+
return MPVError(data["error"]), None
|
|
728
|
+
|
|
702
729
|
class MPVInter:
|
|
703
730
|
"""
|
|
704
731
|
Low-level interface to MPV. Does NOT manage an mpv process. (Internal)
|
|
@@ -708,6 +735,7 @@ class MPVInter:
|
|
|
708
735
|
|
|
709
736
|
*ipc_socket* is the path to the Unix/Linux socket or name of the Windows pipe.
|
|
710
737
|
*callback(event_name, data)* is the function for recieving events.
|
|
738
|
+
It runs on the reader thread; an exception from it is logged.
|
|
711
739
|
*quit_callback* is called when the socket connection to MPV dies.
|
|
712
740
|
"""
|
|
713
741
|
Socket = UnixSocket
|
|
@@ -719,25 +747,64 @@ class MPVInter:
|
|
|
719
747
|
if self.callback is None:
|
|
720
748
|
self.callback = lambda event, data: None
|
|
721
749
|
|
|
722
|
-
self.socket = Socket(ipc_socket, self.event_callback, self.quit_callback)
|
|
723
|
-
self.socket.start()
|
|
724
750
|
self.command_id = 1
|
|
725
751
|
self.rid_lock = threading.Lock()
|
|
726
752
|
self.socket_lock = threading.Lock()
|
|
727
753
|
self.cid_result = {}
|
|
728
754
|
self.cid_wait = {}
|
|
755
|
+
# Before the reader starts: it can deliver a reply, or die, at once.
|
|
756
|
+
self.cid_async = {}
|
|
757
|
+
self.async_lock = threading.Lock()
|
|
758
|
+
self.closed = False
|
|
759
|
+
|
|
760
|
+
self.socket = Socket(ipc_socket, self.event_callback, self._on_quit)
|
|
761
|
+
self.socket.start()
|
|
729
762
|
|
|
730
763
|
def stop(self, join=True):
|
|
731
764
|
"""Terminate the underlying connection."""
|
|
732
765
|
self.socket.stop(join)
|
|
766
|
+
# The reader's _on_quit usually gets here first. This one does not
|
|
767
|
+
# depend on it, so nothing is left pending once stop() returns.
|
|
768
|
+
self._fail_async()
|
|
769
|
+
|
|
770
|
+
def _on_quit(self):
|
|
771
|
+
self._fail_async()
|
|
772
|
+
if self.quit_callback:
|
|
773
|
+
self.quit_callback()
|
|
774
|
+
|
|
775
|
+
def _fail_async(self):
|
|
776
|
+
"""Answer every pending command, async or not: nothing else ever will."""
|
|
777
|
+
with self.async_lock:
|
|
778
|
+
self.closed = True
|
|
779
|
+
pending, self.cid_async = self.cid_async, {}
|
|
780
|
+
for command_id, event in list(self.cid_wait.items()):
|
|
781
|
+
# setdefault: a reply that landed just before still wins.
|
|
782
|
+
self.cid_result.setdefault(command_id, _CLOSED)
|
|
783
|
+
event.set()
|
|
784
|
+
for on_reply in pending.values():
|
|
785
|
+
_call_user("command_async reply handler", on_reply,
|
|
786
|
+
BrokenPipeError("MPV connection closed."), None)
|
|
733
787
|
|
|
734
788
|
def event_callback(self, data):
|
|
735
789
|
"""Internal callback for recieving events from MPV."""
|
|
736
790
|
if "request_id" in data:
|
|
737
|
-
|
|
738
|
-
self.
|
|
791
|
+
request_id = data["request_id"]
|
|
792
|
+
with self.async_lock:
|
|
793
|
+
on_reply = self.cid_async.pop(request_id, None)
|
|
794
|
+
if on_reply is not None:
|
|
795
|
+
_call_user("command_async reply handler", on_reply,
|
|
796
|
+
*_reply_outcome(data))
|
|
797
|
+
return
|
|
798
|
+
# Not indexed: a reply nobody waits for (mpv answers id 0 to a
|
|
799
|
+
# request without one) would raise here, on the reader thread,
|
|
800
|
+
# and end the read loop as if the connection had died.
|
|
801
|
+
event = self.cid_wait.get(request_id)
|
|
802
|
+
if event is not None:
|
|
803
|
+
self.cid_result[request_id] = data
|
|
804
|
+
event.set()
|
|
739
805
|
elif "event" in data:
|
|
740
|
-
|
|
806
|
+
_call_user("MPVInter event callback", self.callback,
|
|
807
|
+
data["event"], data)
|
|
741
808
|
|
|
742
809
|
def command(self, command, *args):
|
|
743
810
|
"""
|
|
@@ -746,7 +813,8 @@ class MPVInter:
|
|
|
746
813
|
*command* is the name of the MPV command
|
|
747
814
|
|
|
748
815
|
All further arguments are forwarded to the MPV command.
|
|
749
|
-
Throws TimeoutError if timeout of 120 seconds is reached
|
|
816
|
+
Throws TimeoutError if timeout of 120 seconds is reached, and
|
|
817
|
+
BrokenPipeError as soon as the connection closes before the reply.
|
|
750
818
|
"""
|
|
751
819
|
self.rid_lock.acquire()
|
|
752
820
|
command_id = self.command_id
|
|
@@ -754,7 +822,12 @@ class MPVInter:
|
|
|
754
822
|
self.rid_lock.release()
|
|
755
823
|
|
|
756
824
|
event = threading.Event()
|
|
757
|
-
|
|
825
|
+
# Under the lock _fail_async takes, so a close either finds this
|
|
826
|
+
# waiter or is seen here first; neither way waits out TIMEOUT.
|
|
827
|
+
with self.async_lock:
|
|
828
|
+
if self.closed:
|
|
829
|
+
raise BrokenPipeError("MPV connection closed.")
|
|
830
|
+
self.cid_wait[command_id] = event
|
|
758
831
|
|
|
759
832
|
command_list = [command]
|
|
760
833
|
command_list.extend(args)
|
|
@@ -766,23 +839,59 @@ class MPVInter:
|
|
|
766
839
|
|
|
767
840
|
has_event = event.wait(timeout=TIMEOUT)
|
|
768
841
|
if has_event:
|
|
769
|
-
data = self.cid_result
|
|
770
|
-
del self.cid_result[command_id]
|
|
842
|
+
data = self.cid_result.pop(command_id)
|
|
771
843
|
del self.cid_wait[command_id]
|
|
772
|
-
if data
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
844
|
+
if data is _CLOSED:
|
|
845
|
+
raise BrokenPipeError("MPV connection closed.")
|
|
846
|
+
error, result = _reply_outcome(data)
|
|
847
|
+
if error is not None:
|
|
848
|
+
raise error
|
|
849
|
+
return result
|
|
778
850
|
else:
|
|
779
851
|
raise TimeoutError("No response from MPV.")
|
|
780
852
|
|
|
853
|
+
def command_async(self, on_reply, command, *args):
|
|
854
|
+
"""
|
|
855
|
+
Issue a command to MPV without waiting for it to finish.
|
|
856
|
+
|
|
857
|
+
*on_reply(error, result)* is called exactly once: with MPV's answer on
|
|
858
|
+
the transport's reader thread; with BrokenPipeError on the thread
|
|
859
|
+
that closes the connection, or on the calling thread if it is already
|
|
860
|
+
closed; or with the send's own error on the calling thread. An
|
|
861
|
+
exception from it is logged when the reader or the close calls it,
|
|
862
|
+
and raised to the caller otherwise. There is no timeout.
|
|
863
|
+
"""
|
|
864
|
+
with self.rid_lock:
|
|
865
|
+
command_id = self.command_id
|
|
866
|
+
self.command_id += 1
|
|
867
|
+
|
|
868
|
+
with self.async_lock:
|
|
869
|
+
closed = self.closed
|
|
870
|
+
if not closed:
|
|
871
|
+
self.cid_async[command_id] = on_reply
|
|
872
|
+
if closed:
|
|
873
|
+
on_reply(BrokenPipeError("MPV connection closed."), None)
|
|
874
|
+
return
|
|
875
|
+
|
|
876
|
+
command_list = [command]
|
|
877
|
+
command_list.extend(args)
|
|
878
|
+
try:
|
|
879
|
+
with self.socket_lock:
|
|
880
|
+
self.socket.send({"command": command_list,
|
|
881
|
+
"request_id": command_id, "async": True})
|
|
882
|
+
except Exception as error:
|
|
883
|
+
with self.async_lock:
|
|
884
|
+
owned = self.cid_async.pop(command_id, None)
|
|
885
|
+
if owned is not None: # else _fail_async has already answered
|
|
886
|
+
on_reply(error, None)
|
|
887
|
+
|
|
781
888
|
class EventHandler(threading.Thread):
|
|
782
889
|
"""Event handling thread. (Internal)"""
|
|
783
890
|
def __init__(self):
|
|
784
891
|
"""Create an instance of the thread."""
|
|
785
892
|
self.queue = queue.Queue()
|
|
893
|
+
self.stopped = False
|
|
894
|
+
self.stop_lock = threading.Lock()
|
|
786
895
|
threading.Thread.__init__(self)
|
|
787
896
|
self.daemon = True
|
|
788
897
|
|
|
@@ -793,24 +902,62 @@ class EventHandler(threading.Thread):
|
|
|
793
902
|
*func* is the function to call
|
|
794
903
|
|
|
795
904
|
All further arguments are forwarded to *func*.
|
|
905
|
+
|
|
906
|
+
Returns False, without queueing it, once the thread has stopped --
|
|
907
|
+
by stop(), or because a task raised SystemExit or the like.
|
|
796
908
|
"""
|
|
797
|
-
self.
|
|
909
|
+
with self.stop_lock:
|
|
910
|
+
if self.stopped:
|
|
911
|
+
return False
|
|
912
|
+
self.queue.put((func, args))
|
|
913
|
+
return True
|
|
798
914
|
|
|
799
915
|
def stop(self, join=True):
|
|
800
916
|
"""Terminate the thread."""
|
|
801
|
-
self.
|
|
802
|
-
|
|
917
|
+
with self.stop_lock:
|
|
918
|
+
self.stopped = True
|
|
919
|
+
self.queue.put("quit")
|
|
920
|
+
# A callback that calls terminate() is on this thread. The guard is
|
|
921
|
+
# the transports' (see UnixSocket.stop); `join` stays the timeout.
|
|
922
|
+
if self.ident is not None and self is not threading.current_thread():
|
|
923
|
+
self.join(join)
|
|
803
924
|
|
|
804
925
|
def run(self):
|
|
805
926
|
"""Process socket events. Do not run this directly. Use *start*."""
|
|
927
|
+
try:
|
|
928
|
+
while True:
|
|
929
|
+
event = self.queue.get()
|
|
930
|
+
if event == "quit":
|
|
931
|
+
break
|
|
932
|
+
try:
|
|
933
|
+
event[0](*event[1])
|
|
934
|
+
except Exception:
|
|
935
|
+
log.error("EventHandler caught exception from %s.", event, exc_info=1)
|
|
936
|
+
except BaseException as ex:
|
|
937
|
+
log.warning("A callback raised %r, ending the event thread: "
|
|
938
|
+
"property observers, key bindings and events will no "
|
|
939
|
+
"longer be delivered; command_async callbacks now run "
|
|
940
|
+
"on whichever thread delivers their outcome.", ex)
|
|
941
|
+
# Not _call_user, deliberately: a callback's SystemExit ends this
|
|
942
|
+
# thread, as it has since 2020.
|
|
943
|
+
raise
|
|
944
|
+
finally:
|
|
945
|
+
self._drain()
|
|
946
|
+
|
|
947
|
+
def _drain(self):
|
|
948
|
+
"""A callback's SystemExit (or similar) ends this thread, as it always
|
|
949
|
+
has. Refuse work from here on, so put_task says so, and run what was
|
|
950
|
+
already queued: a command_async future may be waiting in it."""
|
|
951
|
+
with self.stop_lock:
|
|
952
|
+
self.stopped = True
|
|
806
953
|
while True:
|
|
807
|
-
event = self.queue.get()
|
|
808
|
-
if event == "quit":
|
|
809
|
-
break
|
|
810
954
|
try:
|
|
811
|
-
event
|
|
812
|
-
except
|
|
813
|
-
|
|
955
|
+
event = self.queue.get_nowait()
|
|
956
|
+
except queue.Empty:
|
|
957
|
+
return
|
|
958
|
+
if event == "quit":
|
|
959
|
+
continue
|
|
960
|
+
_call_user("EventHandler task", event[0], *event[1])
|
|
814
961
|
|
|
815
962
|
class MPV:
|
|
816
963
|
"""
|
|
@@ -833,7 +980,8 @@ class MPV:
|
|
|
833
980
|
*mpv_location* is the location of MPV for *start_mpv*. (Default: Use MPV in PATH)
|
|
834
981
|
*log_handler(level, prefix, text)* is an optional handler for log events. (Default: Disabled)
|
|
835
982
|
*loglevel* is the level for log messages. Levels are fatal, error, warn, info, v, debug, trace. (Default: Disabled)
|
|
836
|
-
*quit_callback* is called when the socket connection to MPV dies.
|
|
983
|
+
*quit_callback* is called on the reader thread when the socket connection to MPV dies.
|
|
984
|
+
An exception from it is logged, and MPV is still terminated.
|
|
837
985
|
*diagnose_start_failures* re-runs MPV once, on the first failed start, to find out
|
|
838
986
|
why it refused to start. If MPV names an option it does not have, MPVProcessError is
|
|
839
987
|
raised immediately with *bad_option* set instead of spending every retry on something
|
|
@@ -958,7 +1106,7 @@ class MPV:
|
|
|
958
1106
|
Internal handler for quit events.
|
|
959
1107
|
"""
|
|
960
1108
|
if self.quit_callback:
|
|
961
|
-
self.quit_callback
|
|
1109
|
+
_call_user("quit_callback", self.quit_callback)
|
|
962
1110
|
self.terminate(join=False)
|
|
963
1111
|
|
|
964
1112
|
def bind_event(self, name, callback):
|
|
@@ -1068,6 +1216,11 @@ class MPV:
|
|
|
1068
1216
|
Waits for the value of a property to change.
|
|
1069
1217
|
|
|
1070
1218
|
*name* is the name of the property.
|
|
1219
|
+
|
|
1220
|
+
Never call it from inside a callback here: the change is delivered on
|
|
1221
|
+
the event thread, which the wait would be blocking, so it never ends.
|
|
1222
|
+
It also never returns once a callback has ended the event thread,
|
|
1223
|
+
since the change is then never delivered.
|
|
1071
1224
|
"""
|
|
1072
1225
|
event = threading.Event()
|
|
1073
1226
|
first_event = True
|
|
@@ -1126,6 +1279,80 @@ class MPV:
|
|
|
1126
1279
|
"""
|
|
1127
1280
|
return self.mpv_inter.command(command, *args)
|
|
1128
1281
|
|
|
1282
|
+
def command_async(self, command, *args, callback=None):
|
|
1283
|
+
"""
|
|
1284
|
+
Send a command to MPV without waiting for it to finish, for one that
|
|
1285
|
+
can take a long time (``sub-add`` of a slow URL). Unlike *command*,
|
|
1286
|
+
it holds neither the caller nor the connection: other commands are
|
|
1287
|
+
answered while it runs, and it has no timeout.
|
|
1288
|
+
|
|
1289
|
+
Returns a ``concurrent.futures.Future``. With no *callback* it holds
|
|
1290
|
+
MPV's result or the error, as *command* would have returned or raised.
|
|
1291
|
+
*callback(error, result)* is called first, with *error* None on
|
|
1292
|
+
success, and the future then holds what it returns or raises.
|
|
1293
|
+
|
|
1294
|
+
The callback runs on the event thread, like every other callback
|
|
1295
|
+
here. If an earlier callback ended that thread by raising SystemExit
|
|
1296
|
+
or the like, a warning is logged, and property observers, key
|
|
1297
|
+
bindings and events are no longer delivered at all; command_async
|
|
1298
|
+
callbacks still run, on whichever thread delivers their outcome
|
|
1299
|
+
instead -- the connection's reader while MPV is connected, or the
|
|
1300
|
+
thread that closes the connection -- and a SystemExit from one there
|
|
1301
|
+
is logged, not raised. With the event thread gone, a call made once
|
|
1302
|
+
the connection has closed (by *terminate* or by MPV exiting), or
|
|
1303
|
+
whose send fails, runs its callback on the calling thread before
|
|
1304
|
+
command_async returns, and a SystemExit from it is raised to that
|
|
1305
|
+
caller. A logging handler that raises SystemExit is not caught
|
|
1306
|
+
anywhere: as in the standard library, it ends the thread that logged,
|
|
1307
|
+
and in the close that leaves the futures after it unresolved.
|
|
1308
|
+
|
|
1309
|
+
The future resolves when MPV answers or, if the connection closes
|
|
1310
|
+
first, with BrokenPipeError -- meaning no answer will come, not that
|
|
1311
|
+
the command did not run. Until then it is pending: a command MPV is
|
|
1312
|
+
still running has no timeout.
|
|
1313
|
+
|
|
1314
|
+
Never block on the future from inside a callback here (a property
|
|
1315
|
+
observer, a key binding, an event, another command_async callback).
|
|
1316
|
+
Those run on the event thread, the thread that would resolve it, so
|
|
1317
|
+
the wait never ends. python-mpv behaves the same.
|
|
1318
|
+
|
|
1319
|
+
Once the event thread is gone, a callback must not block at all. On
|
|
1320
|
+
the reader, a sync *command* waits out TIMEOUT with nothing else read
|
|
1321
|
+
meanwhile, and a wait on a future never ends -- nor does a
|
|
1322
|
+
*terminate* called from another thread. python-mpv fails here too:
|
|
1323
|
+
once a callback's SystemExit stops its event loop, no callback or
|
|
1324
|
+
future is delivered at all.
|
|
1325
|
+
|
|
1326
|
+
The future cannot be cancelled: MPV's IPC protocol cannot abort a
|
|
1327
|
+
command. Needs MPV 0.33 or later; an older MPV ignores the request to
|
|
1328
|
+
run in the background and runs the command as *command* does, blocking
|
|
1329
|
+
every other command on the connection until it finishes.
|
|
1330
|
+
"""
|
|
1331
|
+
future = Future()
|
|
1332
|
+
future.set_running_or_notify_cancel()
|
|
1333
|
+
|
|
1334
|
+
def finish(error, result):
|
|
1335
|
+
try:
|
|
1336
|
+
if callback is not None:
|
|
1337
|
+
future.set_result(callback(error, result))
|
|
1338
|
+
elif error is not None:
|
|
1339
|
+
future.set_exception(error)
|
|
1340
|
+
else:
|
|
1341
|
+
future.set_result(result)
|
|
1342
|
+
except BaseException as ex:
|
|
1343
|
+
future.set_exception(ex)
|
|
1344
|
+
if not isinstance(ex, Exception):
|
|
1345
|
+
raise # which thread it reaches, and what happens: see the docstring
|
|
1346
|
+
log.error("command_async callback for {0} raised.".format(
|
|
1347
|
+
command), exc_info=1)
|
|
1348
|
+
|
|
1349
|
+
def on_reply(error, result):
|
|
1350
|
+
if not self.event_handler.put_task(finish, error, result):
|
|
1351
|
+
finish(error, result)
|
|
1352
|
+
|
|
1353
|
+
self.mpv_inter.command_async(on_reply, command, *args)
|
|
1354
|
+
return future
|
|
1355
|
+
|
|
1129
1356
|
def __getattr__(self, name):
|
|
1130
1357
|
if name in self.properties:
|
|
1131
1358
|
return self.command("get_property", name.replace("_", "-"))
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
python_mpv_jsonipc.py,sha256=g5BxL4Bzljm4QnsXG5kOKGGfHuiKlpqQqKmd5atQ-nM,50502
|
|
2
|
-
python_mpv_jsonipc-1.4.0.dist-info/licenses/LICENSE.md,sha256=7GQBUOA3dNG1Jk-yaPOCncNnRVURCRMev23QpZ1ew-s,10495
|
|
3
|
-
python_mpv_jsonipc-1.4.0.dist-info/METADATA,sha256=-gKDmmYj96EQjMvcTwgj_1AnBouKSD9ENAr1r77sVXU,2277
|
|
4
|
-
python_mpv_jsonipc-1.4.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
5
|
-
python_mpv_jsonipc-1.4.0.dist-info/top_level.txt,sha256=nGk4yjpVHKmIrL39XdGD2PhCeWF0oMjwey0nsNOd2TM,19
|
|
6
|
-
python_mpv_jsonipc-1.4.0.dist-info/RECORD,,
|
|
File without changes
|
{python_mpv_jsonipc-1.4.0.dist-info → python_mpv_jsonipc-1.5.0.dist-info}/licenses/LICENSE.md
RENAMED
|
File without changes
|
|
File without changes
|