[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
squeeze.el v. 0.0.2
From: |
paul . huff |
Subject: |
squeeze.el v. 0.0.2 |
Date: |
Tue, 08 May 2007 22:41:41 -0600 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.0.90 (darwin) |
This is my first post here, and I'm kind of a rookie elisper, so
any comments/suggestion or fan/hate-mail would be appreciated.
The following file is version 0.0.2 of squeeze.el which adds a bunch
of squeeze box functionality to emacs. You can find the squeezebox
here: http://slimdevices.com.
;;; squeeze.el --- Access your squeezebox from emacs.
;; Copyright (C) 2007 Paul Huff <address@hidden>
;; Author: Paul Huff <address@hidden>
;; Keywords: convenience
;; squeeze is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
;; {at your option} any later version.
;; squeeze is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING, or type `C-h C-c'. If
;; not, write to the Free Software Foundation at this address:
;; Free Software Foundation
;; 51 Franklin Street, Fifth Floor
;; Boston, MA 02110-1301
;; USA
;;; Commentary:
;; This is version 0.0.2, so I've only tested it on my own version of emacs
which is currently:
;; GNU Emacs 22.0.90.1 (i386-apple-darwin8.8.1, Carbon Version 1.6.0) of
2006-10-28
;; Not sure if it'll work anywhere else, but it doesn't require anything
apple-ish, just emacs-ish.
;; The following basic commands are implemented, currently:
;; squeeze-pause, squeeze-play, squeeze-stop, squeeze-volume-up,
squeeze-volume-down
;; squeeze-next, squeeze-prev, squeeze-browse-artists, squeeze-browse-genres,
squeeze-browse-artists,
;; squeeze-browse-songs, squeeze-now-playing
;; Version 0.0.2 adds squeeze-fast-forward and squeeze-rewind as well as
;; an early attempt at an ediff-like control panel.
;; |> next to an item means load as playlist, and + means add to current
playlist.
;; In future versions I'd like to flesh out the navigation of the playlist,
load saved playlists, allow for more than
;; one squeezebox, etc., but for right now, this satisfies my most basic needs
with my squeezebox. Please feel free to send patches.
;; sourceforge.net project page: http://sf.net/projects/squeeze-el
;; Web page coming soon.
;; I put the following things in my .emacs:
;; (global-set-key [(f6)] 'squeeze-now-playing)
;; (global-set-key [(f7)] 'squeeze-volume-down)
;; (global-set-key [(f8)] 'squeeze-volume-up)
;; Additionally, I run my emacs and slimserver on the same box, so I've set
slimserver-host to be localhost. You'll need to over-write that with a (setq)
in your .emacs, or use the customization options.
;; I'm a newbie elisper, and I'm relatively new to my squeezebox, so please let
me know if I'm a. doing things the wrong way, b.
;; making things work they way they shouldn't in elisp or slim-world.
;;; History:
;;
;;; Code:
;;;###autoload
(provide 'squeeze)
(require 'url-util)
(require 'button)
(defcustom slimserver-host "localhost" "The host your slimserver runs on" )
(defcustom slimserver-port 9090 "The port your slimserver CLI is running on")
(setq squeeze-fast-forwarding nil)
(setq squeeze-rewinding nil)
(defun squeeze-url-encode (in_string)
(let* ((parts (split-string in_string ":"))
(first (nth 0 parts))
(second (cond ((< 1 (length (cdr parts)))
(mapconcat 'identity (cdr parts) ":"))
((equal 1 (length (cdr parts)))
(cadr parts))
(t nil))))
(if (equal second nil)
(url-encode first)
(concat first (url-encode ":") (url-encode second)))))
(defun url-encode (string)
(apply 'concat
(mapcar (lambda (c)
(if (or (and (>= c ?a) (<= c ?z))
(and (>= c ?A) (<= c ?Z))
(and (>= c ?0) (<= c ?9)))
(string c)
(format "%%%02X" c)))
(encode-coding-string string 'utf-8))))
(defun squeeze-flatten (lis)
"Removes nestings from a list. Thank you random site on the internet: "
"http://cogsci.uwaterloo.ca/CoherenceCode/COHERE/utilities.lisp.html"
(cond ((atom lis) lis)
((listp (car lis))
(append (squeeze-flatten (car lis)) (squeeze-flatten (cdr lis)))
)
(t (append (list (car lis)) (squeeze-flatten (cdr lis))))))
(defun slimserver-command (command)
(let* ((slimserver-process
(open-network-stream "slim-connection" "slim-buffer" "localhost"
9090))
(buf (process-buffer slimserver-process))
(response ""))
(process-send-string slimserver-process (concat command "\n"))
(accept-process-output slimserver-process 5)
(with-current-buffer buf
(set-buffer-multibyte nil)
(setq response (replace-regexp-in-string "\n$" "" (buffer-string)))
(kill-buffer buf)
(delete-process slimserver-process))
response))
(defun slimserver-command-no-decode (&rest command-parts)
(let* ((flattened-command-parts (squeeze-flatten command-parts))
(slimserver-process
(open-network-stream "slim-connection" "slim-buffer" "localhost"
9090))
(buf (process-buffer slimserver-process))
(command (mapconcat 'squeeze-url-encode flattened-command-parts " "))
(response ""))
(message "Sending command: %s" command)
(process-send-string slimserver-process (concat command "\n"))
(accept-process-output slimserver-process 5)
(with-current-buffer buf
(set-buffer-multibyte nil)
(setq response (replace-regexp-in-string "\n$" "" (buffer-string)))
(kill-buffer buf)
(delete-process slimserver-process))
response))
;;A big thanks to fledermaus for helping me out.
(defun url-unhex-utf-8 (in_string)
(interactive)
(decode-coding-string (url-unhex-string in_string t) 'utf-8))
(defun slimserver-get-player-id (which-player)
(let* ((player-id-string
(url-unhex-utf-8 (slimserver-command (concat "player id "
(int-to-string which-player) " ?"))))
(player-id-string-parts (split-string player-id-string " "))
(player-id (nth (- (length player-id-string-parts) 1)
player-id-string-parts)))
player-id))
;;;###autoload
(defun squeeze-now-playing ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0))
(song (replace-regexp-in-string
(concat player-id " current_title ") ""
(url-unhex-utf-8 (slimserver-command (concat player-id "
current_title ?")))))
(artist (replace-regexp-in-string
(concat player-id " artist ") ""
(url-unhex-utf-8 (slimserver-command (concat player-id "
artist ?")))))
(album (replace-regexp-in-string
(concat player-id " album ") ""
(url-unhex-utf-8 (slimserver-command (concat player-id "
album ?"))))))
(message "%s" (concat "Now playing: " song " from \"" album "\" - "
artist))))
;;;###autoload
(defun squeeze-stop ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " stop"))
(message "Sent stop command")))
;;;###autoload
(defun squeeze-on ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " power 1"))
(message "Powering on player")))
;;;###autoload
(defun squeeze-off ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " power 0"))
(message "Powering off player")))
;;;###autoload
(defun squeeze-volume-up ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " button volume_up"))))
;;;###autoload
(defun squeeze-volume-down ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " button volume_down"))))
;;;###autoload
(defun squeeze-play ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(if (or (equal squeeze-fast-forwarding t)
(equal squeeze-rewinding t))
(progn (slimserver-command (concat player-id " rate 1"))
(setq squeeze-fast-forwarding nil)
(setq squeeze-rewinding nil)))
(slimserver-command (concat player-id " play"))
(message "Playing player")))
;;;###autoload
(defun squeeze-fast-forward ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0))
(current-rate (string-to-number (replace-regexp-in-string (concat
player-id " rate") "" (url-unhex-utf-8 (slimserver-command (concat player-id "
rate ?")))))))
(if (< current-rate 1) ;; If we're paused or rewinding, pretend that
we're playing (play rate = 1 = playing...)
(setq current-rate 1))
(slimserver-command (concat player-id " rate " (int-to-string (+ 1
current-rate))))
(setq squeeze-fast-forwarding t)
(message "Fast forwarding")))
;;;###autoload
(defun squeeze-rewind ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0))
(current-rate (string-to-number (replace-regexp-in-string (concat
player-id " rate") "" (url-unhex-utf-8 (slimserver-command (concat player-id "
rate ?")))))))
(if (> current-rate 0) ;; If we're playing or fastforwarding, pretend that
we're paused (play rate = 0 = pause...)
(setq current-rate 0))
(slimserver-command (concat player-id " rate " (int-to-string (-
current-rate 1))))
(setq squeeze-rewinding t)
(message "Rewinding")))
;;;###autoload
(defun squeeze-pause ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " pause"))
(message "Pausing player")))
;;;###autoload
(defun squeeze-next ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " playlist index +1"))))
;;;###autoload
(defun squeeze-prev ()
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " playlist index -1"))))
(defun squeeze-get-genres ()
(interactive)
(let* ((genre-count (replace-regexp-in-string "genres count:" ""
(url-unhex-utf-8 (slimserver-command "genres"))))
(genre-list-text (replace-regexp-in-string (concat "genres 0 "
genre-count " count%3A" genre-count) ""
(slimserver-command (concat
"genres 0 " genre-count))))
(n 0)
(genre-list-intermediate (split-string genre-list-text))
(genre-list-return '()))
(progn
(while (< n (string-to-number genre-count))
(setq genre-list-return (cons (cons (url-unhex-utf-8 (car
genre-list-intermediate)) (url-unhex-utf-8 (cadr genre-list-intermediate)))
genre-list-return))
(setq genre-list-intermediate (cddr genre-list-intermediate))
(setq n (+ n 1)))
genre-list-return)))
(defun squeeze-assoc-list-remove-tags (in-list)
"Though it offends my sensibilities, elisp's lack of deep recursion
makes me afraid to write this recursively because we might use it on
a list of 2000+ songs...
This function takes a assoc list and removes all the little tags
that the slimserver adds to each part."
(interactive)
(let* ((n 0)
(result-list '()))
(while (< n (length in-list))
(setq result-list (cons (cons (replace-regexp-in-string "[[:word:]]+:" ""
(car (nth n in-list)))
(replace-regexp-in-string "[[:word:]]+:" "" (cdr (nth n
in-list)))) result-list))
(setq n (+ n 1))
)
result-list))
(defun squeeze-str-cmp-p (arg1 arg2)
(string< (cdr arg1) (cdr arg2)))
(defun squeeze-browse-genres ()
(interactive)
(let ((genre-list (sort (squeeze-assoc-list-remove-tags (squeeze-get-genres))
'squeeze-str-cmp-p)))
(with-output-to-temp-buffer "*Squeeze Browse*"
(set-buffer "*Squeeze Browse*")
(insert "Please pick a genre...\n")
(mapc
(lambda (c)
(let ((n 4))
(insert "\n ")
(insert-text-button
(cdr c)
'action (lambda (b)
(squeeze-browse-artists (concat "genre_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"|>"
'action (lambda (b)
(squeeze-load-thingee (concat "genre_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"+"
'action (lambda (b)
(squeeze-add-thingee (concat "genre_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
))
genre-list))))
(defun squeeze-get-artists (&rest qualifiers)
(interactive)
(let* ((qualifier-string (mapconcat 'identity (squeeze-flatten qualifiers) "
"))
(encoded-qualifier-string (mapconcat 'squeeze-url-encode
(squeeze-flatten qualifiers) " "))
(total-artist-count (replace-regexp-in-string "info total artists " ""
(url-unhex-utf-8 (slimserver-command "info total artists ?"))))
(artist-list-text (replace-regexp-in-string (concat "artists 0 "
total-artist-count " " encoded-qualifier-string) ""
(slimserver-command
(concat "artists 0 " total-artist-count " " encoded-qualifier-string))))
(n 0)
(artist-list-split (split-string artist-list-text))
(artist-list-intermediate (cdr artist-list-split))
(artist-count (replace-regexp-in-string "count:" ""
(url-unhex-utf-8 (car
artist-list-split))))
(artist-list-return '()))
(progn
(while (< n (string-to-number artist-count))
(setq artist-list-return (cons (cons (url-unhex-utf-8 (car
artist-list-intermediate)) (url-unhex-utf-8 (cadr artist-list-intermediate)))
artist-list-return))
(setq artist-list-intermediate (cddr artist-list-intermediate))
(setq n (+ n 1)))
artist-list-return)))
(defun squeeze-browse-artists (&rest args)
(interactive)
(let ((artist-list (if (< 0 (length args))
(sort (squeeze-assoc-list-remove-tags
(squeeze-get-artists args)) 'squeeze-str-cmp-p)
(sort (squeeze-assoc-list-remove-tags
(squeeze-get-artists)) 'squeeze-str-cmp-p))))
(with-output-to-temp-buffer "*Squeeze Browse*"
(set-buffer "*Squeeze Browse*")
(insert "Please pick an artist...\n")
(mapc
(lambda (c)
(let ((n 4))
(insert "\n ")
(insert-text-button
(cdr c)
'action (lambda (b)
(squeeze-browse-albums (concat "artist_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"|>"
'action (lambda (b)
(squeeze-load-thingee (concat "artist_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"+"
'action (lambda (b)
(squeeze-add-thingee (concat "artist_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
))
artist-list))))
(defun squeeze-get-albums (&rest qualifiers)
(interactive)
(let* ((qualifier-string (mapconcat 'identity (squeeze-flatten qualifiers) "
"))
(encoded-qualifier-string (mapconcat 'squeeze-url-encode
(squeeze-flatten qualifiers) " "))
(total-artist-count (replace-regexp-in-string "info total albums " ""
(url-unhex-utf-8 (slimserver-command "info total albums ?"))))
(artist-list-text (replace-regexp-in-string (concat "albums 0 "
total-artist-count " " encoded-qualifier-string) ""
(slimserver-command
(concat "albums 0 " total-artist-count " " encoded-qualifier-string))))
(n 0)
(artist-list-split (split-string artist-list-text))
(artist-list-intermediate (cdr artist-list-split))
(artist-count (replace-regexp-in-string "count:" ""
(url-unhex-utf-8 (car
artist-list-split))))
(artist-list-return '()))
(progn
(while (< n (string-to-number artist-count))
(setq artist-list-return (cons (cons (url-unhex-utf-8 (car
artist-list-intermediate)) (url-unhex-utf-8 (cadr artist-list-intermediate)))
artist-list-return))
(setq artist-list-intermediate (cddr artist-list-intermediate))
(setq n (+ n 1)))
artist-list-return)))
(defun squeeze-browse-albums (&rest args)
(interactive)
(let ((artist-list (if (< 0 (length args))
(sort (squeeze-assoc-list-remove-tags
(squeeze-get-albums args)) 'squeeze-str-cmp-p)
(sort (squeeze-assoc-list-remove-tags
(squeeze-get-albums)) 'squeeze-str-cmp-p))))
(with-output-to-temp-buffer "*Squeeze Browse*"
(set-buffer "*Squeeze Browse*")
(insert "Please pick an album...\n")
(mapc
(lambda (c)
(let ((n 4))
(insert "\n ")
(insert-text-button
(cdr c)
'action (lambda (b)
(squeeze-browse-songs (concat "album_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"|>"
'action (lambda (b)
(squeeze-load-thingee (concat "album_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"+"
'action (lambda (b)
(squeeze-add-thingee (concat "album_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
))
artist-list))))
(defun squeeze-get-songs (&rest qualifiers)
(interactive)
(let* ((qualifier-string (mapconcat 'identity (squeeze-flatten qualifiers) "
"))
(encoded-qualifier-string (mapconcat 'squeeze-url-encode
(squeeze-flatten qualifiers) " "))
(total-song-count (replace-regexp-in-string "info total songs " ""
(url-unhex-utf-8 (slimserver-command "info total songs ?"))))
(song-list-text (replace-regexp-in-string (concat "songs 0 "
total-song-count " " encoded-qualifier-string " sort%3Atracknum tags%3A") ""
(slimserver-command
(concat "songs 0 " total-song-count " " encoded-qualifier-string "
sort:tracknum tags:"))))
(n 0)
(song-list-split (split-string song-list-text))
(song-list-intermediate (cdr song-list-split))
(song-count (replace-regexp-in-string "count:" ""
(url-unhex-utf-8 (car
song-list-split))))
(song-list-return '()))
(progn
(while (< n (string-to-number song-count))
(setq song-list-return (cons (cons (url-unhex-utf-8 (car
song-list-intermediate)) (url-unhex-utf-8 (cadr song-list-intermediate)))
song-list-return))
(setq song-list-intermediate (cdddr song-list-intermediate))
(setq n (+ n 1)))
song-list-return)))
(defun squeeze-browse-songs (&rest args)
(interactive)
(let ((song-list (if (< 0 (length args))
(squeeze-assoc-list-remove-tags (squeeze-get-songs
args))
(squeeze-assoc-list-remove-tags (squeeze-get-songs)))))
(with-output-to-temp-buffer "*Squeeze Browse*"
(set-buffer "*Squeeze Browse*")
(insert "Please pick a song...\n")
(if (< 0 (length args))
(progn (insert " ")
(insert-text-button "(Play all)" 'action (lambda (b)
(squeeze-load-thingee (button-get b 'expression))) 'expression (mapconcat
'identity args " ") 'follow-link t)
(insert " ")
(insert-text-button "(Load all)" 'action (lambda (b)
(squeeze-add-thingee (button-get b 'expression))) 'expression (mapconcat
'identity args " ") 'follow-link t)))
(mapc
(lambda (c)
(let ((n 4))
(insert "\n ")
(insert-text-button
(cdr c)
'action (lambda (b)
(squeeze-songinfo (concat "track_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"|>"
'action (lambda (b)
(squeeze-load-thingee (concat "track_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t)
(insert " ")
(insert-text-button
"+"
'action (lambda (b)
(squeeze-add-thingee (concat "track_id:" (button-get b
'expression))))
'expression (car c) 'follow-link t 'follow-link t)
))
song-list))))
(defun squeeze-insert-thingee (thingee)
"Insert thingee at the begining of the playlist for player 0. It's presumed
that thingee is not in need of url-encoding."
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " playlistcontrol cmd:insert "
thingee))))
(defun squeeze-add-thingee (thingee)
"Add thingee to the end of the playlist for player 0. It's presumed that
thingee is not in need of url-encoding."
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " playlistcontrol cmd:add "
thingee))))
(defun squeeze-load-thingee (thingee)
"Load thingee as the playlist for player 0. It's presumed that thingee is not
in need of url-encoding."
(interactive)
(let* ((player-id (slimserver-get-player-id 0)))
(slimserver-command (concat player-id " playlistcontrol cmd:load "
thingee))))
;;;###autoload
(defun squeeze-play-url (url)
"Play a url."
(interactive)
(let* ((player-id (slimserver-get-player-id 0))
(encoded-url (squeeze-url-encode url)))
(slimserver-command (concat player-id " playlist play " encoded-url))))
(defconst squeeze-control-frame-parameters1
(list
'(name . "Squeeze")
;;'(unsplittable . t)
'(minibuffer . nil)
'(user-position . t) ; Emacs only
'(vertical-scroll-bars . nil) ; Emacs only
'(scrollbar-width . 0) ; XEmacs only
'(scrollbar-height . 0) ; XEmacs only
'(menu-bar-lines . 0) ; Emacs only
'(tool-bar-lines . 0) ; Emacs 21+ only
'(left-fringe . 0)
'(right-fringe . 0)
;; don't lower but auto-raise
'(auto-lower . nil)
'(auto-raise . t)
;; make initial frame small to avoid distraction
'(width . 25) '(height . 2)
;; this blocks queries from window manager as to where to put
;; squeeze's control frame. we put the frame outside the display,
;; so the initial frame won't jump all over the screen
(cons 'top (if (fboundp 'squeeze-display-pixel-height)
(1+ (squeeze-display-pixel-height))
3000))
(cons 'left (if (fboundp 'squeeze-display-pixel-width)
(1+ (squeeze-display-pixel-width))
3000))
))
(defcustom squeeze-display-panel 'f "Whether squeezebox panel frame is
displayed")
(setq squeeze-ctrl-frame nil)
;;;###autoload
(defun squeeze-toggle-panel ()
"Toggle the squeeze panel frame."
(interactive)
(let ((populate 'f))
(if (equal squeeze-ctrl-frame nil)
(setq squeeze-ctrl-frame (make-frame squeeze-control-frame-parameters1))
(progn
(delete-frame squeeze-ctrl-frame t)
(kill-buffer "*Squeeze Panel*")
(setq squeeze-ctrl-frame nil)))
(if (not (equal squeeze-ctrl-frame nil)) ;;Probably a much more lispish
way to do this. Perhaps roll all the rest of this in a (progn) in the first
equal?
(progn
(select-frame squeeze-ctrl-frame)
(if (equal (get-buffer "*Squeeze Panel*") nil)
(setq populate 't))
(set-buffer (get-buffer-create "*Squeeze Panel*"))
(if populate
(progn
(insert " ")
(insert-text-button "<<" 'action (lambda (b) (squeeze-prev))
'follow-link t)
(insert " ")
(insert-text-button "<" 'action (lambda (b) (squeeze-rewind))
'follow-link t)
(insert " ")
(insert-text-button "||" 'action (lambda (b) (squeeze-pause))
'follow-link t)
(insert " ")
(insert-text-button "|>" 'action (lambda (b) (squeeze-play))
'follow-link t)
(insert " ")
(insert-text-button ">" 'action (lambda (b)
(squeeze-fast-forward)) 'follow-link t)
(insert " ")
(insert-text-button ">>" 'action (lambda (b) (squeeze-next))
'follow-link t)))
(switch-to-buffer "*Squeeze Panel*")))))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- squeeze.el v. 0.0.2,
paul . huff <=