source: branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/getid3.php @ 1300

Last change on this file since 1300 was 1300, checked in by gogo, 8 years ago

Update the MootoolsFileManager? to the latest cpojer with some modifications.
Add a demo for the MFM examples/mootools-file-manager.php
Change the default config for ImageManager? and ExtendedFileManager? for added security.

File size: 53.5 KB
Line 
1<?php
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
13define('GETID3_VERSION', '1.8.5-20110218');
14define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
15
16// attempt to define temp dir as something flexible but reliable
17$temp_dir = ini_get('upload_tmp_dir');
18if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
19        $temp_dir = '';
20}
21if (!$temp_dir && function_exists('sys_get_temp_dir')) {
22        // PHP v5.2.1+
23        // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
24        $temp_dir = sys_get_temp_dir();
25}
26$temp_dir = realpath($temp_dir);
27$open_basedir = ini_get('open_basedir');
28if ($open_basedir) {
29        // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
30        $temp_dir     = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
31        $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
32        if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
33                $temp_dir .= DIRECTORY_SEPARATOR;
34        }
35        $found_valid_tempdir = false;
36        $open_basedirs = explode(':', $open_basedir);
37        foreach ($open_basedirs as $basedir) {
38                if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
39                        $basedir .= DIRECTORY_SEPARATOR;
40                }
41                if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
42                        $found_valid_tempdir = true;
43                        break;
44                }
45        }
46        if (!$found_valid_tempdir) {
47                $temp_dir = '';
48        }
49        unset($open_basedirs, $found_valid_tempdir, $basedir);
50}
51if (!$temp_dir) {
52        $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
53}
54// $temp_dir = '/something/else/';  // feel free to override temp dir here if it works better for your system
55define('GETID3_TEMP_DIR', $temp_dir);
56unset($open_basedir, $temp_dir);
57
58
59// define a constant rather than looking up every time it is needed
60if (!defined('GETID3_OS_ISWINDOWS')) {
61        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
62                define('GETID3_OS_ISWINDOWS', true);
63        } else {
64                define('GETID3_OS_ISWINDOWS', false);
65        }
66}
67
68// Get base path of getID3() - ONCE
69if (!defined('GETID3_INCLUDEPATH')) {
70        foreach (get_included_files() as $key => $val) {
71                if (basename($val) == 'getid3.php') {
72                        define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
73                        break;
74                }
75        }
76}
77
78// End: Defines
79
80
81class getID3
82{
83        // public: Settings
84        var $encoding        = 'ISO-8859-1';   // CASE SENSITIVE! - i.e. (must be supported by iconv())
85                                                                                   // Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
86
87        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'
88
89        var $tempdir         = GETID3_TEMP_DIR;
90
91        // public: Optional tag checks - disable for speed.
92        var $option_tag_id3v1         = true;  // Read and process ID3v1 tags
93        var $option_tag_id3v2         = true;  // Read and process ID3v2 tags
94        var $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
95        var $option_tag_apetag        = true;  // Read and process APE tags
96        var $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
97        var $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
98
99        // public: Optional tag/comment calucations
100        var $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
101
102        // public: Optional calculations
103        var $option_md5_data          = false; // Get MD5 sum of data part - slow
104        var $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
105        var $option_sha1_data         = false; // Get SHA1 sum of data part - slow
106        var $option_max_2gb_check     = null;  // Check whether file is larger than 2GB and thus not supported by 32-bit PHP
107
108        // private
109        var $filename        = '';
110        var $startup_error   = '';
111        var $startup_warning = '';
112
113
114        // public: constructor
115        function getID3() {
116
117                // Check for PHP version
118                $required_php_version = '5.0.5';
119                if (!function_exists('version_compare') || version_compare(PHP_VERSION, $required_php_version, '<')) {
120                        // version_compare not available before PHP v4.1.0, so don't use for first version checking
121                        $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
122                        return false;
123                }
124
125                // Check memory
126                $memory_limit = ini_get('memory_limit');
127                if (preg_match('#([0-9]+)M#i', $memory_limit, $matches)) {
128                        // could be stored as "16M" rather than 16777216 for example
129                        $memory_limit = $matches[1] * 1048576;
130                } elseif (preg_match('#([0-9]+)G#i', $memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
131                        // could be stored as "2G" rather than 2147483648 for example
132                        $memory_limit = $matches[1] * 1073741824;
133                }
134                if ($memory_limit <= 0) {
135                        // memory limits probably disabled
136                } elseif ($memory_limit <= 4194304) {
137                        $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
138                } elseif ($memory_limit <= 12582912) {
139                        $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';
140                }
141
142                // Check safe_mode off
143                if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
144                        $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
145                }
146
147                if (intval(ini_get('mbstring.func_overload')) > 0) {
148                        $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
149                }
150
151                /*
152                // Check timezone config setting
153                // this is needed to prevent E_STRICT warnings with any time/date functions
154                if (!ini_get('date.timezone')) {
155                        if (function_exists('date_default_timezone_set')) { // exists since PHP v5.1.0
156                                $this->warning('php.ini should have "date.timezone" set, but it does not. Setting timezone to "'.date_default_timezone_get().'"');
157                                date_default_timezone_set(date_default_timezone_get());
158                        } else {
159                                $this->warning('php.ini should have "date.timezone" set, but it does not.');
160                        }
161                }
162                */
163
164                // Check for magic_quotes_runtime
165                if (function_exists('get_magic_quotes_runtime')) {
166                        if (get_magic_quotes_runtime()) {
167                                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).');
168                        }
169                }
170
171                // Check for magic_quotes_gpc
172                if (function_exists('magic_quotes_gpc')) {
173                        if (get_magic_quotes_gpc()) {
174                                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).');
175                        }
176                }
177
178                // Load support library
179                if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
180                        $this->startup_error .= 'getid3.lib.php is missing or corrupt';
181                }
182
183                if ($this->option_max_2gb_check === null) {
184                        $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
185                }
186
187
188                // Needed for Windows only:
189                // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
190                //   as well as other helper functions such as head, tail, md5sum, etc
191                // This path cannot contain spaces, but the below code will attempt to get the
192                //   8.3-equivalent path automatically
193                // IMPORTANT: This path must include the trailing slash
194                if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
195
196                        $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
197
198                        if (!is_dir($helperappsdir)) {
199                                $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
200                        } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
201                                $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
202                                $path_so_far = array();
203                                foreach ($DirPieces as $key => $value) {
204                                        if (strpos($value, ' ') !== false) {
205                                                if (!empty($path_so_far)) {
206                                                        $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
207                                                        $dir_listing = `$commandline`;
208                                                        $lines = explode("\n", $dir_listing);
209                                                        foreach ($lines as $line) {
210                                                                $line = trim($line);
211                                                                if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
212                                                                        list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
213                                                                        if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
214                                                                                $value = $shortname;
215                                                                        }
216                                                                }
217                                                        }
218                                                } else {
219                                                        $this->startup_error .= '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.';
220                                                }
221                                        }
222                                        $path_so_far[] = $value;
223                                }
224                                $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
225                        }
226                        define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
227                }
228
229        }
230
231
232        // public: setOption
233        function setOption($optArray) {
234                if (!is_array($optArray) || empty($optArray)) {
235                        return false;
236                }
237                foreach ($optArray as $opt => $val) {
238                        //if (isset($this, $opt) === false) {
239                        if (isset($this->$opt) === false) {
240                                continue;
241                        }
242                        $this->$opt = $val;
243                }
244                return true;
245        }
246
247
248        // public: analyze file
249        function analyze($filename) {
250                try {
251                        if (!empty($this->startup_error)) {
252                                return $this->error($this->startup_error);
253                        }
254                        if (!empty($this->startup_warning)) {
255                                $this->warning($this->startup_warning);
256                        }
257
258                        // init result array and set parameters
259                        $this->info = array();
260                        $this->info['GETID3_VERSION'] = GETID3_VERSION;
261
262                        // Check encoding/iconv support
263                        if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
264                                $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
265                                if (GETID3_OS_ISWINDOWS) {
266                                        $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';
267                                } else {
268                                        $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
269                                }
270                                return $this->error($errormessage);
271                        }
272
273                        // remote files not supported
274                        if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
275                                return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
276                        }
277
278                        $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
279                        $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
280
281                        // open local file
282                        if (file_exists($filename) && is_file($filename)) {
283                                ob_start();
284                                if ($fp = fopen($filename, 'rb')) {
285                                        // great
286                                        ob_end_clean();
287                                } else {
288                                        $fopen_error = ob_get_contents();
289                                        ob_end_clean();
290                                        return $this->error('Could not open file "'.$filename.'" (fopen says: '.$fopen_error.')');
291                                }
292                        } else {
293                                return $this->error('Could not open "'.$filename.'" (does not exist, or is not a file)');
294                        }
295
296                        // set parameters
297                        $this->info['filesize'] = filesize($filename);
298
299                        // option_max_2gb_check
300                        if ($this->option_max_2gb_check) {
301                                // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
302                                // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
303                                // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
304                                $fseek = fseek($fp, 0, SEEK_END);
305                                if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($fp) == 0)) ||
306                                        ($this->info['filesize'] < 0) ||
307                                        (ftell($fp) < 0)) {
308                                                $real_filesize = false;
309                                                if (GETID3_OS_ISWINDOWS) {
310                                                        $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
311                                                        $dir_output = `$commandline`;
312                                                        if (preg_match('#1 File\(s\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
313                                                                $real_filesize = (float) $matches[1];
314                                                        }
315                                                } else {
316                                                        $commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename);
317                                                        $dir_output = `$commandline`;
318                                                        if (preg_match('#([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.str_replace('#', '\\#', preg_quote($filename)).'$#', $dir_output, $matches)) {
319                                                                $real_filesize = (float) $matches[1];
320                                                        }
321                                                }
322                                                if ($real_filesize === false) {
323                                                        unset($this->info['filesize']);
324                                                        fclose($fp);
325                                                        return $this->error('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
326                                                } elseif (getid3_lib::intValueSupported($real_filesize)) {
327                                                        unset($this->info['filesize']);
328                                                        fclose($fp);
329                                                        return $this->error('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');
330                                                }
331                                                $this->info['filesize'] = $real_filesize;
332                                                $this->error('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.');
333                                }
334                        }
335
336                        // set more parameters
337                        $this->info['avdataoffset']        = 0;
338                        $this->info['avdataend']           = $this->info['filesize'];
339                        $this->info['fileformat']          = '';                // filled in later
340                        $this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
341                        $this->info['video']['dataformat'] = '';                // filled in later, unset if not used
342                        $this->info['tags']                = array();           // filled in later, unset if not used
343                        $this->info['error']               = array();           // filled in later, unset if not used
344                        $this->info['warning']             = array();           // filled in later, unset if not used
345                        $this->info['comments']            = array();           // filled in later, unset if not used
346                        $this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
347
348                        // set redundant parameters - might be needed in some include file
349                        $this->info['filename']            = basename($filename);
350                        $this->info['filepath']            = str_replace('\\', '/', realpath(dirname($filename)));
351                        $this->info['filenamepath']        = $this->info['filepath'].'/'.$this->info['filename'];
352
353
354                        // handle ID3v2 tag - done first - already at beginning of file
355                        // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
356                        if ($this->option_tag_id3v2) {
357
358                                $GETID3_ERRORARRAY = &$this->info['warning'];
359                                if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) {
360                                        $tag = new getid3_id3v2($fp, $this->info);
361                                        unset($tag);
362                                }
363
364                        } else {
365
366                                fseek($fp, 0, SEEK_SET);
367                                $header = fread($fp, 10);
368                                if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
369                                        $this->info['id3v2']['header']           = true;
370                                        $this->info['id3v2']['majorversion']     = ord($header{3});
371                                        $this->info['id3v2']['minorversion']     = ord($header{4});
372                                        $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
373
374                                        $this->info['id3v2']['tag_offset_start'] = 0;
375                                        $this->info['id3v2']['tag_offset_end']   = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
376                                        $this->info['avdataoffset']              = $this->info['id3v2']['tag_offset_end'];
377                                }
378
379                        }
380
381
382                        // handle ID3v1 tag
383                        if ($this->option_tag_id3v1) {
384                                if (!file_exists(GETID3_INCLUDEPATH.'module.tag.id3v1.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) {
385                                        return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
386                                }
387                                $tag = new getid3_id3v1($fp, $this->info);
388                                unset($tag);
389                        }
390
391                        // handle APE tag
392                        if ($this->option_tag_apetag) {
393                                if (!file_exists(GETID3_INCLUDEPATH.'module.tag.apetag.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) {
394                                        return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
395                                }
396                                $tag = new getid3_apetag($fp, $this->info);
397                                unset($tag);
398                        }
399
400                        // handle lyrics3 tag
401                        if ($this->option_tag_lyrics3) {
402                                if (!file_exists(GETID3_INCLUDEPATH.'module.tag.lyrics3.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) {
403                                        return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
404                                }
405                                $tag = new getid3_lyrics3($fp, $this->info);
406                                unset($tag);
407                        }
408
409                        // read 32 kb file data
410                        fseek($fp, $this->info['avdataoffset'], SEEK_SET);
411                        $formattest = fread($fp, 32774);
412
413                        // determine format
414                        $determined_format = $this->GetFileFormat($formattest, $filename);
415
416                        // unable to determine file format
417                        if (!$determined_format) {
418                                fclose($fp);
419                                return $this->error('unable to determine file format');
420                        }
421
422                        // check for illegal ID3 tags
423                        if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
424                                if ($determined_format['fail_id3'] === 'ERROR') {
425                                        fclose($fp);
426                                        return $this->error('ID3 tags not allowed on this file type.');
427                                } elseif ($determined_format['fail_id3'] === 'WARNING') {
428                                        $this->info['warning'][] = 'ID3 tags not allowed on this file type.';
429                                }
430                        }
431
432                        // check for illegal APE tags
433                        if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
434                                if ($determined_format['fail_ape'] === 'ERROR') {
435                                        fclose($fp);
436                                        return $this->error('APE tags not allowed on this file type.');
437                                } elseif ($determined_format['fail_ape'] === 'WARNING') {
438                                        $this->info['warning'][] = 'APE tags not allowed on this file type.';
439                                }
440                        }
441
442                        // set mime type
443                        $this->info['mime_type'] = $determined_format['mime_type'];
444
445                        // supported format signature pattern detected, but module deleted
446                        if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
447                                fclose($fp);
448                                return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
449                        }
450
451                        // module requires iconv support
452                        if (!function_exists('iconv') && !empty($determined_format['iconv_req'])) {
453                                return $this->error('iconv support is required for this module ('.$determined_format['include'].').');
454                        }
455
456                        // include module
457                        include_once(GETID3_INCLUDEPATH.$determined_format['include']);
458
459                        // instantiate module class
460                        $class_name = 'getid3_'.$determined_format['module'];
461                        if (!class_exists($class_name)) {
462                                return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
463                        }
464                        if (isset($determined_format['option'])) {
465                                $class = new $class_name($fp, $this->info, $determined_format['option']);
466                        } else {
467                                $class = new $class_name($fp, $this->info);
468                        }
469                        unset($class);
470
471                        // close file
472                        fclose($fp);
473
474                        // process all tags - copy to 'tags' and convert charsets
475                        if ($this->option_tags_process) {
476                                $this->HandleAllTags();
477                        }
478
479                        // perform more calculations
480                        if ($this->option_extra_info) {
481                                $this->ChannelsBitratePlaytimeCalculations();
482                                $this->CalculateCompressionRatioVideo();
483                                $this->CalculateCompressionRatioAudio();
484                                $this->CalculateReplayGain();
485                                $this->ProcessAudioStreams();
486                        }
487
488                        // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
489                        if ($this->option_md5_data) {
490                                // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
491                                if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
492                                        $this->getHashdata('md5');
493                                }
494                        }
495
496                        // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
497                        if ($this->option_sha1_data) {
498                                $this->getHashdata('sha1');
499                        }
500
501                        // remove undesired keys
502                        $this->CleanUp();
503
504                } catch (Exception $e) {
505                        if (isset($this->info['error'])) {
506                                $this->info['error'][] = 'Caught exception: '.$e->getMessage();
507                        } else {
508                                $this->info['error'] = array('Caught exception: '.$e->getMessage());
509                        }
510                }
511
512                // return info array
513                return $this->info;
514        }
515
516
517        // private: error handling
518        function error($message) {
519                $this->CleanUp();
520
521                $this->info['error'][] = $message;
522                return $this->info;
523        }
524
525
526        // private: warning handling
527        function warning($message) {
528                $this->info['warning'][] = $message;
529                return true;
530        }
531
532
533        // private: CleanUp
534        function CleanUp() {
535
536                // remove possible empty keys
537                $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
538                foreach ($AVpossibleEmptyKeys as $dummy => $key) {
539                        if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
540                                unset($this->info['audio'][$key]);
541                        }
542                        if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
543                                unset($this->info['video'][$key]);
544                        }
545                }
546
547                // remove empty root keys
548                if (!empty($this->info)) {
549                        foreach ($this->info as $key => $value) {
550                                if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
551                                        unset($this->info[$key]);
552                                }
553                        }
554                }
555
556                // remove meaningless entries from unknown-format files
557                if (empty($this->info['fileformat'])) {
558                        if (isset($this->info['avdataoffset'])) {
559                                unset($this->info['avdataoffset']);
560                        }
561                        if (isset($this->info['avdataend'])) {
562                                unset($this->info['avdataend']);
563                        }
564                }
565        }
566
567
568        // return array containing information about all supported formats
569        function GetFileFormatArray() {
570                static $format_info = array();
571                if (empty($format_info)) {
572                        $format_info = array(
573
574                                // Audio formats
575
576                                // AC-3   - audio      - Dolby AC-3 / Dolby Digital
577                                'ac3'  => array(
578                                                        'pattern'   => '^\x0B\x77',
579                                                        'group'     => 'audio',
580                                                        'module'    => 'ac3',
581                                                        'mime_type' => 'audio/ac3',
582                                                ),
583
584                                // AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
585                                'adif' => array(
586                                                        'pattern'   => '^ADIF',
587                                                        'group'     => 'audio',
588                                                        'module'    => 'aac',
589                                                        'option'    => 'adif',
590                                                        'mime_type' => 'application/octet-stream',
591                                                        'fail_ape'  => 'WARNING',
592                                                ),
593
594
595                                // AA   - audio       - Audible Audiobook
596                                'adts' => array(
597                                                        'pattern'   => '^.{4}\x57\x90\x75\x36',
598                                                        'group'     => 'audio',
599                                                        'module'    => 'aa',
600                                                        'mime_type' => 'audio/audible ',
601                                                ),
602
603                                // AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
604                                'adts' => array(
605                                                        'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
606                                                        'group'     => 'audio',
607                                                        'module'    => 'aac',
608                                                        'option'    => 'adts',
609                                                        'mime_type' => 'application/octet-stream',
610                                                        'fail_ape'  => 'WARNING',
611                                                ),
612
613
614                                // AU   - audio       - NeXT/Sun AUdio (AU)
615                                'au'   => array(
616                                                        'pattern'   => '^\.snd',
617                                                        'group'     => 'audio',
618                                                        'module'    => 'au',
619                                                        'mime_type' => 'audio/basic',
620                                                ),
621
622                                // AVR  - audio       - Audio Visual Research
623                                'avr'  => array(
624                                                        'pattern'   => '^2BIT',
625                                                        'group'     => 'audio',
626                                                        'module'    => 'avr',
627                                                        'mime_type' => 'application/octet-stream',
628                                                ),
629
630                                // BONK - audio       - Bonk v0.9+
631                                'bonk' => array(
632                                                        'pattern'   => '^\x00(BONK|INFO|META| ID3)',
633                                                        'group'     => 'audio',
634                                                        'module'    => 'bonk',
635                                                        'mime_type' => 'audio/xmms-bonk',
636                                                ),
637
638                                // DSS  - audio       - Digital Speech Standard
639                                'dss'  => array(
640                                                        'pattern'   => '^[\x02-\x03]dss',
641                                                        'group'     => 'audio',
642                                                        'module'    => 'dss',
643                                                        'mime_type' => 'application/octet-stream',
644                                                ),
645
646                                // DTS  - audio       - Dolby Theatre System
647                                'dts'  => array(
648                                                        'pattern'   => '^\x7F\xFE\x80\x01',
649                                                        'group'     => 'audio',
650                                                        'module'    => 'dts',
651                                                        'mime_type' => 'audio/dts',
652                                                ),
653
654                                // FLAC - audio       - Free Lossless Audio Codec
655                                'flac' => array(
656                                                        'pattern'   => '^fLaC',
657                                                        'group'     => 'audio',
658                                                        'module'    => 'flac',
659                                                        'mime_type' => 'audio/x-flac',
660                                                ),
661
662                                // LA   - audio       - Lossless Audio (LA)
663                                'la'   => array(
664                                                        'pattern'   => '^LA0[2-4]',
665                                                        'group'     => 'audio',
666                                                        'module'    => 'la',
667                                                        'mime_type' => 'application/octet-stream',
668                                                ),
669
670                                // LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
671                                'lpac' => array(
672                                                        'pattern'   => '^LPAC',
673                                                        'group'     => 'audio',
674                                                        'module'    => 'lpac',
675                                                        'mime_type' => 'application/octet-stream',
676                                                ),
677
678                                // MIDI - audio       - MIDI (Musical Instrument Digital Interface)
679                                'midi' => array(
680                                                        'pattern'   => '^MThd',
681                                                        'group'     => 'audio',
682                                                        'module'    => 'midi',
683                                                        'mime_type' => 'audio/midi',
684                                                ),
685
686                                // MAC  - audio       - Monkey's Audio Compressor
687                                'mac'  => array(
688                                                        'pattern'   => '^MAC ',
689                                                        'group'     => 'audio',
690                                                        'module'    => 'monkey',
691                                                        'mime_type' => 'application/octet-stream',
692                                                ),
693
694// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
695//                              // MOD  - audio       - MODule (assorted sub-formats)
696//                              'mod'  => array(
697//                                                      'pattern'   => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
698//                                                      'group'     => 'audio',
699//                                                      'module'    => 'mod',
700//                                                      'option'    => 'mod',
701//                                                      'mime_type' => 'audio/mod',
702//                                              ),
703
704                                // MOD  - audio       - MODule (Impulse Tracker)
705                                'it'   => array(
706                                                        'pattern'   => '^IMPM',
707                                                        'group'     => 'audio',
708                                                        'module'    => 'mod',
709                                                        'option'    => 'it',
710                                                        'mime_type' => 'audio/it',
711                                                ),
712
713                                // MOD  - audio       - MODule (eXtended Module, various sub-formats)
714                                'xm'   => array(
715                                                        'pattern'   => '^Extended Module',
716                                                        'group'     => 'audio',
717                                                        'module'    => 'mod',
718                                                        'option'    => 'xm',
719                                                        'mime_type' => 'audio/xm',
720                                                ),
721
722                                // MOD  - audio       - MODule (ScreamTracker)
723                                's3m'  => array(
724                                                        'pattern'   => '^.{44}SCRM',
725                                                        'group'     => 'audio',
726                                                        'module'    => 'mod',
727                                                        'option'    => 's3m',
728                                                        'mime_type' => 'audio/s3m',
729                                                ),
730
731                                // MPC  - audio       - Musepack / MPEGplus
732                                'mpc'  => array(
733                                                        '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])',
734                                                        'group'     => 'audio',
735                                                        'module'    => 'mpc',
736                                                        'mime_type' => 'audio/x-musepack',
737                                                ),
738
739                                // MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
740                                'mp3'  => array(
741                                                        '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]',
742                                                        'group'     => 'audio',
743                                                        'module'    => 'mp3',
744                                                        'mime_type' => 'audio/mpeg',
745                                                ),
746
747                                // OFR  - audio       - OptimFROG
748                                'ofr'  => array(
749                                                        'pattern'   => '^(\*RIFF|OFR)',
750                                                        'group'     => 'audio',
751                                                        'module'    => 'optimfrog',
752                                                        'mime_type' => 'application/octet-stream',
753                                                ),
754
755                                // RKAU - audio       - RKive AUdio compressor
756                                'rkau' => array(
757                                                        'pattern'   => '^RKA',
758                                                        'group'     => 'audio',
759                                                        'module'    => 'rkau',
760                                                        'mime_type' => 'application/octet-stream',
761                                                ),
762
763                                // SHN  - audio       - Shorten
764                                'shn'  => array(
765                                                        'pattern'   => '^ajkg',
766                                                        'group'     => 'audio',
767                                                        'module'    => 'shorten',
768                                                        'mime_type' => 'audio/xmms-shn',
769                                                        'fail_id3'  => 'ERROR',
770                                                        'fail_ape'  => 'ERROR',
771                                                ),
772
773                                // TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
774                                'tta'  => array(
775                                                        'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
776                                                        'group'     => 'audio',
777                                                        'module'    => 'tta',
778                                                        'mime_type' => 'application/octet-stream',
779                                                ),
780
781                                // VOC  - audio       - Creative Voice (VOC)
782                                'voc'  => array(
783                                                        'pattern'   => '^Creative Voice File',
784                                                        'group'     => 'audio',
785                                                        'module'    => 'voc',
786                                                        'mime_type' => 'audio/voc',
787                                                ),
788
789                                // VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
790                                'vqf'  => array(
791                                                        'pattern'   => '^TWIN',
792                                                        'group'     => 'audio',
793                                                        'module'    => 'vqf',
794                                                        'mime_type' => 'application/octet-stream',
795                                                ),
796
797                                // WV  - audio        - WavPack (v4.0+)
798                                'wv'   => array(
799                                                        'pattern'   => '^wvpk',
800                                                        'group'     => 'audio',
801                                                        'module'    => 'wavpack',
802                                                        'mime_type' => 'application/octet-stream',
803                                                ),
804
805
806                                // Audio-Video formats
807
808                                // ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
809                                'asf'  => array(
810                                                        'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
811                                                        'group'     => 'audio-video',
812                                                        'module'    => 'asf',
813                                                        'mime_type' => 'video/x-ms-asf',
814                                                        'iconv_req' => false,
815                                                ),
816
817                                // BINK - audio/video - Bink / Smacker
818                                'bink' => array(
819                                                        'pattern'   => '^(BIK|SMK)',
820                                                        'group'     => 'audio-video',
821                                                        'module'    => 'bink',
822                                                        'mime_type' => 'application/octet-stream',
823                                                ),
824
825                                // FLV  - audio/video - FLash Video
826                                'flv' => array(
827                                                        'pattern'   => '^FLV\x01',
828                                                        'group'     => 'audio-video',
829                                                        'module'    => 'flv',
830                                                        'mime_type' => 'video/x-flv',
831                                                ),
832
833                                // MKAV - audio/video - Mastroka
834                                'matroska' => array(
835                                                        'pattern'   => '^\x1A\x45\xDF\xA3',
836                                                        'group'     => 'audio-video',
837                                                        'module'    => 'matroska',
838                                                        'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
839                                                ),
840
841                                // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
842                                'mpeg' => array(
843                                                        'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
844                                                        'group'     => 'audio-video',
845                                                        'module'    => 'mpeg',
846                                                        'mime_type' => 'video/mpeg',
847                                                ),
848
849                                // NSV  - audio/video - Nullsoft Streaming Video (NSV)
850                                'nsv'  => array(
851                                                        'pattern'   => '^NSV[sf]',
852                                                        'group'     => 'audio-video',
853                                                        'module'    => 'nsv',
854                                                        'mime_type' => 'application/octet-stream',
855                                                ),
856
857                                // Ogg  - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
858                                'ogg'  => array(
859                                                        'pattern'   => '^OggS',
860                                                        'group'     => 'audio',
861                                                        'module'    => 'ogg',
862                                                        'mime_type' => 'application/ogg',
863                                                        'fail_id3'  => 'WARNING',
864                                                        'fail_ape'  => 'WARNING',
865                                                ),
866
867                                // QT   - audio/video - Quicktime
868                                'quicktime' => array(
869                                                        'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
870                                                        'group'     => 'audio-video',
871                                                        'module'    => 'quicktime',
872                                                        'mime_type' => 'video/quicktime',
873                                                ),
874
875                                // 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)
876                                'riff' => array(
877                                                        'pattern'   => '^(RIFF|SDSS|FORM)',
878                                                        'group'     => 'audio-video',
879                                                        'module'    => 'riff',
880                                                        'mime_type' => 'audio/x-wave',
881                                                        'fail_ape'  => 'WARNING',
882                                                ),
883
884                                // Real - audio/video - RealAudio, RealVideo
885                                'real' => array(
886                                                        'pattern'   => '^(\\.RMF|\\.ra)',
887                                                        'group'     => 'audio-video',
888                                                        'module'    => 'real',
889                                                        'mime_type' => 'audio/x-realaudio',
890                                                ),
891
892                                // SWF - audio/video - ShockWave Flash
893                                'swf' => array(
894                                                        'pattern'   => '^(F|C)WS',
895                                                        'group'     => 'audio-video',
896                                                        'module'    => 'swf',
897                                                        'mime_type' => 'application/x-shockwave-flash',
898                                                ),
899
900
901                                // Still-Image formats
902
903                                // BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
904                                'bmp'  => array(
905                                                        'pattern'   => '^BM',
906                                                        'group'     => 'graphic',
907                                                        'module'    => 'bmp',
908                                                        'mime_type' => 'image/bmp',
909                                                        'fail_id3'  => 'ERROR',
910                                                        'fail_ape'  => 'ERROR',
911                                                ),
912
913                                // GIF  - still image - Graphics Interchange Format
914                                'gif'  => array(
915                                                        'pattern'   => '^GIF',
916                                                        'group'     => 'graphic',
917                                                        'module'    => 'gif',
918                                                        'mime_type' => 'image/gif',
919                                                        'fail_id3'  => 'ERROR',
920                                                        'fail_ape'  => 'ERROR',
921                                                ),
922
923                                // JPEG - still image - Joint Photographic Experts Group (JPEG)
924                                'jpg'  => array(
925                                                        'pattern'   => '^\xFF\xD8\xFF',
926                                                        'group'     => 'graphic',
927                                                        'module'    => 'jpg',
928                                                        'mime_type' => 'image/jpeg',
929                                                        'fail_id3'  => 'ERROR',
930                                                        'fail_ape'  => 'ERROR',
931                                                ),
932
933                                // PCD  - still image - Kodak Photo CD
934                                'pcd'  => array(
935                                                        'pattern'   => '^.{2048}PCD_IPI\x00',
936                                                        'group'     => 'graphic',
937                                                        'module'    => 'pcd',
938                                                        'mime_type' => 'image/x-photo-cd',
939                                                        'fail_id3'  => 'ERROR',
940                                                        'fail_ape'  => 'ERROR',
941                                                ),
942
943
944                                // PNG  - still image - Portable Network Graphics (PNG)
945                                'png'  => array(
946                                                        'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
947                                                        'group'     => 'graphic',
948                                                        'module'    => 'png',
949                                                        'mime_type' => 'image/png',
950                                                        'fail_id3'  => 'ERROR',
951                                                        'fail_ape'  => 'ERROR',
952                                                ),
953
954
955                                // SVG  - still image - Scalable Vector Graphics (SVG)
956                                'svg'  => array(
957                                                        'pattern'   => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
958                                                        'group'     => 'graphic',
959                                                        'module'    => 'svg',
960                                                        'mime_type' => 'image/svg+xml',
961                                                        'fail_id3'  => 'ERROR',
962                                                        'fail_ape'  => 'ERROR',
963                                                ),
964
965
966                                // TIFF  - still image - Tagged Information File Format (TIFF)
967                                'tiff' => array(
968                                                        'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
969                                                        'group'     => 'graphic',
970                                                        'module'    => 'tiff',
971                                                        'mime_type' => 'image/tiff',
972                                                        'fail_id3'  => 'ERROR',
973                                                        'fail_ape'  => 'ERROR',
974                                                ),
975
976
977                                // Data formats
978
979                                // ISO  - data        - International Standards Organization (ISO) CD-ROM Image
980                                'iso'  => array(
981                                                        'pattern'   => '^.{32769}CD001',
982                                                        'group'     => 'misc',
983                                                        'module'    => 'iso',
984                                                        'mime_type' => 'application/octet-stream',
985                                                        'fail_id3'  => 'ERROR',
986                                                        'fail_ape'  => 'ERROR',
987                                                        'iconv_req' => false,
988                                                ),
989
990                                // RAR  - data        - RAR compressed data
991                                'rar'  => array(
992                                                        'pattern'   => '^Rar\!',
993                                                        'group'     => 'archive',
994                                                        'module'    => 'rar',
995                                                        'mime_type' => 'application/octet-stream',
996                                                        'fail_id3'  => 'ERROR',
997                                                        'fail_ape'  => 'ERROR',
998                                                ),
999
1000                                // SZIP - audio/data  - SZIP compressed data
1001                                'szip' => array(
1002                                                        'pattern'   => '^SZ\x0A\x04',
1003                                                        'group'     => 'archive',
1004                                                        'module'    => 'szip',
1005                                                        'mime_type' => 'application/octet-stream',
1006                                                        'fail_id3'  => 'ERROR',
1007                                                        'fail_ape'  => 'ERROR',
1008                                                ),
1009
1010                                // TAR  - data        - TAR compressed data
1011                                'tar'  => array(
1012                                                        '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}',
1013                                                        'group'     => 'archive',
1014                                                        'module'    => 'tar',
1015                                                        'mime_type' => 'application/x-tar',
1016                                                        'fail_id3'  => 'ERROR',
1017                                                        'fail_ape'  => 'ERROR',
1018                                                ),
1019
1020                                // GZIP  - data        - GZIP compressed data
1021                                'gz'  => array(
1022                                                        'pattern'   => '^\x1F\x8B\x08',
1023                                                        'group'     => 'archive',
1024                                                        'module'    => 'gzip',
1025                                                        'mime_type' => 'application/x-gzip',
1026                                                        'fail_id3'  => 'ERROR',
1027                                                        'fail_ape'  => 'ERROR',
1028                                                ),
1029
1030                                // ZIP  - data         - ZIP compressed data
1031                                'zip'  => array(
1032                                                        'pattern'   => '^PK\x03\x04',
1033                                                        'group'     => 'archive',
1034                                                        'module'    => 'zip',
1035                                                        'mime_type' => 'application/zip',
1036                                                        'fail_id3'  => 'ERROR',
1037                                                        'fail_ape'  => 'ERROR',
1038                                                ),
1039
1040
1041                                // Misc other formats
1042
1043                                // PAR2 - data        - Parity Volume Set Specification 2.0
1044                                'par2' => array (
1045                                                        'pattern'   => '^PAR2\x00PKT',
1046                                                        'group'     => 'misc',
1047                                                        'module'    => 'par2',
1048                                                        'mime_type' => 'application/octet-stream',
1049                                                        'fail_id3'  => 'ERROR',
1050                                                        'fail_ape'  => 'ERROR',
1051                                                ),
1052
1053                                // PDF  - data        - Portable Document Format
1054                                'pdf'  => array(
1055                                                        'pattern'   => '^\x25PDF',
1056                                                        'group'     => 'misc',
1057                                                        'module'    => 'pdf',
1058                                                        'mime_type' => 'application/pdf',
1059                                                        'fail_id3'  => 'ERROR',
1060                                                        'fail_ape'  => 'ERROR',
1061                                                ),
1062
1063                                // MSOFFICE  - data   - ZIP compressed data
1064                                'msoffice' => array(
1065                                                        'pattern'   => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
1066                                                        'group'     => 'misc',
1067                                                        'module'    => 'msoffice',
1068                                                        'mime_type' => 'application/octet-stream',
1069                                                        'fail_id3'  => 'ERROR',
1070                                                        'fail_ape'  => 'ERROR',
1071                                                ),
1072
1073                                 // CUE  - data       - CUEsheet (index to single-file disc images)
1074                                 'cue' => array(
1075                                                        'pattern'   => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
1076                                                        'group'     => 'misc',
1077                                                        'module'    => 'cue',
1078                                                        'mime_type' => 'application/octet-stream',
1079                                                   ),
1080
1081                        );
1082                }
1083
1084                return $format_info;
1085        }
1086
1087
1088
1089        function GetFileFormat(&$filedata, $filename='') {
1090                // this function will determine the format of a file based on usually
1091                // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
1092                // and in the case of ISO CD image, 6 bytes offset 32kb from the start
1093                // of the file).
1094
1095                // Identify file format - loop through $format_info and detect with reg expr
1096                foreach ($this->GetFileFormatArray() as $format_name => $info) {
1097                        // The /s switch on preg_match() forces preg_match() NOT to treat
1098                        // newline (0x0A) characters as special chars but do a binary match
1099                        if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1100                                $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1101                                return $info;
1102                        }
1103                }
1104
1105
1106                if (preg_match('#\.mp[123a]$#i', $filename)) {
1107                        // Too many mp3 encoders on the market put gabage in front of mpeg files
1108                        // use assume format on these if format detection failed
1109                        $GetFileFormatArray = $this->GetFileFormatArray();
1110                        $info = $GetFileFormatArray['mp3'];
1111                        $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1112                        return $info;
1113                } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1114                        // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
1115                        // so until I think of something better, just go by filename if all other format checks fail
1116                        // and verify there's at least one instance of "TRACK xx AUDIO" in the file
1117                        $GetFileFormatArray = $this->GetFileFormatArray();
1118                        $info = $GetFileFormatArray['cue'];
1119                        $info['include']   = 'module.'.$info['group'].'.'.$info['module'].'.php';
1120                        return $info;
1121                }
1122
1123                return false;
1124        }
1125
1126
1127        // converts array to $encoding charset from $this->encoding
1128        function CharConvert(&$array, $encoding) {
1129
1130                // identical encoding - end here
1131                if ($encoding == $this->encoding) {
1132                        return;
1133                }
1134
1135                // loop thru array
1136                foreach ($array as $key => $value) {
1137
1138                        // go recursive
1139                        if (is_array($value)) {
1140                                $this->CharConvert($array[$key], $encoding);
1141                        }
1142
1143                        // convert string
1144                        elseif (is_string($value)) {
1145                                $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1146                        }
1147                }
1148        }
1149
1150
1151        function HandleAllTags() {
1152
1153                // key name => array (tag name, character encoding)
1154                static $tags;
1155                if (empty($tags)) {
1156                        $tags = array(
1157                                'asf'       => array('asf'           , 'UTF-16LE'),
1158                                'midi'      => array('midi'          , 'ISO-8859-1'),
1159                                'nsv'       => array('nsv'           , 'ISO-8859-1'),
1160                                'ogg'       => array('vorbiscomment' , 'UTF-8'),
1161                                'png'       => array('png'           , 'UTF-8'),
1162                                'tiff'      => array('tiff'          , 'ISO-8859-1'),
1163                                'quicktime' => array('quicktime'     , 'UTF-8'),
1164                                'real'      => array('real'          , 'ISO-8859-1'),
1165                                'vqf'       => array('vqf'           , 'ISO-8859-1'),
1166                                'zip'       => array('zip'           , 'ISO-8859-1'),
1167                                'riff'      => array('riff'          , 'ISO-8859-1'),
1168                                'lyrics3'   => array('lyrics3'       , 'ISO-8859-1'),
1169                                'id3v1'     => array('id3v1'         , $this->encoding_id3v1),
1170                                '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
1171                                'ape'       => array('ape'           , 'UTF-8'),
1172                                'cue'       => array('cue'           , 'ISO-8859-1'),
1173                        );
1174                }
1175
1176                // loop thru comments array
1177                foreach ($tags as $comment_name => $tagname_encoding_array) {
1178                        list($tag_name, $encoding) = $tagname_encoding_array;
1179
1180                        // fill in default encoding type if not already present
1181                        if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1182                                $this->info[$comment_name]['encoding'] = $encoding;
1183                        }
1184
1185                        // copy comments if key name set
1186                        if (!empty($this->info[$comment_name]['comments'])) {
1187
1188                                foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1189                                        foreach ($valuearray as $key => $value) {
1190                                                $value = (is_string($value) ? trim($value) : $value);
1191                                                if (!empty($value) > 0) {
1192                                                        $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
1193                                                }
1194                                        }
1195                                }
1196
1197                                if (!isset($this->info['tags'][$tag_name])) {
1198                                        // comments are set but contain nothing but empty strings, so skip
1199                                        continue;
1200                                }
1201
1202                                if ($this->option_tags_html) {
1203                                        foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1204                                                foreach ($valuearray as $key => $value) {
1205                                                        if (is_string($value)) {
1206                                                                //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
1207                                                                $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
1208                                                        } else {
1209                                                                $this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
1210                                                        }
1211                                                }
1212                                        }
1213                                }
1214
1215                                $this->CharConvert($this->info['tags'][$tag_name], $encoding);           // only copy gets converted!
1216                        }
1217
1218                }
1219                return true;
1220        }
1221
1222
1223        function getHashdata($algorithm) {
1224                switch ($algorithm) {
1225                        case 'md5':
1226                        case 'sha1':
1227                                break;
1228
1229                        default:
1230                                return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1231                                break;
1232                }
1233
1234                if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1235
1236                        // We cannot get an identical md5_data value for Ogg files where the comments
1237                        // span more than 1 Ogg page (compared to the same audio data with smaller
1238                        // comments) using the normal getID3() method of MD5'ing the data between the
1239                        // end of the comments and the end of the file (minus any trailing tags),
1240                        // because the page sequence numbers of the pages that the audio data is on
1241                        // do not match. Under normal circumstances, where comments are smaller than
1242                        // the nominal 4-8kB page size, then this is not a problem, but if there are
1243                        // very large comments, the only way around it is to strip off the comment
1244                        // tags with vorbiscomment and MD5 that file.
1245                        // This procedure must be applied to ALL Ogg files, not just the ones with
1246                        // comments larger than 1 page, because the below method simply MD5's the
1247                        // whole file with the comments stripped, not just the portion after the
1248                        // comments block (which is the standard getID3() method.
1249
1250                        // The above-mentioned problem of comments spanning multiple pages and changing
1251                        // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1252                        // currently vorbiscomment only works on OggVorbis files.
1253
1254                        if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1255
1256                                $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)';
1257                                $this->info[$algorithm.'_data']  = false;
1258
1259                        } else {
1260
1261                                // Prevent user from aborting script
1262                                $old_abort = ignore_user_abort(true);
1263
1264                                // Create empty file
1265                                $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1266                                touch($empty);
1267
1268                                // Use vorbiscomment to make temp file without comments
1269                                $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1270                                $file = $this->info['filenamepath'];
1271
1272                                if (GETID3_OS_ISWINDOWS) {
1273
1274                                        if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1275
1276                                                $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1277                                                $VorbisCommentError = `$commandline`;
1278
1279                                        } else {
1280
1281                                                $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1282
1283                                        }
1284
1285                                } else {
1286
1287                                        $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1288                                        $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1289                                        $VorbisCommentError = `$commandline`;
1290
1291                                }
1292
1293                                if (!empty($VorbisCommentError)) {
1294
1295                                        $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;
1296                                        $this->info[$algorithm.'_data']  = false;
1297
1298                                } else {
1299
1300                                        // Get hash of newly created file
1301                                        switch ($algorithm) {
1302                                                case 'md5':
1303                                                        $this->info[$algorithm.'_data'] = md5_file($temp);
1304                                                        break;
1305
1306                                                case 'sha1':
1307                                                        $this->info[$algorithm.'_data'] = sha1_file($temp);
1308                                                        break;
1309                                        }
1310                                }
1311
1312                                // Clean up
1313                                unlink($empty);
1314                                unlink($temp);
1315
1316                                // Reset abort setting
1317                                ignore_user_abort($old_abort);
1318
1319                        }
1320
1321                } else {
1322
1323                        if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1324
1325                                // get hash from part of file
1326                                $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1327
1328                        } else {
1329
1330                                // get hash from whole file
1331                                switch ($algorithm) {
1332                                        case 'md5':
1333                                                $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1334                                                break;
1335
1336                                        case 'sha1':
1337                                                $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1338                                                break;
1339                                }
1340                        }
1341
1342                }
1343                return true;
1344        }
1345
1346
1347        function ChannelsBitratePlaytimeCalculations() {
1348
1349                // set channelmode on audio
1350                if (!isset($this->info['audio']['channels'])) {
1351                        // ignore
1352                } elseif ($this->info['audio']['channels'] == 1) {
1353                        $this->info['audio']['channelmode'] = 'mono';
1354                } elseif ($this->info['audio']['channels'] == 2) {
1355                        $this->info['audio']['channelmode'] = 'stereo';
1356                }
1357
1358                // Calculate combined bitrate - audio + video
1359                $CombinedBitrate  = 0;
1360                $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1361                $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1362                if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1363                        $this->info['bitrate'] = $CombinedBitrate;
1364                }
1365                //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1366                //      // for example, VBR MPEG video files cannot determine video bitrate:
1367                //      // should not set overall bitrate and playtime from audio bitrate only
1368                //      unset($this->info['bitrate']);
1369                //}
1370
1371                // video bitrate undetermined, but calculable
1372                if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1373                        // if video bitrate not set
1374                        if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1375                                // AND if audio bitrate is set to same as overall bitrate
1376                                if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1377                                        // AND if playtime is set
1378                                        if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1379                                                // AND if AV data offset start/end is known
1380                                                // THEN we can calculate the video bitrate
1381                                                $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1382                                                $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1383                                        }
1384                                }
1385                        }
1386                }
1387
1388                if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1389                        $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1390                }
1391
1392                if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1393                        $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1394                }
1395                if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1396                        if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1397                                // audio only
1398                                $this->info['audio']['bitrate'] = $this->info['bitrate'];
1399                        } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1400                                // video only
1401                                $this->info['video']['bitrate'] = $this->info['bitrate'];
1402                        }
1403                }
1404
1405                // Set playtime string
1406                if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1407                        $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1408                }
1409        }
1410
1411
1412        function CalculateCompressionRatioVideo() {
1413                if (empty($this->info['video'])) {
1414                        return false;
1415                }
1416                if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1417                        return false;
1418                }
1419                if (empty($this->info['video']['bits_per_sample'])) {
1420                        return false;
1421                }
1422
1423                switch ($this->info['video']['dataformat']) {
1424                        case 'bmp':
1425                        case 'gif':
1426                        case 'jpeg':
1427                        case 'jpg':
1428                        case 'png':
1429                        case 'tiff':
1430                                $FrameRate = 1;
1431                                $PlaytimeSeconds = 1;
1432                                $BitrateCompressed = $this->info['filesize'] * 8;
1433                                break;
1434
1435                        default:
1436                                if (!empty($this->info['video']['frame_rate'])) {
1437                                        $FrameRate = $this->info['video']['frame_rate'];
1438                                } else {
1439                                        return false;
1440                                }
1441                                if (!empty($this->info['playtime_seconds'])) {
1442                                        $PlaytimeSeconds = $this->info['playtime_seconds'];
1443                                } else {
1444                                        return false;
1445                                }
1446                                if (!empty($this->info['video']['bitrate'])) {
1447                                        $BitrateCompressed = $this->info['video']['bitrate'];
1448                                } else {
1449                                        return false;
1450                                }
1451                                break;
1452                }
1453                $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1454
1455                $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1456                return true;
1457        }
1458
1459
1460        function CalculateCompressionRatioAudio() {
1461                if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
1462                        return false;
1463                }
1464                $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));
1465
1466                if (!empty($this->info['audio']['streams'])) {
1467                        foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1468                                if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1469                                        $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1470                                }
1471                        }
1472                }
1473                return true;
1474        }
1475
1476
1477        function CalculateReplayGain() {
1478                if (isset($this->info['replay_gain'])) {
1479                        $this->info['replay_gain']['reference_volume'] = 89;
1480                        if (isset($this->info['replay_gain']['track']['adjustment'])) {
1481                                $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1482                        }
1483                        if (isset($this->info['replay_gain']['album']['adjustment'])) {
1484                                $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1485                        }
1486
1487                        if (isset($this->info['replay_gain']['track']['peak'])) {
1488                                $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1489                        }
1490                        if (isset($this->info['replay_gain']['album']['peak'])) {
1491                                $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1492                        }
1493                }
1494                return true;
1495        }
1496
1497        function ProcessAudioStreams() {
1498                if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1499                        if (!isset($this->info['audio']['streams'])) {
1500                                foreach ($this->info['audio'] as $key => $value) {
1501                                        if ($key != 'streams') {
1502                                                $this->info['audio']['streams'][0][$key] = $value;
1503                                        }
1504                                }
1505                        }
1506                }
1507                return true;
1508        }
1509
1510        function getid3_tempnam() {
1511                return tempnam($this->tempdir, 'gI3');
1512        }
1513
1514}
1515
1516
1517class getid3_exception extends Exception
1518{
1519    public $message;
1520}
1521
1522?>
Note: See TracBrowser for help on using the repository browser.