mpvd

Control mpv using the MPD protocol

git clone https://git.8pit.net/mpvd.git

  1(import socket json threading
  2  [collections [defaultdict]]
  3  [mpv.queue [MSGQueue]]
  4  [mpv.message [ServerMsg ClientMsg]])
  5(require [hy.contrib.walk [let]])
  6
  7(defclass MPVException [Exception])
  8
  9;; TODO make this resemble the mpv lua api
 10;; See: https://mpv.io/manual/master/#mp-functions
 11(defclass Connection [object]
 12  (defn --init-- [self path]
 13    (setv self.socket (socket.socket socket.AF_UNIX
 14                                     socket.SOCK_STREAM))
 15    (self.socket.connect path)
 16    (setv self.event-handlers (defaultdict list))
 17    (setv self.property-handlers {})
 18    (setv self.observe-id 1)
 19    (setv self.observe-lock (threading.Lock))
 20    (setv self.request-id 0)
 21    (setv self.socket-lock (threading.Lock))
 22    (setv self.queue (MSGQueue))
 23    (setv self.thread (threading.Thread :target self.recv-thread))
 24    (self.register-event "property-change" self.handle-property)
 25    (self.thread.start))
 26
 27  (defn shutdown [self]
 28    (self.socket.shutdown socket.SHUT_RD)
 29    (.join self.thread))
 30
 31  ;; TODO overflow handling?
 32  (defn get-request-id [self]
 33    (let [id self.request-id]
 34      (setv self.request-id (inc self.request-id))
 35      id))
 36
 37  (defn handle-property [self msg]
 38    ((get self.property-handlers (get msg.dict "id"))
 39                                 (msg.get-data)))
 40
 41  (defn handle-event [self msg]
 42    (let [name (get msg.dict "event")]
 43      (if (in name self.event-handlers)
 44        (for [h (get self.event-handlers name)]
 45          (h msg)))))
 46
 47  (defn handle-input [self input]
 48    (let [msg (ServerMsg input)]
 49      (if (msg.event?)
 50        (self.handle-event msg)
 51        (self.queue.release (msg.get-id) msg))))
 52
 53  (defn recv-thread [self]
 54    (with [file (self.socket.makefile)]
 55      (for [input (iter file.readline "")]
 56        (self.handle-input input))))
 57
 58  (defn register-event [self name callable]
 59    (.append (get self.event-handlers name) callable)
 60    (get self.event-handlers name))
 61
 62  (defn unregister-event [self callable]
 63    (setv self.event-handlers
 64      (dfor (, key value) (.items self.event-handlers)
 65        :if (!= key callable)
 66        [key value])))
 67
 68  (defn send-command [self name &rest params]
 69    (unless (isinstance name str)
 70      (raise (TypeError "command name must be a string")))
 71    (with (self.socket-lock)
 72      (let [rid (self.get-request-id)
 73            req (ClientMsg name :args (list params) :id rid)]
 74        (self.socket.sendall (req.to-bytes))
 75        (let [resp (self.queue.wait rid)]
 76          (if (resp.error?)
 77            (raise (MPVException (get resp.dict "error")))
 78            (resp.get-data))))))
 79
 80  (defn observe-property [self name callable]
 81    (unless (isinstance name str)
 82      (raise (TypeError "name must be a string")))
 83    (with (self.observe-lock)
 84        (setv id self.observe-id)
 85        (setv self.observe-id (inc id))
 86        (assoc self.property-handlers id callable))
 87    (self.send-command "observe_property" id name)
 88    id)
 89
 90  (defn get-property [self name &optional default]
 91    (try
 92      (self.send-command "get_property" name)
 93      (except [e MPVException]
 94        (if (and (= (str e) "property unavailable")
 95                 (not (is None default)))
 96          default (raise e)))))
 97
 98  (defn set-property [self name value]
 99    (self.send-command "set_property" name value))
100
101  ;; TODO: pass handler instead of id as argument
102  (defn unobserve-property [self id]
103    (self.send-command "unobserve_property" id)
104    (del (get self.property-handlers id))))