X-Git-Url: http://christophe.rhodes.io/gitweb/?p=squeeze-el.git;a=blobdiff_plain;f=squeeze.el;h=3808a20f117ddbd86028122c7061c4a873336453;hp=7dbf1202f3a658c63025d5d50d599716cc9b6622;hb=HEAD;hpb=54a98afcc7041cfbc06c3652703c3ad690dc86a8 diff --git a/squeeze.el b/squeeze.el index 7dbf120..3808a20 100644 --- a/squeeze.el +++ b/squeeze.el @@ -9,7 +9,9 @@ (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) @@ -47,8 +49,36 @@ (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.\\") @@ -82,6 +112,11 @@ (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) @@ -98,6 +133,8 @@ (and current (max 0 (min 100 (+ current number))))) (setf (squeeze-player-volume player) number)))) +(require 'notifications) + (defun squeeze-update-state-from-line (string) (cond ((string-match "^players 0" string) @@ -106,10 +143,18 @@ ((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)))) (cond + ((string-match "^playlist newsong \\(.*\\) \\([0-9]+\\)$" substring) + (let ((value (save-match-data (url-unhex-string (match-string 1 substring)))) + (index (url-unhex-string (match-string 2 substring)))) + (notifications-notify :title "Now playing" :body (encode-coding-string (format "%s: %s" index value) 'utf-8))) + t) ((string-match "^power\\(?: \\([01]\\)\\)?" substring) (let ((state (match-string 1 substring)) (player (squeeze-find-player id))) @@ -332,8 +377,9 @@ (let ((syncgroups squeeze-syncgroups) (seen)) (while syncgroups - (let ((names (getf syncgroups :names)) - (members (split-string (getf syncgroups :members) ","))) + (let ((names (getf syncgroups :sync_member_names)) + ;; new server version has sync_members and sync_member_names + (members (split-string (getf syncgroups :sync_members) ","))) (insert (propertize names 'face 'squeeze-syncgroup-face) "\n") (dolist (member members) (let ((player (squeeze-find-player member))) @@ -403,6 +449,76 @@ (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 "\\_" 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 "\\_" + (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 @@ -455,32 +571,54 @@ (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)