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

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

Updates to http://www.github.com/sleemanj/mootools-filemanager from GerHoblett?

http://www.github.com/GerHoblett/

Changes to said updates by gogo (sleemanj @ github)

Modifications to MootoolsFileManager? to work with those updates, some courtesy of GerHoblett?, some sleemanj

GerHoblett? provided a large diff which accomplished the goal in a quite different way. It has merit, however I have opted for a less-affecting path in so far as Xinha's "way" is concerned, namely, not splitting the config for a single plugin into several calls to backend config passing functions which seemed a little cumbersome.

Instead I take the option of using POST to send backend data around, at the minor expense of an extra round trip when displaying thumbnails (for each one). This could be reduced by checking for thumbnail existence and returning the thumbnail name directly in "onView" rather than the backend request to generate said thumbnail.

Still to do, is to make the preview pane thumbnail also work.


File size: 53.5 KB
RevLine 
[1300]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) {
[1302]1190                                                // $value = (is_string($value) ? trim($value) : $value);     // [i_a] http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1136
[1300]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.