Installing Zhaan

I have had several people contact me lately asking if they can run Zhaan.  Unfortunatly there is a fairly significant amount of setup required to get Zhaan to run properly.  This includes some code for gupnp, gupnp-av and pygi which is not yet upstream.  I believe the gupnp and gupnp-av stuff is actually being merged today/tomorrow and the pygi stuff will be merged within a week or two.

To make it easier for other people to test with I have made a bunch of .deb’s (well, Anderson Lizardo and WIller @ OpenBossa/PyMaemo did 99% of the work on the debs, I just put all the right ones in a the tarball) and an installer script which installs all the needed dependencies as well as installing gupnp and gupnp-av from source.  Once the code for those makes it into an upstream release I’ll replace those source install steps with proper deb installs.

Alright, enough babel, on with the install instructions:

  1. Install the Maemo 5 SDK (forum nokia provides a nice GUI installer).  This will take 10-15 minutes.
  2. Run the Maemo 5 SDK link which was placed on your desktop (or start_xephr.sh which should be in you path)
  3. Open scratchbox and run the following commands:
    wget http://zachgoldberg.com/data/Zhaan.tgz
    tar -xzf Zhaan.tgz
    cd ZhaanInstall
    ./install.sh  # This step will spit out a ton of stuff and take about 5 minutes
    cd Zhaan
    ./run_zhaan
  4. Voila!  Zhaan should be running in your Maemo 5 window.  Note that because of a yet-to-be identified bug in (GUPnP?  Scratchbox?) Zhaan cannot auto-detect your network interfaces within scratchbox.  (Outside of scratchbox this feature works very well and is a feature of GUPnP).  You may have to edit line 105 of UPnPDeviceManager.py in the Zhaan folder and replace “eth0” with your network interface.

Please do let me know if you have any issues, or success, with Zhaan.   Patches are welcome 🙂

Note: install.sh does some pretty ‘unkosher’ things.  This is because several of Zhaan’s dependencies are not yet packaged 100% properly and are still a work in progress.  I will push a new Zhaan.tgz (or even better, eventually just a ppa or even a big apt-get line) as the packaging situation improves.

Pandora-UPnP sees the light of day

This is becoming a very bad habit.  Have a crazy and only semi useful idea one night and get a terrible itch all the next day until I can sit down and work on it.  In this case I had an itch all day while at the gym and at work and then a passover seder.   Once I finally sat down at night though everything actually came together very very quickly.

I started with vanilla pianobar, an application which can play pandora music and be controlled through an external fifo file descriptor.  I then pulled some code from the pyPianobar/qtPianobar projects and stripped it down to be a barebones python API to pianobar.  After about 2 hours of work, maybe less, I could control pandora playback on my speakers via ipython.

I spent another 90 minutes this evening (after a lovely passover seder) and, using MPD-UPnP’s code as a model, was able to expose about 70% of pandora’s usefullness over UPnP and controllable through Zhaan.  Zhaan can pause/play, stop, next, list and change stations, view current song title/artist/album, view current elapsed time over total time for the current song.  The only things that are missing are some bug fixes in station changing (it works only semi-reliably right now, but it does work sometimes) and the “like it/hate it” features of pandora.  The favorites/”liking” of songs doesn’t map so well to the native UPnP AVTransport API nor to the Zhaan UI so I am inclined to just leave that out.

So lets recap Zhaan’s (and any UPnP Controller’s) capabilities now a days:

  • Browse any General UPnP Media Source (rygel, mediatomb etc.)
  • Play media to any UPnP Media Renderer (rygel, gmediarenderer etc.)
  • Control music from MPD, the Music Playing Daemon (via MPD-UPnP)
    • Browse current MPD playlist
    • Browse all other playlists
    • Browse all songs
    • Send media from another UPnP Media Source to MPD
    • Send media from MPD to another UPnP Media Source
  • Control music from Pandora, the Internet Radio Service (via Pandora-UPnP)
    • Browse and change station
    • Pause/Play/Next
  • Display current song and song progress like a normal media player for any of the above media renderers (mpd, pandora, UPnP Media Renderer)
  • Work on Maemo via hildon/gtk
  • Work on Linux via GTK+

The largest missing feature I can think of is album art, but that’s coming.  That and it to actually work on the N900, but that’s still in PyMaemo’s hands.

And of course all of this work is available on my GitHub.  You’ll notice two new repositories (links on the righthand side) for the pyPandora and Pandora-UPnP repositories.

Pandora-UPnP?

So I feel fairly comfortable saying that I have ‘solved’ the problem of using MPD via UPnP at least in a rough slightly-better-than-prototype sense.  I will of course continue to polish MPD-UPnP but I wonder, how can I make Zhaan even more useful?

There is this nifty library called pianobar with this even more nifty python api.  Pianobar is an API to pandora.  I wonder how much effort it would take to wrap pandora in UPnP?

Update I had the following pseudo monolog on IRC with a friend of mine discussing the upnp-pandora idea.  Perhaps there are more upnp wrappers to come:

01:51 < zgold> Wouldn't that be awesome
01:51 < zgold> have one UI and one app on your N900
01:51 < zgold> to control any UPnP device in your house
01:51 < zgold> any MPD device on the house (even on the phone)
01:52 < zgold> and Pandora locally on the phone
01:52 < zgold> now if only the local media player exposed itself as a UPnP
 MediaRenderer
01:52 < zgold> Zhaan would be the universal media UI =P
01:52 < zgold> though
01:52 < zgold> it does have a dbus api
01:52 < zgold> I could build a UPnP MediaRender wrapper around that
01:52 < zgold> *hmmmmm*

Zhaan Controller

I had some pretty serious issues with my scratchbox environment but thanks to the PR1.2 SDK my life is much easier and things are working again.  Since things are working Zhaan has been making some lovely progress.  In particular I have now implemented a complete control panel in Zhaan.   I have also implemented the needed functions in MPD-UPnP to support this kind of controlling and the result, as you can see below, is effectively a full GUI frontend for MPD over UPnP.  What’s even better is that this same application can control *ANY* UPnP Renderer 🙂

I’ve been in good communication lately with Anderson Lizardo and the crew at PyMaemo and, with any luck, all of this will be available on the N900 via extras devel very very soon!

Zhaan Controller

Hildon-ized Zhaan

You know how sometimes you just get really excited about a project and put a hold on the rest of life until you get to some point that makes you happy?

Well, once I got Zhaan to run in any shape whatsoever inside scratchbox with the Maemo 5 SDK I had one of those days. I spent a good chunk of my day today hildon-izing Zhaan and the results are really cool. Anderson Lizardo is currently working on making everything work under the ARM target and he intends to post finished .debs within the next day or two. Once those are in extras-devel anybody will be able to run Zhaan on their N900 🙂

Some screenshots of the fruit of today’s labor:

Zhaan working in scratchbox!

Step 1 to porting all of my work thus far to the Maemo platform itself is now complete with a (rough) proof of concept: Zhaan working in scratchbox.

Other than some really bad logistical (placement of files, prefix nonsense etc.) hacks I did not have to actually hack at any code at all to take what was working inside jhbuild to work in scratchbox. Sweet.

Victory for tonight.

MPD-UPnP Media Serving!

Just a quick update on the status of MPD-UPnP. The 1 person who watches my github repo will have already noticed my lastest big push: http://github.com/ZachGoldberg/MPD-UPnP/commit/8a7fce2b4a11e63bc91ae372f6dfcd59beabd7c7 . As of this commit one can start a server from which one can entirely control playback via MPD. Steps 1) and 2) from my previous post are now complete!

Controlling the new MPD server via Zhaan actually works rather pleasantly and bug-free. I am doing most of my work remotely and have Zhaan open via ssh+xorg. My roomates must be freaking out with the music constantly jumping around in my room! I cannot wait to port Zhaan to Maemo 5 and use this from my N900!

MPD-UPnP Status update

I have decided that, for now anyway. I am going to substitute my MPD-UPnP project for the Canola GUPnP plugin as part of my project for Penn. If time permits (and I can get Canola to work on my machine, sigh) I will still do the Canola integration.

So, to finish of MPD-UPnP we need the following features:

1) MPD as a Media Renderer of content that MPD already knows about
2) MPD as a Media Server of content MPD already knows about that only MPD can consume

To make this really awesome, we should consider also implementing:

3) MPD as a Media Renderer of arbitrary UPnP-exposed audio content
4) MPD as a Media Server of its own data to other peers.

1) and 2) require simply mapping MPD commands and database content to a UPnP interface. Components 3 and 4 require the wrapper to actually become a full fledged server (to host via UPnP all of MPD’s content for 4) and a full fledged proxy (for MPD to use ‘downloaded’ UPnP content, for 3).

As of now 1) is basically complete. I am working on 2) and have come across the need to generate DIDL data. Before we get into that, lets do a quick overview of how UPnP works for media browsing and serving.

UPnP, at its core, is merely a specification for device discovery (multicast + ssdp), state control (SOAP) and resource access (plain old HTTP 1.0/1.1). The most importaqnt here is the SOAP definitions. What functions should exist? What datatypes are passed? These questions are answered by several pre-defined UPnP specifications, such as a MediaServer (a ContentDirectory) or a MediaRenderer (an AVTransport). The specifications define the functions and arguements to for all of these devices and services.

One can imagine this works very well for simple things. For example, an AVTransport has a function “Play” which takes very few arguments (a speed is oen of the few significant ones). However, for something more complicated, like Browse on a ContentDirectory more than simple argument passing is required. Why? Well, lets say a user asks to Browse some folder foo which has 100 pieces of data. One could simply return a list of filenames, but what about all the other important data for those items? Their UPnP Class, if its audio what the title is, a resource url to download them etc. To solve this problem a DIDL (Digital Item Declaration Language) is used. The result of a Browse call isnt a simple list, its a DIDL. (DIDL is obsentisbly just XML, so the actual datatype that Browse returns is just a string).

So, back to the MPD wrapper. To implement a server we must be able to respond to Browse calls; hence we need to generate DIDL. GUPnP-AV provides a convenient mechanism for doing this. A simple usage looks as follows:


w = GUPnPAV.GUPnPDIDLLiteWriter.new("English")
container = w.add_container()
container.set_title("Container Title String (a folder name or other)")
didl_string = w.get_string()

You’ll notice this is rather not-pythonic. The writer keeps an internal reference to the container it returned in add_container such that when you later call writer.get_string() it knows the containers (and items and resources etc.) that it has allocated and how to render them as XML. It is, however, a rather nice API when all is said and done and is thus far working wonderfully for component #2 of MPD-UPnP.

Know what your not good at, and don’t do it

So these past couple weeks I have continued to hack with Python+GUPnP via PyGI. I’ve pushed some important patches lately, and have even begun making progress in getting the biggest components of my work upstream (woohoo!. GUPnP-AV, GSSDP have been merged. GUPnP itself is 80% the way there, and PyGI is in review).

So, where do I stand on AutoRemote/Zhaan? Zhaan hasn’t gotten any love lately and is actually using some older code that I have replaced with much nicer libraries inside AutoRemote.

So, most of my time has gone into AutoRemote (what a crappy name). I am at the point where the UI needs to be written. So I wrote some of it. And its REALLY ugly. And HORRIBLE to use. And I did NOT enjoy writing it. I am happy to say I can recognize what is good UI and what is bad UI, but my own skills to design (and implement!) that good UI apparently don’t exist. I think I just proved that P != NP. Awesome.

It’s Alive — MPD + UPnP

I’ve put together a (working) quick and dirty script to wrap MPD in a UPnP Media Renderer. This is far from a complete solution, but it allows playing/pausing via UPnP Control Points. (Python-GUPnP CP works just dandy for it!)


from gi.repository import GUPnP, GObject, GLib
import mpd

CON_ID = None
MPDCLIENT = None

GObject.threads_init()

def setup_server():

ctx = GUPnP.Context(interface="eth0")

ctx.host_path("device.xml", "/device.xml")
ctx.host_path("AVTransport2.xml", "/AVTransport2.xml")

desc = "device.xml"
desc_loc = "./"

rd = GUPnP.RootDevice.new(ctx, desc, desc_loc)
rd.set_available(True)

return rd

def setup_mpd():
global CON_ID, MPDCLIENT
HOST = 'localhost'
PORT = '6600'
CON_ID = {'host':HOST, 'port':PORT}

MPDCLIENT = mpd.MPDClient()


def on_play_action(service, action):
print "Play"
MPDCLIENT.connect(**CON_ID)
MPDCLIENT.play()
MPDCLIENT.disconnect()

def on_pause_action(service, action):
print "Pause"
MPDCLIENT.connect(**CON_ID)
MPDCLIENT.pause()
MPDCLIENT.disconnect()

rd = setup_server()
print "UPnP MediaRenderer Service Exported"

setup_mpd()
print "MPD Client Setup"

service = rd.get_service("urn:schemas-upnp-org:service:AVTransport:1")
service.connect("action-invoked::Play", on_play_action)
service.connect("action-invoked::Pause", on_pause_action)

print "Awaiting commands..."
GObject.MainLoop().run()

Update: This is an updated version of the script that supports more commands and properly ends each UPnP action. Unfortunately it doesn’t entirely work because of either 1) A bug in g-i or 2) a problem with the gupnp api that will require a break to fix. Spent a lot of time today to understand the problem, hopefully we’ll have either a gupnp or a g-i fix in a few days.

Update 2: Now available on github. Zhaan (formerly known as “GUPnP-Python UPnP Control Point” has also been pushed)