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.