/*****************************************************************************
 * Customized MediaTomb import script.
 * Copyright (C) 2009 Mike Green <mikey@badpenguins.com>
 * Released under the terms of the GNU General Public License v2
 *
 * Features:
 * Maintains top level music chains for Classical, Holiday and Country Music.
 * All other music goes into standard Audio top level chain.
 *
 * Classical and Holiday music get parsed for orchestras, choirs, and
 * composers.  Each gets a By Orchestra, By Choir, By Composer etc.. section.
 *
 * Categories of shared items will not cross, for example photos found during
 * the import of music will not be populated into the Pictures chain.
 *
 * Rock gets 4 new entries under the Genres chain - 50's Rock, 60's Rock,
 * 70's Rock, and 80's Rock.
 *
 * Images are added in chains that mirror the filesystem directory structure.
 * With the exception that the top level of each 1-off image category gets an
 * "All XXXX Photos" category.  For example /pics/pets/dogs/jessi.jpg will
 * get an entry in /pics/pets/All Pets and /pics/pets/dogs.
 *
 * If video imports are in /video/Series, it will create chains for
 * /video/Series/Seaason.  If the video is not in series, it will import into
 * chains that mirror the filesystem directory structure.
 *
 * For composers to be parsed, add the following to config.xml:
 * <library-options>
 *   <id3>
 *      <auxdata>
 *         <add-data tag="TCOM">
 *      </auxdata>
 *   </id3>
 * </library-options>
 *****************************************************************************/
/**
 * Define the home filesystem location for objects, so images found in
 * audio subdirectories don't get added as image objects etc...
 */
var homeAudio = '/storage03/media/music';
var homeVideo = '/storage03/media/video';
var homeImage = '/storage03/media/pictures';

/**
 * Custom addAudio that will split out classical music into its
 * own top level and create sub levels for composers, orchestras,
 * and choirs.
 */
function addAudioCustom(obj) {
	// Do not add audio objects that are not within audioHome
	if (obj.location.substr(0,homeAudio.length) !== homeAudio) {
		print('Skipping: ' + obj.location);
		return;
		}

	var album         = getAudioAlbum(obj);
	var artistTagName = 'Artists';
	var artist        = getAudioArtist(obj);
	var date          = obj.meta[M_DATE];
	var genre         = getAudioGenre(obj);
	var titleShort    = getAudioTitle(obj);
	var pattern;
	var match;
	var year;
	// If the album title has christmas in it, send it
	// to the holiday genre ;)
	pattern = /(.*)christmas(.*)/i;
	match = pattern.exec(album);
	if (match) { genre = 'Holiday'; }
	//
	// Process according to genre
	//
	switch(genre) {
		// Put classical music in it's own top level, split out special
		// categories for composer, orchestra, and choir.
		case 'Classical':
			// Add to standard top level for classical music
			addAudio(obj,'Classical Music','Artists',artist);
			// Can we pull out a known composer?
			var composer = parseComposer(obj.aux['TCOM']);
			// Try pulling composer from the title...
			if (!composer) {
				composer = parseComposer(titleShort);
				}
			if (composer) {
				addAudioByCategory(obj,'Classical Music','Composer',composer);
				}
			// Is the artist an orchestra?
			pattern = /(.*)orchestra(.*)/i
			match = pattern.exec(artist);
			if (match) {
				addAudioByCategory(obj,'Classical Music','Orchestra',artist);
				}
			// Is the artist a choir?
			pattern = /(.*)choir(.*)/i
			match = pattern.exec(artist);
			if (match) {
				addAudioByCategory(obj,'Classical Music','Choir',artist);
				}
		break;
		// Country music goes to its own top level
		case 'Country':
			// Add to standard top level for classical music
			addAudio(obj,'Country Music','Artists',artist);
		break;
		//
		// Put holiday music in it's own top level, split out special
		// categories for composer, orchestra, and choir.
		//
		case 'Holiday':
			addAudio(obj,'Holiday Music','Artists',artist);
			// Can we pull out a known composer?
			var composer = parseComposer(obj.aux['TCOM']);
			// Try pulling composer from the title...
			if (!composer) {
				composer = parseComposer(titleShort);
				}
			if (composer) {
				addAudioByCategory(obj,'Holiday Music','Composer',composer);
				}
			// Is the artist an orchestra?
			pattern = /(.*)orchestra(.*)/i
			match = pattern.exec(artist);
			if (match) {
				addAudioByCategory(obj,'Holiday Music','Orchestra',artist);
				}
			// Is the artist a choir?
			pattern = /(.*)choir(.*)/i
			match = pattern.exec(artist);
			if (match) {
				addAudioByCategory(obj,'Holiday Music','Choir',artist);
				}
		break;
		//
		// Create date genre categories for Rock (70's Rock, 80's Rock, etc..)
		//
		case 'Rock':
			addAudio(obj,'Audio','Artists',artist);
			if (date) { year = getYear(date); }
			if (year) {
				if (year > 1949 && year < 1960) {
					addAudioDecade(obj,"50's Rock");
					} else if (year > 1959 && year < 1970) {
					addAudioDecade(obj,"60's Rock");
					} else if (year > 1969 && year < 1980) {
					addAudioDecade(obj,"70's Rock");
					} else if (year > 1979 && year < 1990) {
					addAudioDecade(obj,"80's Rock");
					}
				}
		break;
		default:
			addAudio(obj,'Audio','Artists',artist);
		break;
		}
	}

/**
 * Mostly standard addAudio(), will not import unknown anything.
 */
function addAudio(obj,topLevel,artistTag,artist) {
	obj.meta[M_ARTIST] = artist;
	var album          = getAudioAlbum(obj);
	var artist         = getAudioArtist(obj);
	var genre          = getAudioGenre(obj);
	var titleLong      = getAudioTitleLong(obj);
    var titleShort     = getAudioTitle(obj);
	var track          = getAudioTrack(obj);
	if (!obj.meta[M_DESCRIPTION]) {
		obj.meta[M_DESCRIPTION] = getAudioDescription(obj);
		}

    // [Top Level Name]/All [Top Level Name]
	obj.title = titleLong;
	var chain = new Array(topLevel, 'All ' + topLevel);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

    // [Top Level Name]/[Artists Tag]/[Artist Name]/All - Short Name
	obj.title = titleShort;
	chain = new Array(topLevel, artistTag, artist, 'All - Short Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// [Top Level Name/[Artists Tag]/[Artist Name]/All - Full Name
	obj.title = titleLong;
	chain = new Array(topLevel, artistTag, artist, 'All - Full Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// [Top Level Name]/[Artists Tag]/[Artist Name]/[Album Name]
	obj.title = track + titleShort;
	chain = new Array(topLevel, artistTag, artist, album);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);

	// [Top Level Name]/Albums/[Album Name]
	obj.title = track + titleShort;
	chain = new Array(topLevel, 'Albums', album);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);

	// Don't want to add into genre unless top level name is Audio
	if (topLevel == 'Audio') {
		// [Top Level Name]/Genres/[Genre Name]
		obj.title = titleLong;
		chain = new Array(topLevel, 'Genres', genre);
		addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_GENRE);
		}
	}

/**
 * Add a By Composer chain
 */
function addAudioByCategory(obj,topLevel,category,artist) {
	obj.meta[M_ARTIST] = artist;
	var album          = getAudioAlbum(obj);
	var artist         = getAudioArtist(obj);
	var titleLong      = getAudioTitleLong(obj);
    var titleShort     = getAudioTitle(obj);
	var track          = getAudioTrack(obj);
	if (!obj.meta[M_DESCRIPTION]) {
		obj.meta[M_DESCRIPTION] = getAudioDescription(obj);
		}

	// [Top Level Name]/By [Category Name]/[Artist Name]/All - Short Name
	obj.title = titleShort;
	chain = new Array(topLevel, 'By ' + category, artist, 'All - Short Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// [Top Level Name]/By [Category Name]/[Artist Name]/All - Full Name
	obj.title = titleLong;
	chain = new Array(topLevel, 'By ' + category, artist, 'All - Full Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// [Top Level Name]/By [Category Name]/[Artist Name]/Albums/[Album Name]
	chain = new Array(topLevel, 'By ' + category, artist, 'Albums', album);
	obj.title = track + titleShort;
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
	}

/**
 * Create a chain for rock decades (70's Rock, 80's Rock, etc...)
 */
function addAudioDecade(obj,category) {
	var album      = getAudioAlbum(obj);
	var artist     = getAudioArtist(obj);
	var genre      = getAudioGenre(obj);
	var titleLong  = getAudioTitleLong(obj);
    var titleShort = getAudioTitle(obj);
	var track      = getAudioTrack(obj);
	if (!obj.meta[M_DESCRIPTION]) {
		obj.meta[M_DESCRIPTION] = getAudioDescription(obj);
		}

	// Audio/Genres/[Category Name]/All - Short Name
	obj.title = titleShort;
	chain = new Array('Audio','Genres',category,'All - Short Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// Audio/Genres/[Category Name]/All - Full Name
	obj.title = titleLong;
	chain = new Array('Audio','Genres',category,'All - Full Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// Audio/Genres/[Category Name]/Artists/[Artist Name]/All - Short Name
	obj.title = titleShort;
	chain = new Array('Audio','Genres',category,'Artists',artist,'All - Short Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// Audio/Genres/[Category Name]/Artists/[Artist Name]/All - Full Name
	obj.title = titleLong;
	chain = new Array('Audio','Genres',category,'Artists',artist,'All - Full Name');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC);

	// Audio/Genres/[Category Name]/Albums/[Album Name]
	obj.title = track + titleShort;
	chain = new Array('Audio','Genres',category,'Albums',album);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);

	// Audio/Genres/[Category Name]/Artists/[Artist Name]/[Album Name]
	obj.title = track + titleShort;
	chain = new Array('Audio','Genres',category,'Artists',artist,album);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
	}

/**
 * Add image objects
 */
function addImage(obj) {
	if (obj.location.substr(0,homeImage.length) !== homeImage) {
		return;
		}
	//
	// Find out what the top level category is.
	//
	var junk = homeImage.split('/');
	var topLevelNum = junk.length;
	junk = obj.location.split('/');
	var categoryName = junk[topLevelNum];
	addImageByCategory(obj,obj.location.substr(homeImage.length + 1));
	}

/**
 * Add image object by categorizing on filesystem path.
 */
function addImageByCategory(obj,strippedPath) {
	var paths = strippedPath.split('/');
	var chain = new Array('Photos', 'All Photos');
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
	chain = new Array('Photos');
	for(var i=0; i < (paths.length - 1); i++) {
		chain.push(paths[i]);
		}
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
	var topCategory = paths[0];
	chain = new Array('Photos',topCategory,'All ' + topCategory);
	addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
	}

/**
 * Add video objects
 */
function addVideo(obj) {
	if (obj.location.substr(0,homeVideo.length) !== homeVideo) {
		return;
		}
	// Ditch homeVideo from the path.
	var junk = homeVideo.split('/');
	var offset = junk.length;
	var paths = obj.location.split('/');
	var categoryOffset = junk.length;
	var seriesOffset   = categoryOffset + 1;
	var seasonOffset   = seriesOffset + 1;
	var episodeOffset  = seasonOffset + 1;
	var chain = new Array('Video');

	if (paths[offset] == 'Series') {
		var series = paths[seriesOffset];
		var season = paths[seasonOffset];
		var episode = paths[episodeOffset];
		if (!series || !season || !episode) {
			print('Error parsing series: ' + obj.location);
			return;
			}
		chain.push('Series');
		chain.push(series);
		chain.push(season);
		} else {
		// Add an All  - [Top Category]
		var all = 'All - ' + paths[offset];
		chain.push(paths[offset]);
		chain.push(all);
		addCdsObject(obj,createContainerChain(chain));
		// remove it back off ;)
		junk = chain.pop();
		junk = chain.pop();
		junk = paths.pop();
		for(var j=offset; j < paths.length; j++) {
			chain.push(paths[j]);
			}
		}
	addCdsObject(obj,createContainerChain(chain));
	}

/**
 * Compose standard artist name
 */
function getAudioArtist(obj) {
	if (!obj) { return 'Unknown Artist'; }
	return (obj.meta[M_ARTIST]) ? obj.meta[M_ARTIST] : 'Unknown Artist';
	}

/**
 * Compose standard album name.
 */
function getAudioAlbum(obj) {
	if (!obj) { return 'Unknown Album'; }
	return (obj.meta[M_ALBUM]) ? obj.meta[M_ALBUM] : 'Unknown Album';
	}

/**
 * Compose fallback description for audio object
 */
function getAudioDescription(obj) {
	return getAudioArtist(obj) + ', '
	+ getAudioAlbum(obj) + ', '
	+ getAudioTitle(obj) + ', '
	+ getAudioGenre(obj);
	}

/**
 * Compose standard genre name
 */
function getAudioGenre(obj) {
	if (!obj) { return 'Unknown Genre'; }
	return (obj.meta[M_GENRE]) ? obj.meta[M_GENRE] : 'Unknown Genre';
	}

/**
 * Compose standard short audio file track name.
 */
function getAudioTitle(obj) {
	if (!obj) { return 'Unknown Title'; }
	return (obj.meta[M_TITLE]) ? obj.meta[M_TITLE] : obj.title;
	}

/**
 * Compose standard long audio file track name.
 */
function getAudioTitleLong(obj) {
	return getAudioArtist(obj) + ' - ' + getAudioAlbum(obj) + ' - ' +
	getAudioTitle(obj);
	}

/**
 * Compose standard track number string
 */
function getAudioTrack(obj) {
	var track = obj.meta[M_TRACKNUMBER];
	if (!track) { return ''; }
	if (track.length == 1) {
		track = '0' + track;
		}
	return track + ' - ';
	}

/**
 * Attempt to parse out popular composers from the provided string.
 */
function parseComposer(c) {
	var pattern;
	var composer;
	var junk;
	var match;
	var patterns = [
	new Array(/(.*)bach(.*)/i,'Bach, Johann Sebastian'),
	new Array(/(.*)beethoven(.*)/i,'Beethoven, Ludwig Van'),
	new Array(/(.*)brahms(.*)/i,'Brahms, Johannes'),
	new Array(/(.*)britten(.*)/i,'Britten, Benjamin'),
	new Array(/(.*)chopin(.*)/i,'Chopin, Frederic'),
	new Array(/(.*)copland(.*)/i,'Copland, Aaron'),
	new Array(/(.*)debussy(.*)/i,'Debussy, Claude'),
	new Array(/(.*)grieg(.*)/i,'Grieg, Edvard'),
	new Array(/(.*)gruber(.*)/i,'Gruber, Franz Xaver'),
	new Array(/(.*)handel(.*)/i,'Handel, George Frideric'),
	new Array(/(.*)liszt(.*)/i,'Liszt, Franz'),
	new Array(/(.*)mendelssohn(.*)/i,'Mendelssohn, Felix'),
	new Array(/(.*)mozart(.*)/i,'Mozart, Wolfgang Amadeus'),
	new Array(/(.*)respighi(.*)/i,'Respighi, Ottornino'),
	new Array(/(.*)schubert(.*)/i,'Schubert, Franz'),
	new Array(/(.*)stravinsky(.*)/i,'Stravinsky, Igor'),
	new Array(/(.*)tchaikovsky(.*)/i,'Tchaikovsky, Peter Ilyich'),
	new Array(/(.*)vaughan williams(.*)/i,'Williams, Ralph Vaughan'),
	new Array(/(.*)vivaldi(.*)/i,'Vivaldi, Antonio') ];
	for(var i=0;i<patterns.length;i++) {
		pattern = patterns[i][0];
		composer = patterns[i][1];
		match = pattern.exec(c);
		if (match) {
			return composer;
			}
		}
	return;
	}

/*****************************************************************************
 * Main flow starts here
 *****************************************************************************/
if (getPlaylistType(orig.mimetype) == '') {
	var arr = orig.mimetype.split('/');
	var mime = arr[0];
	// var obj = copyObject(orig);
	var obj = orig;
	obj.refID = orig.id;

	if (mime == 'audio') {
		addAudioCustom(obj);
		}

	if (mime == 'video') {
		addVideo(obj);
		}

	if (mime == 'image') {
		addImage(obj);
		}

	if (orig.mimetype == 'application/ogg') {
		if (orig.theora == 1) {
			addVideo(obj);
			} else {
			addAudioCustom(obj);
			}
		}
	}
