This tutorial will utilize jQuery Mobile, HTML 5, the PugPig framework, and the iOS SDK to create an application using the MusicBrainz API.
With the introduction of the iPad and various Android tablets, magazine styled applications have become extremely popular. Several of the most prominent of these include FlipBoard for iOS, Zinio for Android, and Pulse for both iOS and Android. There is likewise great demand for tools that can provide magazine layout functionality. Some of the popular tools include FlipBoard (via RSS / Twitter imports), OnSwipe, and PugPig. PugPig is an open-source HTML5 publishing platform that allows us to create beautiful newspapers, magazines, and books for the iPad, iPhone, and Android. For this tutorial we will use the PugPig Framework plugin for Xcode 4.2 to create an HTML5 Magazine.
The design of the magazine will be based on jQuery Mobile, although another equally good option is to use Magazine Grid. We go with jQuery Mobile in this tutorial since it offers extensive widgets and excellent page transitioning workflow for mobile web apps.
The content for the Magazine will be powered by MusicBrainz (the Open Music Encyclopedia). We will build offline HTML pages for popular artists with the jQuery UI. We did a random pick of 20 popular artists from SongKick.com.
Introducing the MusicBrainz API
The MusicBrainz website provides a rich collection of music data, ranging from artist information to their latest releases, with complete details of all the song tracks on each album. It aims to be an open music encyclopedia and it must be doing that job well considering that music services like Last.fm, GrooveShark, Pandora, and Echonest use the data from MusicBrainz. MusicBrainz sources this data back to the community via:
We will use the following Web Services to extract the rich artist information from MusicBrainz:
Artist Information
The artist information available within MusicBrainz includes name, birth and death dates, country of origin, gender, aliases, and type of artist (whether single or group). For the sake of our application use case, let's take Lady Gaga as an example artist. The MusicBrainz page for Lady Gaga is http://musicbrainz.org/artist/650e7db6-b795-4eb5-a702-5ea2fc46c848 where 650e7db6-b795-4eb5-a702-5ea2fc46c848 is the unique Artist ID (which is also referenced by third party music sites, like Last.fm and others).
As seen from the screenshot below, our interest is in most of the right column bar (artist info, tags, external links) and the second tab "Releases" described in the next section.
Artist Release
The artist release contains a list of all albums released by an artist, whether single hits or group performances. It also includes the date of release, label, and even barcode. Just like before, the release ID is unique as well. This release ID can be used to fetch further information from the album, including the Track Lists (songs).
Artist Relation
The artist relation defines other artists to another artist.
Similarly there are API calls for release and tracks relations to similar release and tracks.
We are interested in the Artist Relation attribute and specifically the relation with further attribute as "Url", which lists the Artist Urls on different social and music websites like WikiPedia, IMDB, Twitter, MySpace, Youtube and others.
Having glanced through the MusicBrainz Web Services API, let's step further to create our Magazine App.
Step 1: Creating a PugPig App with Xcode
After you have download the Pugpig Xcode template from the PugPig Site, double-click the package to install the template in Xcode. Open Xcode and start a new project by selecting the PugPig template, as shown below:
Name it "MusicBrainz - Magazine" and complete the process. In your project directory, create a folder named "Data" and add it your project, this is the folder where PugPig looks for the HTML files to render. Your project should now look like:
Now, let's add an index.html to this data folder so we have a strarting point for our magazine app. We use jQuery Mobile's default design here and show a searchable list of Popular Artists. In the index.html file, add the following code, along with the jQuery Mobile framework and jQuery javascript library.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>MusicBrainz - Magazine</title> <link rel="stylesheet" href="jquery.mobile-1.0/jquery.mobile-1.0.css"/> <script src="jquery-1.7.js"></script> <script src="jquery.mobile-1.0/jquery.mobile-1.0.js"></script> </head> <body> <div data-role="page" class="type-interior"> <div data-role="header" data-theme="b"> <h1>Popular Artists</h1> <a href="#" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a> </div><!-- /header --> <script type="text/javascript"> var admob_vars = { pubid: 'a14e4b353ccfa57', // publisher id bgcolor: '356FA8', // background color (hex) text: 'FFFFFF', // font-color (hex) test: false // test mode, set to false to receive live ads }; </script> <script type="text/javascript" src="http://mmv.admob.com/static/iphone/iadmob.js"></script> <div data-role="content"> <div class="content-primary"> <ul data-role="listview" data-filter="true"> <li><a href="650e7db6-b795-4eb5-a702-5ea2fc46c848.html">Lady Gaga</a></li> <li><a href="cc197bad-dc9c-440d-a5b5-d52ba2e14234.html">Coldplay</a></li> <li><a href="73e5e69d-3554-40d8-8516-00cb38737a1c.html">Rihanna</a></li> <li><a href="122d63fc-8671-43e4-9752-34e846d62a9c.html">Katy Perry</a></li> <li><a href="f82bcf78-5b69-4622-a5ef-73800768d9ac.html">Jay-Z</a></li> <li><a href="d5be5333-4171-427e-8e12-732087c6b78e.html">Black Eyed Peas</a></li> <li><a href="20244d07-534f-4eff-b4d4-930878889970.html">Taylor Swift</a></li> <li><a href="eeb1195b-f213-4ce1-b28c-8565211f8e43.html">Guns N' Roses</a></li> <li><a href="e21857d5-3256-4547-afb3-4b6ded592596.html">Gorillaz</a></li> <li><a href="72c536dc-7137-4477-a521-567eeb840fa8.html">Bob Dylan</a></li> <li><a href="65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab.html">Metallica</a></li> <li><a href="45a663b5-b1cb-4a91-bff6-2bef7bbfdd76.html">Britney Spears</a></li> <li><a href="f5dfa020-ad69-41cd-b3d4-fd7af0414e94.html">Wiz Khalifa</a></li> <li><a href="ada7a83c-e3e1-40f1-93f9-3e73dbc9298a.html">Arctic Monkeys</a></li> <li><a href="e0140a67-e4d1-4f13-8a01-364355bee46e.html">Justin Bieber</a></li> <li><a href="3d2b98e5-556f-4451-a3ff-c50ea18d57cb.html">Aerosmith</a></li> <li><a href="0103c1cc-4a09-4a5d-a344-56ad99a77193.html">Avril Lavigne</a></li> <li><a href="d8354b38-e942-4c89-ba93-29323432abc3.html">30 Seconds to Mars</a></li> <li><a href="d262ea27-3ffe-40f7-b922-85c42d625e67.html">PitBull</a></li> <li><a href="35f866dc-c061-48ba-8157-cf2e0eac4857.html">Enrique Iglesias</a></li> </ul> </div><!--/content-primary --> </div><!-- /content --> <div data-role="footer" class="footer-docs" data-theme="b"> <h2>Data from MusicBrainz.org</h2> </div> </div><!-- /page --> </body> </html>
Running the Xcode project now produces the following result:
Double-tap on the app. Somewhere near the footer of the page, it should show you a small window allowing you to browse the other pages (the .html files in the Data folder) like a magazine!
Step 2: Generating HTML Pages with PHP and MusicBrainz Web Services
So far, we have seen how to build the index.html page for our Magazine App, now it is time to look at how we build the artist details page and save them as .html files in the data folder.
We will use PHP and its XML parsing library SimpleXml to parse the MusicBrainz Web Services calls. Before that we will build a simple artist details page Template using jQuery Mobile page layout and CSS. We also put placeholders in the template that will later on be replaced with actual results retrieved from the MusicBrainz Web Services, so we use this page template with real artist data and generate the final artist details page and save it on disk. Here is the artist template:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{ARTIST_NAME}</title> <link rel="stylesheet" href="jquery.mobile-1.0/jquery.mobile-1.0.css"/> <script src="jquery-1.7.js"></script> <script src="jquery.mobile-1.0/jquery.mobile-1.0.js"></script> </head> <body> <div data-role="page" class="type-interior"> <div data-role="header" data-theme="b"> <a href="index.html" data-icon="back" data-iconpos="back" class="ui-btn-left jqm-home">Back</a> <h1>{ARTIST_NAME}</h1> <a href="#" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a> </div><!-- /header --> <div data-role="content"> <div class="content-primary"> <script type="text/javascript"> var admob_vars = { pubid: 'a14e4b353ccfa57', // publisher id bgcolor: '356FA8', // background color (hex) text: 'FFFFFF', // font-color (hex) test: false // test mode, set to false to receive live ads }; </script> <script type="text/javascript" src="http://mmv.admob.com/static/iphone/iadmob.js"></script> <div data-role="collapsible" data-collapsed="false"> <div id='lifeSpanBegin'>Born: {ARTIST_BIRTH_DATE}</div> <div id='gender'>Gender: {ARTIST_GENDER} </div> <div id='country'>Country: {ARTIST_COUNTRY} </div> <div id='alias'>Alias: {ARTIST_ALIAS} </div> <div id='tags'>Tags: {ARTIST_TAGS}</div> </div> <div data-role="collapsible-set"> <div data-role="collapsible" data-collapsed="false"> <h3>Releases</h3> <ul data-role="listview" data-inset="true"> {RELEASES_LIST} </ul> </div> <div data-role="collapsible"> <h3>Links</h3> <ul data-role="listview" data-inset="true"> {LINKS_LIST} </ul> </div> </div> </div><!--/content-primary --> </div><!-- /content --> <div data-role="footer" class="footer-docs" data-theme="b"> <h2>Data from MusicBrainz.org</h2> </div> </div><!-- /page --> </body> </html>
We now create the PHP file that will access the MusicBrainz Web Services for the 20 artists listed before and generate HTML files for each of them. We have used a PHP Array to store the Artist ID for the 20 Popular Artists. You can also store the same in a database and use PHP-Mysql code to retrieve Artist ID and other information necessary.
MusicBrainz provides its database for developers to download and process on their end, instead of using the Web Services. You can download the database and learn how to configure the same on your local machine.
<?php header("Content-type: text/html; charset=utf-8"); include 'classes/artist.php'; include 'classes/release.php'; include 'classes/relation.php'; $artist = new artist(); $release = new release(); $relation = new relation(); $artistTemplate = file_get_contents("artistTemplate.html"); $outputFolder = "Data"; $artistId = array("650e7db6-b795-4eb5-a702-5ea2fc46c848", "cc197bad-dc9c-440d-a5b5-d52ba2e14234", "73e5e69d-3554-40d8-8516-00cb38737a1c", "122d63fc-8671-43e4-9752-34e846d62a9c", "f82bcf78-5b69-4622-a5ef-73800768d9ac", "d5be5333-4171-427e-8e12-732087c6b78e", "20244d07-534f-4eff-b4d4-930878889970", "eeb1195b-f213-4ce1-b28c-8565211f8e43", "e21857d5-3256-4547-afb3-4b6ded592596", "72c536dc-7137-4477-a521-567eeb840fa8", "65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab", "45a663b5-b1cb-4a91-bff6-2bef7bbfdd76", "f5dfa020-ad69-41cd-b3d4-fd7af0414e94", "ada7a83c-e3e1-40f1-93f9-3e73dbc9298a", "e0140a67-e4d1-4f13-8a01-364355bee46e", "3d2b98e5-556f-4451-a3ff-c50ea18d57cb", "0103c1cc-4a09-4a5d-a344-56ad99a77193", "d8354b38-e942-4c89-ba93-29323432abc3", "d262ea27-3ffe-40f7-b922-85c42d625e67", "35f866dc-c061-48ba-8157-cf2e0eac4857"); foreach ($artistId as $id) { $MBArtistQuery = "http://musicbrainz.org/ws/2/artist/?query=arid:$id"; $MBArtistReleasesQuery = "http://musicbrainz.org/ws/2/release/?query=arid:$id"; $MBArtistLinksQuery = "http://musicbrainz.org/ws/2/artist/$id?inc=url-rels"; $fileHandle = fopen("$outputFolder/$id.html", "w+"); $artistXML = simplexml_load_file("$MBArtistQuery"); $artistData = showArtist($id, $artistXML, $artistTemplate); sleep(20); $artistReleaseXML = simplexml_load_file("$MBArtistReleasesQuery"); $artistData = showArtistReleases($id, $artistReleaseXML, $artistData); sleep(20); $artistLinksXML = simplexml_load_file("$MBArtistLinksQuery"); $artistData = showArtistLinks($id, $artistLinksXML, $artistData); fwrite($fileHandle, $artistData); fclose($fileHandle); sleep(20); } function showArtistLinks($artistId, $artistXML, $artistTemplate) { $links = ""; foreach ($artistXML->artist->{'relation-list'}->relation as $artistsRelation) { $relation->type = $artistsRelation['type']; $relation->target = $artistsRelation->target; $links .= "<li><a href='$relation->target'><h4>$relation->type</h4><p><strong>$relation->target</strong></p></a></li>"; } $artistTemplate = str_replace("{LINKS_LIST}", $links, $artistTemplate); return $artistTemplate; } function showArtistReleases($artistId, $artistReleaseXML, $artistTemplate) { $releases = ""; foreach ($artistReleaseXML->{'release-list'}->release as $releaseInfo) { $release->id = $releaseInfo['id']; $release->title = $releaseInfo->title; $release->date = $releaseInfo->date; $release->status = $releaseInfo->status; $release->type = $releaseInfo->{'release-group'}['type']; $release->format = $releaseInfo->{'medium-list'}->medium->format; $release->tracks = $releaseInfo->{'medium-list'}->{'track-count'}; $releases .= "<li>$release->title<span class='ui-li-count'>$release->date</span></li>\n"; } $artistTemplate = str_replace("{RELEASES_LIST}", $releases, $artistTemplate); return $artistTemplate; } function showArtist($artistId, $artistXML, $artistTemplate) { $artist->id = $artistId; $artist->name = $artistXML->{'artist-list'}->artist->name; $artist->gender = $artistXML->{'artist-list'}->artist->gender; $artist->country = $artistXML->{'artist-list'}->artist->country; $artist->lifeSpanBegin = $artistXML->{'artist-list'}->artist->{'life-span'}->begin; foreach ($artistXML->{'artist-list'}->artist->{'alias-list'}->alias as $artistAlias) { $artist->alias.=$artistAlias . ","; } foreach ($artistXML->{'artist-list'}->artist->{'tag-list'}->tag as $artistTags) { $artist->tags.=$artistTags->name . ","; } $artistTemplate = str_replace("{ARTIST_NAME}", $artist->name, $artistTemplate); $artistTemplate = str_replace("{ARTIST_BIRTH_DATE}", $artist->lifeSpanBegin, $artistTemplate); $artistTemplate = str_replace("{ARTIST_GENDER}", $artist->gender, $artistTemplate); $artistTemplate = str_replace("{ARTIST_COUNTRY}", $artist->country, $artistTemplate); $artistTemplate = str_replace("{ARTIST_ALIAS}", $artist->alias, $artistTemplate); $artistTemplate = str_replace("{ARTIST_TAGS}", $artist->tags, $artistTemplate); return $artistTemplate; } ?>
The artist, release, and relation classes included on the top of index.php are simple class objects to hold each type of information for an artist. Find the same in the PHP folder in the Project Download link.
Step 3: Copying the Generated Artist Files in Your Xcode Project
Run the index.php from your CLI, it should generate 20 HTML files with the format ARTIST-ID.html within the Data folder. Copy these 20 files over to your data folder in your Xcode project from Step 1. Now, run your Xcode project and click on an artist to navigate to its details page. You should see the following result for the iPad:
Do not forget to make your Xcode project as "Universal", so your magazine app works fine on iPhone as well. In our case it does and looks great!
Step 4: Controlling the Page Order of Your Magazine
PugPig by default show all the HTML files in the data folder irrespective of index.html or other file names without any order. To always show the index.html page first and then the rest of the pages we need to specify a manifest file in the data folder. Name it custom.manifest and add the following content to it.
CACHE MANIFEST index.html 35f866dc-c061-48ba-8157-cf2e0eac4857.html d262ea27-3ffe-40f7-b922-85c42d625e67.html d8354b38-e942-4c89-ba93-29323432abc3.html 0103c1cc-4a09-4a5d-a344-56ad99a77193.html 3d2b98e5-556f-4451-a3ff-c50ea18d57cb.html e0140a67-e4d1-4f13-8a01-364355bee46e.html ada7a83c-e3e1-40f1-93f9-3e73dbc9298a.html f5dfa020-ad69-41cd-b3d4-fd7af0414e94.html 45a663b5-b1cb-4a91-bff6-2bef7bbfdd76.html 65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab.html 72c536dc-7137-4477-a521-567eeb840fa8.html e21857d5-3256-4547-afb3-4b6ded592596.html eeb1195b-f213-4ce1-b28c-8565211f8e43.html 20244d07-534f-4eff-b4d4-930878889970.html d5be5333-4171-427e-8e12-732087c6b78e.html 82bcf78-5b69-4622-a5ef-73800768d9ac.html 122d63fc-8671-43e4-9752-34e846d62a9c.html 73e5e69d-3554-40d8-8516-00cb38737a1c.html cc197bad-dc9c-440d-a5b5-d52ba2e14234.html 650e7db6-b795-4eb5-a702-5ea2fc46c848.html
We also need to tweak a bit of Objective-C code in our project. Open the MusicBrainz_MagazineViewController.m file and replace the following line in the viewDidLoad method:
[pageControl setDataSource:[[[KGLocalFileDataSource alloc] initWithPath:@"Data"] autorelease]];
to
[pageControl setDataSource:[[[KGHTMLManifestDataSource alloc] initWithPath:@"Data/custom.manifest"] autorelease]];
Also, change the scale of the pageControl to 1.0 from 0.4166667 in the same file.
[pageControl setScale:1.0];
The scale is automatically handle by jQuery Mobile based on the iOS device, so we do not need to scale it down for iPhone.
Enhancements
I have taken the popular artists on tour now and created the HTML pages for them, further enhancements to the app can be done, by automating the PHP process to generate the HTML pages for all the artists and store them in the data folder, thereby creating a complete offline artist magazine. For images, you can take the WikiPedia CC images and include them in the app to make it more appealing.
To make the app more lively, add some Ajax magic and use the rich links, API, RSS, from various music and event websites like eventful.com or songkick.com to create a dynamic page. For example, fetch the latest events or concerts for an artist from eventful.com -food for thought!
Comments