Christophe Weblog Wiki Code Publications Music
allow squeeze completion on known squeeze player ids
[squeeze-el.git] / squeeze.el
index 33be4516a73d1df7e791a7a4578a1a0a2318947e..7dbf1202f3a658c63025d5d50d599716cc9b6622 100644 (file)
     (define-key map (kbd "TAB") 'completion-at-point)
     map))
 
+(defun squeeze-unhex-string (string)
+  (with-temp-buffer
+    (let ((case-fold-search t)
+          (start 0))
+      (while (string-match "%[0-9a-f][0-9a-f]" string start)
+        (let* ((s (match-beginning 0))
+               (ch1 (url-unhex (elt string (+ s 1))))
+               (code (+ (* 16 ch1)
+                        (url-unhex (elt string (+ s 2))))))
+          (insert (substring string start s)
+                  (byte-to-string code))
+          (setq start (match-end 0))))
+      (insert (substring string start)))
+    (buffer-string)))
+
 (defun squeeze-unhex-and-decode-utf8-string (string)
-  (decode-coding-string (url-unhex-string string) 'utf-8))
+  (decode-coding-string (squeeze-unhex-string string) 'utf-8))
 
 (define-derived-mode squeeze-mode comint-mode "Squeeze"
   "Major mode for interacting with the Squeezebox Server CLI.\\<squeeze-mode-map>"
 
 (defvar squeeze-control-inhibit-display nil)
 
-(lexical-let ((buffer ""))
+(lexical-let ((buffer (get-buffer-create " *squeeze-update-state*")))
   (defun squeeze-update-state (string)
+    ;; FIXME: we could make this a lot more elegant by using the
+    ;; buffer abstraction more
     (if (cl-position ?\n string)
         (let (done-something)
-          (setq string (concat buffer string))
+          (with-current-buffer buffer
+            (insert string)
+            (setq string (buffer-string))
+            (erase-buffer))
           (dolist (line (split-string string "\n"))
             (when (squeeze-update-state-from-line line)
               (setq done-something t)))
           (when done-something
             (unless squeeze-control-inhibit-display
-              (squeeze-control-display-players)))
-          (setq buffer ""))
-      (setq buffer (concat buffer string)))
+              (squeeze-control-display-players))))
+      (with-current-buffer buffer
+        (insert string)))
     string))
 
 (defconst squeeze-player-line-regexp
   (save-match-data
     (let (result)
       (loop
-       (if (string-match "\\([a-z]+\\)%3A\\([^ \n]+\\)" string start)
+       (if (string-match "\\([a-z_]+\\)%3A\\([^ \n]+\\)" string start)
            (let ((mend (match-end 0)))
              (when (> mend end)
                (return))
   (let ((count (squeeze-parse-count string))
         (startpos (string-match "playerindex" string))
         result endpos)
-    (dotimes (i (1- count))
-      (setq endpos (progn (string-match " connected%3A[0-1] " string startpos)
-                          (match-end 0)))
-      (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos endpos)) result)
-      (setq startpos endpos))
-    (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos (length string))) result)
+    (when (> count 0)
+      (dotimes (i (1- count))
+        (setq endpos (progn (string-match " connected%3A[0-1] " string startpos)
+                            (match-end 0)))
+        (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos endpos)) result)
+        (setq startpos endpos))
+      (push (apply 'squeeze-make-player (squeeze-string-plistify string startpos (length string))) result))
     result))
 
 
   (save-excursion
     (list (progn (backward-word) (point))
           (progn (forward-word) (point))
-          '(;; General commands and queries
-            "login" "can" "version" "listen" "subscribe" "pref"
-            "logging" "getstring" "setsncredentials" "debug"
-            "exit" "shutdown"
-
-            ;; Player commands and queries
-            "player" "count" "id" "uuid" "name" "ip" "model" "isplayer"
-            "displaytype" "canpoweroff" "?" "signalstrength" "connected"
-            "sleep" "sync" "syncgroups" "power" "mixer" "volume" "muting"
-            "bass" "treble" "pitch" "show" "display" "linesperscreen"
-            "displaynow" "playerpref" "button" "ir" "irenable"
-            "connect" "client" "forget" "disconnect" "players"
-            
-            ;; Database commands and queries
-            "rescan" "rescanprogress" "abortscan" "wipecache" "info"
-            "total" "genres" "artists" "albums" "songs" "years"
-            "musicfolder" "playlists" "tracks" "new" "rename" "delete"
-            "edit" "songinfo" "titles" "search" "pragma"
-
-            ;; Playlist commands and queries
-            "play" "stop" "pause" "mode" "time" "genre" "artist" "album"
-            "title" "duration" "remote" "current_title" "path" "playlist"
-            "add" "insert" "deleteitem" "move" "delete" "preview" "resume"
-            "save" "loadalbum" "addalbum" "loadtracks" "addtracks"
-            "insertalbum" "deletealbum" "clear" "zap" "name" "url"
-            "modified" "playlistsinfo" "index" "shuffle" "repeat"
-            "playlistcontrol"
-
-            ;; Compound queries
-            "serverstatus" "status" "displaystatus" "readdirectory"
-
-            ;; Notifications
-
-            ;; Alarm commands and queries
-            "alarm" "alarms"
-
-            ;; Plugins commands and queries
-            "favorites"
-            ))))
+          (append
+           (mapcar 'squeeze-player-playerid squeeze-players)
+           '(;; General commands and queries
+             "login" "can" "version" "listen" "subscribe" "pref"
+             "logging" "getstring" "setsncredentials" "debug"
+             "exit" "shutdown"
+
+             ;; Player commands and queries
+             "player" "count" "id" "uuid" "name" "ip" "model" "isplayer"
+             "displaytype" "canpoweroff" "?" "signalstrength" "connected"
+             "sleep" "sync" "syncgroups" "power" "mixer" "volume" "muting"
+             "bass" "treble" "pitch" "show" "display" "linesperscreen"
+             "displaynow" "playerpref" "button" "ir" "irenable"
+             "connect" "client" "forget" "disconnect" "players"
+
+             ;; Database commands and queries
+             "rescan" "rescanprogress" "abortscan" "wipecache" "info"
+             "total" "genres" "artists" "albums" "songs" "years"
+             "musicfolder" "playlists" "tracks" "new" "rename" "delete"
+             "edit" "songinfo" "titles" "search" "pragma"
+
+             ;; Playlist commands and queries
+             "play" "stop" "pause" "mode" "time" "genre" "artist" "album"
+             "title" "duration" "remote" "current_title" "path" "playlist"
+             "add" "insert" "deleteitem" "move" "delete" "preview" "resume"
+             "save" "loadalbum" "addalbum" "loadtracks" "addtracks"
+             "insertalbum" "deletealbum" "clear" "zap" "name" "url"
+             "modified" "playlistsinfo" "index" "shuffle" "repeat"
+             "playlistcontrol"
+
+             ;; Compound queries
+             "serverstatus" "status" "displaystatus" "readdirectory"
+
+             ;; Notifications
+
+             ;; Alarm commands and queries
+             "alarm" "alarms"
+
+             ;; Plugins commands and queries
+             "favorites"
+             )))))
 
 (defun squeeze-read-server-parameters (address port)
   (let ((host (read-string "Host: " nil nil address))