iTunes Export to RSS (class::ItunesExport)
I've recently been downloading lots of music (legally of course *cough*) and thought it would be a fun idea to display this in my "mini-feed" on facebook so my friends could see what new music I had added to my library. After a bit of thinking, I worked out that I could probably run a bit of AppleScript to export my library and FTP it to my server, and then I'd just need a bit of PHP to work out what was new, get the details and album art, and then build it into an RSS feed which could be imported into Facebook. Unfortunately, I'm only a beginner at AppleScript and this was a little out of my league, but I have completed the PHP class to do such a thing and you can find it below along with the RSS feed that I'm currently importing into my facebook account:
RSS Feed of the latest music added to my iTunes library
The code
This code is released under Creative Commons GNU GPL and so may be freely used and edited. Just copy and paste it from below.
<?php
/**
*
*
* [0] => Name
* [1] => Artist
* [2] => Composer
* [3] => Album
* [4] => Grouping
* [5] => Genre
* [6] => Size
* [7] => Time
* [8] => Disc Number
* [9] => Disc Count
* [10] => Track Number
* [11] => Track Count
* [12] => Year
* [13] => Date Modified
* [14] => Date Added
* [15] => Bit Rate
* [16] => Sample Rate
* [17] => Volume Adjustment
* [18] => Kind
* [19] => Equalizer
* [20] => Comments
* [21] => Play Count
* [22] => Last Played
* [23] => Skip Count
* [24] => Last Skipped
* [25] => My Rating
* [26] => Location
*
*/
class ItunesExport
{
// define constants
const export_file = 'music.txt';
const line_endings = "\n";
const export_cache = 'export.cache';
const amazon_access_key_id = '0SWDKFA2SDTA1YFZBYG2';
const amazon_affiliate_id = 'bendodsoncom-21';
const amazon_url = 'http://ecs.amazonaws.co.uk/onca/xml';
const rss_template = 'rss.php';
const rss_feed = 'rss.xml';
const rss_title = 'Ben Dodson\'s iTunes Updates';
const rss_link = 'http://labs.bendodson.com/itunes-export/rss.xml';
const rss_description = 'The latest music added to Ben Dodson\'s iTunes Library';
const rss_ttl = '60';
// define variables
protected $tracks = array();
public $albums = array();
public $last_export = '';
// set the last_export variable with constructor
function __construct()
{
$this->get_last_export_date();
}
protected function get_last_export_date()
{
$this->last_export = @file_get_contents(ItunesExport::export_cache);
}
public function update_last_export_date()
{
$timestamp = date('U');
@file_put_contents(ItunesExport::export_cache,$timestamp);
$this->last_export = $timestamp;
}
public function export($update=true)
{
// check if the itunes export actually exists.
if (!file_exists(ItunesExport::export_file)) {
echo 'iTunes export file not found at '.ItunesExport::export_file.'... aborting.';
exit;
}
// get contents of export file into string
$lines = explode(ItunesExport::line_endings,file_get_contents(ItunesExport::export_file));
// remove the first line which contains column headers
$headers = array_shift($lines);
// loop through each line and get relevant columns only if all are present and if after last export
$i = 0;
foreach ($lines as $line) {
// break each line into it's columns
$columns = explode("\t",$line);
// assign variables to relevant columns
$title = $columns[0];
$artist = $columns[1];
$album = $columns[3];
$date_added = $columns[14];
$location = $columns[26];
// if all variables are present (so if ID3 tags filled out correctly)
if ($title && $artist && $album && $date_added) {
// get timestamp (have to rearrange the iTunes date_added format a little...)
$timestamp = date('U',strtotime(str_replace('/','-',substr($date_added,0,10)).' '.substr($date_added,11).':00'));
if ($timestamp > $this->last_export) {
$this->tracks[$i]['title'] = $title;
$this->tracks[$i]['artist'] = $artist;
$this->tracks[$i]['album'] = $album;
$this->tracks[$i]['timestamp'] = $timestamp;
$this->tracks[$i]['location'] = $location;
$i++;
}
}
}
// if there are no tracks since last export, then stop.
if (!$this->tracks) {
echo 'No new tracks added since last export... aborting.';
exit;
}
// sort the tracks
$this->sort_tracks();
// group tracks into albums / singles
$this->group_tracks();
// update last export date
if ($update) {
$this->update_last_export_date();
}
}
protected function sort_tracks()
{
usort($this->tracks, array('ItunesExport','timestamp_compare'));
$this->tracks = array_reverse($this->tracks);
while(count($this->tracks) > 75) {
array_pop($this->tracks);
}
}
static function timestamp_compare($a,$b)
{
return strcmp($a['timestamp'], $b['timestamp']);
}
protected function group_tracks()
{
$i = 0;
$temp = '';
foreach ($this->tracks as $track) {
if (strpos($track['location'], 'Various Artists') !== false) {
$track['artist'] = 'Various Artists';
}
$current = $track['artist'].'-'.$track['album'];
// if we've moved to a new album
if ($current != $temp) {
// increase counter and set temp variable to current album
$temp = $current;
$i++;
// set album, artist, and a timestamp
$this->albums[$i]['album'] = $track['album'];
$this->albums[$i]['artist'] = $track['artist'];
$this->albums[$i]['timestamp'] = $track['timestamp'];
// get amazon data
$this->get_amazon_data($i);
}
$this->albums[$i]['tracks'][] = $track['title'];
}
// set type as 'album' or 'single'
$this->add_album_type();
}
protected function get_amazon_data($i)
{
// get amazon link
$request = ItunesExport::amazon_url;
$request .= '?Service=AWSECommerceService';
$request .= '&AssociateTag='.ItunesExport::amazon_affiliate_id;
$request .= '&AWSAccessKeyId='.ItunesExport::amazon_access_key_id;
$request .= '&Operation=ItemSearch';
$request .= '&Artist='.urlencode($this->albums[$i]['artist']);
$request .= '&Title='.urlencode($this->albums[$i]['album']);
$request .= '&SearchIndex=Music';
$request .= '&ResponseGroup=Images,ItemAttributes';
$response = file_get_contents($request);
if ($response) {
$xml = simplexml_load_string($response);
if ($xml) {
$amazon = (array) $xml->Items->Item;
}
}
if ($amazon) {
// get images
$this->albums[$i]['small_image'] = (string) $amazon['SmallImage']->URL;
$this->albums[$i]['medium_image'] = (string) $amazon['MediumImage']->URL;
$this->albums[$i]['large_image'] = (string) $amazon['LargeImage']->URL;
$this->albums[$i]['url'] = $amazon['DetailPageURL'];
}
$this->albums[$i]['album_link'] = ($this->albums[$i]['url']) ? '<a href="'.$this->albums[$i]['url'].'">'.$this->albums[$i]['album'].'</a>' : $this->albums[$i]['album'];
}
protected function add_album_type()
{
for ($i=1; $i <= count($this->albums); $i++) {
$this->albums[$i]['type'] = (count($this->albums[$i]['tracks']) > 1) ? 'album' : 'single';
}
}
public function generate_rss()
{
global $feed,$items;
$feed['title'] = ItunesExport::rss_title;
$feed['link'] = ItunesExport::rss_link;
$feed['description'] = ItunesExport::rss_description;
$feed['ttl'] = ItunesExport::rss_ttl;
$i = 0;
foreach ($this->albums as $album) {
$items[$i]['pubdate'] = date('r',$album['timestamp']);
$items[$i]['link'] = $album['url'];
if ($album['type'] == 'album') {
$items[$i]['title'] = $album['album'].' by '.$album['artist'];
$items[$i]['description'] = 'Added the album '.$album['album'].' by <em>'.$album['artist'].'</em> which contains '.count($album['tracks']).' tracks';
} else {
$items[$i]['title'] = $album['tracks'][0].' by '.$album['artist'];
$items[$i]['description'] = 'Added the song '.$album['tracks'][0].' by <em>'.$album['artist'].'</em> from the album '.$album['album'];
}
if ($album['small_image']) {
$items[$i]['description'] = '<img src="'.$album['small_image'].'" align="left" />'.$items[$i]['description'];
}
$i++;
}
$compiled = $this->compile_template('feed','items');
file_put_contents(ItunesExport::rss_feed,$compiled);
}
protected function compile_template()
{
$arguments = func_get_args();
foreach ($arguments as $var) {
global $$var;
}
ob_start();
require ItunesExport::rss_template;
return ob_get_clean();
}
}
?>
It's not 100% ready yet and the class still needs tidying up and commenting made throughout, but for now it works which is the main thing. It's currently running on a CRON job that checks every hour to see if the music.txt file has been updated (and if it has it generates a new RSS feed). The main problem at the moment is that I have to do a manual export but I'm hoping to remove the need for that once I've mastered some AppleScript!
Future Improvements: Full documentation of the above code and a bit of a code cleanup. My iTunes library is a bit of a mess at the moment but I'm currently going through and correcting the ID3 tags, etc. Once that is done, I'll be making a page here which shows all of the albums in my library (along with some funky jQuery magic to make it look good) sortable via different criteria.
- Uses a plain text iTunes export
- Gets album artwork from Amazon
- Groups albums together
- Compatible with iTunes 8