Add UMPV_SOCKET_DIR for socket base dir selection and fallback to TMPDIR as last resort.
Unified · Split
| @@ -26,62 +26,67 @@ Note: you can supply custom mpv path and options with the MPV environment · 25 unmodified lines | ||
| 26 | 26 | is already running), this will be ignored. |
| 27 | 27 | """ |
| 28 | 28 | |
| 29 | −import sys | |
| 30 | 29 | import os |
| 30 | +import shlex | |
| 31 | 31 | import socket |
| 32 | −import errno | |
| 33 | −import subprocess | |
| 34 | 32 | import string |
| 35 | −import shlex | |
| 33 | +import subprocess | |
| 34 | +import sys | |
| 35 | +from collections.abc import Iterable | |
| 36 | 36 | |
| 37 | −files = sys.argv[1:] | |
| 38 | 37 | |
| 39 | −# this is the same method mpv uses to decide this | |
| 40 | −def is_url(filename): | |
| 38 | +def is_url(filename: str) -> bool: | |
| 41 | 39 | parts = filename.split("://", 1) |
| 42 | 40 | if len(parts) < 2: |
| 43 | 41 | return False |
| 44 | 42 | # protocol prefix has no special characters => it's an URL |
| 45 | − allowed_symbols = string.ascii_letters + string.digits + '_' | |
| 43 | + allowed_symbols = string.ascii_letters + string.digits + "_" | |
| 46 | 44 | prefix = parts[0] |
| 47 | − return all(map(lambda c: c in allowed_symbols, prefix)) | |
| 48 | − | |
| 49 | −# make them absolute; also makes them safe against interpretation as options | |
| 50 | −def make_abs(filename): | |
| 51 | − if not is_url(filename): | |
| 52 | − return os.path.abspath(filename) | |
| 53 | − return filename | |
| 54 | −files = (make_abs(f) for f in files) | |
| 55 | − | |
| 56 | −SOCK = os.path.join(os.getenv("XDG_RUNTIME_DIR", os.getenv("HOME")), ".umpv_socket") | |
| 57 | − | |
| 58 | −sock = None | |
| 59 | −try: | |
| 60 | − sock = socket.socket(socket.AF_UNIX) | |
| 61 | − sock.connect(SOCK) | |
| 62 | −except socket.error as e: | |
| 63 | − if e.errno == errno.ECONNREFUSED: | |
| 64 | − sock = None | |
| 65 | − pass # abandoned socket | |
| 66 | − elif e.errno == errno.ENOENT: | |
| 67 | − sock = None | |
| 68 | − pass # doesn't exist | |
| 69 | − else: | |
| 70 | − raise e | |
| 71 | − | |
| 72 | −if sock: | |
| 45 | + return all(c in allowed_symbols for c in prefix) | |
| 46 | + | |
| 47 | +def get_socket_path() -> str: | |
| 48 | + base_dir = ( | |
| 49 | + os.getenv("UMPV_SOCKET_DIR") or | |
| 50 | + os.getenv("XDG_RUNTIME_DIR") or | |
| 51 | + os.getenv("HOME") or | |
| 52 | + os.getenv("TMPDIR") | |
| 53 | + ) | |
| 54 | + | |
| 55 | + if not base_dir: | |
| 56 | + raise Exception("Could not determine a base directory for the socket. " | |
| 57 | + "Ensure that one of the following environment variables is set: " | |
| 58 | + "UMPV_SOCKET_DIR, XDG_RUNTIME_DIR, HOME or TMPDIR.") | |
| 59 | + | |
| 60 | + return os.path.join(base_dir, ".umpv_socket") | |
| 61 | + | |
| 62 | +def send_files_to_mpv(sock: socket.socket, files: Iterable[str]) -> None: | |
| 73 | 63 | # Unhandled race condition: what if mpv is terminating right now? |
| 74 | 64 | for f in files: |
| 75 | 65 | # escape: \ \n " |
| 76 | − f = f.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n") | |
| 77 | − f = "\"" + f + "\"" | |
| 78 | − sock.send(("raw loadfile " + f + " append\n").encode("utf-8")) | |
| 79 | −else: | |
| 80 | − # Let mpv recreate socket if it doesn't already exist. | |
| 81 | − | |
| 82 | − opts = shlex.split(os.getenv("MPV") or "mpv") | |
| 83 | − opts.extend(["--no-terminal", "--force-window", "--input-ipc-server=" + SOCK, | |
| 84 | − "--"]) | |
| 85 | − opts.extend(files) | |
| 86 | − | |
| 87 | − subprocess.check_call(opts) | |
| 66 | + f = f.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") | |
| 67 | + sock.send(f'raw loadfile "{f}" append\n'.encode()) | |
| 68 | + | |
| 69 | +def start_mpv(files: Iterable[str], socket_path: str) -> None: | |
| 70 | + mpv_command = shlex.split(os.getenv("MPV", "mpv")) | |
| 71 | + mpv_command.extend([ | |
| 72 | + "--no-terminal", | |
| 73 | + "--force-window", | |
| 74 | + f"--input-ipc-server={socket_path}", | |
| 75 | + "--", | |
| 76 | + ]) | |
| 77 | + mpv_command.extend(files) | |
| 78 | + subprocess.check_call(mpv_command) | |
| 79 | + | |
| 80 | +def main() -> None: | |
| 81 | + files = (os.path.abspath(f) if not is_url(f) else f for f in sys.argv[1:]) | |
| 82 | + socket_path = get_socket_path() | |
| 83 | + | |
| 84 | + try: | |
| 85 | + with socket.socket(socket.AF_UNIX) as sock: | |
| 86 | + sock.connect(socket_path) | |
| 87 | + send_files_to_mpv(sock, files) | |
| 88 | + except (FileNotFoundError, ConnectionRefusedError): | |
| 89 | + start_mpv(files, socket_path) | |
| 90 | + | |
| 91 | +if __name__ == "__main__": | |
| 92 | + main() | |