Advertisement
Dijit

umpv.py

Oct 4th, 2018
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.31 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. """
  4. This script emulates "unique application" functionality on Linux. When starting
  5. playback with this script, it will try to reuse an already running instance of
  6. mpv (but only if that was started with umpv). Other mpv instances (not started
  7. by umpv) are ignored, and the script doesn't know about them.
  8.  
  9. This only takes filenames as arguments. Custom options can't be used; the script
  10. interprets them as filenames. If mpv is already running, the files passed to
  11. umpv are appended to mpv's internal playlist. If a file does not exist or is
  12. otherwise not playable, mpv will skip the playlist entry when attempting to
  13. play it (from the GUI perspective, it's silently ignored).
  14.  
  15. If mpv isn't running yet, this script will start mpv and let it control the
  16. current terminal. It will not write output to stdout/stderr, because this
  17. will typically just fill ~/.xsession-errors with garbage.
  18.  
  19. mpv will terminate if there are no more files to play, and running the umpv
  20. script after that will start a new mpv instance.
  21.  
  22. Note that you can control the mpv instance by writing to the command fifo:
  23.  
  24.    echo "cycle fullscreen" > ~/.umpv_fifo
  25.  
  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.  
  33. import sys
  34. import os
  35. import errno
  36. import subprocess
  37. import fcntl
  38. import stat
  39. import string
  40.  
  41. files = sys.argv[1:]
  42.  
  43. # this is the same method mpv uses to decide this
  44. def is_url(filename):
  45.     parts = filename.split("://", 1)
  46.     if len(parts) < 2:
  47.         return False
  48.     # protocol prefix has no special characters => it's an URL
  49.     allowed_symbols = string.ascii_letters + string.digits + '_'
  50.     prefix = parts[0]
  51.     return all(map(lambda c: c in allowed_symbols, prefix))
  52.  
  53. # make them absolute; also makes them safe against interpretation as options
  54. def make_abs(filename):
  55.     if not is_url(filename):
  56.         return os.path.abspath(filename)
  57.     return filename
  58. files = [make_abs(f) for f in files]
  59.  
  60. FIFO = os.path.join(os.getenv("HOME"), ".umpv_fifo")
  61.  
  62. fifo_fd = -1
  63. try:
  64.     fifo_fd = os.open(FIFO, os.O_NONBLOCK | os.O_WRONLY)
  65. except OSError as e:
  66.     if e.errno == errno.ENXIO:
  67.         pass  # pipe has no writer
  68.     elif e.errno == errno.ENOENT:
  69.         pass # doesn't exist
  70.     else:
  71.         raise e
  72.  
  73. if fifo_fd >= 0:
  74.     # Unhandled race condition: what if mpv is terminating right now?
  75.     fcntl.fcntl(fifo_fd, fcntl.F_SETFL, 0) # set blocking mode
  76.     fifo = os.fdopen(fifo_fd, "w")
  77.     for f in files:
  78.         # escape: \ \n "
  79.         f = f.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
  80.         f = "\"" + f + "\""
  81.         fifo.write("raw loadfile " + f + " append\n")
  82. else:
  83.     # Recreate pipe if it doesn't already exist.
  84.     # Also makes sure it's safe, and no other user can create a bogus pipe
  85.     # that breaks security.
  86.     try:
  87.         os.unlink(FIFO)
  88.     except OSError as e:
  89.         pass
  90.     os.mkfifo(FIFO, 0o600)
  91.  
  92.     opts = (os.getenv("MPV") or "mpv").split()
  93.     opts.extend(["--no-terminal", "--force-window", "--input-file=" + FIFO,
  94.                  "--"])
  95.     opts.extend(files)
  96.  
  97.     subprocess.check_call(opts)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement