Developer:Davaar:PlaylistService
Contents
Architecture Overview
To overcome a number of limitations in the Upnp Av Media Renderer specification, the DS has extensive support for on device playlists.
Largely, this service can be viewed as a database. You can read, insert, and delete entries in the playlist. The playlist can also be placed in shuffle or repeat modes. Each entry in the playlist is given unique id. This unique id is completely unrelated to the play order of the playlist. The id of 0 is special and represents the empty playlist.
Operations
Insert
To insert, provide the id you wish to insert 'after' and the associated uri and metadata will be added to the playlist in the correct position. After insertion, a new id, representing the inserted track will be returned. Insertion will always succeed unless the id to insert after does not exist. To insert in the first position in the playlist, insert after the special id of 0.
Read
There are two read actions. One to read a single entry and the other to read a list of entries. In the first, the uri and metadata of the requested id are returned. This operation can fail if the requested id does not exist in the playlist. In the second action, ReadList, a list of decimal id's is provided in a comma separated list. The DS will return the associated data for every entry that exists in the playlists. Given a valid comma separated list, ReadList, will not fail. Requests for ids not in the playlist are silently ignored.
The following example shows the xml fragment to pass to the ReadList action. You can request the metadata for as many ids as you wish (up to the maximum number of tracks in the playlist).
212,3885,5
And here is an example of the response:
<MetaDataList> <Entry> <Id>212</Id> <Uri>http://mylocaldomain.com/~ds/TheBestSongEver1.flac</Uri> <MetaData>...</MetaData> </Entry> <Entry> <Id>3885</Id> <Uri>http://mylocaldomain.com/~ds/TheBestSongEver2.flac</Uri> <MetaData>...</MetaData> </Entry> <Entry> <Id>5</Id> <Uri>http://mylocaldomain.com/~ds/TheBestSongEver3.flac</Uri> <MetaData>...</MetaData> </Entry> </MetaDataList>
If id 3885 did not exist you would get the following xml fragment instead:
<MetaDataList> <Entry> <Id>212</Id> <Uri>http://mylocaldomain.com/~ds/TheBestSongEver1.flac</Uri> <MetaData>...</MetaData> </Entry> <Entry> <Id>5</Id> <Uri>http://mylocaldomain.com/~ds/TheBestSongEver3.flac</Uri> <MetaData>...</MetaData> </Entry> </MetaDataList>
Note that both the Uri and MetaData elements will be xml escaped. They will therefore need to be unescaped by the application.
Delete
There are two variants of delete allowing the deletion of a single track or the deletion of the entire playlist. The former requires the id of the entry to delete. The latter takes no parameters. Both variants always succeed.
IdArray
The playlist service has a state variable called IdArray. This IdArray is a base64 encoded array of 32 bit, big endian integers. It represents the ids as they are ordered in the playlist. An empty IdArray indicates an empty playlist.
For example the IdArray of:
AAAAAgAAABQAAAAT
Decoded into ascii hex is:
00 00 00 02 00 00 00 14 00 00 00 13
Which indicates that you have 3 tracks in your playlist and in order from first to third they are the decimal ids: 2, 20, and 19.
IdArray Recommended Algorithm
With respect to the playlist, control point authors are interested in both:
- the order of tracks in the playlist, and
- the metadata of those tracks
Whether you acquire the IdArray via eventing or polling, you immediately know the order of the ids in the playlist. What you don't necessarily know is the metadata for those ids.
As the metadata for a large playlist can easily be several megabytes of xml, blindly fetching all the metadata every time the playlist changes is inefficient and will give a poor user experience. In order to quickly display playlist updates and ensure interoperability with other control points on the network, it is strongly recommended that you maintain the following two data structures:
- A copy of the latest IdArray
- A cache mapping id's to their associated metadata.
With this decoupled approach, you only need to fetch the metadata for the ids added to the playlist. Changes to the order of ids in the playlist requires no action.
As a further enhancement, when an insert completes successfully, you know both the metadata (as you supplied it), and the new id (returned by the DS on completion of the insert action). This information can be directly inserted into the cache, saving a later retrieval.
Events
A short while (approx 300ms) after any change to the playlist, the DS will event all subscribers the current IdArray. The eventing of this state variable is moderated to prevent excessive updates.
Polling
For control points that can't subscribe to events, two additional actions are provided. The first, IdArray, will return the request array of ids in the same format as detailed above. This action also returns an IdArrayToken. This token should be saved and periodically passed to a second action, IdArrayChanged, which will tell you whether or not your copy of the IdArray is out of date. If it is, calling IdArray again will ensure you have an up to date version.
API Reference
Playlist Service Description (XML)
Migration Guide
In bute, playlist handling was part of the Media service. All playlist functionality has moved to the Playlist service.
In addition to this two major things have changed in Cara:
- Bute playlists relied on a sequence number to ensure consistency of the playlist. This has been completely abandoned in favour of permanently unique track ids. Actions now succeed or fail solely on the existence of that id -- not whether you hold the correct sequence number.
- Bute playlist actions referenced the index or position of a track inside a playlist. Cara playlist actions reference the unique id -- which is completely unrelated to it's position. You can only determine the position of an id via the IdArray. This means that multiple control points can successfully modify the playlist at the same time in a sensible fashion.
Bute playlist handling code will need to be refactored in line with the above. However, our experience is that the resulting code will be substantially simpler and more efficient.