Using Pandora’s Artist Explorer service to discover new music on Windows Phone 7

DZone 's Guide to

Using Pandora’s Artist Explorer service to discover new music on Windows Phone 7

· Mobile Zone ·
Free Resource

Did you know that Pandora has a dedicated web service that fetches information about the currently playing artist as well as about associated artists? It is relatively simple to use, so I decided to build a Windows Phone 7 application prototype that would display some artist information and similar artists based on the data I pass to it.

Every single query to the service returns XML-formatted data. The endpoint is the following:


Here is how the data would look if I would be searching for, let’s say, B.o.B.:


(Click to see the larger version)

The query parameter is not case sensitive. Therefore, BoB will return the same result as bOB.

So what do we have here? Quite a few attributes that show some interesting information.

  • name – the name of the artist with proper capitalization
  • detailUrl – the URL that can be used to view some details about the artist.


  • artUrl – the URL that points to the most recent album artwork, associated with the artist.
  • shareUrl – the URL used to share the artist data with a friend.


  • musicId – an identifier for the artist. Current use is unknown.

The separate nodes include the following:

  • bio – shows biographical information related to the searched artist.
  • similar – contains artist entities that show performers similar to the one queried. Also contains similar metadata as the main artist, but without the bio.

That’s pretty much it for the main skeleton. It’s time to put everything in a Windows Phone 7 application.

I created a simple Silverlight project:


The first thing I did is replace the header with an entry panel, that will allow the user to type in the artist he is looking for. I am keeping everything as simple as possible, since I am only showing the mechanism behind it.

Here is the header:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="ARTIST NAME:" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBox x:Name="ArtistBox"></TextBox>
<Button x:Name="SearchButton" Content="Search"></Button>

Here is how my ContentPanel looks like:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image Source="{Binding Art}" Margin="12,17,275,368"></Image>
<TextBlock FontSize="32" Margin="10,190,10,306" x:Name="ArtistName" TextAlignment="Left" Text="{Binding Name}"></TextBlock>

<ScrollViewer Margin="0,260,0,160" VerticalScrollBarVisibility="Auto">
<TextBlock Margin="10,0,10,0" x:Name="ArtistBio" TextWrapping="Wrap" Text="{Binding Bio}"></TextBlock>
<Button Content="Details" Height="72" HorizontalAlignment="Left" Margin="187,17,0,0" Name="button2" VerticalAlignment="Top" Width="259" Click="button2_Click" />
<Button Content="Share" Height="72" HorizontalAlignment="Left" Margin="187,82,0,0" Name="button3" VerticalAlignment="Top" Width="259" Click="button3_Click" />

<ListBox x:Name="SimilarList" ItemsSource="{Binding Similar}" Margin="0,398,0,0">
<Image Height="100" Width="100" Margin="0,0,260,0"  Source="{Binding Art}"></Image>
<TextBlock Margin="130,0,0,0" Text="{Binding Name}"></TextBlock>

SimilarList is where similar artists will be displayed. The custom ItemTemplate for the ListBox is built in a way so that it allows the user to view the image associated with the artist, as well as the name. I could also put the link references here, but for now I just want to show the basic info.

You can see that elements here are bound to different properties. In fact, the DataContext for my page is set to reference itself:

DataContext="{Binding ElementName=MainWindow,Path=MainArtist}"

But what’s MainArtist? In my application, I created a custom class to carry artist information:

public class Artist
public Artist()
Similar = new ObservableCollection<Artist>();

public string Name { get; set; }
public string DetailUrl { get; set; }
public BitmapImage Art { get; set; }
public string ShareUrl { get; set; }
public string Bio { get; set; }

public ObservableCollection<Artist> Similar {get;set;}

There is nothing new with properties here, since the data is obtained from the parsed attributes, the source being the returned XML data. BitmapImage will instantly initiate the download of the associated album art file, so I don’t need to build any additional ‘plumbing’ around this.

Notice that the collection also contains instances of Artist, since the data I am working with is pretty much the same (with the exception of a lacking bio and similar references).

I have one instance of the Artist class in the main page:

public Artist MainArtist
return (Artist)GetValue(artistProp);
SetValue(artistProp, value);

DependencyProperty artistProp = DependencyProperty.Register("MainArtist", typeof(Artist), typeof(PhoneApplicationPage),
new PropertyMetadata(new Artist()));     

I am exposing it as a dependency property for easier binding.

When the Search button is clicked, I initiate the process of getting data from the Pandora service:

private void SearchButton_Click(object sender, RoutedEventArgs e)

GetArtistData is a method that doesn’t return anything, but sends a call to the WebClient instance to download the XML string:

public void GetArtistData(string ArtistName)
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri("<a href="http://pandora.com/xml/music/artist/"">http://pandora.com/xml/music/artist/"</a> + ArtistName));

Here is what’s in the event handler:

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
XDocument doc = XDocument.Parse(e.Result);
if (doc.Root.Name != "error")
Artist artist = new Artist();
artist.Name = doc.Root.Attribute("name") != null ?doc.Root.Attribute("name").Value : string.Empty;
artist.DetailUrl = doc.Root.Attribute("detailUrl") != null ? doc.Root.Attribute("detailUrl").Value : string.Empty;
artist.Art = doc.Root.Attribute("artUrl") != null ? new BitmapImage(new Uri(doc.Root.Attribute("artUrl").Value)) : null;
artist.ShareUrl = doc.Root.Attribute("shareUrl") != null ? doc.Root.Attribute("shareUrl").Value : string.Empty;
artist.Bio = doc.Root.Element("bio") != null ?doc.Root.Element("bio").Value : string.Empty;

if (doc.Root.Element("similar") != null)
foreach (XElement element in doc.Root.Element("similar").Elements())
Artist similarArtist = new Artist();
similarArtist.Name = element.Attribute("name") != null ? element.Attribute("name").Value : string.Empty;
similarArtist.DetailUrl = element.Attribute("detailUrl") != null ?element.Attribute("detailUrl").Value : string.Empty;
similarArtist.Art = element.Attribute("artUrl") != null ? new BitmapImage(new Uri(element.Attribute("artUrl").Value)) : null;
similarArtist.ShareUrl = element.Attribute("shareUrl") != null ?element.Attribute("shareUrl").Value : string.Empty;
MainArtist = artist;
MessageBox.Show("Artist not found!");

If the root node name is not error (it only happens in case the artist is not found), then I proceed to parse data from the file, setting the properties for a new Artist instance and then assign it to the one that is bound to the view.

Node-by-node parsing applies to the similar artist list, and I am assigning instance properties the same way as I did for the main class.

Don’t be scared by the abundance of ternary operators. Due to the fact that sometimes artists don’t have data associated with their profile, Pandora will return only partial results, therefore it is a good idea to make sure that every property is set correctly and won’t cause an exception.

In case the artist is not found, I  am simply showing a message that there is nobody Pandora knows of with the name specified in ArtistBox.

Once you launch the application, type an artist and click on Search. Your results should resemble mine (UI-wise):



The Details and Share buttons do nothing but launch a WebBrowserTask with the preset URL that will take the user to the details and share pages accordingly.

You can grab the archived project here.

WARNING: This is a raw experimental solution. It is used for demo purposes only.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}