PageRenderTime 76ms CodeModel.GetById 3ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 1ms

Lisp | 1639 lines | 1288 code | 169 blank | 182 comment | 127 complexity | e164f5f7bef8f40a413994ec251ad7e7 MD5 | raw file
   1;;; LIBMPDEE.EL --- Client end library for mpd, a music playing daemon
   3;; Copyright (C) 2004, 2005, 2006 R.Ramkumar
   5;; Author: 	R.Ramkumar <>
   6;; Created: 	10 May 2004
   7;; Version: 	2.1
   8;; Keywords:	music, mpd
  10;; This file is *NOT* part of GNU Emacs
  12;; This program is free software; you can redistribute it and/or modify
  13;; it under the terms of the GNU General Public License as published by
  14;; the Free Software Foundation; either version 2, or (at your option)
  15;; any later version.
  17;; This program is distributed in the hope that it will be useful,
  18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  20;; GNU General Public License for more details.
  22;; A copy of the GNU General Public License can be obtained from this
  23;; program's author (send electronic mail to
  24;; or from the Free Software Foundation, Inc.,
  25;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  27;;; Commentary:
  29;; This package is a client end library to the wonderful music playing daemon by
  30;; name mpd. Hence, an obvious prerequisite for this package is mpd itself,
  31;; which you can get at For those who haven't heard of
  32;; mpd, all I can say is that it is definitely worth a try, and is surely
  33;; different from the rest. This package is aimed at developers, and though
  34;; there are some interactive functions, much of the power of the library lies
  35;; away from the user.
  37;; This package started off to implement libmpdclient.c, which came with mpd, in
  38;; elisp. But as it stands of now, this package is not an exact translation.
  39;; Notable amongst the deviations are -
  41;;      - This package contains quite a bit of higher level functionality as
  42;;      compared with the original. An action or a query needs only a single
  43;;      call and the library user can choose to get either the raw response, the
  44;;      formatted response, or hook on a callback for each logical token of the
  45;;      response. However, dig deeper, and you will find the lower level
  46;;      functionality available as well.
  47;;      - The error throwing scheme consistent with what is expected of elisp
  48;;      programs.
  49;;      - Command list mode is transparent in most cases, as wherever
  50;;      appropriate, functions taking arguments can accept either a single item
  51;;      or a list of it for each argument.
  52;;      - Apart from this, command list functionality is limited to actions
  53;;      rather than queries, as it is anyway not that easy to parse out the
  54;;      individual responses from command-list queries (it is firstly possible
  55;;      only from 0.11, which allows for a list_OK to be added at the end of
  56;;      response for each command in the list).
  57;;      - command_list_ok_begin isn't implemented. It is still possible to
  58;;      explicitly use "command_list_(ok)begin\n..\ncommand_list_end" for
  59;;      `mpd-execute-command' and get the response tokens for queries. A better
  60;;      interface may be available in the future.
  61;;      - There is a small interactive interface as well, but this is
  62;;      intentionally incomplete. The point is that this exists only to the
  63;;      extent of adding an interactive part to the functions, without modifying
  64;;      the functions per se.
  66;; Most of the functions below require a connection object as an argument, which
  67;; you can create by a call to `mpd-conn-new'. The recommended way to use
  68;; customization to choose parameters for mpd connections is to use the widget
  69;; `mpd-connection'.
  71;; As this package caters to developers, it should be helpful to browse the file
  72;; in order for atleast the functions and the documentation. The file is well
  73;; structured and documented, so go for it. The impatient could do a selective
  74;; display to 3 (C-u 3 C-x $) before proceeding.
  76;;; Installation:
  78;; Put this file somewhere on your load-path. Then, you could use
  79;; (require 'libmpdee) whenever the services of this package are needed.
  81;; Parameters used for the interactive calls can be customized in the group mpd
  82;; Use:
  83;;         M-x customize-group mpd
  84;; to change the values to your liking.
  87;;; History: (See the SVN logs/ChangeLog for the list of all changes)
  89;; v2.1
  90;; Introducing automatic mode with hooking, for connections.
  91;; See `mpd-set-automatic-mode'.
  93;; v2.0
  94;; The interface has changed since version 1 of this library and there is no
  95;; backward compatibility. This change applies to functions which returned
  96;; vector whose descriptions were given `*-data' variables. Such functions have
  97;; been modified to return property lists, whose keys now describe the values
  98;; returned. This should hopefully be a much more scalable representation.
 100;;; Code:
 102(defvar libmpdee-version "2.1"
 103  "libmpdee version information.")
 105;;;; User serviceable variable(s).
 107(require 'custom)
 108(require 'wid-edit)
 110(defun widget-mpd-format-handler (widget esc)
 111  "Widget format handler for the MPD connection widget."
 112  (cond
 113   ((eq esc ?l)
 114    (widget-create-child-and-convert widget 'documentation-string "\
 115This variable specifies a MPD connection.
 116The value is a list of the mpd host name, port number, and the timeout for
 117server replies. See `mpd-conn-new' for more details.")
 118    (insert "To know more about libmpdee, read ")
 119    (widget-create 'emacs-commentary-link :tag "this" "libmpdee"))
 120   (t (funcall (widget-get (widget-convert 'lazy) :format-handler)
 121	       widget esc))))
 123(defun mpd-connection-tidy (val)
 124  "Initialize unset parameters for a MPD connection.
 125Replace parameters in the MPD connection VAL with sane defaults and return."
 126  (or val (setq val '(nil nil nil)))
 127  (let ((val val))
 128    (and (listp val)
 129	 (or (car val)
 130	     (setcar val (or (getenv "MPD_HOST") "localhost")))
 131	 (setq val (cdr val))
 132	 (or (car val) (setcar val 6600))
 133	 (setq val (cdr val))
 134	 (or (car val) (setcar val 10.0)))) val)
 136(define-widget 'mpd-connection 'lazy
 137  "A widget for a MPD connection."
 138  :tag "MPD Connection"
 139  :format "%{%t%}:\n\n%l\n\n%v"
 140  :format-handler 'widget-mpd-format-handler
 141  :value-to-internal '(lambda (wid val) (mpd-connection-tidy val))
 142  :type '(list :format "%v"
 143	       (string :format "%t: %v\n" :tag "Hostname" :size 15)
 144	       (integer :format "%t:     %v\n" :tag "Port" :size 5
 145			:match (lambda (widget value) (> value 0))
 146			:type-error "Port must be a natural number")
 147	       (float :format "%t:  %v\n\n" :tag "Timeout" :size 10
 148		      :match (lambda (widget value) (> value 0))
 149		      :type-error "Timeout must be a positive number")))
 151(defgroup mpd nil
 152  "The client end library for MPD, the music playing daemon."
 153  :group 'external :group 'multimedia
 154  :link '(emacs-commentary-link "libmpdee"))
 156(defcustom mpd-db-root (getenv "MPD_DB_ROOT")
 157  "*MPD database directory root."
 158  :type 'directory :group 'mpd)
 160(defcustom mpd-interactive-connection-parameters (mpd-connection-tidy nil)
 161  "Parameters for the interactive mpd connection.
 162These determine the connection used by interactive functions in `libmpdee'."
 163  :type 'mpd-connection :group 'mpd)
 165(defface mpd-separator-face '((((background dark)) (:foreground "lightyellow"))
 166			      (((background light)) (:foreground "darkgreen")))
 167  "Face for display of separator lines in interactive mpd queries."
 168  :group 'mpd)
 170(defface mpd-header-face '((((background dark)) (:foreground "gold"))
 171			   (((background light)) (:foreground "brown")))
 172  "Face for display of header lines in interactive mpd queries."
 173  :group 'mpd)
 175(defface mpd-first-field-face '((((background dark)) (:foreground "cyan"))
 176				(((background light)) (:foreground "orange")))
 177  "Face for display of the first field in interactive mpd queries.
 178Most lines in interactive displays are split into two fields."
 179  :group 'mpd)
 181(defface mpd-second-field-face
 182  '((((background dark)) (:foreground "lightgreen"))
 183    (((background light)) (:foreground "blue")))
 184  "Face for display of the second field in interactive mpd queries.
 185Most lines in interactive displays are split into two fields."
 186  :group 'mpd)
 188;;;; Constants and internal variables.
 190(eval-and-compile (defconst mpd-welcome-message " MPD "))
 191(defmacro mpd-welcome-length () (length mpd-welcome-message))
 192(defconst mpd-ver-string-length 3)
 194;;;; Package independent helper functions.
 196(defmacro mpd-assert-type (obj func)
 197  "Ensure that OBJ succeeds on type checking with predicate FUNC.
 198The function emits a \"Wrong type argument\" signal on failure.
 199Note that the arguments are evalled twice in this process."
 200  `(or (,func ,obj) (signal 'wrong-type-argument (list (quote ,func) ,obj))))
 202(defmacro mpd-assert-string (obj) `(mpd-assert-type ,obj stringp))
 203(defmacro mpd-assert-wholenump (obj) `(mpd-assert-type ,obj wholenump))
 204(defmacro mpd-assert-numberp (obj) `(mpd-assert-type ,obj numberp))
 206(defun mpd-string-to-number-strict (str &optional allowneg)
 207  "Convert string STR to a number strictly.
 208Return nil if there are any unmatched characters.
 209Allow negative numbers if ALLOWNEG is non-nil."
 210  (let ((num (string-to-number str)))
 211    (and (if allowneg (numberp num) (wholenump num))
 212	 (string-equal str (number-to-string num)) num)))
 213(put 'mpd-string-to-number-strict 'side-effect-free t)
 215(defun mpd-get-lines (str)
 216  "Split STR into newline separated lines.
 217Differ from `split-string' in that tokens are created
 218for leading and trailing newlines."
 219  (let ((packets (split-string str "\n")))
 220    (when packets
 221      (if (= (aref str 0) ?\n)
 222	  (setq packets (cons "" packets)))
 223      (if (= (aref str (1- (length str))) ?\n)
 224	  (nconc packets (list ""))
 225	packets))))
 227;;; Modified from the pcomplete package.
 228(defun mpd-sort-uniq-list (l lessp eqp)
 229  "Sort and remove multiples in list L.
 230Use LESSP and EQP as predicates for the \"lesser\" and \"equal\" operations."
 231  (setq l (sort l lessp))
 232  (let ((m l))
 233    (while m
 234      (while (and (cdr m) (funcall eqp (car m) (cadr m)))
 235	(setcdr m (cddr m)))
 236      (setq m (cdr m)))) l)
 238;;; For edebug macro specifications.
 239(eval-when-compile (require 'edebug))
 241(defmacro with-mpd-temp-widen (&rest args)
 242  "Evaluate ARGS while temporarily widening the current buffer."
 243  `(save-restriction
 244     (widen)
 245     ,@args))
 246(put 'with-mpd-temp-widen 'lisp-indent-function 0)
 247(def-edebug-spec with-mpd-temp-widen (body))
 249(defmacro with-mpd-free-buffer (&rest args)
 250  "Evaluate ARGS with temporary widening and saved excursion."
 251  `(save-excursion
 252     (with-mpd-temp-widen ,@args)))
 253(put 'with-mpd-free-buffer 'lisp-indent-function 0)
 254(def-edebug-spec with-mpd-free-buffer (body))
 256(defmacro mpd-safe-nreverse (list)
 257  "Reverse LIST if it is a list, leave alone otherwise.
 258Note that LIST is evaluated thrice."
 259  `(if (listp ,list) (nreverse ,list) ,list))
 261(defun mpd-seq-add (seq &optional spec &rest args)
 262  "Operate the sequence SEQ on SPEC depending on its type.
 263Add a copy of SEQ to SPEC, if it's a list and call it with SEQ as an argument
 264followed by other ARGS specified, if it is a function. Return SPEC."
 265  (if (not (functionp spec))
 266      (cons (copy-sequence seq) spec)
 267    (apply spec seq args) spec))
 269(defun mpd-elt-add (elt &optional spec &rest args)
 270  "Operate the object ELT on SPEC depending on its type.
 271Add ELT to SPEC, if it's a list and call it with ELT as an argument
 272followed by other ARGS specified, if it is a function. Return SPEC."
 273  (if (not (functionp spec))
 274      (cons elt spec)
 275    (apply spec elt args) spec))
 277(defun mpd-parse-line (str)
 278  "Parse line STR of form \"KEY: VALUE\" to a cons (KEY . VALUE).
 279Return (STR . nil) on a parse failure."
 280  (if (string-match "^\\([^:]*\\): ?\\(.*\\)$" str)
 281      (cons (match-string 1 str) (match-string 2 str))
 282    (cons str nil)))
 284(defun mpd-safe-string (str)
 285  "Quote and escape string STR for sending to the mpd server."
 286  (if str
 287      (let ((start 0))
 288	(while (string-match "[\\\"]" str start)
 289	  ;; We add an extra character,
 290	  ;; so place start a character beyond end of match.
 291	  (setq start (1+ (match-end 0)))
 292	  (setq str (replace-match "\\\\\\&" t nil str)))
 293	(if (string-match " " str) (concat "\"" str "\"") str))))
 295;;; (defun mpd-log (fmt &rest args)
 296;;;   (write-region (concat (apply 'format fmt args) "\n") nil "~/mpd.log" t 1))
 298;;; (write-region "" nil "~/mpd.log" nil 1)
 300;;;; Connection object internals, library users... please close ur eyes ;)
 302;;; A connection to mpd is represented by the vector whose elements are below:
 303;;; This is just for the hackers amongst you, and for my reference :)
 304;;; *WARNING* No program using this package should depend on this description.
 305;;; 0 : vector of mpd-ver-string-length version numbers
 306;;; 1 : process object for the connection to the server.
 307;;; 2 : transaction-buffer used by the process filter to handle partial recvs.
 308;;; 3 : list-mode - refer `mpd-execute-command'
 309;;; 4 : connection-status: should be t at the beginning of a command.
 310;;;     stringp -> Result of command, contains the message after OK/ACK
 311;;;                till the next end of line. No filtering occurs after this
 312;;;                stage is reached.
 313;;;     numberp -> Intermediate state, when OK/ACK has been got
 314;;;                but no end of line found. Then store start pos of the OK/ACK.
 315;;;                Hope that eol will come in a subsequent packet.
 316;;;     listp   -> In commandlist mode, the list is what is to be sent to the
 317;;;                server after the commandlist gets over.
 318;;; 5 : last-command-result-flag - t if OK, nil if ACK.
 319;;; 6 : timeout - The timeout used for replies from server.
 320;;; 7 : host - The name of the host used to connect to mpd.
 321;;; 8 : port number - The port number of mpd.
 322;;; 9 : automode - not of the noauto argument to `mpd-conn-new'.
 324;; Don't expose these macros, unless required.
 326  (defmacro _mpdgv () `(aref conn 0))
 327  (defmacro _mpdsv (val) `(aset conn 0 ,val))
 328  (defmacro _mpdgo () `(aref conn 1))
 329  (defmacro _mpdso (val) `(aset conn 1 ,val))
 330  (defmacro _mpdgb () `(aref conn 2))
 331  (defmacro _mpdsb (val) `(aset conn 2 ,val))
 332  (defmacro _mpdgl () `(aref conn 3))
 333  (defmacro _mpdsl (val) `(aset conn 3 ,val))
 334  (defmacro _mpdgs () `(aref conn 4))
 335  (defmacro _mpdss (val) `(aset conn 4 ,val))
 336  (defmacro _mpdgf () `(aref conn 5))
 337  (defmacro _mpdsf (val) `(aset conn 5 ,val))
 338  (defmacro _mpdgt () `(aref conn 6))
 339  (defmacro _mpdst (val) `(aset conn 6 ,val))
 340  (defmacro _mpdgh () `(aref conn 7))
 341  (defmacro _mpdsh (val) `(aset conn 7 ,val))
 342  (defmacro _mpdgp () `(aref conn 8))
 343  (defmacro _mpdsp (val) `(aset conn 8 ,val))
 344  (defmacro _mpdga () `(aref conn 9))
 345  (defmacro _mpdsa (val) `(aset conn 9 ,val)))
 347;;;; Sanity check functions.
 349(defun mpd-connp (conn)
 350  "Return t if CONN is a connection to the mpd server."
 351  (and (vectorp conn) (= (length conn) 10)))
 352(put 'mpd-connp 'side-effect-free 'error-free)
 354(defun mpd-conn-strongp (conn)
 355  (and (mpd-connp conn)
 356       (vectorp (_mpdgv))
 357       (= (length (_mpdgv)) mpd-ver-string-length)
 358       (or (not (_mpdgo)) (processp (_mpdgo)))
 359       (stringp (_mpdgb))
 360       (or (not (_mpdgt)) (numberp (_mpdgt)))
 361       (stringp (_mpdgh))
 362       (wholenump (_mpdgp))))
 363(put 'mpd-conn-strongp 'side-effect-free 'error-free)
 365(defmacro mpd-assert-mpd-conn (conn) `(mpd-assert-type ,conn mpd-connp))
 367(defun mpd-assert-idle (conn)
 368  "Assert mpd connection CONN to be free to receive a command."
 369  (mpd-assert-mpd-conn conn)
 370  (or (stringp (_mpdgs))
 371      (error (if (listp (_mpdgs)) "Command list mode has not ended"
 372	       "Not done processing current command"))))
 374(defun mpd-end-conn (conn fmt &rest args)
 375  "Abort mpd conection CONN and signal error.
 376`format' error message using FMT and ARGS."
 377  (when (_mpdgo)
 378    (delete-process (_mpdgo))
 379    (_mpdso nil))
 380  (signal 'error (list (apply 'format fmt args))))
 382(defun mpd-end-cmd (conn fmt &rest args)
 383  "Abort current mpd command for connection CONN and signal error.
 384`format' error message using FMT and ARGS."
 385  (_mpdsf nil)
 386  (_mpdss (apply 'format fmt args))
 387  (signal 'error (list (apply 'format fmt args))))
 389;;;; Internal functions.
 391(defun mpd-process-filter (conn str)
 392  "Filter replies received from the mpd server.
 393CONN represents the connection object for which the filter is invoked.
 394STR is the packet received from the server to be processed.
 395This is an internal function, do not use this in your code."
 396  (cond
 397   ((eq (_mpdgs) t)
 398    (let ((start (length (_mpdgb))) status (case-fold-search nil))
 399      ;; Can be 4 if not for the : as mentioned below
 400      (setq start (if (< start 5) 0 (- start 5)))
 401      (_mpdsb (concat (_mpdgb) str))
 402      ;; The optional : in ACK accomodates the ACK: reply to a failed `add'
 403      ;; given by MPD servers till version 0.10.*
 404      (setq status (string-match "^\\(ACK:? \\|OK\\)"
 405				 (substring (_mpdgb) start)))
 406      (if status (setq status (+ status start)))
 407      (or (eq (_mpdgl) t)
 408	  (let ((packets
 409		 (if (not (equal status 0))
 410		     (mpd-get-lines (substring (_mpdgb) 0
 411					       (and status (1- status)))))))
 412	    (if status
 413		(progn
 414		  (_mpdsb (substring (_mpdgb) status))
 415		  (setq status 0))
 416	      (if (cdr packets)
 417		  (let ((last (last packets 2)))
 418		    (_mpdsb (cadr last))
 419		    (setcdr last nil))
 420		(_mpdsb (car packets))
 421		(setq packets nil)))
 422	    (cond
 423	     ((functionp (_mpdgl))
 424	      (mapcar '(lambda (str)
 425			 (funcall (_mpdgl) conn (mpd-parse-line str)))
 426		      packets))
 427	     ((listp (_mpdgl))
 428	      (_mpdsl (nconc (mapcar 'mpd-parse-line packets) (_mpdgl))))
 429	     (t (error "Invalid line mode filter")))))
 430      (when status
 431	(_mpdss status)
 432	(mpd-process-filter conn ""))))
 433   ((numberp (_mpdgs))
 434    (_mpdsb (concat (_mpdgb) str))
 435    (let* ((resp-end (_mpdgs))
 436	   (bufend (string-match "\n" (_mpdgb) resp-end)))
 437      (when bufend
 438	(_mpdss (substring
 439		 (_mpdgb)
 440		 ;; The `4` below should be `5` if ACK: is found, see above.
 441		 ;; This may then leave a space before the error message.
 442		 (+ resp-end (if (_mpdsf (eq (aref (_mpdgb) resp-end)?O)) 2 4))
 443		 bufend))
 444	(_mpdsb (substring (_mpdgb) 0 resp-end))
 445	(throw 'mpd-response-over nil))))))
 447(defun mpd-transact (conn &optional mode input)
 448  "Do an I/O transacton with the mpd server.
 449Use connection CONN for contacting the server. MODE is as described in
 450`mpd-execute-command'. Send INPUT, if non-nil, to the server before waiting for
 451the response. This is an internal function, do not use this in your code."
 452  (_mpdsb "")
 453  (_mpdsl mode)
 454  (_mpdss t)
 455  (unwind-protect
 456      (let ((timeout (_mpdgt)))
 457	(and input (process-send-string (_mpdgo) input))
 458	(while (not (stringp (_mpdgs)))
 459	  (catch 'mpd-response-over
 460	    (or (accept-process-output
 461		 (_mpdgo) (and timeout (/ timeout 1000))
 462		 (and timeout (mod timeout 1000)))
 463		;; This essentially reduces the probability of timeout due to
 464		;; program suspension to a really small probability.
 465		(accept-process-output (_mpdgo) 0 0)
 466		(error "Timed out getting a response from the mpd server")))))
 467    (unless (stringp (_mpdgs))
 468      (_mpdsf nil)
 469      (_mpdss "")
 470      (_mpdsb ""))))
 472;;;; Low level public interface.
 474(defun mpd-conn-new (host port &optional timeout noauto)
 475  "Construct a mpd connection object from given parameters.
 476Connections made using this object are made to host HOST at port number PORT.
 477TIMEOUT is a floating-point value specifying the number of seconds to wait
 478before giving up waiting for a reply from the server. Unspecified or zero
 479TIMEOUT correspond to infinite timeout. Set automatic mode if NOAUTO is nil,
 480hooked automatic function if NOAUTO is a function, and do not set automatic mode
 481otherwise. See `mpd-set-automatic-mode' for a description of automatic mode."
 482  (or (and (stringp host) (wholenump port) (or (not timeout) (numberp timeout)))
 483      (error "Invalid parameters passed for making new connection"))
 484  (and timeout
 485       (if (= timeout 0)
 486	   (setq timeout nil)
 487	 (or (> timeout 0) (error "Invalid (negative) timeout value"))))
 488  ;; Declaration conn object structure dependent.
 489  (vector (make-vector mpd-ver-string-length nil) nil
 490	  "" nil "" nil (and timeout (floor (* timeout 1000)))
 491	  host port (if noauto (if (functionp noauto) noauto nil) t)))
 494  (or (fboundp 'set-process-query-on-exit-flag)
 495      (defmacro set-process-query-on-exit-flag (process flag)
 496	`(process-kill-without-query process flag))))
 498(defun mpd-connect (conn)
 499  "Connect to the mpd server using the connection object CONN.
 500The connection object is constructed using `mpd-conn-new'. Note that this
 501function doesn't need to be explicitly called when the connection is in
 502automatic mode (the default). Close the connection using `mpd-close-connection'
 503when you are done."
 504  (let (proc rt welc)
 505    (setq proc (or (open-network-stream "mpd" nil (_mpdgh) (_mpdgp))
 506		   (error "Unable to open connection with mpd")))
 507    (and (_mpdgo) (delete-process (_mpdgo)))
 508    (_mpdso proc)
 509    (set-process-query-on-exit-flag proc nil)
 510    (set-process-coding-system proc 'utf-8-unix 'utf-8-unix)
 511    (set-process-filter proc
 512			`(lambda (proc str)
 513			   (mpd-process-filter ,conn str)))
 514    (unwind-protect
 515	(mpd-transact conn t)
 516      (or (_mpdgf)
 517	  (mpd-end-conn conn "Handshake failed, server returned: %s" (_mpdgs))))
 518    (setq welc (_mpdgs))
 519    (or (string-equal (substring welc 0 (mpd-welcome-length))
 520		      mpd-welcome-message)
 521	(mpd-end-conn conn "Process mpd not running on port %d in host \"%s\""
 522		      (_mpdgp) (_mpdgh)))
 523    (let ((verlist (split-string (substring welc (mpd-welcome-length))
 524				 "[\\.\n]")))
 525      (or (= (length verlist) mpd-ver-string-length)
 526	  (mpd-end-conn conn "Error parsing version information from server"))
 527      (let ((i 0))
 528	(while (< i mpd-ver-string-length)
 529	  (or (aset (_mpdgv) i (mpd-string-to-number-strict
 530				(pop verlist)))
 531	      (mpd-end-conn
 532	       conn "Error parsing version information from server"))
 533	  (setq i (1+ i))))))
 534  (message "Opened connection with mpd"))
 536(make-obsolete 'mpd-new-connection "use `mpd-conn-new' to create a connection \
 537object. If automatic mode is not used, a call to `mpd-connect' might be \
 538necessary." "2.1")
 539(defun mpd-new-connection (host port &optional timeout noauto)
 540  "Create a new connection and connect using it to the mpd server.
 541Return a connection object, which could be used for further transactions with
 542the server. See `mpd-conn-new' for a description of the parameters. Close the
 543connection using `mpd-close-connection' when you are done.
 545This function is deprecated since version 2.1.
 546Use `mpd-conn-new' to create a connection object.
 547If automatic mode is not used, a call to `mpd-connect' might be necessary."
 548  (let ((conn (mpd-conn-new host port timeout noauto)))
 549    (mpd-connect conn) conn))
 551(defun mpd-conn-wakeup (conn)
 552  "Try to ensure that the MPD connection is alive, when in automatic mode."
 553  (mpd-assert-idle conn)
 554  (when (and (not (and (_mpdgo) (eq (process-status (_mpdgo)) 'open)))
 555	     (_mpdga) (not (and (functionp (_mpdga))
 556				(funcall (_mpdga) conn 'pre))))
 557    (and (_mpdgo) (message "Connection with mpd broken, attempting reconnect"))
 558    (mpd-connect conn)
 559    (and (functionp (_mpdga)) (funcall (_mpdga) conn 'post))))
 561(defun mpd-execute-command (conn cmd &optional mode)
 562  "Send the command CMD to the mpd server using CONN.
 563Append a newline to CMD before sending to the server. Use the value of MODE to
 564decide how the response of the command is processed. MODE could take one of the
 565following values:
 566 - A list, usually nil, to which cons-cells got by formatting each line
 567   of the response, except the last one, using `mpd-parse-line', are appended.
 568   The new list thus got is the result of the function.
 569 - t, by which all response before the last line, as a string,
 570   is the result of the function.
 571 - A function, by which each cons-cell, got as described above, is sent to
 572   this function. Two parameters are passed to the function, the connection
 573   object and this cons-cell. An empty string is the result.
 574Return a cons-cell, whose car is non-nil if the server succeeds, and cdr is the
 575result as specified in the description of MODE above."
 576  (mpd-conn-wakeup conn)
 577  (mpd-transact conn mode (concat cmd "\n"))
 578  (prog1
 579      (cons (_mpdgf)
 580	    ;; Declaration conn object structure dependent.
 581	    (aref conn (if (_mpdgf) (if (or (eq (_mpdgl) 't)
 582					    (functionp (_mpdgl))) 2 3) 4)))
 583    (_mpdsb "")))
 585(defun mpd-simple-exec (conn cmd)
 586  "Execute mpd command CMD using CONN ignoring response.
 587Note that an OK/ACK message still has to come. Return non-nil iff the command
 588succeeds. `mpd-get-last-error' gives the server error message in case of
 589failure. See also `mpd-execute-command'."
 590  (if (not (listp (_mpdgs)))
 591      (car (mpd-execute-command conn cmd t))
 592    (_mpdss (cons cmd (_mpdgs))) t))
 594(defun mpd-close-connection (conn)
 595  "Close the mpd server connection given by CONN."
 596  (mpd-assert-idle conn)
 597  (when (_mpdgo)
 598    (delete-process (_mpdgo))
 599    (_mpdso nil)))
 601(defvar mpd-inter-conn
 602  (apply 'mpd-conn-new `(,@(mpd-connection-tidy
 603			    mpd-interactive-connection-parameters) nil))
 604  "The global mpd connection used for interactive queries.")
 606(defsubst mpd-get-version (conn)
 607  "Get version information for the mpd server CONN is connected to.
 608Return a vector of three numbers, for the major, minor and patch levels."
 609  (mpd-assert-mpd-conn conn)
 610  (or (aref (_mpdgv) 0) (mpd-conn-wakeup conn))
 611  (_mpdgv))
 612(put 'mpd-get-version 'side-effect-free t)
 614(defsubst mpd-get-last-error (conn)
 615  "Get the last server error message for mpd connection CONN.
 616Return nil in case of a successful last command."
 617  (mpd-assert-mpd-conn conn)
 618  (and (not (_mpdgf)) (_mpdgs)))
 619(put 'mpd-get-last-error 'side-effect-free t)
 621(defsubst mpd-get-connection-timeout (conn)
 622  "Get the timeout of mpd connection CONN.
 623Return nil if CONN isn't a mpd connection object."
 624  (mpd-assert-mpd-conn conn)
 625  (_mpdgt))
 626(put 'mpd-get-connection-timeout 'side-effect-free t)
 629(defun mpd-set-connection-timeout (conn timeout)
 630  "Set the timeout of mpd connection object CONN to TIMEOUT.
 631See also `mpd-new-connection'."
 632  (interactive
 633   (list mpd-inter-conn
 634	 (string-to-number
 635	  (read-string
 636	   "Connection timeout (None): "
 637	   (let ((timeout (mpd-get-connection-timeout mpd-inter-conn)))
 638	     (and timeout (number-to-string timeout)))))))
 639  (if (or (not (mpd-connp conn)) (and timeout (not (numberp timeout))))
 640      (error "Invalid parameters used to set connection timeout")
 641    (and (= timeout 0) (setq timeout nil))
 642    (_mpdst timeout)))
 644(defsubst mpd-get-automatic-mode (conn)
 645  "Return the automatic mode value for mpd connection CONN.
 646See `mpd-set-automatic-mode' for a description of automatic mode. Return value
 647is like the MODE parameter of that function."
 648  (mpd-assert-mpd-conn conn)
 649  (_mpdga))
 650(put 'mpd-get-automatic-mode 'side-effect-free t)
 652(defsubst mpd-set-automatic-mode (conn mode)
 653  "Set the automatic mode for mpd connection CONN.
 654Set automatic mode iff MODE is non-nil. Set to hooked automatic mode if the
 655non-nil MODE is a function.
 657The connection is said to be in automatic mode if it connects on demand (usually
 658as a result of a server request using the connection) and takes care of
 659reconnecting whenever the connection gets broken. The automatic mode could be
 660hooked by specifying a function. In this case, the function is called with two
 661parameters, the connection object and the second parameter being the symbol 'pre
 662or 'post. The function is called with 'pre when reconnection is needed. If the
 663function returns a non-nil value, it is assumed that the function has done the
 664reconnection. Else, the library reconnects by itself, and the function is once
 665again called after that, with the parameter 'post (Return value is ignored)."
 666  (mpd-assert-mpd-conn conn)
 667  (_mpdsa mode))
 669(make-obsolete 'mpd-get-reconnectible 'mpd-get-automatic-mode "2.1")
 670(defalias 'mpd-get-reconnectible 'mpd-get-automatic-mode
 671  "Return t if CONN reconnects when its mpd connection breaks.
 672This function is deprecated since version 2.1.
 673Use `mpd-get-automatic-mode' instead.")
 675(make-obsolete 'mpd-make-reconnectible 'mpd-set-automatic-mode "2.1")
 676(defsubst mpd-make-reconnectible (conn &optional noreconn)
 677  "Make CONN reconnectible when its mpd connection breaks.
 678Unset the reconnectibility on non-nil prefix arg NORECONN.
 679This function is deprecated since version 2.1.
 680Use `mpd-set-automatic-mode' instead."
 681  (mpd-set-automatic-mode conn (not noreconn)))
 683(defun mpd-force-accept-command (conn)
 684  "Force the mpd connection CONN to accept the next command.
 685*WARNING* DON'T use this unless you are really desperate. Shelf this off for
 686debugging purposes. Normally, the package should signal correctly whether it is
 687safe to receive the next command. Doing this could mean losing out on the
 688response of the current command, and worse, the response of the current command
 689could creep into the next one."
 690  (if (mpd-command-list-mode-p conn)
 691      (error "Command list mode has not ended")
 692    (_mpdsf nil)
 693    (_mpdss "")
 694    (_mpdsb "")))
 696(defun mpd-command-list-begin (conn)
 697  "Start the command-list mode for CONN.
 698Only commands that can be used with `mpd-simple-exec' are allowed in
 699command-list mode. Commands can be issued as they are usually done.
 700Always return t, as the commands are queued up and not sent to the server.
 701`mpd-command-list-end' ends the command-list and executes the list built up."
 702  (mpd-assert-idle conn)
 703  (_mpdsf nil)				; FIXME: Why is this here ??
 704  (_mpdss nil))
 706(defun mpd-command-list-mode-p (conn)
 707  "Return non-nil if mpd connection CONN is in command list mode."
 708  (mpd-assert-mpd-conn conn)
 709  (listp (_mpdgs)))
 710(put 'mpd-command-list-mode-p 'side-effect-free t)
 712(defun mpd-command-list-end (conn)
 713  "End command-list mode for CONN.
 714This function needs to be preceded by a call to `mpd-command-list-begin'."
 715  (or (mpd-command-list-mode-p conn)
 716      (error "The connection is not in command-list mode"))
 717  (let (str)
 718    (setq str (concat "command_list_begin\n"
 719		      (mapconcat '(lambda (item) item) (nreverse (_mpdgs)) "\n")
 720		      "\ncommand_list_end"))
 721    (_mpdss "")
 722    (mpd-simple-exec conn str)))
 724(defun mpd-connection-status (conn)
 725  "Get the status of mpd connection CONN.
 726Return one of 'busy for being in the midst of a request, 'ready for the ready
 727state, and 'command-list to indicate being in command-list mode."
 728  (mpd-assert-mpd-conn conn)
 729  (cond
 730   ((listp (_mpdgs)) 'command-list)
 731   ((stringp (_mpdgs)) 'ready)
 732   (t 'busy)))
 733(put 'mpd-connection-status 'side-effect-free t)
 735(defmacro with-mpd-timeout-disabled (&rest args)
 736  `(progn
 737     (mpd-assert-mpd-conn conn)
 738     (let ((timeout (_mpdgt)))
 739       (unwind-protect
 740	   (progn
 741	     (_mpdst nil)
 742	     ,@args)
 743	 (_mpdst timeout)))))
 744(put 'with-mpd-timeout-disabled 'lisp-indent-function 0)
 745(def-edebug-spec with-mpd-timeout-disabled (body))
 747;;; High level public interface helper functions.
 749;;; Silence the compiler.
 750(defvar mpd-song-receiver)
 751(defvar foreach)
 752(defvar mpd-song-receiver-args)
 754(defun mpd-song-receiver (conn cell)
 755  "Handle song data response from the mpd server.
 756See `mpd-execute-command' for a description of response handlers.
 757This is an internal function, do not use this in your code."
 758  (let ((key (car cell)))
 759    (unless (or (string-equal key "directory")
 760		(string-equal key "playlist"))
 761      (when (string-equal key "file")
 762	(when (plist-get mpd-song-receiver 'file)
 763	  (setq foreach (apply 'mpd-seq-add mpd-song-receiver
 764			       foreach mpd-song-receiver-args)))
 765	(setq mpd-song-receiver nil))
 766      (setq mpd-song-receiver
 767	    (plist-put mpd-song-receiver (intern key)
 768		       (if (member key '("Time" "Pos" "Id"))
 769			   (string-to-number (cdr cell)) (cdr cell)))))))
 771(defun mpd-get-songs (conn cmd &optional foreach)
 772  "Get details of songs from the mpd server using connection CONN.
 773Use command CMD to get the songs. Call function FOREACH, if specified, for each
 774song, with the song provided as the argument. Return list of all songs if
 775FOREACH is not specified and FOREACH otherwise. When a list is returned, each
 776element of the list is a property list, some known keys being `file', `Pos' and
 777`Id'. The last two keys, along with `Time' if present, have integers as their
 778value. `Time' refers to the total length of the song. `Pos' and `Id' are present
 779only when the song retrieved is a part of a playlist. Other song tag data might
 780be present as well."
 781  (or (functionp foreach) (setq foreach nil))
 782  (let (mpd-song-receiver mpd-song-receiver-args)
 783    (mpd-execute-command conn cmd 'mpd-song-receiver)
 784    (and mpd-song-receiver
 785	 (setq foreach (mpd-seq-add mpd-song-receiver foreach)))
 786    (mpd-safe-nreverse foreach)))
 788(defun mpd-make-cmd-concat (cmd arg &optional normal-nil)
 789  "Make mpd command string using command CMD and argument ARG.
 790ARG could be a string or a list of strings. If NORMAL-NIL is non-nil, do nothing
 791if ARG is nil, and return CMD for nil NORMAL-NIL and ARG. Use command list
 792mode implicitly for lists. Sanitize arguments before composition.
 793This is an internal function, do not use this in your code."
 794  (cond
 795   ((not (or arg normal-nil)) cmd)
 796   ((listp arg)
 797    (concat "command_list_begin\n"
 798	    (mapconcat '(lambda (item) (concat cmd " " (mpd-safe-string item)))
 799		       arg "\n") "\ncommand_list_end"))
 800   (t (concat cmd " " (mpd-safe-string arg)))))
 801(put 'mpd-make-cmd-concat 'side-effect-free t)
 803(defun mpd-make-cmd-format (cmd validate arg1 &optional arg2)
 804  "Make mpd command string with format CMD and arguments ARG1, ARG2.
 805ARG1/ARG2 could be a list of arguments each. Use command list mode implicitly
 806for lists. Send each of the arguments pairs to VALIDATE before composition.
 807This is an internal function, do not use this in your code."
 808  (if (listp arg1)
 809      (let ((tail2 arg2))
 810	(and arg2 (or (= (length arg1) (length arg2))
 811		      (error "Argument lists are of unequal lengths")))
 812	(concat "command_list_begin\n"
 813		(mapconcat '(lambda (item)
 814			      (and validate (funcall validate item (car tail2)))
 815			      (prog1
 816				  (format cmd item (car tail2))
 817				(setq tail2 (cdr tail2)))) arg1 "\n")
 818		"\ncommand_list_end"))
 819    (and validate (funcall validate arg1 arg2))
 820    (format cmd arg1 arg2)))
 822;;; Helper functions for interactive display.
 824(defun mpd-line-to-buffer (str)
 825  "Insert STR as a line to the mpd display buffer."
 826  (with-current-buffer (get-buffer-create "*mpd-display*")
 827    (with-mpd-free-buffer
 828      (goto-char (point-max))
 829      (insert (concat str "\n"))
 830      (display-buffer (current-buffer)))))
 832(defsubst mpd-separator-line (&optional num)
 833  "Make a separator line for insertion to the mpd display buffer.
 834The number of columns used is 80, unless specified using NUM."
 835  (propertize (concat (make-string (or num 80) ?*) "\n")
 836	      'face 'mpd-separator-face))
 838(defun mpd-init-buffer (&optional str1 str2 str3)
 839  "Initialize the mpd display buffer using strings STR1, STR2, STR3.
 840Layout as follows:
 841	STR1 		if non-empty
 842	***...*** 	if STR1 is non-nil
 843	STR2 		if non-empty
 844	***...*** 	if STR2 is non-nil
 845	STR3		if non-empty"
 846  (let ((max (max (length str1) (length str2) (length str3))))
 847    (with-current-buffer (get-buffer-create "*mpd-display*")
 848      (erase-buffer)
 849      (insert
 850       (concat "\n" (and str1 (not (string-equal str1 ""))
 851			 (propertize (concat str1 "\n") 'face 'mpd-header-face))
 852	       (and str1 (mpd-separator-line max))
 853	       (and str2 (propertize (concat str2 "\n") 'face 'mpd-header-face))
 854	       (and str3 (mpd-separator-line max))
 855	       (and str3 (not (string-equal str3 ""))
 856		    (propertize (concat str3 "\n")
 857				'face 'mpd-header-face)) "\n")))))
 859(defun mpd-render-field (desc val &optional nosep)
 860  "Format to a colorized line of form \"\\nDESC: VAL\".
 861Include the separating colon unless NOSEP is non-nil."
 862  (and val (not (string-equal val ""))
 863       (concat (propertize desc 'face 'mpd-first-field-face)
 864	       (and (not nosep) ": ")
 865	       (propertize val 'face 'mpd-second-field-face) "\n")))
 866(put 'mpd-render-field 'side-effect-free t)
 868(defun mpd-render-plist (plist table)
 869  "Display property list PLIST as a pretty table in the display buffer.
 870TABLE is a list of entries is used to translate between keys and strings
 871displayed. Each entry of the table is a list of the key symbol, the
 872corresponding string to display, and optionally a function to call with the
 873value to get the string to display as the value. If the string to display is
 874nil, then those keys are ignored and not displayed at all."
 875  (when plist
 876    (let ((ptr plist) (str "") key value entry trans filter)
 877      (while ptr
 878	(setq key (car ptr))
 879	(setq value (cadr ptr))
 880	(when (and key value)
 881	  (setq entry (assq key table))
 882	  (setq trans (if entry (cadr entry) (symbol-name key)))
 883	  (setq filter (cadr (cdr entry)))
 884	  (when trans
 885	    (setq str (concat str (mpd-render-field
 886				   (format "%-13s" trans)
 887				   (format "%s" (if filter
 888						    (funcall filter value)
 889						  value)))))))
 890	(setq ptr (cddr ptr)))
 891      (mpd-line-to-buffer (concat str "\n" (mpd-separator-line))))))
 893(defconst mpd-display-song-key-table
 894  '((Time "Length" (lambda (time) (format "%s seconds" time)))
 895    (file "Filename")
 896    (Pos)
 897    (Id)))
 899(defsubst mpd-display-song (song)
 900  "Display mpd song data SONG in the mpd display buffer."
 901  (mpd-render-plist song mpd-display-song-key-table))
 903(defun mpd-display-playlist-item (title num)
 904  "Display playlist item with TITLE and index NUM in mpd buffer."
 905  (mpd-line-to-buffer
 906   (concat (propertize (format "%4d  " (1+ num)) 'face 'mpd-first-field-face)
 907	   (propertize title 'face 'mpd-second-field-face))))
 909(defun mpd-display-dir-info (item type)
 910  "Display mpd directory information to the mpd display buffer."
 911  (if (eq type 'file)
 912      (mpd-display-song item)
 913    (mpd-line-to-buffer
 914     (concat (mpd-render-field
 915	      (if (eq type 'playlist) "Playlist    " "Directory   ") item)
 916	     "\n" (mpd-separator-line)))))
 918(defun mpd-display-dir-listing (item dir)
 919  "Display mpd directory listing to the mpd display buffer."
 920  (mpd-line-to-buffer
 921   (concat (mpd-render-field (if dir "Directory " "File      " ) item)
 922	   "\n" (mpd-separator-line))))
 924(defsubst mpd-display-bullet (str)
 925  "Display a bulleted line to the mpd display buffer."
 926  (mpd-line-to-buffer (mpd-render-field " o " (format "%s" str) t)))
 928(defconst mpd-display-output-key-table
 929  '((outputid "ID")
 930    (outputname "Name")
 931    (outputenabled "Enabled"
 932		   (lambda (enabled)
 933		     (cond
 934		      ((eq enabled t) "Yes")
 935		      ((eq enabled nil) "No")
 936		      (t enabled))))))
 938(defsubst mpd-display-output (output)
 939  "Display mpd output OUTPUT in the mpd display buffer."
 940  (mpd-render-plist output mpd-display-output-key-table))
 942(defun mpd-read-item (prompt &optional default zero allowneg)
 943  "Read a number from the minibuffer.
 944Display PROMPT as the prompt string prefix. Append the DEFAULT value,
 945if present, in brackets. Return the number read. Unless ZERO is non-nil,
 946add default value by one before operation, and decrement number read
 947by 1 before returning. If ALLOWNEG is non-nil, allow negative numbers."
 948  (let (num str)
 949    (and default (setq str (number-to-string (if zero default (1+ default)))))
 950    (setq
 951     num (mpd-string-to-number-strict
 952	  (read-string (concat prompt (if default (concat " (" str ")")) ": ")
 953		       nil nil str) allowneg))
 954    (if (and num (not zero)) (1- num) num)))
 956;;;; High level public interface.
 958;;;  These functions require response, and hence cannot be queued by using the
 959;;;  command-line mode.
 961(defun mpd-status-receiver (lsym cell)
 962  "Handle response for the 'status' command to the mpd server.
 963See `mpd-execute-command' for a description of response handlers.
 964This is an internal function, do not use this in your code."
 965  (let ((sym (car cell)))
 966    (cond
 967     ((member sym '("volume" "repeat" "random" "playlist" "playlistlength"
 968		    "bitrate" "song" "songid" "xfade" "updating_db"))
 969      (put lsym (intern sym) (string-to-number (cdr cell))))
 970     ((string-equal sym "state")
 971      (and (member (cdr cell) '("play" "pause" "stop"))
 972	   (put lsym (intern sym) (intern (cdr cell)))))
 973     ((string-equal sym "time")
 974      (when (string-match "^\\([0-9]*\\):\\([0-9]*\\)$" (cdr cell))
 975	(put lsym 'time-elapsed (string-to-number (match-string 1 (cdr cell))))
 976	(put lsym 'time-total (string-to-number (match-string 2 (cdr cell))))))
 977     ((string-equal sym "audio")
 978      (when (string-match "^\\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)$" (cdr cell))
 979	(put lsym 'sample-rate (string-to-number (match-string 1 (cdr cell))))
 980	(put lsym 'bits-per-sample
 981	     (string-to-number (match-string 2 (cdr cell))))
 982	(put lsym 'channels (string-to-number (match-string 3 (cdr cell))))))
 983     ;; currently only "error"
 984     (t (put lsym (intern sym) (cdr cell))))))
 986(defun mpd-get-status (conn)
 987  "Get status of the mpd server, using connection CONN.
 988Return a property list, the known keys being `volume', `repeat', `random',
 989`playlist', `playlistlength', `bitrate', `song', `songid', `xfade', `state',
 990`time-elapsed', `time-total', `sample-rate', `bits-per-sample', `channels'
 991and `error'. Other keys might be present in the plist depending on the version
 992of MPD used. Some of the less obvious descriptions are:
 995| playlist  |The playlist version number - a 'checksum' for the current        |
 996|           |playlist, guaranteed to change after a change in the playlist.    |
 998|  xfade    |The crossfade time in seconds between two songs occurs. This value|
 999|           |could be zero in case of no crossfade.                            |
1001|   song    |Position of the current song in the playlist.                     |
1003|  songid   |Song ID of the current song in the playlist.                      |
1005|  state    |Could be one of 'play, 'pause or 'stop                            |
1007|updating_db|The updating job id, displayed when there is a update taking      |
1008|           |place.                                                            |
1010|  error    |A description of the error, if one occurs. Could be nil, in case  |
1011|           |there is no error.                                                |
1014All fields except error and state are whole numbers. `repeat' and `random' are
1015in addition, bi-state variables (0/1)"
1016  (setplist 'mpd-get-status-local-sym nil)
1017  (mpd-execute-command
1018   conn "status" '(lambda (conn cell)
1019		    (mpd-status-receiver 'mpd-get-status-local-sym cell)))
1020  (symbol-plist 'mpd-get-status-local-sym))
1022(defun mpd-stats-receiver (lsym cell)
1023  "Handle response for the 'stats' command to the mpd server.
1024See `mpd-execute-command' for a description of response handlers.
1025This is an internal function, do not use this in your code."
1026  (let ((sym (car cell)))
1027    (cond
1028     ((member sym '("artists" "albums" "songs" "uptime"
1029		    "playtime" "db_playtime" "db_update"))
1030      (put lsym (intern sym) (string-to-number (cdr cell))))
1031     (t (put lsym (intern sym) (cdr cell))))))
1033(defun mpd-get-stats (conn)
1034  "Get statistics for the mpd server connected using CONN.
1035Return a property list, the known keys being `artists', `albums', `songs',
1036`db_playtime' corresponding to the number of artists, albums and songs and the
1037total time in seconds of all songs in the database respectively; `db_update',
1038the time stamp of the last update to the database; `playtime', the total time
1039for which music has been played in seconds and `uptime', the server uptime,
1040in seconds as well. Other keys might be present in the plist depending on the
1041version of MPD used."
1042  (setplist 'mpd-get-stats-local-sym nil)
1043  (mpd-execute-command
1044   conn "stats" '(lambda (conn cell)
1045		   (mpd-stats-receiver 'mpd-get-stats-local-sym cell)))
1046  (symbol-plist 'mpd-get-stats-local-sym))
1049(defun mpd-get-playlist (conn &optional foreach)
1050  "Get all songs in the current playlist managed by the mpd server.
1051CONN and FOREACH are as in `mpd-get-songs'."
1052  (interactive (progn (mpd-init-buffer "" "Current MPD Playlist" "")
1053		      (list mpd-inter-conn 'mpd-display-playlist-item)))
1054  (or (functionp foreach) (setq foreach nil))
1055  (mpd-execute-command
1056   conn "playlist" '(lambda (conn cell)
1057		      (setq foreach (mpd-elt-add
1058				     (cdr cell) foreach
1059				     (string-to-number (car cell))))))
1060  (mpd-safe-nreverse foreach))
1063(defun mpd-get-playlist-entry (conn &optional item foreach use-id)
1064  "Get song data for entr(y/ies) ITEM in the current mpd playlist.
1065CONN, FOREACH and the return value are as in `mpd-get-songs'.
1066ITEM is the item position/id or a list of it. Note that ITEM as nil fetches
1067data for all entries in the current playlist rather than not doing anything.
1068Interpret ITEM as song id(s) iff USE-ID is non-nil."
1069  (interactive
1070   (let ((item (mpd-read-item
1071		"Enter item number"
1072		(plist-get (mpd-get-status mpd-inter-conn) 'song))))
1073     (mpd-init-buffer "" (format "MPD Playlist Item # %d" (1+ item)) "")
1074     (list mpd-inter-conn item 'mpd-display-song)))
1075  (setq use-id (if use-id "playlistid" "playlistinfo"))
1076  (mpd-get-songs
1077   conn (if item
1078	    (mpd-make-cmd-format
1079	     (concat use-id " %d")
1080	     '(lambda (item item2) (mpd-assert-wholenump item))
1081	     item) use-id) foreach))
1084(defun mpd-get-current-song (conn &optional foreach)
1085  "Get song data for the current song in mpd.
1086CONN and FOREACH are as in `mpd-get-songs'. Return FOREACH if specified, and
1087the current song (see `mpd-get-songs' for how a song is represented) otherwise."
1088  (interactive (progn (mpd-init-buffer "" "Current MPD Song" "")
1089		      (list mpd-inter-conn 'mpd-display-song)))
1090  (setq foreach (mpd-get-songs conn "currentsong" foreach))
1091  (or (functionp foreach) (setq foreach (car foreach))) foreach)
1093(defun mpd-get-playlist-changes (conn version &optional foreach nometa)
1094  "Get a list of changed song entries in the current mpd playlist.
1095CONN, FOREACH and the return value are as in `mpd-get-songs'.
1096Calculate the change between the current playlist and the playlist
1097with version number VERSION. Do not fetch complete metadata (only position and
1098song id is returned for each song) if NOMETA is non-nil."
1099  (mpd-assert-numberp version)
1100  (mpd-get-songs conn (format "%s %d" (if nometa "plchangesposid" "plchanges")
1101			      version) foreach))
1104(defun mpd-get-directory-songs (conn &optional directory foreach)
1105  "Get all songs in a directory of the mpd database.
1106CONN, FOREACH and the return value are as in `mpd-get-songs'.
1107DIRECTORY is the relative directory path wrt the database root.
1108DIRECTORY could be a list as well, the action then corresponds to all songs
1109in all the directories. Note that the nil value for DIRECTORY corresponds
1110to the database toplevel rather than an empty list."
1111  (interactive
1112   (let ((str (read-string "Enter relative directory: ")))
1113     (progn (mpd-init-buffer "" (concat "Songs in directory " str) "")
1114	    (list mpd-inter-conn str 'mpd-display-song))))
1115  (mpd-get-songs
1116   conn (mpd-make-cmd-concat "listallinfo" directory) foreach))
1119(defun mpd-get-directory-info (conn &optional directory foreach)
1120  "Get directory info for DIRECTORY in the mpd database.
1121Use CONN as the mpd connection for the purpose and call function FOREACH,
1122if specified, with each information field. The arguments passed to FOREACH is a
1123song object, directory string or playlist string and one of the symbols 'file,
1124'playlist or 'directory describing the data sent. DIRECTORY could be a list as
1125well, the action then corresponds information for all the directories. Note that
1126a nil for DIRECTORY corresponds to the database toplevel rather than an empty
1127list. Return FOREACH, if non-nil; else a vector of three elements: a list of
1128songs in the directory (see `mpd-get-songs' for how a song is represented),
1129a list of playlists and a list of subdirectories."
1130  (interactive
1131   (let ((str (read-string "Enter relative directory: ")))
1132     (progn (mpd-init-buffer "" (concat "Information on directory " str) "")
1133	    (list mpd-inter-conn str 'mpd-display-dir-info))))
1134  (or (functionp foreach) (setq foreach nil))
1135  (let (filemode (pl foreach) (dir foreach)
1136		 (mpd-song-receiver-args '(file)) mpd-song-receiver)
1137    (mpd-execute-command
1138     conn (mpd-make-cmd-concat "lsinfo" directory)
1139     '(lambda (conn cell)
1140	(if (string-equal (car cell) "directory")
1141	    (setq dir (mpd-elt-add (cdr cell) dir 'directory))
1142	  (if (string-equal (car cell) "playlist")
1143	      (setq pl (mpd-elt-add (cdr cell) pl 'playlist))
1144	    (mpd-song-receiver conn cell)))))
1145    (and mpd-song-receiver
1146	 (setq foreach (mpd-seq-add mpd-song-receiver foreach 'file)))
1147    (if (functionp foreach) foreach
1148      (vector (nreverse foreach) (nreverse pl) (nreverse dir)))))
1151(defun mpd-list-directory-recursive (conn foreach &optional directory)
1152  "Get the file-directory hierarchy of a directory in the mpd database.
1153Use CONN for the connection and use function FOREACH to report each entry,
1154along with a non-nil second argument if the entry is a directory. DIRECTORY
1155could be a list as well, the action then corresponds to listing of all the
1156directories. Note that the nil value for DIRECTORY corresponds to the
1157database toplevel rather than an empty list."
1158  (interactive
1159   (let ((str (read-string "Enter relative directory: ")))
1160     (progn
1161       (mpd-init-buffer "" (concat "Recursive listing of directory " str) "")
1162       (list mpd-inter-conn 'mpd-display-dir-listing str))))
1163  (mpd-assert-type foreach functionp)
1164  (mpd-execute-command conn (mpd-make-cmd-concat "listall" directory)
1165		       '(lambda (conn cell)
1166			  (funcall foreach (cdr cell)
1167				   (string-equal (car cell) "directory")))))
1170(defun mpd-search (conn by for &optional foreach)
1171  "Search for songs in the mpd database.
1172CONN, FOREACH and the return values are as in `mpd-get-songs'.
1173The valid values for BY are 'artist, 'album and 'title;
1174indicating the field to search for; and FOR is the search string.
1175If FOR is a non-empty list, search by BY for all FOR."
1176  (interactive
1177   (let ((reqb (intern-soft
1178		(completing-read "Search by: "
1179				 '(("artist") ("album") ("title")) nil t)))
1180	 (reqf (read-string "Search for: ")))
1181     (mpd-init-buffer "" (format "Search results for %s %s" reqb reqf) "")
1182     (list mpd-inter-conn reqb reqf 'mpd-display-song)))
1183  (or (eq by 'artist) (eq by 'album) (eq by 'title)
1184      (error "Invalid mpd search field %s" by))
1185  (mpd-get-songs
1186   conn
1187   (mpd-make-cmd-concat (concat "find " (symbol-name by)) for t) foreach))
1190(defun mpd-get-artists (conn &optional foreach)
1191  "Get the names of all artists whose songs are in the mpd database.
1192Use CONN for the connection, and call function FOREACH, if specified, with
1193each name. If FOREACH is a function, return FOREACH, else return a list of
1194artist names."
1195  (interactive
1196   (progn
1197     (mpd-init-buffer "" "List of artists" "")
1198     (list mpd-inter-conn 'mpd-display-bullet)))
1199  (or (functionp foreach) (setq foreach nil))
1200  (mpd-execute-command
1201   conn "list artist"
1202   '(lambda (conn cell)
1203      (and (string-equal (car cell) "Artist")
1204	   (setq foreach (mpd-elt-add (cdr cell) foreach)))))
1205  (mpd-safe-nreverse foreach))
1208(defun mpd-get-artist-albums (conn &optional artist foreach)
1209  "Get all albums in the mpd database featuring artist(s) ARTIST.
1210Get all albums if ARTIST is not specified. If ARTIST is a list, find the albums
1211of all the artists in the list. Use CONN for the connection, and call function
1212FOREACH, if specified, with each name. If FOREACH is a function, return FOREACH,
1213else return the list of albums."
1214  (interactive
1215   (let ((str (read-string "Name of the artist (All): ")))
1216     (and (string-equal str "") (setq str nil))
1217     (mpd-init-buffer "" (if str (concat "Albums of artist " str)
1218			   "List of albums") "")
1219     (list mpd-inter-conn str 'mpd-display-bullet)))
1220  (or (functionp foreach) (setq foreach nil))
1221  (mpd-execute-command
1222   conn (mpd-make-cmd-concat "list album" artist)
1223   '(lambda (conn cell)
1224      (and (string-equal (car cell) "Album")
1225	   (setq foreach (mpd-elt-add (cdr cell) foreach)))))
1226  (mpd-safe-nreverse foreach))
1229(defun mpd-get-handled-remote-url-prefixes (conn &optional foreach)
1230  "Use CONN to query remote URL prefixes handled by the mpd server.
1231Call function FOREACH, if specified, with each prefix.
1232If FOREACH is a function, return FOREACH, else return the URL prefixes."
1233  (interactive
1234   (progn
1235     (mpd-init-buffer "" "List of remote URL prefixes handled" "")
1236     (list mpd-inter-conn 'mpd-display-bullet)))
1237  (or (functionp foreach) (setq foreach nil))
1238  (mpd-execute-command
1239   conn "urlhandlers"
1240   '(lambda (conn cell)
1241      (and (string-equal (car cell) "handler")
1242	   (setq foreach (mpd-elt-add (cdr cell) foreach)))))
1243  (mpd-safe-nreverse foreach))
1245(defvar mpd-output-receiver)
1247(defun mpd-output-receiver (conn cell)
1248  "Handle output descriptions from the mpd server.
1249See `mpd-execute-command' for a description of response handlers.
1250This is an internal function, do not use this in your code."
1251  (let ((key (car cell)))
1252    (when (string-equal key "outputid")
1253      (when (plist-get mpd-output-receiver 'outputid)
1254	(setq foreach (apply 'mpd-seq-add mpd-output-receiver foreach)))
1255      (setq mpd-output-receiver nil))
1256    (setq mpd-output-receiver
1257	  (plist-put mpd-output-receiver (intern key)
1258		     (cond
1259		      ((string-equal key "outputid")
1260		       (string-to-number (cdr cell)))
1261		      ((string-equal key "outputenabled")
1262		       (cond
1263			((string-equal (cdr cell) "0") nil)
1264			((string-equal (cdr cell) "1") t)
1265			(t (cdr cell))))
1266		      (t (cdr cell)))))))
1269(defun mpd-get-outputs (conn &optional foreach)
1270  "Get output descriptions from the mpd server using connection CONN.
1271Call function FOREACH, if specified, for each output, with the output provided
1272as the argument. Return list of all outputs if FOREACH is not specified and
1273FOREACH otherwise. When a list is returned, each element of the list is a
1274property list, some known keys being `outputid' (integer), `outputname' (string)
1275and `outputenabled' (boolean)."
1276  (interactive
1277   (progn
1278     (mpd-init-buffer "" "List of outputs" "")
1279     (list mpd-inter-conn 'mpd-display-output)))
1280  (or (functionp foreach) (setq foreach nil))
1281  (let (mpd-output-receiver)
1282    (mpd-execute-command conn "outputs" 'mpd-output-receiver)
1283    (and mpd-output-receiver
1284	 (setq foreach (mpd-seq-add mpd-output-receiver foreach)))
1285    (mpd-safe-nreverse foreach)))
1287;;; These are command functions. These functions can be queued by using the
1288;;; command-list mode. See `mpd-command-list-begin' and `mpd-command-list-end'.
1290(defun mpd-file-to-mpd-resource (file)
1291  "Convert FILE to a resource string understood by mpd."
1292  (let ((prefixes
1293	 (mpd-get-handled-remote-url-prefixes mpd-inter-conn)) length)
1294    (catch
1295	'mpd-enqueue-is-url
1296      (while prefixes
1297	(setq length (length (car prefixes)))
1298	(and (>= (length file) length)
1299	     (string-equal (substring file 0 length) (car prefixes))
1300	     (throw 'mpd-enqueue-is-url file))
1301	(setq prefixes (cdr prefixes)))
1302      (file-relative-name file mpd-db-root))))
1305(defun mpd-enqueue (conn res)
1306  "Enqueue resource RES (or list of RES) to the mpd playlist.
1307Each of RES can be a file or a directory in the mpd database, or an URL."
1308  (interactive
1309   (list mpd-inter-conn
1310	 (if (and (stringp mpd-db-root) (not (string-equal mpd-db-root "")))
1311	     (mpd-file-to-mpd-resource
1312	      (read-file-name "Enqueue what: "
1313			      (file-name-as-directory mpd-db-root)))
1314	   (read-string "Enqueue what: "))))
1315  (mpd-simple-exec conn (mpd-make-cmd-concat "add" res)))
1318(defun mpd-delete (conn pos &optional use-id never-sort)
1319  "Delete song at position/id POS from the mpd playlist.
1320Interpret POS as a list of song id's if USE-ID is non-nil. POS could be a list
1321to delete as well. If POS is a list and USE-ID is nil, sort it in descending
1322order and remove duplicates before proceeding, unless NEVER-SORT is non-nil.
1323Note that this is necessary for the correctness of the deletion, and NEVER-SORT
1324is only provided in case the arguments already satisfy the condition."
1325  (interactive (list mpd-inter-conn (mpd-read-item "Enter item to be deleted")))
1326  ;; Deletion changes the playlist ordering of all those below the deleted item.
1327  ;; Hence, sort and uniquify the list in descending order.
1328  (and (not (or use-id (not (listp pos)) never-sort))
1329       (setq pos (mpd-sort-uniq-list pos '> '=)))
1330  (mpd-simple-exec
1331   conn
1332   (mpd-make-cmd-format (if use-id "deleteid %d" "delete %d")
1333			'(lambda (item ig) (mpd-assert-wholenump item)) pos)))
1336(defun mpd-save-playlist (conn file)
1337  "Save current mpd playlist to FILE (or list of FILE)."
1338  (interactive (list mpd-inter-conn (read-string "Save playlist to: ")))
1339  (mpd-simple-exec conn (mpd-make-cmd-concat "save" file t)))
1342(defun mpd-load-playlist (conn plname)
1343  "Load playlist PLNAME (or list of PLNAME) to the mpd server."
1344  (interactive (list mpd-inter-conn (read-string "Load playlist: ")))
1345  (mpd-simple-exec conn (mpd-make-cmd-concat "load" plname t)))
1348(defun mpd-remove-playlist (conn plname)
1349  "Remove playlist PLNAME from the mpd playlist directory.
1350PLNAME could as well be a list of playlist names."
1351  (interactive (list mpd-inter-conn (read-string "Remove playlist: ")))
1352  (mpd-simple-exec conn (mpd-make-cmd-concat "rm" plname t)))
1355(defun mpd-shuffle-playlist (conn)
1356  "Shuffle current mpd playlist using connection CONN."
1357  (interactive (list mpd-inter-conn))
1358  (mpd-simple-exec conn "shuffle"))
1361(defun mpd-clear-playlist (conn)
1362  "Clear current mpd playlist using connection CONN."
1363  (interactive (list mpd-inter-conn))
1364  (mpd-simple-exec conn "clear"))
1367(defun mpd-play (conn &optional pos use-id)
1368  "Play song at position/id POS (default first) in the mpd playlist.
1369Interpret POS as a song id iff USE-ID is non-nil."
1370  (interactive (list mpd-inter-conn (mpd-read-item "Enter item to play" 0)))
1371  (and pos (mpd-assert-wholenump pos))
1372  (mpd-simple-exec
1373   conn (concat (if use-id "playid" "play")
1374		(and pos (concat " " (number-to-string pos))))))
1377(defun mpd-stop (conn)
1378  "Stop playing the current mpd playlist."
1379  (interactive (list mpd-inter-conn))
1380  (mpd-simple-exec conn "stop"))
1383(defun mpd-pause (conn &optional arg)
1384  "Toggle the pause state of the current mpd playlist.
1385If prefix argument ARG is non-nil, pause iff ARG is positive
1386and resume playing otherwise."
1387  (interactive (list mpd-inter-conn current-prefix-arg))
1388  (mpd-simple-exec
1389   conn
1390   (if arg
1391       (format "pause %d" (if (> (prefix-numeric-value arg) 0) 1 0))
1392     "pause")))
1395(defun mpd-next (conn)
1396  "Play next song in the current mpd playlist."
1397  (interactive (list mpd-inter-conn))
1398  (mpd-simple-exec conn "next"))
1401(defun mpd-prev (conn)
1402  "Play previous song in the current mpd playlist."
1403  (interactive (list mpd-inter-conn))
1404  (mpd-simple-exec conn "previous"))
1407(defun mpd-move (conn from to &optional use-id)
1408  "Move item from position/id FROM in the current mpd playlist to TO.
1409For lists of FROM and TO, do action in that order for each pair of items.
1410Interpret FROM as a list of song ids iff USE-ID is non-nil. Retain TO as a list
1411of positions irrespective of the value of USE-ID. If sending a list for FROM and
1412TO, note that every move changes the order of items in the playlist."
1413  (interactive (list mpd-inter-conn (mpd-read-item "Source item number")
1414		     (mpd-read-item "Destination item number")))
1415  (and from to
1416       (mpd-simple-exec
1417	conn (mpd-make-cmd-format
1418	      (concat (if use-id "moveid" "move") " %d %d")
1419	      '(lambda (i j)
1420		 (mpd-assert-wholenump i)
1421		 (mpd-assert-wholenump j))
1422	      from to))))
1425(defun mpd-swap (conn first second &optional use-id)
1426  "Swap positions/ids FIRST and SECOND in the current mpd playlist.
1427For lists of FROM and TO, do action in that order for each pair of items.
1428Interpret FIRST and SECOND as song ids iff USE-ID is non-nil.
1429See also `mpd-move'."
1430  (interactive (list mpd-inter-conn (mpd-read-item "Swap item at number")
1431		     (mpd-read-item "With item at number")))
1432  (and first second
1433       (mpd-simple-exec
1434	conn (mpd-make-cmd-format
1435	      (concat (if use-id "swapid" "swap") " %d %d")
1436	      '(lambda (i j)
1437		 (mpd-assert-wholenump i)
1438		 (mpd-assert-wholenump j))
1439	      first second))))
1442(defun mpd-seek (conn song &optional time use-id)
1443  "Seek to song position/id SONG and time TIME in the mpd playlist.
1444Take TIME to be 0 by default. Interpret SONG as a song id
1445iff USE-ID is non-nil."
1446  (interactive
1447   (let (status)
1448     (and (eq (mpd-connection-status mpd-inter-conn) 'ready)
1449	  (setq status (mpd-get-status mpd-inter-conn)))
1450     (list mpd-inter-conn
1451	   (mpd-read-item "Seek to song" (and status (plist-get status 'song)))
1452	   (mpd-read-item "Time in seconds"
1453			  (and status (plist-get status 'time-elapsed)) t))))
1454  (mpd-assert-wholenump song)
1455  (if time (mpd-assert-wholenump time) (setq time 0))
1456  (mpd-simple-exec conn (format "%s %d %d"
1457				(if use-id "seekid" "seek") song time)))
1460(defun mpd-toggle-random (conn &optional arg)
1461  "Change random mode of mpd using connection CONN.
1462With ARG, set random on iff ARG is positive."
1463  (interactive (list mpd-inter-conn current-prefix-arg))
1464  (setq arg (if arg (> (prefix-numeric-value arg) 0)
1465	      (= (plist-get (mpd-get-status conn) 'random) 0)))
1466  (mpd-simple-exec conn (concat "random " (if arg "1" "0"))))
1469(defun mpd-toggle-repeat (conn &optional arg)
1470  "Change repeat mode of mpd using connection CONN.
1471With ARG, set repeat on iff ARG is positive."
1472  (interactive (list mpd-inter-conn current-prefix-arg))
1473  (setq arg (if arg (> (prefix-numeric-value arg) 0)
1474	      (= (plist-get (mpd-get-status conn) 'repeat) 0)))
1475  (mpd-simple-exec conn (concat "repeat " (if arg "1" "0"))))
1478(defun mpd-set-volume (conn vol)
1479  "Set the volume for the mpd player to volume VOL."
1480  (interactive
1481   (list mpd-inter-conn
1482	 (mpd-read-item
1483	  "New volume"
1484	  (and (eq (mpd-connection-status mpd-inter-conn) 'ready)
1485	       (plist-get (mpd-get-status mpd-inter-conn) 'volume))
1486	  t)))
1487  (mpd-assert-wholenump vol)
1488  (mpd-simple-exec conn (format "setvol %d" vol)))
1491(defun mpd-adjust-volume (conn vol)
1492  "Adjust the volume for the mpd player by volume VOL.
1493If VOL is positive, increase the volume, and decrease otherwise."
1494  (interactive (list mpd-inter-conn
1495		     (mpd-string-to-number-strict
1496		      (read-string "Adjust volume by: ") t)))
1497  (mpd-assert-numberp vol)
1498  (mpd-simple-exec conn (format "volume %d" vol)))
1501(defun mpd-set-crossfade (conn time)
1502  "Set cross-fading time for the mpd player to TIME in seconds.
1503Turn off cross-fading if TIME is 0."
1504  (interactive (list mpd-inter-conn
1505		     (mpd-read-item "New crossfade time in seconds"
1506				    (plist-get (mpd-get-status mpd-inter-conn)
1507					       'xfade) t)))
1508  (mpd-assert-wholenump time)
1509  (mpd-simple-exec conn (format "crossfade %d" time)))
1511(defvar mpd-inter-password-remember-queried nil)
1512(defvar mpd-inter-password nil)
1514;; For temporary binding
1515(defvar mpd-inter-password-inhibit-update nil)
1517(defun mpd-inter-password-update (conn when)
1518  (when (and (eq when 'post) (not mpd-inter-password-inhibit-update))
1519    (let ((passwd mpd-inter-password)
1520	  (mpd-inter-password-inhibit-update t))
1521      (or passwd (setq passwd (read-passwd "Enter MPD password: ")))
1522      (or (mpd-set-password conn passwd)
1523	  (message "Unable to set password: %s" (mpd-get-last-error conn))))))
1526(defun mpd-set-password (conn pass)
1527  "Set the password for access to the mpd server.
1528*WARNING* The password is sent to the server in plaintext. The processing done
1529by libmpdee to send the command for setting the password also has its data as
1530plaintext. When called interactively, offer to store and set the password on
1531reconnection. Note that this is not done when the call is not interactive.
1532Use hooked automatic mode (see `mpd-set-automatic-mode') to achieve the same."
1533  (interactive
1534   (let ((password (read-passwd "Enter password: ")))
1535     ;; Prevent password update triggering for this call itself
1536     ;; in case the connection is dead currently.
1537     (let ((mpd-inter-password-inhibit-update t))
1538       (mpd-conn-wakeup mpd-inter-conn))
1539     (if mpd-inter-password-remember-queried
1540	 (and mpd-inter-password (setq mpd-inter-password password))
1541       (when (yes-or-no-p "Do you want the password to be set again \
1542if we happen to reconnect? ")
1543	 (mpd-set-automatic-mode mpd-inter-conn 'mpd-inter-password-update)
1544	 (when (yes-or-no-p "Would you like me to remember your password \
1545for this session, in case it needs to be set again? ")
1546	   (setq mpd-inter-password password)))
1547       (setq mpd-inter-password-remember-queried t))
1548     (list mpd-inter-conn password)))
1549  (mpd-assert-string pass)
1550  (mpd-simple-exec conn (concat "password " (mpd-safe-string pass))))
1552(defun mpd-update-1 (conn path)
1553  "Internal function instructing the mpd server to update.
1554Please use `mpd-update' for updation purposes."
1555  (let ((response (mpd-execute-command
1556		   conn (mpd-make-cmd-concat "update" path))))
1557    (and (car response)
1558	 (string-to-number (cdr (assoc "updating_db" (cdr response)))))))
1561(defun mpd-update (conn &optional path ignore-timeout)
1562  "Instruct the mpd server using CONN to update its database.
1563PATH is the path or a list of paths to be updated. Note that PATH as nil updates
1564the root directory rather than not updating at all. Ignore connection timeout,
1565if IGNORE-TIMEOUT is non-nil and the connection is not in command list mode.
1566Return update job id on success."
1567  (interactive (list mpd-inter-conn (read-string "Enter relative path: ")))
1568  (if (or (not ignore-timeout) (mpd-command-list-mode-p conn))
1569      (mpd-update-1 conn path)
1570    (with-mpd-timeout-disabled (mpd-update-1 conn path))))
1573(defun mpd-output-enable (conn id)
1574  "Use connection CONN to enable mpd output ID."
1575  (interactive (list mpd-inter-conn (mpd-read-item "Output ID" 0 t)))
1576  (mpd-assert-wholenump id)
1577  (mpd-simple-exec conn (format "enableoutput %d" id)))
1580(defun mpd-output-disable (conn id)
1581  "Use connection CONN to disable mpd output ID."
1582  (interactive (list mpd-inter-conn (mpd-read-item "Output ID" 0 t)))
1583  (mpd-assert-wholenump id)
1584  (mpd-simple-exec conn (format "disableoutput %d" id)))
1586(defun mpd-ping (conn)
1587  "Use connection CONN to ping the mpd server.
1588Return non-nil on success."
1589  (mpd-simple-exec conn "ping"))
1591(defun mpd-clear-status-error (conn)
1592  "Use connection CONN to clear the error status of the mpd server.
1593Return non-nil on success."
1594  (mpd-simple-exec conn "clearerror"))
1596;;; Adapted from bbdb.el
1598(defun mpd-libmpdee-submit-bug-report ()
1599  "Interactively submit a bug report about `libmpdee'."
1600  (interactive)
1601  (eval-and-compile
1602    (require 'reporter)
1603    (require 'sendmail))
1604  (delete-other-windows)
1605  (reporter-submit-bug-report
1606   ""
1607   (concat "libmpdee " libmpdee-version)
1608   (append
1609    ;; all mpd connections
1610    (apropos-internal
1611     "" '(lambda (sym)
1612	   (and (boundp sym) (mpd-conn-strongp (symbol-value sym)))))
1613    ;; some variables
1614    '(emacs-version features))
1615   nil nil
1616   "Please change the Subject header to a concise bug description.
1617In this report, remember to cover the basics, that is,
1618what you expected to happen and what in fact did happen.
1619If you got an error, please enable debugging by
1620	M-x set-variable debug-on-error t
1621or if you have `dbfrobs', M-x debug-on-interesting-errors
1622Then reproduce the error and immediately call
1623	M-x mpd-libmpdee-submit-bug-report
1624The backtrace will be automatically included with the report.
1625Please remove these instructions from your message.")
1627  ;; insert the backtrace buffer content if present
1628  (let ((backtrace (get-buffer "*Backtrace*")))
1629    (when backtrace
1630      (goto-char (point-max))
1631      (insert "\nPossible backtrace for libmpdee:\n\n")
1632      (insert-buffer-substring backtrace)))
1634  (goto-char (point-min))
1635  (mail-position-on-field "Subject"))
1637(provide 'libmpdee)
1639;;; LIBMPDEE.EL ends here