PageRenderTime 63ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/external/libmpdee.el

https://bitbucket.org/henrik/emacs-old
Emacs Lisp | 1639 lines | 1288 code | 169 blank | 182 comment | 127 complexity | e164f5f7bef8f40a413994ec251ad7e7 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-3.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. ;;; LIBMPDEE.EL --- Client end library for mpd, a music playing daemon
  2. ;; Copyright (C) 2004, 2005, 2006 R.Ramkumar
  3. ;; Author: R.Ramkumar <andyetitmoves@gmail.com>
  4. ;; Created: 10 May 2004
  5. ;; Version: 2.1
  6. ;; Keywords: music, mpd
  7. ;; This file is *NOT* part of GNU Emacs
  8. ;; This program is free software; you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 2, or (at your option)
  11. ;; any later version.
  12. ;; This program is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; A copy of the GNU General Public License can be obtained from this
  17. ;; program's author (send electronic mail to andyetitmoves@gmail.com)
  18. ;; or from the Free Software Foundation, Inc.,
  19. ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. ;;; Commentary:
  21. ;; This package is a client end library to the wonderful music playing daemon by
  22. ;; name mpd. Hence, an obvious prerequisite for this package is mpd itself,
  23. ;; which you can get at http://www.musicpd.org. For those who haven't heard of
  24. ;; mpd, all I can say is that it is definitely worth a try, and is surely
  25. ;; different from the rest. This package is aimed at developers, and though
  26. ;; there are some interactive functions, much of the power of the library lies
  27. ;; away from the user.
  28. ;; This package started off to implement libmpdclient.c, which came with mpd, in
  29. ;; elisp. But as it stands of now, this package is not an exact translation.
  30. ;; Notable amongst the deviations are -
  31. ;;
  32. ;; - This package contains quite a bit of higher level functionality as
  33. ;; compared with the original. An action or a query needs only a single
  34. ;; call and the library user can choose to get either the raw response, the
  35. ;; formatted response, or hook on a callback for each logical token of the
  36. ;; response. However, dig deeper, and you will find the lower level
  37. ;; functionality available as well.
  38. ;; - The error throwing scheme consistent with what is expected of elisp
  39. ;; programs.
  40. ;; - Command list mode is transparent in most cases, as wherever
  41. ;; appropriate, functions taking arguments can accept either a single item
  42. ;; or a list of it for each argument.
  43. ;; - Apart from this, command list functionality is limited to actions
  44. ;; rather than queries, as it is anyway not that easy to parse out the
  45. ;; individual responses from command-list queries (it is firstly possible
  46. ;; only from 0.11, which allows for a list_OK to be added at the end of
  47. ;; response for each command in the list).
  48. ;; - command_list_ok_begin isn't implemented. It is still possible to
  49. ;; explicitly use "command_list_(ok)begin\n..\ncommand_list_end" for
  50. ;; `mpd-execute-command' and get the response tokens for queries. A better
  51. ;; interface may be available in the future.
  52. ;; - There is a small interactive interface as well, but this is
  53. ;; intentionally incomplete. The point is that this exists only to the
  54. ;; extent of adding an interactive part to the functions, without modifying
  55. ;; the functions per se.
  56. ;; Most of the functions below require a connection object as an argument, which
  57. ;; you can create by a call to `mpd-conn-new'. The recommended way to use
  58. ;; customization to choose parameters for mpd connections is to use the widget
  59. ;; `mpd-connection'.
  60. ;; As this package caters to developers, it should be helpful to browse the file
  61. ;; in order for atleast the functions and the documentation. The file is well
  62. ;; structured and documented, so go for it. The impatient could do a selective
  63. ;; display to 3 (C-u 3 C-x $) before proceeding.
  64. ;;; Installation:
  65. ;; Put this file somewhere on your load-path. Then, you could use
  66. ;; (require 'libmpdee) whenever the services of this package are needed.
  67. ;; Parameters used for the interactive calls can be customized in the group mpd
  68. ;; Use:
  69. ;; M-x customize-group mpd
  70. ;; to change the values to your liking.
  71. ;;; History: (See the SVN logs/ChangeLog for the list of all changes)
  72. ;; v2.1
  73. ;; Introducing automatic mode with hooking, for connections.
  74. ;; See `mpd-set-automatic-mode'.
  75. ;; v2.0
  76. ;; The interface has changed since version 1 of this library and there is no
  77. ;; backward compatibility. This change applies to functions which returned
  78. ;; vector whose descriptions were given `*-data' variables. Such functions have
  79. ;; been modified to return property lists, whose keys now describe the values
  80. ;; returned. This should hopefully be a much more scalable representation.
  81. ;;; Code:
  82. (defvar libmpdee-version "2.1"
  83. "libmpdee version information.")
  84. ;;;; User serviceable variable(s).
  85. (require 'custom)
  86. (require 'wid-edit)
  87. (defun widget-mpd-format-handler (widget esc)
  88. "Widget format handler for the MPD connection widget."
  89. (cond
  90. ((eq esc ?l)
  91. (widget-create-child-and-convert widget 'documentation-string "\
  92. This variable specifies a MPD connection.
  93. The value is a list of the mpd host name, port number, and the timeout for
  94. server replies. See `mpd-conn-new' for more details.")
  95. (insert "To know more about libmpdee, read ")
  96. (widget-create 'emacs-commentary-link :tag "this" "libmpdee"))
  97. (t (funcall (widget-get (widget-convert 'lazy) :format-handler)
  98. widget esc))))
  99. (defun mpd-connection-tidy (val)
  100. "Initialize unset parameters for a MPD connection.
  101. Replace parameters in the MPD connection VAL with sane defaults and return."
  102. (or val (setq val '(nil nil nil)))
  103. (let ((val val))
  104. (and (listp val)
  105. (or (car val)
  106. (setcar val (or (getenv "MPD_HOST") "localhost")))
  107. (setq val (cdr val))
  108. (or (car val) (setcar val 6600))
  109. (setq val (cdr val))
  110. (or (car val) (setcar val 10.0)))) val)
  111. (define-widget 'mpd-connection 'lazy
  112. "A widget for a MPD connection."
  113. :tag "MPD Connection"
  114. :format "%{%t%}:\n\n%l\n\n%v"
  115. :format-handler 'widget-mpd-format-handler
  116. :value-to-internal '(lambda (wid val) (mpd-connection-tidy val))
  117. :type '(list :format "%v"
  118. (string :format "%t: %v\n" :tag "Hostname" :size 15)
  119. (integer :format "%t: %v\n" :tag "Port" :size 5
  120. :match (lambda (widget value) (> value 0))
  121. :type-error "Port must be a natural number")
  122. (float :format "%t: %v\n\n" :tag "Timeout" :size 10
  123. :match (lambda (widget value) (> value 0))
  124. :type-error "Timeout must be a positive number")))
  125. (defgroup mpd nil
  126. "The client end library for MPD, the music playing daemon."
  127. :group 'external :group 'multimedia
  128. :link '(emacs-commentary-link "libmpdee"))
  129. (defcustom mpd-db-root (getenv "MPD_DB_ROOT")
  130. "*MPD database directory root."
  131. :type 'directory :group 'mpd)
  132. (defcustom mpd-interactive-connection-parameters (mpd-connection-tidy nil)
  133. "Parameters for the interactive mpd connection.
  134. These determine the connection used by interactive functions in `libmpdee'."
  135. :type 'mpd-connection :group 'mpd)
  136. (defface mpd-separator-face '((((background dark)) (:foreground "lightyellow"))
  137. (((background light)) (:foreground "darkgreen")))
  138. "Face for display of separator lines in interactive mpd queries."
  139. :group 'mpd)
  140. (defface mpd-header-face '((((background dark)) (:foreground "gold"))
  141. (((background light)) (:foreground "brown")))
  142. "Face for display of header lines in interactive mpd queries."
  143. :group 'mpd)
  144. (defface mpd-first-field-face '((((background dark)) (:foreground "cyan"))
  145. (((background light)) (:foreground "orange")))
  146. "Face for display of the first field in interactive mpd queries.
  147. Most lines in interactive displays are split into two fields."
  148. :group 'mpd)
  149. (defface mpd-second-field-face
  150. '((((background dark)) (:foreground "lightgreen"))
  151. (((background light)) (:foreground "blue")))
  152. "Face for display of the second field in interactive mpd queries.
  153. Most lines in interactive displays are split into two fields."
  154. :group 'mpd)
  155. ;;;; Constants and internal variables.
  156. (eval-and-compile (defconst mpd-welcome-message " MPD "))
  157. (defmacro mpd-welcome-length () (length mpd-welcome-message))
  158. (defconst mpd-ver-string-length 3)
  159. ;;;; Package independent helper functions.
  160. (defmacro mpd-assert-type (obj func)
  161. "Ensure that OBJ succeeds on type checking with predicate FUNC.
  162. The function emits a \"Wrong type argument\" signal on failure.
  163. Note that the arguments are evalled twice in this process."
  164. `(or (,func ,obj) (signal 'wrong-type-argument (list (quote ,func) ,obj))))
  165. (defmacro mpd-assert-string (obj) `(mpd-assert-type ,obj stringp))
  166. (defmacro mpd-assert-wholenump (obj) `(mpd-assert-type ,obj wholenump))
  167. (defmacro mpd-assert-numberp (obj) `(mpd-assert-type ,obj numberp))
  168. (defun mpd-string-to-number-strict (str &optional allowneg)
  169. "Convert string STR to a number strictly.
  170. Return nil if there are any unmatched characters.
  171. Allow negative numbers if ALLOWNEG is non-nil."
  172. (let ((num (string-to-number str)))
  173. (and (if allowneg (numberp num) (wholenump num))
  174. (string-equal str (number-to-string num)) num)))
  175. (put 'mpd-string-to-number-strict 'side-effect-free t)
  176. (defun mpd-get-lines (str)
  177. "Split STR into newline separated lines.
  178. Differ from `split-string' in that tokens are created
  179. for leading and trailing newlines."
  180. (let ((packets (split-string str "\n")))
  181. (when packets
  182. (if (= (aref str 0) ?\n)
  183. (setq packets (cons "" packets)))
  184. (if (= (aref str (1- (length str))) ?\n)
  185. (nconc packets (list ""))
  186. packets))))
  187. ;;; Modified from the pcomplete package.
  188. (defun mpd-sort-uniq-list (l lessp eqp)
  189. "Sort and remove multiples in list L.
  190. Use LESSP and EQP as predicates for the \"lesser\" and \"equal\" operations."
  191. (setq l (sort l lessp))
  192. (let ((m l))
  193. (while m
  194. (while (and (cdr m) (funcall eqp (car m) (cadr m)))
  195. (setcdr m (cddr m)))
  196. (setq m (cdr m)))) l)
  197. ;;; For edebug macro specifications.
  198. (eval-when-compile (require 'edebug))
  199. (defmacro with-mpd-temp-widen (&rest args)
  200. "Evaluate ARGS while temporarily widening the current buffer."
  201. `(save-restriction
  202. (widen)
  203. ,@args))
  204. (put 'with-mpd-temp-widen 'lisp-indent-function 0)
  205. (def-edebug-spec with-mpd-temp-widen (body))
  206. (defmacro with-mpd-free-buffer (&rest args)
  207. "Evaluate ARGS with temporary widening and saved excursion."
  208. `(save-excursion
  209. (with-mpd-temp-widen ,@args)))
  210. (put 'with-mpd-free-buffer 'lisp-indent-function 0)
  211. (def-edebug-spec with-mpd-free-buffer (body))
  212. (defmacro mpd-safe-nreverse (list)
  213. "Reverse LIST if it is a list, leave alone otherwise.
  214. Note that LIST is evaluated thrice."
  215. `(if (listp ,list) (nreverse ,list) ,list))
  216. (defun mpd-seq-add (seq &optional spec &rest args)
  217. "Operate the sequence SEQ on SPEC depending on its type.
  218. Add a copy of SEQ to SPEC, if it's a list and call it with SEQ as an argument
  219. followed by other ARGS specified, if it is a function. Return SPEC."
  220. (if (not (functionp spec))
  221. (cons (copy-sequence seq) spec)
  222. (apply spec seq args) spec))
  223. (defun mpd-elt-add (elt &optional spec &rest args)
  224. "Operate the object ELT on SPEC depending on its type.
  225. Add ELT to SPEC, if it's a list and call it with ELT as an argument
  226. followed by other ARGS specified, if it is a function. Return SPEC."
  227. (if (not (functionp spec))
  228. (cons elt spec)
  229. (apply spec elt args) spec))
  230. (defun mpd-parse-line (str)
  231. "Parse line STR of form \"KEY: VALUE\" to a cons (KEY . VALUE).
  232. Return (STR . nil) on a parse failure."
  233. (if (string-match "^\\([^:]*\\): ?\\(.*\\)$" str)
  234. (cons (match-string 1 str) (match-string 2 str))
  235. (cons str nil)))
  236. (defun mpd-safe-string (str)
  237. "Quote and escape string STR for sending to the mpd server."
  238. (if str
  239. (let ((start 0))
  240. (while (string-match "[\\\"]" str start)
  241. ;; We add an extra character,
  242. ;; so place start a character beyond end of match.
  243. (setq start (1+ (match-end 0)))
  244. (setq str (replace-match "\\\\\\&" t nil str)))
  245. (if (string-match " " str) (concat "\"" str "\"") str))))
  246. ;;; (defun mpd-log (fmt &rest args)
  247. ;;; (write-region (concat (apply 'format fmt args) "\n") nil "~/mpd.log" t 1))
  248. ;;; (write-region "" nil "~/mpd.log" nil 1)
  249. ;;;; Connection object internals, library users... please close ur eyes ;)
  250. ;;; A connection to mpd is represented by the vector whose elements are below:
  251. ;;; This is just for the hackers amongst you, and for my reference :)
  252. ;;; *WARNING* No program using this package should depend on this description.
  253. ;;; 0 : vector of mpd-ver-string-length version numbers
  254. ;;; 1 : process object for the connection to the server.
  255. ;;; 2 : transaction-buffer used by the process filter to handle partial recvs.
  256. ;;; 3 : list-mode - refer `mpd-execute-command'
  257. ;;; 4 : connection-status: should be t at the beginning of a command.
  258. ;;; stringp -> Result of command, contains the message after OK/ACK
  259. ;;; till the next end of line. No filtering occurs after this
  260. ;;; stage is reached.
  261. ;;; numberp -> Intermediate state, when OK/ACK has been got
  262. ;;; but no end of line found. Then store start pos of the OK/ACK.
  263. ;;; Hope that eol will come in a subsequent packet.
  264. ;;; listp -> In commandlist mode, the list is what is to be sent to the
  265. ;;; server after the commandlist gets over.
  266. ;;; 5 : last-command-result-flag - t if OK, nil if ACK.
  267. ;;; 6 : timeout - The timeout used for replies from server.
  268. ;;; 7 : host - The name of the host used to connect to mpd.
  269. ;;; 8 : port number - The port number of mpd.
  270. ;;; 9 : automode - not of the noauto argument to `mpd-conn-new'.
  271. ;; Don't expose these macros, unless required.
  272. (eval-when-compile
  273. (defmacro _mpdgv () `(aref conn 0))
  274. (defmacro _mpdsv (val) `(aset conn 0 ,val))
  275. (defmacro _mpdgo () `(aref conn 1))
  276. (defmacro _mpdso (val) `(aset conn 1 ,val))
  277. (defmacro _mpdgb () `(aref conn 2))
  278. (defmacro _mpdsb (val) `(aset conn 2 ,val))
  279. (defmacro _mpdgl () `(aref conn 3))
  280. (defmacro _mpdsl (val) `(aset conn 3 ,val))
  281. (defmacro _mpdgs () `(aref conn 4))
  282. (defmacro _mpdss (val) `(aset conn 4 ,val))
  283. (defmacro _mpdgf () `(aref conn 5))
  284. (defmacro _mpdsf (val) `(aset conn 5 ,val))
  285. (defmacro _mpdgt () `(aref conn 6))
  286. (defmacro _mpdst (val) `(aset conn 6 ,val))
  287. (defmacro _mpdgh () `(aref conn 7))
  288. (defmacro _mpdsh (val) `(aset conn 7 ,val))
  289. (defmacro _mpdgp () `(aref conn 8))
  290. (defmacro _mpdsp (val) `(aset conn 8 ,val))
  291. (defmacro _mpdga () `(aref conn 9))
  292. (defmacro _mpdsa (val) `(aset conn 9 ,val)))
  293. ;;;; Sanity check functions.
  294. (defun mpd-connp (conn)
  295. "Return t if CONN is a connection to the mpd server."
  296. (and (vectorp conn) (= (length conn) 10)))
  297. (put 'mpd-connp 'side-effect-free 'error-free)
  298. (defun mpd-conn-strongp (conn)
  299. (and (mpd-connp conn)
  300. (vectorp (_mpdgv))
  301. (= (length (_mpdgv)) mpd-ver-string-length)
  302. (or (not (_mpdgo)) (processp (_mpdgo)))
  303. (stringp (_mpdgb))
  304. (or (not (_mpdgt)) (numberp (_mpdgt)))
  305. (stringp (_mpdgh))
  306. (wholenump (_mpdgp))))
  307. (put 'mpd-conn-strongp 'side-effect-free 'error-free)
  308. (defmacro mpd-assert-mpd-conn (conn) `(mpd-assert-type ,conn mpd-connp))
  309. (defun mpd-assert-idle (conn)
  310. "Assert mpd connection CONN to be free to receive a command."
  311. (mpd-assert-mpd-conn conn)
  312. (or (stringp (_mpdgs))
  313. (error (if (listp (_mpdgs)) "Command list mode has not ended"
  314. "Not done processing current command"))))
  315. (defun mpd-end-conn (conn fmt &rest args)
  316. "Abort mpd conection CONN and signal error.
  317. `format' error message using FMT and ARGS."
  318. (when (_mpdgo)
  319. (delete-process (_mpdgo))
  320. (_mpdso nil))
  321. (signal 'error (list (apply 'format fmt args))))
  322. (defun mpd-end-cmd (conn fmt &rest args)
  323. "Abort current mpd command for connection CONN and signal error.
  324. `format' error message using FMT and ARGS."
  325. (_mpdsf nil)
  326. (_mpdss (apply 'format fmt args))
  327. (signal 'error (list (apply 'format fmt args))))
  328. ;;;; Internal functions.
  329. (defun mpd-process-filter (conn str)
  330. "Filter replies received from the mpd server.
  331. CONN represents the connection object for which the filter is invoked.
  332. STR is the packet received from the server to be processed.
  333. This is an internal function, do not use this in your code."
  334. (cond
  335. ((eq (_mpdgs) t)
  336. (let ((start (length (_mpdgb))) status (case-fold-search nil))
  337. ;; Can be 4 if not for the : as mentioned below
  338. (setq start (if (< start 5) 0 (- start 5)))
  339. (_mpdsb (concat (_mpdgb) str))
  340. ;; The optional : in ACK accomodates the ACK: reply to a failed `add'
  341. ;; given by MPD servers till version 0.10.*
  342. (setq status (string-match "^\\(ACK:? \\|OK\\)"
  343. (substring (_mpdgb) start)))
  344. (if status (setq status (+ status start)))
  345. (or (eq (_mpdgl) t)
  346. (let ((packets
  347. (if (not (equal status 0))
  348. (mpd-get-lines (substring (_mpdgb) 0
  349. (and status (1- status)))))))
  350. (if status
  351. (progn
  352. (_mpdsb (substring (_mpdgb) status))
  353. (setq status 0))
  354. (if (cdr packets)
  355. (let ((last (last packets 2)))
  356. (_mpdsb (cadr last))
  357. (setcdr last nil))
  358. (_mpdsb (car packets))
  359. (setq packets nil)))
  360. (cond
  361. ((functionp (_mpdgl))
  362. (mapcar '(lambda (str)
  363. (funcall (_mpdgl) conn (mpd-parse-line str)))
  364. packets))
  365. ((listp (_mpdgl))
  366. (_mpdsl (nconc (mapcar 'mpd-parse-line packets) (_mpdgl))))
  367. (t (error "Invalid line mode filter")))))
  368. (when status
  369. (_mpdss status)
  370. (mpd-process-filter conn ""))))
  371. ((numberp (_mpdgs))
  372. (_mpdsb (concat (_mpdgb) str))
  373. (let* ((resp-end (_mpdgs))
  374. (bufend (string-match "\n" (_mpdgb) resp-end)))
  375. (when bufend
  376. (_mpdss (substring
  377. (_mpdgb)
  378. ;; The `4` below should be `5` if ACK: is found, see above.
  379. ;; This may then leave a space before the error message.
  380. (+ resp-end (if (_mpdsf (eq (aref (_mpdgb) resp-end)?O)) 2 4))
  381. bufend))
  382. (_mpdsb (substring (_mpdgb) 0 resp-end))
  383. (throw 'mpd-response-over nil))))))
  384. (defun mpd-transact (conn &optional mode input)
  385. "Do an I/O transacton with the mpd server.
  386. Use connection CONN for contacting the server. MODE is as described in
  387. `mpd-execute-command'. Send INPUT, if non-nil, to the server before waiting for
  388. the response. This is an internal function, do not use this in your code."
  389. (_mpdsb "")
  390. (_mpdsl mode)
  391. (_mpdss t)
  392. (unwind-protect
  393. (let ((timeout (_mpdgt)))
  394. (and input (process-send-string (_mpdgo) input))
  395. (while (not (stringp (_mpdgs)))
  396. (catch 'mpd-response-over
  397. (or (accept-process-output
  398. (_mpdgo) (and timeout (/ timeout 1000))
  399. (and timeout (mod timeout 1000)))
  400. ;; This essentially reduces the probability of timeout due to
  401. ;; program suspension to a really small probability.
  402. (accept-process-output (_mpdgo) 0 0)
  403. (error "Timed out getting a response from the mpd server")))))
  404. (unless (stringp (_mpdgs))
  405. (_mpdsf nil)
  406. (_mpdss "")
  407. (_mpdsb ""))))
  408. ;;;; Low level public interface.
  409. (defun mpd-conn-new (host port &optional timeout noauto)
  410. "Construct a mpd connection object from given parameters.
  411. Connections made using this object are made to host HOST at port number PORT.
  412. TIMEOUT is a floating-point value specifying the number of seconds to wait
  413. before giving up waiting for a reply from the server. Unspecified or zero
  414. TIMEOUT correspond to infinite timeout. Set automatic mode if NOAUTO is nil,
  415. hooked automatic function if NOAUTO is a function, and do not set automatic mode
  416. otherwise. See `mpd-set-automatic-mode' for a description of automatic mode."
  417. (or (and (stringp host) (wholenump port) (or (not timeout) (numberp timeout)))
  418. (error "Invalid parameters passed for making new connection"))
  419. (and timeout
  420. (if (= timeout 0)
  421. (setq timeout nil)
  422. (or (> timeout 0) (error "Invalid (negative) timeout value"))))
  423. ;; Declaration conn object structure dependent.
  424. (vector (make-vector mpd-ver-string-length nil) nil
  425. "" nil "" nil (and timeout (floor (* timeout 1000)))
  426. host port (if noauto (if (functionp noauto) noauto nil) t)))
  427. (eval-when-compile
  428. (or (fboundp 'set-process-query-on-exit-flag)
  429. (defmacro set-process-query-on-exit-flag (process flag)
  430. `(process-kill-without-query process flag))))
  431. (defun mpd-connect (conn)
  432. "Connect to the mpd server using the connection object CONN.
  433. The connection object is constructed using `mpd-conn-new'. Note that this
  434. function doesn't need to be explicitly called when the connection is in
  435. automatic mode (the default). Close the connection using `mpd-close-connection'
  436. when you are done."
  437. (let (proc rt welc)
  438. (setq proc (or (open-network-stream "mpd" nil (_mpdgh) (_mpdgp))
  439. (error "Unable to open connection with mpd")))
  440. (and (_mpdgo) (delete-process (_mpdgo)))
  441. (_mpdso proc)
  442. (set-process-query-on-exit-flag proc nil)
  443. (set-process-coding-system proc 'utf-8-unix 'utf-8-unix)
  444. (set-process-filter proc
  445. `(lambda (proc str)
  446. (mpd-process-filter ,conn str)))
  447. (unwind-protect
  448. (mpd-transact conn t)
  449. (or (_mpdgf)
  450. (mpd-end-conn conn "Handshake failed, server returned: %s" (_mpdgs))))
  451. (setq welc (_mpdgs))
  452. (or (string-equal (substring welc 0 (mpd-welcome-length))
  453. mpd-welcome-message)
  454. (mpd-end-conn conn "Process mpd not running on port %d in host \"%s\""
  455. (_mpdgp) (_mpdgh)))
  456. (let ((verlist (split-string (substring welc (mpd-welcome-length))
  457. "[\\.\n]")))
  458. (or (= (length verlist) mpd-ver-string-length)
  459. (mpd-end-conn conn "Error parsing version information from server"))
  460. (let ((i 0))
  461. (while (< i mpd-ver-string-length)
  462. (or (aset (_mpdgv) i (mpd-string-to-number-strict
  463. (pop verlist)))
  464. (mpd-end-conn
  465. conn "Error parsing version information from server"))
  466. (setq i (1+ i))))))
  467. (message "Opened connection with mpd"))
  468. (make-obsolete 'mpd-new-connection "use `mpd-conn-new' to create a connection \
  469. object. If automatic mode is not used, a call to `mpd-connect' might be \
  470. necessary." "2.1")
  471. (defun mpd-new-connection (host port &optional timeout noauto)
  472. "Create a new connection and connect using it to the mpd server.
  473. Return a connection object, which could be used for further transactions with
  474. the server. See `mpd-conn-new' for a description of the parameters. Close the
  475. connection using `mpd-close-connection' when you are done.
  476. This function is deprecated since version 2.1.
  477. Use `mpd-conn-new' to create a connection object.
  478. If automatic mode is not used, a call to `mpd-connect' might be necessary."
  479. (let ((conn (mpd-conn-new host port timeout noauto)))
  480. (mpd-connect conn) conn))
  481. (defun mpd-conn-wakeup (conn)
  482. "Try to ensure that the MPD connection is alive, when in automatic mode."
  483. (mpd-assert-idle conn)
  484. (when (and (not (and (_mpdgo) (eq (process-status (_mpdgo)) 'open)))
  485. (_mpdga) (not (and (functionp (_mpdga))
  486. (funcall (_mpdga) conn 'pre))))
  487. (and (_mpdgo) (message "Connection with mpd broken, attempting reconnect"))
  488. (mpd-connect conn)
  489. (and (functionp (_mpdga)) (funcall (_mpdga) conn 'post))))
  490. (defun mpd-execute-command (conn cmd &optional mode)
  491. "Send the command CMD to the mpd server using CONN.
  492. Append a newline to CMD before sending to the server. Use the value of MODE to
  493. decide how the response of the command is processed. MODE could take one of the
  494. following values:
  495. - A list, usually nil, to which cons-cells got by formatting each line
  496. of the response, except the last one, using `mpd-parse-line', are appended.
  497. The new list thus got is the result of the function.
  498. - t, by which all response before the last line, as a string,
  499. is the result of the function.
  500. - A function, by which each cons-cell, got as described above, is sent to
  501. this function. Two parameters are passed to the function, the connection
  502. object and this cons-cell. An empty string is the result.
  503. Return a cons-cell, whose car is non-nil if the server succeeds, and cdr is the
  504. result as specified in the description of MODE above."
  505. (mpd-conn-wakeup conn)
  506. (mpd-transact conn mode (concat cmd "\n"))
  507. (prog1
  508. (cons (_mpdgf)
  509. ;; Declaration conn object structure dependent.
  510. (aref conn (if (_mpdgf) (if (or (eq (_mpdgl) 't)
  511. (functionp (_mpdgl))) 2 3) 4)))
  512. (_mpdsb "")))
  513. (defun mpd-simple-exec (conn cmd)
  514. "Execute mpd command CMD using CONN ignoring response.
  515. Note that an OK/ACK message still has to come. Return non-nil iff the command
  516. succeeds. `mpd-get-last-error' gives the server error message in case of
  517. failure. See also `mpd-execute-command'."
  518. (if (not (listp (_mpdgs)))
  519. (car (mpd-execute-command conn cmd t))
  520. (_mpdss (cons cmd (_mpdgs))) t))
  521. (defun mpd-close-connection (conn)
  522. "Close the mpd server connection given by CONN."
  523. (mpd-assert-idle conn)
  524. (when (_mpdgo)
  525. (delete-process (_mpdgo))
  526. (_mpdso nil)))
  527. (defvar mpd-inter-conn
  528. (apply 'mpd-conn-new `(,@(mpd-connection-tidy
  529. mpd-interactive-connection-parameters) nil))
  530. "The global mpd connection used for interactive queries.")
  531. (defsubst mpd-get-version (conn)
  532. "Get version information for the mpd server CONN is connected to.
  533. Return a vector of three numbers, for the major, minor and patch levels."
  534. (mpd-assert-mpd-conn conn)
  535. (or (aref (_mpdgv) 0) (mpd-conn-wakeup conn))
  536. (_mpdgv))
  537. (put 'mpd-get-version 'side-effect-free t)
  538. (defsubst mpd-get-last-error (conn)
  539. "Get the last server error message for mpd connection CONN.
  540. Return nil in case of a successful last command."
  541. (mpd-assert-mpd-conn conn)
  542. (and (not (_mpdgf)) (_mpdgs)))
  543. (put 'mpd-get-last-error 'side-effect-free t)
  544. (defsubst mpd-get-connection-timeout (conn)
  545. "Get the timeout of mpd connection CONN.
  546. Return nil if CONN isn't a mpd connection object."
  547. (mpd-assert-mpd-conn conn)
  548. (_mpdgt))
  549. (put 'mpd-get-connection-timeout 'side-effect-free t)
  550. ;;;###autoload
  551. (defun mpd-set-connection-timeout (conn timeout)
  552. "Set the timeout of mpd connection object CONN to TIMEOUT.
  553. See also `mpd-new-connection'."
  554. (interactive
  555. (list mpd-inter-conn
  556. (string-to-number
  557. (read-string
  558. "Connection timeout (None): "
  559. (let ((timeout (mpd-get-connection-timeout mpd-inter-conn)))
  560. (and timeout (number-to-string timeout)))))))
  561. (if (or (not (mpd-connp conn)) (and timeout (not (numberp timeout))))
  562. (error "Invalid parameters used to set connection timeout")
  563. (and (= timeout 0) (setq timeout nil))
  564. (_mpdst timeout)))
  565. (defsubst mpd-get-automatic-mode (conn)
  566. "Return the automatic mode value for mpd connection CONN.
  567. See `mpd-set-automatic-mode' for a description of automatic mode. Return value
  568. is like the MODE parameter of that function."
  569. (mpd-assert-mpd-conn conn)
  570. (_mpdga))
  571. (put 'mpd-get-automatic-mode 'side-effect-free t)
  572. (defsubst mpd-set-automatic-mode (conn mode)
  573. "Set the automatic mode for mpd connection CONN.
  574. Set automatic mode iff MODE is non-nil. Set to hooked automatic mode if the
  575. non-nil MODE is a function.
  576. The connection is said to be in automatic mode if it connects on demand (usually
  577. as a result of a server request using the connection) and takes care of
  578. reconnecting whenever the connection gets broken. The automatic mode could be
  579. hooked by specifying a function. In this case, the function is called with two
  580. parameters, the connection object and the second parameter being the symbol 'pre
  581. or 'post. The function is called with 'pre when reconnection is needed. If the
  582. function returns a non-nil value, it is assumed that the function has done the
  583. reconnection. Else, the library reconnects by itself, and the function is once
  584. again called after that, with the parameter 'post (Return value is ignored)."
  585. (mpd-assert-mpd-conn conn)
  586. (_mpdsa mode))
  587. (make-obsolete 'mpd-get-reconnectible 'mpd-get-automatic-mode "2.1")
  588. (defalias 'mpd-get-reconnectible 'mpd-get-automatic-mode
  589. "Return t if CONN reconnects when its mpd connection breaks.
  590. This function is deprecated since version 2.1.
  591. Use `mpd-get-automatic-mode' instead.")
  592. (make-obsolete 'mpd-make-reconnectible 'mpd-set-automatic-mode "2.1")
  593. (defsubst mpd-make-reconnectible (conn &optional noreconn)
  594. "Make CONN reconnectible when its mpd connection breaks.
  595. Unset the reconnectibility on non-nil prefix arg NORECONN.
  596. This function is deprecated since version 2.1.
  597. Use `mpd-set-automatic-mode' instead."
  598. (mpd-set-automatic-mode conn (not noreconn)))
  599. (defun mpd-force-accept-command (conn)
  600. "Force the mpd connection CONN to accept the next command.
  601. *WARNING* DON'T use this unless you are really desperate. Shelf this off for
  602. debugging purposes. Normally, the package should signal correctly whether it is
  603. safe to receive the next command. Doing this could mean losing out on the
  604. response of the current command, and worse, the response of the current command
  605. could creep into the next one."
  606. (if (mpd-command-list-mode-p conn)
  607. (error "Command list mode has not ended")
  608. (_mpdsf nil)
  609. (_mpdss "")
  610. (_mpdsb "")))
  611. (defun mpd-command-list-begin (conn)
  612. "Start the command-list mode for CONN.
  613. Only commands that can be used with `mpd-simple-exec' are allowed in
  614. command-list mode. Commands can be issued as they are usually done.
  615. Always return t, as the commands are queued up and not sent to the server.
  616. `mpd-command-list-end' ends the command-list and executes the list built up."
  617. (mpd-assert-idle conn)
  618. (_mpdsf nil) ; FIXME: Why is this here ??
  619. (_mpdss nil))
  620. (defun mpd-command-list-mode-p (conn)
  621. "Return non-nil if mpd connection CONN is in command list mode."
  622. (mpd-assert-mpd-conn conn)
  623. (listp (_mpdgs)))
  624. (put 'mpd-command-list-mode-p 'side-effect-free t)
  625. (defun mpd-command-list-end (conn)
  626. "End command-list mode for CONN.
  627. This function needs to be preceded by a call to `mpd-command-list-begin'."
  628. (or (mpd-command-list-mode-p conn)
  629. (error "The connection is not in command-list mode"))
  630. (let (str)
  631. (setq str (concat "command_list_begin\n"
  632. (mapconcat '(lambda (item) item) (nreverse (_mpdgs)) "\n")
  633. "\ncommand_list_end"))
  634. (_mpdss "")
  635. (mpd-simple-exec conn str)))
  636. (defun mpd-connection-status (conn)
  637. "Get the status of mpd connection CONN.
  638. Return one of 'busy for being in the midst of a request, 'ready for the ready
  639. state, and 'command-list to indicate being in command-list mode."
  640. (mpd-assert-mpd-conn conn)
  641. (cond
  642. ((listp (_mpdgs)) 'command-list)
  643. ((stringp (_mpdgs)) 'ready)
  644. (t 'busy)))
  645. (put 'mpd-connection-status 'side-effect-free t)
  646. (defmacro with-mpd-timeout-disabled (&rest args)
  647. `(progn
  648. (mpd-assert-mpd-conn conn)
  649. (let ((timeout (_mpdgt)))
  650. (unwind-protect
  651. (progn
  652. (_mpdst nil)
  653. ,@args)
  654. (_mpdst timeout)))))
  655. (put 'with-mpd-timeout-disabled 'lisp-indent-function 0)
  656. (def-edebug-spec with-mpd-timeout-disabled (body))
  657. ;;; High level public interface helper functions.
  658. ;;; Silence the compiler.
  659. (defvar mpd-song-receiver)
  660. (defvar foreach)
  661. (defvar mpd-song-receiver-args)
  662. (defun mpd-song-receiver (conn cell)
  663. "Handle song data response from the mpd server.
  664. See `mpd-execute-command' for a description of response handlers.
  665. This is an internal function, do not use this in your code."
  666. (let ((key (car cell)))
  667. (unless (or (string-equal key "directory")
  668. (string-equal key "playlist"))
  669. (when (string-equal key "file")
  670. (when (plist-get mpd-song-receiver 'file)
  671. (setq foreach (apply 'mpd-seq-add mpd-song-receiver
  672. foreach mpd-song-receiver-args)))
  673. (setq mpd-song-receiver nil))
  674. (setq mpd-song-receiver
  675. (plist-put mpd-song-receiver (intern key)
  676. (if (member key '("Time" "Pos" "Id"))
  677. (string-to-number (cdr cell)) (cdr cell)))))))
  678. (defun mpd-get-songs (conn cmd &optional foreach)
  679. "Get details of songs from the mpd server using connection CONN.
  680. Use command CMD to get the songs. Call function FOREACH, if specified, for each
  681. song, with the song provided as the argument. Return list of all songs if
  682. FOREACH is not specified and FOREACH otherwise. When a list is returned, each
  683. element of the list is a property list, some known keys being `file', `Pos' and
  684. `Id'. The last two keys, along with `Time' if present, have integers as their
  685. value. `Time' refers to the total length of the song. `Pos' and `Id' are present
  686. only when the song retrieved is a part of a playlist. Other song tag data might
  687. be present as well."
  688. (or (functionp foreach) (setq foreach nil))
  689. (let (mpd-song-receiver mpd-song-receiver-args)
  690. (mpd-execute-command conn cmd 'mpd-song-receiver)
  691. (and mpd-song-receiver
  692. (setq foreach (mpd-seq-add mpd-song-receiver foreach)))
  693. (mpd-safe-nreverse foreach)))
  694. (defun mpd-make-cmd-concat (cmd arg &optional normal-nil)
  695. "Make mpd command string using command CMD and argument ARG.
  696. ARG could be a string or a list of strings. If NORMAL-NIL is non-nil, do nothing
  697. if ARG is nil, and return CMD for nil NORMAL-NIL and ARG. Use command list
  698. mode implicitly for lists. Sanitize arguments before composition.
  699. This is an internal function, do not use this in your code."
  700. (cond
  701. ((not (or arg normal-nil)) cmd)
  702. ((listp arg)
  703. (concat "command_list_begin\n"
  704. (mapconcat '(lambda (item) (concat cmd " " (mpd-safe-string item)))
  705. arg "\n") "\ncommand_list_end"))
  706. (t (concat cmd " " (mpd-safe-string arg)))))
  707. (put 'mpd-make-cmd-concat 'side-effect-free t)
  708. (defun mpd-make-cmd-format (cmd validate arg1 &optional arg2)
  709. "Make mpd command string with format CMD and arguments ARG1, ARG2.
  710. ARG1/ARG2 could be a list of arguments each. Use command list mode implicitly
  711. for lists. Send each of the arguments pairs to VALIDATE before composition.
  712. This is an internal function, do not use this in your code."
  713. (if (listp arg1)
  714. (let ((tail2 arg2))
  715. (and arg2 (or (= (length arg1) (length arg2))
  716. (error "Argument lists are of unequal lengths")))
  717. (concat "command_list_begin\n"
  718. (mapconcat '(lambda (item)
  719. (and validate (funcall validate item (car tail2)))
  720. (prog1
  721. (format cmd item (car tail2))
  722. (setq tail2 (cdr tail2)))) arg1 "\n")
  723. "\ncommand_list_end"))
  724. (and validate (funcall validate arg1 arg2))
  725. (format cmd arg1 arg2)))
  726. ;;; Helper functions for interactive display.
  727. (defun mpd-line-to-buffer (str)
  728. "Insert STR as a line to the mpd display buffer."
  729. (with-current-buffer (get-buffer-create "*mpd-display*")
  730. (with-mpd-free-buffer
  731. (goto-char (point-max))
  732. (insert (concat str "\n"))
  733. (display-buffer (current-buffer)))))
  734. (defsubst mpd-separator-line (&optional num)
  735. "Make a separator line for insertion to the mpd display buffer.
  736. The number of columns used is 80, unless specified using NUM."
  737. (propertize (concat (make-string (or num 80) ?*) "\n")
  738. 'face 'mpd-separator-face))
  739. (defun mpd-init-buffer (&optional str1 str2 str3)
  740. "Initialize the mpd display buffer using strings STR1, STR2, STR3.
  741. Layout as follows:
  742. STR1 if non-empty
  743. ***...*** if STR1 is non-nil
  744. STR2 if non-empty
  745. ***...*** if STR2 is non-nil
  746. STR3 if non-empty"
  747. (let ((max (max (length str1) (length str2) (length str3))))
  748. (with-current-buffer (get-buffer-create "*mpd-display*")
  749. (erase-buffer)
  750. (insert
  751. (concat "\n" (and str1 (not (string-equal str1 ""))
  752. (propertize (concat str1 "\n") 'face 'mpd-header-face))
  753. (and str1 (mpd-separator-line max))
  754. (and str2 (propertize (concat str2 "\n") 'face 'mpd-header-face))
  755. (and str3 (mpd-separator-line max))
  756. (and str3 (not (string-equal str3 ""))
  757. (propertize (concat str3 "\n")
  758. 'face 'mpd-header-face)) "\n")))))
  759. (defun mpd-render-field (desc val &optional nosep)
  760. "Format to a colorized line of form \"\\nDESC: VAL\".
  761. Include the separating colon unless NOSEP is non-nil."
  762. (and val (not (string-equal val ""))
  763. (concat (propertize desc 'face 'mpd-first-field-face)
  764. (and (not nosep) ": ")
  765. (propertize val 'face 'mpd-second-field-face) "\n")))
  766. (put 'mpd-render-field 'side-effect-free t)
  767. (defun mpd-render-plist (plist table)
  768. "Display property list PLIST as a pretty table in the display buffer.
  769. TABLE is a list of entries is used to translate between keys and strings
  770. displayed. Each entry of the table is a list of the key symbol, the
  771. corresponding string to display, and optionally a function to call with the
  772. value to get the string to display as the value. If the string to display is
  773. nil, then those keys are ignored and not displayed at all."
  774. (when plist
  775. (let ((ptr plist) (str "") key value entry trans filter)
  776. (while ptr
  777. (setq key (car ptr))
  778. (setq value (cadr ptr))
  779. (when (and key value)
  780. (setq entry (assq key table))
  781. (setq trans (if entry (cadr entry) (symbol-name key)))
  782. (setq filter (cadr (cdr entry)))
  783. (when trans
  784. (setq str (concat str (mpd-render-field
  785. (format "%-13s" trans)
  786. (format "%s" (if filter
  787. (funcall filter value)
  788. value)))))))
  789. (setq ptr (cddr ptr)))
  790. (mpd-line-to-buffer (concat str "\n" (mpd-separator-line))))))
  791. (defconst mpd-display-song-key-table
  792. '((Time "Length" (lambda (time) (format "%s seconds" time)))
  793. (file "Filename")
  794. (Pos)
  795. (Id)))
  796. (defsubst mpd-display-song (song)
  797. "Display mpd song data SONG in the mpd display buffer."
  798. (mpd-render-plist song mpd-display-song-key-table))
  799. (defun mpd-display-playlist-item (title num)
  800. "Display playlist item with TITLE and index NUM in mpd buffer."
  801. (mpd-line-to-buffer
  802. (concat (propertize (format "%4d " (1+ num)) 'face 'mpd-first-field-face)
  803. (propertize title 'face 'mpd-second-field-face))))
  804. (defun mpd-display-dir-info (item type)
  805. "Display mpd directory information to the mpd display buffer."
  806. (if (eq type 'file)
  807. (mpd-display-song item)
  808. (mpd-line-to-buffer
  809. (concat (mpd-render-field
  810. (if (eq type 'playlist) "Playlist " "Directory ") item)
  811. "\n" (mpd-separator-line)))))
  812. (defun mpd-display-dir-listing (item dir)
  813. "Display mpd directory listing to the mpd display buffer."
  814. (mpd-line-to-buffer
  815. (concat (mpd-render-field (if dir "Directory " "File " ) item)
  816. "\n" (mpd-separator-line))))
  817. (defsubst mpd-display-bullet (str)
  818. "Display a bulleted line to the mpd display buffer."
  819. (mpd-line-to-buffer (mpd-render-field " o " (format "%s" str) t)))
  820. (defconst mpd-display-output-key-table
  821. '((outputid "ID")
  822. (outputname "Name")
  823. (outputenabled "Enabled"
  824. (lambda (enabled)
  825. (cond
  826. ((eq enabled t) "Yes")
  827. ((eq enabled nil) "No")
  828. (t enabled))))))
  829. (defsubst mpd-display-output (output)
  830. "Display mpd output OUTPUT in the mpd display buffer."
  831. (mpd-render-plist output mpd-display-output-key-table))
  832. (defun mpd-read-item (prompt &optional default zero allowneg)
  833. "Read a number from the minibuffer.
  834. Display PROMPT as the prompt string prefix. Append the DEFAULT value,
  835. if present, in brackets. Return the number read. Unless ZERO is non-nil,
  836. add default value by one before operation, and decrement number read
  837. by 1 before returning. If ALLOWNEG is non-nil, allow negative numbers."
  838. (let (num str)
  839. (and default (setq str (number-to-string (if zero default (1+ default)))))
  840. (setq
  841. num (mpd-string-to-number-strict
  842. (read-string (concat prompt (if default (concat " (" str ")")) ": ")
  843. nil nil str) allowneg))
  844. (if (and num (not zero)) (1- num) num)))
  845. ;;;; High level public interface.
  846. ;;; These functions require response, and hence cannot be queued by using the
  847. ;;; command-line mode.
  848. (defun mpd-status-receiver (lsym cell)
  849. "Handle response for the 'status' command to the mpd server.
  850. See `mpd-execute-command' for a description of response handlers.
  851. This is an internal function, do not use this in your code."
  852. (let ((sym (car cell)))
  853. (cond
  854. ((member sym '("volume" "repeat" "random" "playlist" "playlistlength"
  855. "bitrate" "song" "songid" "xfade" "updating_db"))
  856. (put lsym (intern sym) (string-to-number (cdr cell))))
  857. ((string-equal sym "state")
  858. (and (member (cdr cell) '("play" "pause" "stop"))
  859. (put lsym (intern sym) (intern (cdr cell)))))
  860. ((string-equal sym "time")
  861. (when (string-match "^\\([0-9]*\\):\\([0-9]*\\)$" (cdr cell))
  862. (put lsym 'time-elapsed (string-to-number (match-string 1 (cdr cell))))
  863. (put lsym 'time-total (string-to-number (match-string 2 (cdr cell))))))
  864. ((string-equal sym "audio")
  865. (when (string-match "^\\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)$" (cdr cell))
  866. (put lsym 'sample-rate (string-to-number (match-string 1 (cdr cell))))
  867. (put lsym 'bits-per-sample
  868. (string-to-number (match-string 2 (cdr cell))))
  869. (put lsym 'channels (string-to-number (match-string 3 (cdr cell))))))
  870. ;; currently only "error"
  871. (t (put lsym (intern sym) (cdr cell))))))
  872. (defun mpd-get-status (conn)
  873. "Get status of the mpd server, using connection CONN.
  874. Return a property list, the known keys being `volume', `repeat', `random',
  875. `playlist', `playlistlength', `bitrate', `song', `songid', `xfade', `state',
  876. `time-elapsed', `time-total', `sample-rate', `bits-per-sample', `channels'
  877. and `error'. Other keys might be present in the plist depending on the version
  878. of MPD used. Some of the less obvious descriptions are:
  879. +-----------+------------------------------------------------------------------+
  880. | playlist |The playlist version number - a 'checksum' for the current |
  881. | |playlist, guaranteed to change after a change in the playlist. |
  882. +-----------+------------------------------------------------------------------+
  883. | xfade |The crossfade time in seconds between two songs occurs. This value|
  884. | |could be zero in case of no crossfade. |
  885. +-----------+------------------------------------------------------------------+
  886. | song |Position of the current song in the playlist. |
  887. +-----------+------------------------------------------------------------------+
  888. | songid |Song ID of the current song in the playlist. |
  889. +-----------+------------------------------------------------------------------+
  890. | state |Could be one of 'play, 'pause or 'stop |
  891. +-----------+------------------------------------------------------------------+
  892. |updating_db|The updating job id, displayed when there is a update taking |
  893. | |place. |
  894. +-----------+------------------------------------------------------------------+
  895. | error |A description of the error, if one occurs. Could be nil, in case |
  896. | |there is no error. |
  897. +-----------+------------------------------------------------------------------+
  898. All fields except error and state are whole numbers. `repeat' and `random' are
  899. in addition, bi-state variables (0/1)"
  900. (setplist 'mpd-get-status-local-sym nil)
  901. (mpd-execute-command
  902. conn "status" '(lambda (conn cell)
  903. (mpd-status-receiver 'mpd-get-status-local-sym cell)))
  904. (symbol-plist 'mpd-get-status-local-sym))
  905. (defun mpd-stats-receiver (lsym cell)
  906. "Handle response for the 'stats' command to the mpd server.
  907. See `mpd-execute-command' for a description of response handlers.
  908. This is an internal function, do not use this in your code."
  909. (let ((sym (car cell)))
  910. (cond
  911. ((member sym '("artists" "albums" "songs" "uptime"
  912. "playtime" "db_playtime" "db_update"))
  913. (put lsym (intern sym) (string-to-number (cdr cell))))
  914. (t (put lsym (intern sym) (cdr cell))))))
  915. (defun mpd-get-stats (conn)
  916. "Get statistics for the mpd server connected using CONN.
  917. Return a property list, the known keys being `artists', `albums', `songs',
  918. `db_playtime' corresponding to the number of artists, albums and songs and the
  919. total time in seconds of all songs in the database respectively; `db_update',
  920. the time stamp of the last update to the database; `playtime', the total time
  921. for which music has been played in seconds and `uptime', the server uptime,
  922. in seconds as well. Other keys might be present in the plist depending on the
  923. version of MPD used."
  924. (setplist 'mpd-get-stats-local-sym nil)
  925. (mpd-execute-command
  926. conn "stats" '(lambda (conn cell)
  927. (mpd-stats-receiver 'mpd-get-stats-local-sym cell)))
  928. (symbol-plist 'mpd-get-stats-local-sym))
  929. ;;;###autoload
  930. (defun mpd-get-playlist (conn &optional foreach)
  931. "Get all songs in the current playlist managed by the mpd server.
  932. CONN and FOREACH are as in `mpd-get-songs'."
  933. (interactive (progn (mpd-init-buffer "" "Current MPD Playlist" "")
  934. (list mpd-inter-conn 'mpd-display-playlist-item)))
  935. (or (functionp foreach) (setq foreach nil))
  936. (mpd-execute-command
  937. conn "playlist" '(lambda (conn cell)
  938. (setq foreach (mpd-elt-add
  939. (cdr cell) foreach
  940. (string-to-number (car cell))))))
  941. (mpd-safe-nreverse foreach))
  942. ;;;###autoload
  943. (defun mpd-get-playlist-entry (conn &optional item foreach use-id)
  944. "Get song data for entr(y/ies) ITEM in the current mpd playlist.
  945. CONN, FOREACH and the return value are as in `mpd-get-songs'.
  946. ITEM is the item position/id or a list of it. Note that ITEM as nil fetches
  947. data for all entries in the current playlist rather than not doing anything.
  948. Interpret ITEM as song id(s) iff USE-ID is non-nil."
  949. (interactive
  950. (let ((item (mpd-read-item
  951. "Enter item number"
  952. (plist-get (mpd-get-status mpd-inter-conn) 'song))))
  953. (mpd-init-buffer "" (format "MPD Playlist Item # %d" (1+ item)) "")
  954. (list mpd-inter-conn item 'mpd-display-song)))
  955. (setq use-id (if use-id "playlistid" "playlistinfo"))
  956. (mpd-get-songs
  957. conn (if item
  958. (mpd-make-cmd-format
  959. (concat use-id " %d")
  960. '(lambda (item item2) (mpd-assert-wholenump item))
  961. item) use-id) foreach))
  962. ;;;###autoload
  963. (defun mpd-get-current-song (conn &optional foreach)
  964. "Get song data for the current song in mpd.
  965. CONN and FOREACH are as in `mpd-get-songs'. Return FOREACH if specified, and
  966. the current song (see `mpd-get-songs' for how a song is represented) otherwise."
  967. (interactive (progn (mpd-init-buffer "" "Current MPD Song" "")
  968. (list mpd-inter-conn 'mpd-display-song)))
  969. (setq foreach (mpd-get-songs conn "currentsong" foreach))
  970. (or (functionp foreach) (setq foreach (car foreach))) foreach)
  971. (defun mpd-get-playlist-changes (conn version &optional foreach nometa)
  972. "Get a list of changed song entries in the current mpd playlist.
  973. CONN, FOREACH and the return value are as in `mpd-get-songs'.
  974. Calculate the change between the current playlist and the playlist
  975. with version number VERSION. Do not fetch complete metadata (only position and
  976. song id is returned for each song) if NOMETA is non-nil."
  977. (mpd-assert-numberp version)
  978. (mpd-get-songs conn (format "%s %d" (if nometa "plchangesposid" "plchanges")
  979. version) foreach))
  980. ;;;###autoload
  981. (defun mpd-get-directory-songs (conn &optional directory foreach)
  982. "Get all songs in a directory of the mpd database.
  983. CONN, FOREACH and the return value are as in `mpd-get-songs'.
  984. DIRECTORY is the relative directory path wrt the database root.
  985. DIRECTORY could be a list as well, the action then corresponds to all songs
  986. in all the directories. Note that the nil value for DIRECTORY corresponds
  987. to the database toplevel rather than an empty list."
  988. (interactive
  989. (let ((str (read-string "Enter relative directory: ")))
  990. (progn (mpd-init-buffer "" (concat "Songs in directory " str) "")
  991. (list mpd-inter-conn str 'mpd-display-song))))
  992. (mpd-get-songs
  993. conn (mpd-make-cmd-concat "listallinfo" directory) foreach))
  994. ;;;###autoload
  995. (defun mpd-get-directory-info (conn &optional directory foreach)
  996. "Get directory info for DIRECTORY in the mpd database.
  997. Use CONN as the mpd connection for the purpose and call function FOREACH,
  998. if specified, with each information field. The arguments passed to FOREACH is a
  999. song object, directory string or playlist string and one of the symbols 'file,
  1000. 'playlist or 'directory describing the data sent. DIRECTORY could be a list as
  1001. well, the action then corresponds information for all the directories. Note that
  1002. a nil for DIRECTORY corresponds to the database toplevel rather than an empty
  1003. list. Return FOREACH, if non-nil; else a vector of three elements: a list of
  1004. songs in the directory (see `mpd-get-songs' for how a song is represented),
  1005. a list of playlists and a list of subdirectories."
  1006. (interactive
  1007. (let ((str (read-string "Enter relative directory: ")))
  1008. (progn (mpd-init-buffer "" (concat "Information on directory " str) "")
  1009. (list mpd-inter-conn str 'mpd-display-dir-info))))
  1010. (or (functionp foreach) (setq foreach nil))
  1011. (let (filemode (pl foreach) (dir foreach)
  1012. (mpd-song-receiver-args '(file)) mpd-song-receiver)
  1013. (mpd-execute-command
  1014. conn (mpd-make-cmd-concat "lsinfo" directory)
  1015. '(lambda (conn cell)
  1016. (if (string-equal (car cell) "directory")
  1017. (setq dir (mpd-elt-add (cdr cell) dir 'directory))
  1018. (if (string-equal (car cell) "playlist")
  1019. (setq pl (mpd-elt-add (cdr cell) pl 'playlist))
  1020. (mpd-song-receiver conn cell)))))
  1021. (and mpd-song-receiver
  1022. (setq foreach (mpd-seq-add mpd-song-receiver foreach 'file)))
  1023. (if (functionp foreach) foreach
  1024. (vector (nreverse foreach) (nreverse pl) (nreverse dir)))))
  1025. ;;;###autoload
  1026. (defun mpd-list-directory-recursive (conn foreach &optional directory)
  1027. "Get the file-directory hierarchy of a directory in the mpd database.
  1028. Use CONN for the connection and use function FOREACH to report each entry,
  1029. along with a non-nil second argument if the entry is a directory. DIRECTORY
  1030. could be a list as well, the action then corresponds to listing of all the
  1031. directories. Note that the nil value for DIRECTORY corresponds to the
  1032. database toplevel rather than an empty list."
  1033. (interactive
  1034. (let ((str (read-string "Enter relative directory: ")))
  1035. (progn
  1036. (mpd-init-buffer "" (concat "Recursive listing of directory " str) "")
  1037. (list mpd-inter-conn 'mpd-display-dir-listing str))))
  1038. (mpd-assert-type foreach functionp)
  1039. (mpd-execute-command conn (mpd-make

Large files files are truncated, but you can click here to view the full file