(defcustom squeeze-server-port 9090
"Port number for the Squeezebox server"
:group 'squeeze)
-
+(defcustom squeeze-server-http-port 9000
+ "Port number for the Squeezebox HTTP server"
+ :group 'squeeze)
(defvar squeeze-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "TAB") 'completion-at-point)
(define-key map (kbd "+") 'squeeze-control-volume-up)
(define-key map (kbd "-") 'squeeze-control-volume-down)
(define-key map (kbd "t") 'squeeze-control-toggle-syncgroup-display)
+ (define-key map (kbd ">") 'squeeze-control-next-track)
+ (define-key map (kbd "<") 'squeeze-control-previous-track)
+ (define-key map (kbd "s") 'squeeze-control-select-player)
+ (define-key map (kbd "a") 'squeeze-list-albums)
+ (define-key map (kbd "!") 'squeeze-control-reconnect)
map))
+(defun squeeze-control-current-player ()
+ (or squeeze-control-current-player
+ (if (= (length squeeze-players) 1)
+ (setq squeeze-control-current-player (squeeze-player-playerid (car squeeze-players)))
+ (call-interactively 'squeeze-control-select-player))))
+
+(defvar squeeze-control-current-player nil)
+
+(defun squeeze-control-select-player (id)
+ (interactive
+ (list (or (get-text-property (point) 'squeeze-playerid)
+ (let ((name (completing-read "Select player: " (mapcar 'squeeze-player-name squeeze-players))))
+ (squeeze-player-playerid (squeeze-find-player-from-name name))))))
+ (setq squeeze-control-current-player id))
+
+(defun squeeze-control-next-track ()
+ (interactive)
+ (squeeze-send-string "%s playlist index +1" (squeeze-control-current-player)))
+
+(defun squeeze-control-previous-track ()
+ (interactive)
+ (squeeze-send-string "%s playlist index -1" (squeeze-control-current-player)))
+
(define-derived-mode squeeze-control-mode special-mode "SqueezeControl"
"Major mode for controlling Squeezebox Servers.\\<squeeze-control-mode-map>")
(when (string= id (squeeze-player-playerid player))
(return player))))
+(defun squeeze-find-player-from-name (name)
+ (dolist (player squeeze-players)
+ (when (string= name (squeeze-player-name player))
+ (return player))))
+
(defun squeeze-update-power (player state)
(if state
(setf (squeeze-player-power player) state)
((string-match "^syncgroups" string)
(setq squeeze-syncgroups (squeeze-parse-syncgroups-line string))
t)
+ ((string-match "^albums" string)
+ (squeeze-parse-albums-line string)
+ t)
((string-match squeeze-player-line-regexp string)
(let ((substring (substring string (match-end 0)))
(id (url-unhex-string (match-string 1 string))))
(push (apply 'squeeze-make-player (squeeze-string-plistify string startpos (length string))) result))
result))
+(defcustom squeeze-artwork-directory "~/.emacs.d/squeeze/artwork/"
+ "Base directory for album and track artwork")
+
+(defvar squeeze-albums nil)
+
+(cl-defstruct (squeeze-album (:constructor squeeze-make-album))
+ id album artwork_track_id artist index)
+
+(defun squeeze-parse-albums-line (string)
+ (let ((count (squeeze-parse-count string))
+ (countpos (string-match "\\_<count%3a" string))
+ (start-index-pos (progn (string-match "^albums \\([0-9]+\\)\\>" string)
+ (match-beginning 1)))
+ index start end)
+ (unless squeeze-albums
+ (setq squeeze-albums (make-vector count nil)))
+ (when start-index-pos
+ (setq index (string-to-number (substring string start-index-pos)))
+ (setq start (string-match "\\_<id%3a" string start-index-pos))
+ (while start
+ (setq end (string-match "\\_<\\(id%3a\\|count%3a\\)" string (1+ start)))
+ (aset squeeze-albums index
+ (apply 'squeeze-make-album
+ :index index
+ (squeeze-string-plistify string start end)))
+ (incf index)
+ (setq start (if (= end countpos) nil end))))))
+
+(defun squeeze-get-albums ()
+ (squeeze-send-string "albums 0 1000 tags:lja")
+ (squeeze-accept-process-output))
+
+(defvar squeeze-albums-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "RET") 'squeeze-albums-load-album)
+ map))
+
+(define-derived-mode squeeze-albums-mode tabulated-list-mode "SqueezeAlbums"
+ "Major mode for displaying Albums from Squeezebox Servers.\\<squeeze-albums-mode-map>"
+ (setq tabulated-list-format [("Cover" 10 nil) ("Title" 30 t)])
+ (add-hook 'tabulated-list-revert-hook 'squeeze-get-albums nil t)
+ (setq tabulated-list-entries 'squeeze-albums-tabulated-list-entries)
+ (tabbar-mode -1)
+ (setq tabulated-list-use-header-line nil)
+ (tabulated-list-init-header))
+
+(defun squeeze-albums-artwork-callback (status artwork_track_id filename)
+ (unless status
+ (goto-char 1)
+ (let ((end (search-forward "\n\n")))
+ (delete-region 1 end)
+ (write-file (format "%s%s/%s" squeeze-artwork-directory artwork_track_id filename))
+ (kill-buffer))))
+
+(defun squeeze-albums-tabulated-list-entry (x)
+ (let* ((ati (squeeze-album-artwork_track_id x))
+ (file (format "%s%s/cover_50x50_o.jpg" squeeze-artwork-directory ati)))
+ (unless (file-exists-p file)
+ (make-directory (format "%s%s" squeeze-artwork-directory ati) t)
+ (let ((url (format "http://%s:%s/music/%s/cover_50x50_o.jpg"
+ squeeze-server-address squeeze-server-http-port ati)))
+ (url-queue-retrieve url 'squeeze-albums-artwork-callback
+ (list ati "cover_50x50_o.jpg") t t)))
+ (list x (vector (propertize (format "%s" ati)
+ 'display `(when (file-exists-p ,file)
+ image :type jpeg :file ,file))
+ (squeeze-album-album x)))))
+
+(defun squeeze-albums-tabulated-list-entries ()
+ (mapcar 'squeeze-albums-tabulated-list-entry (append squeeze-albums nil)))
(defun squeeze-complete-command-at-point ()
(save-excursion
(port (read-number "Port: " port)))
(cons host port)))
+(cl-defmacro with-squeeze-parameters ((address port) &body body)
+ (declare (indent 1))
+ `(progn
+ (unless ,address (setq ,address squeeze-server-address))
+ (unless ,port (setq ,port squeeze-server-port))
+ (when current-prefix-arg
+ (let ((parameters (squeeze-read-server-parameters ,address ,port)))
+ (setq ,address (car parameters)
+ ,port (cdr parameters))))
+ ,@body))
+
(defun squeeze (&optional address port)
(interactive)
- (unless address (setq address squeeze-server-address))
- (unless port (setq port squeeze-server-port))
- (when current-prefix-arg
- (let ((parameters (squeeze-read-server-parameters address port)))
- (setq address (car parameters)
- port (cdr parameters))))
- (let ((buffer (make-comint-in-buffer "squeeze" nil (cons address port))))
- (switch-to-buffer buffer)
- (squeeze-mode)))
+ (with-squeeze-parameters (address port)
+ (let ((buffer (make-comint-in-buffer "squeeze" nil (cons address port))))
+ (switch-to-buffer buffer)
+ (squeeze-mode))))
(defun squeeze-control (&optional address port)
(interactive)
- (unless address (setq address squeeze-server-address))
- (unless port (setq port squeeze-server-port))
- (when current-prefix-arg
- (let ((parameters (squeeze-read-server-parameters address port)))
- (setq address (car parameters)
- port (cdr parameters))))
- (let ((current-prefix-arg nil))
- (squeeze address port))
- (let ((buffer (get-buffer-create "*squeeze-control*")))
+ (with-squeeze-parameters (address port)
+ (let ((current-prefix-arg nil))
+ (squeeze address port))
+ (let ((buffer (get-buffer-create "*squeeze-control*")))
+ (switch-to-buffer buffer)
+ (squeeze-control-listen)
+ (squeeze-control-refresh)
+ (squeeze-control-display-players))))
+
+(defun squeeze-control-reconnect (&optional address port)
+ (interactive)
+ (with-squeeze-parameters (address port)
+ (kill-buffer (get-buffer "*squeeze*"))
+ (let ((current-prefix-arg nil))
+ (squeeze-control address port))))
+
+(defun squeeze-list-albums ()
+ (interactive)
+ (squeeze-get-albums)
+ (let ((buffer (get-buffer-create "*squeeze-albums*")))
(switch-to-buffer buffer)
- (squeeze-control-listen)
- (squeeze-control-refresh)
- (squeeze-control-display-players)))
+ (squeeze-albums-mode)
+ (tabulated-list-print)))
+
+(defun squeeze-albums-load-album ()
+ (interactive)
+ (squeeze-send-string "%s playlistcontrol cmd:load album_id:%s"
+ (squeeze-control-current-player)
+ (squeeze-album-id (tabulated-list-get-id))))
(provide 'squeeze)