From: Fredrik Tolf Date: Fri, 15 Apr 2016 22:50:15 +0000 (+0200) Subject: Check in mpsync. X-Git-Url: http://git.dolda2000.com/gitweb/?a=commitdiff_plain;h=c0897f292783f95e30c95784d9e265f29e0279ff;p=utils.git Check in mpsync. --- diff --git a/mpsync b/mpsync new file mode 100755 index 0000000..8783e28 --- /dev/null +++ b/mpsync @@ -0,0 +1,246 @@ +#!/usr/bin/python3 + +import os, sys, getopt, io, termios +import socket, json, pprint + +class key(object): + def __init__(self, nm): + self.nm = nm + def __repr__(self): + return "" % (self.nm,) +class akey(key): + def __init__(self, cc): + super().__init__(repr(cc)) + self.cc = cc + def __eq__(self, o): + return self.cc == o + +class rawtty(object): + K_LEFT = key("K_LEFT") + K_RIGHT = key("K_RIGHT") + K_UP = key("K_UP") + K_DOWN = key("K_DOWN") + MOD_SHIFT = key("MOD_SHIFT") + MOD_META = key("MOD_META") + MOD_CTRL = key("MOD_CTRL") + + def __init__(self, *, path="/dev/tty"): + self.io = io.FileIO(os.open(path, os.O_RDWR | os.O_NOCTTY), "r+") + attr = termios.tcgetattr(self.io.fileno()) + self.bka = list(attr) + attr[3] &= ~termios.ECHO & ~termios.ICANON + termios.tcsetattr(self.io.fileno(), termios.TCSANOW, attr) + + def getc(self): + b = self.io.read(1) + return None if b == b"" else b[0] + + _csikeys = {'A': K_UP, 'B': K_DOWN, 'C': K_RIGHT, 'D': K_LEFT} + def readkey(self): + c = self.getc() + if c == 27: + c = self.getc() + if c == 27: + return akey("\x1b"), set() + elif c == ord('O'): + return None, set() + elif c == ord('['): + pars = [] + par = None + while True: + c = self.getc() + if 48 <= c <= 57: + if par is None: + par = 0 + par = (par * 10) + (c - 48) + elif c == ord(';'): + pars.append(par) + par = None + else: + if par is not None: + pars.append(par) + break + if c == ord('~'): + key = None + elif chr(c) in self._csikeys: + key = self._csikeys[chr(c)] + else: + key = None + mods = set() + if len(pars) > 1: + if (pars[1] - 1) & 1: + mods.add(self.MOD_SHIFT) + if (pars[1] - 1) & 2: + mods.add(self.MOD_META) + if (pars[1] - 1) & 4: + mods.add(self.MOD_CTRL) + return key, mods + else: + return akey(chr(c)), [self.MOD_META] + else: + return akey(chr(c)), set() + + def close(self): + termios.tcsetattr(self.io.fileno(), termios.TCSANOW, self.bka) + self.io.close() + + def __enter__(self): + return self + + def __exit__(self, *exc): + self.close() + return False + +class target(object): + def __init__(self, path): + self.path = path + self.sk = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sk.connect(path) + self.obuf = bytearray() + self.ibuf = bytearray() + + def write(self, data): + self.obuf.extend(data) + + def flush(self): + while len(self.obuf) > 0: + ret = self.sk.send(self.obuf) + self.obuf[:ret] = b"" + + def recv(self, hint=1024): + data = self.sk.recv(hint) + if data == b"": + raise EOFError() + self.ibuf.extend(data) + + def readline(self): + p = 0 + while True: + p2 = self.ibuf.find(b'\n', p) + if p2 != -1: + ret = bytes(self.ibuf[:p2]) + self.ibuf[:p2 + 1] = b"" + return ret + p = len(self.ibuf) + self.recv() + + def send(self, data): + self.write(data) + self.flush() + + def getresp(self): + while True: + resp = json.loads(self.readline().decode("utf-8")) + if "event" in resp: + continue + return resp + + def runcmd(self, cmd): + self.send(json.dumps(cmd).encode("utf-8") + b"\n") + resp = self.getresp() + if "error" not in resp: + sys.stderr.write("mpsync: strange response from %s: %r\n" % (self.path, resp)) + if resp["error"] != "success": + sys.stderr.write("mpsync: error response from %s: %r\n" % (self.path, resp)) + return resp + + def getprop(self, pname): + return self.runcmd({"command": ["get_property", pname]})["data"] + + def setprop(self, pname, val): + self.runcmd({"command": ["set_property", pname, val]}) + +def usage(out): + out.write("usage: mpsync [-h] SOCKET...\n") + out.write("players: mpv -input-unix-socket SOCKET -pause [ARGS...] FILE\n") + +opts, args = getopt.getopt(sys.argv[1:], "h") +for o, a in opts: + if o == "-h": + usage(sys.stdout) + sys.exit(0) +if len(args) < 1: + usage(sys.stderr) + sys.exit(1) +for path in args: + if not os.path.exists(path): + sys.stderr.write("mpsync: %s: no such file or directory\n" % path) + sys.exit(1) + +targets = [] +for path in args: + targets.append(target(path)) + +def runcmd(*cmd): + cmd = {"command": cmd} + for tgt in targets: + tgt.runcmd(cmd) + +def simulcmd(*cmd): + cmd = json.dumps({"command": cmd}).encode("utf-8") + b"\n" + for tgt in targets: + tgt.send(cmd) + for tgt in targets: + resp = tgt.getresp() + if "error" not in resp: + sys.stderr.write("mpsync: strange response from %s: %r\n" % (tgt.path, resp)) + if resp["error"] != "success": + sys.stderr.write("mpsync: error response from %s: %r\n" % (tgt.path, resp)) + +def relseek(ss, offsets): + cur = targets[0].getprop("time-pos") + for tgt, off in zip(targets, offsets): + tgt.setprop("time-pos", cur + ss + off) + +def getoffsets(): + opos = targets[0].getprop("time-pos") + ret = [] + for tgt in targets: + ret.append(tgt.getprop("time-pos") - opos) + return ret + +def main(tty): + paused = targets[0].getprop("pause") + runcmd("set_property", "hr-seek", "yes") + offsets = [0.0] * len(targets) + while True: + c, mods = tty.readkey() + if c == 'q': + return + elif c == 'Q': + runcmd("quit") + return + elif c == ' ': + paused = not paused + simulcmd("set_property", "pause", paused) + elif c == rawtty.K_LEFT and not mods: + relseek(-10, offsets) + elif c == rawtty.K_RIGHT and not mods: + relseek(10, offsets) + elif c == rawtty.K_UP and not mods: + relseek(60, offsets) + elif c == rawtty.K_DOWN and not mods: + relseek(-60, offsets) + elif c == rawtty.K_LEFT and mods == {rawtty.MOD_SHIFT}: + relseek(-2, offsets) + elif c == rawtty.K_RIGHT and mods == {rawtty.MOD_SHIFT}: + relseek(2, offsets) + elif c == rawtty.K_UP and mods == {rawtty.MOD_SHIFT}: + relseek(5, offsets) + elif c == rawtty.K_DOWN and mods == {rawtty.MOD_SHIFT}: + relseek(-5, offsets) + elif c == 'S': + offsets = getoffsets() + print(offsets) + elif c == '.': + runcmd("frame_step") + paused = True + elif c == ',': + runcmd("frame_back_step") + paused = True + +with rawtty() as tty: + try: + main(tty) + except KeyboardInterrupt: + pass