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 .= "\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 .= "\n"; } $tempHTML .= ""; } $tempHTML .= "
$col
"; 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"; $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 .= "\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 . '
$col
'; } // 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 .= ""; $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 .= ""; } $tempHTML .= ""; } $tempHTML .= "
" . $this->cols[$i] . "
"; $val = htmlentities($tune[$this->cols[$k]]); if(($val === 0) || ($val == '0')) $val = ' '; $tempHTML .= $val; $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 } ?>