diff --git a/app/Library/getid3/.gitattributes b/app/Library/getid3/.gitattributes old mode 100755 new mode 100644 diff --git a/app/Library/getid3/.gitignore b/app/Library/getid3/.gitignore old mode 100755 new mode 100644 index ecc47c4f..d4575fce --- a/app/Library/getid3/.gitignore +++ b/app/Library/getid3/.gitignore @@ -1,4 +1,4 @@ -helperapps/sha1sum.exe +helperapps/*.exe helperapps/*.dll @@ -110,7 +110,9 @@ ClientBin stylecop.* ~$* *.dbmdl -Generated_Code #added for RIA/Silverlight projects + +#added for RIA/Silverlight projects +Generated_Code # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) @@ -165,3 +167,4 @@ pip-log.txt # Mac crap .DS_Store +demos/php_error.log diff --git a/app/Library/getid3/README.md b/app/Library/getid3/README.md old mode 100755 new mode 100644 index c7e7522a..da7e7923 --- a/app/Library/getid3/README.md +++ b/app/Library/getid3/README.md @@ -186,7 +186,7 @@ if ($fp_remote = fopen($remotefilename, 'rb')) { fclose($fp_local); // Initialize getID3 engine $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($filename); + $ThisFileInfo = $getID3->analyze($localtempfilename); // Delete temporary file unlink($localtempfilename); } diff --git a/app/Library/getid3/changelog.txt b/app/Library/getid3/changelog.txt old mode 100755 new mode 100644 index 1bd712dd..2c0cca62 --- a/app/Library/getid3/changelog.txt +++ b/app/Library/getid3/changelog.txt @@ -18,6 +18,17 @@ Version History =============== +1.9.10: [2015-09-14] James Heinrich + * bugfix (G:49): Declaration of getID3_cached_sqlite3 + * bugfix (#1892): extension.cache.mysql + * bugfix (#1891): duplicate default clause [Quicktime] + * bugfix (G:41): incorrect MP3 playtime + * bugfix: iconv problems on musl with //TRANSLIT + * Add arguments to analyze() for original filesize (and filename) + * ID3v2 simplify handling of multiple genres + * Corrected merging of multiple genres for ID3v2 + * getid3_lib::GetDataImageSize return false on error + 1.9.9: [2014-12-18] James Heinrich » Added basic support for OggOpus » Add ID3v2 CHAP + CTOC support diff --git a/app/Library/getid3/composer.json b/app/Library/getid3/composer.json old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.audioinfo.class.php b/app/Library/getid3/demos/demo.audioinfo.class.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.basic.php b/app/Library/getid3/demos/demo.basic.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.browse.php b/app/Library/getid3/demos/demo.browse.php old mode 100755 new mode 100644 index 65be568a..cde883aa --- a/app/Library/getid3/demos/demo.browse.php +++ b/app/Library/getid3/demos/demo.browse.php @@ -480,8 +480,11 @@ function table_var_dump($variable, $wrap_in_td=false, $encoding='ISO-8859-1') { //if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) { if (($key == 'data') && isset($variable['image_mime'])) { $imageinfo = array(); - $imagechunkcheck = getid3_lib::GetDataImageSize($value, $imageinfo); - $returnstring .= ''."\n".''."\n"; + if ($imagechunkcheck = getid3_lib::GetDataImageSize($value, $imageinfo)) { + $returnstring .= ''."\n".''."\n"; + } else { + $returnstring .= ''."\n".'invalid image data'."\n"; + } } else { $returnstring .= ''."\n".table_var_dump($value, true, $encoding).''."\n"; } @@ -515,8 +518,7 @@ function table_var_dump($variable, $wrap_in_td=false, $encoding='ISO-8859-1') { default: $imageinfo = array(); - $imagechunkcheck = getid3_lib::GetDataImageSize($variable, $imageinfo); - if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + if (($imagechunkcheck = getid3_lib::GetDataImageSize($variable, $imageinfo)) && ($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { $returnstring .= ($wrap_in_td ? '' : ''); $returnstring .= ''; $returnstring .= ''."\n"; diff --git a/app/Library/getid3/demos/demo.cache.dbm.php b/app/Library/getid3/demos/demo.cache.dbm.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.cache.mysql.php b/app/Library/getid3/demos/demo.cache.mysql.php old mode 100755 new mode 100644 index 037a7e27..e2ca9c9e --- a/app/Library/getid3/demos/demo.cache.mysql.php +++ b/app/Library/getid3/demos/demo.cache.mysql.php @@ -17,6 +17,7 @@ die('Due to a security issue, this demo has been disabled. It can be enabled by require_once('../getid3/getid3.php'); +require_once('../getid3/getid3.lib.php'); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.mysql.php', __FILE__, true); $getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password'); diff --git a/app/Library/getid3/demos/demo.joinmp3.php b/app/Library/getid3/demos/demo.joinmp3.php old mode 100755 new mode 100644 index 215af94f..b819e930 --- a/app/Library/getid3/demos/demo.joinmp3.php +++ b/app/Library/getid3/demos/demo.joinmp3.php @@ -9,37 +9,47 @@ // /demo/demo.joinmp3.php - part of getID3() // // Sample script for splicing two or more MP3s together into // // one file. Does not attempt to fix VBR header frames. // +// Can also be used to extract portion from single file. // // See readme.txt for more details // // /// ///////////////////////////////////////////////////////////////// // sample usage: -// $FilenameOut = 'combined.mp3'; -// $FilenamesIn[] = 'file1.mp3'; -// $FilenamesIn[] = 'file2.mp3'; -// $FilenamesIn[] = 'file3.mp3'; +// $FilenameOut = 'combined.mp3'; +// $FilenamesIn[] = 'first.mp3'; // filename with no start/length parameters +// $FilenamesIn[] = array('second.mp3', 0, 0); // filename with zero for start/length is the same as not specified (start = beginning, length = full duration) +// $FilenamesIn[] = array('third.mp3', 0, 10); // extract first 10 seconds of audio +// $FilenamesIn[] = array('fourth.mp3', -10, 0); // extract last 10 seconds of audio +// $FilenamesIn[] = array('fifth.mp3', 10, 0); // extract everything except first 10 seconds of audio +// $FilenamesIn[] = array('sixth.mp3', 0, -10); // extract everything except last 10 seconds of audio +// if (CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)) { +// echo 'Successfully copied '.implode(' + ', $FilenamesIn).' to '.$FilenameOut; +// } else { +// echo 'Failed to copy '.implode(' + ', $FilenamesIn).' to '.$FilenameOut; +// } // -// if (CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)) { -// echo 'Successfully copied '.implode(' + ', $FilenamesIn).' to '.$FilenameOut; -// } else { -// echo 'Failed to copy '.implode(' + ', $FilenamesIn).' to '.$FilenameOut; -// } +// Could also be called like this to extract portion from single file: +// CombineMultipleMP3sTo('sample.mp3', array(array('input.mp3', 0, 30))); // extract first 30 seconds of audio + function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) { foreach ($FilenamesIn as $nextinputfilename) { + if (is_array($nextinputfilename)) { + $nextinputfilename = $nextinputfilename[0]; + } if (!is_readable($nextinputfilename)) { echo 'Cannot read "'.$nextinputfilename.'"
'; return false; } } - if (!is_writeable($FilenameOut)) { + if ((file_exists($FilenameOut) && !is_writeable($FilenameOut)) || (!file_exists($FilenameOut) && !is_writeable(dirname($FilenameOut)))) { echo 'Cannot write "'.$FilenameOut.'"
'; return false; } - require_once('../getid3/getid3.php'); + require_once(dirname(__FILE__).'/../getid3/getid3.php'); ob_start(); if ($fp_output = fopen($FilenameOut, 'wb')) { @@ -47,7 +57,11 @@ function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) { // Initialize getID3 engine $getID3 = new getID3; foreach ($FilenamesIn as $nextinputfilename) { - + $startoffset = 0; + $length_seconds = 0; + if (is_array($nextinputfilename)) { + @list($nextinputfilename, $startoffset, $length_seconds) = $nextinputfilename; + } $CurrentFileInfo = $getID3->analyze($nextinputfilename); if ($CurrentFileInfo['fileformat'] == 'mp3') { @@ -58,17 +72,35 @@ function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) { $CurrentOutputPosition = ftell($fp_output); // copy audio data from first file - fseek($fp_source, $CurrentFileInfo['avdataoffset'], SEEK_SET); - while (!feof($fp_source) && (ftell($fp_source) < $CurrentFileInfo['avdataend'])) { - fwrite($fp_output, fread($fp_source, 32768)); + $start_offset_bytes = $CurrentFileInfo['avdataoffset']; + if ($startoffset > 0) { // start X seconds from start of audio + $start_offset_bytes = $CurrentFileInfo['avdataoffset'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset); + } elseif ($startoffset < 0) { // start X seconds from end of audio + $start_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset); + } + $start_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $start_offset_bytes)); + + $end_offset_bytes = $CurrentFileInfo['avdataend']; + if ($length_seconds > 0) { // seconds from start of audio + $end_offset_bytes = $start_offset_bytes + round($CurrentFileInfo['bitrate'] / 8 * $length_seconds); + } elseif ($length_seconds < 0) { // seconds from start of audio + $end_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset); + } + $end_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $end_offset_bytes)); + + if ($end_offset_bytes <= $start_offset_bytes) { + echo 'failed to copy '.$nextinputfilename.' from '.$startoffset.'-seconds start for '.$length_seconds.'-seconds length (not enough data)'; + fclose($fp_source); + fclose($fp_output); + return false; + } + + fseek($fp_source, $start_offset_bytes, SEEK_SET); + while (!feof($fp_source) && (ftell($fp_source) < $end_offset_bytes)) { + fwrite($fp_output, fread($fp_source, min(32768, $end_offset_bytes - ftell($fp_source)))); } fclose($fp_source); - // trim post-audio data (if any) copied from first file that we don't need or want - $EndOfFileOffset = $CurrentOutputPosition + ($CurrentFileInfo['avdataend'] - $CurrentFileInfo['avdataoffset']); - fseek($fp_output, $EndOfFileOffset, SEEK_SET); - ftruncate($fp_output, $EndOfFileOffset); - } else { $errormessage = ob_get_contents(); diff --git a/app/Library/getid3/demos/demo.mimeonly.php b/app/Library/getid3/demos/demo.mimeonly.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.mp3header.php b/app/Library/getid3/demos/demo.mp3header.php old mode 100755 new mode 100644 index 846db679..ad5a3d8a --- a/app/Library/getid3/demos/demo.mp3header.php +++ b/app/Library/getid3/demos/demo.mp3header.php @@ -29,10 +29,10 @@ if (!function_exists('table_var_dump')) { $returnstring = ''; switch (gettype($variable)) { case 'array': - $returnstring .= '
type'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]).'
'; + $returnstring .= '
'; foreach ($variable as $key => $value) { - $returnstring .= ''; - $returnstring .= ''; + $returnstring .= ''; + } else { + $returnstring .= ''; } - $returnstring .= ''; } else { - $returnstring .= ''; + $returnstring .= ''; } } - $returnstring .= '
'.str_replace(chr(0), ' ', $key).''.gettype($value); + $returnstring .= '
'.str_replace(chr(0), ' ', $key).''.gettype($value); if (is_array($value)) { $returnstring .= ' ('.count($value).')'; } elseif (is_string($value)) { @@ -41,18 +41,21 @@ if (!function_exists('table_var_dump')) { if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) { require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php'); $imageinfo = array(); - $imagechunkcheck = GetDataImageSize($value, $imageinfo); - $DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.ImageTypesLookup($imagechunkcheck[2]); - if ($tempimagefile = fopen($DumpedImageSRC, 'wb')) { - fwrite($tempimagefile, $value); - fclose($tempimagefile); + if ($imagechunkcheck = GetDataImageSize($value, $imageinfo)) { + $DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.ImageTypesLookup($imagechunkcheck[2]); + if ($tempimagefile = fopen($DumpedImageSRC, 'wb')) { + fwrite($tempimagefile, $value); + fclose($tempimagefile); + } + $returnstring .= '
invalid image data
'.table_var_dump($value).'
'.table_var_dump($value).'
'; + $returnstring .= ''; break; case 'boolean': @@ -86,9 +89,7 @@ if (!function_exists('table_var_dump')) { default: require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php'); $imageinfo = array(); - $imagechunkcheck = GetDataImageSize(substr($variable, 0, 32768), $imageinfo); - - if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + if (($imagechunkcheck = GetDataImageSize(substr($variable, 0, 32768), $imageinfo)) && ($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { $returnstring .= ''; $returnstring .= ''; $returnstring .= ''; diff --git a/app/Library/getid3/demos/demo.mysql.php b/app/Library/getid3/demos/demo.mysql.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.simple.php b/app/Library/getid3/demos/demo.simple.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.simple.write.php b/app/Library/getid3/demos/demo.simple.write.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.write.php b/app/Library/getid3/demos/demo.write.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/demo.zip.php b/app/Library/getid3/demos/demo.zip.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/getid3.css b/app/Library/getid3/demos/getid3.css old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/getid3.demo.dirscan.php b/app/Library/getid3/demos/getid3.demo.dirscan.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/demos/index.php b/app/Library/getid3/demos/index.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/dependencies.txt b/app/Library/getid3/dependencies.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/extension.cache.dbm.php b/app/Library/getid3/getid3/extension.cache.dbm.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/extension.cache.mysql.php b/app/Library/getid3/getid3/extension.cache.mysql.php old mode 100755 new mode 100644 index 2647584f..a94eb9d9 --- a/app/Library/getid3/getid3/extension.cache.mysql.php +++ b/app/Library/getid3/getid3/extension.cache.mysql.php @@ -134,7 +134,7 @@ class getID3_cached_mysql extends getID3 // public: analyze file - public function analyze($filename) { + public function analyze($filename, $filesize=null, $original_filename='') { if (file_exists($filename)) { @@ -157,7 +157,7 @@ class getID3_cached_mysql extends getID3 } // Miss - $analysis = parent::analyze($filename); + $analysis = parent::analyze($filename, $filesize, $original_filename); // Save result if (file_exists($filename)) { @@ -178,11 +178,11 @@ class getID3_cached_mysql extends getID3 private function create_table($drop=false) { $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` ('; - $SQLquery .= '`filename` VARCHAR(255) NOT NULL DEFAULT \'\''; + $SQLquery .= '`filename` VARCHAR(500) NOT NULL DEFAULT \'\''; $SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\''; - $SQLquery .= ', `value` TEXT NOT NULL'; + $SQLquery .= ', `value` LONGTEXT NOT NULL'; $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM'; $this->cursor = mysql_query($SQLquery, $this->connection); echo mysql_error($this->connection); diff --git a/app/Library/getid3/getid3/extension.cache.sqlite3.php b/app/Library/getid3/getid3/extension.cache.sqlite3.php old mode 100755 new mode 100644 index dd232390..58d4dc88 --- a/app/Library/getid3/getid3/extension.cache.sqlite3.php +++ b/app/Library/getid3/getid3/extension.cache.sqlite3.php @@ -49,20 +49,20 @@ * * sqlite3 table='getid3_cache', hide=false (PHP5) * - -*** database file will be stored in the same directory as this script, -*** webserver must have write access to that directory! -*** set $hide to TRUE to prefix db file with .ht to pervent access from web client -*** this is a default setting in the Apache configuration: - -# The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients. - - - Order allow,deny - Deny from all - Satisfy all - - +* +* *** database file will be stored in the same directory as this script, +* *** webserver must have write access to that directory! +* *** set $hide to TRUE to prefix db file with .ht to pervent access from web client +* *** this is a default setting in the Apache configuration: +* +* The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients. +* +* +* Order allow,deny +* Deny from all +* Satisfy all +* +* ******************************************************************************** * * ------------------------------------------------------------------- @@ -159,13 +159,13 @@ class getID3_cached_sqlite3 extends getID3 { * @param type $filename * @return boolean */ - public function analyze($filename) { + public function analyze($filename, $filesize=null, $original_filename='') { if (!file_exists($filename)) { return false; } // items to track for caching $filetime = filemtime($filename); - $filesize = filesize($filename); + $filesize_real = filesize($filename); // this will be saved for a quick directory lookup of analized files // ... why do 50 seperate sql quries when you can do 1 for the same result $dirname = dirname($filename); @@ -173,25 +173,25 @@ class getID3_cached_sqlite3 extends getID3 { $db = $this->db; $sql = $this->get_id3_data; $stmt = $db->prepare($sql); - $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); - $stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER); - $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); + $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); + $stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER); + $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); $res = $stmt->execute(); list($result) = $res->fetchArray(); if (count($result) > 0 ) { return unserialize(base64_decode($result)); } // if it hasn't been analyzed before, then do it now - $analysis = parent::analyze($filename); + $analysis = parent::analyze($filename, $filesize, $original_filename); // Save result $sql = $this->cache_file; $stmt = $db->prepare($sql); - $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); - $stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT); - $stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER); - $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); - $stmt->bindValue(':atime', time(), SQLITE3_INTEGER); - $stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT); + $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); + $stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT); + $stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER); + $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); + $stmt->bindValue(':atime', time(), SQLITE3_INTEGER); + $stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT); $res = $stmt->execute(); return $analysis; } @@ -253,7 +253,8 @@ class getID3_cached_sqlite3 extends getID3 { return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)"; break; case 'make_table': - return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))"; + //return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))"; + return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))"; break; case 'get_cached_dir': return "SELECT val FROM $this->table WHERE dirname = :dirname"; diff --git a/app/Library/getid3/getid3/getid3.lib.php b/app/Library/getid3/getid3/getid3.lib.php old mode 100755 new mode 100644 index efb3ba5e..1dc97a62 --- a/app/Library/getid3/getid3/getid3.lib.php +++ b/app/Library/getid3/getid3/getid3.lib.php @@ -414,6 +414,20 @@ class getid3_lib return $newarray; } + public static function flipped_array_merge_noclobber($array1, $array2) { + if (!is_array($array1) || !is_array($array2)) { + return false; + } + # naturally, this only works non-recursively + $newarray = array_flip($array1); + foreach (array_flip($array2) as $key => $val) { + if (!isset($newarray[$key])) { + $newarray[$key] = count($newarray); + } + } + return array_flip($newarray); + } + public static function ksort_recursive(&$theArray) { ksort($theArray); @@ -522,12 +536,12 @@ class getid3_lib if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) { // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html // https://core.trac.wordpress.org/changeset/29378 - $loader = libxml_disable_entity_loader(true); - $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT); - $return = self::SimpleXMLelement2array($XMLobject); - libxml_disable_entity_loader($loader); - return $return; - } + $loader = libxml_disable_entity_loader(true); + $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT); + $return = self::SimpleXMLelement2array($XMLobject); + libxml_disable_entity_loader($loader); + return $return; + } return false; } @@ -606,7 +620,7 @@ class getid3_lib if (empty($tempdir)) { // yes this is ugly, feel free to suggest a better way - require_once(dirname(__FILE__) . '/getid3.php'); + require_once(dirname(__FILE__).'/getid3.php'); $getid3_temp = new getID3(); $tempdir = $getid3_temp->tempdir; unset($getid3_temp); @@ -1153,11 +1167,19 @@ class getid3_lib public static function GetDataImageSize($imgData, &$imageinfo=array()) { static $tempdir = ''; if (empty($tempdir)) { + if (function_exists('sys_get_temp_dir')) { + $tempdir = sys_get_temp_dir(); // https://github.com/JamesHeinrich/getID3/issues/52 + } + // yes this is ugly, feel free to suggest a better way - require_once(dirname(__FILE__) . '/getid3.php'); - $getid3_temp = new getID3(); - $tempdir = $getid3_temp->tempdir; - unset($getid3_temp); + if (include_once(dirname(__FILE__).'/getid3.php')) { + if ($getid3_temp = new getID3()) { + if ($getid3_temp_tempdir = $getid3_temp->tempdir) { + $tempdir = $getid3_temp_tempdir; + } + unset($getid3_temp, $getid3_temp_tempdir); + } + } } $GetDataImageSize = false; if ($tempfilename = tempnam($tempdir, 'gI3')) { @@ -1165,6 +1187,9 @@ class getid3_lib fwrite($tmp, $imgData); fclose($tmp); $GetDataImageSize = @getimagesize($tempfilename, $imageinfo); + if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) { + return false; + } $GetDataImageSize['height'] = $GetDataImageSize[0]; $GetDataImageSize['width'] = $GetDataImageSize[1]; } diff --git a/app/Library/getid3/getid3/getid3.php b/app/Library/getid3/getid3/getid3.php old mode 100755 new mode 100644 index 25a52fdd..b7b54d35 --- a/app/Library/getid3/getid3/getid3.php +++ b/app/Library/getid3/getid3/getid3.php @@ -109,7 +109,7 @@ class getID3 protected $startup_error = ''; protected $startup_warning = ''; - const VERSION = '1.9.9-20141121'; + const VERSION = '1.9.10-201511241457'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; @@ -166,7 +166,7 @@ class getID3 } // Load support library - if (!include_once(GETID3_INCLUDEPATH . 'getid3.lib.php')) { + if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { $this->startup_error .= 'getid3.lib.php is missing or corrupt'; } @@ -243,7 +243,7 @@ class getID3 } - public function openfile($filename) { + public function openfile($filename, $filesize=null) { try { if (!empty($this->startup_error)) { throw new getid3_exception($this->startup_error); @@ -287,7 +287,7 @@ class getID3 throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')'); } - $this->info['filesize'] = filesize($filename); + $this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename)); // set redundant parameters - might be needed in some include file // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion $filename = str_replace('\\', '/', $filename); @@ -342,9 +342,9 @@ class getID3 } // public: analyze file - public function analyze($filename) { + public function analyze($filename, $filesize=null, $original_filename='') { try { - if (!$this->openfile($filename)) { + if (!$this->openfile($filename, $filesize)) { return $this->info; } @@ -389,7 +389,7 @@ class getID3 $formattest = fread($this->fp, 32774); // determine format - $determined_format = $this->GetFileFormat($formattest, $filename); + $determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename)); // unable to determine file format if (!$determined_format) { @@ -876,7 +876,7 @@ class getID3 'pattern' => '^(RIFF|SDSS|FORM)', 'group' => 'audio-video', 'module' => 'riff', - 'mime_type' => 'audio/x-wave', + 'mime_type' => 'audio/x-wav', 'fail_ape' => 'WARNING', ), @@ -1235,6 +1235,29 @@ class getID3 } } + // ID3v1 encoding detection hack start + // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets + // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess + if ($comment_name == 'id3v1') { + if ($encoding == 'ISO-8859-1') { + if (function_exists('iconv')) { + foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { + foreach ($valuearray as $key => $value) { + if (preg_match('#^[\\x80-\\xFF]+$#', $value)) { + foreach (array('windows-1251', 'KOI8-R') as $id3v1_bad_encoding) { + if (@iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) { + $encoding = $id3v1_bad_encoding; + break 3; + } + } + } + } + } + } + } + } + // ID3v1 encoding detection hack end + $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! } diff --git a/app/Library/getid3/getid3/module.archive.gzip.php b/app/Library/getid3/getid3/module.archive.gzip.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.archive.rar.php b/app/Library/getid3/getid3/module.archive.rar.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.archive.szip.php b/app/Library/getid3/getid3/module.archive.szip.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.archive.tar.php b/app/Library/getid3/getid3/module.archive.tar.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.archive.zip.php b/app/Library/getid3/getid3/module.archive.zip.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.asf.php b/app/Library/getid3/getid3/module.audio-video.asf.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.bink.php b/app/Library/getid3/getid3/module.audio-video.bink.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.flv.php b/app/Library/getid3/getid3/module.audio-video.flv.php old mode 100755 new mode 100644 index c5fbd4b0..2f86ebd9 --- a/app/Library/getid3/getid3/module.audio-video.flv.php +++ b/app/Library/getid3/getid3/module.audio-video.flv.php @@ -541,7 +541,7 @@ class AMFReader { // Long string default: $value = '(unknown or unsupported data type)'; - break; + break; } return $value; diff --git a/app/Library/getid3/getid3/module.audio-video.matroska.php b/app/Library/getid3/getid3/module.audio-video.matroska.php old mode 100755 new mode 100644 index f2cc5ac0..09793602 --- a/app/Library/getid3/getid3/module.audio-video.matroska.php +++ b/app/Library/getid3/getid3/module.audio-video.matroska.php @@ -457,6 +457,7 @@ class getid3_matroska extends getid3_handler default: $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"'); + break; } $info['audio']['streams'][] = $track_info; @@ -524,6 +525,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('header', __LINE__, $element_data); + break; } unset($element_data['offset'], $element_data['end']); @@ -562,6 +564,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); } + break; } if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required @@ -571,6 +574,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('seekhead', __LINE__, $seek_entry); + break; } } break; @@ -653,6 +657,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('track.video', __LINE__, $sub_subelement); + break; } } break; @@ -678,6 +683,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('track.audio', __LINE__, $sub_subelement); + break; } } break; @@ -713,6 +719,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); + break; } } break; @@ -736,24 +743,28 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); + break; } } break; default: $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement); + break; } } break; default: $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement); + break; } } break; default: $this->unhandledElement('track', __LINE__, $subelement); + break; } } @@ -762,6 +773,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('tracks', __LINE__, $track_entry); + break; } } break; @@ -825,6 +837,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement); + break; } } $info_entry[$subelement['id_name']] = $chaptertranslate_entry; @@ -832,6 +845,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('info', __LINE__, $subelement); + break; } } $info['matroska']['info'][] = $info_entry; @@ -868,6 +882,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement); + break; } } $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry; @@ -879,6 +894,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement); + break; } } $cues_entry[] = $cuepoint_entry; @@ -886,6 +902,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('cues', __LINE__, $subelement); + break; } } $info['matroska']['cues'] = $cues_entry; @@ -927,6 +944,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement); + break; } } $tag_entry[$sub_subelement['id_name']] = $targets_entry; @@ -938,6 +956,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('tags.tag', __LINE__, $sub_subelement); + break; } } $tags_entry[] = $tag_entry; @@ -945,6 +964,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('tags', __LINE__, $subelement); + break; } } $info['matroska']['tags'] = $tags_entry; @@ -985,6 +1005,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement); + break; } } $info['matroska']['attachments'][] = $attachedfile_entry; @@ -992,6 +1013,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('attachments', __LINE__, $subelement); + break; } } break; @@ -1051,6 +1073,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement); + break; } } $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry; @@ -1070,6 +1093,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement); + break; } } $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry; @@ -1077,6 +1101,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement); + break; } } $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry; @@ -1084,6 +1109,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement); + break; } } $info['matroska']['chapters'][] = $editionentry_entry; @@ -1091,6 +1117,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('chapters', __LINE__, $subelement); + break; } } break; @@ -1119,6 +1146,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement); + break; } } $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks; @@ -1149,6 +1177,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement); + break; } } $cluster_entry[$subelement['id_name']][] = $cluster_block_group; @@ -1160,6 +1189,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('cluster', __LINE__, $subelement); + break; } $this->current_offset = $subelement['end']; } @@ -1181,12 +1211,14 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('segment', __LINE__, $element_data); + break; } } break; default: $this->unhandledElement('root', __LINE__, $top_element); + break; } } } @@ -1339,6 +1371,7 @@ class getid3_matroska extends getid3_handler default: $this->unhandledElement('tag.simpletag', __LINE__, $element); + break; } } diff --git a/app/Library/getid3/getid3/module.audio-video.mpeg.php b/app/Library/getid3/getid3/module.audio-video.mpeg.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.nsv.php b/app/Library/getid3/getid3/module.audio-video.nsv.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.quicktime.php b/app/Library/getid3/getid3/module.audio-video.quicktime.php old mode 100755 new mode 100644 index 482c0912..73853d09 --- a/app/Library/getid3/getid3/module.audio-video.quicktime.php +++ b/app/Library/getid3/getid3/module.audio-video.quicktime.php @@ -81,6 +81,81 @@ class getid3_quicktime extends getid3_handler unset($info['avdataend_tmp']); } + if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) { + $durations = $this->quicktime_time_to_sample_table($info); + for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) { + $bookmark = array(); + $bookmark['title'] = $info['quicktime']['comments']['chapters'][$i]; + if (isset($durations[$i])) { + $bookmark['duration_sample'] = $durations[$i]['sample_duration']; + if ($i > 0) { + $bookmark['start_sample'] = $info['quicktime']['bookmarks'][($i - 1)]['start_sample'] + $info['quicktime']['bookmarks'][($i - 1)]['duration_sample']; + } else { + $bookmark['start_sample'] = 0; + } + if ($time_scale = $this->quicktime_bookmark_time_scale($info)) { + $bookmark['duration_seconds'] = $bookmark['duration_sample'] / $time_scale; + $bookmark['start_seconds'] = $bookmark['start_sample'] / $time_scale; + } + } + $info['quicktime']['bookmarks'][] = $bookmark; + } + } + + if (isset($info['quicktime']['temp_meta_key_names'])) { + unset($info['quicktime']['temp_meta_key_names']); + } + + if (!empty($info['quicktime']['comments']['location.ISO6709'])) { + // https://en.wikipedia.org/wiki/ISO_6709 + foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) { + $latitude = false; + $longitude = false; + $altitude = false; + if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) { + @list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches; + + if (strlen($lat_deg) == 2) { // [+-]DD.D + $latitude = floatval(ltrim($lat_deg, '0').$lat_deg_dec); + } elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M + $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60); + } elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S + $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600); + } + + if (strlen($lon_deg) == 3) { // [+-]DDD.D + $longitude = floatval(ltrim($lon_deg, '0').$lon_deg_dec); + } elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M + $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60); + } elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S + $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600); + } + + if (strlen($alt_deg) == 3) { // [+-]DDD.D + $altitude = floatval(ltrim($alt_deg, '0').$alt_deg_dec); + } elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M + $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60); + } elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S + $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600); + } + + if ($latitude !== false) { + $info['quicktime']['comments']['gps_latitude'][] = (($lat_sign == '-') ? -1 : 1) * floatval($latitude); + } + if ($longitude !== false) { + $info['quicktime']['comments']['gps_longitude'][] = (($lon_sign == '-') ? -1 : 1) * floatval($longitude); + } + if ($altitude !== false) { + $info['quicktime']['comments']['gps_altitude'][] = (($alt_sign == '-') ? -1 : 1) * floatval($altitude); + } + } + if ($latitude === false) { + $info['warning'][] = 'location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug'; + } + break; + } + } + if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) { $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } @@ -120,6 +195,7 @@ class getid3_quicktime extends getid3_handler public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm + // https://code.google.com/p/mp4v2/wiki/iTunesMetadata $info = &$this->getid3->info; @@ -222,81 +298,88 @@ class getid3_quicktime extends getid3_handler break; + case "\xA9".'alb': // ALBum + case "\xA9".'ART': // + case "\xA9".'art': // ARTist + case "\xA9".'aut': // + case "\xA9".'cmt': // CoMmenT + case "\xA9".'com': // COMposer + case "\xA9".'cpy': // + case "\xA9".'day': // content created year + case "\xA9".'dir': // + case "\xA9".'ed1': // + case "\xA9".'ed2': // + case "\xA9".'ed3': // + case "\xA9".'ed4': // + case "\xA9".'ed5': // + case "\xA9".'ed6': // + case "\xA9".'ed7': // + case "\xA9".'ed8': // + case "\xA9".'ed9': // + case "\xA9".'enc': // + case "\xA9".'fmt': // + case "\xA9".'gen': // GENre + case "\xA9".'grp': // GRouPing + case "\xA9".'hst': // + case "\xA9".'inf': // + case "\xA9".'lyr': // LYRics + case "\xA9".'mak': // + case "\xA9".'mod': // + case "\xA9".'nam': // full NAMe + case "\xA9".'ope': // + case "\xA9".'PRD': // + case "\xA9".'prf': // + case "\xA9".'req': // + case "\xA9".'src': // + case "\xA9".'swr': // + case "\xA9".'too': // encoder + case "\xA9".'trk': // TRacK + case "\xA9".'url': // + case "\xA9".'wrn': // + case "\xA9".'wrt': // WRiTer + case '----': // itunes specific case 'aART': // Album ARTist + case 'akID': // iTunes store account type + case 'apID': // Purchase Account + case 'atID': // case 'catg': // CaTeGory + case 'cmID': // + case 'cnID': // case 'covr': // COVeR artwork case 'cpil': // ComPILation case 'cprt': // CoPyRighT case 'desc': // DESCription case 'disk': // DISK number case 'egid': // Episode Global ID + case 'geID': // case 'gnre': // GeNRE + case 'hdvd': // HD ViDeo case 'keyw': // KEYWord - case 'ldes': + case 'ldes': // Long DEScription case 'pcst': // PodCaST case 'pgap': // GAPless Playback + case 'plID': // case 'purd': // PURchase Date case 'purl': // Podcast URL - case 'rati': - case 'rndu': - case 'rpdu': + case 'rati': // + case 'rndu': // + case 'rpdu': // case 'rtng': // RaTiNG - case 'stik': + case 'sfID': // iTunes store country + case 'soaa': // SOrt Album Artist + case 'soal': // SOrt ALbum + case 'soar': // SOrt ARtist + case 'soco': // SOrt COmposer + case 'sonm': // SOrt NaMe + case 'sosn': // SOrt Show Name + case 'stik': // case 'tmpo': // TeMPO (BPM) case 'trkn': // TRacK Number + case 'tven': // tvEpisodeID case 'tves': // TV EpiSode case 'tvnn': // TV Network Name case 'tvsh': // TV SHow Name case 'tvsn': // TV SeasoN - case 'akID': // iTunes store account type - case 'apID': - case 'atID': - case 'cmID': - case 'cnID': - case 'geID': - case 'plID': - case 'sfID': // iTunes store country - case "\xA9".'alb': // ALBum - case "\xA9".'art': // ARTist - case "\xA9".'ART': - case "\xA9".'aut': - case "\xA9".'cmt': // CoMmenT - case "\xA9".'com': // COMposer - case "\xA9".'cpy': - case "\xA9".'day': // content created year - case "\xA9".'dir': - case "\xA9".'ed1': - case "\xA9".'ed2': - case "\xA9".'ed3': - case "\xA9".'ed4': - case "\xA9".'ed5': - case "\xA9".'ed6': - case "\xA9".'ed7': - case "\xA9".'ed8': - case "\xA9".'ed9': - case "\xA9".'enc': - case "\xA9".'fmt': - case "\xA9".'gen': // GENre - case "\xA9".'grp': // GRouPing - case "\xA9".'hst': - case "\xA9".'inf': - case "\xA9".'lyr': // LYRics - case "\xA9".'mak': - case "\xA9".'mod': - case "\xA9".'nam': // full NAMe - case "\xA9".'ope': - case "\xA9".'PRD': - case "\xA9".'prd': - case "\xA9".'prf': - case "\xA9".'req': - case "\xA9".'src': - case "\xA9".'swr': - case "\xA9".'too': // encoder - case "\xA9".'trk': // TRacK - case "\xA9".'url': - case "\xA9".'wrn': - case "\xA9".'wrt': // WRiTer - case '----': // itunes specific if ($atom_parent == 'udta') { // User data atom handler $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); @@ -361,17 +444,21 @@ class getid3_quicktime extends getid3_handler case 21: // tmpo/cpil flag switch ($atomname) { case 'cpil': + case 'hdvd': case 'pcst': case 'pgap': + // 8-bit integer (boolean) $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); break; case 'tmpo': + // 16-bit integer $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); break; case 'disk': case 'trkn': + // binary $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); $atom_structure['data'] = empty($num) ? '' : $num; @@ -379,21 +466,25 @@ class getid3_quicktime extends getid3_handler break; case 'gnre': + // enum $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); break; case 'rtng': + // 8-bit integer $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); break; case 'stik': + // 8-bit integer (enum) $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); break; case 'sfID': + // 32-bit integer $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); break; @@ -403,7 +494,18 @@ class getid3_quicktime extends getid3_handler $atom_structure['data'] = substr($boxdata, 8); break; + case 'plID': + // 64-bit integer + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8)); + break; + + case 'atID': + case 'cnID': + case 'geID': + case 'tves': + case 'tvsn': default: + // 32-bit integer $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); } break; @@ -928,13 +1030,13 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ for ($i = 0; $i < $atom_structure['number_entries']; $i++) { $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); $drefDataOffset += 4; - $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); + $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); $drefDataOffset += 4; $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); $drefDataOffset += 1; $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 $drefDataOffset += 3; - $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); + $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); @@ -1004,7 +1106,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; return false; } - $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); @@ -1019,7 +1121,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ case 'pnot': // Preview atom $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 - $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' + $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); @@ -1029,7 +1131,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ case 'crgn': // Clipping ReGioN atom $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields - $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. + $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. break; @@ -1120,7 +1222,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ } $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); - $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; break; @@ -1240,14 +1342,20 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ } // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field - while (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) + while (($mdat_offset < (strlen($atom_data) - 8)) + && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) && ($chapter_string_length < 1000) && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) - && preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) { + && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) { + list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches; $mdat_offset += (2 + $chapter_string_length); - @$info['quicktime']['comments']['chapters'][] = $chapter_matches[0]; - } + @$info['quicktime']['comments']['chapters'][] = $chapter_string; + // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled) + if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8 + $mdat_offset += 12; + } + } if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { @@ -1397,22 +1505,53 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ break; case "\x00\x00\x00\x00": - case 'meta': // METAdata atom // some kind of metacontainer, may contain a big data dump such as: // mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4 // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); break; + case 'meta': // METAdata atom + // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html + + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; + case 'data': // metaDATA atom + static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data $atom_structure['language'] = substr($atom_data, 4 + 0, 2); $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); $atom_structure['data'] = substr($atom_data, 4 + 4); + $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; + + if ($atom_structure['key_name'] && $atom_structure['data']) { + @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; + } + break; + + case 'keys': // KEYS that may be present in the metadata atom. + // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21 + // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom. + // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys". + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $keys_atom_offset = 8; + for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { + $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); + $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); + $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); + $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace + + $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; + } break; default: @@ -1753,56 +1892,56 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ static $QuicktimeIODSaudioProfileNameLookup = array(); if (empty($QuicktimeIODSaudioProfileNameLookup)) { $QuicktimeIODSaudioProfileNameLookup = array( - 0x00 => 'ISO Reserved (0x00)', - 0x01 => 'Main Audio Profile @ Level 1', - 0x02 => 'Main Audio Profile @ Level 2', - 0x03 => 'Main Audio Profile @ Level 3', - 0x04 => 'Main Audio Profile @ Level 4', - 0x05 => 'Scalable Audio Profile @ Level 1', - 0x06 => 'Scalable Audio Profile @ Level 2', - 0x07 => 'Scalable Audio Profile @ Level 3', - 0x08 => 'Scalable Audio Profile @ Level 4', - 0x09 => 'Speech Audio Profile @ Level 1', - 0x0A => 'Speech Audio Profile @ Level 2', - 0x0B => 'Synthetic Audio Profile @ Level 1', - 0x0C => 'Synthetic Audio Profile @ Level 2', - 0x0D => 'Synthetic Audio Profile @ Level 3', - 0x0E => 'High Quality Audio Profile @ Level 1', - 0x0F => 'High Quality Audio Profile @ Level 2', - 0x10 => 'High Quality Audio Profile @ Level 3', - 0x11 => 'High Quality Audio Profile @ Level 4', - 0x12 => 'High Quality Audio Profile @ Level 5', - 0x13 => 'High Quality Audio Profile @ Level 6', - 0x14 => 'High Quality Audio Profile @ Level 7', - 0x15 => 'High Quality Audio Profile @ Level 8', - 0x16 => 'Low Delay Audio Profile @ Level 1', - 0x17 => 'Low Delay Audio Profile @ Level 2', - 0x18 => 'Low Delay Audio Profile @ Level 3', - 0x19 => 'Low Delay Audio Profile @ Level 4', - 0x1A => 'Low Delay Audio Profile @ Level 5', - 0x1B => 'Low Delay Audio Profile @ Level 6', - 0x1C => 'Low Delay Audio Profile @ Level 7', - 0x1D => 'Low Delay Audio Profile @ Level 8', - 0x1E => 'Natural Audio Profile @ Level 1', - 0x1F => 'Natural Audio Profile @ Level 2', - 0x20 => 'Natural Audio Profile @ Level 3', - 0x21 => 'Natural Audio Profile @ Level 4', - 0x22 => 'Mobile Audio Internetworking Profile @ Level 1', - 0x23 => 'Mobile Audio Internetworking Profile @ Level 2', - 0x24 => 'Mobile Audio Internetworking Profile @ Level 3', - 0x25 => 'Mobile Audio Internetworking Profile @ Level 4', - 0x26 => 'Mobile Audio Internetworking Profile @ Level 5', - 0x27 => 'Mobile Audio Internetworking Profile @ Level 6', - 0x28 => 'AAC Profile @ Level 1', - 0x29 => 'AAC Profile @ Level 2', - 0x2A => 'AAC Profile @ Level 4', - 0x2B => 'AAC Profile @ Level 5', - 0x2C => 'High Efficiency AAC Profile @ Level 2', - 0x2D => 'High Efficiency AAC Profile @ Level 3', - 0x2E => 'High Efficiency AAC Profile @ Level 4', - 0x2F => 'High Efficiency AAC Profile @ Level 5', - 0xFE => 'Not part of MPEG-4 audio profiles', - 0xFF => 'No audio capability required', + 0x00 => 'ISO Reserved (0x00)', + 0x01 => 'Main Audio Profile @ Level 1', + 0x02 => 'Main Audio Profile @ Level 2', + 0x03 => 'Main Audio Profile @ Level 3', + 0x04 => 'Main Audio Profile @ Level 4', + 0x05 => 'Scalable Audio Profile @ Level 1', + 0x06 => 'Scalable Audio Profile @ Level 2', + 0x07 => 'Scalable Audio Profile @ Level 3', + 0x08 => 'Scalable Audio Profile @ Level 4', + 0x09 => 'Speech Audio Profile @ Level 1', + 0x0A => 'Speech Audio Profile @ Level 2', + 0x0B => 'Synthetic Audio Profile @ Level 1', + 0x0C => 'Synthetic Audio Profile @ Level 2', + 0x0D => 'Synthetic Audio Profile @ Level 3', + 0x0E => 'High Quality Audio Profile @ Level 1', + 0x0F => 'High Quality Audio Profile @ Level 2', + 0x10 => 'High Quality Audio Profile @ Level 3', + 0x11 => 'High Quality Audio Profile @ Level 4', + 0x12 => 'High Quality Audio Profile @ Level 5', + 0x13 => 'High Quality Audio Profile @ Level 6', + 0x14 => 'High Quality Audio Profile @ Level 7', + 0x15 => 'High Quality Audio Profile @ Level 8', + 0x16 => 'Low Delay Audio Profile @ Level 1', + 0x17 => 'Low Delay Audio Profile @ Level 2', + 0x18 => 'Low Delay Audio Profile @ Level 3', + 0x19 => 'Low Delay Audio Profile @ Level 4', + 0x1A => 'Low Delay Audio Profile @ Level 5', + 0x1B => 'Low Delay Audio Profile @ Level 6', + 0x1C => 'Low Delay Audio Profile @ Level 7', + 0x1D => 'Low Delay Audio Profile @ Level 8', + 0x1E => 'Natural Audio Profile @ Level 1', + 0x1F => 'Natural Audio Profile @ Level 2', + 0x20 => 'Natural Audio Profile @ Level 3', + 0x21 => 'Natural Audio Profile @ Level 4', + 0x22 => 'Mobile Audio Internetworking Profile @ Level 1', + 0x23 => 'Mobile Audio Internetworking Profile @ Level 2', + 0x24 => 'Mobile Audio Internetworking Profile @ Level 3', + 0x25 => 'Mobile Audio Internetworking Profile @ Level 4', + 0x26 => 'Mobile Audio Internetworking Profile @ Level 5', + 0x27 => 'Mobile Audio Internetworking Profile @ Level 6', + 0x28 => 'AAC Profile @ Level 1', + 0x29 => 'AAC Profile @ Level 2', + 0x2A => 'AAC Profile @ Level 4', + 0x2B => 'AAC Profile @ Level 5', + 0x2C => 'High Efficiency AAC Profile @ Level 2', + 0x2D => 'High Efficiency AAC Profile @ Level 3', + 0x2E => 'High Efficiency AAC Profile @ Level 4', + 0x2F => 'High Efficiency AAC Profile @ Level 5', + 0xFE => 'Not part of MPEG-4 audio profiles', + 0xFF => 'No audio capability required', ); } return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private'); @@ -2111,8 +2250,18 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'
$data, 'image_mime'=>$image_mime); } } - $info['quicktime']['comments'][$comment_key][] = $data; + $gooddata = array($data); + if ($comment_key == 'genre') { + // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" + $gooddata = explode(';', $data); + } + foreach ($gooddata as $data) { + $info['quicktime']['comments'][$comment_key][] = $data; + } } return true; } @@ -2243,4 +2395,79 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'
$value) { + $key_history = $history.'/'.$key; + if ($key === $tag) { + $result[] = array($key_history, $info); + } else { + if (is_array($value)) { + $this->search_tag_by_key($value, $tag, $key_history, $result); + } + } + } + } + + public function search_tag_by_pair($info, $k, $v, $history, &$result) { + foreach ($info as $key => $value) { + $key_history = $history.'/'.$key; + if (($key === $k) && ($value === $v)) { + $result[] = array($key_history, $info); + } else { + if (is_array($value)) { + $this->search_tag_by_pair($value, $k, $v, $key_history, $result); + } + } + } + } + + public function quicktime_time_to_sample_table($info) { + $res = array(); + $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res); + foreach ($res as $value) { + $stbl_res = array(); + $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res); + if (count($stbl_res) > 0) { + $stts_res = array(); + $this->search_tag_by_key($value[1], 'time_to_sample_table', $value[0], $stts_res); + if (count($stts_res) > 0) { + return $stts_res[0][1]['time_to_sample_table']; + } + } + } + return array(); + } + + function quicktime_bookmark_time_scale($info) { + $time_scale = ''; + $ts_prefix_len = 0; + $res = array(); + $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res); + foreach ($res as $value) { + $stbl_res = array(); + $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res); + if (count($stbl_res) > 0) { + $ts_res = array(); + $this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res); + foreach ($ts_res as $value) { + $prefix = substr($value[0], 0, -12); + if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) { + $time_scale = $value[1]['time_scale']; + $ts_prefix_len = strlen($prefix); + } + } + } + } + return $time_scale; + } + /* + // END helper functions for m4b audiobook chapters + */ + + } diff --git a/app/Library/getid3/getid3/module.audio-video.real.php b/app/Library/getid3/getid3/module.audio-video.real.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.riff.php b/app/Library/getid3/getid3/module.audio-video.riff.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.swf.php b/app/Library/getid3/getid3/module.audio-video.swf.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio-video.ts.php b/app/Library/getid3/getid3/module.audio-video.ts.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.aa.php b/app/Library/getid3/getid3/module.audio.aa.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.aac.php b/app/Library/getid3/getid3/module.audio.aac.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.ac3.php b/app/Library/getid3/getid3/module.audio.ac3.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.amr.php b/app/Library/getid3/getid3/module.audio.amr.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.au.php b/app/Library/getid3/getid3/module.audio.au.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.avr.php b/app/Library/getid3/getid3/module.audio.avr.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.bonk.php b/app/Library/getid3/getid3/module.audio.bonk.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.dss.php b/app/Library/getid3/getid3/module.audio.dss.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.dts.php b/app/Library/getid3/getid3/module.audio.dts.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.flac.php b/app/Library/getid3/getid3/module.audio.flac.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.la.php b/app/Library/getid3/getid3/module.audio.la.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.lpac.php b/app/Library/getid3/getid3/module.audio.lpac.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.midi.php b/app/Library/getid3/getid3/module.audio.midi.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.mod.php b/app/Library/getid3/getid3/module.audio.mod.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.monkey.php b/app/Library/getid3/getid3/module.audio.monkey.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.mp3.php b/app/Library/getid3/getid3/module.audio.mp3.php old mode 100755 new mode 100644 index 329f7a67..cba36193 --- a/app/Library/getid3/getid3/module.audio.mp3.php +++ b/app/Library/getid3/getid3/module.audio.mp3.php @@ -437,7 +437,6 @@ class getid3_mp3 extends getid3_handler // and $cc... is the audio data $head4 = substr($headerstring, 0, 4); - static $MPEGaudioHeaderDecodeCache = array(); if (isset($MPEGaudioHeaderDecodeCache[$head4])) { $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; @@ -648,9 +647,20 @@ class getid3_mp3 extends getid3_handler } //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { - if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + //if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + if (!empty($thisfile_mpeg_audio['VBR_frames'])) { + $used_filesize = 0; + if (!empty($thisfile_mpeg_audio['VBR_bytes'])) { + $used_filesize = $thisfile_mpeg_audio['VBR_bytes']; + } elseif (!empty($info['filesize'])) { + $used_filesize = $info['filesize']; + $used_filesize -= intval(@$info['id3v2']['headerlength']); + $used_filesize -= (isset($info['id3v1']) ? 128 : 0); + $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0); + $info['warning'][] = 'MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes'; + } - $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; + $framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames']; if ($thisfile_mpeg_audio['layer'] == '1') { // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 @@ -948,7 +958,7 @@ class getid3_mp3 extends getid3_handler } $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0); if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { - $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; + $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion } break; diff --git a/app/Library/getid3/getid3/module.audio.mpc.php b/app/Library/getid3/getid3/module.audio.mpc.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.ogg.php b/app/Library/getid3/getid3/module.audio.ogg.php old mode 100755 new mode 100644 index 2a77768b..3ebf8faa --- a/app/Library/getid3/getid3/module.audio.ogg.php +++ b/app/Library/getid3/getid3/module.audio.ogg.php @@ -562,6 +562,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get default: return false; + break; } $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); diff --git a/app/Library/getid3/getid3/module.audio.optimfrog.php b/app/Library/getid3/getid3/module.audio.optimfrog.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.rkau.php b/app/Library/getid3/getid3/module.audio.rkau.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.shorten.php b/app/Library/getid3/getid3/module.audio.shorten.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.tta.php b/app/Library/getid3/getid3/module.audio.tta.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.voc.php b/app/Library/getid3/getid3/module.audio.voc.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.vqf.php b/app/Library/getid3/getid3/module.audio.vqf.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.audio.wavpack.php b/app/Library/getid3/getid3/module.audio.wavpack.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.bmp.php b/app/Library/getid3/getid3/module.graphic.bmp.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.efax.php b/app/Library/getid3/getid3/module.graphic.efax.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.gif.php b/app/Library/getid3/getid3/module.graphic.gif.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.jpg.php b/app/Library/getid3/getid3/module.graphic.jpg.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.pcd.php b/app/Library/getid3/getid3/module.graphic.pcd.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.png.php b/app/Library/getid3/getid3/module.graphic.png.php old mode 100755 new mode 100644 index 11ae3ede..0ce82a69 --- a/app/Library/getid3/getid3/module.graphic.png.php +++ b/app/Library/getid3/getid3/module.graphic.png.php @@ -17,8 +17,10 @@ class getid3_png extends getid3_handler { + public $max_data_bytes = 10000000; // if data chunk is larger than this do not read it completely (getID3 only needs the first few dozen bytes for parsing) public function Analyze() { + $info = &$this->getid3->info; // shortcut @@ -44,14 +46,24 @@ class getid3_png extends getid3_handler while ((($this->ftell() - (strlen($PNGfiledata) - $offset)) < $info['filesize'])) { $chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); - $offset += 4; - while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && ($this->ftell() < $info['filesize'])) { - $PNGfiledata .= $this->fread($this->getid3->fread_buffer_size()); + if ($chunk['data_length'] === false) { + $info['error'][] = 'Failed to read data_length at offset '.$offset; + return false; } - $chunk['type_text'] = substr($PNGfiledata, $offset, 4); + $offset += 4; + $truncated_data = false; + while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && ($this->ftell() < $info['filesize'])) { + if (strlen($PNGfiledata) < $this->max_data_bytes) { + $PNGfiledata .= $this->fread($this->getid3->fread_buffer_size()); + } else { + $info['warning'][] = 'At offset '.$offset.' chunk "'.substr($PNGfiledata, $offset, 4).'" exceeded max_data_bytes value of '.$this->max_data_bytes.', data chunk will be truncated at '.(strlen($PNGfiledata) - 8).' bytes'; + break; + } + } + $chunk['type_text'] = substr($PNGfiledata, $offset, 4); $offset += 4; $chunk['type_raw'] = getid3_lib::BigEndian2Int($chunk['type_text']); - $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']); + $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']); $offset += $chunk['data_length']; $chunk['crc'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); $offset += 4; @@ -162,7 +174,7 @@ class getid3_png extends getid3_handler case 'iCCP': // Embedded ICC Profile $thisfile_png_chunk_type_text['header'] = $chunk; - list($profilename, $compressiondata) = explode("\x00", $chunk['data'], 2); + list($profilename, $compressiondata) = explode("\x00", $chunk['data'], 2); $thisfile_png_chunk_type_text['profile_name'] = $profilename; $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1)); $thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1); @@ -173,7 +185,7 @@ class getid3_png extends getid3_handler case 'tEXt': // Textual Data $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $text) = explode("\x00", $chunk['data'], 2); + list($keyword, $text) = explode("\x00", $chunk['data'], 2); $thisfile_png_chunk_type_text['keyword'] = $keyword; $thisfile_png_chunk_type_text['text'] = $text; @@ -183,7 +195,7 @@ class getid3_png extends getid3_handler case 'zTXt': // Compressed Textual Data $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2); + list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2); $thisfile_png_chunk_type_text['keyword'] = $keyword; $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); $thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1); @@ -206,7 +218,7 @@ class getid3_png extends getid3_handler case 'iTXt': // International Textual Data $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2); + list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2); $thisfile_png_chunk_type_text['keyword'] = $keyword; $thisfile_png_chunk_type_text['compression'] = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1)); @@ -307,7 +319,7 @@ class getid3_png extends getid3_handler case 'sPLT': // Suggested Palette $thisfile_png_chunk_type_text['header'] = $chunk; - list($palettename, $otherdata) = explode("\x00", $chunk['data'], 2); + list($palettename, $otherdata) = explode("\x00", $chunk['data'], 2); $thisfile_png_chunk_type_text['palette_name'] = $palettename; $sPLToffset = 0; $thisfile_png_chunk_type_text['sample_depth_bits'] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1)); diff --git a/app/Library/getid3/getid3/module.graphic.svg.php b/app/Library/getid3/getid3/module.graphic.svg.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.graphic.tiff.php b/app/Library/getid3/getid3/module.graphic.tiff.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.cue.php b/app/Library/getid3/getid3/module.misc.cue.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.exe.php b/app/Library/getid3/getid3/module.misc.exe.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.iso.php b/app/Library/getid3/getid3/module.misc.iso.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.msoffice.php b/app/Library/getid3/getid3/module.misc.msoffice.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.par2.php b/app/Library/getid3/getid3/module.misc.par2.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.misc.pdf.php b/app/Library/getid3/getid3/module.misc.pdf.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.tag.apetag.php b/app/Library/getid3/getid3/module.tag.apetag.php old mode 100755 new mode 100644 index 724b8b0f..819e5e33 --- a/app/Library/getid3/getid3/module.tag.apetag.php +++ b/app/Library/getid3/getid3/module.tag.apetag.php @@ -260,12 +260,16 @@ class getid3_apetag extends getid3_handler $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); - $thisfile_ape_items_current['image_mime'] = ''; - $imageinfo = array(); - $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); - $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); - do { + $thisfile_ape_items_current['image_mime'] = ''; + $imageinfo = array(); + $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); + if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) { + $info['warning'][] = 'APEtag "'.$item_key.'" contains invalid image data'; + break; + } + $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); + if ($this->inline_attachments === false) { // skip entirely unset($thisfile_ape_items_current['data']); diff --git a/app/Library/getid3/getid3/module.tag.id3v1.php b/app/Library/getid3/getid3/module.tag.id3v1.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.tag.id3v2.php b/app/Library/getid3/getid3/module.tag.id3v2.php old mode 100755 new mode 100644 index 70925048..de334135 --- a/app/Library/getid3/getid3/module.tag.id3v2.php +++ b/app/Library/getid3/getid3/module.tag.id3v2.php @@ -442,10 +442,14 @@ class getid3_id3v2 extends getid3_handler } // end footer if (isset($thisfile_id3v2['comments']['genre'])) { + $genres = array(); foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { - unset($thisfile_id3v2['comments']['genre'][$key]); - $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value))); + foreach ($this->ParseID3v2GenreString($value) as $genre) { + $genres[] = $genre; + } } + $thisfile_id3v2['comments']['genre'] = array_unique($genres); + unset($key, $value, $genres, $genre); } if (isset($thisfile_id3v2['comments']['track'])) { @@ -503,6 +507,16 @@ class getid3_id3v2 extends getid3_handler if (strpos($genrestring, "\x00") === false) { $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring); } + + // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here: + // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name + $genrestring = str_replace('/', "\x00", $genrestring); + $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring); + $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring); + + // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" + $genrestring = str_replace(';', "\x00", $genrestring); + $genre_elements = explode("\x00", $genrestring); foreach ($genre_elements as $element) { $element = trim($element); @@ -635,7 +649,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; @@ -728,8 +743,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); @@ -971,7 +986,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); @@ -979,7 +995,6 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - $parsedFrame['data'] = $parsedFrame['data']; $parsedFrame['language'] = $frame_language; $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; @@ -1079,7 +1094,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); @@ -1383,7 +1399,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $parsedFrame['encodingid'] = $frame_textencoding; @@ -1402,14 +1419,15 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['image_mime'] = ''; $imageinfo = array(); - $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); - if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { - $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); - if ($imagechunkcheck[0]) { - $parsedFrame['image_width'] = $imagechunkcheck[0]; - } - if ($imagechunkcheck[1]) { - $parsedFrame['image_height'] = $imagechunkcheck[1]; + if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { + if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); + if ($imagechunkcheck[0]) { + $parsedFrame['image_width'] = $imagechunkcheck[0]; + } + if ($imagechunkcheck[1]) { + $parsedFrame['image_height'] = $imagechunkcheck[1]; + } } } @@ -1507,7 +1525,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); @@ -1589,7 +1608,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1614,7 +1634,7 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { - $frame_ownerid == ''; + $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); $parsedFrame['ownerid'] = $frame_ownerid; @@ -1789,7 +1809,8 @@ class getid3_id3v2 extends getid3_handler $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { + if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { + // if description only contains a BOM or terminator then make it blank $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); diff --git a/app/Library/getid3/getid3/module.tag.lyrics3.php b/app/Library/getid3/getid3/module.tag.lyrics3.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/module.tag.xmp.php b/app/Library/getid3/getid3/module.tag.xmp.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.apetag.php b/app/Library/getid3/getid3/write.apetag.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.id3v1.php b/app/Library/getid3/getid3/write.id3v1.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.id3v2.php b/app/Library/getid3/getid3/write.id3v2.php old mode 100755 new mode 100644 index 7b6eaa1d..236e2ff4 --- a/app/Library/getid3/getid3/write.id3v2.php +++ b/app/Library/getid3/getid3/write.id3v2.php @@ -1759,10 +1759,15 @@ class getid3_write_id3v2 } public function ID3v2IsValidTextEncoding($textencodingbyte) { + // 0 = ISO-8859-1 + // 1 = UTF-16 with BOM + // 2 = UTF-16BE without BOM + // 3 = UTF-8 static $ID3v2IsValidTextEncoding_cache = array( - 2 => array(true, true), - 3 => array(true, true), - 4 => array(true, true, true, true)); + 2 => array(true, true), // ID3v2.2 - allow 0=ISO-8859-1, 1=UTF-16 + 3 => array(true, true), // ID3v2.3 - allow 0=ISO-8859-1, 1=UTF-16 + 4 => array(true, true, true, true), // ID3v2.4 - allow 0=ISO-8859-1, 1=UTF-16, 2=UTF-16BE, 3=UTF-8 + ); return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]); } @@ -1906,6 +1911,7 @@ class getid3_write_id3v2 $ID3v2ShortFrameNameLookup[2]['comment'] = 'COM'; $ID3v2ShortFrameNameLookup[2]['album'] = 'TAL'; $ID3v2ShortFrameNameLookup[2]['beats_per_minute'] = 'TBP'; + $ID3v2ShortFrameNameLookup[2]['bpm'] = 'TBP'; $ID3v2ShortFrameNameLookup[2]['composer'] = 'TCM'; $ID3v2ShortFrameNameLookup[2]['genre'] = 'TCO'; $ID3v2ShortFrameNameLookup[2]['itunescompilation'] = 'TCP'; @@ -1966,6 +1972,7 @@ class getid3_write_id3v2 $ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes'] = 'SYTC'; $ID3v2ShortFrameNameLookup[3]['album'] = 'TALB'; $ID3v2ShortFrameNameLookup[3]['beats_per_minute'] = 'TBPM'; + $ID3v2ShortFrameNameLookup[3]['bpm'] = 'TBPM'; $ID3v2ShortFrameNameLookup[3]['itunescompilation'] = 'TCMP'; $ID3v2ShortFrameNameLookup[3]['composer'] = 'TCOM'; $ID3v2ShortFrameNameLookup[3]['genre'] = 'TCON'; diff --git a/app/Library/getid3/getid3/write.lyrics3.php b/app/Library/getid3/getid3/write.lyrics3.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.metaflac.php b/app/Library/getid3/getid3/write.metaflac.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.php b/app/Library/getid3/getid3/write.php old mode 100755 new mode 100644 index 4c3ed620..9a7ec519 --- a/app/Library/getid3/getid3/write.php +++ b/app/Library/getid3/getid3/write.php @@ -23,7 +23,7 @@ if (!defined('GETID3_INCLUDEPATH')) { throw new Exception('getid3.php MUST be included before calling getid3_writetags'); } -if (!include_once(GETID3_INCLUDEPATH . 'getid3.lib.php')) { +if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { throw new Exception('write.php depends on getid3.lib.php, which is missing.'); } diff --git a/app/Library/getid3/getid3/write.real.php b/app/Library/getid3/getid3/write.real.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/getid3/write.vorbiscomment.php b/app/Library/getid3/getid3/write.vorbiscomment.php old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/head.exe b/app/Library/getid3/helperapps/head.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/md5sum.exe b/app/Library/getid3/helperapps/md5sum.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/metaflac.exe b/app/Library/getid3/helperapps/metaflac.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/readme.helperapps.txt b/app/Library/getid3/helperapps/readme.helperapps.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/shorten.exe b/app/Library/getid3/helperapps/shorten.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/tail.exe b/app/Library/getid3/helperapps/tail.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/helperapps/vorbiscomment.exe b/app/Library/getid3/helperapps/vorbiscomment.exe old mode 100755 new mode 100644 diff --git a/app/Library/getid3/license.txt b/app/Library/getid3/license.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/licence.gpl-10.txt b/app/Library/getid3/licenses/licence.gpl-10.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/licence.gpl-20.txt b/app/Library/getid3/licenses/licence.gpl-20.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/licence.gpl-30.txt b/app/Library/getid3/licenses/licence.gpl-30.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/licence.lgpl-30.txt b/app/Library/getid3/licenses/licence.lgpl-30.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/licence.mpl-20.txt b/app/Library/getid3/licenses/licence.mpl-20.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/licenses/license.commercial.txt b/app/Library/getid3/licenses/license.commercial.txt old mode 100755 new mode 100644 diff --git a/app/Library/getid3/readme.txt b/app/Library/getid3/readme.txt old mode 100755 new mode 100644 index deebf4dc..04dca2eb --- a/app/Library/getid3/readme.txt +++ b/app/Library/getid3/readme.txt @@ -187,7 +187,7 @@ if ($fp_remote = fopen($remotefilename, 'rb')) { // Initialize getID3 engine $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($filename); + $ThisFileInfo = $getID3->analyze($localtempfilename); // Delete temporary file unlink($localtempfilename); @@ -195,6 +195,11 @@ if ($fp_remote = fopen($remotefilename, 'rb')) { fclose($fp_remote); } +Note: since v1.9.9-20150212 it is possible a second and third parameter +to $getID3->analyze(), for original filesize and original filename +respectively. This permits you to download only a portion of a large remote +file but get accurate playtime estimates, assuming the format only requires +the beginning of the file for correct format analysis. See /demos/demo.write.php for how to write tags. @@ -432,6 +437,11 @@ Known Bugs/Issues in other programs ----------------------------------- http://www.getid3.org/phpBB3/viewtopic.php?t=25 +* MusicBrainz Picard (at least up to v1.3.2) writes multiple + ID3v2.3 genres in non-standard forward-slash separated text + rather than parenthesis-numeric+refinement style per the ID3v2.3 + specs. Tags written in ID3v2.4 mode are written correctly. + (detected and worked around by getID3()) * PZ TagEditor v4.53.408 has been known to insert ID3v2.3 frames into an existing ID3v2.2 tag which, of course, breaks things * Windows Media Player (up to v11) and iTunes (up to v10+) do @@ -604,3 +614,4 @@ Reference material: * http://trac.musepack.net/trac/wiki/SV8Specification * http://wyday.com/cuesharp/specification.php * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html +* http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header \ No newline at end of file diff --git a/app/Library/getid3/structure.txt b/app/Library/getid3/structure.txt old mode 100755 new mode 100644
type'.ImageTypesLookup($imagechunkcheck[2]).'
width'.number_format($imagechunkcheck[0]).' px