Music makes the world go around. Funny.. I thought it was the conservation of angular momentum. What do I know?
Those that know me will certainly see that I love my music. It's a big part of my life, and something that is always on hand to help your day.
Since writing the Spotify MetaData API and Last.FM API wrappers, I wanted to extend these by enhancing the results returned from a music search, and display the lyrics of the selected / queried song.
The open-source code for the Lyric Search API wrapper is available now to download from RIA Forge: http://lyricsearch.riaforge.org/
What is it?
The lyricSearch ColdFusion wrapper interacts with a lyrcis database API provided by lyricsfly. Registration is free, and the service requires a permanent API key to access the full, un-restricted results, which can be obtained from a request form on their site.
However, they do offer a temporary API key (that is generated on a weekly basis) to access the API and test the implementation of your code against it.
The CFC wrapper contains only two methods, which are:
- search for lyrics by the artist AND song title
- search for lyrics by a text string
Instantiate the lyricSearch object
Invoking the object is a simple matter of passing through the one required parameter, which is the API key.
// current temporary key at time of writing strAPIKey = '42cbcce797c8ec06f-temporary.API.access'; // invoke the component /* parseOutput is set to true, to get a structural representation of the response */ objLyrics = createObject('component', 'com.coldfumonkeh.lyricsfly.lyrics').init( apiKey=strAPIKey, parseOutput=true );
Here, I am also sending through the parseOutput parameter, which is actually true by default. This boolean value controls the output generated from the API wrapper, and if set to true, will return the response in a structural format (parsed XML or structural JSON). If false, the response will be in a literal string format.
Search using artist and track name
<!--- Run a search for an artist and track title. Bring back the response in JSON format, and convert the placeholder [br] tags into the actual HTML tags. ---> <cfset artistResults = objLyrics.searchArtistTitle( artist="Butch Walker", title="If", format="json", convertHTML=true ) /> <cfdump var="#artistResults#" label="artist / song query results" />
The code above is fairly self-explanatory. One of the parameters (convertHTML) is an optional boolean value set to false by default. In any response from the API, the lyrics text contains forced line-break tags, but in a placeholder format, like so:
And if I could be the chains, I'd fall from you and let you fly to the angels[br]
If I could be your pain I'd run from you, so far away.[br]
Now, this wouldn't be a problem for any user to take this response and replace the placeholders with the correct HTML tags to output onto the screen.
However, it seems like an extra effort that the end user really shouldn't have to worry about and manage themselves; the API should handle that for them, or at least give them the option to choose to do so, which is why the CFC wrapper contains convertHTML boolean parameter.
Replacing XML object values
Converting and replacing the sub-strings within the API response was very easy.
All response directly from the API are returned as XML, and so it's very easy to run a search within the XML object for all track responses, which will generate an array, thanks to xmlSearch().
We can then loop over the array, and run a replaceNoCase() function on every lyric text xml node in the response and update the HTML tags, as seen in the code snippet below:
<cfset var xmlObj = xmlParse(arguments.response) /> <cfset var arrText = '' /> <!--- Run this if we want to convert the HTML ---> <cfif arguments.convertHTML> <!--- Create an array of tracks ---> <cfset arrText = xmlSearch(xmlObj, '/start/sg/tx') /> <cfif arraylen(arrText)> <cfset intLoop = 1 /> <!--- Loop over the array, and replace all instances of the placeholder tags with the correct HTML ---> <cfloop array="#arrText#" index="i"> <cfset xmlObj.start.sg[intLoop].tx.xmlText = replaceNoCase(i.xmlText, '[br]', '<br />', 'all') /> <!--- Increment the loop ---> <cfset intLoop ++ /> </cfloop> </cfif> </cfif>
After any conversions have (or haven't, depending on the selection) been done, the CFC then returns the response to the user, and the example above would generate the following structural response:
Search using a text string
The second method within the API lets you query the database using a text string, like so:
<!--- Run a search for a lyric string ---> <cfset lyricsResults = objLyrics.searchLyric( lyric="just a shot away", format="xml", convertHTML=true ) /> <cfdump var="#lyricsResults#" label="lyrics query results" />
The resulting output for this call returns an XML object finding any matches to the supplied string:
The first result was the correct song I was looking for, which was "Gimme Shelter" by the Rolling Stones (one of the greatest songs of all time, in my opinion).
Where can I get it?
The code is available to download from RIAforge.org, here: http://lyricsearch.riaforge.org/