1: <?php
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14: if (!defined('GETID3_OS_ISWINDOWS')) {
15: define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
16: }
17:
18: if (!defined('GETID3_INCLUDEPATH')) {
19: define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
20: }
21:
22: if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
23: define('IMG_JPG', IMAGETYPE_JPEG);
24: }
25:
26:
27: $temp_dir = ini_get('upload_tmp_dir');
28: if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
29: $temp_dir = '';
30: }
31: if (!$temp_dir && function_exists('sys_get_temp_dir')) {
32:
33: $temp_dir = sys_get_temp_dir();
34: }
35: $temp_dir = @realpath($temp_dir);
36: $open_basedir = ini_get('open_basedir');
37: if ($open_basedir) {
38:
39: $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
40: $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
41: if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
42: $temp_dir .= DIRECTORY_SEPARATOR;
43: }
44: $found_valid_tempdir = false;
45: $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
46: foreach ($open_basedirs as $basedir) {
47: if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
48: $basedir .= DIRECTORY_SEPARATOR;
49: }
50: if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
51: $found_valid_tempdir = true;
52: break;
53: }
54: }
55: if (!$found_valid_tempdir) {
56: $temp_dir = '';
57: }
58: unset($open_basedirs, $found_valid_tempdir, $basedir);
59: }
60: if (!$temp_dir) {
61: $temp_dir = '*';
62: }
63:
64: if (!defined('GETID3_TEMP_DIR')) {
65: define('GETID3_TEMP_DIR', $temp_dir);
66: }
67: unset($open_basedir, $temp_dir);
68:
69:
70:
71:
72: class getID3
73: {
74:
75: public $encoding = 'UTF-8';
76: public $encoding_id3v1 = 'ISO-8859-1';
77:
78:
79: public $option_tag_id3v1 = true;
80: public $option_tag_id3v2 = true;
81: public $option_tag_lyrics3 = true;
82: public $option_tag_apetag = true;
83: public $option_tags_process = true;
84: public $option_tags_html = true;
85:
86:
87: public $option_extra_info = true;
88:
89:
90: public $option_save_attachments = true;
91:
92:
93: public $option_md5_data = false;
94: public $option_md5_data_source = false;
95: public $option_sha1_data = false;
96: public $option_max_2gb_check = null;
97:
98:
99: public $option_fread_buffer_size = 32768;
100:
101:
102: public $filename;
103: public $fp;
104: public $info;
105: public $tempdir = GETID3_TEMP_DIR;
106: public $memory_limit = 0;
107:
108:
109: protected $startup_error = '';
110: protected $startup_warning = '';
111:
112: const VERSION = '1.9.10-201511170924';
113: const FREAD_BUFFER_SIZE = 32768;
114:
115: const ATTACHMENTS_NONE = false;
116: const ATTACHMENTS_INLINE = true;
117:
118:
119: public function __construct() {
120:
121:
122: $required_php_version = '5.3.0';
123: if (version_compare(PHP_VERSION, $required_php_version, '<')) {
124: $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
125: return false;
126: }
127:
128:
129: $this->memory_limit = ini_get('memory_limit');
130: if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
131:
132: $this->memory_limit = $matches[1] * 1048576;
133: } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) {
134:
135: $this->memory_limit = $matches[1] * 1073741824;
136: }
137: if ($this->memory_limit <= 0) {
138:
139: } elseif ($this->memory_limit <= 4194304) {
140: $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
141: } elseif ($this->memory_limit <= 12582912) {
142: $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';
143: }
144:
145:
146: if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
147: $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
148: }
149:
150: if (intval(ini_get('mbstring.func_overload')) > 0) {
151: $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
152: }
153:
154:
155: if (function_exists('get_magic_quotes_runtime')) {
156: if (get_magic_quotes_runtime()) {
157: return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
158: }
159: }
160:
161:
162: if (function_exists('magic_quotes_gpc')) {
163: if (get_magic_quotes_gpc()) {
164: return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
165: }
166: }
167:
168:
169: if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
170: $this->startup_error .= 'getid3.lib.php is missing or corrupt';
171: }
172:
173: if ($this->option_max_2gb_check === null) {
174: $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
175: }
176:
177:
178:
179:
180:
181:
182:
183:
184: if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
185:
186: $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps';
187:
188: if (!is_dir($helperappsdir)) {
189: $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
190: } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
191: $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
192: $path_so_far = array();
193: foreach ($DirPieces as $key => $value) {
194: if (strpos($value, ' ') !== false) {
195: if (!empty($path_so_far)) {
196: $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
197: $dir_listing = `$commandline`;
198: $lines = explode("\n", $dir_listing);
199: foreach ($lines as $line) {
200: $line = trim($line);
201: if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
202: list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
203: if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
204: $value = $shortname;
205: }
206: }
207: }
208: } else {
209: $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
210: }
211: }
212: $path_so_far[] = $value;
213: }
214: $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
215: }
216: define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
217: }
218:
219: return true;
220: }
221:
222: public function version() {
223: return self::VERSION;
224: }
225:
226: public function fread_buffer_size() {
227: return $this->option_fread_buffer_size;
228: }
229:
230:
231:
232: public function setOption($optArray) {
233: if (!is_array($optArray) || empty($optArray)) {
234: return false;
235: }
236: foreach ($optArray as $opt => $val) {
237: if (isset($this->$opt) === false) {
238: continue;
239: }
240: $this->$opt = $val;
241: }
242: return true;
243: }
244:
245:
246: public function openfile($filename, $filesize=null) {
247: try {
248: if (!empty($this->startup_error)) {
249: throw new getid3_exception($this->startup_error);
250: }
251: if (!empty($this->startup_warning)) {
252: $this->warning($this->startup_warning);
253: }
254:
255:
256: $this->filename = $filename;
257: $this->info = array();
258: $this->info['GETID3_VERSION'] = $this->version();
259: $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
260:
261:
262: if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
263: throw new getid3_exception('Remote files are not supported - please copy the file locally first');
264: }
265:
266: $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
267: $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
268:
269:
270:
271: if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
272:
273: } else {
274: $errormessagelist = array();
275: if (!is_readable($filename)) {
276: $errormessagelist[] = '!is_readable';
277: }
278: if (!is_file($filename)) {
279: $errormessagelist[] = '!is_file';
280: }
281: if (!file_exists($filename)) {
282: $errormessagelist[] = '!file_exists';
283: }
284: if (empty($errormessagelist)) {
285: $errormessagelist[] = 'fopen failed';
286: }
287: throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
288: }
289:
290: $this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename));
291:
292:
293: $filename = str_replace('\\', '/', $filename);
294: $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
295: $this->info['filename'] = getid3_lib::mb_basename($filename);
296: $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
297:
298:
299:
300: if ($this->option_max_2gb_check) {
301:
302:
303:
304: $fseek = fseek($this->fp, 0, SEEK_END);
305: if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
306: ($this->info['filesize'] < 0) ||
307: (ftell($this->fp) < 0)) {
308: $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
309:
310: if ($real_filesize === false) {
311: unset($this->info['filesize']);
312: fclose($this->fp);
313: throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
314: } elseif (getid3_lib::intValueSupported($real_filesize)) {
315: unset($this->info['filesize']);
316: fclose($this->fp);
317: throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
318: }
319: $this->info['filesize'] = $real_filesize;
320: $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
321: }
322: }
323:
324:
325: $this->info['avdataoffset'] = 0;
326: $this->info['avdataend'] = $this->info['filesize'];
327: $this->info['fileformat'] = '';
328: $this->info['audio']['dataformat'] = '';
329: $this->info['video']['dataformat'] = '';
330: $this->info['tags'] = array();
331: $this->info['error'] = array();
332: $this->info['warning'] = array();
333: $this->info['comments'] = array();
334: $this->info['encoding'] = $this->encoding;
335:
336: return true;
337:
338: } catch (Exception $e) {
339: $this->error($e->getMessage());
340: }
341: return false;
342: }
343:
344:
345: public function analyze($filename, $filesize=null, $original_filename='') {
346: try {
347: if (!$this->openfile($filename, $filesize)) {
348: return $this->info;
349: }
350:
351:
352: foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
353: $option_tag = 'option_tag_'.$tag_name;
354: if ($this->$option_tag) {
355: $this->include_module('tag.'.$tag_name);
356: try {
357: $tag_class = 'getid3_'.$tag_name;
358: $tag = new $tag_class($this);
359: $tag->Analyze();
360: }
361: catch (getid3_exception $e) {
362: throw $e;
363: }
364: }
365: }
366: if (isset($this->info['id3v2']['tag_offset_start'])) {
367: $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
368: }
369: foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
370: if (isset($this->info[$tag_key]['tag_offset_start'])) {
371: $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
372: }
373: }
374:
375:
376: if (!$this->option_tag_id3v2) {
377: fseek($this->fp, 0);
378: $header = fread($this->fp, 10);
379: if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
380: $this->info['id3v2']['header'] = true;
381: $this->info['id3v2']['majorversion'] = ord($header{3});
382: $this->info['id3v2']['minorversion'] = ord($header{4});
383: $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10;
384: }
385: }
386:
387:
388: fseek($this->fp, $this->info['avdataoffset']);
389: $formattest = fread($this->fp, 32774);
390:
391:
392: $determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename));
393:
394:
395: if (!$determined_format) {
396: fclose($this->fp);
397: return $this->error('unable to determine file format');
398: }
399:
400:
401: if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
402: if ($determined_format['fail_id3'] === 'ERROR') {
403: fclose($this->fp);
404: return $this->error('ID3 tags not allowed on this file type.');
405: } elseif ($determined_format['fail_id3'] === 'WARNING') {
406: $this->warning('ID3 tags not allowed on this file type.');
407: }
408: }
409:
410:
411: if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
412: if ($determined_format['fail_ape'] === 'ERROR') {
413: fclose($this->fp);
414: return $this->error('APE tags not allowed on this file type.');
415: } elseif ($determined_format['fail_ape'] === 'WARNING') {
416: $this->warning('APE tags not allowed on this file type.');
417: }
418: }
419:
420:
421: $this->info['mime_type'] = $determined_format['mime_type'];
422:
423:
424: if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
425: fclose($this->fp);
426: return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
427: }
428:
429:
430:
431: if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
432: $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
433: if (GETID3_OS_ISWINDOWS) {
434: $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';
435: } else {
436: $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
437: }
438: return $this->error($errormessage);
439: }
440:
441:
442: include_once(GETID3_INCLUDEPATH.$determined_format['include']);
443:
444:
445: $class_name = 'getid3_'.$determined_format['module'];
446: if (!class_exists($class_name)) {
447: return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
448: }
449: $class = new $class_name($this);
450: $class->Analyze();
451: unset($class);
452:
453:
454: fclose($this->fp);
455:
456:
457: if ($this->option_tags_process) {
458: $this->HandleAllTags();
459: }
460:
461:
462: if ($this->option_extra_info) {
463: $this->ChannelsBitratePlaytimeCalculations();
464: $this->CalculateCompressionRatioVideo();
465: $this->CalculateCompressionRatioAudio();
466: $this->CalculateReplayGain();
467: $this->ProcessAudioStreams();
468: }
469:
470:
471: if ($this->option_md5_data) {
472:
473: if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
474: $this->getHashdata('md5');
475: }
476: }
477:
478:
479: if ($this->option_sha1_data) {
480: $this->getHashdata('sha1');
481: }
482:
483:
484: $this->CleanUp();
485:
486: } catch (Exception $e) {
487: $this->error('Caught exception: '.$e->getMessage());
488: }
489:
490:
491: return $this->info;
492: }
493:
494:
495:
496: public function error($message) {
497: $this->CleanUp();
498: if (!isset($this->info['error'])) {
499: $this->info['error'] = array();
500: }
501: $this->info['error'][] = $message;
502: return $this->info;
503: }
504:
505:
506:
507: public function warning($message) {
508: $this->info['warning'][] = $message;
509: return true;
510: }
511:
512:
513:
514: private function CleanUp() {
515:
516:
517: $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
518: foreach ($AVpossibleEmptyKeys as $dummy => $key) {
519: if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
520: unset($this->info['audio'][$key]);
521: }
522: if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
523: unset($this->info['video'][$key]);
524: }
525: }
526:
527:
528: if (!empty($this->info)) {
529: foreach ($this->info as $key => $value) {
530: if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
531: unset($this->info[$key]);
532: }
533: }
534: }
535:
536:
537: if (empty($this->info['fileformat'])) {
538: if (isset($this->info['avdataoffset'])) {
539: unset($this->info['avdataoffset']);
540: }
541: if (isset($this->info['avdataend'])) {
542: unset($this->info['avdataend']);
543: }
544: }
545:
546:
547: if (!empty($this->info['error'])) {
548: $this->info['error'] = array_values(array_unique($this->info['error']));
549: }
550: if (!empty($this->info['warning'])) {
551: $this->info['warning'] = array_values(array_unique($this->info['warning']));
552: }
553:
554:
555: unset($this->info['php_memory_limit']);
556:
557: return true;
558: }
559:
560:
561:
562: public function GetFileFormatArray() {
563: static $format_info = array();
564: if (empty($format_info)) {
565: $format_info = array(
566:
567:
568:
569:
570: 'ac3' => array(
571: 'pattern' => '^\x0B\x77',
572: 'group' => 'audio',
573: 'module' => 'ac3',
574: 'mime_type' => 'audio/ac3',
575: ),
576:
577:
578: 'adif' => array(
579: 'pattern' => '^ADIF',
580: 'group' => 'audio',
581: 'module' => 'aac',
582: 'mime_type' => 'application/octet-stream',
583: 'fail_ape' => 'WARNING',
584: ),
585:
586: 587: 588: 589: 590: 591: 592: 593: 594:
595:
596: 'adts' => array(
597: 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
598: 'group' => 'audio',
599: 'module' => 'aac',
600: 'mime_type' => 'application/octet-stream',
601: 'fail_ape' => 'WARNING',
602: ),
603:
604:
605:
606: 'au' => array(
607: 'pattern' => '^\.snd',
608: 'group' => 'audio',
609: 'module' => 'au',
610: 'mime_type' => 'audio/basic',
611: ),
612:
613:
614: 'amr' => array(
615: 'pattern' => '^\x23\x21AMR\x0A',
616: 'group' => 'audio',
617: 'module' => 'amr',
618: 'mime_type' => 'audio/amr',
619: ),
620:
621:
622: 'avr' => array(
623: 'pattern' => '^2BIT',
624: 'group' => 'audio',
625: 'module' => 'avr',
626: 'mime_type' => 'application/octet-stream',
627: ),
628:
629:
630: 'bonk' => array(
631: 'pattern' => '^\x00(BONK|INFO|META| ID3)',
632: 'group' => 'audio',
633: 'module' => 'bonk',
634: 'mime_type' => 'audio/xmms-bonk',
635: ),
636:
637:
638: 'dss' => array(
639: 'pattern' => '^[\x02-\x03]ds[s2]',
640: 'group' => 'audio',
641: 'module' => 'dss',
642: 'mime_type' => 'application/octet-stream',
643: ),
644:
645:
646: 'dts' => array(
647: 'pattern' => '^\x7F\xFE\x80\x01',
648: 'group' => 'audio',
649: 'module' => 'dts',
650: 'mime_type' => 'audio/dts',
651: ),
652:
653:
654: 'flac' => array(
655: 'pattern' => '^fLaC',
656: 'group' => 'audio',
657: 'module' => 'flac',
658: 'mime_type' => 'audio/x-flac',
659: ),
660:
661:
662: 'la' => array(
663: 'pattern' => '^LA0[2-4]',
664: 'group' => 'audio',
665: 'module' => 'la',
666: 'mime_type' => 'application/octet-stream',
667: ),
668:
669:
670: 'lpac' => array(
671: 'pattern' => '^LPAC',
672: 'group' => 'audio',
673: 'module' => 'lpac',
674: 'mime_type' => 'application/octet-stream',
675: ),
676:
677:
678: 'midi' => array(
679: 'pattern' => '^MThd',
680: 'group' => 'audio',
681: 'module' => 'midi',
682: 'mime_type' => 'audio/midi',
683: ),
684:
685:
686: 'mac' => array(
687: 'pattern' => '^MAC ',
688: 'group' => 'audio',
689: 'module' => 'monkey',
690: 'mime_type' => 'application/octet-stream',
691: ),
692:
693:
694:
695:
696:
697:
698:
699:
700:
701:
702:
703:
704: 'it' => array(
705: 'pattern' => '^IMPM',
706: 'group' => 'audio',
707: 'module' => 'mod',
708:
709: 'mime_type' => 'audio/it',
710: ),
711:
712:
713: 'xm' => array(
714: 'pattern' => '^Extended Module',
715: 'group' => 'audio',
716: 'module' => 'mod',
717:
718: 'mime_type' => 'audio/xm',
719: ),
720:
721:
722: 's3m' => array(
723: 'pattern' => '^.{44}SCRM',
724: 'group' => 'audio',
725: 'module' => 'mod',
726:
727: 'mime_type' => 'audio/s3m',
728: ),
729:
730:
731: 'mpc' => array(
732: '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])',
733: 'group' => 'audio',
734: 'module' => 'mpc',
735: 'mime_type' => 'audio/x-musepack',
736: ),
737:
738:
739: 'mp3' => array(
740: 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
741: 'group' => 'audio',
742: 'module' => 'mp3',
743: 'mime_type' => 'audio/mpeg',
744: ),
745:
746:
747: 'ofr' => array(
748: 'pattern' => '^(\*RIFF|OFR)',
749: 'group' => 'audio',
750: 'module' => 'optimfrog',
751: 'mime_type' => 'application/octet-stream',
752: ),
753:
754:
755: 'rkau' => array(
756: 'pattern' => '^RKA',
757: 'group' => 'audio',
758: 'module' => 'rkau',
759: 'mime_type' => 'application/octet-stream',
760: ),
761:
762:
763: 'shn' => array(
764: 'pattern' => '^ajkg',
765: 'group' => 'audio',
766: 'module' => 'shorten',
767: 'mime_type' => 'audio/xmms-shn',
768: 'fail_id3' => 'ERROR',
769: 'fail_ape' => 'ERROR',
770: ),
771:
772:
773: 'tta' => array(
774: 'pattern' => '^TTA',
775: 'group' => 'audio',
776: 'module' => 'tta',
777: 'mime_type' => 'application/octet-stream',
778: ),
779:
780:
781: 'voc' => array(
782: 'pattern' => '^Creative Voice File',
783: 'group' => 'audio',
784: 'module' => 'voc',
785: 'mime_type' => 'audio/voc',
786: ),
787:
788:
789: 'vqf' => array(
790: 'pattern' => '^TWIN',
791: 'group' => 'audio',
792: 'module' => 'vqf',
793: 'mime_type' => 'application/octet-stream',
794: ),
795:
796:
797: 'wv' => array(
798: 'pattern' => '^wvpk',
799: 'group' => 'audio',
800: 'module' => 'wavpack',
801: 'mime_type' => 'application/octet-stream',
802: ),
803:
804:
805:
806:
807:
808: 'asf' => array(
809: 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
810: 'group' => 'audio-video',
811: 'module' => 'asf',
812: 'mime_type' => 'video/x-ms-asf',
813: 'iconv_req' => false,
814: ),
815:
816:
817: 'bink' => array(
818: 'pattern' => '^(BIK|SMK)',
819: 'group' => 'audio-video',
820: 'module' => 'bink',
821: 'mime_type' => 'application/octet-stream',
822: ),
823:
824:
825: 'flv' => array(
826: 'pattern' => '^FLV\x01',
827: 'group' => 'audio-video',
828: 'module' => 'flv',
829: 'mime_type' => 'video/x-flv',
830: ),
831:
832:
833: 'matroska' => array(
834: 'pattern' => '^\x1A\x45\xDF\xA3',
835: 'group' => 'audio-video',
836: 'module' => 'matroska',
837: 'mime_type' => 'video/x-matroska',
838: ),
839:
840:
841: 'mpeg' => array(
842: 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
843: 'group' => 'audio-video',
844: 'module' => 'mpeg',
845: 'mime_type' => 'video/mpeg',
846: ),
847:
848:
849: 'nsv' => array(
850: 'pattern' => '^NSV[sf]',
851: 'group' => 'audio-video',
852: 'module' => 'nsv',
853: 'mime_type' => 'application/octet-stream',
854: ),
855:
856:
857: 'ogg' => array(
858: 'pattern' => '^OggS',
859: 'group' => 'audio',
860: 'module' => 'ogg',
861: 'mime_type' => 'application/ogg',
862: 'fail_id3' => 'WARNING',
863: 'fail_ape' => 'WARNING',
864: ),
865:
866:
867: 'quicktime' => array(
868: 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
869: 'group' => 'audio-video',
870: 'module' => 'quicktime',
871: 'mime_type' => 'video/quicktime',
872: ),
873:
874:
875: 'riff' => array(
876: 'pattern' => '^(RIFF|SDSS|FORM)',
877: 'group' => 'audio-video',
878: 'module' => 'riff',
879: 'mime_type' => 'audio/x-wav',
880: 'fail_ape' => 'WARNING',
881: ),
882:
883:
884: 'real' => array(
885: 'pattern' => '^(\\.RMF|\\.ra)',
886: 'group' => 'audio-video',
887: 'module' => 'real',
888: 'mime_type' => 'audio/x-realaudio',
889: ),
890:
891:
892: 'swf' => array(
893: 'pattern' => '^(F|C)WS',
894: 'group' => 'audio-video',
895: 'module' => 'swf',
896: 'mime_type' => 'application/x-shockwave-flash',
897: ),
898:
899:
900: 'ts' => array(
901: 'pattern' => '^(\x47.{187}){10,}',
902: 'group' => 'audio-video',
903: 'module' => 'ts',
904: 'mime_type' => 'video/MP2T',
905: ),
906:
907:
908:
909:
910:
911: 'bmp' => array(
912: 'pattern' => '^BM',
913: 'group' => 'graphic',
914: 'module' => 'bmp',
915: 'mime_type' => 'image/bmp',
916: 'fail_id3' => 'ERROR',
917: 'fail_ape' => 'ERROR',
918: ),
919:
920:
921: 'gif' => array(
922: 'pattern' => '^GIF',
923: 'group' => 'graphic',
924: 'module' => 'gif',
925: 'mime_type' => 'image/gif',
926: 'fail_id3' => 'ERROR',
927: 'fail_ape' => 'ERROR',
928: ),
929:
930:
931: 'jpg' => array(
932: 'pattern' => '^\xFF\xD8\xFF',
933: 'group' => 'graphic',
934: 'module' => 'jpg',
935: 'mime_type' => 'image/jpeg',
936: 'fail_id3' => 'ERROR',
937: 'fail_ape' => 'ERROR',
938: ),
939:
940:
941: 'pcd' => array(
942: 'pattern' => '^.{2048}PCD_IPI\x00',
943: 'group' => 'graphic',
944: 'module' => 'pcd',
945: 'mime_type' => 'image/x-photo-cd',
946: 'fail_id3' => 'ERROR',
947: 'fail_ape' => 'ERROR',
948: ),
949:
950:
951:
952: 'png' => array(
953: 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
954: 'group' => 'graphic',
955: 'module' => 'png',
956: 'mime_type' => 'image/png',
957: 'fail_id3' => 'ERROR',
958: 'fail_ape' => 'ERROR',
959: ),
960:
961:
962:
963: 'svg' => array(
964: 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
965: 'group' => 'graphic',
966: 'module' => 'svg',
967: 'mime_type' => 'image/svg+xml',
968: 'fail_id3' => 'ERROR',
969: 'fail_ape' => 'ERROR',
970: ),
971:
972:
973:
974: 'tiff' => array(
975: 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
976: 'group' => 'graphic',
977: 'module' => 'tiff',
978: 'mime_type' => 'image/tiff',
979: 'fail_id3' => 'ERROR',
980: 'fail_ape' => 'ERROR',
981: ),
982:
983:
984:
985: 'efax' => array(
986: 'pattern' => '^\xDC\xFE',
987: 'group' => 'graphic',
988: 'module' => 'efax',
989: 'mime_type' => 'image/efax',
990: 'fail_id3' => 'ERROR',
991: 'fail_ape' => 'ERROR',
992: ),
993:
994:
995:
996:
997:
998: 'iso' => array(
999: 'pattern' => '^.{32769}CD001',
1000: 'group' => 'misc',
1001: 'module' => 'iso',
1002: 'mime_type' => 'application/octet-stream',
1003: 'fail_id3' => 'ERROR',
1004: 'fail_ape' => 'ERROR',
1005: 'iconv_req' => false,
1006: ),
1007:
1008:
1009: 'rar' => array(
1010: 'pattern' => '^Rar\!',
1011: 'group' => 'archive',
1012: 'module' => 'rar',
1013: 'mime_type' => 'application/octet-stream',
1014: 'fail_id3' => 'ERROR',
1015: 'fail_ape' => 'ERROR',
1016: ),
1017:
1018:
1019: 'szip' => array(
1020: 'pattern' => '^SZ\x0A\x04',
1021: 'group' => 'archive',
1022: 'module' => 'szip',
1023: 'mime_type' => 'application/octet-stream',
1024: 'fail_id3' => 'ERROR',
1025: 'fail_ape' => 'ERROR',
1026: ),
1027:
1028:
1029: 'tar' => array(
1030: '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}',
1031: 'group' => 'archive',
1032: 'module' => 'tar',
1033: 'mime_type' => 'application/x-tar',
1034: 'fail_id3' => 'ERROR',
1035: 'fail_ape' => 'ERROR',
1036: ),
1037:
1038:
1039: 'gz' => array(
1040: 'pattern' => '^\x1F\x8B\x08',
1041: 'group' => 'archive',
1042: 'module' => 'gzip',
1043: 'mime_type' => 'application/x-gzip',
1044: 'fail_id3' => 'ERROR',
1045: 'fail_ape' => 'ERROR',
1046: ),
1047:
1048:
1049: 'zip' => array(
1050: 'pattern' => '^PK\x03\x04',
1051: 'group' => 'archive',
1052: 'module' => 'zip',
1053: 'mime_type' => 'application/zip',
1054: 'fail_id3' => 'ERROR',
1055: 'fail_ape' => 'ERROR',
1056: ),
1057:
1058:
1059:
1060:
1061:
1062: 'par2' => array (
1063: 'pattern' => '^PAR2\x00PKT',
1064: 'group' => 'misc',
1065: 'module' => 'par2',
1066: 'mime_type' => 'application/octet-stream',
1067: 'fail_id3' => 'ERROR',
1068: 'fail_ape' => 'ERROR',
1069: ),
1070:
1071:
1072: 'pdf' => array(
1073: 'pattern' => '^\x25PDF',
1074: 'group' => 'misc',
1075: 'module' => 'pdf',
1076: 'mime_type' => 'application/pdf',
1077: 'fail_id3' => 'ERROR',
1078: 'fail_ape' => 'ERROR',
1079: ),
1080:
1081:
1082: 'msoffice' => array(
1083: 'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1',
1084: 'group' => 'misc',
1085: 'module' => 'msoffice',
1086: 'mime_type' => 'application/octet-stream',
1087: 'fail_id3' => 'ERROR',
1088: 'fail_ape' => 'ERROR',
1089: ),
1090:
1091:
1092: 'cue' => array(
1093: 'pattern' => '',
1094: 'group' => 'misc',
1095: 'module' => 'cue',
1096: 'mime_type' => 'application/octet-stream',
1097: ),
1098:
1099: );
1100: }
1101:
1102: return $format_info;
1103: }
1104:
1105:
1106:
1107: public function GetFileFormat(&$filedata, $filename='') {
1108:
1109:
1110:
1111:
1112:
1113:
1114: foreach ($this->GetFileFormatArray() as $format_name => $info) {
1115:
1116:
1117: if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1118: $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1119: return $info;
1120: }
1121: }
1122:
1123:
1124: if (preg_match('#\.mp[123a]$#i', $filename)) {
1125:
1126:
1127: $GetFileFormatArray = $this->GetFileFormatArray();
1128: $info = $GetFileFormatArray['mp3'];
1129: $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1130: return $info;
1131: } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1132:
1133:
1134:
1135: $GetFileFormatArray = $this->GetFileFormatArray();
1136: $info = $GetFileFormatArray['cue'];
1137: $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1138: return $info;
1139: }
1140:
1141: return false;
1142: }
1143:
1144:
1145:
1146: public function CharConvert(&$array, $encoding) {
1147:
1148:
1149: if ($encoding == $this->encoding) {
1150: return;
1151: }
1152:
1153:
1154: foreach ($array as $key => $value) {
1155:
1156:
1157: if (is_array($value)) {
1158: $this->CharConvert($array[$key], $encoding);
1159: }
1160:
1161:
1162: elseif (is_string($value)) {
1163: $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1164: }
1165: }
1166: }
1167:
1168:
1169: public function HandleAllTags() {
1170:
1171:
1172: static $tags;
1173: if (empty($tags)) {
1174: $tags = array(
1175: 'asf' => array('asf' , 'UTF-16LE'),
1176: 'midi' => array('midi' , 'ISO-8859-1'),
1177: 'nsv' => array('nsv' , 'ISO-8859-1'),
1178: 'ogg' => array('vorbiscomment' , 'UTF-8'),
1179: 'png' => array('png' , 'UTF-8'),
1180: 'tiff' => array('tiff' , 'ISO-8859-1'),
1181: 'quicktime' => array('quicktime' , 'UTF-8'),
1182: 'real' => array('real' , 'ISO-8859-1'),
1183: 'vqf' => array('vqf' , 'ISO-8859-1'),
1184: 'zip' => array('zip' , 'ISO-8859-1'),
1185: 'riff' => array('riff' , 'ISO-8859-1'),
1186: 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1187: 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1188: 'id3v2' => array('id3v2' , 'UTF-8'),
1189: 'ape' => array('ape' , 'UTF-8'),
1190: 'cue' => array('cue' , 'ISO-8859-1'),
1191: 'matroska' => array('matroska' , 'UTF-8'),
1192: 'flac' => array('vorbiscomment' , 'UTF-8'),
1193: 'divxtag' => array('divx' , 'ISO-8859-1'),
1194: 'iptc' => array('iptc' , 'ISO-8859-1'),
1195: );
1196: }
1197:
1198:
1199: foreach ($tags as $comment_name => $tagname_encoding_array) {
1200: list($tag_name, $encoding) = $tagname_encoding_array;
1201:
1202:
1203: if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1204: $this->info[$comment_name]['encoding'] = $encoding;
1205: }
1206:
1207:
1208: if (!empty($this->info[$comment_name]['comments'])) {
1209: foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1210: foreach ($valuearray as $key => $value) {
1211: if (is_string($value)) {
1212: $value = trim($value, " \r\n\t");
1213: }
1214: if ($value) {
1215: if (!is_numeric($key)) {
1216: $this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
1217: } else {
1218: $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
1219: }
1220: }
1221: }
1222: if ($tag_key == 'picture') {
1223: unset($this->info[$comment_name]['comments'][$tag_key]);
1224: }
1225: }
1226:
1227: if (!isset($this->info['tags'][$tag_name])) {
1228:
1229: continue;
1230: }
1231:
1232: if ($this->option_tags_html) {
1233: foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1234: $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
1235: }
1236: }
1237:
1238:
1239:
1240:
1241: if ($comment_name == 'id3v1') {
1242: if ($encoding == 'ISO-8859-1') {
1243: if (function_exists('iconv')) {
1244: foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1245: foreach ($valuearray as $key => $value) {
1246: if (preg_match('#^[\\x80-\\xFF]+$#', $value)) {
1247: foreach (array('windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
1248: if (@iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
1249: $encoding = $id3v1_bad_encoding;
1250: break 3;
1251: }
1252: }
1253: }
1254: }
1255: }
1256: }
1257: }
1258: }
1259:
1260:
1261: $this->CharConvert($this->info['tags'][$tag_name], $encoding);
1262: }
1263:
1264: }
1265:
1266:
1267:
1268: if (!empty($this->info['tags'])) {
1269: $unset_keys = array('tags', 'tags_html');
1270: foreach ($this->info['tags'] as $tagtype => $tagarray) {
1271: foreach ($tagarray as $tagname => $tagdata) {
1272: if ($tagname == 'picture') {
1273: foreach ($tagdata as $key => $tagarray) {
1274: $this->info['comments']['picture'][] = $tagarray;
1275: if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
1276: if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
1277: unset($this->info['tags'][$tagtype][$tagname][$key]);
1278: }
1279: if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
1280: unset($this->info['tags_html'][$tagtype][$tagname][$key]);
1281: }
1282: }
1283: }
1284: }
1285: }
1286: foreach ($unset_keys as $unset_key) {
1287:
1288: if (empty($this->info[$unset_key][$tagtype]['picture'])) {
1289: unset($this->info[$unset_key][$tagtype]['picture']);
1290: }
1291: if (empty($this->info[$unset_key][$tagtype])) {
1292: unset($this->info[$unset_key][$tagtype]);
1293: }
1294: if (empty($this->info[$unset_key])) {
1295: unset($this->info[$unset_key]);
1296: }
1297: }
1298:
1299: if (isset($this->info[$tagtype]['comments']['picture'])) {
1300: unset($this->info[$tagtype]['comments']['picture']);
1301: }
1302: if (empty($this->info[$tagtype]['comments'])) {
1303: unset($this->info[$tagtype]['comments']);
1304: }
1305: if (empty($this->info[$tagtype])) {
1306: unset($this->info[$tagtype]);
1307: }
1308: }
1309: }
1310: return true;
1311: }
1312:
1313: public function getHashdata($algorithm) {
1314: switch ($algorithm) {
1315: case 'md5':
1316: case 'sha1':
1317: break;
1318:
1319: default:
1320: return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1321: break;
1322: }
1323:
1324: if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1325:
1326:
1327:
1328:
1329:
1330:
1331:
1332:
1333:
1334:
1335:
1336:
1337:
1338:
1339:
1340:
1341:
1342:
1343:
1344: if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1345:
1346: $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
1347: $this->info[$algorithm.'_data'] = false;
1348:
1349: } else {
1350:
1351:
1352: $old_abort = ignore_user_abort(true);
1353:
1354:
1355: $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1356: touch($empty);
1357:
1358:
1359: $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1360: $file = $this->info['filenamepath'];
1361:
1362: if (GETID3_OS_ISWINDOWS) {
1363:
1364: if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1365:
1366: $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1367: $VorbisCommentError = `$commandline`;
1368:
1369: } else {
1370:
1371: $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1372:
1373: }
1374:
1375: } else {
1376:
1377: $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1378: $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1379: $VorbisCommentError = `$commandline`;
1380:
1381: }
1382:
1383: if (!empty($VorbisCommentError)) {
1384:
1385: $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;
1386: $this->info[$algorithm.'_data'] = false;
1387:
1388: } else {
1389:
1390:
1391: switch ($algorithm) {
1392: case 'md5':
1393: $this->info[$algorithm.'_data'] = md5_file($temp);
1394: break;
1395:
1396: case 'sha1':
1397: $this->info[$algorithm.'_data'] = sha1_file($temp);
1398: break;
1399: }
1400: }
1401:
1402:
1403: unlink($empty);
1404: unlink($temp);
1405:
1406:
1407: ignore_user_abort($old_abort);
1408:
1409: }
1410:
1411: } else {
1412:
1413: if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1414:
1415:
1416: $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1417:
1418: } else {
1419:
1420:
1421: switch ($algorithm) {
1422: case 'md5':
1423: $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1424: break;
1425:
1426: case 'sha1':
1427: $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1428: break;
1429: }
1430: }
1431:
1432: }
1433: return true;
1434: }
1435:
1436:
1437: public function ChannelsBitratePlaytimeCalculations() {
1438:
1439:
1440: if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
1441:
1442: } elseif ($this->info['audio']['channels'] == 1) {
1443: $this->info['audio']['channelmode'] = 'mono';
1444: } elseif ($this->info['audio']['channels'] == 2) {
1445: $this->info['audio']['channelmode'] = 'stereo';
1446: }
1447:
1448:
1449: $CombinedBitrate = 0;
1450: $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1451: $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1452: if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1453: $this->info['bitrate'] = $CombinedBitrate;
1454: }
1455:
1456:
1457:
1458:
1459:
1460:
1461:
1462: if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1463:
1464: if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1465:
1466: if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1467:
1468: if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1469:
1470:
1471: $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1472: $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1473: }
1474: }
1475: }
1476: }
1477:
1478: if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1479: $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1480: }
1481:
1482: if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1483: $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1484: }
1485: if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1486: if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1487:
1488: $this->info['audio']['bitrate'] = $this->info['bitrate'];
1489: } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1490:
1491: $this->info['video']['bitrate'] = $this->info['bitrate'];
1492: }
1493: }
1494:
1495:
1496: if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1497: $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1498: }
1499: }
1500:
1501:
1502: public function CalculateCompressionRatioVideo() {
1503: if (empty($this->info['video'])) {
1504: return false;
1505: }
1506: if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1507: return false;
1508: }
1509: if (empty($this->info['video']['bits_per_sample'])) {
1510: return false;
1511: }
1512:
1513: switch ($this->info['video']['dataformat']) {
1514: case 'bmp':
1515: case 'gif':
1516: case 'jpeg':
1517: case 'jpg':
1518: case 'png':
1519: case 'tiff':
1520: $FrameRate = 1;
1521: $PlaytimeSeconds = 1;
1522: $BitrateCompressed = $this->info['filesize'] * 8;
1523: break;
1524:
1525: default:
1526: if (!empty($this->info['video']['frame_rate'])) {
1527: $FrameRate = $this->info['video']['frame_rate'];
1528: } else {
1529: return false;
1530: }
1531: if (!empty($this->info['playtime_seconds'])) {
1532: $PlaytimeSeconds = $this->info['playtime_seconds'];
1533: } else {
1534: return false;
1535: }
1536: if (!empty($this->info['video']['bitrate'])) {
1537: $BitrateCompressed = $this->info['video']['bitrate'];
1538: } else {
1539: return false;
1540: }
1541: break;
1542: }
1543: $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1544:
1545: $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1546: return true;
1547: }
1548:
1549:
1550: public function CalculateCompressionRatioAudio() {
1551: if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
1552: return false;
1553: }
1554: $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));
1555:
1556: if (!empty($this->info['audio']['streams'])) {
1557: foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1558: if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1559: $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1560: }
1561: }
1562: }
1563: return true;
1564: }
1565:
1566:
1567: public function CalculateReplayGain() {
1568: if (isset($this->info['replay_gain'])) {
1569: if (!isset($this->info['replay_gain']['reference_volume'])) {
1570: $this->info['replay_gain']['reference_volume'] = (double) 89.0;
1571: }
1572: if (isset($this->info['replay_gain']['track']['adjustment'])) {
1573: $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1574: }
1575: if (isset($this->info['replay_gain']['album']['adjustment'])) {
1576: $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1577: }
1578:
1579: if (isset($this->info['replay_gain']['track']['peak'])) {
1580: $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1581: }
1582: if (isset($this->info['replay_gain']['album']['peak'])) {
1583: $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1584: }
1585: }
1586: return true;
1587: }
1588:
1589: public function ProcessAudioStreams() {
1590: if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1591: if (!isset($this->info['audio']['streams'])) {
1592: foreach ($this->info['audio'] as $key => $value) {
1593: if ($key != 'streams') {
1594: $this->info['audio']['streams'][0][$key] = $value;
1595: }
1596: }
1597: }
1598: }
1599: return true;
1600: }
1601:
1602: public function getid3_tempnam() {
1603: return tempnam($this->tempdir, 'gI3');
1604: }
1605:
1606: public function include_module($name) {
1607:
1608: if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
1609: throw new getid3_exception('Required module.'.$name.'.php is missing.');
1610: }
1611: include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
1612: return true;
1613: }
1614:
1615: }
1616:
1617:
1618: abstract class getid3_handler {
1619:
1620: 1621: 1622:
1623: protected $getid3;
1624:
1625: protected $data_string_flag = false;
1626: protected $data_string = '';
1627: protected $data_string_position = 0;
1628: protected $data_string_length = 0;
1629:
1630: private $dependency_to = null;
1631:
1632:
1633: public function __construct(getID3 $getid3, $call_module=null) {
1634: $this->getid3 = $getid3;
1635:
1636: if ($call_module) {
1637: $this->dependency_to = str_replace('getid3_', '', $call_module);
1638: }
1639: }
1640:
1641:
1642:
1643: abstract public function Analyze();
1644:
1645:
1646:
1647: public function AnalyzeString($string) {
1648:
1649: $this->setStringMode($string);
1650:
1651:
1652: $saved_avdataoffset = $this->getid3->info['avdataoffset'];
1653: $saved_avdataend = $this->getid3->info['avdataend'];
1654: $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null);
1655:
1656:
1657: $this->getid3->info['avdataoffset'] = 0;
1658: $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
1659:
1660:
1661: $this->Analyze();
1662:
1663:
1664: $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
1665: $this->getid3->info['avdataend'] = $saved_avdataend;
1666: $this->getid3->info['filesize'] = $saved_filesize;
1667:
1668:
1669: $this->data_string_flag = false;
1670: }
1671:
1672: public function setStringMode($string) {
1673: $this->data_string_flag = true;
1674: $this->data_string = $string;
1675: $this->data_string_length = strlen($string);
1676: }
1677:
1678: protected function ftell() {
1679: if ($this->data_string_flag) {
1680: return $this->data_string_position;
1681: }
1682: return ftell($this->getid3->fp);
1683: }
1684:
1685: protected function fread($bytes) {
1686: if ($this->data_string_flag) {
1687: $this->data_string_position += $bytes;
1688: return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
1689: }
1690: $pos = $this->ftell() + $bytes;
1691: if (!getid3_lib::intValueSupported($pos)) {
1692: throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
1693: }
1694: return fread($this->getid3->fp, $bytes);
1695: }
1696:
1697: protected function fseek($bytes, $whence=SEEK_SET) {
1698: if ($this->data_string_flag) {
1699: switch ($whence) {
1700: case SEEK_SET:
1701: $this->data_string_position = $bytes;
1702: break;
1703:
1704: case SEEK_CUR:
1705: $this->data_string_position += $bytes;
1706: break;
1707:
1708: case SEEK_END:
1709: $this->data_string_position = $this->data_string_length + $bytes;
1710: break;
1711: }
1712: return 0;
1713: } else {
1714: $pos = $bytes;
1715: if ($whence == SEEK_CUR) {
1716: $pos = $this->ftell() + $bytes;
1717: } elseif ($whence == SEEK_END) {
1718: $pos = $this->getid3->info['filesize'] + $bytes;
1719: }
1720: if (!getid3_lib::intValueSupported($pos)) {
1721: throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
1722: }
1723: }
1724: return fseek($this->getid3->fp, $bytes, $whence);
1725: }
1726:
1727: protected function feof() {
1728: if ($this->data_string_flag) {
1729: return $this->data_string_position >= $this->data_string_length;
1730: }
1731: return feof($this->getid3->fp);
1732: }
1733:
1734: final protected function isDependencyFor($module) {
1735: return $this->dependency_to == $module;
1736: }
1737:
1738: protected function error($text) {
1739: $this->getid3->info['error'][] = $text;
1740:
1741: return false;
1742: }
1743:
1744: protected function warning($text) {
1745: return $this->getid3->warning($text);
1746: }
1747:
1748: protected function notice($text) {
1749:
1750: }
1751:
1752: public function saveAttachment($name, $offset, $length, $image_mime=null) {
1753: try {
1754:
1755:
1756: if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
1757:
1758: $attachment = null;
1759:
1760:
1761: } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
1762:
1763: $this->fseek($offset);
1764: $attachment = $this->fread($length);
1765: if ($attachment === false || strlen($attachment) != $length) {
1766: throw new Exception('failed to read attachment data');
1767: }
1768:
1769:
1770: } else {
1771:
1772:
1773: $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1774: if (!is_dir($dir) || !is_writable($dir)) {
1775: throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
1776: }
1777: $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
1778:
1779:
1780: if (($fp_dest = fopen($dest, 'wb')) == false) {
1781: throw new Exception('failed to create file '.$dest);
1782: }
1783:
1784:
1785: $this->fseek($offset);
1786: $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
1787: $bytesleft = $length;
1788: while ($bytesleft > 0) {
1789: if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
1790: throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
1791: }
1792: $bytesleft -= $byteswritten;
1793: }
1794:
1795: fclose($fp_dest);
1796: $attachment = $dest;
1797:
1798: }
1799:
1800: } catch (Exception $e) {
1801:
1802:
1803: if (isset($fp_dest) && is_resource($fp_dest)) {
1804: fclose($fp_dest);
1805: unlink($dest);
1806: }
1807:
1808:
1809: $attachment = null;
1810: $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
1811:
1812: }
1813:
1814:
1815: $this->fseek($offset + $length);
1816:
1817: return $attachment;
1818: }
1819:
1820: }
1821:
1822:
1823: class getid3_exception extends Exception
1824: {
1825: public $message;
1826: }
1827: