lms.el: squeezebox controller for emacs

Slim Devices was a now-defunct company who produced network music players well ahead of its time. It offered more-than-good music streaming quality and many other special features, like music synchronization with devices placed on different rooms.

../../../_images/slim-devices.jpeg

The squeezebox devices are the music players, which work with a free central software server (perl-based) called Logitech Media Server (aka Squeezebox Server, aka SqueezeCenter, aka SlimServer) installable on Linux, *BSD, Windows or Mac computers or even NAS devices, and where you could store and manage your local music library or even listen to internet stations.

In 2006, company was purchased by Logitech and six years later these products were discontinued.

Anyway, it is still one of the best solutions available today for music management and local or remote music streaming. It works and active thanks to the community. And do not be fooled by appearances, it’s very simple to build, even for people without computer skills.

Server software is freely available and it gets new features and enhancements or new plugins often. Even there are many different DYI software/hardware players from the enthusiastic community. Building a player/server with a RaspberryPi, a DAC, and piCorePlayer software is funny, easy and quite cheap indeed.

If you are interested in this solution there are lots of information in internet, but the community meets mainly at http://forums.slimdevices.com/.


Well, that’s enough for the enthusiastic me and the marketing buzzwords ;-).

../../../_images/squeezebox-radio.jpeg

I own a Squeezebox Boom and a Squeezebox Radio, use a Marantz NR1504 as well (thanks to a plugin called UPnPBridge) and planning to build a couple DYI players in next months.

I control the players with a program on my linux desktop (jivelite) or from an app in my mobile phone (Orange Squeeze, is not free, but there are some others such as free & open source Squeezer).

Ok, very nice… but… hey… there isn’t an emacs package to control squeezebox!


lms.el

This is an emacs frontend to interact with Squeezebox Server / Logitech Media Server. It is released under GPL version 3 license or later.

It requires emacs version 25 or higher.

Quick instructions: customize some basic parameters lms-hostname, lms-telnet-port, lms-html-port, lms-username, lms-password and run it with lms-ui. From there, you could read complete documentation after pressing h key.

Package should appear in MELPA repository soon, and the code is in BitBucket repository as well.

Some of the features:

- Display song: title, artist, album, year, cover…
- Play, pause, stop, select next / previous song
- Control players: select player, power on/off, volume, repeat and shuffle modes
- Playlist control: list, select song, delete track, clear
- Show track information and change rating

As today, July 30th, 2017, there are some missing features (like music browsing or searching), but they will be added during next weeks (read September as I will leave for holidays in a few days).

It is not aimed to be a complete controller, as it can’t - and won’t - manage external sources such us BBC, Deezer, Pandora, Spotify, or TuneIn Radio.

Of course, any ideas, suggestions and comments will be well received.

../../../_images/lms.el-ss1.jpeg ../../../_images/lms.el-ss2.jpeg ../../../_images/lms.el-ss3.jpeg ../../../_images/lms.el-ss5.jpeg ../../../_images/lms.el-ss4.jpeg

lfm v3.1 released

Just released a maintenance new version of lfm, with some bugs fixes from last times.

More information

Of course, all comments, suggestions, etc. are welcome.

Changes since last version

Version 3.1 ("Recovering inertia") - 2017/06/25:

+ New features
  - support for new compression programs: lzip, lz4

+ Changes & improvements:
  - Improved resources files (docs, default keys and theme) installation
    and loading at runtime

+ Fixes:
  - when installing data files with pip >7.0 & wheel.
    Thanks to Jean-François Bercher
  - PowerCLI: error message is always shown when command finishes.
    Thanks to Jean Terrier for the report
  - filters: glob when parent dir contains special chars (BB issue #5).
    Thanks to Viktor Vad for the report
  - lfm crashes when terminal is too narrow (BB issue #4).
    Thanks to Wayne Tan for the report

lfm v3.0 released

../../../_images/lfm-ss1.jpeg

Yesterday I released lfm version 3.0, the powerful file manager for the UNIX console I started coding in 2000 or 2001.

This is an important release as it’s the first compatible with Python 3, in fact it requires Python 3.4+.

Some of the features you can find in lfm:

  • console-based file manager for UNIX platforms
  • 1-pane or 2-pane view
  • tabs
  • files filters
  • bookmarks
  • history
  • VFS for compressed files
  • tree view
  • dialogs with entry completion
  • PowerCLI, a command line interface with advanced features
  • fast access to the shell
  • direct integration of find/grep, df and other tools
  • color files by extension [Andrey Skvortsov]
  • fully customizable themes (colors)
  • fully customizable key bindings
  • support for filenames with wide chars, f.e. East Asian
  • …and many others

NOTE: If you come from v2.3 please read the Upgrade section in the documentation.

More information

Of course, all comments, suggestions, etc. are welcome.

../../../_images/lfm-ss2.jpeg ../../../_images/lfm-ss3.jpeg ../../../_images/lfm-ss4.jpeg ../../../_images/lfm-ss5.jpeg

Changes since last version

Version 3.0 ("Only you") - 2015/10/23:

+ About the code
  - almost completely rewritten from scratch
    . it hasn't been tested as much as lfm v2.x series on non-linux OS
  - requires Python v3.4+
  - pyview, the file viewer, has been removed from lfm package
  - configuration location has changed to a new directory and files:
    ~/.config/lfm/{lfm.ini, lfm.keys, lfm.theme, lfm.history}

+ New features
  - fully customizable themes (colors)
  - fully customizable key bindings
    . allow Alt-key shorcuts (A-)
    . only for main window (not for dialogs)
  - files filters (using globs)
    . information in pane frame: ".F" => show dotfiles, active filters
    . filters are a property of a tab, they remain active even when chdir
    . Ctrl-f: edit current filter
    . Some examples:
      . "*.png,*.jpg" => hide all PNG and JPEG files
      . "*.jpg,!*shot*" => hide all JPEG files except those with 'shot' in the name
      . "*,!*py" => hide all except python source files
  - up to 35 bookmarks (0-9, a-z)
    . b: go to bookmark, B: set bookmark, C-d: select bookmark
    . fix: don't delete bookmark at start if path does not exist
  - nested archive handling (vfs inside vfs) works now
  - added optional support for filenames with wide chars, f.e. East Asian
    . to enable, set 'use_wide_chars' flag in configuration or use -w command line flag
    . it's disabled by default for performance
    . it's not perfect, but it mostly works
  - there are 2 different versions of move_file to chose from in the key
    bindings file:
    . move_file: old implementation
    . move_file2: alternative version using shtutil.move instead of copy & delete. Faster but less control of errors
  - new action: redraw screen (default key A-r)

+ Changes & improvements (vs v2.x):
  - chmod & chown/chgrp are 2 different actions now
  - cursor_goto_file (C-s): find text pattern (no regex or glob) in the whole file name, not at the beginning as v2.x
  - cursor_goto_file_1char (A-s): go to file by 1st letter of name (old C-s behaviour)
  - bookmarks have new key bindings:
    b: go to bookmark, B: set bookmark, C-d: select bookmark
  - PowerCLI:
    . ending command with % must be $ now
    . added new date variables: dm, dc, da, dn
    . ending command with % must be $ now
    . added new date variables: dm, dc, da, dn
    . old $d variable is $p now
  - find/grep: panelize = create vfs with matched files
    . if rebuild: all files modifications or deletions are translated to original directory so be careful!
  - pyview, the file viewer, has been removed from lfm package

+ Fixes in v3.0 (vs v2.x):
  - nested archive handling (vfs inside vfs) works now
  - find & grep with spaces in file name
  - wide chars file names support (f.e. Eastern languages) => lfm -w
  - sort by size after show dirs size
  - move_file: not overwritten files in destination are not deleted

pmdm - a poor’s man desktop-mode replacement for Emacs

../../../_images/emacs-logo.jpeg

As usual, much time has elapsed without updating the blog. I had some ideas in my head but was too lazy to write anything.

Anyway, I come back today with possibly my preferred software application… want to guess? Linux? GNOME? Firefox? even my own lfm? Well, the only application which really matters!

In fact, as time goes by, I appreciate emacs more and more. And as I’ve mentioned somewhere, I can spend a great part of my decreasing spare and idle hours tweaking here or there in my .emacs file configuration and testing new modules - MELPA, so much pain have you inflicted.

Ok, less ramblings and let’s go into detail.

Problems with emacs daemon, desktop-mode, and perspective

I’m using emacs --daemon since a couple of years ago. emacs starts with my GNOME graphical session (nowdays as user systemd service) and ends with it. Very convenient.

I also used desktop-mode, which can save your opened files, frames sizes and much more at emacs session ending and recover all of them the next time you start emacs. It worked more or less for me with emacs daemon mode.

But desktop-mode definitively stopped working when I added perspective to my configuration. [Copying project page description: “perspective provides tagged workspaces in Emacs”.]

Usually this wouldn’t be a serious inconvenient as I use helm-mini (or helm-recentf) to open recent files quickly. But… hey! this is emacs, let’s write a new and superfluous package (and learn in the process)!

Writing pmdm, a simple replacement for saving and restoring opened files

I need to confess that I don’t know much about emacs-lisp, in fact the only language I can consider myself proficient enough nowdays is python, but coding simple stuff in elisp (or haskell, btw) is a good way to relax myself for a couple of hours.

Ok, so we need to write a function to get all buffers containing live files and save their name onto a file. Then, another function to read the stored file names and open them.

Sound simple, uhm?

Let’s start step by step.

Getting the name of opened files

We can get all opened buffers with buffer-list, which returns a list (of course, this is emacs-lisp, there are lists everywhere ;). And buffer-file-name can be used to get the name of the file for the buffer.

Let’s run these functions interactively using ielm and show their output:

ELISP> (mapcar 'buffer-file-name (buffer-list))
(nil "/home/inigo/devel/web.inigo/blog/drafts/pmdm_a_poor_s_man_desktop_mode_replacement_for_emacs.rst" "/home/inigo/devel/emacs/pmdm/pmdm.el" nil nil nil nil nil "/home/inigo/.emacsmine/my-dotemacs.el" nil "/home/inigo/personal/agenda/todo.org" nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil "/home/inigo/personal/agenda/diary" nil nil nil nil nil nil nil nil)

Lots of buffers. Those nil elements correspond to buffers with no file, so we need to filter them out:

ELISP> (delq nil (mapcar 'buffer-file-name (buffer-list)))
("/home/inigo/personal/agenda/todo.org" "/home/inigo/devel/web.inigo/blog/drafts/pmdm_a_poor_s_man_desktop_mode_replacement_for_emacs.rst" "/home/inigo/devel/emacs/pmdm/pmdm.el" "/home/inigo/.emacsmine/my-dotemacs.el")

Much better.

Writing the list to a file

Now we have to save this list into a file.

Diving into elisp documentation we can find a write-region function with this syntax:

(write-region START END FILENAME &optional APPEND VISIT LOCKNAME MUSTBENEW)

it looks like it works with current buffer, so we could create a temporary buffer, insert the list as a string (prin1-to-string), select the text and save it finally.

It’s not very clear from the definition above, but reading the description carefully we noted the function can also be used by passing a string as the first parameter, so we don’t need the buffer!

(write-region TEXT nil FILENAME &optional APPEND VISIT LOCKNAME MUSTBENEW)

We also add some comments to the file in case someone read it. We use format to format and concatenate the text to be written.

And now we have our first function finished!

(defun pmdm/write-opened-files()
  (interactive)
  (let ((files (delq nil (mapcar 'buffer-file-name (buffer-list)))))
    (write-region (format ";; PMDM file.\n;; Please do not edit manually.\n%s"
                          (prin1-to-string files))
                  nil
                  "~/.emacs.d/.pmdm-files")))

Reading it back

In Emacs, the best way to read a file content is to insert it in a temporary file. Something as fast as:

(with-temp-buffer
  (insert-file-contents "~/.emacs.d/.pmdm-files")

We can delete the comments in the file with:

(delete-matching-lines "^;; ")

and get the list with our files to open:

(buffer-substring-no-properties (point-min) (point-max))

Wow, very easy… but there is problem here… what we have is the string representation of the list, not the list itself!

After half an hour thinking and searching internet the solution was simple, the read function can cast the string into a list.

This is how the finished function looks like:

(defun pmdm~read-files-list ()
  (with-temp-buffer
    (insert-file-contents "~/.emacs.d/.pmdm-files")
    (delete-matching-lines "^;; ")
    (read (buffer-substring-no-properties (point-min) (point-max)))))

[Please consider the security implications of blindly reading a file from the file system and using the contents without any check.]

And finally, visit the files

We loop the files list returned by previous function with dolist and call find-file-noselect to open them in the background.

(dolist (file (pmdm~read-files-list))
  (find-file-noselect file))

Though in emacs it’s harmless trying to load an already opened file, it’s better to check and avoid it. We know how to get a list of currently opened files so for our new file we will check if it is present in the already-opened-files list.

(let ((opened-files (delq nil (mapcar 'buffer-file-name (buffer-list))))
      (files (pmdm~read-files-list)))
  (dolist (file files)
    (unless (member file opened-files)
      (find-file-noselect file))))

We can enhance the function displaying how many files we have opened, so we add a variable that we’ll increment for each new opened file.

(defun pmdm/load-files ()
  (interactive)
  (let ((opened-files (delq nil (mapcar 'buffer-file-name (buffer-list))))
        (files (pmdm~read-files-list))
        (count 0))
    (dolist (file files)
      (unless (member file opened-files)
        (find-file-noselect file)
        (setq count (1+ count))))
    (message (if (zerop count)
                 "No files opened"
               (format "%d file%s opened" count (if (> count 1) "s" ""))))))

Final thoughts

Add functions comments, use of defvar instead of hard-coding file name and voilà, we have the final version of pmdm.el.

I know/suppose there are a lot of better implementations of this same idea out here. But using them would be less fun than coding it ourselves!

Bookmarks v1.0 released

../../../_images/bk-ss1.jpeg

Bookmarks is a simple personal web-based application to manage web bookmarks.

It’s written in Python 3.2+ and uses BottlePy web microframework and jQuery javascript library. Both are included with the package.

It was coded as a funny practice, but it includes some nice features: advanced search, tags cloud, years cloud.

I think this little application can be useful for anyone learning Python web programming.

It’s under GNU Affero GPL License version 3 or later.

../../../_images/bk-ss2.jpeg

More information, and download link at:

Of course, all comments, suggestions etc. are welcome.