17 April 2016
For a long time I have been using Clementine music player on my workstation. Recently I reinstalled Gentoo on my desktop and I wanted to avoid installing QT libraries of any sort. So I switched to mpd and I have fallen in love with it. It is very flexible, fast and enriched by a lot of community software. For some weeks I used mpc client as my primary client for mpd but I was not satisfied with it. Even though it is pretty minimal but packed with every feature mpd permits, the search feels uncomfortable because is case sensitive and need artist, album, etc. flags before any entry. This is why I have written kpd together with Francesco Gallà
Kyuss Player Client
kpd is an acronym for Kyuss Player Client because we have been listening only to Kyuss while programming this client. We have reimplemented the search functions to suit our habits. No more case sensitive, optional 'artist, album, title' flags. kpd accepts only one string as the search argument and implements optional filter arguments to narrow the search in a grep like way. I welcome you to read the readme in my git to understand how the search works. Anyway in this post I want to explain bits of the code.
The main kpd file invoked when the command is run in the console is kpd.py The most interesting part in this file IMHO is these lines:
.. code:: python
for el in argsOrder: if dictArgs[el] != False: client.update_status () methodToCall = getattr (util, el) retUtil = methodToCall (client, dictArgs[el], searchRes)
argsOrder is a list of the arguments on the command line in the order the user wrote them. kpd uses a dictionary to store for every argument the corrispective string for the function that will be invoked using getattr. In this way any argument can be added to the main file without writing any other line of code. WE used this method to avoid using switch alike solutions.
The util.py source file is a pretty easy source file to read. It contains every function that can be invoked by command line arguments. Every function has the same 'prototypes' so that they can be called using the method explained above.
output function I have used a class:
to suppress the output on the console the program assign to sys.stdout a dummy class that save the original stdout on a variable and replaces write and flush functions so that they are just pass. and no output is written.
To permit output after suppression the program just reassing the original value to sys.stdout.
In MPDdatabase.py we have written the search functions.
Originally we intended to just read and import in a dictionary the whole mpd database that is stored compressed in the home directory.
This list of dictionaries stores every entry related to the song and if any of them matches the search string or the filter string (considering also flags if any) the related song is printed on the output and saved in a list so it can be added by the add function.
This approach result very efficent in term of precision but it lacked speed. For a database of about 77 thousand songs (about 550k lines) a search query could last almost 2 seconds.
To improve the speed of the search we used the pickle module. The pickle module allows kpd to dump the data structure used to store the database in memory on a file that can be read easily by using the
In this way the search lasts about 40 milliseconds on the same database that wastes about 16MiB of memory on disk.
This was really fun. It was our first hand on python project and the first real program we have written since we started learning programming at our university. I discovered that programming allows me to relax and that is really cool to have custom software for activities you do every day. The source for our program is stored in my git here and you are free to modify it.