Using The Zune Web API On Windows Phone 7
Table of Contents
Fair Warning: You’re stepping in the undocumented land right now. Microsoft offers no support whatsoever for any of the endpoints mentioned below. Microsoft can also change any of these at any time without prior modification, so plan accordingly.
Introduction #
I use Zune a lot, having started with the 4GB player, and now it’s available on Windows Phone 7. Though the WP7 player is labeled as Music Hub, it appears under the Zune icon and incorporates Zune’s organization, providing many of the same capabilities as the desktop client.
Existing endpoints #
For quite a while, two public Zune endpoints have given out some very basic data (profile information and recent plays):
http://social.zune.net/zcard/usercardservice.ashx?zunetag=TAG
http://origin-social.zune.net/zcard/usercardservice.ashx?zunetag=TAG
Both endpoints return plain XML-formatted data, and the information is pretty basic:
- User ID
- Name
- Status
- Big tile image
- Small tile image
- Location
- Large background image
- Small background image
- Number of plays
- Badges
- Card destinations (e.g. share)
- Playlists (with artist names and album art references)
It is worth mentioning that Zune play count isn’t updated automatically as songs are played on the phone — it needs to be connected to the desktop Zune client to update the count.
That’s pretty much it. Now let’s look at the default XML result returned by the endpoints I mentioned above.
Image lost since blog transition
Notice that I highlighted the user ID. For some strange reason, the ID returned isn’t compatible with the API endpoints I am going to describe below, so obtaining it for future reference is not something to rely on.
The default endpoints don’t give much information about the songs that were played (metadata-wise) and there is no information at all about existing friends.
NOTE: If your Zune account is linked to Xbox Live, your Xbox Live friends will be placed in the Zune list.
Discovering new endpoints #
After taking a close look with WireShark when Zune desktop client was downloading profile information, I noticed that Microsoft uses a set of additional endpoints and the Zune card that is created locally does not use either of the two public URLs to retrieve data.
What triggered my curiosity was the fact that a friend list downloaded:
Image lost since blog transition
Note that there is also a GUID associated with the user. This identifier is a bit different from what is returned by the default calls. So, the structure for the profile info endpoint appears as this:
http://socialapi.zune.net/en-US/members/MEMBER_GUID
I am not entirely sure exactly where this ID comes from, but the returned XML is pretty similar to the initial one, though it is formatted as an Atom feed (which is unrecognizable by browsers as a native feed—developers have to actually download the text contents and parse it).
If the user GUID is not known, it is possible to pass the Zune Tag (as pointed out by RoguePlanetoid), but for some reason in several cases it will be problematic because of a random redirect to the authentication page.
Here is a snapshot of its contents:
Image lost since blog transition
Here’s the line that most interested me when I reviewed the code:
<a:link rel="related" type="application/atom+xml"
href="http://socialapi.zune.net/en-US/members/509cd2cf-9400-4e4c-b3d8-07aa46ce464d/friends" title="friends" />
Getting data from custom endpoints #
There it was—the list of friends that is not available in the basic feeds. This feed is a valid Atom entity, and can therefore be opened in a browser. What surprised me is the fact that I don’t have to send any authentication data in order to view it, and it is nice to have it that way:
Image lost since blog transition
Going even further, the source of the page reveals some bonus details that are not displayed in the feed when it is rendered by a compatible web browser. Here is a snapshot:
Image lost since blog transition
This feed exposes the user GUIDs (and associated URLs) of my friends, so I can get profile information about them the same way I did it for me. Ultimately, you can search for friends of friends and so on to infinity (or, to be exact—to the extent of the Zune user base).
In my initial profile feed, there are links that represent recent songs. Though the basic feed only provides the names of tracks and the artist, by using the undocumented feed I can get some additional metadata associated with media content. Once again, the feed is viewable in a browser:
Image lost since blog transition
The necessary metadata is behind the curtain:
Image lost since blog transition
Additional available information includes play rank, disc number, track number, and an indicator that shows whether the song is explicit. Some songs, however, have more metadata than others.
Here is an example of an entity with more metadata associated with it:
Image lost since blog transition
Notice that there are now song length details, the play rank is not empty, and there are actually disc and track numbers. Take a closer look at the rights tag—that’s where it’s possible to track current song offers on the Zune Marketplace, including the song encoding format (in this case 192kbps WMA), the type of purchase (Album), and the file size. For some offers also provide the price (which can be shown in both USD and Microsoft Points):
Image lost since blog transition
If a song is registered with enough additional metadata, the artist picture displays in the Zune window (if in play mode and not playlist) as the song plays.
Ultimately, this data significantly enhances my Zune experience on Windows Phone 7. I built a sample application that allows me to see my friends and generally keep up-to-date with my Zune stats.
To show a simple code snippet, here is how I am obtaining the data:
private void ParseXml(stringxmlDoc)
{
XDocument doc = XDocument.Parse(xmlDoc);
txtZuneName.Text = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}zunetag").Value.ToString();
txtRealName.Text = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}displayname").Value.ToString();
txtLocation.Text = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}location").Value.ToString();
txtPlayCount.Text = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}playcount").Value.ToString();
txtLastUpdate.Text = doc.Root.Element("{http://www.w3.org/2005/Atom}updated").Value.ToString();
XElement images = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}images");
profileLocation = (from c in images.Elements() where c.Attribute("title").Value == "usertile" select c).First().Attribute("href").Value.ToString();
backgroundLocation = (from c in images.Elements() where c.Attribute("title").Value == "background" select c).First().Attribute("href").Value.ToString();
XElement playlists = doc.Root.Element("{http://schemas.zune.net/profiles/2008/01}playlists");
recentPlaylist = (from c in playlists.Elements() where c.Attribute("title").Value == "BuiltIn-RecentTracks" select c).First().Attribute("href").Value.ToString();
mostPlayed = (from c in playlists.Elements() where c.Attribute("title").Value == "BuiltIn-MostPlayedArtists" select c).First().Attribute("href").Value.ToString();
}
Notice that I am directly passing the namespace. There is a way around this when using a regular Atom feed formatter, but I decided to go the simpler, XML way.
I’d love to see these features integrated with the mobile Zune client on Windows Phone 7. But for now, I have my own application that shows me the information.
Conclusion #
I am pretty sure that this is just the surface of the actual Zune API – there is much more to it, like message exchange, badge assignment depending on the songs played and so on. Some of the more complex parts are less accessible due to existing protection mechanisms – for example, sending messages isn’t done directly through HTTP but rather through TCP in an encrypted manner. I am still looking into that.