file = $_GET['file'];
else
$this->file = $file;
$this->cid = -1;
$this->trackid = false;
$this->parsedHTML = "";
$this->tunes = array();
$this->curTune = array();
$this->sortColumn = array("Artist");
$this->sortOrder = 0;
$this->totaltime = 0;
$this->totalcount = 0;
$this->totalsize = 0;
$this->mysqlEnabled = 0;
$this->enableSorting = 0;
$this->allCols = array(
'Track ID', 'Name', 'Artist', 'Composer', 'Album', 'Genre', 'Kind', 'Size', 'Total Time', 'Disc Number', 'Disc Count', 'Track Number', 'Track Count', 'Year', 'BPM', 'Date Modified', 'Date Added', 'Bit Rate', 'Sample Rate', 'Equalizer', 'Comments', 'Play Count', 'Play Date', 'Play Date UTC', 'Normalization', 'Rating', 'File Type', 'File Creator', 'Location', 'File Folder Count', 'Library Folder Count'
);
}
// Set settings
function setSettings(&$settings)
{
// Sets columns (to be printed)
$this->cols = $settings['columns'];
// Opens connection to MySQL database and creates a table named "phptunest_tunes" if needed
// All old rows are removed from the table
if ($settings['mysqlEnabled'] == 1)
{
$this->mysqlEnabled = 1;
$this->mysqlHost = $settings['mysqlHost'];
$this->mysqlUser = $settings['mysqlUser'];
$this->mysqlPasswd = $settings['mysqlPassword'];
$this->mysqlDB = $settings['mysqlDB'];
// Create table if needed
$quer = 'CREATE TABLE IF NOT EXISTS phptunest_tunes ( trackID INT NOT NULL, name TEXT, artist TEXT, composer TEXT, album TEXT, genre VARCHAR(255), kind VARCHAR(128), size BIGINT, totalTime BIGINT, discNumber SMALLINT, discCount SMALLINT, trackNumber SMALLINT, trackCount SMALLINT, year SMALLINT, bpm SMALLINT, dateModified VARCHAR(64), dateAdded VARCHAR(64), bitRate SMALLINT, sampleRate mediumINT, equalizer VARCHAR(128), comments TEXT, playCount INT, playDate BIGINT, playDateUTC VARCHAR(64), normalization INT, rating INT, fileType BIGINT, fileCreator BIGINT, location TEXT, fileFolderCount INT, libFolderCount INT, primary key(trackID))';
$this->mysqlLink = mysql_connect($this->mysqlHost, $this->mysqlUser, $this->mysqlPasswd) or die("Could not connect to MySQL database");
mysql_select_db($this->mysqlDB) or die("Could not select database");
$result = mysql_query($quer);
if(!$result)
{
$this->mysqlEnabled=0;
print("
SQL query failed, falling back to non-mysql mode
");
}
$quer = "DELETE FROM phptunest_tunes";
$result = mysql_query($quer);
if(!$result)
{
$this->mysqlEnabled=0;
print("SQL query failed, falling back to non-mysql mode
");
}
}
else
$this->mysqlEnabled = 0;
// Sets columns which will be used in sorting (either mysql-sort or ram-sort)
if($settings['sortColumn'] != "")
{
if (strpos($settings['sortColumn'], ",") !== false)
{
$this->sortColumn = array();
$tempcols = explode(",", $settings['sortColumn']);
foreach($tempcols as $col)
array_push($this->sortColumn, $col);
}
else
$this->sortColumn[0] = $settings['sortColumn'];
}
// Sets sort order (ascending or descending)
if (($settings['order'] == 0) || ($settings['order'] == 1))
$this->sortOrder = $settings['order'];
else
$this->sortOrder = 0;
// Enables/disables sorting
$this->enableSorting = $settings['enableSorting'];
}
// Return XML file
function getXmlFile()
{
return $this->file;
}
// This method simply chooses parsing method:
// - ramParse() - doesn't require database, eats LOT of memory and is a bit slow
// - mysqlParse() - uses MySQL database. Works with default memory_limit (8M)
// and is more optimized
function parse()
{
if($this->mysqlEnabled)
$this->mysqlParse();
elseif($this->enableSorting)
$this->ramSortParse();
else
$this->ramParse();
}
// THE one :) Parsing XML data with MySQL backend
function mysqlParse()
{
$tune = array();
$trackid = false;
$tunestr = "";
$tempHTML = "";
// iTunes Music lists are UTF-8 -encoded
if (!$tuneXML = xml_parser_create("UTF-8"))
die("Couldn't create XML parse");
// Using implode(file()), because file_get_contents() requires
// PHP 4.3, so why bother :)
if (!$tunestr = implode("\n", file($this->file)))
die("Couldn't load XML data");
// We won't use xml_parse_into_struct() here.
// Let's use event-driven track-by-track parsing, with saving each
// track into MySQL database (this saves LOT of RAM).
xml_set_element_handler($tuneXML, array($this, "xmlStartElement"), array($this, "xmlEndElement"));
xml_set_character_data_handler($tuneXML, array($this, "xmlCharacterData"));
if (!xml_parse($tuneXML, $tunestr, true))
{
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($tuneXML)),
xml_get_current_line_number($tuneXML)));
}
if (!xml_parser_free($tuneXML))
die("Couldn't free XML parser");
// Data is now ready to be printed from MySQL table.
$colWidth = round(100/count($this->cols),0);
$tempHTML = '';
$tempHTML .= '';
foreach($this->cols as $col)
$tempHTML .= "| $col | \n";
$tempHTML .= "
\n\n";
$selectCols = $this->formatCols($this->cols);
if ($this->sortOrder == 0)
$quer = "SELECT $selectCols FROM phptunest_tunes ORDER BY " . $this->formatCols($this->sortColumn);
else
$quer = "SELECT $selectCols FROM phptunest_tunes ORDER BY " . $this->formatColsDesc($this->sortColumn);
if (!$result = mysql_query($quer))
die("Couldn't execute SQL query, closing script.
" . mysql_error() . "
Query: $quer
");
while($line = mysql_fetch_array($result, MYSQL_ASSOC))
{
$tempHTML .= "\n\n";
foreach($line as $key=>$val)
{
$tempHTML .= "| ";
if($key == "totaltime")
$val = $this->formatTime(round((abs($val) / 1000), 0));
if($key == "size")
$val = $this->formatSize(abs($val));
$val = htmlentities($val);
if(($val === 0) || ($val == '0')) $val = ' ';
$tempHTML .= $val;
$tempHTML .= " | \n";
}
$tempHTML .= "
";
}
$tempHTML .= "
\n";
$result = mysql_query("SELECT count(*) from phptunest_tunes");
$row = mysql_fetch_array($result, MYSQL_NUM);
$this->totalcount = $row[0];
$result = mysql_query("SELECT sum(size) from phptunest_tunes");
$row = mysql_fetch_array($result, MYSQL_NUM);
$this->totalsize = $row[0];
$result = mysql_query("SELECT sum(totalTime) from phptunest_tunes");
$row = mysql_fetch_array($result, MYSQL_NUM);
$this->totaltime = $row[0];
$time = $this->formatTime(round((abs($this->totaltime)/1000),0));
$size = $this->formatSize($this->totalsize);
$tempHTML = "Total Tracks: " . $this->totalcount . "
Total Time: $time
Total Size: $size
\n" . $tempHTML;
$this->parsedHTML = $tempHTML;
mysql_close($this->mysqlLink);
}
// Parses XML data without database and without keeping it all in memory.
// This should consume much less RAM memory than ramSortParse()
function ramParse()
{
global $phptunestTempParsedHTML, $phptunestTempTotalCount, $phptunestTempTotalTime, $phptunestTempTotalSize;
$tune = array();
$trackid = false;
$tunestr = "";
$tempHTML = "";
// iTunes Music lists are UTF-8 -encoded
if (!$tuneXML = xml_parser_create("UTF-8"))
die("Couldn't create XML parse");
if (!$tunestr = @implode("\n", file($this->file)))
die("Couldn't load XML data");
// Let's start printing HTML table here
$colWidth = round(100/count($this->cols),0);
$tempHTML = '';
$tempHTML .= '';
foreach($this->cols as $col)
$tempHTML .= "| $col | \n";
$tempHTML .= "
\n\n";
$this->parsedHTML .= $tempHTML;
$tempHTML = "";
// Parse the XML data track-by-track, and output data right away as HTML
xml_set_element_handler($tuneXML, array($this, "xmlStartElement"), array($this, "xmlEndElement"));
xml_set_character_data_handler($tuneXML, array($this, "xmlCharacterData"));
if (!xml_parse($tuneXML, $tunestr, true))
{
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($tuneXML)),
xml_get_current_line_number($tuneXML)));
}
if (!xml_parser_free($tuneXML))
die("Couldn't free XML parser");
$this->totalcount = $phptunestTempTotalCount;
$this->totalsize = $phptunestTempTotalSize;
$this->totaltime = $phptunestTempTotalTime;
$time = $this->formatTime(round((abs($this->totaltime)/1000),0));
$size = $this->formatSize($this->totalsize);
$this->parsedHTML = "Total Tracks: " . $this->totalcount . "
Total Time: $time
Total Size: $size
\n" . $this->parsedHTML;
$this->parsedHTML .= $phptunestTempParsedHTML . '
';
}
// Parses XML data without database, supports sorting.
// This is nice method if XML list isn't too large.
// Otherwise, it won't work if PHP is compiled with --enable-memory-limit and if
// memory_limit is set too low in php.ini configuration file
function ramSortParse()
{
$tunevals = array();
$tune = array();
$trackid = false;
$tunestr = "";
if (!$tuneXML = xml_parser_create("UTF-8"))
die("Couldn't create XML parse");
if (!$tunestr = @implode("\n", file($this->file)))
die("Couldn't load XML data");
if (!xml_parse_into_struct($tuneXML, $tunestr, $tuneValues))
die("Couldn't parse XML data");
if (!xml_parser_free($tuneXML))
die("Couldn't free XML parser");
$laskuri = 0;
$tunestr = NULL;
$this->tunes = array();
foreach ($tuneValues as $key=>$val)
if ($val['level'] == 5)
{
array_push($this->tunes, $val);
// We don't want to see ugly index errors :)
// This should speed up parsing a bit.
@$tunevals[] = utf8_decode($val['value']);
}
$this->tunes = NULL;
$this->tunes = array();
for($i=0; $i < count($tunevals); $i+=2)
{
$temp = $tunevals[$i];
if ($temp == "Track ID")
$tune = array();
if($temp == "Total Time")
{
$this->totaltime += round((abs($tunevals[$i+1]) / 1000), 0);
$tunevals[$i+1] = $this->formatTime(round((abs($tunevals[$i+1]) / 1000), 0));
}
if($temp == "Size")
{
$this->totalsize += abs($tunevals[$i+1]);
$tunevals[$i+1] = $this->formatSize(abs($tunevals[$i+1]));
}
if($temp == "Library Folder Count")
{
$tune[$tunevals[$i]] = $tunevals[$i+1];
array_push($this->tunes, $tune);
$this->totalcount++;
continue;
}
@$tune[$tunevals[$i]] = $tunevals[$i+1];
}
$tunevals = NULL;
// Multicolumn sort in RAM! This isn't as slow as you would expect.
uksort($this->tunes, array($this, "tuneSort"));
// Data is now ready to be printed from RAM.
$colWidth = 100/count($this->cols);
$tempHTML = '';
$tempHTML .= '';
for($i=0; $i < count($this->cols); $i++)
$tempHTML .= "| " . $this->cols[$i] . " | ";
$tempHTML .= "
\n";
foreach ($this->tunes as $tune)
{
$empty = true;
foreach ($tune as $key=>$val)
if($val != "")
$empty = false;
if($empty)
{
$this->totalcount--;
continue;
}
$tempHTML .= "\n";
$keys= array();
foreach ($tune as $key=>$val)
$keys[]=$key;
for($j=0; $j < count($this->cols); $j++)
{
if (!in_array($this->cols[$j], $keys))
{
$col = $this->cols[$j];
$tune["$col"] = "";
}
}
for($k = 0; $k < count($this->cols); $k++)
{
$tempHTML .= "| ";
$val = htmlentities($tune[$this->cols[$k]]);
if(($val === 0) || ($val == '0')) $val = ' ';
$tempHTML .= $val;
$tempHTML .= " | ";
}
$tempHTML .= "
";
}
$tempHTML .= "
";
$time = $this->formatTime($this->totaltime);
$size = $this->formatSize($this->totalsize);
$tempHTML = "Total Tracks: " . $this->totalcount . "
Total Time: $time
Total Size: $size
\n" . $tempHTML;
$this->parsedHTML = $tempHTML;
}
// Returns parsed HTML code as string.
function getHTMLTable()
{
return $this->parsedHTML;
}
// Sorting function for ramParse()
function tuneSort($a, $b)
{
$sa = $sb = "";
foreach($this->sortColumn as $col)
{
if(isset($this->tunes[$a][$col]))
$sa = $this->tunes[$a][$col];
if(isset($this->tunes[$b][$col]))
$sb = $this->tunes[$b][$col];
if ($sa == $sb)
continue;
if ($this->sortOrder == 0)
return ($sa > $sb) ? 1 : -1;
else
return ($sa < $sb) ? 1 : -1;
}
return 0;
}
// Formats numerical time into more suitable format
function formatTime($time)
{
$days = round($time/86400, 0);
$hours = round(($time % 86400)/3600, 0);
$minutes = round((($time % 86400) % 3600) / 60, 0);
$seconds = round((($time % 86400) % 3600) % 60, 0);
$time = "";
if ($days)
$time .= "$days d ";
if ($hours)
$time .= "$hours h ";
if ($minutes)
$time .= "$minutes min ";
if ($seconds)
$time .= "$seconds sec";
return $time;
}
// Formats bytes into megabytes (and decimals)
function formatSize($size)
{
$mb = round($size/(1024*1024), 0);
$kb = round(($size % 1024*1024)/1024, 0);
$size = "";
if ($mb)
{ $mb += round($kb/1024,2);
$size = "$mb MB";
}
else
$size = "$kb KB";
return $size;
}
function outputTune(&$tune)
{
global $phptunestTempParsedHTML,$phptunestTempTotalCount, $phptunestTempTotalTime, $phptunestTempTotalSize;
$tempHTML = "";
$colWidth = 100/count($this->cols);
if (!isset($tune['Track ID']))
return;
foreach($tune as $key=>$val)
{
$tune["$key"] = str_replace("%", "\%", mysql_escape_string($val));
}
foreach($tune as $key=>$val)
$tune["$key"] = utf8_decode($val);
if ($tune['Track ID'] == 0)
die("phptunest error, Track ID is 0! Please report this error to nikita@maczsoftware.com.
");
$tempHTML = "\n\n";
foreach($this->cols as $key)
{
$val = $tune["$key"];
$key = str_replace(" ", "", strtolower($key));
$tempHTML .= "| ";
if($key == "totaltime")
{
$phptunestTempTotalTime += abs($val);
$val = $this->formatTime(round((abs($val) / 1000), 0));
}
if($key == "size")
{
$phptunestTempTotalSize += abs($val);
$val = $this->formatSize(abs($val));
}
$val = htmlentities($val);
if(($val === 0) || ($val == '0'))
$val = ' ';
$tempHTML .= $val;
$tempHTML .= " | \n";
}
$phptunestTempTotalCount++;
$tempHTML .= "
";
$phptunestTempParsedHTML .= $tempHTML;
$tempHTML = "";
}
// Pushes a single tune into MySQL table
function mysqlPushIntoTable(&$tune)
{
if (!isset($tune['Track ID']))
return;
foreach($this->allCols as $col)
{
if(!isset($tune["$col"]))
$tune["$col"] = 0;
$tune["$col"] = str_replace("%", "\%", mysql_real_escape_string($tune[$col]));
}
foreach($tune as $key=>$val)
$tune["$key"] = utf8_decode($val);
if ($tune['Track ID'] == 0)
die("phptunest error, Track ID is 0! Please report this error to nikita@maczsoftware.com.
");
$quer = "INSERT INTO phptunest_tunes values(\"" . $tune['Track ID'] . "\", \"" . $tune['Name'] . "\", \"" . $tune['Artist'] . "\", \"" . $tune['Composer'] . "\", \"" . $tune['Album'] . "\", \"" . $tune['Genre'] . "\", \"" . $tune['Kind'] . "\"," . $tune['Size'] . "," . $tune['Total Time'] . "," . $tune['Disc Number'] . ", " . $tune['Disc Count'] . "," . $tune['Track Number'] . "," . $tune['Track Count'] . "," . $tune['Year'] . "," . $tune['BPM'] . ", " . "\"" . $tune['Date Modified'] . "\", \"" . $tune['Date Added'] . "\", " . $tune['Bit Rate'] . "," . $tune['Sample Rate'] . "," . "\"" . $tune['Equalizer'] . "\", \"" . $tune['Comments'] . "\"," . $tune['Play Count'] . "," . $tune['Play Date'] . "," . "\"" . $tune['Play Date UTC'] . "\", " . $tune['Normalization'] . "," . $tune['Rating'] . "," . $tune['File Type'] . "," . $tune['File Creator'] . "," . "\"" . $tune['Location'] . "\", " . $tune['File Folder Count'] . "," . $tune['Library Folder Count'] . ")";
mysql_query($quer) or die("Couldn't insert new tune records into database
" . mysql_error() . "
Query was: $quer
");
}
// "private" function, formats columns from user-entered form into format used in MySQL
function formatCols($cols)
{
$temp = "";
for($i=0; $i < count($cols); $i++)
{
$temp = str_replace(" ", "", ucfirst(strtolower($cols[$i])));
$cols[$i] = strtolower(substr($temp,0,1)) . substr($temp, 1);
}
return implode(" , ", $cols);
}
// "private" function, formats columns from user-entered form into format used in MySQ
// Uses descending order
function formatColsDesc($cols)
{
$temp = "";
for($i=0; $i < count($cols); $i++)
{
$temp = str_replace(" ", "", ucfirst(strtolower($cols[$i])));
$cols[$i] = strtolower(substr($temp,0,1)) . substr($temp, 1) . " DESC";
}
return implode(" , ", $cols);
}
// XML parser function: We're starting new element here.
function xmlStartElement($parser, $name, $attrs)
{
global $phptunestXmlDepth;
$phptunestXmlDepth++;
}
// XML parser function: We're ending current element here.
function xmlEndElement($parser, $name)
{
global $phptunestXmlDepth;
$phptunestXmlDepth--;
}
// XML parser function: We're between starting and ending tags.
// If depth level is 5 (song info is at level 5), then let's use the data.
function xmlCharacterData($parser, $data)
{
global $phptunestXmlDepth,$phptunestAboutToClose, $phptunestValueComing, $phptunestLastKey;
if (($phptunestXmlDepth == 5) && ($data != ""))
{
if($phptunestAboutToClose) // last entry!
{
$this->curTune['Library Folder Count'] = $data;
if ($this->enableSorting == 0 && $this->mysqlEnabled == 0)
$this->outputTune($this->curTune);
else
$this->mysqlPushIntoTable($this->curTune);
$this->curTune = array();
$phptunestAboutToClose = false;
$phptunestValueComing = false;
return;
}
if ($data == 'Library Folder Count')
// Last entry coming up, let's close things
$phptunestAboutToClose = true;
else
{
if($phptunestValueComing)
{
$this->curTune["$phptunestLastKey"] = $data;
$phptunestValueComing = false;
}
else
{
$phptunestLastKey = $data;
$phptunestValueComing = true;
}
}
}
}
// Class phptunest ends here
}
?>