1(import socket json threading2 [collections [defaultdict]]3 [mpv.queue [MSGQueue]]4 [mpv.message [ServerMsg ClientMsg]])5(require [hy.contrib.walk [let]])67(defclass MPVException [Exception])89;; TODO make this resemble the mpv lua api10;; See: https://mpv.io/manual/master/#mp-functions11(defclass Connection [object]12 (defn --init-- [self path]13 (setv self.socket (socket.socket socket.AF_UNIX14 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))2627 (defn shutdown [self]28 (self.socket.shutdown socket.SHUT_RD)29 (.join self.thread))3031 ;; 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))3637 (defn handle-property [self msg]38 ((get self.property-handlers (get msg.dict "id"))39 (msg.get-data)))4041 (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)))))4647 (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))))5253 (defn recv-thread [self]54 (with [file (self.socket.makefile)]55 (for [input (iter file.readline "")]56 (self.handle-input input))))5758 (defn register-event [self name callable]59 (.append (get self.event-handlers name) callable)60 (get self.event-handlers name))6162 (defn unregister-event [self callable]63 (setv self.event-handlers64 (dfor (, key value) (.items self.event-handlers)65 :if (!= key callable)66 [key value])))6768 (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))))))7980 (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)8990 (defn get-property [self name &optional default]91 (try92 (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)))))9798 (defn set-property [self name value]99 (self.send-command "set_property" name value))100101 ;; TODO: pass handler instead of id as argument102 (defn unobserve-property [self id]103 (self.send-command "unobserve_property" id)104 (del (get self.property-handlers id))))