aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Olson <mwolson@gnu.org>2006-01-04 07:52:00 +0000
committerMichael Olson <mwolson@gnu.org>2006-01-04 07:52:00 +0000
commitb64ef4cef7b58b20a952d78638125efd21a185ac (patch)
tree3dba5b44b76204d2822da7defd80615ef361a7de
parent7fe71e6381915e775deed571f7b5c0babdf0f394 (diff)
emms-player-mpd: Bring this up-to-par with the other backends, in that it can update the current playlist position and load the contents of the current playlist into MusicPD.
darcs-hash:20060104075237-1bfb2-09f9e463614cb8a2f067b16a7d197463e96c7718.gz
-rw-r--r--emms-player-mpd.el196
1 files changed, 150 insertions, 46 deletions
diff --git a/emms-player-mpd.el b/emms-player-mpd.el
index a236e58..fff4135 100644
--- a/emms-player-mpd.el
+++ b/emms-player-mpd.el
@@ -81,17 +81,16 @@
;; additional options available as well, but the defaults should be
;; sufficient for most uses.
+;; You will have to set `emms-player-mpd-sync-playlist' to non-nil if
+;; you want to use MusicPD in a similar way as most other EMMS
+;; backends. If your EMMS playlist contains music files rather than
+;; playlists, set this to non-nil, otherwise if your EMMS playlist
+;; contains stored playlists, leave this set to nil.
+
;;; TODO
-;; If you try to play individual songs, the tracks will not advance.
-;; I recommend playing playlists instead. This should be addresed
-;; eventually, though, perhaps with a timer watching the mpd process.
-;;
-;; It might also be good to "sync" the mpd playlist with the emms one.
-;; Currently we just clear the mpd playlist, add the track, and play,
-;; for each track. Not the best approach, unless your track is a
-;; playlist in itself, in which case all tracks from the playlist are
-;; added immediately after clearing the mpd playlist.
+;; Write a function that imports the current MusicPD playlist into
+;; EMMS.
(require 'emms-player-simple)
@@ -167,6 +166,25 @@ and errors."
:type 'boolean
:group 'emms-player-mpd)
+(defcustom emms-player-mpd-sync-playlist nil
+ "Whether to syncronize the EMMS playlist with the MusicPD playlist.
+
+If your EMMS playlist contains stored playlists, leave this set
+to nil.
+
+If your EMMS playlist contains music files rather than playlists,
+set this to non-nil."
+ :type 'boolean
+ :group 'emms-player-mpd)
+
+(defcustom emms-player-mpd-check-interval 2
+ "The number of seconds to wait before checking to see whether
+MusicPD has moved on to the next song.
+
+This is used only if `emms-player-mpd-sync-playlist' is non-nil"
+ :type 'integer
+ :group 'emms-player-mpd)
+
(define-emms-simple-player mpd '(file url playlist)
emms-player-mpd-supported-regexp "mpd")
@@ -187,6 +205,12 @@ and errors."
(defvar emms-player-mpd-process nil)
(defvar emms-player-mpd-returned-data nil)
+(defvar emms-player-mpd-playlist-id nil)
+(make-variable-buffer-local 'emms-player-mpd-playlist-id)
+
+(defvar emms-player-mpd-current-song nil)
+(defvar emms-player-mpd-status-timer nil)
+
(defun emms-player-mpd-sentinel (proc str)
"The process sentinel for MusicPD."
(let ((status (process-status proc)))
@@ -222,7 +246,8 @@ and errors."
(if (fboundp 'set-process-query-on-exit-flag)
(set-process-query-on-exit-flag emms-player-mpd-process nil)
(process-kill-without-query emms-player-mpd-process))
- ;; wait a bit for the process to finish starting
+ ;; wait a bit for the process to finish starting, as it likes to
+ ;; send us an "OK" message initially
(accept-process-output emms-player-mpd-process 0 200)))
(defun emms-player-mpd-send (command)
@@ -257,7 +282,7 @@ Otherwise, it will be nil."
(car cruft)
(cadr cruft))))
(setcdr cruft nil)
- (when (string-match "^OK MPD " (car data))
+ (when (string-match "^OK\\( MPD \\)?" (car data))
(setq data (cdr data)))
(if (string-match "^ACK \\[\\([0-9]+\\)@[0-9]+\\] \\(.+\\)" status)
(cons (cons (match-string 1 status)
@@ -273,14 +298,54 @@ The format of the alist is (name . value)."
(cdr info)) ; data exists
(let (alist)
(dolist (line (cdr info))
- (string-match "\\`\\([^:]+\\):\\s-*\\(.+\\)" line)
- (let ((name (match-string 1 line))
- (value (match-string 2 line)))
- (when (and name value)
- (setq name (downcase name))
- (add-to-list 'alist (cons name value) t))))
+ (when (string-match "\\`\\([^:]+\\):\\s-*\\(.+\\)" line)
+ (let ((name (match-string 1 line))
+ (value (match-string 2 line)))
+ (when (and name value)
+ (setq name (downcase name))
+ (add-to-list 'alist (cons name value) t)))))
alist)))
+(defun emms-player-mpd-get-playlist-id ()
+ "Get the current playlist ID from MusicPD."
+ (let ((info (emms-player-mpd-get-alist
+ (emms-player-mpd-parse-response
+ (emms-player-mpd-send-and-wait "status")))))
+ (cdr (assoc "playlist" info))))
+
+(defun emms-player-mpd-get-current-song ()
+ "Get the current song from MusicPD.
+This is in the form of a number that indicates the position of
+the song on the current playlist."
+ (let ((info (emms-player-mpd-get-alist
+ (emms-player-mpd-parse-response
+ (emms-player-mpd-send-and-wait "status")))))
+ (cdr (assoc "song" info))))
+
+(defun emms-player-mpd-sync-from-emms ()
+ "Synchronize the MusicPD playlist with the contents of the
+current EMMS playlist."
+ (emms-player-mpd-clear)
+ (with-current-emms-playlist
+ (save-excursion
+ (mapc #'emms-player-mpd-add
+ (nreverse
+ (emms-playlist-tracks-in-region (point-min) (point-max)))))
+ (setq emms-player-mpd-playlist-id (emms-player-mpd-get-playlist-id))))
+
+(defun emms-player-mpd-detect-song-change ()
+ "Detect whether a song change has occurred.
+This is usually called by a timer."
+ (let ((song (emms-player-mpd-get-current-song)))
+ (unless (or (null emms-player-mpd-current-song)
+ (null song)
+ (string= song emms-player-mpd-current-song))
+ (setq emms-player-mpd-current-song song)
+ (with-current-emms-playlist
+ (emms-playlist-select (progn
+ (goto-line (1+ (string-to-number song)))
+ (point)))))))
+
(defun emms-player-mpd-get-filename (file)
"Turn FILE into something that MusicPD can understand.
This usually means removing a prefix."
@@ -296,27 +361,27 @@ This usually means removing a prefix."
"Clear the playlist."
(emms-player-mpd-send "clear"))
-(defun emms-player-mpd-add (file)
+(defun emms-player-mpd-add-file (file)
"Add FILE to the current MusicPD playlist.
-If we do not succeed in adding the file, return the string from
-the process, nil otherwise."
+If we succeed in adding the file, return non-nil, nil otherwise."
(setq file (emms-player-mpd-get-filename file))
(let ((output (emms-player-mpd-parse-response
(emms-player-mpd-send-and-wait (concat "add " file)))))
- (when (car output)
- (when emms-player-mpd-verbose
- (message "MusicPD error: %s: %s" file (cdar output)))
- (cdar output))))
-
-(defun emms-player-mpd-load (playlist)
+ (if (car output)
+ (progn
+ (when emms-player-mpd-verbose
+ (message "MusicPD error: %s: %s" file (cdar output)))
+ nil)
+ t)))
+
+(defun emms-player-mpd-add-playlist (playlist)
"Load contents of PLAYLIST into MusicPD by adding each line.
This handles both m3u and pls type playlists."
;; This allows us to keep playlists anywhere and not worry about
;; having to mangle their names. Also, mpd can't handle pls
;; playlists by itself.
- (let ((pls-p (if (string-match "\\.pls\\'" playlist)
- t
- nil)))
+ (let ((pls-p (if (string-match "\\.pls\\'" playlist) t nil))
+ any-success)
(mapc #'(lambda (file)
(when pls-p
(if (string-match "\\`File[0-9]*=\\(.*\\)\\'" file)
@@ -324,31 +389,73 @@ This handles both m3u and pls type playlists."
(setq file "")))
(unless (or (string= file "")
(string-match "\\`#" file))
- (emms-player-mpd-add file)))
+ (when (emms-player-mpd-add-file file)
+ (setq any-success t))))
(split-string (with-temp-buffer
(insert-file-contents playlist)
(buffer-string))
- "\n"))))
-
-(defun emms-player-mpd-play ()
- "Play whatever is in the current MusicPD playlist."
- (emms-player-mpd-send "play"))
+ "\n"))
+ any-success))
+
+(defun emms-player-mpd-add (track)
+ "Add TRACK to the MusicPD playlist."
+ (let ((name (emms-track-get track 'name))
+ (type (emms-track-get track 'type)))
+ (cond ((eq type 'url)
+ (emms-player-mpd-add-file name))
+ ((or (eq type 'playlist)
+ (string-match "\\.\\(m3u\\|pls\\)\\'" name))
+ (emms-player-mpd-add-playlist name))
+ ((eq type 'file)
+ (emms-player-mpd-add-file name)))))
+
+;;; EMMS API
+
+(defun emms-player-mpd-play (&optional id)
+ "Play whatever is in the current MusicPD playlist.
+If ID is specified, play the song at that position in the MusicPD
+playlist."
+ (if id
+ (progn
+ (unless (stringp id)
+ (setq id (number-to-string id)))
+ (emms-player-mpd-send (concat "play " id))
+ (setq emms-player-mpd-current-song id))
+ (emms-player-mpd-send "play")))
+
+(defun emms-player-mpd-start-and-sync (track)
+ "Starts a process playing TRACK.
+This is called if `emms-player-mpd-sync-playlist' is non-nil.
+
+It ensures that MusicPD's playlist is up-to-date with EMMS's
+playlist, and then plays the current track."
+ (let ((id (emms-player-mpd-get-playlist-id)))
+ (unless (and (stringp emms-player-mpd-playlist-id)
+ (string= emms-player-mpd-playlist-id id))
+ (emms-player-mpd-sync-from-emms))
+ (with-current-emms-playlist
+ (emms-player-mpd-play (1- (line-number-at-pos
+ emms-playlist-selected-marker)))))
+ (unless emms-player-mpd-status-timer
+ (setq emms-player-mpd-status-timer
+ (run-at-time t emms-player-mpd-check-interval
+ 'emms-player-mpd-detect-song-change))))
(defun emms-player-mpd-start (track)
"Starts a process playing TRACK."
(interactive)
- (emms-player-mpd-clear)
- (let ((name (emms-track-get track 'name)))
- ;; If it's a playlist, we have to `load' rather than `add' it
- (if (string-match "\\.\\(m3u\\|pls\\)\\'" name)
- (emms-player-mpd-load name)
- (emms-player-mpd-add name)))
- ;; Now that we've added/loaded the file/playlist, play it
- (emms-player-mpd-play))
+ (if emms-player-mpd-sync-playlist
+ (emms-player-mpd-start-and-sync track)
+ (emms-player-mpd-clear)
+ (when (emms-player-mpd-add track)
+ ;; if we have loaded the item successfully, play it
+ (emms-player-mpd-play))))
(defun emms-player-mpd-stop ()
"Stop the currently playing song."
(interactive)
+ (emms-cancel-timer emms-player-mpd-status-timer)
+ (setq emms-player-mpd-status-timer nil)
(emms-player-mpd-send "stop"))
(defun emms-player-mpd-pause ()
@@ -363,9 +470,6 @@ This handles both m3u and pls type playlists."
(if (> sec 0) "+" "")
sec)))
-;; Not currently used by the API (to my knowledge), but I make use of
-;; these to advance my playlists.
-
(defun emms-player-mpd-next ()
"Move forward by one track in MusicPD's internal playlist."
(interactive)