Songza Music App

From DevWiki

Jump to: navigation, search

< Building Your First Application

Songza

a week ago i had some spare time at the boxee office (very rare, probably just me trying to mess with things i love instead of getting things done) and i was playing around with songza, a service i have been using for a while. songza lets you search for free and legal music on the internet, the music is usually fetched from youtube or imeem and starts playing immediately.

while i was listening to some music i decided to open firebug (a super cool tool) and look at what’s going on behind the scenes. after 15 minutes of poking around i found that the basic songza service of searching for songs and playing them is based on three simple api calls. as the site also has a very simple design which could work well on a tv (hence i dont need to distract our graphic designer from the important work he is doing), i decided to go ahead and build a songza application.

first step was indeed mapping the api calls i was going to use. i knew i needed the auto-complete feature, the search feature and the play feature. after looking closely at the http calls going back and forth in firebug i found them.

this api call gets a short list of matches to the auto-complete box (i’m searching for enjoy the silence as i was in a depeche mode concert in tlv last week):

GET http://songza.com/complete?q=enjoy+the

and in python it would look something like this:

import urllib
text = "enjoy+the"
socket = urllib.urlopen( "http://songza.com/complete?q=" + urllib.quote(text) )
result = socket.read()
socket.close()

the response is a list of up to ten lines:

  • enjoy the silence
  • enjoy the scilence
  • enjoy the ride
  • enjoy the abuse
  • enjoy the silence remix
  • enjoy the silence depeche mode
  • enjoy the silence by depeche mode
  • enjoy the silence 2004
  • enjoy the show
  • enjoy the slience

this api call searches the songza database for music matching the search phrase:

POST "http://songza.com/a/query"

and in python it would look something like this:

import urllib, urllib2
text = "enjoy the silence depeche mode"
params = urllib.urlencode({ "q": text })
req = urllib2.Request("http://songza.com/a/query", params)
response = urllib2.urlopen(req)
res = response.read()

the response is an html list of matching songs with some information on them like songza title and a songza id:

<li id="component-3" class="za-song-alt  za-song za-song-default first">
    <a id="m1t5-fIH4Jdny3B" class="song" href="/song/depeche-mode-enjoy-the-silence:m1t5-fIH4Jdny3B"
        title="Depeche Mode - Enjoy the Silence">Depeche Mode - Enjoy the Silence</a>
 </li>
 <li id="component-4" class=" za-song za-song-default">
    <a id="a2r3-lK8N3k1bhuo" class="song" href="/song/depeche-mode-enjoy-the-silence-live:a2r3-lK8N3k1bhuo"
        title="Depeche Mode - Enjoy The Silence (live)">Depeche Mode - Enjoy The Silence (live)</a>
 </li>
 <li id="component-5" class="za-song-alt  za-song za-song-default">
    <a id="m1t5-nOmkDfFBqz" class="song" href="/song/depeche-mode-enjoy-the-silence-reinterpreted:m1t5-nOmkDfFBqz"
        title="Depeche Mode - Enjoy the Silence (Reinterpreted)">Depeche Mode - Enjoy the Silence (Reinterpreted)</a>
 </li>

and the last api call gets more information on each song according to its songza id and most important the link to its flash player:

GET "http://songza.com/a/song?z=a2r3-lK8N3k1bhuo"

and this is how it would look like in python:

import urllib
socket = urllib.urlopen( "http://songza.com/a/song?z=a2r3-lK8N3k1bhuo" )
result = socket.read()
socket.close()

with a JSON response which includes the flash player and the link to the actual web page of the song:

{"id":"a2r3-lK8N3k1bhuo","title":"Depeche Mode - Enjoy The Silence (live)",
     "url":"http:\/\/www.youtube.com\/watch?v=lK8N3k1bhuo","duration":"0",
     "dasherized_title":"depeche-mode-enjoy-the-silence-live","tiny":"t24q4c",
     "embed":"<object width=\"425\" height=\"344\"><param name=\"movie\" 
     value=\"http:\/\/www.youtube.com\/v\/lK8N3k1bhuo&hl=en&fs=1\"><\/param><param 
     name=\"allowFullScreen\" value=\"true\"><\/param><param name=\"allowscriptaccess\" 
     value=\"always\"><\/param><embed src=\"http:\/\/www.youtube.com\/v\/lK8N3k1bhuo&hl=en&fs=1\" 
     type=\"application\/x-shockwave-flash\" allowscriptaccess=\"always\" allowfullscreen=\"true\" 
     width=\"425\" height=\"344\"><\/embed><\/object>"}

so now when i had all the main parts needed to write the application i went ahead and started writing the application itself. i created the framework needed (as described here) with some graphics and logos from the songza site. i knew i would need an edit control for the text entry, an edit control allows free text input either through the virtual keyboard or by using a keyboard and receives events for every character entered, which allows to preform actions exactly like auto-complete.

this is how my edit control looks like with its code for the ontextchange event. when the text changes get the text from the control and call the auto-complete api call with it, once i got the line list back i broke it apart and created a list of list items out of it which i then set to a list control.

<control type="edit" id="110">
    <posx>4</posx>
    <posy>4</posy>
    <width>680</width>
    <height>50</height>
    <onleft>-</onleft>
    <onright>120</onright>
    <onup>-</onup>
    <ondown>140</ondown>
    <onnext>-</onnext>
    <onprev>-</onprev>
    <label>-</label>
    <textoffsetx>8</textoffsetx>
    <align>left</align>
    <aligny>center</aligny>
    <texturenofocus>white.png</texturenofocus>
    <texturefocus>white.png</texturefocus>
    <font>font36</font>
    <textcolor>black</textcolor>
    <disabledcolor>darkgrey</disabledcolor>
    <ontextchange lang="python"><![CDATA[
 import urllib
 try:
    import urllib
 #get the text from the edit control
    text = mc.GetWindow(14000).GetEdit(110).GetText()
    if len(text) > 0:
        mc.GetWindow(14000).GetControl(130).SetVisible(True)
 #send an api request for the list of auto-complete results
        socket = urllib.urlopen( "http://songza.com/complete?q=" + urllib.quote(text) )
        result = socket.read()
        socket.close()
        items = mc.ListItems()
 #create a lit from the lines of the results
        result = result.split("\n")
        if len(result) == 11:
            result.pop(10)
 #create and item from every line in the result and push it into the auto-complete list
        for line in result:
            item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
            item.SetLabel(line)
            items.append(item)
        mc.GetWindow(14000).GetList(140).SetItems(items)
        mc.GetWindow(14000).GetList(140).SetVisible(True)
        mc.GetWindow(14000).GetControl(130).SetVisible(False)
    else:
        mc.GetWindow(14000).GetList(140).SetVisible(False)
 except Exception, e:
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
    mc.ShowDialogOk("Songza", "Error. " + str( e ))
 ]]></ontextchange>
 </control>

the list control itself becomes visible only when the list is populated with lines. when the user clicks either the list or the search button to its right a search call is made using either the text in the list item or in the edit control accordingly. when the response is received i parse through it and create a new list of items with the song titles and the links to the songza page of each of them (built using the sonza id).

<control type="list" id="140">
    <posx>4</posx>
    <posy>56</posy>
    <width>680</width>
    <height>300</height>
    <onleft>-</onleft>
    <onright>-</onright>
    <onup>110</onup>
    <ondown>300</ondown>
    <scrolltime>200</scrolltime>
    <pagecontrol>-</pagecontrol>
    <orientation>vertical</orientation>
    <itemlayout width="680" height="30">
	    <control type="image">
		    <posx>0</posx>
		    <posy>0</posy>
		    <width>680</width>
		    <height>30</height>
		    <texture>white.png</texture>
	    </control>
	    <control type="label">
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>660</width>
		    <height>26</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>black</textcolor>
	    </control>
    </itemlayout>
    <focusedlayout width="680" height="30">
	    <control type="image">
		    <visible>!Control.HasFocus(140)</visible>
		    <posx>0</posx>
		    <posy>0</posy>
		    <width>680</width>
		    <height>30</height>
		    <texture>white.png</texture>
	    </control>
	    <control type="image">
		    <visible>Control.HasFocus(140)</visible>
		    <posx>0</posx>
		    <posy>0</posy>
		    <width>680</width>
		    <height>30</height>
		    <texture>black.png</texture>
	    </control>
	    <control type="label">
		    <visible>!Control.HasFocus(140)</visible>
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>660</width>
		    <height>26</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>black</textcolor>
	    </control>
	    <control type="label">
		    <visible>Control.HasFocus(140)</visible>
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>660</width>
		    <height>26</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>white</textcolor>
	    </control>
    </focusedlayout>
    <content type="action">
	    <onclick lang="python"><![CDATA[
 import urllib, urllib2
 import util
 try:
 #get the selected item from the auto-complete list
    n = mc.GetWindow(14000).GetList(140).GetFocusedItem()
    item = mc.GetWindow(14000).GetList(140).GetItem(n)
    mc.GetWindow(14000).GetControl(140).SetVisible(False)
    mc.GetWindow(14000).GetControl(130).SetVisible(True)
 #send a search api request for the exact phrase
    params = urllib.urlencode({ "q": item.GetLabel() })
    req = urllib2.Request("http://songza.com/a/query", params)
    response = urllib2.urlopen(req)
    res = response.read()
    songs = res.split("<li ")
    songs.pop(0)
    items = mc.ListItems()
 #parse through the search results and create a list item from each one
    for song in songs:
        song = song[song.find("<a id=\"") + len("<a id=\""):len(song)]
        id = song[0:song.find("\"")]
        song = song[song.find("title=\"") + len("title=\""):len(song)]
        title = song[0:song.find("\"")]
        item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_CLIP)
        item.SetLabel(util.clean_html_tags(title))
        item.SetPath("http://songza.com/a/song?z=" + id)
        items.append(item)
 #push the song items in to the song list
    mc.GetWindow(14000).GetList(200).SetItems(items)
    mc.GetWindow(14000).GetList(200).SetVisible(True)
    mc.GetWindow(14000).GetList(200).SetFocus()
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
 except Exception, e:
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
    mc.ShowDialogOk("Songza", "Error. " + str( e ))
 ]]></onclick>
    </content>
 </control>

same is done with the search button only on the text in the edit control.

<control type="button" id="120">
    <posx>700</posx>
    <posy>0</posy>
    <width>148</width>
    <height>58</height>
    <onleft>110</onleft>
    <onright>-</onright>
    <onup>-</onup>
    <ondown>140</ondown>
    <texturenofocus>-</texturenofocus>
    <texturefocus>white.png</texturefocus>
    <colordiffuse>FF222222</colordiffuse>
    <label>-</label>
    <onclick lang="python"><![CDATA[
 import urllib, urllib2
 import util
 try:
 #get the text from the edit control
    text = mc.GetWindow(14000).GetEdit(110).GetText()
    mc.GetWindow(14000).GetControl(140).SetVisible(False)
    mc.GetWindow(14000).GetControl(130).SetVisible(True)
 #send a search api request for the exact phrase in the edit control
    params = urllib.urlencode({ "q": text })
    req = urllib2.Request("http://songza.com/a/query", params)
    response = urllib2.urlopen(req)
    res = response.read()
    songs = res.split("<li ")
    songs.pop(0)
    items = mc.ListItems()
 #create a list item for each song and push into the song list
    for song in songs:
        song = song[song.find("<a id=\"") + len("<a id=\""):len(song)]
        id = song[0:song.find("\"")]
        song = song[song.find("title=\"") + len("title=\""):len(song)]
        title = song[0:song.find("\"")]
        item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_CLIP)
        item.SetLabel(util.clean_html_tags(title))
        item.SetPath("http://songza.com/a/song?z=" + id)
        items.append(item)
    mc.GetWindow(14000).GetList(200).SetItems(items)
    mc.GetWindow(14000).GetList(200).SetVisible(True)
    mc.GetWindow(14000).GetList(200).SetFocus()
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
 except Exception, e:
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
    mc.ShowDialogOk("Songza", "Error. " + str( e ))
 ]]></onclick>
 </control>

at this point i had a list with all the matching songs and their urls on songza, when a user clicked one of the songza i had to fetch the song information get the real song location, either on youtube or on imeem, set the thumbnail for it (turns out thumbnails are stored on songza according to the songza id) and send it to the boxee player. this is the onclick code for the main song list.

<control type="list" id="200">
    <visible>false</visible>
    <posx>0</posx>
    <posy>0</posy>
    <width>800</width>
    <height>408</height>
    <onleft>-</onleft>
    <onright>310</onright>
    <onup>100</onup>
    <ondown>-</ondown>
    <scrolltime>200</scrolltime>
    <pagecontrol>60</pagecontrol>
    <orientation>vertical</orientation>
    <itemlayout width="800" height="34">
	    <control type="label">
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>780</width>
		    <height>34</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>white</textcolor>
	    </control>
    </itemlayout>
    <focusedlayout width="800" height="34">
	    <control type="label">
		    <visible>!Control.HasFocus(200)</visible>
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>780</width>
		    <height>34</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>white</textcolor>
	    </control>
	    <control type="label">
		    <visible>Control.HasFocus(200)</visible>
		    <posx>10</posx>
		    <posy>0</posy>
		    <width>780</width>
		    <height>34</height>
		    <font>font23</font>
		    <align>left</align>
		    <aligny>center</aligny>
		    <label>$INFO[ListItem.Label]</label>
		    <textcolor>black</textcolor>
		    <selectedcolor>black</selectedcolor>
	    </control>
    </focusedlayout>
    <content type="action">
	    <onclick lang="python"><![CDATA[
 import urllib
 try:
 #get the selected item from the song list
    n = mc.GetWindow(14000).GetList(200).GetFocusedItem()
    item = mc.GetWindow(14000).GetList(200).GetItem(n)
    mc.GetWindow(14000).GetControl(130).SetVisible(True)
 #send an api request for the full information on the song
    socket = urllib.urlopen( item.GetPath() )
    result = socket.read()
    socket.close()
 #create the url for the song thumbnail
    thumb = result[result.find("\"id\":\"") + len("\"id\":\""):len(result)]
    thumb = thumb[0:thumb.find("\"")]
    thumb = "http://songza.com/album-art/" + thumb + ".jpg"
    item.SetThumbnail(thumb)
 #create a url for the song http page location
    path = result[result.find("\"url\":\"") + len("\"url\":\""):len(result)]
    path = path[0:path.find("\"")]
    path = path.replace("\/", "/")
    item.SetPath(path)
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
 #play the song
    mc.Player().Play(item)
 except Exception, e:
    mc.GetWindow(14000).GetControl(130).SetVisible(False)
    mc.ShowDialogOk("Songza", "Error. " + str( e ))
 ]]></onclick>
    </content>
 </control>

this application was written in less than a day. very simple, very useful and pretty cool.

i urge you all to start looking for api’s of cool services out there on the internet (or start perfecting your firebug skills) and write some new boxee applications. the boxee dev challenge is just around the corner and there are some very cool prizes to be won.

dont hesitate to contact me with questions regarding application development, boxee apis or the world economic crisis. either through our forum or through my twitter @idancohen

Personal tools