Changeset 3544 for extensions/charlies_content/getid3/getid3/getid3.php
- Timestamp:
- Jul 7, 2009, 10:27:37 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/charlies_content/getid3/getid3/getid3.php
r3318 r3544 1 1 <?php 2 // +----------------------------------------------------------------------+ 3 // | PHP version 5 | 4 // +----------------------------------------------------------------------+ 5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | 6 // +----------------------------------------------------------------------+ 7 // | This source file is subject to version 2 of the GPL license, | 8 // | that is bundled with this package in the file license.txt and is | 9 // | available through the world-wide-web at the following url: | 10 // | http://www.gnu.org/copyleft/gpl.html | 11 // +----------------------------------------------------------------------+ 12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | 13 // +----------------------------------------------------------------------+ 14 // | Authors: James Heinrich <infoØgetid3*org> | 15 // | Allan Hansen <ahØartemis*dk> | 16 // +----------------------------------------------------------------------+ 17 // | getid3.php | 18 // | Main getID3() file. | 19 // | dependencies: modules. | 20 // +----------------------------------------------------------------------+ 21 // 22 // $Id$ 23 24 25 class getid3 2 ///////////////////////////////////////////////////////////////// 3 /// getID3() by James Heinrich <info@getid3.org> // 4 // available at http://getid3.sourceforge.net // 5 // or http://www.getid3.org // 6 ///////////////////////////////////////////////////////////////// 7 // // 8 // Please see readme.txt for more information // 9 // /// 10 ///////////////////////////////////////////////////////////////// 11 12 // Defines 13 define('GETID3_VERSION', '1.7.9-20090308'); 14 define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes 15 16 17 18 class getID3 26 19 { 27 //// Settings Section - do NOT modify this file - change setting after newing getid3! 28 29 // Encoding 30 public $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv() - see http://www.gnu.org/software/libiconv/). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE. 31 public $encoding_id3v1 = 'ISO-8859-1'; // Override SPECIFICATION encoding for broken ID3v1 tags caused by bad tag programs. Examples: 'EUC-CN' for "Chinese MP3s" and 'CP1251' for "Cyrillic". 32 public $encoding_id3v2 = 'ISO-8859-1'; // Override ISO-8859-1 encoding for broken ID3v2 tags caused by BRAINDEAD tag programs that writes system codepage as 'ISO-8859-1' instead of UTF-8. 33 34 // Tags - disable for speed 35 public $option_tag_id3v1 = true; // Read and process ID3v1 tags. 36 public $option_tag_id3v2 = true; // Read and process ID3v2 tags. 37 public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags. 38 public $option_tag_apetag = true; // Read and process APE tags. 39 40 // Misc calucations - disable for speed 41 public $option_analyze = true; // Analyze file - disable if you only need to detect file format. 42 public $option_accurate_results = true; // Disable to greatly speed up parsing of some file formats at the cost of accuracy. 43 public $option_tags_process = true; // Copy tags to root key 'tags' and 'comments' and encode to $this->encoding. 44 public $option_tags_images = false; // Scan tags for binary image data - ID3v2 and vorbiscomments only. 45 public $option_extra_info = true; // Calculate/return additional info such as bitrate, channelmode etc. 46 public $option_max_2gb_check = false; // Check whether file is larger than 2 Gb and thus not supported by PHP. 47 48 // Misc data hashes - slow - require hash module 49 public $option_md5_data = false; // Get MD5 sum of data part - slow. 50 public $option_md5_data_source = false; // Use MD5 of source file if available - only FLAC, MAC, OptimFROG and Wavpack4. 51 public $option_sha1_data = false; // Get SHA1 sum of data part - slow. 52 53 // Public variables 54 public $filename; // Filename of file being analysed. 55 public $fp; // Filepointer to file being analysed. 56 public $info; // Result array. 57 58 // Protected variables 59 protected $include_path; // getid3 include path. 60 protected $warnings = array (); 61 protected $iconv_present; 62 63 // Class constants 64 const VERSION = '2.0.0b4'; 65 const FREAD_BUFFER_SIZE = 16384; // Read buffer size in bytes. 66 const ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 67 ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'; 68 69 70 71 // Constructor - check PHP enviroment and load library. 72 public function __construct() { 73 74 // Static varibles - no need to recalc every time we new getid3. 75 static $include_path; 76 static $iconv_present; 77 78 79 static $initialized; 80 if ($initialized) { 81 82 // Import static variables 83 $this->include_path = $include_path; 84 $this->iconv_present = $iconv_present; 85 86 // Run init checks only on first instance. 87 return; 88 } 89 90 // Get include_path 91 $this->include_path = $include_path = dirname(__FILE__) . '/'; 92 93 // Check for presence of iconv() and make sure it works (simpel test only). 94 if (function_exists('iconv') && @iconv('UTF-16LE', 'ISO-8859-1', @iconv('ISO-8859-1', 'UTF-16LE', getid3::ICONV_TEST_STRING)) == getid3::ICONV_TEST_STRING) { 95 $this->iconv_present = $iconv_present = true; 96 } 97 98 // iconv() not present - load replacement module. 99 else { 100 $this->include_module('lib.iconv_replacement'); 101 $this->iconv_present = $iconv_present = false; 102 } 103 104 105 // Require magic_quotes_runtime off 106 if (get_magic_quotes_runtime()) { 107 throw new getid3_exception('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'); 108 } 109 110 111 // Check memory limit. 112 $memory_limit = ini_get('memory_limit'); 113 if (eregi('([0-9]+)M', $memory_limit, $matches)) { 114 // could be stored as "16M" rather than 16777216 for example 115 $memory_limit = $matches[1] * 1048576; 116 } 117 if ($memory_limit <= 0) { 118 // Should not happen. 119 } elseif ($memory_limit <= 4194304) { 120 $this->warning('[SERIOUS] PHP has less than 4 Mb available memory and will very likely run out. Increase memory_limit in php.ini.'); 121 } elseif ($memory_limit <= 12582912) { 122 $this->warning('PHP has less than 12 Mb available memory and might run out if all modules are loaded. Increase memory_limit in php.ini if needed.'); 123 } 124 125 126 // Check safe_mode off 127 if ((bool)ini_get('safe_mode')) { 128 $this->warning('Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbis/flac tag writing disabled.'); 129 } 130 131 $initialized = true; 132 } 133 134 135 136 // Analyze file by name 137 public function Analyze($filename) { 138 139 // Init and save values 140 $this->filename = $filename; 141 $this->warnings = array (); 142 143 // Init result array and set parameters 144 $this->info = array (); 145 $this->info['GETID3_VERSION'] = getid3::VERSION; 146 147 // Remote files not supported 148 if (preg_match('/^(ht|f)tp:\/\//', $filename)) { 149 throw new getid3_exception('Remote files are not supported - please copy the file locally first.'); 150 } 151 152 // Open local file 153 if (!$this->fp = @fopen($filename, 'rb')) { 154 throw new getid3_exception('Could not open file "'.$filename.'"'); 155 } 156 157 // Set filesize related parameters 158 $this->info['filesize'] = filesize($filename); 159 $this->info['avdataoffset'] = 0; 160 $this->info['avdataend'] = $this->info['filesize']; 161 162 // Option_max_2gb_check 163 if ($this->option_max_2gb_check) { 164 // PHP doesn't support integers larger than 31-bit (~2GB) 165 // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize 166 // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer 167 fseek($this->fp, 0, SEEK_END); 168 if ((($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || 169 ($this->info['filesize'] < 0) || 170 (ftell($this->fp) < 0)) { 171 unset($this->info['filesize']); 172 fclose($this->fp); 173 throw new getid3_exception('File is most likely larger than 2GB and is not supported by PHP.'); 174 } 175 } 176 177 178 // ID3v2 detection (NOT parsing) done to make fileformat easier. 179 if (!$this->option_tag_id3v2) { 180 181 fseek($this->fp, 0, SEEK_SET); 182 $header = fread($this->fp, 10); 183 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 184 $this->info['id3v2']['header'] = true; 185 $this->info['id3v2']['majorversion'] = ord($header{3}); 186 $this->info['id3v2']['minorversion'] = ord($header{4}); 187 $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length 188 } 189 } 190 191 192 // Handle tags 193 foreach (array ("id3v2", "id3v1", "apetag", "lyrics3") as $tag_name) { 194 195 $option_tag = 'option_tag_' . $tag_name; 196 if ($this->$option_tag) { 197 $this->include_module('tag.'.$tag_name); 198 try { 199 $tag_class = 'getid3_' . $tag_name; 200 $tag = new $tag_class($this); 201 $tag->Analyze(); 202 } 203 catch (getid3_exception $e) { 204 throw $e; 205 } 206 } 207 } 208 209 210 211 //// Determine file format by magic bytes in file header. 212 213 // Read 32 kb file data 214 fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); 215 $filedata = fread($this->fp, 32774); 216 217 // Get huge FileFormatArray 218 $file_format_array = getid3::GetFileFormatArray(); 219 220 // Identify file format - loop through $format_info and detect with reg expr 221 foreach ($file_format_array as $name => $info) { 222 223 if (preg_match('/'.$info['pattern'].'/s', $filedata)) { // The /s switch on preg_match() forces preg_match() NOT to treat newline (0x0A) characters as special chars but do a binary match 224 225 // Format detected but not supported 226 if (!@$info['module'] || !@$info['group']) { 227 fclose($this->fp); 228 $this->info['fileformat'] = $name; 229 $this->info['mime_type'] = $info['mime_type']; 230 $this->warning('Format only detected. Parsing not available yet.'); 231 $this->info['warning'] = $this->warnings; 232 return $this->info; 233 } 234 235 $determined_format = $info; // copy $info deleted by foreach() 236 continue; 237 } 238 } 239 240 // Unable to determine file format 241 if (!@$determined_format) { 242 243 // Too many mp3 encoders on the market put gabage in front of mpeg files 244 // use assume format on these if format detection failed 245 if (preg_match('/\.mp[123a]$/i', $filename)) { 246 $determined_format = $file_format_array['mp3']; 247 } 248 249 else { 250 fclose($this->fp); 251 throw new getid3_exception('Unable to determine file format'); 252 } 253 } 254 255 // Free memory 256 unset($file_format_array); 257 258 // Check for illegal ID3 tags 259 if (@$determined_format['fail_id3'] && (@$this->info['id3v1'] || @$this->info['id3v2'])) { 260 if ($determined_format['fail_id3'] === 'ERROR') { 261 fclose($this->fp); 262 throw new getid3_exception('ID3 tags not allowed on this file type.'); 263 } 264 elseif ($determined_format['fail_id3'] === 'WARNING') { 265 @$this->info['id3v1'] and $this->warning('ID3v1 tags not allowed on this file type.'); 266 @$this->info['id3v2'] and $this->warning('ID3v2 tags not allowed on this file type.'); 267 } 268 } 269 270 // Check for illegal APE tags 271 if (@$determined_format['fail_ape'] && @$this->info['tags']['ape']) { 272 if ($determined_format['fail_ape'] === 'ERROR') { 273 fclose($this->fp); 274 throw new getid3_exception('APE tags not allowed on this file type.'); 275 } elseif ($determined_format['fail_ape'] === 'WARNING') { 276 $this->warning('APE tags not allowed on this file type.'); 277 } 278 } 279 280 281 // Set mime type 282 $this->info['mime_type'] = $determined_format['mime_type']; 283 284 // Calc module file name 285 $determined_format['include'] = 'module.'.$determined_format['group'].'.'.$determined_format['module'].'.php'; 286 287 // Supported format signature pattern detected, but module deleted. 288 if (!file_exists($this->include_path.$determined_format['include'])) { 289 fclose($this->fp); 290 throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', was removed.'); 291 } 292 293 // Include module 294 $this->include_module($determined_format['group'].'.'.$determined_format['module']); 295 296 // Instantiate module class and analyze 297 $class_name = 'getid3_'.$determined_format['module']; 298 if (!class_exists($class_name)) { 299 throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', is corrupt.'); 300 } 301 $class = new $class_name($this); 302 303 try { 304 $this->option_analyze and $class->Analyze(); 305 } 306 catch (getid3_exception $e) { 307 throw $e; 308 } 309 catch (Exception $e) { 310 throw new getid3_exception('Corrupt file.'); 311 } 312 313 // Close file 314 fclose($this->fp); 315 316 // Optional - Process all tags - copy to 'tags' and convert charsets 317 if ($this->option_tags_process) { 318 $this->HandleAllTags(); 319 } 320 321 322 //// Optional - perform more calculations 323 if ($this->option_extra_info) { 324 325 // Set channelmode on audio 326 if (@$this->info['audio']['channels'] == '1') { 327 $this->info['audio']['channelmode'] = 'mono'; 328 } elseif (@$this->info['audio']['channels'] == '2') { 329 $this->info['audio']['channelmode'] = 'stereo'; 330 } 331 332 // Calculate combined bitrate - audio + video 333 $combined_bitrate = 0; 334 $combined_bitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); 335 $combined_bitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); 336 if (($combined_bitrate > 0) && empty($this->info['bitrate'])) { 337 $this->info['bitrate'] = $combined_bitrate; 338 } 339 if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) { 340 $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; 341 } 342 343 // Set playtime string 344 if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { 345 $this->info['playtime_string'] = floor(round($this->info['playtime_seconds']) / 60) . ':' . str_pad(floor(round($this->info['playtime_seconds']) % 60), 2, 0, STR_PAD_LEFT);; 346 } 347 348 349 // CalculateCompressionRatioVideo() { 350 if (@$this->info['video'] && @$this->info['video']['resolution_x'] && @$this->info['video']['resolution_y'] && @$this->info['video']['bits_per_sample']) { 351 352 // From static image formats 353 if (in_array($this->info['video']['dataformat'], array ('bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff'))) { 354 $frame_rate = 1; 355 $bitrate_compressed = $this->info['filesize'] * 8; 356 } 357 358 // From video formats 359 else { 360 $frame_rate = @$this->info['video']['frame_rate']; 361 $bitrate_compressed = @$this->info['video']['bitrate']; 362 } 363 364 if ($frame_rate && $bitrate_compressed) { 365 $this->info['video']['compression_ratio'] = $bitrate_compressed / ($this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $frame_rate); 366 } 367 } 368 369 370 // CalculateCompressionRatioAudio() { 371 if (@$this->info['audio']['bitrate'] && @$this->info['audio']['channels'] && @$this->info['audio']['sample_rate']) { 372 $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (@$this->info['audio']['bits_per_sample'] ? $this->info['audio']['bits_per_sample'] : 16)); 373 } 374 375 if (@$this->info['audio']['streams']) { 376 foreach ($this->info['audio']['streams'] as $stream_number => $stream_data) { 377 if (@$stream_data['bitrate'] && @$stream_data['channels'] && @$stream_data['sample_rate']) { 378 $this->info['audio']['streams'][$stream_number]['compression_ratio'] = $stream_data['bitrate'] / ($stream_data['channels'] * $stream_data['sample_rate'] * (@$stream_data['bits_per_sample'] ? $stream_data['bits_per_sample'] : 16)); 379 } 380 } 381 } 382 383 384 // CalculateReplayGain() { 385 if (@$this->info['replay_gain']) { 386 if (!@$this->info['replay_gain']['reference_volume']) { 387 $this->info['replay_gain']['reference_volume'] = 89; 388 } 389 if (isset($this->info['replay_gain']['track']['adjustment'])) { 390 $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; 391 } 392 if (isset($this->info['replay_gain']['album']['adjustment'])) { 393 $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; 394 } 395 396 if (isset($this->info['replay_gain']['track']['peak'])) { 397 $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['track']['peak']); 398 } 399 if (isset($this->info['replay_gain']['album']['peak'])) { 400 $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['album']['peak']); 401 } 402 } 403 404 405 // ProcessAudioStreams() { 406 if (@!$this->info['audio']['streams'] && (@$this->info['audio']['bitrate'] || @$this->info['audio']['channels'] || @$this->info['audio']['sample_rate'])) { 407 foreach ($this->info['audio'] as $key => $value) { 408 if ($key != 'streams') { 409 $this->info['audio']['streams'][0][$key] = $value; 410 } 411 } 412 } 413 } 414 415 416 // Get the md5/sha1sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags. 417 if ($this->option_md5_data || $this->option_sha1_data) { 418 419 // Load data-hash library if needed 420 $this->include_module('lib.data_hash'); 421 422 if ($this->option_sha1_data) { 423 new getid3_lib_data_hash($this, 'sha1'); 424 } 425 426 if ($this->option_md5_data) { 427 428 // no md5_data_source or option disabled -- md5_data_source supported by FLAC, MAC, OptimFROG, Wavpack4 429 if (!$this->option_md5_data_source || !@$this->info['md5_data_source']) { 430 new getid3_lib_data_hash($this, 'md5'); 431 } 432 433 // copy md5_data_source to md5_data if option set to true 434 elseif ($this->option_md5_data_source && @$this->info['md5_data_source']) { 435 $this->info['md5_data'] = $this->info['md5_data_source']; 436 } 437 } 438 } 439 440 // Set warnings 441 if ($this->warnings) { 442 $this->info['warning'] = $this->warnings; 443 } 444 445 // Return result 446 return $this->info; 447 } 448 449 450 451 // Return array of warnings 452 public function warnings() { 453 454 return $this->warnings; 455 } 456 457 458 459 // Add warning(s) to $this->warnings[] 460 public function warning($message) { 461 462 if (is_array($message)) { 463 $this->warnings = array_merge($this->warnings, $message); 464 } 465 else { 466 $this->warnings[] = $message; 467 } 468 } 469 470 471 472 // Clear all warnings when cloning 473 public function __clone() { 474 475 $this->warnings = array (); 476 477 // Copy info array, otherwise it will be a reference. 478 $temp = $this->info; 479 unset($this->info); 480 $this->info = $temp; 481 } 482 483 484 485 // Convert string between charsets -- iconv() wrapper 486 public function iconv($in_charset, $out_charset, $string, $drop01 = false) { 487 488 if ($drop01 && ($string === "\x00" || $string === "\x01")) { 489 return ''; 490 } 491 492 493 if (!$this->iconv_present) { 494 return getid3_iconv_replacement::iconv($in_charset, $out_charset, $string); 495 } 496 497 498 // iconv() present 499 if ($result = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { 500 501 if ($out_charset == 'ISO-8859-1') { 502 return rtrim($result, "\x00"); 503 } 504 return $result; 505 } 506 507 $this->warning('iconv() was unable to convert the string: "' . $string . '" from ' . $in_charset . ' to ' . $out_charset); 508 return $string; 509 } 510 511 512 513 public function include_module($name) { 514 515 if (!file_exists($this->include_path.'module.'.$name.'.php')) { 516 throw new getid3_exception('Required module.'.$name.'.php is missing.'); 517 } 518 519 include_once($this->include_path.'module.'.$name.'.php'); 520 } 521 522 523 524 public function include_module_optional($name) { 525 526 if (!file_exists($this->include_path.'module.'.$name.'.php')) { 527 return; 528 } 529 530 include_once($this->include_path.'module.'.$name.'.php'); 531 return true; 532 } 533 534 535 // Return array containing information about all supported formats 536 public static function GetFileFormatArray() { 537 538 static $format_info = array ( 539 540 // Audio formats 541 542 // AC-3 - audio - Dolby AC-3 / Dolby Digital 543 'ac3' => array ( 544 'pattern' => '^\x0B\x77', 545 'group' => 'audio', 546 'module' => 'ac3', 547 'mime_type' => 'audio/ac3', 548 ), 549 550 // AAC - audio - Advanced Audio Coding (AAC) - ADIF format 551 'adif' => array ( 552 'pattern' => '^ADIF', 553 'group' => 'audio', 554 'module' => 'aac_adif', 555 'mime_type' => 'application/octet-stream', 556 'fail_ape' => 'WARNING', 557 ), 558 559 560 // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) 561 'adts' => array ( 562 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', 563 'group' => 'audio', 564 'module' => 'aac_adts', 565 'mime_type' => 'application/octet-stream', 566 'fail_ape' => 'WARNING', 567 ), 568 569 570 // AU - audio - NeXT/Sun AUdio (AU) 571 'au' => array ( 572 'pattern' => '^\.snd', 573 'group' => 'audio', 574 'module' => 'au', 575 'mime_type' => 'audio/basic', 576 ), 577 578 // AVR - audio - Audio Visual Research 579 'avr' => array ( 580 'pattern' => '^2BIT', 581 'group' => 'audio', 582 'module' => 'avr', 583 'mime_type' => 'application/octet-stream', 584 ), 585 586 // BONK - audio - Bonk v0.9+ 587 'bonk' => array ( 588 'pattern' => '^\x00(BONK|INFO|META| ID3)', 589 'group' => 'audio', 590 'module' => 'bonk', 591 'mime_type' => 'audio/xmms-bonk', 592 ), 593 594 // DTS - audio - Dolby Theatre System 20 // public: Settings 21 var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv()) 22 // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE 23 24 var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' 25 26 var $tempdir = '*'; // default '*' should use system temp dir 27 28 // public: Optional tag checks - disable for speed. 29 var $option_tag_id3v1 = true; // Read and process ID3v1 tags 30 var $option_tag_id3v2 = true; // Read and process ID3v2 tags 31 var $option_tag_lyrics3 = true; // Read and process Lyrics3 tags 32 var $option_tag_apetag = true; // Read and process APE tags 33 var $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding 34 var $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities 35 36 // public: Optional tag/comment calucations 37 var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc 38 39 // public: Optional calculations 40 var $option_md5_data = false; // Get MD5 sum of data part - slow 41 var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG 42 var $option_sha1_data = false; // Get SHA1 sum of data part - slow 43 var $option_max_2gb_check = true; // Check whether file is larger than 2 Gb and thus not supported by PHP 44 45 // private 46 var $filename; 47 48 49 // public: constructor 50 function getID3() 51 { 52 53 $this->startup_error = ''; 54 $this->startup_warning = ''; 55 56 // Check for PHP version >= 4.2.0 57 if (phpversion() < '4.2.0') { 58 $this->startup_error .= 'getID3() requires PHP v4.2.0 or higher - you are running v'.phpversion(); 59 } 60 61 // Check memory 62 $memory_limit = ini_get('memory_limit'); 63 if (eregi('([0-9]+)M', $memory_limit, $matches)) { 64 // could be stored as "16M" rather than 16777216 for example 65 $memory_limit = $matches[1] * 1048576; 66 } 67 if ($memory_limit <= 0) { 68 // memory limits probably disabled 69 } elseif ($memory_limit <= 3145728) { 70 $this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini'; 71 } elseif ($memory_limit <= 12582912) { 72 $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'; 73 } 74 75 // Check safe_mode off 76 if ((bool) ini_get('safe_mode')) { 77 $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.'); 78 } 79 80 81 // define a constant rather than looking up every time it is needed 82 if (!defined('GETID3_OS_ISWINDOWS')) { 83 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { 84 define('GETID3_OS_ISWINDOWS', true); 85 } else { 86 define('GETID3_OS_ISWINDOWS', false); 87 } 88 } 89 90 // Get base path of getID3() - ONCE 91 if (!defined('GETID3_INCLUDEPATH')) { 92 foreach (get_included_files() as $key => $val) { 93 if (basename($val) == 'getid3.php') { 94 define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR); 95 break; 96 } 97 } 98 } 99 100 // Load support library 101 if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { 102 $this->startup_error .= 'getid3.lib.php is missing or corrupt'; 103 } 104 105 106 // Needed for Windows only: 107 // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC 108 // as well as other helper functions such as head, tail, md5sum, etc 109 // IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent 110 // ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/" 111 // IMPORTANT: This path must include the trailing slash 112 if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) { 113 114 $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path 115 116 if (!is_dir($helperappsdir)) { 117 $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; 118 } elseif (strpos(realpath($helperappsdir), ' ') !== false) { 119 $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir)); 120 foreach ($DirPieces as $key => $value) { 121 if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) { 122 if (strpos($value, '.') > 8) { 123 $value = substr($value, 0, 6).'~1'; 124 } 125 } elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) { 126 $value = substr($value, 0, 6).'~1'; 127 } 128 $DirPieces[$key] = strtoupper($value); 129 } 130 $this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(DIRECTORY_SEPARATOR, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names.'; 131 } 132 define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR); 133 } 134 135 } 136 137 138 // public: setOption 139 function setOption($optArray) { 140 if (!is_array($optArray) || empty($optArray)) { 141 return false; 142 } 143 foreach ($optArray as $opt => $val) { 144 //if (isset($this, $opt) === false) { 145 if (isset($this->$opt) === false) { 146 continue; 147 } 148 $this->$opt = $val; 149 } 150 return true; 151 } 152 153 154 // public: analyze file - replaces GetAllFileInfo() and GetTagOnly() 155 function analyze($filename) { 156 157 if (!empty($this->startup_error)) { 158 return $this->error($this->startup_error); 159 } 160 if (!empty($this->startup_warning)) { 161 $this->warning($this->startup_warning); 162 } 163 164 // init result array and set parameters 165 $this->info = array(); 166 $this->info['GETID3_VERSION'] = GETID3_VERSION; 167 168 // Check encoding/iconv support 169 if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { 170 $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; 171 if (GETID3_OS_ISWINDOWS) { 172 $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; 173 } else { 174 $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; 175 } 176 return $this->error($errormessage); 177 } 178 179 // Disable magic_quotes_runtime, if neccesary 180 $old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime 181 if ($old_magic_quotes_runtime) { 182 set_magic_quotes_runtime(0); // turn off magic_quotes_runtime 183 if (get_magic_quotes_runtime()) { 184 return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled'); 185 } 186 } 187 188 // remote files not supported 189 if (preg_match('/^(ht|f)tp:\/\//', $filename)) { 190 return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first'); 191 } 192 193 $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename); 194 $filename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#', DIRECTORY_SEPARATOR, $filename); 195 196 // open local file 197 if (file_exists($filename) && ($fp = @fopen($filename, 'rb'))) { 198 // great 199 } else { 200 return $this->error('Could not open file "'.$filename.'"'); 201 } 202 203 // set parameters 204 $this->info['filesize'] = filesize($filename); 205 206 // option_max_2gb_check 207 if ($this->option_max_2gb_check) { 208 // PHP doesn't support integers larger than 31-bit (~2GB) 209 // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize 210 // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer 211 fseek($fp, 0, SEEK_END); 212 if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) || 213 ($this->info['filesize'] < 0) || 214 (ftell($fp) < 0)) { 215 $real_filesize = false; 216 if (GETID3_OS_ISWINDOWS) { 217 $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"'; 218 $dir_output = `$commandline`; 219 if (eregi('1 File\(s\)[ ]+([0-9]+) bytes', $dir_output, $matches)) { 220 $real_filesize = (float) $matches[1]; 221 } 222 } else { 223 $commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename); 224 $dir_output = `$commandline`; 225 if (eregi('([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.preg_quote($filename).'$', $dir_output, $matches)) { 226 $real_filesize = (float) $matches[1]; 227 } 228 } 229 if ($real_filesize === false) { 230 unset($this->info['filesize']); 231 fclose($fp); 232 return $this->error('File is most likely larger than 2GB and is not supported by PHP'); 233 } elseif ($real_filesize < pow(2, 31)) { 234 unset($this->info['filesize']); 235 fclose($fp); 236 return $this->error('PHP seems to think the file is larger than 2GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org'); 237 } 238 $this->info['filesize'] = $real_filesize; 239 $this->error('File is larger than 2GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.'); 240 } 241 } 242 243 // set more parameters 244 $this->info['avdataoffset'] = 0; 245 $this->info['avdataend'] = $this->info['filesize']; 246 $this->info['fileformat'] = ''; // filled in later 247 $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used 248 $this->info['video']['dataformat'] = ''; // filled in later, unset if not used 249 $this->info['tags'] = array(); // filled in later, unset if not used 250 $this->info['error'] = array(); // filled in later, unset if not used 251 $this->info['warning'] = array(); // filled in later, unset if not used 252 $this->info['comments'] = array(); // filled in later, unset if not used 253 $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired 254 255 // set redundant parameters - might be needed in some include file 256 $this->info['filename'] = basename($filename); 257 $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); 258 $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; 259 260 261 // handle ID3v2 tag - done first - already at beginning of file 262 // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect 263 if ($this->option_tag_id3v2) { 264 265 $GETID3_ERRORARRAY = &$this->info['warning']; 266 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) { 267 $tag = new getid3_id3v2($fp, $this->info); 268 unset($tag); 269 } 270 271 } else { 272 273 fseek($fp, 0, SEEK_SET); 274 $header = fread($fp, 10); 275 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 276 $this->info['id3v2']['header'] = true; 277 $this->info['id3v2']['majorversion'] = ord($header{3}); 278 $this->info['id3v2']['minorversion'] = ord($header{4}); 279 $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length 280 281 $this->info['id3v2']['tag_offset_start'] = 0; 282 $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength']; 283 $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end']; 284 } 285 286 } 287 288 289 // handle ID3v1 tag 290 if ($this->option_tag_id3v1) { 291 if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) { 292 return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.'); 293 } 294 $tag = new getid3_id3v1($fp, $this->info); 295 unset($tag); 296 } 297 298 // handle APE tag 299 if ($this->option_tag_apetag) { 300 if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) { 301 return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.'); 302 } 303 $tag = new getid3_apetag($fp, $this->info); 304 unset($tag); 305 } 306 307 // handle lyrics3 tag 308 if ($this->option_tag_lyrics3) { 309 if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) { 310 return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.'); 311 } 312 $tag = new getid3_lyrics3($fp, $this->info); 313 unset($tag); 314 } 315 316 // read 32 kb file data 317 fseek($fp, $this->info['avdataoffset'], SEEK_SET); 318 $formattest = fread($fp, 32774); 319 320 // determine format 321 $determined_format = $this->GetFileFormat($formattest, $filename); 322 323 // unable to determine file format 324 if (!$determined_format) { 325 fclose($fp); 326 return $this->error('unable to determine file format'); 327 } 328 329 // check for illegal ID3 tags 330 if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { 331 if ($determined_format['fail_id3'] === 'ERROR') { 332 fclose($fp); 333 return $this->error('ID3 tags not allowed on this file type.'); 334 } elseif ($determined_format['fail_id3'] === 'WARNING') { 335 $this->info['warning'][] = 'ID3 tags not allowed on this file type.'; 336 } 337 } 338 339 // check for illegal APE tags 340 if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { 341 if ($determined_format['fail_ape'] === 'ERROR') { 342 fclose($fp); 343 return $this->error('APE tags not allowed on this file type.'); 344 } elseif ($determined_format['fail_ape'] === 'WARNING') { 345 $this->info['warning'][] = 'APE tags not allowed on this file type.'; 346 } 347 } 348 349 // set mime type 350 $this->info['mime_type'] = $determined_format['mime_type']; 351 352 // supported format signature pattern detected, but module deleted 353 if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { 354 fclose($fp); 355 return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.'); 356 } 357 358 // module requires iconv support 359 if (!function_exists('iconv') && @$determined_format['iconv_req']) { 360 return $this->error('iconv support is required for this module ('.$determined_format['include'].').'); 361 } 362 363 // include module 364 include_once(GETID3_INCLUDEPATH.$determined_format['include']); 365 366 // instantiate module class 367 $class_name = 'getid3_'.$determined_format['module']; 368 if (!class_exists($class_name)) { 369 return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.'); 370 } 371 if (isset($determined_format['option'])) { 372 $class = new $class_name($fp, $this->info, $determined_format['option']); 373 } else { 374 $class = new $class_name($fp, $this->info); 375 } 376 unset($class); 377 378 // close file 379 fclose($fp); 380 381 // process all tags - copy to 'tags' and convert charsets 382 if ($this->option_tags_process) { 383 $this->HandleAllTags(); 384 } 385 386 // perform more calculations 387 if ($this->option_extra_info) { 388 $this->ChannelsBitratePlaytimeCalculations(); 389 $this->CalculateCompressionRatioVideo(); 390 $this->CalculateCompressionRatioAudio(); 391 $this->CalculateReplayGain(); 392 $this->ProcessAudioStreams(); 393 } 394 395 // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags 396 if ($this->option_md5_data) { 397 // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too 398 if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { 399 $this->getHashdata('md5'); 400 } 401 } 402 403 // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags 404 if ($this->option_sha1_data) { 405 $this->getHashdata('sha1'); 406 } 407 408 // remove undesired keys 409 $this->CleanUp(); 410 411 // restore magic_quotes_runtime setting 412 set_magic_quotes_runtime($old_magic_quotes_runtime); 413 414 // return info array 415 return $this->info; 416 } 417 418 419 // private: error handling 420 function error($message) { 421 422 $this->CleanUp(); 423 424 $this->info['error'][] = $message; 425 return $this->info; 426 } 427 428 429 // private: warning handling 430 function warning($message) { 431 $this->info['warning'][] = $message; 432 return true; 433 } 434 435 436 // private: CleanUp 437 function CleanUp() { 438 439 // remove possible empty keys 440 $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate'); 441 foreach ($AVpossibleEmptyKeys as $dummy => $key) { 442 if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) { 443 unset($this->info['audio'][$key]); 444 } 445 if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) { 446 unset($this->info['video'][$key]); 447 } 448 } 449 450 // remove empty root keys 451 if (!empty($this->info)) { 452 foreach ($this->info as $key => $value) { 453 if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) { 454 unset($this->info[$key]); 455 } 456 } 457 } 458 459 // remove meaningless entries from unknown-format files 460 if (empty($this->info['fileformat'])) { 461 if (isset($this->info['avdataoffset'])) { 462 unset($this->info['avdataoffset']); 463 } 464 if (isset($this->info['avdataend'])) { 465 unset($this->info['avdataend']); 466 } 467 } 468 } 469 470 471 // return array containing information about all supported formats 472 function GetFileFormatArray() { 473 static $format_info = array(); 474 if (empty($format_info)) { 475 $format_info = array( 476 477 // Audio formats 478 479 // AC-3 - audio - Dolby AC-3 / Dolby Digital 480 'ac3' => array( 481 'pattern' => '^\x0B\x77', 482 'group' => 'audio', 483 'module' => 'ac3', 484 'mime_type' => 'audio/ac3', 485 ), 486 487 // AAC - audio - Advanced Audio Coding (AAC) - ADIF format 488 'adif' => array( 489 'pattern' => '^ADIF', 490 'group' => 'audio', 491 'module' => 'aac', 492 'option' => 'adif', 493 'mime_type' => 'application/octet-stream', 494 'fail_ape' => 'WARNING', 495 ), 496 497 498 // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) 499 'adts' => array( 500 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', 501 'group' => 'audio', 502 'module' => 'aac', 503 'option' => 'adts', 504 'mime_type' => 'application/octet-stream', 505 'fail_ape' => 'WARNING', 506 ), 507 508 509 // AU - audio - NeXT/Sun AUdio (AU) 510 'au' => array( 511 'pattern' => '^\.snd', 512 'group' => 'audio', 513 'module' => 'au', 514 'mime_type' => 'audio/basic', 515 ), 516 517 // AVR - audio - Audio Visual Research 518 'avr' => array( 519 'pattern' => '^2BIT', 520 'group' => 'audio', 521 'module' => 'avr', 522 'mime_type' => 'application/octet-stream', 523 ), 524 525 // BONK - audio - Bonk v0.9+ 526 'bonk' => array( 527 'pattern' => '^\x00(BONK|INFO|META| ID3)', 528 'group' => 'audio', 529 'module' => 'bonk', 530 'mime_type' => 'audio/xmms-bonk', 531 ), 532 533 // DSS - audio - Digital Speech Standard 534 'dss' => array( 535 'pattern' => '^[\x02]dss', 536 'group' => 'audio', 537 'module' => 'dss', 538 'mime_type' => 'application/octet-stream', 539 ), 540 541 // DTS - audio - Dolby Theatre System 595 542 'dts' => array( 596 543 'pattern' => '^\x7F\xFE\x80\x01', … … 600 547 ), 601 548 602 // FLAC - audio - Free Lossless Audio Codec 603 'flac' => array ( 604 'pattern' => '^fLaC', 605 'group' => 'audio', 606 'module' => 'xiph', 607 'mime_type' => 'audio/x-flac', 608 ), 609 610 // LA - audio - Lossless Audio (LA) 611 'la' => array ( 612 'pattern' => '^LA0[2-4]', 613 'group' => 'audio', 614 'module' => 'la', 615 'mime_type' => 'application/octet-stream', 616 ), 617 618 // LPAC - audio - Lossless Predictive Audio Compression (LPAC) 619 'lpac' => array ( 620 'pattern' => '^LPAC', 621 'group' => 'audio', 622 'module' => 'lpac', 623 'mime_type' => 'application/octet-stream', 624 ), 625 626 // MIDI - audio - MIDI (Musical Instrument Digital Interface) 627 'midi' => array ( 628 'pattern' => '^MThd', 629 'group' => 'audio', 630 'module' => 'midi', 631 'mime_type' => 'audio/midi', 632 ), 633 634 // MAC - audio - Monkey's Audio Compressor 635 'mac' => array ( 636 'pattern' => '^MAC ', 637 'group' => 'audio', 638 'module' => 'monkey', 639 'mime_type' => 'application/octet-stream', 640 ), 641 642 // MOD - audio - MODule (assorted sub-formats) 643 'mod' => array ( 644 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)', 645 'mime_type' => 'audio/mod', 646 ), 647 648 // MOD - audio - MODule (Impulse Tracker) 649 'it' => array ( 650 'pattern' => '^IMPM', 651 'mime_type' => 'audio/it', 652 ), 653 654 // MOD - audio - MODule (eXtended Module, various sub-formats) 655 'xm' => array ( 656 'pattern' => '^Extended Module', 657 'mime_type' => 'audio/xm', 658 ), 659 660 // MOD - audio - MODule (ScreamTracker) 661 's3m' => array ( 662 'pattern' => '^.{44}SCRM', 663 'mime_type' => 'audio/s3m', 664 ), 665 666 // MPC - audio - Musepack / MPEGplus SV7+ 667 'mpc' => array ( 668 'pattern' => '^(MP\+)', 669 'group' => 'audio', 670 'module' => 'mpc', 671 'mime_type' => 'audio/x-musepack', 672 ), 673 674 // MPC - audio - Musepack / MPEGplus SV4-6 675 'mpc_old' => array ( 676 'pattern' => '^([\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])', 677 'group' => 'audio', 678 'module' => 'mpc_old', 679 'mime_type' => 'application/octet-stream', 680 ), 681 682 683 // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) 684 'mp3' => array ( 685 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', 686 'group' => 'audio', 687 'module' => 'mp3', 688 'mime_type' => 'audio/mpeg', 689 ), 690 691 // OFR - audio - OptimFROG 692 'ofr' => array ( 693 'pattern' => '^(\*RIFF|OFR)', 694 'group' => 'audio', 695 'module' => 'optimfrog', 696 'mime_type' => 'application/octet-stream', 697 ), 698 699 // RKAU - audio - RKive AUdio compressor 700 'rkau' => array ( 701 'pattern' => '^RKA', 702 'group' => 'audio', 703 'module' => 'rkau', 704 'mime_type' => 'application/octet-stream', 705 ), 706 707 // SHN - audio - Shorten 708 'shn' => array ( 709 'pattern' => '^ajkg', 710 'group' => 'audio', 711 'module' => 'shorten', 712 'mime_type' => 'audio/xmms-shn', 713 'fail_id3' => 'ERROR', 714 'fail_ape' => 'ERROR', 715 ), 716 717 // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) 718 'tta' => array ( 719 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' 720 'group' => 'audio', 721 'module' => 'tta', 722 'mime_type' => 'application/octet-stream', 723 ), 724 725 // VOC - audio - Creative Voice (VOC) 726 'voc' => array ( 727 'pattern' => '^Creative Voice File', 728 'group' => 'audio', 729 'module' => 'voc', 730 'mime_type' => 'audio/voc', 731 ), 732 733 // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) 734 'vqf' => array ( 735 'pattern' => '^TWIN', 736 'group' => 'audio', 737 'module' => 'vqf', 738 'mime_type' => 'application/octet-stream', 739 ), 740 741 // WV - audio - WavPack (v4.0+) 742 'vw' => array( 743 'pattern' => '^wvpk', 744 'group' => 'audio', 745 'module' => 'wavpack', 746 'mime_type' => 'application/octet-stream', 747 ), 748 749 750 // Audio-Video formats 751 752 // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio 753 'asf' => array ( 754 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', 755 'group' => 'audio-video', 756 'module' => 'asf', 757 'mime_type' => 'video/x-ms-asf', 758 ), 759 760 // BINK - audio/video - Bink / Smacker 761 'bink' => array( 762 'pattern' => '^(BIK|SMK)', 763 'mime_type' => 'application/octet-stream', 764 ), 765 766 // FLV - audio/video - FLash Video 767 'flv' => array( 768 'pattern' => '^FLV\x01', 769 'group' => 'audio-video', 770 'module' => 'flv', 771 'mime_type' => 'video/x-flv', 772 ), 773 774 // MKAV - audio/video - Mastroka 775 'matroska' => array ( 776 'pattern' => '^\x1A\x45\xDF\xA3', 777 'mime_type' => 'application/octet-stream', 778 ), 779 780 // MPEG - audio/video - MPEG (Moving Pictures Experts Group) 781 'mpeg' => array ( 782 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', 783 'group' => 'audio-video', 784 'module' => 'mpeg', 785 'mime_type' => 'video/mpeg', 786 ), 787 788 // NSV - audio/video - Nullsoft Streaming Video (NSV) 789 'nsv' => array ( 790 'pattern' => '^NSV[sf]', 791 'group' => 'audio-video', 792 'module' => 'nsv', 793 'mime_type' => 'application/octet-stream', 794 ), 795 796 // Ogg - audio/video - Ogg (Ogg Vorbis, OggFLAC, Speex, Ogg Theora(*), Ogg Tarkin(*)) 797 'ogg' => array ( 798 'pattern' => '^OggS', 799 'group' => 'audio', 800 'module' => 'xiph', 801 'mime_type' => 'application/ogg', 802 'fail_id3' => 'WARNING', 803 'fail_ape' => 'WARNING', 804 ), 805 806 // QT - audio/video - Quicktime 807 'quicktime' => array ( 808 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', 809 'group' => 'audio-video', 810 'module' => 'quicktime', 811 'mime_type' => 'video/quicktime', 812 ), 813 814 // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF) 815 'riff' => array ( 816 'pattern' => '^(RIFF|SDSS|FORM)', 817 'group' => 'audio-video', 818 'module' => 'riff', 819 'mime_type' => 'audio/x-wave', 820 'fail_ape' => 'WARNING', 821 ), 822 823 // Real - audio/video - RealAudio, RealVideo 824 'real' => array ( 825 'pattern' => '^(\.RMF|.ra)', 826 'group' => 'audio-video', 827 'module' => 'real', 828 'mime_type' => 'audio/x-realaudio', 829 ), 830 831 // SWF - audio/video - ShockWave Flash 832 'swf' => array ( 833 'pattern' => '^(F|C)WS', 834 'group' => 'audio-video', 835 'module' => 'swf', 836 'mime_type' => 'application/x-shockwave-flash', 837 ), 838 839 840 // Still-Image formats 841 842 // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) 843 'bmp' => array ( 844 'pattern' => '^BM', 845 'group' => 'graphic', 846 'module' => 'bmp', 847 'mime_type' => 'image/bmp', 848 'fail_id3' => 'ERROR', 849 'fail_ape' => 'ERROR', 850 ), 851 852 // GIF - still image - Graphics Interchange Format 853 'gif' => array ( 854 'pattern' => '^GIF', 855 'group' => 'graphic', 856 'module' => 'gif', 857 'mime_type' => 'image/gif', 858 'fail_id3' => 'ERROR', 859 'fail_ape' => 'ERROR', 860 ), 861 862 // JPEG - still image - Joint Photographic Experts Group (JPEG) 863 'jpeg' => array ( 864 'pattern' => '^\xFF\xD8\xFF', 865 'group' => 'graphic', 866 'module' => 'jpeg', 867 'mime_type' => 'image/jpeg', 868 'fail_id3' => 'ERROR', 869 'fail_ape' => 'ERROR', 870 ), 871 872 // PCD - still image - Kodak Photo CD 873 'pcd' => array ( 874 'pattern' => '^.{2048}PCD_IPI\x00', 875 'group' => 'graphic', 876 'module' => 'pcd', 877 'mime_type' => 'image/x-photo-cd', 878 'fail_id3' => 'ERROR', 879 'fail_ape' => 'ERROR', 880 ), 881 882 883 // PNG - still image - Portable Network Graphics (PNG) 884 'png' => array ( 885 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', 886 'group' => 'graphic', 887 'module' => 'png', 888 'mime_type' => 'image/png', 889 'fail_id3' => 'ERROR', 890 'fail_ape' => 'ERROR', 891 ), 549 // FLAC - audio - Free Lossless Audio Codec 550 'flac' => array( 551 'pattern' => '^fLaC', 552 'group' => 'audio', 553 'module' => 'flac', 554 'mime_type' => 'audio/x-flac', 555 ), 556 557 // LA - audio - Lossless Audio (LA) 558 'la' => array( 559 'pattern' => '^LA0[2-4]', 560 'group' => 'audio', 561 'module' => 'la', 562 'mime_type' => 'application/octet-stream', 563 ), 564 565 // LPAC - audio - Lossless Predictive Audio Compression (LPAC) 566 'lpac' => array( 567 'pattern' => '^LPAC', 568 'group' => 'audio', 569 'module' => 'lpac', 570 'mime_type' => 'application/octet-stream', 571 ), 572 573 // MIDI - audio - MIDI (Musical Instrument Digital Interface) 574 'midi' => array( 575 'pattern' => '^MThd', 576 'group' => 'audio', 577 'module' => 'midi', 578 'mime_type' => 'audio/midi', 579 ), 580 581 // MAC - audio - Monkey's Audio Compressor 582 'mac' => array( 583 'pattern' => '^MAC ', 584 'group' => 'audio', 585 'module' => 'monkey', 586 'mime_type' => 'application/octet-stream', 587 ), 588 589 // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available 590 // // MOD - audio - MODule (assorted sub-formats) 591 // 'mod' => array( 592 // 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', 593 // 'group' => 'audio', 594 // 'module' => 'mod', 595 // 'option' => 'mod', 596 // 'mime_type' => 'audio/mod', 597 // ), 598 599 // MOD - audio - MODule (Impulse Tracker) 600 'it' => array( 601 'pattern' => '^IMPM', 602 'group' => 'audio', 603 'module' => 'mod', 604 'option' => 'it', 605 'mime_type' => 'audio/it', 606 ), 607 608 // MOD - audio - MODule (eXtended Module, various sub-formats) 609 'xm' => array( 610 'pattern' => '^Extended Module', 611 'group' => 'audio', 612 'module' => 'mod', 613 'option' => 'xm', 614 'mime_type' => 'audio/xm', 615 ), 616 617 // MOD - audio - MODule (ScreamTracker) 618 's3m' => array( 619 'pattern' => '^.{44}SCRM', 620 'group' => 'audio', 621 'module' => 'mod', 622 'option' => 's3m', 623 'mime_type' => 'audio/s3m', 624 ), 625 626 // MPC - audio - Musepack / MPEGplus 627 'mpc' => array( 628 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])', 629 'group' => 'audio', 630 'module' => 'mpc', 631 'mime_type' => 'audio/x-musepack', 632 ), 633 634 // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) 635 'mp3' => array( 636 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', 637 'group' => 'audio', 638 'module' => 'mp3', 639 'mime_type' => 'audio/mpeg', 640 ), 641 642 // OFR - audio - OptimFROG 643 'ofr' => array( 644 'pattern' => '^(\*RIFF|OFR)', 645 'group' => 'audio', 646 'module' => 'optimfrog', 647 'mime_type' => 'application/octet-stream', 648 ), 649 650 // RKAU - audio - RKive AUdio compressor 651 'rkau' => array( 652 'pattern' => '^RKA', 653 'group' => 'audio', 654 'module' => 'rkau', 655 'mime_type' => 'application/octet-stream', 656 ), 657 658 // SHN - audio - Shorten 659 'shn' => array( 660 'pattern' => '^ajkg', 661 'group' => 'audio', 662 'module' => 'shorten', 663 'mime_type' => 'audio/xmms-shn', 664 'fail_id3' => 'ERROR', 665 'fail_ape' => 'ERROR', 666 ), 667 668 // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) 669 'tta' => array( 670 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' 671 'group' => 'audio', 672 'module' => 'tta', 673 'mime_type' => 'application/octet-stream', 674 ), 675 676 // VOC - audio - Creative Voice (VOC) 677 'voc' => array( 678 'pattern' => '^Creative Voice File', 679 'group' => 'audio', 680 'module' => 'voc', 681 'mime_type' => 'audio/voc', 682 ), 683 684 // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) 685 'vqf' => array( 686 'pattern' => '^TWIN', 687 'group' => 'audio', 688 'module' => 'vqf', 689 'mime_type' => 'application/octet-stream', 690 ), 691 692 // WV - audio - WavPack (v4.0+) 693 'wv' => array( 694 'pattern' => '^wvpk', 695 'group' => 'audio', 696 'module' => 'wavpack', 697 'mime_type' => 'application/octet-stream', 698 ), 699 700 701 // Audio-Video formats 702 703 // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio 704 'asf' => array( 705 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', 706 'group' => 'audio-video', 707 'module' => 'asf', 708 'mime_type' => 'video/x-ms-asf', 709 'iconv_req' => false, 710 ), 711 712 // BINK - audio/video - Bink / Smacker 713 'bink' => array( 714 'pattern' => '^(BIK|SMK)', 715 'group' => 'audio-video', 716 'module' => 'bink', 717 'mime_type' => 'application/octet-stream', 718 ), 719 720 // FLV - audio/video - FLash Video 721 'flv' => array( 722 'pattern' => '^FLV\x01', 723 'group' => 'audio-video', 724 'module' => 'flv', 725 'mime_type' => 'video/x-flv', 726 ), 727 728 // MKAV - audio/video - Mastroka 729 'matroska' => array( 730 'pattern' => '^\x1A\x45\xDF\xA3', 731 'group' => 'audio-video', 732 'module' => 'matroska', 733 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska 734 ), 735 736 // MPEG - audio/video - MPEG (Moving Pictures Experts Group) 737 'mpeg' => array( 738 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', 739 'group' => 'audio-video', 740 'module' => 'mpeg', 741 'mime_type' => 'video/mpeg', 742 ), 743 744 // NSV - audio/video - Nullsoft Streaming Video (NSV) 745 'nsv' => array( 746 'pattern' => '^NSV[sf]', 747 'group' => 'audio-video', 748 'module' => 'nsv', 749 'mime_type' => 'application/octet-stream', 750 ), 751 752 // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*)) 753 'ogg' => array( 754 'pattern' => '^OggS', 755 'group' => 'audio', 756 'module' => 'ogg', 757 'mime_type' => 'application/ogg', 758 'fail_id3' => 'WARNING', 759 'fail_ape' => 'WARNING', 760 ), 761 762 // QT - audio/video - Quicktime 763 'quicktime' => array( 764 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', 765 'group' => 'audio-video', 766 'module' => 'quicktime', 767 'mime_type' => 'video/quicktime', 768 ), 769 770 // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF) 771 'riff' => array( 772 'pattern' => '^(RIFF|SDSS|FORM)', 773 'group' => 'audio-video', 774 'module' => 'riff', 775 'mime_type' => 'audio/x-wave', 776 'fail_ape' => 'WARNING', 777 ), 778 779 // Real - audio/video - RealAudio, RealVideo 780 'real' => array( 781 'pattern' => '^(\\.RMF|\\.ra)', 782 'group' => 'audio-video', 783 'module' => 'real', 784 'mime_type' => 'audio/x-realaudio', 785 ), 786 787 // SWF - audio/video - ShockWave Flash 788 'swf' => array( 789 'pattern' => '^(F|C)WS', 790 'group' => 'audio-video', 791 'module' => 'swf', 792 'mime_type' => 'application/x-shockwave-flash', 793 ), 794 795 796 // Still-Image formats 797 798 // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) 799 'bmp' => array( 800 'pattern' => '^BM', 801 'group' => 'graphic', 802 'module' => 'bmp', 803 'mime_type' => 'image/bmp', 804 'fail_id3' => 'ERROR', 805 'fail_ape' => 'ERROR', 806 ), 807 808 // GIF - still image - Graphics Interchange Format 809 'gif' => array( 810 'pattern' => '^GIF', 811 'group' => 'graphic', 812 'module' => 'gif', 813 'mime_type' => 'image/gif', 814 'fail_id3' => 'ERROR', 815 'fail_ape' => 'ERROR', 816 ), 817 818 // JPEG - still image - Joint Photographic Experts Group (JPEG) 819 'jpg' => array( 820 'pattern' => '^\xFF\xD8\xFF', 821 'group' => 'graphic', 822 'module' => 'jpg', 823 'mime_type' => 'image/jpeg', 824 'fail_id3' => 'ERROR', 825 'fail_ape' => 'ERROR', 826 ), 827 828 // PCD - still image - Kodak Photo CD 829 'pcd' => array( 830 'pattern' => '^.{2048}PCD_IPI\x00', 831 'group' => 'graphic', 832 'module' => 'pcd', 833 'mime_type' => 'image/x-photo-cd', 834 'fail_id3' => 'ERROR', 835 'fail_ape' => 'ERROR', 836 ), 837 838 839 // PNG - still image - Portable Network Graphics (PNG) 840 'png' => array( 841 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', 842 'group' => 'graphic', 843 'module' => 'png', 844 'mime_type' => 'image/png', 845 'fail_id3' => 'ERROR', 846 'fail_ape' => 'ERROR', 847 ), 892 848 893 849 … … 895 851 'svg' => array( 896 852 'pattern' => '<!DOCTYPE svg PUBLIC ', 853 'group' => 'graphic', 854 'module' => 'svg', 897 855 'mime_type' => 'image/svg+xml', 898 856 'fail_id3' => 'ERROR', … … 901 859 902 860 903 // TIFF - still image - Tagged Information File Format (TIFF) 904 'tiff' => array ( 905 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', 906 'group' => 'graphic', 907 'module' => 'tiff', 908 'mime_type' => 'image/tiff', 909 'fail_id3' => 'ERROR', 910 'fail_ape' => 'ERROR', 911 ), 912 913 914 // Data formats 915 916 'exe' => array( 917 'pattern' => '^MZ', 918 'mime_type' => 'application/octet-stream', 919 'fail_id3' => 'ERROR', 920 'fail_ape' => 'ERROR', 921 ), 922 923 // ISO - data - International Standards Organization (ISO) CD-ROM Image 924 'iso' => array ( 925 'pattern' => '^.{32769}CD001', 926 'group' => 'misc', 927 'module' => 'iso', 928 'mime_type' => 'application/octet-stream', 929 'fail_id3' => 'ERROR', 930 'fail_ape' => 'ERROR', 931 ), 932 933 // RAR - data - RAR compressed data 934 'rar' => array( 935 'pattern' => '^Rar\!', 936 'mime_type' => 'application/octet-stream', 937 'fail_id3' => 'ERROR', 938 'fail_ape' => 'ERROR', 939 ), 940 941 // SZIP - audio - SZIP compressed data 942 'szip' => array ( 943 'pattern' => '^SZ\x0A\x04', 944 'group' => 'archive', 945 'module' => 'szip', 946 'mime_type' => 'application/octet-stream', 947 'fail_id3' => 'ERROR', 948 'fail_ape' => 'ERROR', 949 ), 950 951 // TAR - data - TAR compressed data 952 'tar' => array( 953 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}', 954 'group' => 'archive', 955 'module' => 'tar', 956 'mime_type' => 'application/x-tar', 957 'fail_id3' => 'ERROR', 958 'fail_ape' => 'ERROR', 959 ), 960 961 // GZIP - data - GZIP compressed data 962 'gz' => array( 963 'pattern' => '^\x1F\x8B\x08', 964 'group' => 'archive', 965 'module' => 'gzip', 966 'mime_type' => 'application/x-gzip', 967 'fail_id3' => 'ERROR', 968 'fail_ape' => 'ERROR', 969 ), 970 971 972 // ZIP - data - ZIP compressed data 973 'zip' => array ( 974 'pattern' => '^PK\x03\x04', 975 'group' => 'archive', 976 'module' => 'zip', 977 'mime_type' => 'application/zip', 978 'fail_id3' => 'ERROR', 979 'fail_ape' => 'ERROR', 980 ), 981 861 // TIFF - still image - Tagged Information File Format (TIFF) 862 'tiff' => array( 863 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', 864 'group' => 'graphic', 865 'module' => 'tiff', 866 'mime_type' => 'image/tiff', 867 'fail_id3' => 'ERROR', 868 'fail_ape' => 'ERROR', 869 ), 870 871 872 // Data formats 873 874 // ISO - data - International Standards Organization (ISO) CD-ROM Image 875 'iso' => array( 876 'pattern' => '^.{32769}CD001', 877 'group' => 'misc', 878 'module' => 'iso', 879 'mime_type' => 'application/octet-stream', 880 'fail_id3' => 'ERROR', 881 'fail_ape' => 'ERROR', 882 'iconv_req' => false, 883 ), 884 885 // RAR - data - RAR compressed data 886 'rar' => array( 887 'pattern' => '^Rar\!', 888 'group' => 'archive', 889 'module' => 'rar', 890 'mime_type' => 'application/octet-stream', 891 'fail_id3' => 'ERROR', 892 'fail_ape' => 'ERROR', 893 ), 894 895 // SZIP - audio/data - SZIP compressed data 896 'szip' => array( 897 'pattern' => '^SZ\x0A\x04', 898 'group' => 'archive', 899 'module' => 'szip', 900 'mime_type' => 'application/octet-stream', 901 'fail_id3' => 'ERROR', 902 'fail_ape' => 'ERROR', 903 ), 904 905 // TAR - data - TAR compressed data 906 'tar' => array( 907 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}', 908 'group' => 'archive', 909 'module' => 'tar', 910 'mime_type' => 'application/x-tar', 911 'fail_id3' => 'ERROR', 912 'fail_ape' => 'ERROR', 913 ), 914 915 // GZIP - data - GZIP compressed data 916 'gz' => array( 917 'pattern' => '^\x1F\x8B\x08', 918 'group' => 'archive', 919 'module' => 'gzip', 920 'mime_type' => 'application/x-gzip', 921 'fail_id3' => 'ERROR', 922 'fail_ape' => 'ERROR', 923 ), 924 925 // ZIP - data - ZIP compressed data 926 'zip' => array( 927 'pattern' => '^PK\x03\x04', 928 'group' => 'archive', 929 'module' => 'zip', 930 'mime_type' => 'application/zip', 931 'fail_id3' => 'ERROR', 932 'fail_ape' => 'ERROR', 933 ), 934 935 936 // Misc other formats 982 937 983 938 // PAR2 - data - Parity Volume Set Specification 2.0 984 939 'par2' => array ( 985 940 'pattern' => '^PAR2\x00PKT', 986 'mime_type' => 'application/octet-stream', 987 'fail_id3' => 'ERROR', 988 'fail_ape' => 'ERROR', 989 ), 990 991 992 // PDF - data - Portable Document Format 993 'pdf' => array( 994 'pattern' => '^\x25PDF', 995 'mime_type' => 'application/pdf', 996 'fail_id3' => 'ERROR', 997 'fail_ape' => 'ERROR', 998 ), 999 1000 // DOC - data - Microsoft Word 1001 'msoffice' => array( 1002 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document 1003 'mime_type' => 'application/octet-stream', 1004 'fail_id3' => 'ERROR', 1005 'fail_ape' => 'ERROR', 1006 ), 1007 ); 1008 1009 return $format_info; 1010 } 1011 1012 1013 1014 // Recursive over array - converts array to $encoding charset from $this->encoding 1015 function CharConvert(&$array, $encoding) { 1016 1017 // Identical encoding - end here 1018 if ($encoding == $this->encoding) { 1019 return; 1020 } 1021 1022 // Loop thru array 1023 foreach ($array as $key => $value) { 1024 1025 // Go recursive 1026 if (is_array($value)) { 1027 $this->CharConvert($array[$key], $encoding); 1028 } 1029 1030 // Convert string 1031 elseif (is_string($value)) { 1032 $array[$key] = $this->iconv($encoding, $this->encoding, $value); 1033 } 1034 } 1035 } 1036 1037 1038 1039 // Convert and copy tags 1040 protected function HandleAllTags() { 1041 1042 // Key name => array (tag name, character encoding) 1043 static $tags = array ( 1044 'asf' => array ('asf', 'UTF-16LE'), 1045 'midi' => array ('midi', 'ISO-8859-1'), 1046 'nsv' => array ('nsv', 'ISO-8859-1'), 1047 'ogg' => array ('vorbiscomment', 'UTF-8'), 1048 'png' => array ('png', 'UTF-8'), 1049 'tiff' => array ('tiff', 'ISO-8859-1'), 1050 'quicktime' => array ('quicktime', 'ISO-8859-1'), 1051 'real' => array ('real', 'ISO-8859-1'), 1052 'vqf' => array ('vqf', 'ISO-8859-1'), 1053 'zip' => array ('zip', 'ISO-8859-1'), 1054 'riff' => array ('riff', 'ISO-8859-1'), 1055 'lyrics3' => array ('lyrics3', 'ISO-8859-1'), 1056 'id3v1' => array ('id3v1', ''), // change below - cannot assign variable to static array 1057 'id3v2' => array ('id3v2', 'UTF-8'), // module converts all frames to UTF-8 1058 'ape' => array ('ape', 'UTF-8') 1059 ); 1060 $tags['id3v1'][1] = $this->encoding_id3v1; 1061 1062 // Loop thru tags array 1063 foreach ($tags as $comment_name => $tag_name_encoding_array) { 1064 list($tag_name, $encoding) = $tag_name_encoding_array; 1065 1066 // Fill in default encoding type if not already present 1067 @$this->info[$comment_name] and $this->info[$comment_name]['encoding'] = $encoding; 1068 1069 // Copy comments if key name set 1070 if (@$this->info[$comment_name]['comments']) { 1071 1072 foreach ($this->info[$comment_name]['comments'] as $tag_key => $value_array) { 1073 foreach ($value_array as $key => $value) { 1074 if (strlen(trim($value)) > 0) { 1075 $this->info['tags'][$tag_name][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! 1076 } 1077 } 1078 1079 } 1080 1081 if (!@$this->info['tags'][$tag_name]) { 1082 // comments are set but contain nothing but empty strings, so skip 1083 continue; 1084 } 1085 1086 $this->CharConvert($this->info['tags'][$tag_name], $encoding); 1087 } 1088 } 1089 1090 1091 // Merge comments from ['tags'] into common ['comments'] 1092 if (@$this->info['tags']) { 1093 1094 foreach ($this->info['tags'] as $tag_type => $tag_array) { 1095 1096 foreach ($tag_array as $tag_name => $tagdata) { 1097 1098 foreach ($tagdata as $key => $value) { 1099 1100 if (!empty($value)) { 1101 1102 if (empty($this->info['comments'][$tag_name])) { 1103 1104 // fall through and append value 1105 } 1106 elseif ($tag_type == 'id3v1') { 1107 1108 $new_value_length = strlen(trim($value)); 1109 foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { 1110 $old_value_length = strlen(trim($existing_value)); 1111 if (($new_value_length <= $old_value_length) && (substr($existing_value, 0, $new_value_length) == trim($value))) { 1112 // new value is identical but shorter-than (or equal-length to) one already in comments - skip 1113 break 2; 1114 } 1115 } 1116 } 1117 else { 1118 1119 $new_value_length = strlen(trim($value)); 1120 foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { 1121 $old_value_length = strlen(trim($existing_value)); 1122 if (($new_value_length > $old_value_length) && (substr(trim($value), 0, strlen($existing_value)) == $existing_value)) { 1123 $this->info['comments'][$tag_name][$existing_key] = trim($value); 1124 break 2; 1125 } 1126 } 1127 } 1128 1129 if (empty($this->info['comments'][$tag_name]) || !in_array(trim($value), $this->info['comments'][$tag_name])) { 1130 $this->info['comments'][$tag_name][] = trim($value); 1131 } 1132 } 1133 } 1134 } 1135 } 1136 } 1137 1138 return true; 1139 } 941 'group' => 'misc', 942 'module' => 'par2', 943 'mime_type' => 'application/octet-stream', 944 'fail_id3' => 'ERROR', 945 'fail_ape' => 'ERROR', 946 ), 947 948 // PDF - data - Portable Document Format 949 'pdf' => array( 950 'pattern' => '^\x25PDF', 951 'group' => 'misc', 952 'module' => 'pdf', 953 'mime_type' => 'application/pdf', 954 'fail_id3' => 'ERROR', 955 'fail_ape' => 'ERROR', 956 ), 957 958 // MSOFFICE - data - ZIP compressed data 959 'msoffice' => array( 960 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document 961 'group' => 'misc', 962 'module' => 'msoffice', 963 'mime_type' => 'application/octet-stream', 964 'fail_id3' => 'ERROR', 965 'fail_ape' => 'ERROR', 966 ), 967 ); 968 } 969 970 return $format_info; 971 } 972 973 974 975 function GetFileFormat(&$filedata, $filename='') { 976 // this function will determine the format of a file based on usually 977 // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, 978 // and in the case of ISO CD image, 6 bytes offset 32kb from the start 979 // of the file). 980 981 // Identify file format - loop through $format_info and detect with reg expr 982 foreach ($this->GetFileFormatArray() as $format_name => $info) { 983 // Using preg_match() instead of ereg() - much faster 984 // The /s switch on preg_match() forces preg_match() NOT to treat 985 // newline (0x0A) characters as special chars but do a binary match 986 if (preg_match('/'.$info['pattern'].'/s', $filedata)) { 987 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; 988 return $info; 989 } 990 } 991 992 993 if (preg_match('/\.mp[123a]$/i', $filename)) { 994 // Too many mp3 encoders on the market put gabage in front of mpeg files 995 // use assume format on these if format detection failed 996 $GetFileFormatArray = $this->GetFileFormatArray(); 997 $info = $GetFileFormatArray['mp3']; 998 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; 999 return $info; 1000 } 1001 1002 return false; 1003 } 1004 1005 1006 // converts array to $encoding charset from $this->encoding 1007 function CharConvert(&$array, $encoding) { 1008 1009 // identical encoding - end here 1010 if ($encoding == $this->encoding) { 1011 return; 1012 } 1013 1014 // loop thru array 1015 foreach ($array as $key => $value) { 1016 1017 // go recursive 1018 if (is_array($value)) { 1019 $this->CharConvert($array[$key], $encoding); 1020 } 1021 1022 // convert string 1023 elseif (is_string($value)) { 1024 $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value)); 1025 } 1026 } 1027 } 1028 1029 1030 function HandleAllTags() { 1031 1032 // key name => array (tag name, character encoding) 1033 static $tags; 1034 if (empty($tags)) { 1035 $tags = array( 1036 'asf' => array('asf' , 'UTF-16LE'), 1037 'midi' => array('midi' , 'ISO-8859-1'), 1038 'nsv' => array('nsv' , 'ISO-8859-1'), 1039 'ogg' => array('vorbiscomment' , 'UTF-8'), 1040 'png' => array('png' , 'UTF-8'), 1041 'tiff' => array('tiff' , 'ISO-8859-1'), 1042 'quicktime' => array('quicktime' , 'ISO-8859-1'), 1043 'real' => array('real' , 'ISO-8859-1'), 1044 'vqf' => array('vqf' , 'ISO-8859-1'), 1045 'zip' => array('zip' , 'ISO-8859-1'), 1046 'riff' => array('riff' , 'ISO-8859-1'), 1047 'lyrics3' => array('lyrics3' , 'ISO-8859-1'), 1048 'id3v1' => array('id3v1' , $this->encoding_id3v1), 1049 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8 1050 'ape' => array('ape' , 'UTF-8') 1051 ); 1052 } 1053 1054 // loop thru comments array 1055 foreach ($tags as $comment_name => $tagname_encoding_array) { 1056 list($tag_name, $encoding) = $tagname_encoding_array; 1057 1058 // fill in default encoding type if not already present 1059 if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) { 1060 $this->info[$comment_name]['encoding'] = $encoding; 1061 } 1062 1063 // copy comments if key name set 1064 if (!empty($this->info[$comment_name]['comments'])) { 1065 1066 foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { 1067 foreach ($valuearray as $key => $value) { 1068 if (strlen(trim($value)) > 0) { 1069 $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! 1070 } 1071 } 1072 } 1073 1074 if (!isset($this->info['tags'][$tag_name])) { 1075 // comments are set but contain nothing but empty strings, so skip 1076 continue; 1077 } 1078 1079 if ($this->option_tags_html) { 1080 foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { 1081 foreach ($valuearray as $key => $value) { 1082 if (is_string($value)) { 1083 //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding); 1084 $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $encoding)); 1085 } else { 1086 $this->info['tags_html'][$tag_name][$tag_key][$key] = $value; 1087 } 1088 } 1089 } 1090 } 1091 1092 $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! 1093 } 1094 1095 } 1096 return true; 1097 } 1098 1099 1100 function getHashdata($algorithm) { 1101 switch ($algorithm) { 1102 case 'md5': 1103 case 'sha1': 1104 break; 1105 1106 default: 1107 return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()'); 1108 break; 1109 } 1110 1111 if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) { 1112 1113 // We cannot get an identical md5_data value for Ogg files where the comments 1114 // span more than 1 Ogg page (compared to the same audio data with smaller 1115 // comments) using the normal getID3() method of MD5'ing the data between the 1116 // end of the comments and the end of the file (minus any trailing tags), 1117 // because the page sequence numbers of the pages that the audio data is on 1118 // do not match. Under normal circumstances, where comments are smaller than 1119 // the nominal 4-8kB page size, then this is not a problem, but if there are 1120 // very large comments, the only way around it is to strip off the comment 1121 // tags with vorbiscomment and MD5 that file. 1122 // This procedure must be applied to ALL Ogg files, not just the ones with 1123 // comments larger than 1 page, because the below method simply MD5's the 1124 // whole file with the comments stripped, not just the portion after the 1125 // comments block (which is the standard getID3() method. 1126 1127 // The above-mentioned problem of comments spanning multiple pages and changing 1128 // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but 1129 // currently vorbiscomment only works on OggVorbis files. 1130 1131 if ((bool) ini_get('safe_mode')) { 1132 1133 $this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)'; 1134 $this->info[$algorithm.'_data'] = false; 1135 1136 } else { 1137 1138 // Prevent user from aborting script 1139 $old_abort = ignore_user_abort(true); 1140 1141 // Create empty file 1142 $empty = tempnam('*', 'getID3'); 1143 touch($empty); 1144 1145 1146 // Use vorbiscomment to make temp file without comments 1147 $temp = tempnam('*', 'getID3'); 1148 $file = $this->info['filenamepath']; 1149 1150 if (GETID3_OS_ISWINDOWS) { 1151 1152 if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { 1153 1154 $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"'; 1155 $VorbisCommentError = `$commandline`; 1156 1157 } else { 1158 1159 $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; 1160 1161 } 1162 1163 } else { 1164 1165 $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; 1166 $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; 1167 $VorbisCommentError = `$commandline`; 1168 1169 } 1170 1171 if (!empty($VorbisCommentError)) { 1172 1173 $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError; 1174 $this->info[$algorithm.'_data'] = false; 1175 1176 } else { 1177 1178 // Get hash of newly created file 1179 switch ($algorithm) { 1180 case 'md5': 1181 $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp); 1182 break; 1183 1184 case 'sha1': 1185 $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp); 1186 break; 1187 } 1188 } 1189 1190 // Clean up 1191 unlink($empty); 1192 unlink($temp); 1193 1194 // Reset abort setting 1195 ignore_user_abort($old_abort); 1196 1197 } 1198 1199 } else { 1200 1201 if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) { 1202 1203 // get hash from part of file 1204 $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm); 1205 1206 } else { 1207 1208 // get hash from whole file 1209 switch ($algorithm) { 1210 case 'md5': 1211 $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']); 1212 break; 1213 1214 case 'sha1': 1215 $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']); 1216 break; 1217 } 1218 } 1219 1220 } 1221 return true; 1222 } 1223 1224 1225 function ChannelsBitratePlaytimeCalculations() { 1226 1227 // set channelmode on audio 1228 if (@$this->info['audio']['channels'] == '1') { 1229 $this->info['audio']['channelmode'] = 'mono'; 1230 } elseif (@$this->info['audio']['channels'] == '2') { 1231 $this->info['audio']['channelmode'] = 'stereo'; 1232 } 1233 1234 // Calculate combined bitrate - audio + video 1235 $CombinedBitrate = 0; 1236 $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); 1237 $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); 1238 if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { 1239 $this->info['bitrate'] = $CombinedBitrate; 1240 } 1241 //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) { 1242 // // for example, VBR MPEG video files cannot determine video bitrate: 1243 // // should not set overall bitrate and playtime from audio bitrate only 1244 // unset($this->info['bitrate']); 1245 //} 1246 1247 // video bitrate undetermined, but calculable 1248 if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) { 1249 // if video bitrate not set 1250 if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) { 1251 // AND if audio bitrate is set to same as overall bitrate 1252 if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) { 1253 // AND if playtime is set 1254 if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) { 1255 // AND if AV data offset start/end is known 1256 // THEN we can calculate the video bitrate 1257 $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']); 1258 $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate']; 1259 } 1260 } 1261 } 1262 } 1263 1264 if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) { 1265 $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; 1266 } 1267 1268 if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) { 1269 $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']; 1270 } 1271 //echo '<pre>'; 1272 //var_dump($this->info['bitrate']); 1273 //var_dump($this->info['audio']['bitrate']); 1274 //var_dump($this->info['video']['bitrate']); 1275 //echo '</pre>'; 1276 if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) { 1277 if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) { 1278 // audio only 1279 $this->info['audio']['bitrate'] = $this->info['bitrate']; 1280 } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) { 1281 // video only 1282 $this->info['video']['bitrate'] = $this->info['bitrate']; 1283 } 1284 } 1285 1286 // Set playtime string 1287 if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { 1288 $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); 1289 } 1290 } 1291 1292 1293 function CalculateCompressionRatioVideo() { 1294 if (empty($this->info['video'])) { 1295 return false; 1296 } 1297 if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { 1298 return false; 1299 } 1300 if (empty($this->info['video']['bits_per_sample'])) { 1301 return false; 1302 } 1303 1304 switch ($this->info['video']['dataformat']) { 1305 case 'bmp': 1306 case 'gif': 1307 case 'jpeg': 1308 case 'jpg': 1309 case 'png': 1310 case 'tiff': 1311 $FrameRate = 1; 1312 $PlaytimeSeconds = 1; 1313 $BitrateCompressed = $this->info['filesize'] * 8; 1314 break; 1315 1316 default: 1317 if (!empty($this->info['video']['frame_rate'])) { 1318 $FrameRate = $this->info['video']['frame_rate']; 1319 } else { 1320 return false; 1321 } 1322 if (!empty($this->info['playtime_seconds'])) { 1323 $PlaytimeSeconds = $this->info['playtime_seconds']; 1324 } else { 1325 return false; 1326 } 1327 if (!empty($this->info['video']['bitrate'])) { 1328 $BitrateCompressed = $this->info['video']['bitrate']; 1329 } else { 1330 return false; 1331 } 1332 break; 1333 } 1334 $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; 1335 1336 $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; 1337 return true; 1338 } 1339 1340 1341 function CalculateCompressionRatioAudio() { 1342 if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { 1343 return false; 1344 } 1345 $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); 1346 1347 if (!empty($this->info['audio']['streams'])) { 1348 foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { 1349 if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { 1350 $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); 1351 } 1352 } 1353 } 1354 return true; 1355 } 1356 1357 1358 function CalculateReplayGain() { 1359 if (isset($this->info['replay_gain'])) { 1360 $this->info['replay_gain']['reference_volume'] = 89; 1361 if (isset($this->info['replay_gain']['track']['adjustment'])) { 1362 $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; 1363 } 1364 if (isset($this->info['replay_gain']['album']['adjustment'])) { 1365 $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; 1366 } 1367 1368 if (isset($this->info['replay_gain']['track']['peak'])) { 1369 $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); 1370 } 1371 if (isset($this->info['replay_gain']['album']['peak'])) { 1372 $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); 1373 } 1374 } 1375 return true; 1376 } 1377 1378 function ProcessAudioStreams() { 1379 if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { 1380 if (!isset($this->info['audio']['streams'])) { 1381 foreach ($this->info['audio'] as $key => $value) { 1382 if ($key != 'streams') { 1383 $this->info['audio']['streams'][0][$key] = $value; 1384 } 1385 } 1386 } 1387 } 1388 return true; 1389 } 1390 1391 function getid3_tempnam() { 1392 return tempnam($this->tempdir, 'gI3'); 1393 } 1394 1140 1395 } 1141 1396 1142 1143 abstract class getid3_handler1144 {1145 1146 protected $getid3; // pointer1147 1148 protected $data_string_flag = false; // analyzing filepointer or string1149 protected $data_string; // string to analyze1150 protected $data_string_position = 0; // seek position in string1151 1152 1153 public function __construct(getID3 $getid3) {1154 1155 $this->getid3 = $getid3;1156 }1157 1158 1159 // Analyze from file pointer1160 abstract public function Analyze();1161 1162 1163 1164 // Analyze from string instead1165 public function AnalyzeString(&$string) {1166 1167 // Enter string mode1168 $this->data_string_flag = true;1169 $this->data_string = $string;1170 1171 // Save info1172 $saved_avdataoffset = $this->getid3->info['avdataoffset'];1173 $saved_avdataend = $this->getid3->info['avdataend'];1174 $saved_filesize = $this->getid3->info['filesize'];1175 1176 // Reset some info1177 $this->getid3->info['avdataoffset'] = 0;1178 $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string);1179 1180 // Analyze1181 $this->Analyze();1182 1183 // Restore some info1184 $this->getid3->info['avdataoffset'] = $saved_avdataoffset;1185 $this->getid3->info['avdataend'] = $saved_avdataend;1186 $this->getid3->info['filesize'] = $saved_filesize;1187 1188 // Exit string mode1189 $this->data_string_flag = false;1190 }1191 1192 1193 protected function ftell() {1194 1195 if ($this->data_string_flag) {1196 return $this->data_string_position;1197 }1198 return ftell($this->getid3->fp);1199 }1200 1201 1202 protected function fread($bytes) {1203 1204 if ($this->data_string_flag) {1205 $this->data_string_position += $bytes;1206 return substr($this->data_string, $this->data_string_position - $bytes, $bytes);1207 }1208 return fread($this->getid3->fp, $bytes);1209 }1210 1211 1212 protected function fseek($bytes, $whence = SEEK_SET) {1213 1214 if ($this->data_string_flag) {1215 switch ($whence) {1216 case SEEK_SET:1217 $this->data_string_position = $bytes;1218 return;1219 1220 case SEEK_CUR:1221 $this->data_string_position += $bytes;1222 return;1223 1224 case SEEK_END:1225 $this->data_string_position = strlen($this->data_string) + $bytes;1226 return;1227 }1228 }1229 return fseek($this->getid3->fp, $bytes, $whence);1230 }1231 1232 }1233 1234 1235 1236 1237 abstract class getid3_handler_write1238 {1239 protected $filename;1240 protected $user_abort;1241 1242 private $fp_lock;1243 private $owner;1244 private $group;1245 private $perms;1246 1247 1248 public function __construct($filename) {1249 1250 if (!file_exists($filename)) {1251 throw new getid3_exception('File does not exist: "' . $filename . '"');1252 }1253 1254 if (!is_writeable($filename)) {1255 throw new getid3_exception('File is not writeable: "' . $filename . '"');1256 }1257 1258 if (!is_writeable(dirname($filename))) {1259 throw new getid3_exception('Directory is not writeable: ' . dirname($filename) . ' (need to write lock file).');1260 }1261 1262 $this->user_abort = ignore_user_abort(true);1263 1264 $this->fp_lock = fopen($filename . '.getid3.lock', 'w');1265 flock($this->fp_lock, LOCK_EX);1266 1267 $this->filename = $filename;1268 }1269 1270 1271 public function __destruct() {1272 1273 flock($this->fp_lock, LOCK_UN);1274 fclose($this->fp_lock);1275 unlink($this->filename . '.getid3.lock');1276 1277 ignore_user_abort($this->user_abort);1278 }1279 1280 1281 protected function save_permissions() {1282 1283 $this->owner = fileowner($this->filename);1284 $this->group = filegroup($this->filename);1285 $this->perms = fileperms($this->filename);1286 }1287 1288 1289 protected function restore_permissions() {1290 1291 @chown($this->filename, $this->owner);1292 @chgrp($this->filename, $this->group);1293 @chmod($this->filename, $this->perms);1294 }1295 1296 1297 abstract public function read();1298 1299 abstract public function write();1300 1301 abstract public function remove();1302 1303 }1304 1305 1306 1307 1308 class getid3_exception extends Exception1309 {1310 public $message;1311 1312 }1313 1314 1315 1316 1317 class getid3_lib1318 {1319 1320 // Convert Little Endian byte string to int - max 32 bits1321 public static function LittleEndian2Int($byte_word, $signed = false) {1322 1323 return getid3_lib::BigEndian2Int(strrev($byte_word), $signed);1324 }1325 1326 1327 1328 // Convert number to Little Endian byte string1329 public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {1330 $intstring = '';1331 while ($number > 0) {1332 if ($synchsafe) {1333 $intstring = $intstring.chr($number & 127);1334 $number >>= 7;1335 } else {1336 $intstring = $intstring.chr($number & 255);1337 $number >>= 8;1338 }1339 }1340 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);1341 }1342 1343 1344 1345 // Convert Big Endian byte string to int - max 32 bits1346 public static function BigEndian2Int($byte_word, $signed = false) {1347 1348 $int_value = 0;1349 $byte_wordlen = strlen($byte_word);1350 1351 for ($i = 0; $i < $byte_wordlen; $i++) {1352 $int_value += ord($byte_word{$i}) * pow(256, ($byte_wordlen - 1 - $i));1353 }1354 1355 if ($signed) {1356 $sign_mask_bit = 0x80 << (8 * ($byte_wordlen - 1));1357 if ($int_value & $sign_mask_bit) {1358 $int_value = 0 - ($int_value & ($sign_mask_bit - 1));1359 }1360 }1361 1362 return $int_value;1363 }1364 1365 1366 1367 // Convert Big Endian byte sybc safe string to int - max 32 bits1368 public static function BigEndianSyncSafe2Int($byte_word) {1369 1370 $int_value = 0;1371 $byte_wordlen = strlen($byte_word);1372 1373 // disregard MSB, effectively 7-bit bytes1374 for ($i = 0; $i < $byte_wordlen; $i++) {1375 $int_value = $int_value | (ord($byte_word{$i}) & 0x7F) << (($byte_wordlen - 1 - $i) * 7);1376 }1377 return $int_value;1378 }1379 1380 1381 1382 // Convert Big Endian byte string to bit string1383 public static function BigEndian2Bin($byte_word) {1384 1385 $bin_value = '';1386 $byte_wordlen = strlen($byte_word);1387 for ($i = 0; $i < $byte_wordlen; $i++) {1388 $bin_value .= str_pad(decbin(ord($byte_word{$i})), 8, '0', STR_PAD_LEFT);1389 }1390 return $bin_value;1391 }1392 1393 1394 1395 public static function BigEndian2Float($byte_word) {1396 1397 // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic1398 // http://www.psc.edu/general/software/packages/ieee/ieee.html1399 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html1400 1401 $bit_word = getid3_lib::BigEndian2Bin($byte_word);1402 if (!$bit_word) {1403 return 0;1404 }1405 $sign_bit = $bit_word{0};1406 1407 switch (strlen($byte_word) * 8) {1408 case 32:1409 $exponent_bits = 8;1410 $fraction_bits = 23;1411 break;1412 1413 case 64:1414 $exponent_bits = 11;1415 $fraction_bits = 52;1416 break;1417 1418 case 80:1419 // 80-bit Apple SANE format1420 // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/1421 $exponent_string = substr($bit_word, 1, 15);1422 $is_normalized = intval($bit_word{16});1423 $fraction_string = substr($bit_word, 17, 63);1424 $exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383);1425 $fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string);1426 $float_value = $exponent * $fraction;1427 if ($sign_bit == '1') {1428 $float_value *= -1;1429 }1430 return $float_value;1431 break;1432 1433 default:1434 return false;1435 break;1436 }1437 $exponent_string = substr($bit_word, 1, $exponent_bits);1438 $fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits);1439 $exponent = bindec($exponent_string);1440 $fraction = bindec($fraction_string);1441 1442 if (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction != 0)) {1443 // Not a Number1444 $float_value = false;1445 } elseif (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction == 0)) {1446 if ($sign_bit == '1') {1447 $float_value = '-infinity';1448 } else {1449 $float_value = '+infinity';1450 }1451 } elseif (($exponent == 0) && ($fraction == 0)) {1452 if ($sign_bit == '1') {1453 $float_value = -0;1454 } else {1455 $float_value = 0;1456 }1457 $float_value = ($sign_bit ? 0 : -0);1458 } elseif (($exponent == 0) && ($fraction != 0)) {1459 // These are 'unnormalized' values1460 $float_value = pow(2, (-1 * (pow(2, $exponent_bits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fraction_string);1461 if ($sign_bit == '1') {1462 $float_value *= -1;1463 }1464 } elseif ($exponent != 0) {1465 $float_value = pow(2, ($exponent - (pow(2, $exponent_bits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fraction_string));1466 if ($sign_bit == '1') {1467 $float_value *= -1;1468 }1469 }1470 return (float) $float_value;1471 }1472 1473 1474 1475 public static function LittleEndian2Float($byte_word) {1476 1477 return getid3_lib::BigEndian2Float(strrev($byte_word));1478 }1479 1480 1481 1482 public static function DecimalBinary2Float($binary_numerator) {1483 $numerator = bindec($binary_numerator);1484 $denominator = bindec('1'.str_repeat('0', strlen($binary_numerator)));1485 return ($numerator / $denominator);1486 }1487 1488 1489 public static function PrintHexBytes($string, $hex=true, $spaces=true, $html_safe=true) {1490 1491 $return_string = '';1492 for ($i = 0; $i < strlen($string); $i++) {1493 if ($hex) {1494 $return_string .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);1495 } else {1496 $return_string .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '¤');1497 }1498 if ($spaces) {1499 $return_string .= ' ';1500 }1501 }1502 if ($html_safe) {1503 $return_string = htmlentities($return_string);1504 }1505 return $return_string;1506 }1507 1508 1509 1510 // Process header data string - read several values with algorithm and add to target1511 // algorithm is one one the getid3_lib::Something2Something() function names1512 // parts_array is index => length - $target[index] = algorithm(substring(data))1513 // - OR just substring(data) if length is negative!1514 // indexes == 'IGNORE**' are ignored1515 1516 public static function ReadSequence($algorithm, &$target, &$data, $offset, $parts_array) {1517 1518 // Loop thru $parts_array1519 foreach ($parts_array as $target_string => $length) {1520 1521 // Add to target1522 if (!strstr($target_string, 'IGNORE')) {1523 1524 // substr(....length)1525 if ($length < 0) {1526 $target[$target_string] = substr($data, $offset, -$length);1527 }1528 1529 // algorithm(substr(...length))1530 else {1531 $target[$target_string] = getid3_lib::$algorithm(substr($data, $offset, $length));1532 }1533 }1534 1535 // Move pointer1536 $offset += abs($length);1537 }1538 }1539 1540 }1541 1542 1543 1544 class getid3_lib_replaygain1545 {1546 1547 public static function NameLookup($name_code) {1548 1549 static $lookup = array (1550 0 => 'not set',1551 1 => 'Track Gain Adjustment',1552 2 => 'Album Gain Adjustment'1553 );1554 1555 return @$lookup[$name_code];1556 }1557 1558 1559 1560 public static function OriginatorLookup($originator_code) {1561 1562 static $lookup = array (1563 0 => 'unspecified',1564 1 => 'pre-set by artist/producer/mastering engineer',1565 2 => 'set by user',1566 3 => 'determined automatically'1567 );1568 1569 return @$lookup[$originator_code];1570 }1571 1572 1573 1574 public static function AdjustmentLookup($raw_adjustment, $sign_bit) {1575 1576 return (float)$raw_adjustment / 10 * ($sign_bit == 1 ? -1 : 1);1577 }1578 1579 1580 1581 public static function GainString($name_code, $originator_code, $replaygain) {1582 1583 $sign_bit = $replaygain < 0 ? 1 : 0;1584 1585 $stored_replaygain = intval(round($replaygain * 10));1586 $gain_string = str_pad(decbin($name_code), 3, '0', STR_PAD_LEFT);1587 $gain_string .= str_pad(decbin($originator_code), 3, '0', STR_PAD_LEFT);1588 $gain_string .= $sign_bit;1589 $gain_string .= str_pad(decbin($stored_replaygain), 9, '0', STR_PAD_LEFT);1590 1591 return $gain_string;1592 }1593 1594 }1595 1596 1597 1598 1599 1397 ?>
Note: See TracChangeset
for help on using the changeset viewer.