A
lex
Hu
ghes
2013
Un
it 3
5 W
eb
Ap
pli
cati
on
D
ev
elo
pm
en
t: O
nli
ne
Mu
sic
Sto
re
You are tasked by the ailing high street music store INW to create a web application which will enable users to search for and buy MP3 tracks online, in the hope that a new online market will improve the company’s sales figures. The website should enable a user to view all the available artists. The user should then be able to select an artist and view all the MP3s that are available for that artist. MP3s should be listed with the option to “add to basket” so that a user’s basket can contain a number of MP3s. The site should have the following functionality: 1. Create a user Account, 2. View a filtered list of artists with pictures, 3. View a filtered list of albums for a selected artist with pictures, 4. View a filtered list of tracks (with prices) available for a selected artist or belonging to a selected album, 5. Listen to an MP3 sample of a selected track, 6. The home page should advertise a “featured artist” showing the artist details and their discography.
Chichester College HND Computing
Year 2
1. Create a user Account (details required are title (Mr, Miss, Dr etc.) first name,
surname, email, username, password, first line of address, postcode, date of birth).
2. View a filtered list of artists with pictures.
3. View a filtered list of albums for a selected artist with pictures.
4. View a filtered list of tracks (with prices) available for a selected artist or
belonging to a selected album.
5. Listen to an MP3 sample of a selected track.
6. The home page should advertise a “featured artist” showing the artist details and
their discography.
You are required to meet the following requirements for robustness and useability:
i. Users creating accounts must be protected from causing errors with full validation
and meaningful error messages.
ii. Users creating accounts must provide a password which is hidden on screen so will
require verification.
iii. Users creating accounts must be prevented from submitting a username that is
already in use.
iv. Useful help should be available for customers to explain what is required for each
part of a form used to create an account.
The web application must include:
a) A carefully designed logo and banner for INW Music Store.
b) A carefully presented layout and web design.
c) PHP, javaScript and AJAX appropriately implemented.
d) HTML5 and CSS3 outputs.
Tasks (tick when complete)
Design a Web Application
structure to support the basic requirements above.
me,
layout, fonts, headings and images). Choose one design and explain why.XXX
page. Describe the validation and verification you intend and how you will
implement it (provide details down to the error messages and help you will
provide).
Build a Web Application
extended ERD). Evidence this with annotated screen-shots or SQL script.
account).
owing artists (with pictures of each artist) including filter and/or
search facilities.
MP3 sample.
Test and Review a Web Application
Evidence this with annotated screen shots.
Make detailed notes of their comments and present the evaluation sheets as
evidence.
-by-Step guide using annotated screen-shots to show how a user
should work with the web application.
Anything Else? Hints towards D3...
this changes things. Can you incorporate this into the application?
this changes things. Can you incorporate this into the application?
this into the application?
days. Can you incorporate this into the application?
Deliverables
A single PDF document containing design notes, code samples, screen shots and annotation
to demonstrate your web application.
Don’t forget to include a critical reflection! Write a 700-800 word conclusion in which
you describe your learning during this module. Did you meet the learning outcomes you
intended? What have you learned about your approach to learning?
INW Website
The Designs
Design 1
I wanted to do something around the dog and speaker motif used by another music retailer so I got
working in Photoshop with 2 images and the logo came out. I gave 3 copies the Andy Warhol
treatment and this is the result. I thought about using a large version as a background but if you look
at real sites the logo is quite small (to make maximum space for sales materials). I don’t have quite
as many adverts (though I might add a few) so my logo is a bit bigger.
I thought about how one could steal a march on Amazon and iTunes and I think the only way is by
offering Lossless downloads (the apple codec for lossless is now open source).
Most retail websites are white. White means simplicity, corporate style, friendly and easy to use. The
orange colour (of a popular retailer) signifies value. However the way the web and computers are
going, the dark grey is in ascendancy. Look at new versions of Expression Studio and Photoshop and
the trend is clear.
Fontwise I am using Nunito on Google webfonts. It accurately copies the original and is a bit cooler
on some lower case letters.
You can really easily download the font free of charge to mock up in Photoshop and then google will
give you CSS to cut and paste into your style sheet to link it to the web resource for your web
punters.
I’m not really happy with the button colours. But I will make button dynamically using rounded
boxes and fountain fills in css3 (or maybe a sliver for slightly older browsers – not sure how long we
keep using jQuery and slivers – a web company I visited still are)
Search box and navigation will need adding and the input tag will need styling specifically.
The use of drop shadow on more or less everything except the letters of the logo gives a subtle 3d
effect and the flat logo text looks modern and smooth.
I like the clean open look of the page. Pete MacKean (fellow student on the course) says the golden
rule of graphic design is not to leave too much whitespace so it could do with a bit more on the page
– possibly a “you may also like” feature.
Design 2
This design uses alpha transparency and a sort of organic looking shape that would lend itself well to
animation. The pale grey suggests a colder, more technical look and the overall effect is a lot flatter.
I like the stretched font logo and the bleed of the tab to the right side of the page also the black and
purple look of the text is appealing.
I am using the same font (all three weights) in pretty much the same formats.
This design definitely needs something. It may be a massive bass bin background image under the 3d
dog. Or just a warm up of colour-scheme.
It’s definitely intriguing but the judge, Poli Fowdrey, reckoned the first was more effective.
“Trust me on this one”,
“The second just looks really clunky. I know there are sites that use that kind of thing
but it just looks really dated.”
It could be the pixelated wireframe render from Maya which I think I can fix. But the first design is
the one to go for.
Goodies to include in the site
Zend framework 2
http://framework.zend.com/
This modular set of PHP classes provides functions such as :
Authentication
MVC layout
Email composition and delivery
getID3()
http://getid3.sourceforge.net/
This PHP class analyses mp3 and other file formats and extracts the meta tag information from them
to inject directly into your database.
iD3.org
http://id3.org/id3v2.3.0#head-0ef7011e13ae8b3678a676a65b64760b9cedf1de
Useful info on stripping image from id3 tag.
KTaglib
KTaglib
http://www.php.net/manual/en/book.ktaglib.php
another tag library.
BF_Download
class.Bf_Download.php
http://www.phpclasses.org/browse/file/15045.html
This class allows you to keep your files in a private directory and download them only on demand
from a dynamically created URL. Thus preventing someone who has downloaded a file once from
“hotlinking” to the file at any other time. It also supports partial downloads and bandwidth
management.
Critical Reflection (half way through…) I was so enthusiastic about web design in the summer of 2011 that I self-taught myself a lot of the
material in this course and built and published an e-commerce site with authentication email
verification via SHA1 hashing. I took it to a web design company and they said it was no good, that all
PHP was object oriented and the best thing to do was learn a framework (their own.)
I timed out on the assignment, couldn’t get to grips with the sheer monolith of code and submitted a
piece of work that bypassed most of the framework (except the db connection.) They had a better
candidate.
Doing PHP’s sister unit: ASP.net MVC gave me an insight into the Models Views and Controllers
paradigm. So I decided to implement a bit of the Zend2 framework in the site.
I will be using the Zend\Db, Zend\Authentication, Zend\Validator, ***** stop *****remix 4 weeks
later and site due in 2 days.
****Due to a serious time crisis I am not implementing Zend in this revision. Rather, I am building a
very basic framework based on MVC. This will actually be quicker and may make for a nice resource
for someone to learn from. Zend will be used in release 2.
PHP
The Models Here is the basic data model with some methods roughed out.
<?php
class user
{
public $id;
public $name;
public $email;
}
abstract class item
{
public $id;
public $songAlbumBoo;
public $title;
public $runTime;
public $genre;
public $image;
public $releaseDate;
public $reviewId;
public $price;
public $priceCurrency;
public $payCharityHowMuch;
public $payCharityWhoPayPalId;
public $payCharityCurrency;
public $payArtistHowMuch;
public $payArtistWhoPayPalId;
public $payArtistCurrency;
public static function getById($id)
{
}
}
class album extends item
{
public $numberOfTracks;
public $tracks = array("guns and roses","queen");
public $artistId;
public function __construct()
{
}
public static function getById($id)
{
}
}
class song extends item
{
public $trackNumber;
public $artistId;
}
class genre
{
public $id;
public $name;
}
class artist
{
public $id;
public $name;
public $images;
}
class image
{
public $id;
public $urlLarge;
public $urlMedium;
public $urlThumb;
public $urlIcon;
}
class basket
{
public $userId;
public $dateLastUpdated;
public $basketItems;
public function getFromDb()
{
}
public function putToDb()
{
}
public function addItem(basketItem $basketItem)
{
$this->basketItems[$basketItem->id]=$basketItem;
db::addBasketItem($basketItem);
}
public function removeItem(basketItem $basketItem)
{
unset($this->basketItems[$basketItem->id]);
db::removeBasketItem($basketItem);
}
public function now()
{
$now=array();
foreach($this->basketItems as $itemId => $basketItem)
{
if($basketItem->nowLaterBoo==1)
{$now[]=$basketItem;}
}
return $now;
}
public function later()
{
$later=array();
foreach($this->basketItems as $itemId => $basketItem)
{
if($basketItem->nowLaterBoo!==1)
{$later[]=$basketItem;}
}
return $later;
}
}
class basketItem
{
public $id;
public $userId;
public $itemId;
public $itemHitId;
public $dateAdded;
public $nowLaterBoo;
public $priceToPay;
public $albumCompletePaidSoFar;
}
?>
The original database schema was mocked up in programmer’s notepad 2 and looks like this:
tables
user
id
name
password
item
id
songAlbumBoo
title
runTime time
genreId
imageId
releaseDate
reviewId
price
payCharityHowMuch
payCharityWhoPayPalId
payCharityCurrency
payArtistHowMuch
payArtistWhoPayPalId
payArtistCurrency
album
itemId
numberOfTracks
albumArtistId
song
itemId
trackNumber
artistId
genre
id
name
albumSong
albumId
songId
trackNo
artist
id
name
artistImage
artistId
imageId
mainBoo
artistItem
artistId
itemId
mainBoo
itemHit
id
itemId
userId
viewBasketWish
date
alsoLikeId
alsoLike
id
itemIdL
itemIdH
dateCreated
hitsYes
hitsNo // if someone doesn't look when suggested
alsoLikeHitYes // one of these makes an 'alsoLike' have more 'hitsYes'
alsoLikeId
userId
basketBoo
wishBoo
basket
userId
dateLastUpdated
basketItem
id
itemId
itemHitId
dateAdded
nowLaterBoo
order
id
userId
date
paidBoo
orderItem
orderId
itemId
itemHitId
priceToPay
albumCompletePaidSoFar
downloadIsAvailSong
id
orderId
userId
songId
numberOfDownloads
filename
download
id
date
downloadIsAvailSongId
dlClientId
wishlist
id
userId
name
dateCreated
publicBoo
wishListItem
wishListId
itemId
dateAdded
priority
promoList
id
name
dateCreated
currentBoo
text
pageUrl
***When I have finished this project I will write a css style for google code prettify to output PN2
document as rendered on screen.
I decided to write a parser to save writing out all that SQL and just use my very neat and easy to read
tabulated text.
This turned out to be a greater undertaking than I had thought and I have done it a bit scrappily
“plan to throw away 1 implementation you will anyway” : Fred Brooks.
Here’s the c# console code:
CodeLine.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; using System.IO; namespace lxSGen { public class TxtFile { private string _filename; public string filename { get { return _filename; } } public int currentLineNo { get; set; } public string getLine() { try { string getLine = _reader.ReadLine(); currentLineNo ++; return getLine; } catch { return null ; } } private StreamReader _reader; public TxtFile(string inFilename) { _filename = inFilename; _reader = new StreamReader(_filename); currentLineNo = 0; } } public enum Com { value, db, tables, v, i, boo, time, date, fk, pk, ai, n, nn } public class CodeLine { public static readonly Dictionary<Com, ConsoleColor> cc = new Dictionary<Com, ConsoleColor>() { {Com.boo,ConsoleColor.DarkRed}, {Com.date,ConsoleColor.DarkYellow}, {Com.db,ConsoleColor.Magenta}, {Com.fk,ConsoleColor.Cyan}, {Com.i,ConsoleColor.Red}, {Com.n, ConsoleColor.Gray},
{Com.pk, ConsoleColor.Cyan}, {Com.tables, ConsoleColor.White}, {Com.v, ConsoleColor.Red}, {Com.value, ConsoleColor.DarkYellow}, {Com.time, ConsoleColor.DarkBlue} }; public static readonly Dictionary<Com, DataType> cd = new Dictionary<Com, DataType>() { {Com.boo,DataType.boolean}, {Com.date,DataType.date}, {Com.i,DataType.integer}, {Com.v, DataType.varchar}, }; static string[] tab = { "\t" }; static string[] tabsp = { "\t", " " }; public string[] strCom; public int intArgs { get { return strCom.Length; } } public Com[] eCom; public int level; public int lineNo {get;set;} public void readString(string strLine, int l) { lineNo = l; string[] strTabs = strLine.Split(tab, StringSplitOptions.None); level = 0; if (strTabs != null) { for (level = 0; level < strTabs.Length && strTabs[level] == ""; level++) { } } else return; strCom = strLine.Split(tabsp, StringSplitOptions.RemoveEmptyEntries); if (strCom != null) { eCom = new Com[strCom.Length]; for (int i = 0; i < strCom.Length; i++) { Enum.TryParse(strCom[i], true, out eCom[i]); //{ eCom[i] = Com.value; } } } else level = 0; } public bool readFromStream(StreamReader reader, int l) { string strLine; if ((strLine = reader.ReadLine()) != null) { this.readString(strLine, l); return true; } else return null; } public string ToString()
{ string toString = lineNo+"\t"; for (int i = 0; i < level; i++) { toString+="\t"; } if (eCom != null) { for (int j = 0; j < intArgs; j++) { if ((int)eCom[j] != 0) { toString += eCom[j].ToString() + " "; } else { toString += strCom[j] + " "; } } toString += "\n"; } return toString; } public void ToPrint() { Console.ForegroundColor = ConsoleColor.Cyan; Console.Write(lineNo + "\t"); for (int i = 0; i < level; i++) { Console.Write("\t"); } for (int j = 0; j < intArgs; j++) { if ((int)eCom[j] != 0) { Console.ForegroundColor = cc[eCom[j]]; Console.Write(eCom[j].ToString() + " "); } else { Console.ForegroundColor = cc[eCom[j]]; Console.Write(strCom[j] + " "); } } Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; } } }
DB.cs using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace lxSGen { public enum DataType { varchar, integer, dec, date, boolean } class db { public string name { get; set; } public ICollection<Table> tables { get; set; } public ICollection<Constraint> constraints { get; set; } public int baseLevel { get; set; } public void Load(StreamReader reader, int i) { CodeLine codeLine=new CodeLine(); while((codeLine.readFromStream(reader,i) { i++; if ((codeLine.level==baseLevel+1) && (codeLine.eCom[0]==Com.tables) { Table thisTable = new Table(); thisTable.baseLevel = codeLine.level; thisTable.name = "tbl"+codeLine.strCom[1]; thisTable.db =this; thisTable.Load(reader, i); tables.Add(thisTable); } if (codeLine.level==0) return; } } } class Table { public string name { get; set; } public db db {get;set;} public ICollection<Column> columns { get; set; } public int baseLevel { get; set; } public void Load(StreamReader reader, int i) { CodeLine codeLine=new CodeLine(); while((codeLine.readFromStream(reader,i) { i++; if ((codeLine.level==baseLevel+1) && (codeLine.eCom[0]==Com.value) { Column thisColumn = new Column(); thisColumn.baseLevel = codeLine.level; thisColumn.name = "tbl"+codeLine.strCom[1]; thisColumn.table =this; thisColumn.Load(codeLine);
columns.Add(thisColumn); } if (codeLine.level==0) return; } } } class Column { public string name {get; set;} public Table table { get; set; } public DataType dataType {get; set;} public int[] qualifiers {get;set;} public bool PK {get; set;} public bool FK {get; set;} public Table table {get;set;} public bool AI { get; set; } public int AIstart { get; set; } public void Load(CodeLine codeLine) { name=codeLine.strCom[0]; int i = 0; try{dataType=codeLine.cd[codeLine.eCom[1]];} catch(Exception ex){Console.WriteLine("Error on Line {0}:Bad DataType:{1}:{2}",((codeLine.lineNo),(codeLine.ToString()),(ex.ToString()));return;} try{ switch (dataType) { case DataType.varchar : {qualifiers = new int[] {int.Parse(codeLine.strCom[2])};i=3;} case DataType.integer : {qualifiers = new int[] {int.Parse(codeLine.strCom[2])};i=3;} case DataType.dec : {qualifiers = new int[] {int.Parse(codeLine.strCom[2]),int.Parse(codeLine.strCom[3])};i=4;} case DataType.date : {i=2;} case DataType.boolean : {i=2;} } } } } class Constraint { public string name { get; set; } } class PrimaryKey : Constraint { public ICollection<Column> columns { get; set; } } class ForeignKey : Constraint { public Column onColumn { get; set; } public Column refColumn { get; set; } } }
Program.cs using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace lxSGen { class Program { static void Main(string[] args) { int i; string strInputFilename; string strOutputFilename; string strOutputFile=""; string strTableDefs; string strConstraints; string strLine; string[] dot = new string[] { "." }; // strInputFilename = args[0]; strInputFilename = "inwdbitem.txt"; StreamReader reader = new StreamReader(strInputFilename); strOutputFilename = strInputFilename.Substring(strInputFilename.LastIndexOf("."))+".sql"; i = 0; CodeLine codeLine=new CodeLine(); while(codeLine.readFromStream(reader,i)) { strOutputFile += codeLine.ToString(); codeLine.ToPrint(); if (codeLine.eCom[0] == Com.db) { db thisDb = new db(); try { thisDb.name = codeLine.strCom[1]; } catch (Exception ex) { Console.WriteLine("Error on Line {0}:{1}:{2}", (codeLine.lineNo), (codeLine.ToString()),ex.ToString()); return; } thisDb.Load(reader,i); } i++; } Console.Write(strOutputFile); reader.Close(); Console.ReadLine(); } }
}
ID3 tags I got the ID3() class from the web and tried it out with a simple recursive program that ran through
my iTunes db in PHP gatting the metadata from the files.
Here is the PHP code of my program.
<?php
require_once('getid3/getid3.php');
class DirList {
public $dir;
public $depth =0 ;
public $dh;
public $filename;
public function addToLibrary() {
if(is_dir($this->dir)) {
echo"<ul>\n";
if ($this->dh = opendir($this->dir)) {
while (($this->filename = readdir($this->dh)) !== false) {
if (($this->filename!==".")&&($this->filename!=="..")) {
// for($i=0;$i<$this->depth;$i++){echo" ";}
echo "<li>".$this->filename."</li>\n";
if(is_dir($this->dir.$this->filename)) {
$sub= new DirList();
$sub->depth = $this->depth +1;
$sub->dir = $this->dir.$this->filename."/";
$sub->addToLibrary();
} else {
$getID3 = new getID3;
$getID3->include_module("audio-video.quicktime");
$ThisFileInfo = $getID3->analyze($this->dir.$this->filename);
echo '<pre>'.htmlentities(print_r($getID3->info,
true)).'</pre>';
if (!empty($getID3->info['comments']['picture'])) {
foreach ($getID3->info['comments']['picture'] as $key =>
$picture_array) {
$writefile='images/'.$ThisFileInfo['comments']['title'][0] . $key . '.'
. str_replace('image/', '', $picture_array['image_mime']);
file_put_contents("c:/xampp/htdocs/inw.com/$writefile",
$picture_array['data']);
echo"<img width="100px" src="$writefile" ;="" }=""
closedir($this-="">dh);
}
echo"</ul>";
}
}
}
$d = new DirList();
$d->dir = 'H:/iTunes/Music/jah wobble/';
$d->addToLibrary();
?>
And Here is the output for just that one directory.
As you can see it doesn’t work for Apple Lossless files.
It works ok for normal mp3s.
I looked around a bit for something that would get the data but everything was sketchy.
So I elected for another approach. To export the Itunes Library to an xml file and parse that. Then
get the pictures from the id3 tags as before.
So I read up and wrote my second parser of the project in PHP. Here it is:
Here’s the xml sample:
<plist version="1.0">
<dict>
<key>Major Version</key><integer>1</integer>
<key>Minor Version</key><integer>1</integer>
<key>Date</key><date>2013-02-06T12:58:03Z</date>
<key>Application Version</key><string>11.0.1</string>
<key>Features</key><integer>5</integer>
<key>Show Content Ratings</key><true>
<key>Music Folder</key><string>file://localhost/H:/iTunes/</string>
<key>Library Persistent ID</key><string>49FB3F7105D2F947</string>
<key>Tracks</key>
<dict>
<key>3986</key>
<dict>
<key>Track ID</key><integer>3986</integer>
<key>Name</key><string>Kalimba</string>
<key>Artist</key><string>Mr. Scruff</string>
<key>Album Artist</key><string>Mr. Scruff</string>
<key>Composer</key><string>A. Carthy and A.
Kingslow</string>
<key>Album</key><string>Ninja Tuna</string>
<key>Genre</key><string>Electronic</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>8414449</integer>
<key>Total Time</key><integer>348055</integer>
<key>Track Number</key><integer>1</integer>
<key>Year</key><integer>2008</integer>
<key>Date Modified</key><date>2009-07-
14T06:32:31Z</date>
<key>Date Added</key><date>2010-12-
09T13:20:23Z</date>
<key>Bit Rate</key><integer>192</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Comments</key><string>Ninja Tune
Records</string>
<key>Play Count</key><integer>6</integer>
<key>Play Date</key><integer>3420631253</integer>
<key>Play Date UTC</key><date>2012-05-
23T15:20:53Z</date>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent
ID</key><string>30EA821C13A23AC3</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file://localhost/H:/iTunes/Music/Mr.%20S
cruff/Ninja%20Tuna/01%20Kalimba.mp3</string>
<key>File Folder Count</key><integer>5</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
<key>3988</key>
<dict>
<key>Track ID</key><integer>3988</integer>
<key>Name</key><string>Maid with the Flaxen
Hair</string>
<key>Artist</key><string>Richard Stoltzman/Slovak
Radio Symphony Orchestra</string>
<key>Album Artist</key><string>Richard
Stoltzman</string>
<key>Composer</key><string>Claude Debussy</string>
<key>Album</key><string>Fine Music, Vol. 1</string>
<key>Genre</key><string>Classical</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>4113874</integer>
<key>Total Time</key><integer>169691</integer>
<key>Track Number</key><integer>2</integer>
<key>Year</key><integer>2008</integer>
<key>Date Modified</key><date>2010-11-
06T14:12:52Z</date>
<key>Date Added</key><date>2010-12-
09T13:20:23Z</date>
<key>Bit Rate</key><integer>192</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Comments</key><string>Navona Records</string>
<key>Play Count</key><integer>3</integer>
<key>Play Date</key><integer>3420160303</integer>
<key>Play Date UTC</key><date>2012-05-
18T04:31:43Z</date>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent
ID</key><string>118923E2694E42B5</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file://localhost/H:/iTunes/Music/Richard
%20Stoltzman/Fine%20Music,%20Vol.%201/02%20Maid%20with%20the%20Flaxen%20Hai
r.mp3</string>
<key>File Folder Count</key><integer>5</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
<plist version="1.0">
<dict>
<key>Major Version</key><integer>1</integer>
<key>Minor Version</key><integer>1</integer>
<key>Date</key><date>2013-02-06T12:58:03Z</date>
<key>Application Version</key><string>11.0.1</string>
<key>Features</key><integer>5</integer>
<key>Show Content Ratings</key><true>
<key>Music Folder</key><string>file://localhost/H:/iTunes/</string>
<key>Library Persistent ID</key><string>49FB3F7105D2F947</string>
<key>Tracks</key>
<dict>
<key>3986</key>
<dict>
<key>Track ID</key><integer>3986</integer>
<key>Name</key><string>Kalimba</string>
<key>Artist</key><string>Mr. Scruff</string>
<key>Album Artist</key><string>Mr. Scruff</string>
<key>Composer</key><string>A. Carthy and A.
Kingslow</string>
<key>Album</key><string>Ninja Tuna</string>
<key>Genre</key><string>Electronic</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>8414449</integer>
<key>Total Time</key><integer>348055</integer>
<key>Track Number</key><integer>1</integer>
<key>Year</key><integer>2008</integer>
<key>Date Modified</key><date>2009-07-
14T06:32:31Z</date>
<key>Date Added</key><date>2010-12-
09T13:20:23Z</date>
<key>Bit Rate</key><integer>192</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Comments</key><string>Ninja Tune
Records</string>
<key>Play Count</key><integer>6</integer>
<key>Play Date</key><integer>3420631253</integer>
<key>Play Date UTC</key><date>2012-05-
23T15:20:53Z</date>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent
ID</key><string>30EA821C13A23AC3</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file://localhost/H:/iTunes/Music/Mr.%20S
cruff/Ninja%20Tuna/01%20Kalimba.mp3</string>
<key>File Folder Count</key><integer>5</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
<key>3988</key>
<dict>
<key>Track ID</key><integer>3988</integer>
<key>Name</key><string>Maid with the Flaxen
Hair</string>
<key>Artist</key><string>Richard Stoltzman/Slovak
Radio Symphony Orchestra</string>
<key>Album Artist</key><string>Richard
Stoltzman</string>
<key>Composer</key><string>Claude Debussy</string>
<key>Album</key><string>Fine Music, Vol. 1</string>
<key>Genre</key><string>Classical</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>4113874</integer>
<key>Total Time</key><integer>169691</integer>
<key>Track Number</key><integer>2</integer>
<key>Year</key><integer>2008</integer>
<key>Date Modified</key><date>2010-11-
06T14:12:52Z</date>
<key>Date Added</key><date>2010-12-
09T13:20:23Z</date>
<key>Bit Rate</key><integer>192</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Comments</key><string>Navona Records</string>
<key>Play Count</key><integer>3</integer>
<key>Play Date</key><integer>3420160303</integer>
<key>Play Date UTC</key><date>2012-05-
18T04:31:43Z</date>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent
ID</key><string>118923E2694E42B5</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file://localhost/H:/iTunes/Music/Richard
%20Stoltzman/Fine%20Music,%20Vol.%201/02%20Maid%20with%20the%20Flaxen%20Hai
r.mp3</string>
<key>File Folder Count</key><integer>5</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
</dict>
</true></dict>
</plist></dict></true></dict></plist>
And here’s the slightly better PHP parser.
global $stack , $depth;
$stack=array();
$depth=0;
function tagStart($p, $name, array $attribs)
{
global $stack , $depth;
$stack[$depth]=$name;
++$depth;
if (($depth==4)&&($name=="DICT"))
{
echo"NEW<hr>\n";
}
if (($depth==5)&&($name=="KEY"))
{xml_set_character_data_handler($p,"keyCharHandler");echo$name;}
elseif (($depth==5)&&($name!="KEY"))
{xml_set_character_data_handler($p,"valueCharHandler");echo$name;}
else
{xml_set_character_data_handler($p,"");}
}
function keyCharHandler($p,$data)
{echo"key";var_dump($data);echo"<br>\n";}
function valueCharHandler($p,$data)
{echo"value";var_dump($data);echo"<br>\n";xml_set_character_data_handler($p
,"");}
function tagEnd($p, $name)
{
global $stack , $depth;
$stack[$depth]=null;
--$depth;
}
$data=file_get_contents("library.xml");
$p=xml_parser_create();
xml_set_element_handler($p,'tagStart','tagEnd');
xml_set_character_data_handler($p,"");
xml_parse($p,$data);
And here are the results:
NEW
KEYkeystring(8) "Track ID"
INTEGERvaluestring(4) "3986"
KEYkeystring(4) "Name"
STRINGvaluestring(7) "Kalimba"
KEYkeystring(6) "Artist"
STRINGvaluestring(10) "Mr. Scruff"
KEYkeystring(12) "Album Artist"
STRINGvaluestring(10) "Mr. Scruff"
KEYkeystring(8) "Composer"
STRINGvaluestring(25) "A. Carthy and A. Kingslow"
KEYkeystring(5) "Album"
STRINGvaluestring(10) "Ninja Tuna"
KEYkeystring(5) "Genre"
STRINGvaluestring(10) "Electronic"
KEYkeystring(4) "Kind"
STRINGvaluestring(15) "MPEG audio file"
KEYkeystring(4) "Size"
INTEGERvaluestring(7) "8414449"
KEYkeystring(10) "Total Time"
INTEGERvaluestring(6) "348055"
KEYkeystring(12) "Track Number"
INTEGERvaluestring(1) "1"
KEYkeystring(4) "Year"
INTEGERvaluestring(4) "2008"
KEYkeystring(13) "Date Modified"
DATEvaluestring(20) "2009-07-14T06:32:31Z"
KEYkeystring(10) "Date Added"
DATEvaluestring(20) "2010-12-09T13:20:23Z"
KEYkeystring(8) "Bit Rate"
INTEGERvaluestring(3) "192"
KEYkeystring(11) "Sample Rate"
INTEGERvaluestring(5) "44100"
KEYkeystring(8) "Comments"
STRINGvaluestring(18) "Ninja Tune Records"
KEYkeystring(10) "Play Count"
INTEGERvaluestring(1) "6"
KEYkeystring(9) "Play Date"
INTEGERvaluestring(10) "3420631253"
KEYkeystring(13) "Play Date UTC"
DATEvaluestring(20) "2012-05-23T15:20:53Z"
KEYkeystring(13) "Artwork Count"
INTEGERvaluestring(1) "1"
KEYkeystring(13) "Persistent ID"
STRINGvaluestring(16) "30EA821C13A23AC3"
KEYkeystring(10) "Track Type"
STRINGvaluestring(4) "File"
KEYkeystring(8) "Location"
STRINGvaluestring(75)
"file://localhost/H:/iTunes/Music/Mr.%20Scruff/Ninja%20Tuna/01%20Kalimba.mp3"
KEYkeystring(17) "File Folder Count"
INTEGERvaluestring(1) "5"
KEYkeystring(20) "Library Folder Count"
INTEGERvaluestring(1) "1"
NEW
KEYkeystring(8) "Track ID"
INTEGERvaluestring(4) "3988"
KEYkeystring(4) "Name"
STRINGvaluestring(25) "Maid with the Flaxen Hair"
KEYkeystring(6) "Artist"
STRINGvaluestring(49) "Richard Stoltzman/Slovak Radio Symphony Orchestra"
KEYkeystring(12) "Album Artist"
STRINGvaluestring(17) "Richard Stoltzman"
KEYkeystring(8) "Composer"
STRINGvaluestring(14) "Claude Debussy"
KEYkeystring(5) "Album"
STRINGvaluestring(18) "Fine Music, Vol. 1"
KEYkeystring(5) "Genre"
STRINGvaluestring(9) "Classical"
KEYkeystring(4) "Kind"
STRINGvaluestring(15) "MPEG audio file"
KEYkeystring(4) "Size"
INTEGERvaluestring(7) "4113874"
KEYkeystring(10) "Total Time"
INTEGERvaluestring(6) "169691"
KEYkeystring(12) "Track Number"
INTEGERvaluestring(1) "2"
KEYkeystring(4) "Year"
INTEGERvaluestring(4) "2008"
KEYkeystring(13) "Date Modified"
DATEvaluestring(20) "2010-11-06T14:12:52Z"
KEYkeystring(10) "Date Added"
DATEvaluestring(20) "2010-12-09T13:20:23Z"
KEYkeystring(8) "Bit Rate"
INTEGERvaluestring(3) "192"
KEYkeystring(11) "Sample Rate"
INTEGERvaluestring(5) "44100"
KEYkeystring(8) "Comments"
STRINGvaluestring(14) "Navona Records"
KEYkeystring(10) "Play Count"
INTEGERvaluestring(1) "3"
KEYkeystring(9) "Play Date"
INTEGERvaluestring(10) "3420160303"
KEYkeystring(13) "Play Date UTC"
DATEvaluestring(20) "2012-05-18T04:31:43Z"
KEYkeystring(13) "Artwork Count"
INTEGERvaluestring(1) "1"
KEYkeystring(13) "Persistent ID"
STRINGvaluestring(16) "118923E2694E42B5"
KEYkeystring(10) "Track Type"
STRINGvaluestring(4) "File"
KEYkeystring(8) "Location"
STRINGvaluestring(120)
"file://localhost/H:/iTunes/Music/Richard%20Stoltzman/Fine%20Music,%20Vol.%201/02%
20Maid%20with%20the%20Flaxen%20Hair.mp3"
KEYkeystring(17) "File Folder Count"
INTEGERvaluestring(1) "5"
KEYkeystring(20) "Library Folder Count"
INTEGERvaluestring(1) "1"
SparQL
I wanted to find a way to get the album review off Wikipedia.org that you so often see in the shops.
So I googled Wikipedia APIs. Not surprisingly a page came up detailing the exact meaning of an API.
But then I found a page called dbpedia.
The service links a number of public databases on the web together and I was just blown away by
how powerful the SPARQL query language really is.
By trial and error I was able to construct a simple query to the database.
As you can see it returns results in a number of different languages and also listings for films by the
same name.
This can be fixed by clicking the link to the record you want and further exploring it’s properties you
can then formulate a new query with a bit of tweaking.
Even got an abstract from the encyclopedia.
But what is interesting is I actually have a Cartesian product between the French and English
Language comments and abstracts.
I tried replacing the full stop (period) with a semicolon, a comma and some ampersands but got an
error every time. I couldn’t get the language to be always English either.
I would really like a book on Sparql.