3 import os, sys, getopt, io, termios
4 import socket, json, pprint
7 def __init__(self, nm):
10 return "<key %s>" % (self.nm,)
12 def __init__(self, cc):
13 super().__init__(repr(cc))
19 K_LEFT = key("K_LEFT")
20 K_RIGHT = key("K_RIGHT")
22 K_DOWN = key("K_DOWN")
23 MOD_SHIFT = key("MOD_SHIFT")
24 MOD_META = key("MOD_META")
25 MOD_CTRL = key("MOD_CTRL")
27 def __init__(self, *, path="/dev/tty"):
28 self.io = io.FileIO(os.open(path, os.O_RDWR | os.O_NOCTTY), "r+")
29 attr = termios.tcgetattr(self.io.fileno())
31 attr[3] &= ~termios.ECHO & ~termios.ICANON
32 termios.tcsetattr(self.io.fileno(), termios.TCSANOW, attr)
36 return None if b == b"" else b[0]
38 _csikeys = {'A': K_UP, 'B': K_DOWN, 'C': K_RIGHT, 'D': K_LEFT}
44 return akey("\x1b"), set()
55 par = (par * 10) + (c - 48)
65 elif chr(c) in self._csikeys:
66 key = self._csikeys[chr(c)]
72 mods.add(self.MOD_SHIFT)
74 mods.add(self.MOD_META)
76 mods.add(self.MOD_CTRL)
79 return akey(chr(c)), [self.MOD_META]
81 return akey(chr(c)), set()
84 termios.tcsetattr(self.io.fileno(), termios.TCSANOW, self.bka)
90 def __exit__(self, *exc):
95 def __init__(self, path):
97 self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
99 self.obuf = bytearray()
100 self.ibuf = bytearray()
101 self.fps = self.getprop("container-fps")
103 def write(self, data):
104 self.obuf.extend(data)
107 while len(self.obuf) > 0:
108 ret = self.sk.send(self.obuf)
109 self.obuf[:ret] = b""
111 def recv(self, hint=1024):
112 data = self.sk.recv(hint)
115 self.ibuf.extend(data)
120 p2 = self.ibuf.find(b'\n', p)
122 ret = bytes(self.ibuf[:p2])
123 self.ibuf[:p2 + 1] = b""
128 def send(self, data):
134 resp = json.loads(self.readline().decode("utf-8"))
139 def runcmd(self, *cmd):
140 self.send(json.dumps({"command": cmd}).encode("utf-8") + b"\n")
141 resp = self.getresp()
142 if "error" not in resp:
143 sys.stderr.write("mpsync: strange response from %s: %r\n" % (self.path, resp))
144 if resp["error"] != "success":
145 sys.stderr.write("mpsync: error response from %s: %r\n" % (self.path, resp))
148 def getprop(self, pname):
149 return self.runcmd("get_property", pname)["data"]
151 def setprop(self, pname, val):
152 self.runcmd("set_property", pname, val)
155 out.write("usage: mpsync [-h] SOCKET...\n")
156 out.write("players: mpv -input-unix-socket SOCKET -pause [ARGS...] FILE\n")
158 opts, args = getopt.getopt(sys.argv[1:], "h")
167 if not os.path.exists(path):
168 sys.stderr.write("mpsync: %s: no such file or directory\n" % path)
173 targets.append(target(path))
180 cmd = json.dumps({"command": cmd}).encode("utf-8") + b"\n"
185 if "error" not in resp:
186 sys.stderr.write("mpsync: strange response from %s: %r\n" % (tgt.path, resp))
187 if resp["error"] != "success":
188 sys.stderr.write("mpsync: error response from %s: %r\n" % (tgt.path, resp))
190 def relseek(ss, offsets):
191 cur = targets[0].getprop("time-pos")
192 for tgt, off in zip(targets, offsets):
193 tgt.setprop("time-pos", cur + ss + off)
196 opos = targets[0].getprop("time-pos")
199 ret.append(tgt.getprop("time-pos") - opos)
203 runcmd("set_property", "hr-seek", "yes")
204 paused = targets[0].getprop("pause")
206 offsets = [0.0] * len(targets)
208 c, mods = tty.readkey()
216 simulcmd("set_property", "pause", paused)
217 elif c == rawtty.K_LEFT and not mods:
218 relseek(-10, offsets)
219 elif c == rawtty.K_RIGHT and not mods:
221 elif c == rawtty.K_UP and not mods:
223 elif c == rawtty.K_DOWN and not mods:
224 relseek(-60, offsets)
225 elif c == rawtty.K_LEFT and mods == {rawtty.MOD_SHIFT}:
227 elif c == rawtty.K_RIGHT and mods == {rawtty.MOD_SHIFT}:
229 elif c == rawtty.K_UP and mods == {rawtty.MOD_SHIFT}:
231 elif c == rawtty.K_DOWN and mods == {rawtty.MOD_SHIFT}:
236 mutemode = (mutemode + 1) % 3
239 tgt.setprop("mute", False)
241 for i, tgt in enumerate(targets):
242 tgt.setprop("mute", i != 0)
245 tgt.setprop("mute", True)
246 targets[0].runcmd("show_text", "Audio mode: %s" % ["All", "One", "None"][mutemode])
248 offsets = getoffsets()
249 for tgt, off in zip(targets, offsets):
250 tgt.runcmd("show_text", "Offset: %i" % round(off * tgt.fps))
255 runcmd("frame_back_step")
258 with rawtty() as tty:
261 except KeyboardInterrupt: