umpv 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #!/usr/bin/env python3
  2. """
  3. Original: https://github.com/mpv-player/mpv/blob/master/TOOLS/umpv
  4. Added options to stay open. See around line 88.
  5. Usage:
  6. To use as a keep open option:
  7. 1. Associate your media files with this script (open with this script)
  8. 2. Right click files and open with this script (it will open in already opened
  9. instance)
  10. """
  11. """
  12. This script emulates "unique application" functionality on Linux. When starting
  13. playback with this script, it will try to reuse an already running instance of
  14. mpv (but only if that was started with umpv). Other mpv instances (not started
  15. by umpv) are ignored, and the script doesn't know about them.
  16. This only takes filenames as arguments. Custom options can't be used; the script
  17. interprets them as filenames. If mpv is already running, the files passed to
  18. umpv are appended to mpv's internal playlist. If a file does not exist or is
  19. otherwise not playable, mpv will skip the playlist entry when attempting to
  20. play it (from the GUI perspective, it's silently ignored).
  21. If mpv isn't running yet, this script will start mpv and let it control the
  22. current terminal. It will not write output to stdout/stderr, because this
  23. will typically just fill ~/.xsession-errors with garbage.
  24. mpv will terminate if there are no more files to play, and running the umpv
  25. script after that will start a new mpv instance.
  26. Note: you can supply custom mpv path and options with the MPV environment
  27. variable. The environment variable will be split on whitespace, and the
  28. first item is used as path to mpv binary and the rest is passed as options
  29. _if_ the script starts mpv. If mpv is not started by the script (i.e. mpv
  30. is already running), this will be ignored.
  31. """
  32. import sys
  33. import os
  34. import socket
  35. import errno
  36. import subprocess
  37. import string
  38. files = sys.argv[1:]
  39. # this is the same method mpv uses to decide this
  40. def is_url(filename):
  41. parts = filename.split("://", 1)
  42. if len(parts) < 2:
  43. return False
  44. # protocol prefix has no special characters => it's an URL
  45. allowed_symbols = string.ascii_letters + string.digits + '_'
  46. prefix = parts[0]
  47. return all(map(lambda c: c in allowed_symbols, prefix))
  48. # make them absolute; also makes them safe against interpretation as options
  49. def make_abs(filename):
  50. if not is_url(filename):
  51. return os.path.abspath(filename)
  52. return filename
  53. files = (make_abs(f) for f in files)
  54. SOCK = os.path.join(os.getenv("HOME"), ".umpv_socket")
  55. sock = None
  56. try:
  57. sock = socket.socket(socket.AF_UNIX)
  58. sock.connect(SOCK)
  59. except socket.error as e:
  60. if e.errno == errno.ECONNREFUSED:
  61. sock = None
  62. pass # abandoned socket
  63. elif e.errno == errno.ENOENT:
  64. sock = None
  65. pass # doesn't exist
  66. else:
  67. raise e
  68. if sock:
  69. # Unhandled race condition: what if mpv is terminating right now?
  70. for f in files:
  71. # escape: \ \n "
  72. f = f.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
  73. f = "\"" + f + "\""
  74. sock.send(("raw loadfile " + f + " append\n").encode("utf-8"))
  75. else:
  76. # Let mpv recreate socket if it doesn't already exist.
  77. opts = (os.getenv("MPV") or "mpv").split()
  78. #opts.extend(["--no-terminal", "--force-window", "--input-ipc-server=" + SOCK,
  79. opts.extend(["--no-terminal", "--force-window", "--keep-open=yes", "--keep-open-pause=no", "--start=00:00", "--title=umpv (single instance) - ${filename}", "--input-ipc-server=" + SOCK,
  80. "--"])
  81. opts.extend(files)
  82. subprocess.check_call(opts)