Audacious Dynamic Playlist powered by inotify

July 13, 2010 at 12:22 AM | categories: python, linux | View Comments

I was playing around with StreamRipper today to record a shoutcast stream I enjoy, and I thought to myself: wouldn't it be nice to be able to continuously play all the files I've downloaded without having to manually queue the new files in Audacious?

So, I scratched an itch. With a little DBUS, pyinotify, and an optparse wrapper, I now have a tool to automatically add the tracks to my Audacious playlist. You can grab the latest version on github.

Streamripper does have a relay option (-r) to allow you to listen to the same stream as you're ripping it, and if that's what you want, the following script is superfluous. However, what I wanted was something slightly different: I didn't necessarily want to just listen to the stream live, instead, I wanted the ability to jump around between the tracks already downloaded, exploring different songs, but with the list of tracks ever expanding. That's what this script allows.

You'll need python-dbus and setuptools installed, then just install with:

easy_install http://github.com/EnigmaCurry/audacious-plugins/zipball/master

Startup StreamRipper with your favorite audio stream and point the tool at your target directory:

python -m ec_audacious.dynamic_filesystem_playlist /path/to/your/streamripper_location

Now when StreamRipper creates a new file, it will get automatically (instantaeneously actually, thanks to inotify!) to your Audacious playlist.

Alternatively, you can use the streamripper wrapper script that I've included to start both the filesystem monitor as well as streamripper in one go:

Put the following in your .bashrc file or somewhere equivalent:

alias streamripper="python -m ec_audacious.streamripper"

Then whenever you run streamripper, you'll actually be running the wrapper script instead:

streamripper http://your-cool-stream.com:8000 --audacious

By aliasing streamripper to point to the ec_audacious.streamripper wrapper script, we're effectively adding a new option to streamripper called --audacious which spawns our filesystem monitor.

Read and Post Comments

AutoComplete.el : Python Code Completion in Emacs

January 21, 2009 at 10:01 PM | categories: python, emacs, linux | View Comments

A friend of mine and I like to show off to each other little Emacs tips and tricks we learn. Today, he introduced to me the very cool AutoComplete.el package. AutoComplete.el is intriguing to me because, unlike ropemacs mode which I've blogged about before, the completions AutoComplete.el provides are inline with your code in a dropdown box instead of in a seperate window (windows in Emacs are what most people call frames). However, AutoComplete is only a completion framework, it doesn't know anything about Python. Instead, it allows the user to plug into it, feeding it whatever sorts of intelligent completion you want, including Rope.

Setup

The two most important completions I want to integrate into it are Rope and yasnippet. AutoComplete can handle both of them nicely. You'll need to install the very latest development version (as of December '08) of Rope, Ropemacs and Ropemode:

sudo apt-get install mercurial
mkdir /tmp/rope && cd /tmp/rope
hg clone http://bitbucket.org/agr/rope
hg clone http://bitbucket.org/agr/ropemacs
hg clone http://bitbucket.org/agr/ropemode
sudo easy_install rope
ln -s ../ropemode/ropemode ropemacs/
sudo easy_install ropemacs

You'll also need to install Pymacs and Yasnippet if you haven't already:

mkdir -p ~/.emacs.d/vendor && cd ~/.emacs.d/vendor
wget http://pymacs.progiciels-bpi.ca/archives/Pymacs-0.23.tar.gz
tar xfv Pymacs-0.23.tar.gz
cd Pymacs-0.23
make
sudo easy_install .
cd ~/.emacs.d/vendor
wget http://yasnippet.googlecode.com/files/yasnippet-0.5.9.tar.bz2
tar xfv yasnippet-0.5.9.tar.bz2
cd ~/.emacs.d
ln -s vendor/yasnippet-0.5.9/snippets/ .

Make sure Pymacs and Yasnippet get into your load path, in your .emacs:

(add-to-list 'load-path "~/.emacs.d/vendor")
(progn (cd "~/.emacs.d/vendor")
       (normal-top-level-add-subdirs-to-load-path))

Install AutoComplete.el 0.1.0 (0.2.0 is now out, and is not compatible with this post, I'll try and update later):

cd ~/.emacs.d/vendor
wget http://www.emacswiki.org/emacs/?action=browse;id=auto-complete.el;raw=1;revision=5

Now to add some more elisp to your .emacs somewhere:

(require 'python)
(require 'auto-complete)
(require 'yasnippet)

(autoload 'python-mode "python-mode" "Python Mode." t)
(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
(add-to-list 'interpreter-mode-alist '("python" . python-mode))

;; Initialize Pymacs                                                                                           
(autoload 'pymacs-apply "pymacs")
(autoload 'pymacs-call "pymacs")
(autoload 'pymacs-eval "pymacs" nil t)
(autoload 'pymacs-exec "pymacs" nil t)
(autoload 'pymacs-load "pymacs" nil t)
;; Initialize Rope                                                                                             
(pymacs-load "ropemacs" "rope-")
(setq ropemacs-enable-autoimport t)

;; Initialize Yasnippet                                                                                        
;Don't map TAB to yasnippet                                                                                    
;In fact, set it to something we'll never use because                                                          
;we'll only ever trigger it indirectly.                                                                        
(setq yas/trigger-key (kbd "C-c <kp-multiply>"))
(yas/initialize)
(yas/load-directory "~/.emacs.d/snippets")



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                         
;;; Auto-completion                                                                                            
;;;  Integrates:                                                                                               
;;;   1) Rope                                                                                                  
;;;   2) Yasnippet                                                                                             
;;;   all with AutoComplete.el                                                                                 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                         
(defun prefix-list-elements (list prefix)
  (let (value)
    (nreverse
     (dolist (element list value)
      (setq value (cons (format "%s%s" prefix element) value))))))
(defvar ac-source-rope
  '((candidates
     . (lambda ()
         (prefix-list-elements (rope-completions) ac-target))))
  "Source for Rope")
(defun ac-python-find ()
  "Python `ac-find-function'."
  (require 'thingatpt)
  (let ((symbol (car-safe (bounds-of-thing-at-point 'symbol))))
    (if (null symbol)
        (if (string= "." (buffer-substring (- (point) 1) (point)))
            (point)
          nil)
      symbol)))
(defun ac-python-candidate ()
  "Python `ac-candidates-function'"
  (let (candidates)
    (dolist (source ac-sources)
      (if (symbolp source)
          (setq source (symbol-value source)))
      (let* ((ac-limit (or (cdr-safe (assq 'limit source)) ac-limit))
             (requires (cdr-safe (assq 'requires source)))
             cand)
        (if (or (null requires)
                (>= (length ac-target) requires))
            (setq cand
                  (delq nil
                        (mapcar (lambda (candidate)
                                  (propertize candidate 'source source))
                                (funcall (cdr (assq 'candidates source)))))))
        (if (and (> ac-limit 1)
                 (> (length cand) ac-limit))
            (setcdr (nthcdr (1- ac-limit) cand) nil))
        (setq candidates (append candidates cand))))
    (delete-dups candidates)))
(add-hook 'python-mode-hook
          (lambda ()
                 (auto-complete-mode 1)
                 (set (make-local-variable 'ac-sources)
                      (append ac-sources '(ac-source-rope) '(ac-source-yasnippet)))
                 (set (make-local-variable 'ac-find-function) 'ac-python-find)
                 (set (make-local-variable 'ac-candidate-function) 'ac-python-candidate)
                 (set (make-local-variable 'ac-auto-start) nil)))

;;Ryan's python specific tab completion                                                                        
(defun ryan-python-tab ()
  ; Try the following:                                                                                         
  ; 1) Do a yasnippet expansion                                                                                
  ; 2) Do a Rope code completion                                                                               
  ; 3) Do an indent                                                                                            
  (interactive)
  (if (eql (ac-start) 0)
      (indent-for-tab-command)))

(defadvice ac-start (before advice-turn-on-auto-start activate)
  (set (make-local-variable 'ac-auto-start) t))
(defadvice ac-cleanup (after advice-turn-off-auto-start activate)
  (set (make-local-variable 'ac-auto-start) nil))

(define-key python-mode-map "\t" 'ryan-python-tab)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                         
;;; End Auto Completion                                                                                        
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Github

These changes are also applied to my Emacs environment on Github. I'm continuously trying to improve my emacs setup, so if you're reading this a few months/years after this post was made, you may want to check there for improvements.

Update Jan 30 2009: I made some modifications to the tab complete order, a regular indent is applied first before autocompletion. Autocompletion is also not applied unless you are at the end of a word. This is useful when you press Tab at the beginning of a line to indent/outdent.

Usage

Once you got everything setup, usage is real easy:

  1. Open up a python (.py) file
  2. Press TAB when you want to use code completion or to insert a snippet.

The first time you attempt to use code completion you'll be prompted to enter the root of your project directory.

Future

It wasn't too long ago when Python code completion inside Emacs was just a pipedream. So now that we have it, let's make some more pipedreams: I'd like to popup some nice contextual help for method arguments as well as python documentation for the current method/class being completed.

The possibilities are pretty much endless. I love Emacs.

Read and Post Comments

256 colors on the Linux terminal

January 20, 2009 at 12:30 PM | categories: emacs, linux | View Comments

I've been using Linux as my main OS for well over a decade now. I can hardly believe I didn't pick up on this tip before.

On your linux desktop, open up your favorite terminal emulator. Enter this command:

tput colors

What number do you see? 8?

8 stinking colors. That's what my terminal is capable of displaying. It's 2009 folks, and my terminal can only display 8 stinking colors.

Load up emacs in the terminal (emacs -nw) and see for yourselves, M-x list-colors-display shows you all of the different colors your terminal can display.

Pitiful.

Ok, so how to improve this? On ubuntu:

sudo apt-get install ncurses-term

and stick the following in your ~/.bashrc and/or ~/.bash_profile:

export TERM=xterm-256color

Ahhhhh...

Read and Post Comments

Ubuntu: coping with a height restricted display

December 10, 2008 at 01:06 PM | categories: linux | View Comments

I'm eagerly awaiting my new Samsung NC10 to be shipped from Amazon. It's a nice little netbook that I can use when traveling and when going to social occasions. I'm specifically buying this laptop because of its small size, but I know that one thing specifically will bother me: the screen resolution, at 1024x600 is wide enough for most tasks, but not very tall at all.

So while I'm waiting, I'm playing around with Ubuntu 8.10 inside a VirtualBox instance. I've got the window resized to 1024x600 and am playing around with different settings to make maximum use of the screen. Here are my tweaks:

Gnome Panel (the toolbar):

Open gconf-editor (Alt-F2 'gconf-editor') and navigate to apps -> panel -> toplevels -> top_panel_screen0 and change the following:

  • auto_hide: checked -- makes the main toolbar panel autohide
  • auto_hide_size: 0 -- makes the toolbar completely hide instead of leaving a few pixels visible
  • enable_animations: unchecked -- make the hiding/unhiding as fast as possible
  • hide_delay: 500 -- makes the toolbar stay on screen for half a second after using it
  • unhide_delay: 0 -- makes the toolbar immediately popup when your mouse goes to the very top (or bottom) of the screen

Gnome Terminal:

Open the default profile properties, right click in the window and click "Edit Current Profile"

  • Turn off the menu bar: on the General tab uncheck "Show menubar by default in new terminals"
  • Turn off the scrollbar: on the Scrolling tab select "Scrollbar is: Disabled"

Oversized windows:

Uncheck the following key in gconf-editor:

/apps/compiz/plugins/move/allscreens/options/constrain_y

Now you can Alt-left-click any window and drag it around even above the top of the screen.

Firefox:

  • Move the bookmarks bar up to the file menu bar and get rid of the bookmarks bar:
    • Right click on the Home button and click customize.
    • Your bookmarklets should now disappear and read "Bookmarks Toolbar items."
    • Drag "Bookmarks Toolbar items" along the right hand side of the File .. Edit .... Help menus.
    • Click "Done."
    • Right click on the now empty bookmarks toolbar and uncheck "Bookmarks Toolbar."
  • Get rid of the status bar: Go to View -> uncheck "Status Bar."
  • Install a theme that uses smaller buttons and text: I like Classic Compact.
  • Remember that Firefox has a nice fullscreen option : F11.
  • Check out Tree Style Tabs. It puts the tabs vertically on the left side of the browser and visually keeps track of what sites you were at that prompted you to open a new tab, all in a tree like fashion.

Here is the desktop with gnome panel completely hidden and one terminal with menu bar hidden (click to see native size):

Here is firefox maximised with two tabs open (click to see native size):

Here is firefox in fullscreen (click to see native size):

Firefox maximized with Tree Style Tabs:

Read and Post Comments

Emacs IRC (ERC) with Noticeable Notifications

August 07, 2008 at 12:35 AM | categories: python, lisp, emacs, linux | View Comments

I use ERC for all my IRC chatting. I finally got fed up with not noticing someones message because I didn't have emacs focused. So I spent my evening concocting a more noticeable messaging system through Pymacs and libnotify.

Half way though implementing this, wouldn't you know it, I found ErcPageMe which does exactly what I wanted. I figured I was learning quite a bit and I continued writing my own version. I expanded on their code and (at least for me) made some improvements. So kudos go to whoever wrote ErcPageMe :)

The following code will pop up a message on your gnome desktop alerting you whenever you receive a personal message or when someone mentions your nickname in a channel. It also avoids notification for the same user in the same channel if they triggered a message within the last 30 seconds.

Emacs ERC Notification through libnotify

Here is my lisp and embedded python/pymacs code:

(defun notify-desktop (title message &optional duration &optional icon)
  "Pop up a message on the desktop with an optional duration (forever otherwise)"
  (pymacs-exec "import pynotify")
  (pymacs-exec "pynotify.init('Emacs')")
  (if icon 
      (pymacs-exec (format "msg = pynotify.Notification('%s','%s','%s')"
                           title message icon))
    (pymacs-exec (format "msg = pynotify.Notification('%s','%s')" title message))
    ) 
  (if duration 
      (pymacs-exec (format "msg.set_timeout(%s)" duration))
    )
  (pymacs-exec "msg.show()")
  )

;; Notify me when someone wants to talk to me.
;; Heavily based off of ErcPageMe on emacswiki.org, with some improvements.
;; I wanted to learn and I used my own notification system with pymacs
;; Delay is on a per user, per channel basis now.
(defvar erc-page-nick-alist nil
  "Alist of 'nickname|target' and last time they triggered a notification"
  )
(defun erc-notify-allowed (nick target &optional delay)
  "Return true if a certain nick has waited long enough to notify"
  (unless delay (setq delay 30))
  (let ((cur-time (time-to-seconds (current-time)))
        (cur-assoc (assoc (format "%s|%s" nick target) erc-page-nick-alist))
        (last-time))
    (if cur-assoc
        (progn
          (setq last-time (cdr cur-assoc))
          (setcdr cur-assoc cur-time)
          (> (abs (- cur-time last-time)) delay))
      (push (cons (format "%s|%s" nick target) cur-time) erc-page-nick-alist)
      t)
    )
  )
(defun erc-notify-PRIVMSG (proc parsed)
  (let ((nick (car (erc-parse-user (erc-response.sender parsed))))
	(target (car (erc-response.command-args parsed)))
	(msg (erc-response.contents parsed)))
    ;;Handle true private/direct messages (non channel)
    (when (and (not (erc-is-message-ctcp-and-not-action-p msg))
               (erc-current-nick-p target)
	       (erc-notify-allowed nick target)
	       )
      ;Do actual notification
      (ding)
      (notify-desktop (format "%s - %s" nick
                              (format-time-string "%b %d %I:%M %p"))
                      msg 0 "gnome-emacs")
      )
    ;;Handle channel messages when my nick is mentioned
    (when (and (not (erc-is-message-ctcp-and-not-action-p msg))
               (string-match (erc-current-nick) msg)
               (erc-notify-allowed nick target)
	       )
      ;Do actual notification
      (ding)
      (notify-desktop (format "%s - %s" target
                              (format-time-string "%b %d %I:%M %p"))
                      (format "%s: %s" nick msg) 0 "gnome-emacs")
      )
    )
      
  )

(add-hook 'erc-server-PRIVMSG-functions 'erc-notify-PRIVMSG)
Read and Post Comments

Next Page »