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))))