~canonical-sysadmins/wordpress/4.7.4

« back to all changes in this revision

Viewing changes to wp-includes/ID3/getid3.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

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