diff options
author | Paul Arthur <paul.arthur@flowerysong.com> | 2013-01-26 03:00:32 -0500 |
---|---|---|
committer | Paul Arthur <paul.arthur@flowerysong.com> | 2013-01-26 03:38:46 -0500 |
commit | ef4d3660605efc7f1328d4533b0f4bfb6c1107e2 (patch) | |
tree | e4377fb129a899e65aaaf421f8c97098aecaedd5 /lib/class/vainfo.class.php | |
parent | 8a750c3e875d590d351c3042570a134fcdf03e5d (diff) | |
download | ampache-ef4d3660605efc7f1328d4533b0f4bfb6c1107e2.tar.gz ampache-ef4d3660605efc7f1328d4533b0f4bfb6c1107e2.tar.bz2 ampache-ef4d3660605efc7f1328d4533b0f4bfb6c1107e2.zip |
Cosmetics: death to tabs
The refactoring I've been doing has reminded me of my strong preference
for spaces, and I feel inclined to impose my will on the tree.
Diffstat (limited to 'lib/class/vainfo.class.php')
-rw-r--r-- | lib/class/vainfo.class.php | 2112 |
1 files changed, 1056 insertions, 1056 deletions
diff --git a/lib/class/vainfo.class.php b/lib/class/vainfo.class.php index 2df7e743..b1d825bb 100644 --- a/lib/class/vainfo.class.php +++ b/lib/class/vainfo.class.php @@ -1,5 +1,5 @@ <?php -/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ +/* vim:set softtabstop=4 shiftwidth=4 expandtab: */ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) @@ -28,1061 +28,1061 @@ */ class vainfo { - public $encoding = ''; - public $encoding_id3v1 = ''; - public $encoding_id3v2 = ''; - - public $filename = ''; - public $type = ''; - public $tags = array(); - - protected $_raw = array(); - protected $_getID3 = ''; - protected $_iconv = false; - - protected $_file_encoding = ''; - protected $_file_pattern = ''; - protected $_dir_pattern = ''; - - private $_pathinfo; - private $_broken = false; - - /** - * Constructor - * - * This function just sets up the class, it doesn't - * actually pull the information - * - * @param string $file filename - * @param string $encoding Target encode character set - * @param string $encoding_id3v1 Default id3v1 encode character set - * @param string $encoding_iv3v2 Default id3v2 encode character set - * @param string $dir_pattern Directory pattern - * @param string $file_pattern File pattern - * @return mixed If can't analyze file, return false. default return: void - */ - public function __construct($file, $encoding = null, $encoding_id3v1 = null, $encoding_id3v2 = null, $dir_pattern, $file_pattern) { - - /* Check for ICONV */ - if (function_exists('iconv') && Config::get('use_iconv') == "1") { - $this->_iconv = true; - } - else { - $this->_iconv = false; - } - - $this->filename = $file; - if ($encoding) { - $this->encoding = $encoding; - } - else { - $this->encoding = Config::get('site_charset'); - } - - /* These are needed for the filename mojo */ - $this->_file_pattern = $file_pattern; - $this->_dir_pattern = $dir_pattern; - - if(strtoupper(substr(PHP_OS,0,3)) == 'WIN') { - $this->_pathinfo = str_replace('%3A', ':', urlencode($this->filename)); - $this->_pathinfo = pathinfo(str_replace('%5C', '\\', $this->_pathinfo)); - } - else { - $this->_pathinfo = pathinfo(str_replace('%2F', '/', urlencode($this->filename))); - } - $this->_pathinfo['extension'] = strtolower($this->_pathinfo['extension']); - - // Initialize getID3 engine - $this->_getID3 = new getID3(); - - $this->_getID3->option_md5_data = false; - $this->_getID3->option_md5_data_source = false; - $this->_getID3->option_tags_html = false; - $this->_getID3->option_extra_info = true; - $this->_getID3->option_tag_lyrics3 = true; - $this->_getID3->option_tags_process = true; - $this->_getID3->encoding = $this->encoding; - - // get id3tag encoding (try to work around off-spec id3v1 tags) - try { - $this->_raw = $this->_getID3->analyze($file); - } - catch (Exception $error) { - debug_event('Getid3()',"Broken file detected $file - " . $error->message,'1'); - $this->_broken = true; - return false; - } - - /* Use default mb_detect_order in php.ini or not */ - if (Config::get('mb_detect_order')) { - $mb_order = Config::get('mb_detect_order'); - } - elseif (function_exists('mb_detect_order')) { - $mb_order = implode(", ", mb_detect_order()); - } - else { - $mb_order = "auto"; - } - - $test_tags = array('artist', 'album', 'genre', 'title'); - - if ($encoding_id3v1) { - $this->encoding_id3v1 = $encoding_id3v1; - } - else { - foreach ($test_tags as $tag) { - if ($value = $this->_raw['id3v1'][$tag]) { - $tags[$tag] = $value; - } - } - - $this->encoding_id3v1 = self::_detect_encoding($tags, $mb_order); - } - - if (Config::get('getid3_detect_id3v2_encoding')) { - foreach ($test_tags as $tag) { - if ($value = $this->_raw['id3v2']['comments'][$tag]) { - $tags[$tag] = $value; - } - } - - $this->encoding_id3v2 = self::_detect_encoding($tags, $mb_order); - $this->_getID3->encoding_id3v2 = $this->encoding_id3v2; - } - - $this->_getID3->encoding_id3v1 = $this->encoding_id3v1; - - } // vainfo - - /** - * _detect_encoding - * Takes an array of tags and attempts to automatically detect their - * encoding. - */ - private static function _detect_encoding($tags, $mb_order) { - if (function_exists('mb_detect_encoding')) { - $encodings = array(); - $tags = array('artist', 'album', 'genre', 'title'); - foreach ($tags as $tag) { - $encodings[mb_detect_encoding($tag, $mb_order, true)]++; - } - - debug_event('vainfo', 'encoding detection: ' . json_encode($encodings), 5); - $high = 0; - foreach ($encodings as $key => $value) { - if ($value > $high) { - $encoding = $key; - $high = $value; - } - } - - if ($encoding != 'ASCII' && $encoding != '0') { - return $encoding; - } - else { - return 'ISO-8859-1'; - } - } - return 'ISO-8859-1'; - } - - - /** - * get_info - * This function runs the various steps to gathering the metadata - */ - public function get_info() { - - // If this is broken, don't waste time figuring it out a second - // time, just return their rotting carcass of a media file. - if ($this->_broken) { - $this->tags = $this->set_broken(); - return true; - } - - /* Get the Raw file information */ - try { - $this->_raw = $this->_getID3->analyze($this->filename); - } - catch (Exception $error) { - debug_event('Getid3()',"Unable to catalog file:" . $error->message,'1'); - } - - /* Figure out what type of file we are dealing with */ - $this->type = $this->_get_type(); - - $enabled_sources = (array)Config::get('metadata_order'); - - if (in_array('filename', $enabled_sources)) { - $this->tags['filename'] = $this->_parse_filename($this->filename); - } - - if (in_array('getID3', $enabled_sources)) { - $this->tags['getID3'] = $this->_get_tags(); - } - - $this->_get_plugin_tags(); - - } // get_info - - /** - * get_tag_type - * This takes the result set and the tag_order defined in your config - * file and tries to figure out which tag type(s) it should use. If your - * tag_order doesn't match anything then it throws up its hands and uses - * everything. - */ - public static function get_tag_type($results, $config_key = 'metadata_order') { - - $order = (array)Config::get($config_key); - - /* Iterate through the defined key order adding them to an - * ordered array as we go. - */ - - foreach($order as $key) { - if ($results[$key]) { - $returned_keys[] = $key; - } - } - - /* If we didn't find anything then default to everything. - */ - if (!isset($returned_keys)) { - $returned_keys = array_keys($results); - $returned_keys = sort($returned_keys); - } - - return $returned_keys; - - } // get_tag_type - - /** - * clean_tag_info - * This function takes the array from vainfo along with the - * key we've decided on and the filename and returns it in a - * sanitized format that ampache can actually use - */ - public static function clean_tag_info($results, $keys, $filename = null) { - - $info = array(); - - if ($filename) { - $info['file'] = $filename; - } - - // Iteration! - foreach ($keys as $key) { - $tags = $results[$key]; - - $info['file'] = $info['file'] ?: $tags['file']; - $info['bitrate'] = $info['bitrate'] ?: intval($tags['bitrate']); - $info['rate'] = $info['rate'] ?: intval($tags['rate']); - $info['mode'] = $info['mode'] ?: $tags['mode']; - $info['size'] = $info['size'] ?: $tags['size']; - $info['mime'] = $info['mime'] ?: $tags['mime']; - $info['encoding'] = $info['encoding'] ?: $tags['encoding']; - $info['rating'] = $info['rating'] ?: $tags['rating']; - $info['time'] = $info['time'] ?: intval($tags['time']); - $info['channels'] = $info['channels'] ?: $tags['channels']; - - /* These are used to generate the correct IDs later */ - $info['title'] = $info['title'] ?: stripslashes(trim($tags['title'])); - - $info['year'] = $info['year'] ?: intval($tags['year']); - - $info['disk'] = $info['disk'] ?: intval($tags['disk']); - - $info['totaldiscs'] = $info['totaldiscs'] ?: intval($tags['totaldiscs']); - - $info['artist'] = $info['artist'] ?: trim($tags['artist']); - - $info['album'] = $info['album'] ?: trim($tags['album']); - - // multiple genre support - if ((!$info['genre']) && $tags['genre']) { - if (!is_array($tags['genre'])) { - // not all tag formats will return an array, but we need one - $info['genre'][] = trim($tags['genre']); - } - else { - // if we trim the array we lose everything after 1st entry - foreach ($tags['genre'] as $genre) { - $info['genre'][] = trim($genre); - } - } - } - - $info['mb_trackid'] = $info['mb_trackid'] ?: trim($tags['mb_trackid']); - $info['mb_albumid'] = $info['mb_albumid'] ?: trim($tags['mb_albumid']); - $info['mb_artistid'] = $info['mb_artistid'] ?: trim($tags['mb_artistid']); - - /* @TODO language doesn't import from id3tag. @momo-i */ - $info['language'] = $info['language'] ?: Dba::escape($tags['language']); - - $info['lyrics'] = $info['lyrics'] - ?: str_replace( - array("\r\n","\r","\n"), - '<br />', - strip_tags($tags['lyrics'])); - - $info['track'] = $info['track'] ?: intval($tags['track']); - $info['resolution_x'] = $info['resolution_x'] ?: intval($tags['resolution_x']); - $info['resolution_y'] = $info['resolution_y'] ?: intval($tags['resolution_y']); - $info['audio_codec'] = $info['audio_codec'] ?: Dba::escape($tags['audio_codec']); - $info['video_codec'] = $info['video_codec'] ?: Dba::escape($tags['video_codec']); - } - - if ($info['totaldiscs'] == 1 && $info['disk'] == 1) { - unset($info['disk']); - unset($info['totaldiscs']); - } - - return $info; - - } // clean_tag_info - - /** - * _get_type - * This function takes the raw information and figures out - * what type of file we are dealing with for use by the tag - * function - */ - public function _get_type() { - - /* There are a few places that the file type can - * come from, in the end we trust the encoding - * type - */ - if ($type = $this->_raw['video']['dataformat']) { - $type = $this->_clean_type($type); - return $type; - } - if ($type = $this->_raw['audio']['streams']['0']['dataformat']) { - $type = $this->_clean_type($type); - return $type; - } - if ($type = $this->_raw['audio']['dataformat']) { - $type = $this->_clean_type($type); - return $type; - } - if ($type = $this->_raw['fileformat']) { - $type = $this->_clean_type($type); - return $type; - } - - return false; - - } // _get_type - - - /** - * _get_tags - * This function takes the raw information and the type and - * attempts to gather the tags and then normalize them into - * ['tag_name']['var'] = value - */ - public function _get_tags() { - - $results = array(); - - /* The tags can come in many different shapes and colors - * depending on the encoding time of day and phase of the moon. - */ - foreach ($this->_raw['tags'] as $key => $tag_array) { - switch ($key) { - case 'vorbiscomment': - debug_event('_get_tags', 'Parsing vorbis', '5'); - $results[$key] = $this->_parse_vorbiscomment($tag_array); - break; - case 'id3v1': - debug_event('_get_tags', 'Parsing id3v1', '5'); - $results[$key] = $this->_parse_id3v1($tag_array); - break; - case 'id3v2': - debug_event('_get_tags', 'Parsing id3v2', '5'); - $results[$key] = $this->_parse_id3v2($tag_array); - break; - case 'ape': - debug_event('_get_tags', 'Parsing ape', '5'); - $results[$key] = $this->_parse_ape($tag_array); - break; - case 'quicktime': - debug_event('_get_tags', 'Parsing quicktime', '5'); - $results[$key] = $this->_parse_quicktime($tag_array); - break; - case 'riff': - debug_event('_get_tags', 'Parsing riff', '5'); - $results[$key] = $this->_parse_riff($tag_array); - break; - case 'flv': - debug_event('_get_tags', 'Parsing flv', '5'); - $results[$key] = $this->_parse_flv($this->_raw); - break; - case 'mpg': - case 'mpeg': - debug_event('_get_tags', 'Parsing MPEG', '5'); - $results['mpeg'] = $this->_parse_mpg($this->_raw); - break; - case 'asf': - case 'wmv': - debug_event('_get_tags', 'Parsing WMV/WMA/ASF', '5'); - $results['asf'] = $this->_parse_wmv($this->_raw); - break; - case 'avi': - debug_event('_get_tags', 'Parsing avi', '5'); - $results[$key] = $this->_parse_avi($this->_raw); - break; - case 'lyrics3': - debug_event('_get_tags', 'Parsing lyrics3', '5'); - $results[$key] = $this->_parse_lyrics($tag_array); - break; - default: - debug_event('vainfo','Error: Unable to determine tag type of ' . $key . ' for file ' . $this->filename . ' Assuming id3v2','5'); - $results[$key] = $this->_parse_id3v2($this->_raw['id3v2']['comments']); - break; - } // end switch - } // end foreach - - - $cleaned = self::clean_tag_info($results, self::get_tag_type($results, 'getid3_tag_order'), $this->filename); - $cleaned = array_merge($cleaned, $this->_get_info()); - $cleaned['raw'] = $results; - - return $cleaned; - - } // _get_tags - - /** - * _get_plugin_tags - * Get additional metadata from plugins - */ - private function _get_plugin_tags() { - $tag_order = Config::get('metadata_order'); - if (!is_array($tag_order)) { - $tag_order = array($tag_order); - } - - $plugin_names = Plugin::get_plugins('get_metadata'); - foreach ($tag_order as $key => $tag_source) { - if (in_array($tag_source, $plugin_names)) { - $plugin = new Plugin($tag_source); - if ($plugin->load()) { - $this->tags[$tag_source] = $plugin->_plugin->get_metadata(self::clean_tag_info($this->tags, self::get_tag_type($this->tags), $this->filename)); - } - } - } - } // _get_plugin_tags - - /** - * _get_info - * Gather and return the general information about a song (vbr/cbr, - * sample rate, channels, etc.) - */ - private function _get_info() { - - $array = array(); - - /* Try to pull the information directly from - * the audio array - */ - if ($this->_raw['audio']['bitrate_mode']) { - $array['mode'] = $this->_raw['audio']['bitrate_mode']; - if ($array['mode'] == 'con') { - $array['mode'] = 'cbr'; - } - } - if ($this->_raw['audio']['bitrate']) { - $array['bitrate'] = $this->_raw['audio']['bitrate']; - } - if ($this->_raw['audio']['channels']) { - $array['channels'] = intval($this->_raw['audio']['channels']); - } - if ($this->_raw['audio']['sample_rate']) { - $array['rate'] = intval($this->_raw['audio']['sample_rate']); - } - if ($this->_raw['filesize']) { - $array['size'] = intval($this->_raw['filesize']); - } - if ($this->_raw['encoding']) { - $array['encoding'] = $this->_raw['encoding']; - } - if ($this->_raw['mime_type']) { - $array['mime'] = $this->_raw['mime_type']; - } - if ($this->_raw['playtime_seconds']) { - $array['time'] = $this->_raw['playtime_seconds']; - } - - return $array; - - } // _get_info - - /** - * _clean_type - * This standardizes the type that we are given into a recognized type. - */ - private function _clean_type($type) { - - switch ($type) { - case 'mp3': - case 'mp2': - case 'mpeg3': - return 'mp3'; - break; - case 'vorbis': - return 'ogg'; - break; - case 'flac': - case 'flv': - case 'mpg': - case 'mpeg': - case 'asf': - case 'wmv': - case 'avi': - case 'quicktime': - return $type; - default: - /* Log the fact that we couldn't figure it out */ - debug_event('vainfo','Unable to determine file type from ' . $type . ' on file ' . $this->filename,'5'); - return $type; - break; - } // end switch on type - - } // _clean_type - - /** - * _parse_lyrics - * This function takes a lyrics3 from getid3() - * nothing to do? - */ - private function _parse_lyrics($tags) { - - /* Results array */ - $array = array(); - - /* go through them all! */ - foreach ($tags as $tag => $data) { - if ($tag == 'unsynchedlyrics' || $tag == 'unsynchronised lyric') { - $tag = 'lyrics'; - } - $array[$tag] = $this->_clean_tag($data['0']); - - } // end foreach - - return $array; - - } // _parse_lyrics - - /** - * _parse_vorbiscomment - * This function takes a vorbiscomment from getid3() and then - * returns the elements translated using iconv if needed in a - * pretty little format - */ - private function _parse_vorbiscomment($tags) { - - /* Results array */ - $array = array(); - - /* go through them all! */ - foreach ($tags as $tag=>$data) { - - /* We need to translate a few of these tags */ - switch ($tag) { - - case 'genre': - // multiple genre support - foreach($data as $foo) { - $array['genre'][] = $this->_clean_tag($foo,''); - } - break; - case 'tracknumber': - $array['track'] = $this->_clean_tag($data['0']); - break; - case 'discnumber': - $el = explode('/', $data['0']); - $array['disk'] = $el[0]; - break; - case 'date': - $array['year'] = $this->_clean_tag($data['0']); - break; - default: - $array[$tag] = $this->_clean_tag($data['0']); - break; - } // end switch - - } // end foreach - - return $array; - - } // _parse_vorbiscomment - - /** - * _parse_id3v1 - * This function takes an id3v1 tag set from getid3() and then - * returns the elements (translated using iconv if needed) in a - * pretty little format. - */ - private function _parse_id3v1($tags) { - - $array = array(); - - /* Go through all the tags */ - foreach ($tags as $tag=>$data) { - - /* This is our baseline for naming - * so no translation needed - */ - $array[$tag] = $this->_clean_tag($data['0']); - - } // end foreach - - return $array; - - } // _parse_id3v1 - - /** - * _parse_id3v2 - * This function takes an id3v2 tag set from getid3() and then - * returns the elements (translated using iconv if needed) in a - * pretty little format. - */ - private function _parse_id3v2($tags) { - - $array = array(); - - /* Go through the tags */ - foreach ($tags as $tag=>$data) { - - /** - * the new getid3 handles this differently - * so we now need to account for it :( - */ - switch ($tag) { - case 'genre': - // multiple genre support - foreach($data as $genre) { - $array['genre'][] = $this->_clean_tag($genre); - } - break; - case 'part_of_a_set': - $el = explode('/', $data['0']); - $array['disk'] = $el[0]; - $array['totaldiscs'] = $el[1]; - break; - case 'track_number': - $array['track'] = $this->_clean_tag($data['0']); - break; - case 'comments': - $array['comment'] = $this->_clean_tag($data['0']); - break; - default: - $array[$tag] = $this->_clean_tag($data['0']); - break; - } // end switch on tag - - } // end foreach - - $id3v2 = $this->_raw['id3v2']; - - if(!empty($id3v2['UFID'])) { - // Find the MBID for the track - foreach ($id3v2['UFID'] as $ufid) { - if ($ufid['ownerid'] == 'http://musicbrainz.org') { - $array['mb_trackid'] = $this->_clean_tag($ufid['data']); - } - } - - // Find the MBIDs for the album and artist - foreach ($id3v2['TXXX'] as $txxx) { - switch ($txxx['description']) { - case 'MusicBrainz Album Id': - $array['mb_albumid'] = $this->_clean_tag($txxx['data']); - break; - case 'MusicBrainz Artist Id': - $array['mb_artistid'] = $this->_clean_tag($txxx['data']); - break; - } - } - } - - // Find the rating - if (is_array($id3v2['POPM'])) { - foreach ($id3v2['POPM'] as $popm) { - if (array_key_exists('email', $popm) && - $user = User::get_from_email($popm['email'])) { - // Ratings are out of 255; scale it - $array['rating'][$user->id] = $popm['rating'] / 255 * 5; - } - } - } - - return $array; - - } // _parse_id3v2 - - /** - * _parse_ape - * This function takes ape tags set by getid3() and then - * returns the elements translated using iconv if needed in a - * pretty little format - */ - private function _parse_ape($tags) { - - foreach ($tags as $tag=>$data) { - switch ($tag) { - - case 'genre': - // multiple genre support - foreach($data as $genre) { - $array['genre'][] = $this->_clean_tag($genre); - } - break; - - default: - $array[$tag] = $this->_clean_tag($data['0'], $this->_file_encoding); - break; - } // end switch on tag - - } // end foreach tags - - return $array; - - } // _parse_ape - - /** - * _parse_riff - * this function takes the riff take information passed by getid3() and - * then reformats it so that it matches the other formats. May require iconv - */ - private function _parse_riff($tags) { - - foreach ($tags as $tag=>$data) { - - switch ($tag) { - case 'product': - $array['album'] = $this->_clean_tag($data['0'], $this->_file_encoding); - break; - default: - $array[$tag] = $this->_clean_tag($data['0'], $this->_file_encoding); - break; - } // end switch on tag - - } // foreach tags - - return $array; - - } // _parse_riff - - /** - * _parse_quicktime - * this function takes the quicktime tags set by getid3() and then - * returns the elements translated using iconv if needed in a - * pretty little format - */ - private function _parse_quicktime($tags) { - - /* Results array */ - $array = array(); - - /* go through them all! */ - foreach ($tags as $tag=>$data) { - - /* We need to translate a few of these tags */ - switch ($tag) { - case 'creation_date': - if (strlen($data['0']) > 4) { - /* Weird Date format, attempt to normalize */ - $data['0'] = date("Y",strtotime($data['0'])); - } - $array['year'] = $this->_clean_tag($data['0']); - break; - case 'MusicBrainz Track Id': - $array['mb_trackid'] = $this->_clean_tag($data['0']); - break; - case 'MusicBrainz Album Id': - $array['mb_albumid'] = $this->_clean_tag($data['0']); - break; - case 'MusicBrainz Artist Id': - $array['mb_artistid'] = $this->_clean_tag($data['0']); - break; - } // end switch - - $array[$tag] = $this->_clean_tag($data['0']); - - } // end foreach - - // Also add in any video related stuff we might find - if (strpos($this->_raw['mime_type'], 'video') !== false) { - $info = $this->_parse_avi($this->_raw); - $info['video_codec'] = $this->_raw['quicktime']['ftyp']['fourcc']; - $array = array_merge($info, $array); - } - - return $array; - - } // _parse_quicktime - - /** - * _parse_avi - * This attempts to parse our the information on an avi file and present it in some - * kind of sane format, this is a little hard as these files don't have tags - */ - private function _parse_avi($tags) { - - $array = array(); - - $array['title'] = urldecode($this->_pathinfo['filename']); - $array['video_codec'] = $tags['video']['fourcc']; - $array['audio_codec'] = $tags['audio']['dataformat']; - $array['resolution_x'] = $tags['video']['resolution_x']; - $array['resolution_y'] = $tags['video']['resolution_y']; - $array['mime'] = $tags['mime_type']; - $array['comment'] = $tags['video']['codec']; - - return $array; - - } // _parse_avi - - /** - * _parse_mpg - * This attempts to parse our the information on a mpg file and present it in some - * kind of sane format, this is a little hard as these files don't have tags - */ - private function _parse_mpg($tags) { - - $array = array(); - - $array['title'] = urldecode($this->_pathinfo['filename']); - $array['video_codec'] = $tags['video']['codec']; - $array['audio_codec'] = $tags['audio']['dataformat']; - $array['resolution_x'] = $tags['video']['resolution_x']; - $array['resolution_y'] = $tags['video']['resolution_y']; - $array['mime'] = $tags['mime_type']; - $array['comment'] = $tags['video']['codec']; - - return $array; - - } // _parse_mpg - - /** - * _parse_wmv - * This attempts to parse our the information on a asf/wmv file and present it in some - * kind of sane format, this is a little hard as these files don't have tags - */ - private function _parse_wmv($tags) { - - $array = array(); - - $array['mime'] = $tags['mime_type']; - - switch($array['mime']) { - default: - case 'video/x-ms-wmv': - if(isset($tags['tags']['asf']['title']['0'])) { - $array['title'] = $tags['tags']['asf']['title']['0']; - } - else { - $array['title'] = urldecode($this->_pathinfo['filename']); - } - $array['video_codec'] = $tags['video']['streams']['2']['codec']; - $array['audio_codec'] = $tags['audio']['streams']['1']['codec']; - $array['resolution_x'] = $tags['video']['streams']['2']['resolution_x']; - $array['resolution_y'] = $tags['video']['streams']['2']['resolution_y']; - $array['comment'] = $tags['tags']['asf']['title']['1']; - break; - } - - foreach($tags['tags']['asf'] as $tag => $data) { - $array[$tag] = $this->_clean_tag($data['0']); - } - - return $array; - - } // _parse_wmv - - /** - * _parse_flv - * This attempts to parse our the information on an flv file and present it in some - * kind of sane format, this is a little hard as these files don't have tags - */ - private function _parse_flv($tags) { - - $array = array(); - - $array['title'] = urldecode($this->_pathinfo['filename']); - $array['video_codec'] = $tags['video']['codec']; - $array['audio_codec'] = $tags['audio']['dataformat']; - $array['resolution_x'] = $tags['video']['resolution_x']; - $array['resolution_y'] = $tags['video']['resolution_y']; - $array['mime'] = $tags['mime_type']; - $array['comment'] = $tags['video']['codec']; - - return $array; - - } // _parse_flv - - /** - * _parse_filename - * - * This function uses the passed file and dir patterns - * To pull out extra tag information and populate it into - * its own array - * - * @param string $filename Filename that want to parse - * @return array Parsed results - */ - private function _parse_filename($filename) { - - $results = array(); - - // Correctly detect the slash we need to use here - if (strpos($filename, '/') !== false) { - $slash_type = '/'; - } - else { - $slash_type = '\\'; - } - - // Combine the patterns - $pattern = preg_quote($this->_dir_pattern) . $slash_type . preg_quote($this->_file_pattern); - - // Pull out the pattern codes into an array - preg_match_all('/\%\w/', $pattern, $elements); - - // Mangle the pattern by turning the codes into regex captures - $pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern); - $pattern = preg_replace('/\%\w/', '(.+?)', $pattern); - $pattern = str_replace('/', '\/', $pattern); - $pattern = str_replace(' ', '\s', $pattern); - $pattern = '/' . $pattern . '\..+$/'; - - // Pull out our actual matches - preg_match($pattern, $filename, $matches); - - // The first element is the full match text - $matched = array_shift($matches); - debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filename, 5); - - // Iterate over what we found - foreach ($matches as $key => $value) { - $new_key = translate_pattern_code($elements['0'][$key]); - if ($new_key) { - $results[$new_key] = $value; - } - } - - $results['size'] = filesize($filename); - - return $results; - - } // _parse_filename - - /** - * _id3v2_tag_to_frame - * - * This translates the tag name to a frame, if there a many it returns the first - * one if finds that exists in the raw - * - * @param string $tag_name Tag name - * @return mixed If found id3v2 frame, return frame. If not found, return false. - */ - private function _id3v2_tag_to_frame($tag_name) { - - static $map = array( - 'comment'=>array('COM','COMM'), - 'cd_ident'=>array('MCDI','MCI'), - 'album'=>array('TAL','TALB'), - 'language'=>array('TLA','TLAN'), - 'mood'=>array('TMOO'), - 'artist'=>array('TPE1'), - 'year'=>array('TDRC')); - - foreach ($map[$tag_name] as $frame) { - if (isset($this->_raw['id3v2'][$frame])) { - return $frame; - } - } - - return false; - - } // _id3v2_tag_to_frame - - /** - * _clean_tag - * - * This function cleans up the tag that it's passed using Iconv - * if we've got it. It also takes an optional encoding param - * for the cases where we know what the tags source encoding - * is, and or if it's different then the encoding recorded - * in the file - * - * @param string $tag Encoding string - * @param string $encoding Encode charset - * @return string Return encoded string - */ - private function _clean_tag($tag, $encoding = null) { - - // Default to getID3's native encoding - if (!$encoding) { - $encoding = $this->_getID3->encoding; - } - // If we've got iconv then go ahead and clear her up - if ($encoding == $this->encoding) { - debug_event('vainfo', "\$encoding -> ${encoding}, \$this->encoding -> {$this->encoding}", 5); - return $tag; - } - if ($this->_iconv) { - debug_event('vainfo', 'Use iconv()',5); - - // Try GNU iconv //TRANSLIT extension first - $new_encoding = $this->encoding . '//TRANSLIT'; - $clean = iconv($encoding, $new_encoding, $tag); - - // If that fails, do a plain conversion - if(strcmp($clean, '') == 0) { - $clean = iconv($encoding, $this->encoding, $tag); - } - } - elseif (function_exists('mb_convert_encoding')) { - debug_event('vainfo', 'Use mbstring',5); - debug_event('vainfo', "Try to convert from {$this->encoding} to $encoding", 5); - $clean = mb_convert_encoding($tag, $encoding, $this->encoding); - } - else { - $clean = $tag; - } - - return $clean; - - } // _clean_tag - - /** - * set_broken - * - * This fills all tag types with Unknown (Broken) - * - * @return array Return broken title, album, artist - */ - public function set_broken() { - - /* Pull In the config option */ - $order = Config::get('tag_order'); - - if (!is_array($order)) { - $order = array($order); - } - - $key = array_shift($order); - - $broken[$key]['title'] = '**BROKEN** ' . $this->filename; - $broken[$key]['album'] = 'Unknown (Broken)'; - $broken[$key]['artist'] = 'Unknown (Broken)'; - - return $broken; - - } // set_broken + public $encoding = ''; + public $encoding_id3v1 = ''; + public $encoding_id3v2 = ''; + + public $filename = ''; + public $type = ''; + public $tags = array(); + + protected $_raw = array(); + protected $_getID3 = ''; + protected $_iconv = false; + + protected $_file_encoding = ''; + protected $_file_pattern = ''; + protected $_dir_pattern = ''; + + private $_pathinfo; + private $_broken = false; + + /** + * Constructor + * + * This function just sets up the class, it doesn't + * actually pull the information + * + * @param string $file filename + * @param string $encoding Target encode character set + * @param string $encoding_id3v1 Default id3v1 encode character set + * @param string $encoding_iv3v2 Default id3v2 encode character set + * @param string $dir_pattern Directory pattern + * @param string $file_pattern File pattern + * @return mixed If can't analyze file, return false. default return: void + */ + public function __construct($file, $encoding = null, $encoding_id3v1 = null, $encoding_id3v2 = null, $dir_pattern, $file_pattern) { + + /* Check for ICONV */ + if (function_exists('iconv') && Config::get('use_iconv') == "1") { + $this->_iconv = true; + } + else { + $this->_iconv = false; + } + + $this->filename = $file; + if ($encoding) { + $this->encoding = $encoding; + } + else { + $this->encoding = Config::get('site_charset'); + } + + /* These are needed for the filename mojo */ + $this->_file_pattern = $file_pattern; + $this->_dir_pattern = $dir_pattern; + + if(strtoupper(substr(PHP_OS,0,3)) == 'WIN') { + $this->_pathinfo = str_replace('%3A', ':', urlencode($this->filename)); + $this->_pathinfo = pathinfo(str_replace('%5C', '\\', $this->_pathinfo)); + } + else { + $this->_pathinfo = pathinfo(str_replace('%2F', '/', urlencode($this->filename))); + } + $this->_pathinfo['extension'] = strtolower($this->_pathinfo['extension']); + + // Initialize getID3 engine + $this->_getID3 = new getID3(); + + $this->_getID3->option_md5_data = false; + $this->_getID3->option_md5_data_source = false; + $this->_getID3->option_tags_html = false; + $this->_getID3->option_extra_info = true; + $this->_getID3->option_tag_lyrics3 = true; + $this->_getID3->option_tags_process = true; + $this->_getID3->encoding = $this->encoding; + + // get id3tag encoding (try to work around off-spec id3v1 tags) + try { + $this->_raw = $this->_getID3->analyze($file); + } + catch (Exception $error) { + debug_event('Getid3()',"Broken file detected $file - " . $error->message,'1'); + $this->_broken = true; + return false; + } + + /* Use default mb_detect_order in php.ini or not */ + if (Config::get('mb_detect_order')) { + $mb_order = Config::get('mb_detect_order'); + } + elseif (function_exists('mb_detect_order')) { + $mb_order = implode(", ", mb_detect_order()); + } + else { + $mb_order = "auto"; + } + + $test_tags = array('artist', 'album', 'genre', 'title'); + + if ($encoding_id3v1) { + $this->encoding_id3v1 = $encoding_id3v1; + } + else { + foreach ($test_tags as $tag) { + if ($value = $this->_raw['id3v1'][$tag]) { + $tags[$tag] = $value; + } + } + + $this->encoding_id3v1 = self::_detect_encoding($tags, $mb_order); + } + + if (Config::get('getid3_detect_id3v2_encoding')) { + foreach ($test_tags as $tag) { + if ($value = $this->_raw['id3v2']['comments'][$tag]) { + $tags[$tag] = $value; + } + } + + $this->encoding_id3v2 = self::_detect_encoding($tags, $mb_order); + $this->_getID3->encoding_id3v2 = $this->encoding_id3v2; + } + + $this->_getID3->encoding_id3v1 = $this->encoding_id3v1; + + } // vainfo + + /** + * _detect_encoding + * Takes an array of tags and attempts to automatically detect their + * encoding. + */ + private static function _detect_encoding($tags, $mb_order) { + if (function_exists('mb_detect_encoding')) { + $encodings = array(); + $tags = array('artist', 'album', 'genre', 'title'); + foreach ($tags as $tag) { + $encodings[mb_detect_encoding($tag, $mb_order, true)]++; + } + + debug_event('vainfo', 'encoding detection: ' . json_encode($encodings), 5); + $high = 0; + foreach ($encodings as $key => $value) { + if ($value > $high) { + $encoding = $key; + $high = $value; + } + } + + if ($encoding != 'ASCII' && $encoding != '0') { + return $encoding; + } + else { + return 'ISO-8859-1'; + } + } + return 'ISO-8859-1'; + } + + + /** + * get_info + * This function runs the various steps to gathering the metadata + */ + public function get_info() { + + // If this is broken, don't waste time figuring it out a second + // time, just return their rotting carcass of a media file. + if ($this->_broken) { + $this->tags = $this->set_broken(); + return true; + } + + /* Get the Raw file information */ + try { + $this->_raw = $this->_getID3->analyze($this->filename); + } + catch (Exception $error) { + debug_event('Getid3()',"Unable to catalog file:" . $error->message,'1'); + } + + /* Figure out what type of file we are dealing with */ + $this->type = $this->_get_type(); + + $enabled_sources = (array)Config::get('metadata_order'); + + if (in_array('filename', $enabled_sources)) { + $this->tags['filename'] = $this->_parse_filename($this->filename); + } + + if (in_array('getID3', $enabled_sources)) { + $this->tags['getID3'] = $this->_get_tags(); + } + + $this->_get_plugin_tags(); + + } // get_info + + /** + * get_tag_type + * This takes the result set and the tag_order defined in your config + * file and tries to figure out which tag type(s) it should use. If your + * tag_order doesn't match anything then it throws up its hands and uses + * everything. + */ + public static function get_tag_type($results, $config_key = 'metadata_order') { + + $order = (array)Config::get($config_key); + + /* Iterate through the defined key order adding them to an + * ordered array as we go. + */ + + foreach($order as $key) { + if ($results[$key]) { + $returned_keys[] = $key; + } + } + + /* If we didn't find anything then default to everything. + */ + if (!isset($returned_keys)) { + $returned_keys = array_keys($results); + $returned_keys = sort($returned_keys); + } + + return $returned_keys; + + } // get_tag_type + + /** + * clean_tag_info + * This function takes the array from vainfo along with the + * key we've decided on and the filename and returns it in a + * sanitized format that ampache can actually use + */ + public static function clean_tag_info($results, $keys, $filename = null) { + + $info = array(); + + if ($filename) { + $info['file'] = $filename; + } + + // Iteration! + foreach ($keys as $key) { + $tags = $results[$key]; + + $info['file'] = $info['file'] ?: $tags['file']; + $info['bitrate'] = $info['bitrate'] ?: intval($tags['bitrate']); + $info['rate'] = $info['rate'] ?: intval($tags['rate']); + $info['mode'] = $info['mode'] ?: $tags['mode']; + $info['size'] = $info['size'] ?: $tags['size']; + $info['mime'] = $info['mime'] ?: $tags['mime']; + $info['encoding'] = $info['encoding'] ?: $tags['encoding']; + $info['rating'] = $info['rating'] ?: $tags['rating']; + $info['time'] = $info['time'] ?: intval($tags['time']); + $info['channels'] = $info['channels'] ?: $tags['channels']; + + /* These are used to generate the correct IDs later */ + $info['title'] = $info['title'] ?: stripslashes(trim($tags['title'])); + + $info['year'] = $info['year'] ?: intval($tags['year']); + + $info['disk'] = $info['disk'] ?: intval($tags['disk']); + + $info['totaldiscs'] = $info['totaldiscs'] ?: intval($tags['totaldiscs']); + + $info['artist'] = $info['artist'] ?: trim($tags['artist']); + + $info['album'] = $info['album'] ?: trim($tags['album']); + + // multiple genre support + if ((!$info['genre']) && $tags['genre']) { + if (!is_array($tags['genre'])) { + // not all tag formats will return an array, but we need one + $info['genre'][] = trim($tags['genre']); + } + else { + // if we trim the array we lose everything after 1st entry + foreach ($tags['genre'] as $genre) { + $info['genre'][] = trim($genre); + } + } + } + + $info['mb_trackid'] = $info['mb_trackid'] ?: trim($tags['mb_trackid']); + $info['mb_albumid'] = $info['mb_albumid'] ?: trim($tags['mb_albumid']); + $info['mb_artistid'] = $info['mb_artistid'] ?: trim($tags['mb_artistid']); + + /* @TODO language doesn't import from id3tag. @momo-i */ + $info['language'] = $info['language'] ?: Dba::escape($tags['language']); + + $info['lyrics'] = $info['lyrics'] + ?: str_replace( + array("\r\n","\r","\n"), + '<br />', + strip_tags($tags['lyrics'])); + + $info['track'] = $info['track'] ?: intval($tags['track']); + $info['resolution_x'] = $info['resolution_x'] ?: intval($tags['resolution_x']); + $info['resolution_y'] = $info['resolution_y'] ?: intval($tags['resolution_y']); + $info['audio_codec'] = $info['audio_codec'] ?: Dba::escape($tags['audio_codec']); + $info['video_codec'] = $info['video_codec'] ?: Dba::escape($tags['video_codec']); + } + + if ($info['totaldiscs'] == 1 && $info['disk'] == 1) { + unset($info['disk']); + unset($info['totaldiscs']); + } + + return $info; + + } // clean_tag_info + + /** + * _get_type + * This function takes the raw information and figures out + * what type of file we are dealing with for use by the tag + * function + */ + public function _get_type() { + + /* There are a few places that the file type can + * come from, in the end we trust the encoding + * type + */ + if ($type = $this->_raw['video']['dataformat']) { + $type = $this->_clean_type($type); + return $type; + } + if ($type = $this->_raw['audio']['streams']['0']['dataformat']) { + $type = $this->_clean_type($type); + return $type; + } + if ($type = $this->_raw['audio']['dataformat']) { + $type = $this->_clean_type($type); + return $type; + } + if ($type = $this->_raw['fileformat']) { + $type = $this->_clean_type($type); + return $type; + } + + return false; + + } // _get_type + + + /** + * _get_tags + * This function takes the raw information and the type and + * attempts to gather the tags and then normalize them into + * ['tag_name']['var'] = value + */ + public function _get_tags() { + + $results = array(); + + /* The tags can come in many different shapes and colors + * depending on the encoding time of day and phase of the moon. + */ + foreach ($this->_raw['tags'] as $key => $tag_array) { + switch ($key) { + case 'vorbiscomment': + debug_event('_get_tags', 'Parsing vorbis', '5'); + $results[$key] = $this->_parse_vorbiscomment($tag_array); + break; + case 'id3v1': + debug_event('_get_tags', 'Parsing id3v1', '5'); + $results[$key] = $this->_parse_id3v1($tag_array); + break; + case 'id3v2': + debug_event('_get_tags', 'Parsing id3v2', '5'); + $results[$key] = $this->_parse_id3v2($tag_array); + break; + case 'ape': + debug_event('_get_tags', 'Parsing ape', '5'); + $results[$key] = $this->_parse_ape($tag_array); + break; + case 'quicktime': + debug_event('_get_tags', 'Parsing quicktime', '5'); + $results[$key] = $this->_parse_quicktime($tag_array); + break; + case 'riff': + debug_event('_get_tags', 'Parsing riff', '5'); + $results[$key] = $this->_parse_riff($tag_array); + break; + case 'flv': + debug_event('_get_tags', 'Parsing flv', '5'); + $results[$key] = $this->_parse_flv($this->_raw); + break; + case 'mpg': + case 'mpeg': + debug_event('_get_tags', 'Parsing MPEG', '5'); + $results['mpeg'] = $this->_parse_mpg($this->_raw); + break; + case 'asf': + case 'wmv': + debug_event('_get_tags', 'Parsing WMV/WMA/ASF', '5'); + $results['asf'] = $this->_parse_wmv($this->_raw); + break; + case 'avi': + debug_event('_get_tags', 'Parsing avi', '5'); + $results[$key] = $this->_parse_avi($this->_raw); + break; + case 'lyrics3': + debug_event('_get_tags', 'Parsing lyrics3', '5'); + $results[$key] = $this->_parse_lyrics($tag_array); + break; + default: + debug_event('vainfo','Error: Unable to determine tag type of ' . $key . ' for file ' . $this->filename . ' Assuming id3v2','5'); + $results[$key] = $this->_parse_id3v2($this->_raw['id3v2']['comments']); + break; + } // end switch + } // end foreach + + + $cleaned = self::clean_tag_info($results, self::get_tag_type($results, 'getid3_tag_order'), $this->filename); + $cleaned = array_merge($cleaned, $this->_get_info()); + $cleaned['raw'] = $results; + + return $cleaned; + + } // _get_tags + + /** + * _get_plugin_tags + * Get additional metadata from plugins + */ + private function _get_plugin_tags() { + $tag_order = Config::get('metadata_order'); + if (!is_array($tag_order)) { + $tag_order = array($tag_order); + } + + $plugin_names = Plugin::get_plugins('get_metadata'); + foreach ($tag_order as $key => $tag_source) { + if (in_array($tag_source, $plugin_names)) { + $plugin = new Plugin($tag_source); + if ($plugin->load()) { + $this->tags[$tag_source] = $plugin->_plugin->get_metadata(self::clean_tag_info($this->tags, self::get_tag_type($this->tags), $this->filename)); + } + } + } + } // _get_plugin_tags + + /** + * _get_info + * Gather and return the general information about a song (vbr/cbr, + * sample rate, channels, etc.) + */ + private function _get_info() { + + $array = array(); + + /* Try to pull the information directly from + * the audio array + */ + if ($this->_raw['audio']['bitrate_mode']) { + $array['mode'] = $this->_raw['audio']['bitrate_mode']; + if ($array['mode'] == 'con') { + $array['mode'] = 'cbr'; + } + } + if ($this->_raw['audio']['bitrate']) { + $array['bitrate'] = $this->_raw['audio']['bitrate']; + } + if ($this->_raw['audio']['channels']) { + $array['channels'] = intval($this->_raw['audio']['channels']); + } + if ($this->_raw['audio']['sample_rate']) { + $array['rate'] = intval($this->_raw['audio']['sample_rate']); + } + if ($this->_raw['filesize']) { + $array['size'] = intval($this->_raw['filesize']); + } + if ($this->_raw['encoding']) { + $array['encoding'] = $this->_raw['encoding']; + } + if ($this->_raw['mime_type']) { + $array['mime'] = $this->_raw['mime_type']; + } + if ($this->_raw['playtime_seconds']) { + $array['time'] = $this->_raw['playtime_seconds']; + } + + return $array; + + } // _get_info + + /** + * _clean_type + * This standardizes the type that we are given into a recognized type. + */ + private function _clean_type($type) { + + switch ($type) { + case 'mp3': + case 'mp2': + case 'mpeg3': + return 'mp3'; + break; + case 'vorbis': + return 'ogg'; + break; + case 'flac': + case 'flv': + case 'mpg': + case 'mpeg': + case 'asf': + case 'wmv': + case 'avi': + case 'quicktime': + return $type; + default: + /* Log the fact that we couldn't figure it out */ + debug_event('vainfo','Unable to determine file type from ' . $type . ' on file ' . $this->filename,'5'); + return $type; + break; + } // end switch on type + + } // _clean_type + + /** + * _parse_lyrics + * This function takes a lyrics3 from getid3() + * nothing to do? + */ + private function _parse_lyrics($tags) { + + /* Results array */ + $array = array(); + + /* go through them all! */ + foreach ($tags as $tag => $data) { + if ($tag == 'unsynchedlyrics' || $tag == 'unsynchronised lyric') { + $tag = 'lyrics'; + } + $array[$tag] = $this->_clean_tag($data['0']); + + } // end foreach + + return $array; + + } // _parse_lyrics + + /** + * _parse_vorbiscomment + * This function takes a vorbiscomment from getid3() and then + * returns the elements translated using iconv if needed in a + * pretty little format + */ + private function _parse_vorbiscomment($tags) { + + /* Results array */ + $array = array(); + + /* go through them all! */ + foreach ($tags as $tag=>$data) { + + /* We need to translate a few of these tags */ + switch ($tag) { + + case 'genre': + // multiple genre support + foreach($data as $foo) { + $array['genre'][] = $this->_clean_tag($foo,''); + } + break; + case 'tracknumber': + $array['track'] = $this->_clean_tag($data['0']); + break; + case 'discnumber': + $el = explode('/', $data['0']); + $array['disk'] = $el[0]; + break; + case 'date': + $array['year'] = $this->_clean_tag($data['0']); + break; + default: + $array[$tag] = $this->_clean_tag($data['0']); + break; + } // end switch + + } // end foreach + + return $array; + + } // _parse_vorbiscomment + + /** + * _parse_id3v1 + * This function takes an id3v1 tag set from getid3() and then + * returns the elements (translated using iconv if needed) in a + * pretty little format. + */ + private function _parse_id3v1($tags) { + + $array = array(); + + /* Go through all the tags */ + foreach ($tags as $tag=>$data) { + + /* This is our baseline for naming + * so no translation needed + */ + $array[$tag] = $this->_clean_tag($data['0']); + + } // end foreach + + return $array; + + } // _parse_id3v1 + + /** + * _parse_id3v2 + * This function takes an id3v2 tag set from getid3() and then + * returns the elements (translated using iconv if needed) in a + * pretty little format. + */ + private function _parse_id3v2($tags) { + + $array = array(); + + /* Go through the tags */ + foreach ($tags as $tag=>$data) { + + /** + * the new getid3 handles this differently + * so we now need to account for it :( + */ + switch ($tag) { + case 'genre': + // multiple genre support + foreach($data as $genre) { + $array['genre'][] = $this->_clean_tag($genre); + } + break; + case 'part_of_a_set': + $el = explode('/', $data['0']); + $array['disk'] = $el[0]; + $array['totaldiscs'] = $el[1]; + break; + case 'track_number': + $array['track'] = $this->_clean_tag($data['0']); + break; + case 'comments': + $array['comment'] = $this->_clean_tag($data['0']); + break; + default: + $array[$tag] = $this->_clean_tag($data['0']); + break; + } // end switch on tag + + } // end foreach + + $id3v2 = $this->_raw['id3v2']; + + if(!empty($id3v2['UFID'])) { + // Find the MBID for the track + foreach ($id3v2['UFID'] as $ufid) { + if ($ufid['ownerid'] == 'http://musicbrainz.org') { + $array['mb_trackid'] = $this->_clean_tag($ufid['data']); + } + } + + // Find the MBIDs for the album and artist + foreach ($id3v2['TXXX'] as $txxx) { + switch ($txxx['description']) { + case 'MusicBrainz Album Id': + $array['mb_albumid'] = $this->_clean_tag($txxx['data']); + break; + case 'MusicBrainz Artist Id': + $array['mb_artistid'] = $this->_clean_tag($txxx['data']); + break; + } + } + } + + // Find the rating + if (is_array($id3v2['POPM'])) { + foreach ($id3v2['POPM'] as $popm) { + if (array_key_exists('email', $popm) && + $user = User::get_from_email($popm['email'])) { + // Ratings are out of 255; scale it + $array['rating'][$user->id] = $popm['rating'] / 255 * 5; + } + } + } + + return $array; + + } // _parse_id3v2 + + /** + * _parse_ape + * This function takes ape tags set by getid3() and then + * returns the elements translated using iconv if needed in a + * pretty little format + */ + private function _parse_ape($tags) { + + foreach ($tags as $tag=>$data) { + switch ($tag) { + + case 'genre': + // multiple genre support + foreach($data as $genre) { + $array['genre'][] = $this->_clean_tag($genre); + } + break; + + default: + $array[$tag] = $this->_clean_tag($data['0'], $this->_file_encoding); + break; + } // end switch on tag + + } // end foreach tags + + return $array; + + } // _parse_ape + + /** + * _parse_riff + * this function takes the riff take information passed by getid3() and + * then reformats it so that it matches the other formats. May require iconv + */ + private function _parse_riff($tags) { + + foreach ($tags as $tag=>$data) { + + switch ($tag) { + case 'product': + $array['album'] = $this->_clean_tag($data['0'], $this->_file_encoding); + break; + default: + $array[$tag] = $this->_clean_tag($data['0'], $this->_file_encoding); + break; + } // end switch on tag + + } // foreach tags + + return $array; + + } // _parse_riff + + /** + * _parse_quicktime + * this function takes the quicktime tags set by getid3() and then + * returns the elements translated using iconv if needed in a + * pretty little format + */ + private function _parse_quicktime($tags) { + + /* Results array */ + $array = array(); + + /* go through them all! */ + foreach ($tags as $tag=>$data) { + + /* We need to translate a few of these tags */ + switch ($tag) { + case 'creation_date': + if (strlen($data['0']) > 4) { + /* Weird Date format, attempt to normalize */ + $data['0'] = date("Y",strtotime($data['0'])); + } + $array['year'] = $this->_clean_tag($data['0']); + break; + case 'MusicBrainz Track Id': + $array['mb_trackid'] = $this->_clean_tag($data['0']); + break; + case 'MusicBrainz Album Id': + $array['mb_albumid'] = $this->_clean_tag($data['0']); + break; + case 'MusicBrainz Artist Id': + $array['mb_artistid'] = $this->_clean_tag($data['0']); + break; + } // end switch + + $array[$tag] = $this->_clean_tag($data['0']); + + } // end foreach + + // Also add in any video related stuff we might find + if (strpos($this->_raw['mime_type'], 'video') !== false) { + $info = $this->_parse_avi($this->_raw); + $info['video_codec'] = $this->_raw['quicktime']['ftyp']['fourcc']; + $array = array_merge($info, $array); + } + + return $array; + + } // _parse_quicktime + + /** + * _parse_avi + * This attempts to parse our the information on an avi file and present it in some + * kind of sane format, this is a little hard as these files don't have tags + */ + private function _parse_avi($tags) { + + $array = array(); + + $array['title'] = urldecode($this->_pathinfo['filename']); + $array['video_codec'] = $tags['video']['fourcc']; + $array['audio_codec'] = $tags['audio']['dataformat']; + $array['resolution_x'] = $tags['video']['resolution_x']; + $array['resolution_y'] = $tags['video']['resolution_y']; + $array['mime'] = $tags['mime_type']; + $array['comment'] = $tags['video']['codec']; + + return $array; + + } // _parse_avi + + /** + * _parse_mpg + * This attempts to parse our the information on a mpg file and present it in some + * kind of sane format, this is a little hard as these files don't have tags + */ + private function _parse_mpg($tags) { + + $array = array(); + + $array['title'] = urldecode($this->_pathinfo['filename']); + $array['video_codec'] = $tags['video']['codec']; + $array['audio_codec'] = $tags['audio']['dataformat']; + $array['resolution_x'] = $tags['video']['resolution_x']; + $array['resolution_y'] = $tags['video']['resolution_y']; + $array['mime'] = $tags['mime_type']; + $array['comment'] = $tags['video']['codec']; + + return $array; + + } // _parse_mpg + + /** + * _parse_wmv + * This attempts to parse our the information on a asf/wmv file and present it in some + * kind of sane format, this is a little hard as these files don't have tags + */ + private function _parse_wmv($tags) { + + $array = array(); + + $array['mime'] = $tags['mime_type']; + + switch($array['mime']) { + default: + case 'video/x-ms-wmv': + if(isset($tags['tags']['asf']['title']['0'])) { + $array['title'] = $tags['tags']['asf']['title']['0']; + } + else { + $array['title'] = urldecode($this->_pathinfo['filename']); + } + $array['video_codec'] = $tags['video']['streams']['2']['codec']; + $array['audio_codec'] = $tags['audio']['streams']['1']['codec']; + $array['resolution_x'] = $tags['video']['streams']['2']['resolution_x']; + $array['resolution_y'] = $tags['video']['streams']['2']['resolution_y']; + $array['comment'] = $tags['tags']['asf']['title']['1']; + break; + } + + foreach($tags['tags']['asf'] as $tag => $data) { + $array[$tag] = $this->_clean_tag($data['0']); + } + + return $array; + + } // _parse_wmv + + /** + * _parse_flv + * This attempts to parse our the information on an flv file and present it in some + * kind of sane format, this is a little hard as these files don't have tags + */ + private function _parse_flv($tags) { + + $array = array(); + + $array['title'] = urldecode($this->_pathinfo['filename']); + $array['video_codec'] = $tags['video']['codec']; + $array['audio_codec'] = $tags['audio']['dataformat']; + $array['resolution_x'] = $tags['video']['resolution_x']; + $array['resolution_y'] = $tags['video']['resolution_y']; + $array['mime'] = $tags['mime_type']; + $array['comment'] = $tags['video']['codec']; + + return $array; + + } // _parse_flv + + /** + * _parse_filename + * + * This function uses the passed file and dir patterns + * To pull out extra tag information and populate it into + * its own array + * + * @param string $filename Filename that want to parse + * @return array Parsed results + */ + private function _parse_filename($filename) { + + $results = array(); + + // Correctly detect the slash we need to use here + if (strpos($filename, '/') !== false) { + $slash_type = '/'; + } + else { + $slash_type = '\\'; + } + + // Combine the patterns + $pattern = preg_quote($this->_dir_pattern) . $slash_type . preg_quote($this->_file_pattern); + + // Pull out the pattern codes into an array + preg_match_all('/\%\w/', $pattern, $elements); + + // Mangle the pattern by turning the codes into regex captures + $pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern); + $pattern = preg_replace('/\%\w/', '(.+?)', $pattern); + $pattern = str_replace('/', '\/', $pattern); + $pattern = str_replace(' ', '\s', $pattern); + $pattern = '/' . $pattern . '\..+$/'; + + // Pull out our actual matches + preg_match($pattern, $filename, $matches); + + // The first element is the full match text + $matched = array_shift($matches); + debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filename, 5); + + // Iterate over what we found + foreach ($matches as $key => $value) { + $new_key = translate_pattern_code($elements['0'][$key]); + if ($new_key) { + $results[$new_key] = $value; + } + } + + $results['size'] = filesize($filename); + + return $results; + + } // _parse_filename + + /** + * _id3v2_tag_to_frame + * + * This translates the tag name to a frame, if there a many it returns the first + * one if finds that exists in the raw + * + * @param string $tag_name Tag name + * @return mixed If found id3v2 frame, return frame. If not found, return false. + */ + private function _id3v2_tag_to_frame($tag_name) { + + static $map = array( + 'comment'=>array('COM','COMM'), + 'cd_ident'=>array('MCDI','MCI'), + 'album'=>array('TAL','TALB'), + 'language'=>array('TLA','TLAN'), + 'mood'=>array('TMOO'), + 'artist'=>array('TPE1'), + 'year'=>array('TDRC')); + + foreach ($map[$tag_name] as $frame) { + if (isset($this->_raw['id3v2'][$frame])) { + return $frame; + } + } + + return false; + + } // _id3v2_tag_to_frame + + /** + * _clean_tag + * + * This function cleans up the tag that it's passed using Iconv + * if we've got it. It also takes an optional encoding param + * for the cases where we know what the tags source encoding + * is, and or if it's different then the encoding recorded + * in the file + * + * @param string $tag Encoding string + * @param string $encoding Encode charset + * @return string Return encoded string + */ + private function _clean_tag($tag, $encoding = null) { + + // Default to getID3's native encoding + if (!$encoding) { + $encoding = $this->_getID3->encoding; + } + // If we've got iconv then go ahead and clear her up + if ($encoding == $this->encoding) { + debug_event('vainfo', "\$encoding -> ${encoding}, \$this->encoding -> {$this->encoding}", 5); + return $tag; + } + if ($this->_iconv) { + debug_event('vainfo', 'Use iconv()',5); + + // Try GNU iconv //TRANSLIT extension first + $new_encoding = $this->encoding . '//TRANSLIT'; + $clean = iconv($encoding, $new_encoding, $tag); + + // If that fails, do a plain conversion + if(strcmp($clean, '') == 0) { + $clean = iconv($encoding, $this->encoding, $tag); + } + } + elseif (function_exists('mb_convert_encoding')) { + debug_event('vainfo', 'Use mbstring',5); + debug_event('vainfo', "Try to convert from {$this->encoding} to $encoding", 5); + $clean = mb_convert_encoding($tag, $encoding, $this->encoding); + } + else { + $clean = $tag; + } + + return $clean; + + } // _clean_tag + + /** + * set_broken + * + * This fills all tag types with Unknown (Broken) + * + * @return array Return broken title, album, artist + */ + public function set_broken() { + + /* Pull In the config option */ + $order = Config::get('tag_order'); + + if (!is_array($order)) { + $order = array($order); + } + + $key = array_shift($order); + + $broken[$key]['title'] = '**BROKEN** ' . $this->filename; + $broken[$key]['album'] = 'Unknown (Broken)'; + $broken[$key]['artist'] = 'Unknown (Broken)'; + + return $broken; + + } // set_broken } // end class vainfo ?> |